目的
- CilinumNetworkPolicy(以下、CNP) 設定方法をしっかりと理解する
- 一部の CNI ネットワークプロバイダーでサポート可能な NetworkPolicy との違いを理解する
- L7 設定時の認証要求設定を理解する(required?)
手段
マニュアルベースで手を動かしていきます。
ざっくり理解する
killercoda の Playgroud を活用。
概要を理解する
前提として、k8s 環境で下記 NetworkPolicy を利用できる。
- Kubernetes NetworkPolicy
- CNP <--- 今回の学習範囲
- CiliumClusterwideNetworkPolicy <--- "2" の派生形だと推測。(Namespaceを指定しないタイプの CNP とのこと)
k8s 環境は cilium をサポートしていれば、CNP は全ての node に対して配布及び適用される。(厳密には、CNP の設定方法に依存するようです(ex: CLI, API 経由、等々..)
CNP はポリシー適用の設定が存在する。いわゆる、WAF の検知モード、ブロッキング、のように、設定したポリシーの適用レベルを制御できる。
- default:
- policy enforcement のデフォルト設定。このモードでは、全ての ingress/egress トラフィックは許可される。ポリシーに該当するトラフィックは default-deny 状態に遷移する。つまり、明示的にポリシーで許可された通信のみが成功する。言い換えると、policy enforcement がデフォルト設定の場合、CNP ポリシーが無ければ、全ての通信が許可される。逆に特定エンドポイントと通信の方向(ingress or egress)で特定された通信はdefault-deny に遷移する。
- always:
- 全てのエンドポイントに対してポリシーは有効化/適用される設定。
- never:
- ポリシー設定自体を無効にする。全てのエンドポイントに対して設定したポリシーは適用されない設定。言い換えると、CNP を disable にした状態。全ての ingress/egress 通信は許可される。
apiVersion: cilium.io/v2
kind: CiliumClusterwideNetworkPolicy # CNPを指定
metadata:
name: intercept-all-dns
spec:
endpointSelector: # endpointSelector で本ポリシー適用対象を指定。nodeSelector 設定も存在する
# nodeSelector は CiliumClusterwideNetworkPolicy でのみ利用可能
# 基本的に Namespace, Pod に紐づく label で適用対象を指定
matchExpressions: # matchExpressions で条件を指定可能
- key: "io.kubernetes.pod.namespace"
operator: "NotIn" # NotIn なので、key に対して value が含まれない、ことが条件
values:
- "kube-system"
- key: "k8s-app"
operator: "NotIn"
values:
- kube-dns
enableDefaultDeny: # ここが DefaultDeny 設定を制御する設定項目
egress: false # この egress 及び ingress を false にすれば、default mode が適用されない(無視される)
ingress: false
egress: # egress や ingress を指定して、どの方向のトラフィックを制御するのか指定する
- toEndpoints: # ここでは、to Endpionts で指定したアウトバウンド通信が適用対象となる
- matchLabels: # 下記 label を有する Pod が適用対象
io.kubernetes.pod.namespace: kube-system
k8s-app: kube-dns
toPorts: # ポートとプロトコルを指定
- ports:
- port: "53"
protocol: TCP
- port: "53"
protocol: UDP
rules:
dns:
- matchPattern: "*"
CiliumNetworkPolicy(以下、CNP) は下記設定を実現可能です。
- 特定通信に対する allow 及び deny
- 条件は L3/L4/L7 レイヤーで指定可能
- L7 レイヤーは認証必須として設定可能
基本的な設定内容は CNP を適用する対象 Namespace or Pod(いずれも labelで指定)を指定する。まずは、基本的な設定項目を確認します。
L3 サンプル
L3 ポリシーは下記条件で指定可能です。
- Endpoints Based: ポリシー内にアドレスのような固定値を含めない
- Services Based: こちらもポリシー内にアドレスのハードコードを防げる。また、Servcie オブジェクトが IP アドレスを隠蔽してくれる
- Entities Based: ローカルホストや外部の到達可能なエンドポイントが含まれる
- NodeBased: remote-node エンティティの拡張版。node単位で設定したいときに使うようだ
- IP/CIDR Based: いわゆる、192.168.1.1/32, 192.168.1.0/24 で設定する形式
- DNS Based: DNS lookup で解決可能なエンティティを指定可能
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
name: "l3-rule"
spec:
endpointSelector: # "role: backend " label を有する Pod が適用対象
matchLabels:
role: backend
ingress: # "role: backend " label を有するインバウンド通信が適用対象
- fromEndpoints: # つまり、"role: backend " label を有する Podに対する、
- matchLabels: # "role: backend " label を有する Pod からのインバウンド通信が許可される
role: frontend
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
name: "allow-all-to-victim"
spec:
endpointSelector: # "role: victim" label を有する pod が対象
matchLabels:
role: victim
ingress:
- fromEndpoints: # fromEndPoints をブランク {} 指定で全ての Pod が対象になる
- {} # つまり、全 Pod から "role: victim" label を有する pod への通信が許可される
注意として、ポリシーはあくまで sender(egress) と recevier(ingress) の両方の設定で許可された通信でないと成功しない。上記設定のように、recevier のみの設定だけでは不十分で、sender の設定が存在しないと通信は成立しない。管理面で考えると、ingressとegressを併記した方が分かり易そうに思う。
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
name: "l3-egress-rule"
spec:
endpointSelector: # "role: frontend" を有する Pod から "role: backend" を有する Pod へのアウトバウンド通信を許可
matchLabels: # 通信成功には受け側の igress ポリシーが必要
role: frontend
egress:
- toEndpoints:
- matchLabels:
role: backend
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
name: "deny-all-egress"
spec:
endpointSelector: # 当該ポリシー以外にポリシーが存在しなければ、
matchLabels: # "role: restricted" を有する Pod の Ingress 及び Egress を default-deny にできる?
role: restricted
egress:
- {}
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
name: "requires-rule"
specs:
- description: "For endpoints with env=prod, only allow if source also has label env=prod"
endpointSelector:
matchLabels:
env: prod
ingress:
- fromRequires: # fromRequires は到達可能な基本要件を指定する
- matchLabels: # "env: prod" label を有する Pod から "env: prod" label を有するエンドポイント
env: prod # に通信可能
上記ポリシーだけでは通信を許可するポリシーとして成立しない。
下記ポリシーを組み合わせれば、通信を許可するポリシーとして成立する。
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
name: "l3-rule"
specs:
- description: "For endpoints with env=prod, allow if source also has label role=frontend"
endpointSelector: # 上記ポリシーと合わせると、"env: prod" に対するインバウンド通信は、
matchLabels: # "role: frontend" 及び "env: prod" を有するエンドポイントから許可される
env: prod
ingress:
- fromEndpoints:
- matchLabels:
role: frontend
ここからはやってみようのコーナー!!
テスト環境は以下の通り。各 Namespace に nginx を1つ起動する。
- namespace
- dev
- stg
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Namespace
metadata:
creationTimestamp: null
name: dev
spec: {}
status: {}
EOF
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Namespace
metadata:
creationTimestamp: null
name: stg
spec: {}
status: {}
EOF
- pod * 2
- type: source
- type: dest
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
type: source
name: source
namespace: dev
spec:
containers:
- image: nginx
name: source
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
EOF
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
type: dest
name: dest
namespace: stg
spec:
containers:
- image: nginx
name: dest
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
EOF
Pod 状態を確認する。
controlplane $ k get po -n dev -n stg
NAME READY STATUS RESTARTS AGE
dest 1/1 Running 0 12s
source 1/1 Running 0 45s
続いて、CNPを作成する。
cat <<EOF | kubectl apply -f -
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
name: "requires-rule"
specs:
- description: "For endpoints with env=prod, only allow if source also has label env=prod"
endpointSelector:
matchLabels:
type: dest
ingress:
- fromRequires:
- matchLabels:
type: source
EOF
controlplane $ k apply -f fromRequires.yaml
ciliumnetworkpolicy.cilium.io/requires-rule created
controlplane $ k describe cnp requires-rule
Name: requires-rule
Namespace: default # CNP に対して namespace 指定していないため、default
Labels: <none> # default だと dev,stg namespace に適用されないのでは?
Annotations: <none>
API Version: cilium.io/v2
Kind: CiliumNetworkPolicy
Metadata:
Creation Timestamp: 2024-12-03T09:41:52Z
Generation: 1
Resource Version: 4174
UID: 35d81c5a-0eec-4f22-ac59-e9fa43cfcb45
Specs:
Description: For endpoints with env=prod, only allow if source also has label env=prod
Endpoint Selector:
Match Labels:
Type: dest
Ingress:
From Requires:
Match Labels:
Type: source
Status:
Conditions:
Last Transition Time: 2024-12-03T09:41:52Z
Message: Policy validation succeeded
Status: True
Type: Valid
Events: <none>
続いて Pod の IP アドレスを確認する。
controlplane $ k get po -A -o wide |grep -E 'dev|stg'
dev source 1/1 Running 0 84s 192.168.0.40 controlplane <none> <none>
stg dest 1/1 Running 0 15m 192.168.0.113 controlplane <none> <none>
controlplane $ k exec -it -n dev source -- curl --HEAD http://192.168.0.113
HTTP/1.1 200 OK # sorce pod から dest pod の curl に成功
二つ目の CNP を設定する。
controlplane $ k apply -f fromRequires2.yaml
ciliumnetworkpolicy.cilium.io/l3-rule created
controlplane $ k describe cnp l3-rule
Name: l3-rule
Namespace: default # こちらの CNP も namesapce 指定無し
Labels: <none>
Annotations: <none>
API Version: cilium.io/v2
Kind: CiliumNetworkPolicy
Metadata:
Creation Timestamp: 2024-12-03T09:50:58Z
Generation: 1
Resource Version: 5155
UID: 8a502978-d47c-4774-9fde-b7109beffca3
Specs:
Description: For endpoints with env=prod, allow if source also has label role=frontend
Endpoint Selector:
Match Labels:
Type: dest
Ingress:
From Endpoints:
Match Labels:
Role: nginx
Status:
Conditions:
Last Transition Time: 2024-12-03T09:50:58Z
Message: Policy validation succeeded
Status: True
Type: Valid
Events: <none>
sorce nginx に labels を追加する。
controlplane $ kubectl -n dev label pods source role=nginx
pod/source labeled
controlplane $ k get po -A -o wide --show-labels |grep -E 'dev|stg'
dev source 1/1 Running 0 3m53s 192.168.0.40 controlplane <none> <none> role=nginx,type=source
stg dest 1/1 Running 0 18m 192.168.0.113 controlplane <none> <none> type=dest
source -> dest への curl は成功する。CNP は動作していない。
正確には、namespace は default なので、default 所属の pod に適用されるポリシーとなる。
controlplane $ k exec -it -n dev source -- curl --HEAD http://192.168.0.113
HTTP/1.1 200 OK
CNP の namespace を dev に変更する。
controlplane $ k edit cnp requires-rule
controlplane $ k replace -f /tmp/kubectl-edit-4148436964.yaml --force
controlplane $ k edit cnp l3-rule
controlplane $ k replace -f /tmp/kubectl-edit-2320713019.yaml --force
controlplane $ k describe -n dev cnp requires-rule |grep -i namespace
Namespace: dev
controlplane $ k describe -n dev cnp l3-rule |grep -i namespace
Namespace: dev
正しい結果が得られていない。全 CNP を削除して、一つずつ確認する。
controlplane $ k describe -n stg cnp l3-allow
Name: l3-allow
Namespace: stg # stg に適用する
Labels: <none>
Annotations: <none>
API Version: cilium.io/v2
Kind: CiliumNetworkPolicy
Metadata:
Creation Timestamp: 2024-12-03T10:16:40Z
Generation: 1
Resource Version: 7959
UID: 9e48c541-744c-472f-8f6c-8e0b35b1e06e
Spec:
Endpoint Selector:
Match Labels:
Type: dest
Ingress:
From Endpoints:
Match Labels:
Type: source # なぜ Type の T は大文字なのか?
Status:
Conditions:
Last Transition Time: 2024-12-03T10:16:40Z
Message: Policy validation succeeded
Status: True
Type: Valid
Events: <none>
controlplane $ k exec -it -n dev source -- curl --HEAD -m 2 http://192.168.0.113
curl: (28) Connection timed out after 2000 milliseconds
command terminated with exit code 28
# curl に失敗する。
controlplane $ k get po -A -o wide --show-labels |grep -E 'dev|stg'
dev source 1/1 Running 0 26m 192.168.0.40 controlplane <none> <none> type=source
stg dest 1/1 Running 0 40m 192.168.0.113 controlplane <none> <none> type=dest
# label は正しいように見える。
controlplane $ k get po -A -o wide --show-labels |grep -E 'dev|stg'
dev source 1/1 Running 0 28m 192.168.0.40 controlplane <none> <none> Type=source,type=source
stg dest 1/1 Running 0 43m 192.168.0.113 controlplane <none> <none> Type=dest,type=dest
controlplane $ k exec -it -n dev source -- curl --HEAD -m 2 http://192.168.0.113
curl: (28) Connection timed out after 2001 milliseconds
command terminated with exit code 28
# 大文字の Type=source タグを付与しても curl は失敗。
# 何か根本的に違う気がする。CNP 設定を見直す。
これは receiver のポリシーとなる。通信を許可するには sender のポリシーが必要となる。
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
name: "l3-allow"
namespace: stg
spec:
endpointSelector:
matchLabels:
type: dest
ingress:
- fromEndpoints:
- matchLabels:
type: source
controlplane $ k exec -it -n dev source -- curl --HEAD -m 2 http://192.168.0.80
curl: (28) Connection timed out after 2001 milliseconds
ここまで receiver 及び sender の CNP を作成すれば良いと理解していました。しかし、正しく理解できていないようです。
Killercoda のシナリオを紐解いて理解を深める方針に切り替えます。
cat <<EOF | kubectl apply -f -
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
name: policy1
namespace: app
spec:
endpointSelector:
matchLabels: {}
egress:
- toEndpoints:
- matchLabels:
io.kubernetes.pod.namespace: data
id: data
EOF
# CNP 作成
controlplane $ k get po -A -o wide --show-labels|grep -E 'id=app|id=data|id=manager'
app app1 1/1 Running 0 12m 192.168.0.206 controlplane <none> <none> id=app
app app2 1/1 Running 0 12m 192.168.0.82 controlplane <none> <none> id=app
app manager1 1/1 Running 0 12m 192.168.0.153 controlplane <none> <none> id=manager
app manager2 1/1 Running 0 12m 192.168.0.87 controlplane <none> <none> id=manager
data data-001 1/1 Running 0 12m 192.168.0.172 controlplane <none> <none> id=data
data data-002 1/1 Running 0 12m 192.168.0.241 controlplane <none> <none> id=data
# POD IP 確認
controlplane $ k -n app exec app1 -- curl --head 192.168.0.172
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 615 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0
HTTP/1.1 200 OK
# data namespace 宛は成功
controlplane $ k -n app exec app1 -- curl --head -m 2 192.168.0.153
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- 0:00:02 --:--:-- 0
curl: (28) Connection timed out after 2001 milliseconds
command terminated with exit code 28
# data namespace 宛は許可していない。CNP の namespace を default に変更してみる。
controlplane $ k describe cnp policy1 |grep -i namespace
Namespace: default
# CNP の namespace を app -> default に変更
controlplane $ k -n app exec app1 -- curl --head 192.168.0.172
HTTP/1.1 200 OK
# data namespace 宛は成功
controlplane $ k -n app exec app1 -- curl --head -m 2 192.168.0.153
HTTP/1.1 200 OK
# manager namespace 宛は成功
# つまり、上記 CURL リクエストは CNP 対象ではない。namespace は spec.endpointSelector に
# 指定する対象が所属する namespace を指定するのか?
続いて、CNP の namespace を送信先(namespace: manager)に変更してみる。
controlplane $ k describe -n data cnp policy1 |grep -i namespace
Namespace: data
# CNP の namespace を defatul -> data に変更
controlplane $ k -n app exec app1 -- curl --head 192.168.0.172
HTTP/1.1 200 OK
# data namespace 宛は成功
controlplane $ k -n app exec app1 -- curl --head -m 2 192.168.0.153
HTTP/1.1 200 OK
# manager namespace 宛は成功
# つまり、上記 CURL リクエストは CNP 対象ではない。
上記結果から、CNP が所属する namespace と CNP 適用対象のエンドポイント(pod等)は一致する必要がある。あと、receiver と sender の概念が理解できていない。片側の policy で動作してると思うんですが...振り返ると、policy enforcement のデフォルト設定は当該通信に該当するポリシーが存在しない場合、default-allow なので、今回のケースもsenderはdefault-allowであるため、通信が成功すると推測。
仕切り直して、namespace * 3で検証する。
- namespace
- dev
- stg
- prod
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Namespace
metadata:
creationTimestamp: null
name: dev
spec: {}
status: {}
EOF
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Namespace
metadata:
creationTimestamp: null
name: stg
spec: {}
status: {}
EOF
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Namespace
metadata:
creationTimestamp: null
name: prod
spec: {}
status: {}
EOF
- pod * 3
- type: source
- type: dest
- type: manager
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
type: source
name: source
namespace: dev
spec:
containers:
- image: nginx
name: source
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
EOF
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
type: dest
name: dest
namespace: stg
spec:
containers:
- image: nginx
name: dest
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
EOF
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
type: manager
name: manager
namespace: prod
spec:
containers:
- image: nginx
name: manager
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
EOF
controlplane $ k get po -A -o wide --show-labels | grep -E 'dev|stg|prod'
dev source 1/1 Running 0 2m 192.168.0.108 controlplane <none> <none> type=source
prod manager 1/1 Running 0 67s 192.168.0.152 controlplane <none> <none> type=manager
stg dest 1/1 Running 0 104s 192.168.0.37 controlplane <none> <none> type=dest
# POD IP 確認
controlplane $ k exec -n dev source -- curl --head 192.168.0.37
HTTP/1.1 200
controlplane $ k exec -n dev source -- curl --head 192.168.0.152
HTTP/1.1 200 OK
# dev から stg 及び prod に curl 成功
cat <<EOF | kubectl apply -f -
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
name: policy1
namespace: dev
spec:
endpointSelector:
matchLabels: {}
egress:
- toEndpoints:
- matchLabels:
io/metadata.name: stg
type: dest
EOF
# CNP 作成
k exec -n dev source -- curl --head -m 2 192.168.0.37
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- 0:00:02 --:--:-- 0
curl: (28) Connection timed out after 2001 milliseconds
command terminated with exit code 28
k exec -n dev source -- curl --head -m 2 192.168.0.152
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- 0:00:02 --:--:-- 0
curl: (28) Connection timed out after 2001 milliseconds
command terminated with exit code 28
# 両方とも失敗する
controlplane $ k get -n dev cnp policy1 -oyaml
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"cilium.io/v2","kind":"CiliumNetworkPolicy","metadata":{"annotations":{},"name":"policy1","namespace":"dev"},"spec":{"egress":[{"toEndpoints":[{"matchLabels":{"io/metadata.name":"stg","type":"dest"}}]}],"endpointSelector":{"matchLabels":{}}}}
creationTimestamp: "2024-12-04T10:25:30Z"
generation: 3
name: policy1
namespace: dev
resourceVersion: "8185"
uid: 3bd86930-7ea5-4d6e-b053-ebaf9c46b93f
spec:
egress:
- toEndpoints:
- matchLabels:
io.kubernetes.pod.namespace: stg # この labels を追加すると通信は成功する
type: dest # この label 名はどのように可能可能か?
endpointSelector:
matchLabels: {}
k exec -n dev source -- curl --head -m 2 192.168.0.37
HTTP/1.1 200 OK
k exec -n dev source -- curl --head -m 2 192.168.0.152
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- 0:00:02 --:--:-- 0
curl: (28) Connection timed out after 2000 milliseconds
# CNP は正しく動作している
"io.kubernetes.pod.namespace: stg" の確認方法は?
ここで議論されている。
マニュアルにも説明が存在する。
取り合えず、マニュアルを読んでみる。
ここのサンプルはとても分かり易い。
つまり、CNP において、namespace 単位で制限を掛けたい場合、"io.kubernetes.pod.namespace: stg" の記述方法がお作法となる。マニュアルに利用可能な label 一覧があるため、そちらを参照すると理解が深まると思います。
L4 サンプル
L4 レイヤポリシーは L3 ポリシーに追加可能、独立して設定も可能です。(注意:L4ポリシーが存在しない場合、ICMP を含むすべてのポートは許可される。)
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
name: "l4-rule"
spec:
endpointSelector: # 適用対象は "app: myService" を有するエンドポイント
matchLabels:
app: myService
egress:
- toPorts: # TCP かつ 80でアウトバウンド通信
- ports:
- port: "80"
protocol: TCP
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
name: "l4-port-range-rule"
spec:
endpointSelector:
matchLabels:
app: myService
egress:
- toPorts:
- ports:
- port: "80" # 対象は TCP 80~444 ポートのアウトバウンド通信
endPort: 444
protocol: TCP
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
name: "cidr-l4-rule"
spec:
endpointSelector: # 対象は "role: crawler" を有するエンドポイント
matchLabels:
role: crawler
egress: # 対象は、アウトバウンド通信、192.0.2.0/24 宛、TCP:80ポート通信
- toCIDR:
- 192.0.2.0/24
toPorts:
- ports:
- port: "80"
protocol: TCP
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
name: "icmp-rule"
spec:
endpointSelector: # 対象は "app: myService" を有するエンドポイント
matchLabels:
app: myService
egress: # アウトバウンド通信
- icmps:
- fields:
- type: 8 # IPv4 の type 8
family: IPv4
- type: EchoRequest # IPv6 の EchoRequest
family: IPv6
検証環境で 80 版を Deny する。
controlplane $ k get po -A -o wide --show-labels | grep -E 'dev|stg|prod'
dev source 1/1 Running 0 27s 192.168.0.141 controlplane <none> <none> type=source
prod manager 1/1 Running 0 27s 192.168.0.106 controlplane <none> <none> type=manager
stg dest 1/1 Running 0 27s 192.168.0.102 controlplane <none> <none> type=dest
controlplane $
controlplane $ k exec -n dev source -- curl --head 192.168.0.106
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
HTTP/1.1 200 OK 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0
# dev -> prod への 80ポートは成功
CNP を作成する。
cat <<EOF | kubectl apply -f -
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
name: "cidr-l4-rule"
namespace: dev
spec:
endpointSelector: # 対象は "role: crawler" を有するエンドポイント
matchLabels:
type: source
io.kubernetes.pod.namespace: dev
egress: # 対象は、アウトバウンド通信、192.0.2.0/24 宛、TCP:80ポート通信
- toCIDR:
- 192.168.0.106/32
toPorts:
- ports:
- port: "80"
protocol: TCP
EOF
k exec -n dev source -- curl --head -m2 192.168.0.106
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- 0:00:02 --:--:-- 0
curl: (28) Connection timed out after 2001 milliseconds
command terminated with exit code 28
# 192.168.0.106 宛は失敗する
CNP内容を変更してみる。
cat <<EOF | kubectl apply -f -
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
name: "cidr-l4-rule"
namespace: dev
spec:
endpointSelector: # 対象は "role: crawler" を有するエンドポイント
matchLabels:
type: source
io.kubernetes.pod.namespace: dev
egress: # 対象は、アウトバウンド通信、192.0.2.0/24 宛、TCP:80ポート通信
- toPorts:
- ports:
- port: "80"
protocol: TCP
EOF
controlplane $ k exec -n dev source -- curl --head 192.168.0.106
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 615 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0
HTTP/1.1 200 OK
controlplane $ k exec -n dev source -- curl --head 192.168.0.102
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 615 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0
HTTP/1.1 200 OK
# 80 は allow
controlplane $ k exec -n dev source -- curl --head -m2 https://192.168.0.106
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- 0:00:02 --:--:-- 0
curl: (28) Connection timed out after 2001 milliseconds
command terminated with exit code 28
controlplane $ k exec -n dev source -- curl --head -m2 https://192.168.0.102
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- 0:00:02 --:--:-- 0
curl: (28) Connection timed out after 2000 milliseconds
command terminated with exit code 28
# 443通信は deny
80 のみを deny に変更する。
cat <<EOF | kubectl apply -f -
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
name: "cidr-l4-rule"
namespace: dev
spec:
endpointSelector: # 対象は "role: crawler" を有するエンドポイント
matchLabels:
type: source
io.kubernetes.pod.namespace: dev
egress: # 対象は、アウトバウンド通信、192.0.2.0/24 宛、TCP:80ポート通信
- toPorts:
- ports:
- port: "80"
protocol: TCP
EOF
# 80 番の Deny 設定作成に伴い、default-deny モードに移行する。故に、当該エンドポイントに対して、明示的に許可ポリシーを設定しない限り、アウトバウンド通信は許可されない。
### L7 サンプル
```AllowGET-public.yaml
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
name: "rule1"
spec:
description: "Allow HTTP GET /public from env=prod to app=service"
endpointSelector:
matchLabels:
app: service # 対象
ingress:
- fromEndpoints: # 対象は "env: prod" を有するエンドポイントからのインバウンド通信
- matchLabels: # かつ、TCP:80 で、GET /public のみ許可する
env: prod
toPorts:
- ports:
- port: "80"
protocol: TCP
rules:
http:
- method: "GET"
path: "/public"
GET / で設定してみる。
cat <<EOF | kubectl apply -f -
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
name: "rule1"
namespace: dev
spec:
endpointSelector:
matchLabels:
type: source
io.kubernetes.pod.namespace: dev
ingress:
- fromEndpoints:
- matchLabels:
type: dest
io.kubernetes.pod.namespace: stg
toPorts:
- ports:
- port: "80"
protocol: TCP
rules:
http:
- method: "GET"
path: "/"
EOF
最後に L7 で認証を requiredする方法は?
マニュアルに明記されている。
authentication:
mode: "required"
L7 設定に authentication 設定を追加してみる。
cat <<EOF | kubectl apply -f -
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
name: "rule1"
namespace: dev
spec:
endpointSelector:
matchLabels:
type: source
io.kubernetes.pod.namespace: dev
ingress:
- fromEndpoints:
- matchLabels:
type: dest
io.kubernetes.pod.namespace: stg
toPorts:
- ports:
- port: "80"
protocol: TCP
rules:
http:
- method: "GET"
path: "/"
authentication:
mode: "required"
EOF
# 上記 yaml で設定可能
controlplane $ k describe -n dev cnp rule1
Name: rule1
Namespace: dev
Labels: <none>
Annotations: <none>
API Version: cilium.io/v2
Kind: CiliumNetworkPolicy
Metadata:
Creation Timestamp: 2024-12-10T10:23:43Z
Generation: 1
Resource Version: 6643
UID: b3635ede-e4c4-4bab-8813-3fafe574c2f0
Spec:
Endpoint Selector:
Match Labels:
io.kubernetes.pod.namespace: dev
Type: source
Ingress:
Authentication:
Mode: required
From Endpoints:
Match Labels:
io.kubernetes.pod.namespace: stg
Type: dest
To Ports:
Ports:
Port: 80
Protocol: TCP
Rules:
Http:
Method: GET
Path: /
Status:
Conditions:
Last Transition Time: 2024-12-10T10:23:43Z
Message: Policy validation succeeded
Status: True
Type: Valid
Events: <none>
# describe結果
あとがき
Networkpolicyは別の記事に書きます。。。