【実務・中級編】 Kubernetes Network Policiesによるマイクロセグメンテーション – 暗号理論・認証基盤 & エンドポイントセキュリティ防御ガイド

現場のエンジニア諸君、お疲れ様。CISSPとして数々のインシデント現場を見てきた経験から言わせてもらうが、ネットワーク境界(Perimeter)の防御だけで満足しているなら、それは「鍵をかけた玄関の窓を全開にしている」のと同じだ。

クラウドネイティブな環境、特にKubernetesにおいて、Pod間の通信を野放しにするのは自殺行為に等しい。一度フロントエンドのPodがRCE(リモートコード実行)で突破された瞬間、攻撃者は内部ネットワークを自由に探索(スキャン)し、DBやバックエンドサービスを蹂躙する。これを防ぐ唯一の道が「マイクロセグメンテーション」だ。

今日は、Kubernetes Network Policies(NetPol)を使って、その「横展開(ラテラルムーブメント)」を物理的に封じ込める設計を伝授する。

—

1. なぜ「デフォルト拒否」が必要なのか?

Kubernetesのネットワークモデルは、デフォルトで「すべてのPodはすべてのPodと通信可能」という性善説に基づいている。しかし、本番環境で性善説は通用しない。

攻撃者がPodの脆弱性を突き、内部に入り込んだ場合、真っ先に行うのは nmap 等を用いた内部ポートスキャンだ。彼らは「どのPodがどのサービスに接続できるか」を探り、認証情報がハードコードされた環境変数や、認証なしでアクセスできる内部APIを狙う。

この「内部での動きやすさ」を奪うのが、NetPolの役割だ。

2. 実践:マイクロセグメンテーションの鉄則

まずは、「すべての通信を拒否し、必要なものだけを許可する(Default Deny)」という基本方針を適用する。これを忘れると、設定漏れがそのままバックドアになる。

以下のYAMLは、そのNamespace内のあらゆる受信・送信通信を遮断する「鉄壁」の設定だ。

# 01-default-deny-all.yaml
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
  name: default-deny-all
  namespace: production # 適用したい名前空間を指定
spec:
  podSelector: {} # 空にすることで全Podに適用
  policyTypes:
  - Ingress
  - Egress

3. アプリケーション層でのホワイトリスト制御

次に、具体的なPod間通信を許可する設定だ。例えば、「Frontend」Podから「Backend」Podのポート8080のみを許可する例を見てみよう。

# 02-allow-frontend-to-backend.yaml
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
  name: allow-frontend-to-backend
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: backend # 許可される側のPod(受信側)
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: frontend # 許可する側のPod(送信側)
    ports:
    - protocol: TCP
      port: 8080 # 特定のポートのみ開放

ここがプロの視点:なぜ「ラベル」を慎重に扱うべきか

多くのエンジニアが陥る罠は、ラベルの管理がずさんなことだ。「適当にラベルを付けたPodが、実はセキュリティポリシーの対象外になっていた」というケースは笑い話にならない。ラベルはコードの一部と考え、IaC(Infrastructure as Code)で厳格に管理すること。

—

4. 攻撃者はどこを狙うのか(PoC的視点)

もし君たちが攻撃者なら、NetPolが設定されていない環境でどう動くか?
例えば、Node.jsで書かれたバックエンドの脆弱性を突いて以下のようなコードを忍ばせ、内部ネットワークを探索するだろう。

// 攻撃者の視点:内部ネットワーク探索用スクリプトの断片
const net = require('net');

// 内部サービスのIP範囲をスキャン
for (let i = 1; i <= 255; i++) {
  const target = `10.0.1.${i}`;
  const client = new net.Socket();
  client.setTimeout(500);
  
  client.connect(6379, target, () => {
    console.log(`[!] Redis発見: ${target}`); // Redisを狙ってキャッシュを汚染
    client.destroy();
  }).on('error', () => {});
}

このスキャン自体が、NetPolを導入していれば「Egress(送信)」の段階で遮断される。攻撃者はポートに繋ぐことすらできず、ログには大量の拒否イベントが残り、即座にIDS(侵入検知システム)が反応する。これが「守り」の差だ。

—

5. 運用上のアドバイス:迷ったら「ログ」を見ろ

NetPolを導入すると、正常な通信まで遮断してシステムがダウンすることがある。これを恐れて導入をためらうのは間違いだ。

1. Dry-runはできないが、可視化はできる:
CiliumのようなeBPFベースのネットワークプラグインを使うと、どの通信が拒否されたかというフローを可視化できる。
2. 段階的導入:
まずは default-deny を適用し、その後必要な通信を一つずつホワイトリストに追加する。「動かない」を「一つずつ許可する」に切り替えるのが、最も安全で堅実なアプローチだ。

結び:セキュリティは「諦め」の先にある

セキュリティを完璧にしようとすると、往々にして利便性が損なわれる。しかし、Kubernetesのネットワーク制御は、正しく設計すれば「動くべき通信」を邪魔することなく、「動くべきではない通信」を確実に遮断できる。

今日紹介した設定を、まずは開発環境のNamespaceに投入してみてほしい。もし通信が切れたなら、それは君たちがこれまで「意図しない通信」を許容していた証拠だ。その一つ一つを洗い出し、ホワイトリストへ書き換えていく作業こそが、真の堅牢なインフラを築く唯一の道である。

次は、暗号化通信(mTLS/Service Mesh)によるPod間認証の強化について掘り下げよう。現場からは以上だ。構築を急げ。

コメント

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