CiliumによるKubernetesネットワークポリシー:eBPFマイクロセグメンテーション実践ガイド

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

Kubernetesはデフォルトでフラットなネットワークモデルを採用しています。名前空間を越えてすべてのPodが障壁なく互いに直接通信できる状態です。もし攻撃者が外部公開コンテナへ侵入した場合、内部のRedisキャッシュや決済データベース、Kubeletエンドポイントへの探索を阻止するものは何もありません。

従来のCNIプラグインは、iptablesやIPVSを介して標準的なNetworkPolicyルールを適用します。クラスタが1,000 Podおよび数万件のセキュリティルールを超える規模に拡大すると、Linuxカーネルは順次パケットフィルタを線形(リニア)に評価しなければならなくなります。

これにより顕著な接続レイテンシが発生し、高負荷な接続変動下ではミリ秒未満のセットアップ時間が5〜15ミリ秒まで跳ね上がることがよくあります。CiliumはレガシーなパケットフィルタをeBPF(Extended Berkeley Packet Filter)に置き換え、カーネルのネットワークフック内でコンパイル済みセキュリティプログラムを直接実行することで、この問題を解決します。

クイックスタート:CiliumのインストールとDefault-Denyの適用

Cilium CLIを使用すれば、わずか5分で強固なゼロトラストのベースラインを確立できます。

ステップ 1: Cilium CLIのインストールとデータパスのデプロイ

# 最新のCilium CLIをダウンロードしてインストール
CILIUM_CLI_VERSION=$(curl -s https://raw.githubusercontent.com/cilium/cilium-cli/main/stable.txt)
CLI_ARCH=amd64
if [ "$(uname -m)" = "aarch64" ]; then CLI_ARCH=arm64; fi
curl -L --fail --remote-name-all "https://github.com/cilium/cilium-cli/releases/download/${CILIUM_CLI_VERSION}/cilium-linux-${CLI_ARCH}.tar.gz"
sudo tar xzvfC cilium-linux-${CLI_ARCH}.tar.gz /usr/local/bin
rm cilium-linux-${CLI_ARCH}.tar.gz

# クラスタにCiliumをデプロイ
cilium install --version 1.15.4

# エージェントPodとeBPFマップの初期化を待機
cilium status --wait

ステップ 2: デモ用ワークロードのデプロイ

専用のテスト用名前空間に、フロントエンドWebサーバーとバックエンドRedisキャッシュという2つのシンプルなPodを作成します。

kubectl create namespace demo-secure

# フロントエンドをデプロイ
kubectl run frontend --namespace=demo-secure \
  --image=nginx:alpine --labels="app=frontend,tier=public"

# バックエンドキャッシュをデプロイ
kubectl run backend --namespace=demo-secure \
  --image=redis:7-alpine --labels="app=backend,tier=secure"

ステップ 3: Default-Denyポリシーの適用

名前空間を完全にロックダウンします。IngressやEgressのルールを持たない空のエンドポイントセレクタを適用することで、demo-secure内のすべてのPodを予期しないトラフィックから即座に隔離します。

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: default-deny-all
  namespace: demo-secure
spec:
  endpointSelector:
    matchLabels: {}

隔離用マニフェストを適用します:

kubectl apply -f default-deny.yaml

これで、frontendとbackend間の新しいTCPハンドシェイクは、ネットワークスタックのリソースを消費する前にソケット層で即座にドロップされるようになります。

詳細解説:eBPFがマイクロセグメンテーションを再定義する仕組み

従来のLinuxフィルタリングは、すべてのパケットをnetfilterチェイン(PREROUTING、FORWARD、POSTROUTING)経由でルーティングします。クラスタが20,000件のiptablesルールを保持している場合、それらを1つずつ評価することで大幅なCPUサイクルを消費し、パケット処理のジッター(揺らぎ)を引き起こします。

CiliumはTraffic Control(tc)、XDP、cgroup/sock_opsなどの低レベルフックにeBPFバイトコードプログラムをアタッチすることで、netfilterを完全にバイパスします。このアーキテクチャにより、3つの主要なメリットが得られます:

  • アイデンティティベースのセキュリティ: 変動するPodのIPアドレスを追跡する代わりに、CiliumはKubernetesのラベルセットから導出された数値のセキュリティアイデンティティ(例:ID: 4120)を割り当てます。
  • O(1)のマップルックアップ: パケットが到着すると、eBPFはカーネルBPFハッシュマップを定数時間で検索します。このルックアップは、ルールが10件であっても50,000件であっても、約50ナノ秒で完了します。
  • ソケットレベルのバイパス(sockmap): 同一ワーカーノード上で通信するPod間では、CiliumがUnixソケット間でデータを直接ストリーミングし、TCP/IPのシリアライズ処理を完全にバイパスすることでラウンドトリップレイテンシを15〜20%削減します。

以下は、フロントエンドPodのみがポート6379でRedisにクエリを実行できるように許可する、精密なCiliumNetworkPolicy(CNP)の記述例です:

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: allow-frontend-to-backend
  namespace: demo-secure
spec:
  endpointSelector:
    matchLabels:
      app: backend
  ingress:
  - fromEndpoints:
    - matchLabels:
        app: frontend
    toPorts:
    - ports:
      - port: "6379"
        protocol: TCP

フロントエンドコンテナからの接続性を確認します:

# 接続テスト(正常にオープン状態が返されるはずです)
BACKEND_IP=$(kubectl get pod backend -n demo-secure -o jsonpath='{.status.podIP}')
kubectl exec -n demo-secure frontend -- nc -zv -w 2 $BACKEND_IP 6379

高度なフィルタリング:レイヤー7 HTTPパスとDNS対応Egress

標準的なL3/L4ポリシーはIPアドレスとポートのみを制御します。しかし、現代のマイクロサービスではきめ細かなレイヤー7制御が求められます。Ciliumはローカルエージェント内にEnvoyプロキシインスタンスを直接組み込んでおり、アプリケーションPodにサイドカーを挿入することなくHTTPメソッドやヘッダーを検査できます。

シナリオ 1: HTTPメソッドとRESTエンドポイントの制限

請求サービス(billing-service)からGET /metricsのみを取得する必要があるアナリティクスコレクターを考えてみましょう。POST /chargeやDELETE /accountsのような危険な操作をCNIレイヤーで直接ブロックできます。

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: restrict-billing-api
  namespace: demo-secure
spec:
  endpointSelector:
    matchLabels:
      app: billing-service
  ingress:
  - fromEndpoints:
    - matchLabels:
        app: analytics-collector
    toPorts:
    - ports:
      - port: "8080"
        protocol: TCP
      rules:
        http:
        - method: "GET"
          path: "/metrics"

シナリオ 2: DNS対応Egressの完全制御

コンテナを侵害した攻撃者は、外部の任意のサーバーからペイロードをダウンロードしたりクレデンシャルを持ち出したりすることがよくあります。Ciliumは送信DNSクエリを検査し、承認されたFQDNターゲットに対してのみ動的にEgressの通信を許可します。

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: lock-egress-to-stripe
  namespace: demo-secure
spec:
  endpointSelector:
    matchLabels:
      app: frontend
  egress:
  # CoreDNSの名前解決を許可
  - toEndpoints:
    - matchLabels:
        "k8s:io.kubernetes.pod.namespace": kube-system
        "k8s:k8s-app": kube-dns
    toPorts:
    - ports:
      - port: "53"
        protocol: ANY
      rules:
        dns:
        - matchPattern: "*"
  # Stripe APIへの外部HTTPS通信のみを厳格に許可
  - toFQDNs:
    - matchName: "api.stripe.com"
    toPorts:
    - ports:
      - port: "443"
        protocol: TCP

本番運用の強化とトラブルシューティングのヒント

アクティブなクラスタにマイクロセグメンテーションを展開する際は、意図しないダウンタイムを防ぐために慎重なロールアウト戦略が必要です。

  • 適用前の監査(Audit): ポリシーにio.cilium.audit-mode: "true"のアノテーションを付与します。これによりパケットをドロップすることなくポリシーの一致がHubbleに記録されるため、厳格な適用を有効化する前に不足しているルールを発見できます。
  • Hubbleによるリアルタイムのドロップ追跡: Ciliumの可観測性エンジンを使用して、発生しているパケットドロップをリアルタイムで監視します:
    # ドロップされたネットワークフローをリアルタイムでストリーミング
    hubble observe --namespace demo-secure --verdict DROPPED --follow
  • クラウドメタデータエンドポイントのグローバルブロック: CiliumClusterwideNetworkPolicy(CCNP)をデプロイして、すべての名前空間のPodがクラウドインスタンスのメタデータサービス(169.254.169.254/32)にアクセスするのをブロックし、SSRF攻撃から保護します。
  • 強力な管理者認証情報の徹底: ネットワーク層の保護は対策の半分にすぎず、ノードやクラスタの踏み台サーバーへの管理者アクセスにも同様の厳格さが必要です。セキュアな運用用ランダムパスワードを生成するには、toolcraft.app/ja/tools/security/password-generator のブラウザベースのジェネレータが、ネットワーク露出ゼロの迅速なユーティリティとして役立ちます。

従来のパケットフィルタからeBPFに移行することで、一貫したO(1)のパフォーマンスを維持しながら、レイヤー3からレイヤー7までの精密なポリシー適用が可能になります。これにより、ゼロトラストなKubernetesネットワーキングが運用のボトルネックからスケーラブルなセキュリティ基盤へと進化します。

Share: