ポリシーに「拒否」はない
一言でいうと
NetworkPolicyには、拒否という概念がありません。Podにポリシーが1つもなければすべて許可で、1つでも付いた瞬間に、その方向が許可リストに反転します。
なぜこの切り替えを知る必要があるのか
Kubernetesの既定は、すべて許可です。どのネームスペースのどのPodでも、他のすべてのPodに届きます。侵入者がWebのPodを1つ押さえると、その場でデータベースが見えます。
既定値が反転する地点
NetworkPolicyの動作の仕方は、直感と違います。
Podにポリシーが1つも付いていなければすべて許可で、1つでも付いた瞬間に、そのPodはその方向について、許可リスト方式に変わります。
そのため、ポリシーには「拒否」という概念がそもそもありません。default-denyという名前のポリシーも、実際は何も許可しないポリシーにすぎません。
spec:
podSelector: {} # 이 네임스페이스의 모든 파드
policyTypes: [Ingress] # ingress 만 다룬다
# ingress: 항목이 없다 → 허용하는 것이 하나도 없다
この切り替えを知らないと、「ポリシーを1つ追加しただけなのに、関係のないものが切れた」ということになります。
方向を混同しない方法
ポリシーは、常に受信側に付けます。
| フィールド | 選ぶもの |
|---|---|
podSelector |
保護されるPod(db) |
ingress.from |
許可する送信元(api) |
送信側に付けると、構文は通ってオブジェクトも作られるのに、何も起きません。エラーがないので、かえって混乱します。
複数のポリシーは足し合わされる
同じPodに付いたポリシーが複数あるなら、和集合です。あとから付けたポリシーが、前のものを上書きしません。そのため、「絞り込むためにポリシーを追加したのに、かえって広がる」ことが起きます。絞り込むには、既存のポリシーを直す必要があります。
最もよく起きる障害
egressをデフォルトでブロックした瞬間、DNSも一緒に切れます。CoreDNSへ出ていく53番ポートもegressだからです。
症状は「名前が見つかりません」で、CoreDNSは問題なく、他のネームスペースは正常です。そのため、誰も、今付けたネットワークポリシーを疑いません。
最初にポリシーを入れる順序
デフォルトのブロックから有効にすると、その瞬間にすべてが切れます。順序があります。
1. 何が通信しているかを先に見ます。ポリシーを書く前に、実際のフローを見ます。CiliumならHubble、それ以外はconntrackやアプリケーションのログで描きます。
hubble observe --namespace labhub-prod --last 500 -o json | jq -r '"\(.source.namespace)/\(.source.pod_name) → \(.destination.namespace)/\(.destination.pod_name):\(.l4.TCP.destination_port)"' | sort | uniq -c | sort -rn
2. 許可ポリシーを先に入れます。まだデフォルトのブロックがないので、何もブロックされません。
3. そのあと、デフォルトのブロックを有効にします。このとき、抜かしていたものが明らかになります。
4. DNSを必ず確認します。egressを使った瞬間、DNS(53/UDP)もブロックされます。名前解決ができないと、すべてがタイムアウトに見えて、原因を探しにくくなります。
egress:
- to:
- namespaceSelector:
matchLabels: {kubernetes.io/metadata.name: kube-system}
podSelector:
matchLabels: {k8s-app: kube-dns}
ports:
- {protocol: UDP, port: 53}
- {protocol: TCP, port: 53}
ポリシーが止められないもの
ネットワークポリシーは万能ではありません。止められないものを知っておけば、別の層に回せます。
| 止められないもの | 代わりに |
|---|---|
| 同じPodの中のコンテナ同士 | Podを分けます |
| hostNetworkのPod | Pod SecurityでhostNetworkを禁止します |
| ノード自体から出ていくトラフィック | ノードのファイアウォール・セキュリティグループ |
| L7(パス・メソッド) | CiliumのL7ポリシー、サービスメッシュ |
| すでに確立された接続 | ポリシーは新しい接続にだけ適用されます |
最後の行が落とし穴です。ポリシーを入れても、進行中だった接続は生き続けます。そのため、「ポリシーを入れたのに、まだ通じる」ということが起きます。確認するには、新しい接続を張ってみる必要があります。
デバッグの順序
# 1. 이 파드를 고르는 정책이 있나 (없으면 기본 허용)
kubectl get netpol -n <ns> -o json | jq -r '
.items[] | select(.spec.podSelector.matchLabels // {} | to_entries | length > 0) |
"\(.metadata.name): \(.spec.podSelector.matchLabels) \(.spec.policyTypes)"'
# 2. 양쪽 다 열려 있나 — 출발지 egress, 도착지 ingress
# 3. 실제로 막혔는지 확인 (Connection refused 는 정책이 아니다)
kubectl exec -n <ns> <pod> -- nc -zv -w3 <대상> <포트>
3つ目の区別が重要です。ポリシーに阻まれるとtimeoutで、誰も待ち受けていなければrefusedです。refusedが出たら、ポリシーの問題ではありません。
実務で本当に大切なこと
egressをブロックするときは、DNSを同じコミットで一緒に開けます。CoreDNSへ出ていく53番もegressなので一緒に切れますが、症状は「名前が見つかりません」で、CoreDNSは問題なく、他のネームスペースは正常です。そのため、誰も、今付けたポリシーを疑いません。
ポリシーは、常に受信側に付けます。送信側に付けると、構文は通ってオブジェクトも作られるのに、何も起きません。エラーがないので、より長く迷います。
絞り込むには、ポリシーを追加せず、既存のポリシーを直します。同じPodに付いたポリシーは和集合なので、あとから付けたものが前のものを上書きしません。絞り込もうとして追加したのに、かえって広がることが、ここから起きます。
次のラボで、この障害を自分で起こしてから、直します。そして、直すときにUDP 53だけを開けると、なぜさらに悪い状態になるのかも、一緒に見ます。