Kubernetes Network Policyによるセキュリティ:クラスター内の「ラテラルムーブメント」を阻止する

Security tutorial - IT technology blog
Security tutorial - IT technology blog

「フラットネットワーク」という名の悪夢

Kubernetesはデフォルトで「フラットネットワーク(flat network)」を採用しています。これは、Namespaceが異なっていても、すべてのPodが何の制限もなく互いに通信できることを意味します。利便性は高いですが、これは致命的な脆弱性でもあります。

私は以前、実際の侵入事案に対応したことがあります。ハッカーが古いコードの脆弱性を突いて、あるFrontend Podの制御権を奪いました。クラスターにNetwork Policyが設定されていなかったため、攻撃者はわずか15分で内部IPレンジをスキャンし、別のNamespaceにある機密データベースを見つけ出し、すべてのデータを奪取しました。私のようにトラブル対応で徹夜したくなければ、Network Policyを必須のレイヤー3-4ファイアウォールとして考えるべきです。

なぜKubernetesはデフォルトで「開放的」なのか?

その理由はCNI(Container Network Interface)にあります。Flannelのような古い世代のCNIは、アプリケーションが即座にスムーズに動作することを優先し、すべての接続を許可しています。しかし、本番環境において、テスト用Pod(Dev)が顧客データベース(Prod)に接続できてしまう状態は、許容できないリスクです。

安全性を確保するためには、Zero Trust(ゼロトラスト)の考え方が必要です。内部コンポーネントであっても、デフォルトでは誰も信頼しないという原則です。

CNIの確認:ポリシー実行のための必須条件

重要な注意点:すべてのCNIがNetwork Policyをサポートしているわけではありません。Flannelを使用している場合、作成したYAMLファイルは機能しません。Calico, Cilium, Weave Net、または主要クラウドプロバイダー(GKE, EKS, AKS)のデフォルトCNIが必要です。

使用しているCNI(例:Calico)が動作しているか、次のコマンドで確認します:

kubectl get pods -n kube-system | grep calico

実践的なシステム隔離の3ステップ

1. Default Deny(全遮断)の設定

「許可してから制限する」のではなく、「すべて拒否してから必要なものだけ許可する」のがセキュリティの鉄則です。以下のポリシーは、production Namespaceへのすべてのインバウンド(Ingress)およびアウトバウンド(Egress)トラフィックを遮断します。

default-deny-all.yamlファイルを作成します:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: production
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress

これを適用すると、Podは完全に隔離されます。Pod間通信ができなくなるだけでなく、インターネットへのアクセスやDNS解決さえもできなくなります。

2. 同一Namespace内の通信を許可する

全遮断した後は、アプリケーションが動作しなくなるのを防ぐため、同じproduction Namespace内のPod同士が通信できるように設定します。

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-same-namespace
  namespace: production
spec:
  podSelector: {}
  ingress:
  - from:
    - podSelector: {}

3. 必要なポートのみを開放する(最小権限の原則)

例えば、frontend Podがポート8080経由でapi-serviceを呼び出す必要があるとします。IPレンジ全体を開放するのではなく、Podのラベル(label)を明示的に指定しましょう。

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: api-allow-frontend
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: api-service
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: frontend
    ports:
    - protocol: TCP
      port: 8080

実践シナリオ:モニタリングツールによるデータ収集を許可する

Prometheusなどのモニタリングシステムは、通常別のNamespaceに配置されます。メトリクスを取得するためにapp Namespaceへのアクセスを許可しつつ、他のNamespaceからのアクセスは遮断する必要があります。

まず、monitoring Namespaceにラベルを付与します:

kubectl label namespace monitoring usage=monitoring

次に、このラベルを持つNamespaceからのトラフィックを許可するように設定します:

spec:
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          usage: monitoring

システム障害を防ぐための「痛い教訓」

Network Policy의 導入は、以下の点を見落とすと簡単にアプリケーションをダウンさせてしまいます:

  • DNSを忘れない: Egress(送信)を制限すると、Podはgoogle.comや内部DBの名前解決ができなくなります。常にkube-dnsへのポート53 (UDP/TCP) を開放しておきましょう。
  • 加算的な性質: KubernetesのNetwork Policyは加算的(additive)です。どれか一つのポリシーで許可されていれば、他のポリシーで制限されていても通信は成立します。
  • 可視化ツールの活用: Cilium Service Mapなどのツールを使用して、トラフィックフローを視覚的に確認しましょう。なぜタイムアウトが発生しているのかを勘で探るのはやめましょう。
  • ロギングの有効化: Calicoを使用している場合は、ドロップされたパケットのログを有効にします。これにより、どのPodが不正アクセスを試みているか、あるいは設定漏れがどこにあるかがすぐに分かります。

初期設定には時間がかかるかもしれませんが、これによりクラスターが「ノーガード状態」から脱却できます。今日から最も重要なNamespaceから着手しましょう。

Share: