TT Lab
はじめる
学ぶ 学習パス コース

Kubernetesネットワーク — 本物のクラスタで

ポリシーに「拒否」はない

TT Labで続きを見る

一言でいうと

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だけを開けると、なぜさらに悪い状態になるのかも、一緒に見ます。