Kubernetesの「裸の王様」を救え:NetworkPolicyでラテラルムーブメントを封殺せよ
Kubernetesを導入したばかりのチームで、よくこんな光景を目にする。「デプロイが完了した!疎通確認もOK!」と喜んでいるが、実はそのクラスター、「隣のPodに何でも自由に話しかけられる」という、いわばセキュリティホールだらけの無法地帯になっていることに気づいていない。
もし君のフロントエンドPodが脆弱性(例えば、Remote Code Execution)を突かれたらどうなるか。攻撃者はそのPodを足掛かりに、内部の認証情報サーバーやデータベースへ、一切の制限なく「横展開(ラテラルムーブメント)」を開始する。これこそが、モダンなインフラで最も恐れるべきシナリオだ。
今日は、教科書的な「通信を制限しましょう」という説教ではなく、現場で通用する「デフォルト拒否(Default Deny)」の極意を伝授する。
—
1. 攻撃者の視点:なぜ「Pod間通信の制限」が甘いと死ぬのか
攻撃者は、クラスター内に侵入した後、まずは内部のネットワークをスキャンする。彼らが探しているのは、認証が甘いキャッシュサーバーや、特権を持つ内部APIだ。
もしNetworkPolicyが設定されていないと、攻撃者は以下の手順で瞬時に内部資産を奪取する。
1. 侵入: フロントエンドのWebアプリの脆弱性を突いてシェルを奪取。
2. 偵察: curl や nmap を使って、同じ名前空間内の他PodのIPアドレスとポートを総当たり。
3. 攻撃: 内部APIの脆弱性や、未認証のRedis/DBへアクセスし、機密データを持ち出す。
Kubernetesのデフォルト動作は「すべて許可(Allow All)」。この「性善説」を今すぐ捨て去るのが、プロのエンジニアの第一歩だ。
—
2. 現場で使うべき「デフォルト拒否(Default Deny)」の実装
まず行うべきは、その名前空間内の通信をすべて遮断することだ。これを設定しない限り、どれだけ個別に許可設定をしても、どこかに穴が残る。
以下のYAMLは、指定した名前空間内の全PodへのIngress(流入)とEgress(流出)をシャットダウンする「鉄壁の門番」だ。
default-deny-all.yaml
この名前空間に存在するすべてのPodの通信をデフォルトで拒否する
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: production # 適用したい名前空間を指定
spec:
podSelector: {} # 空にすることで、その名前空間内の全Podが対象になる
policyTypes:
- Ingress
- Egress
これを適用した瞬間、外部からのアクセスすら止まるはずだ。驚く必要はない。これが本来の「ゼロトラスト」の姿だ。ここから、「必要な通信だけ」を穴あけしていく。
—
3. 実践:ラベルベースで安全な通信経路を定義する
次に、フロントエンドPodからバックエンドAPIへの通信だけを許可する設定例を見てみよう。ここでのポイントは、IPアドレスではなくラベルを使うことだ。IPは動的に変わるが、ラベルは開発者が定義したアイデンティティだからだ。
allow-frontend-to-backend.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend-to-backend
namespace: production
spec:
# 適用対象:バックエンドのPod
podSelector:
matchLabels:
app: backend
ingress:
- from:
# 許可する元:フロントエンドのPod
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080 # バックエンドが待ち受けているポート
なぜこれが強力なのか?
もし攻撃者がフロントエンドを乗っ取っても、このポリシーがある限り、他の管理用Pod(例えば app: admin-tool)には一切アクセスできない。攻撃者の手足が縛られ、被害を最小限に抑える(Blast Radiusの制限)ことができる。
—
4. 現場の教訓:インシデントを未然に防ぐためのTips
最後に、運用を楽にするための「現場の知恵」を共有する。
- 名前空間を分ける:
NetworkPolicyは名前空間を跨いだ制御には別の工夫が必要だ。環境(dev/stg/prod)や役割ごとに名前空間を分離し、その境界でポリシーを適用するのが最も確実だ。 - ログを必ず確認する: 突然通信が遮断されると、アプリが503エラーを吐く。
calicoなどのCNIプラグインを使っているなら、拒否されたパケットのログを収集し、ポリシーの漏れがないか定期的にモニタリングしてほしい。 - 「とりあえず全開放」は禁止: 「動かないから」といって
policyTypesをコメントアウトするのは、セキュリティ担当としては許しがたい。必要な通信をkubectl describeで特定し、ポリシーを精緻化するプロセスをCI/CDパイプラインに組み込むべきだ。
まとめ
セキュリティとは「面倒な作業」ではなく「予測可能な安定」を生むための設計だ。KubernetesのNetworkPolicyを活用して、クラスター内に強固な「防壁」を築くこと。それができれば、万が一の侵入時も、君たちは冷静にログを解析し、攻撃を無力化できる。
さあ、今すぐ kubectl apply を実行し、君のクラスターを「真に信頼できる環境」にアップデートしよう。疑問があればいつでも聞く。泥臭いトラブルシューティングこそが、エンジニアを成長させるのだから。
コメント