【テクニカル・上級編】 Kubernetes Network Policiesによる名前空間間の通信制御 – インフラ・ネットワーク & クラウドセキュリティ防御ガイド

Kubernetesの「フラットネットワーク幻想」を断つ:Network Policiesによるゼロトラスト・マイクロセグメンテーションの実装

クラウドネイティブなインフラストラクチャにおいて、私たちは長年にわたって危険な誤解を共有してきた。
「Kubernetesクラスターの内側は安全である」という、いわゆる境界型防御の残滓(ざんし)である。

CiliumやCalico、あるいはKubernetes標準のCNIをデプロイした瞬間、デフォルトの状態では、クラスター内のあらゆるPodは、他のあらゆるPodや外部ネットワークに対して文字通り「全開放(Allow-All)」の状態で通信を行う。これは、社内LANに接続されたすべてのPCが、何のルーター制御もなしに直接パケットを送り合っている状態に等しい。ひとたびひとつのWebアプリケーションPodがRCE(リモートコード実行)脆弱性やサプライチェーン攻撃によって侵害されれば、攻撃者は何のエスカレーションも経ることなく、同一クラスター内にあるデータベースや決済基幹システムへ直接TCPコネクションを確立できる。

ホワイトハッカーの視点から言えば、これは「侵入された瞬間からのゲームオーバー」をあらかじめ設計に組み込んでいるようなものだ。

本稿では、この悪名高いフラットネットワーク構造を根底から覆し、Kubernetes NetworkPolicy を用いた堅牢なホワイトリスト形式の通信制御(マイクロセグメンテーション)を実装するためのアーキテクチャと、現場の泥臭いインシデント対応から導き出された実践知を解説する。

—

1. なぜ「デフォルト拒否(Default Deny)」から始めなければならないのか

セキュリティの鉄則は「拒否がデフォルト、許可が例外」である。しかし、多くの開発現場やインフラチームは、アプリケーションのデプロイ容易性を優先するあまり、この原則を忘却している。

Kubernetesの NetworkPolicy は、OSI参照モデルのレイヤ3およびレイヤ4(IPアドレス、ポート番号)において、パケットのフィルタリングを行う。Linuxカーネルの深部において、これは iptables(あるいはよりモダンな nftables、さらにはeBPFベースのCiliumによるXDP/TCフック)を通じて強制される。

もしあなたが「特定の通信だけをブロックする(ブラックリスト方式)」を採用しているなら、今すぐその設計を破棄すべきだ。攻撃者は常にあなたが想定していない経路や、意図せず公開されたデバッグ用ポートを突いてくる。防衛側が取るべき唯一の現実解は、名前空間(Namespace)レベル、およびPodレベルでの「完全な通信遮断(Default Deny)」をベースラインとし、業務上必要最低限のトラフィックのみをホワイトリストで許可することである。

—

2. アーキテクチャ設計:名前空間間の論理的要塞化

多層防御(Defense-in-Depth)の観点から、名前空間(Namespace)は単なるリソースの論理的分割ツールではない。それはセキュリティ境界(Security Boundary)として機能させるべきだ。

例えば、以下のような3層構造のアプリケーションを想定する。
1. ingress 名前空間:外部からのリバースプロキシやAPIゲートウェイが常駐
2. app 名前空間:ビジネスロジックを処理するマイクロサービスが常駐
3. datastore 名前空間:機密性の高いRDBMSやKVストアが常駐

このアーキテクチャにおいて、datastore 名前空間内のデータベースPodは、原則として app 名前空間からの特定のPod(かつ特定のポート)以外からのパケットを受け入れてはならない。当然、ingress から datastore への直接アクセスは、アーキテクチャとしてもセキュリティとしてもご法度だ。

—

3. 実践:Network Policiesによる厳格なホワイトリスト実装

ここからは、実際にクラスターへ適用するための具体的なマニフェストを見ていく。

ステップ1:全名前空間における「デフォルト拒否」の強制

まずは、該当する名前空間(ここでは datastore)において、すべてのイングレス(受信)およびイグレス(送信)トラフィックを遮断するベースラインポリシーを適用する。

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: datastore
spec:
  # ポッドセレクターを空にすることで、この名前空間内のすべてのPodを対象にする
  podSelector: {}
  # イングレス(受信)とイグレス(送信)の両方を制御対象に指定
  policyTypes:
    - Ingress
    - Egress

このポリシーを適用した瞬間、datastore 内のPodは外部からの疎通を完全に失う。DNSの名前解決さえも失敗する可能性があるため、イグレス側(Egress)の制御には細心の注意が必要となる(DNS通信を許可するポリシーを別途用意するか、CNI側の仕様を考慮すること)。

ステップ2:最小権限に基づくホワイトリストの定義

次に、app 名前空間で稼働する特定のフロントエンド・サービス(ラベル: app: user-api)からのみ、datastore 内のPostgreSQL(ポート 5432)へのアクセスを許可するポリシーを記述する。

ここで重要なのは、IPアドレスベースではなく、Kubernetesのネイティブな概念である「名前空間」と「Podラベル」をセレクターとして使用する点である。IPアドレスはPodの再起動やオートスケーリングによって動的に変動するため、静的なIPフィルタリングはクラウドネイティブ環境では破綻する。

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-app-to-postgres
  namespace: datastore
spec:
  # 制御対象となるデータベースのPodを指定(ラベルで特定)
  podSelector:
    matchLabels:
      app: postgresql-db
  
  policyTypes:
    - Ingress
    
  ingress:
    - from:
        # 1. 許可する送信元が属する「名前空間」をラベルで指定
        # (Namespace側にあらかじめ `name: app` などのラベルを付与しておく必要がある)
        - namespaceSelector:
            matchLabels:
              environment: production
              project: core-app
          
          # 2. さらにその名前空間内の特定の「Pod」に絞り込む
          podSelector:
            matchLabels:
              app: user-api
              
      # 許可するプロトコルとポート番号の指定
      ports:
        - protocol: TCP
          port: 5432

このポリシーにより、仮に app 名前空間内の別の脆弱なバッチ処理Podが侵害されたとしても、そのPodには app: user-api のラベルが付与されていない限り、データベースへのネットワーク層での到達性は完全に断たれることになる。これがマイクロセグメンテーションの本質だ。

—

4. インフラ・セキュリティ監査の現場から:見落としがちな罠と検知

実務で多くのKubernetesクラスターを監査してきた経験上、Network Policiesの導入にはいくつかの致命的な落とし穴が存在する。ここを理解していないと、「設定したつもり」のまま脆弱な環境を放置することになる。

1. CNI(Container Network Interface)の非対応

KubernetesのAPIサーバーは NetworkPolicy リソースを喜んで受け入れるが、それを受け取って実際にパケットをドロップするかどうかは、裏で動いているCNIプラグインに依存している。
例えば、標準的な kube-proxy とプレーンなネットワーク設定の組み合わせ(一部の古い環境や軽量K8s)では、NetworkPolicyが完全に無視されるケースがある。必ず使用しているCNI(Cilium, Calico, Antrea, AWS VPC CNIなど)がNetworkPolicyをサポートし、正しく有効化されていることを確認検証( kubectl auth can-i や実際の疎通テスト)しなければならない。

2. DNS(UDP/TCP 53番ポート)の遮断による障害

デフォルト拒否(Default Deny)をイグレス(Egress)に対しても厳格に適用した場合、Podが外部のAPIエンドポイントや内部のKubernetes DNS(CoreDNS)の名前解決を行えなり、アプリケーションが突然死するインシデントが頻発する。
Egressを制限する場合は、必ずCoreDNSへのトラフィック(通常はkube-system名前空間のCoreDNSのPod、あるいはクラスターIP)を許可するルールを明示的に記述する必要がある。

- to:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: kube-system
          podSelector:
            matchLabels:
              k8s-app: kube-dns
      ports:
        - protocol: UDP
          port: 53
        - protocol: TCP
          port: 53

3. トラブルシューティングの難易度

パケットがNetworkPolicyによってドロップされた場合、アプリケーション側には単なる Connection timed out や Connection refused として記録される。どのポリシーのどのルールに抵触したのかを即座に特定するためには、CNIのログ監査機能や、一時的にデバッグ用コンテナ( ephemeral containers 等)をPodにアタッチして tcpdump や nmap を実行するスキルがインフラエンジニアには求められる。

—

5. 結びにかえて:ゼロトラストは「仕様」ではなく「執念」である

KubernetesのNetwork Policiesの設定は、派手さのない地味な作業だ。しかし、この数行のYAMLファイルの積み重ねこそが、サイバー犯罪者が侵入したその先のラテラルムーブメント(横展開)を完全に封じ込める最後の防壁となる。

コンテナ技術やオーケストレーションがどれほど進化しようとも、セキュリティの基本原則——「信じるな、検証せよ(Never trust, always verify)」——が変わることはない。フラットネットワークという名の甘い幻想を今すぐ捨て去り、コード化された厳格な境界線をあなたのクラスターに刻み込んでほしい。

コメント

タイトルとURLをコピーしました