機雷がなんだ! 全速前進!

SEというかプログラマというか、日々のエンジニア生活の中で体験したことなどを中心に書き残しています。

k8s NetworkPolicyの条件(ports)記述の注意点

NetworkPolicyでハマったので備忘録としてメモしておきます。

やりたいこと

  • 名前空間 A の全 Pod を Namespace B の Pod への送信を制限する np という NetworkPolicy を作成する。(受信は特に制限なし)
  • 名前空間 B の全 Pod が Namespace A の Pod からの受信しかできないよう制限する np という NetworkPolicy も作成する。(送信は特に制限なし)
  • ポート 53 TCP および UDP での DNS 通信を許可する。

やったこと(★失敗)

名前空間 A 用のNetworkPolicy

■ np-A.yaml

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: np
  namespace: A
spec:
  podSelector: {}
  policyTypes:
  - Egress
  egress:
  - to:
    - namespaceSelector:
        matchLabels:
          kubernetes.io/metadata.name: B
    ports:
    - protocol: TCP
      port: 53
    - protocol: UDP
      port: 53

名前空間 B 用のNetworkPolicy

■ np-B.yaml

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: np
  namespace: B
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          kubernetes.io/metadata.name: A

やったこと(☆成功)

名前空間 A 用のNetworkPolicy

■ np-A.yaml

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: np
  namespace: A
spec:
  podSelector: {}
  policyTypes:
  - Egress
  egress:
  - to:
    - namespaceSelector:
        matchLabels:
          kubernetes.io/metadata.name: B
  - ports:
    - protocol: TCP
      port: 53
    - protocol: UDP
      port: 53

名前空間 B 用のNetworkPolicy

■ np-B.yaml

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: np
  namespace: B
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          kubernetes.io/metadata.name: A

ハマった原因

成功と失敗で何が違うのかぱっと見ても分かりにくいと思うので差分表示してみます。

名前空間 A 用のNetworkPolicy(np-A.yaml)

←(左:★失敗) | (右:☆成功)→

これ(ハイフン「-」の有無)に目視で気付くのは、かなり難しい気がしましす。(少なくとも私には)

しかも kubernetes の公式ドキュメント「ネットワークポリシー | Kubernetes」のサンプルは今回失敗したパターンの「 ports:」の記載のみで、成功パターンの「- ports:」という記載の仕方については、特に触れられていませんでした。これはハマりやすいやつですね。

今回の「ports」とは別のNetworkPolicy設定項目(「namespaceSelector」と「podSelector」)で類似の問題について解説されている記事「Kubernetes道場 16日目 - NetworkPolicyについて - Toku's Blog」を発見して、やっと原因に気付くことができました。(※以下の内容は、記事から抜粋して引用)

しかし、このフィールドは一部のフィールドの組み合わせが許可されており、以下のような指定が可能だ。 (中略) なんとも分かりづらいが、 podSelector のハイフンが消えて、1要素になっている。 この設定は project=test-app とマッチするNamespaceにあるPodで role=frontend とマッチするものが対象になる。 要はこの記述でNamespaceとPodを同時に指定できる。

本当になんとも分かりづらい問題でした。

今後もNetworkPolicyのマニフェストを記述する際は、(portsのみならず)この点に注意したいと思いました。

さいごに

とはいえ、この手の問題は、実際に動作確認して気付くのが近道な気もするので、意図した挙動か下記のようなコマンドでとっととチェックした方が良いかもしれません。

■ 通信OK

kubectl -n A exec app1-0 -- curl -m 1 app1.B.svc.cluster.local
kubectl -n A exec app1-0 -- curl -m 1 app2.B.svc.cluster.local
kubectl -n A exec app1-0 -- nslookup tester.default.svc.cluster.local
kubectl -n kube-system exec -it test-pod -- curl -m 1 app1.A.svc.cluster.local

■ 通信NG

kubectl -n A exec app1-0 -- curl -m 1 tester.default.svc.cluster.local
kubectl -n kube-system exec -it test-pod -- curl -m 1 app1.B.svc.cluster.local
kubectl -n kube-system exec -it test-pod -- curl -m 1 app2.B.svc.cluster.local
kubectl -n default run nginx --image=nginx --restart=Never -i --rm  -- curl -m 1 app1.B.svc.cluster.local