Certified Kubernetes Security Specialist (CKS) #12 Pod 間 mTLS: Cilium

CKS #11 では gVisor と Kata Containers でコンテナをカーネルレベルで隔離する方法を扱いました。隔離が Pod 1 個の爆発半径を減らす仕事だったとすれば、今回の記事は Pod の間を行き交う通信そのものを守る 仕事です。Minimize Microservice Vulnerabilities ドメインの最後のテーマとして、転送中の暗号化 (encryption in transit) とその代表的な実装である mTLS を押さえます。

Kubernetes のデフォルトのネットワークはフラットです。Pod が別の Pod に送るパケットは、別途の設定がなければ 平文 (plaintext) でノードの間を流れます。同じクラスターの中だから安全というわけではありません。ノード間トラフィックを盗聴できる攻撃者や、クラスターネットワークに足を踏み入れた侵入者は、平文パケットをそのまま読みます。今回の記事は、この平文の問題をどう暗号化で覆うのか、そしてその方式が NetworkPolicy と何が違うのかを概念中心に整理します。

なぜ Pod 間通信が平文だと危険なのか #

マイクロサービス構造では 1 つのリクエストが複数の Pod を経由します。フロントエンド Pod が認証サービスに尋ね、認証サービスがユーザー DB に尋ね、さらに決済サービスへ渡る、という具合です。この間を行き交うデータには、セッショントークン、パスワード、決済情報のような機微な値が含まれます。

デフォルトのクラスターではこのトラフィックは暗号化されません。次のような状況を思い浮かべると危険がはっきりします。

  • ノード間の盗聴。CNI によっては Pod トラフィックがノードの間を平文で流れます。物理ネットワークやクラウド VPC のレベルでパケットをキャプチャできる攻撃者は、内容をそのまま読みます。
  • 横方向の移動 (lateral movement)。攻撃者が 1 つの Pod を掌握すると、同じネットワークで他のサービスへ向かう平文トラフィックをスニッフィングしたり、身元の検証なしに内部サービスへ直接接続したりします。
  • 身元の不在。平文通信は相手が誰なのかを検証しません。IP アドレスだけでは「本当にそのサービスなのか」を保証できません。IP は偽造されたり再割り当てされたりします。

CKS #2 で扱った NetworkPolicy は、このうち 誰が誰に接続できるか を制御します。しかし NetworkPolicy は許可された接続の 内容を暗号化しません。許可された通信であっても、そのパケットは依然として平文です。ここで転送中の暗号化が必要になります。

NetworkPolicy と mTLS は別の層の制御 #

CKS で最も混同しやすい点が、この 2 つの役割の区別です。まず表で整理します。

区分NetworkPolicymTLS
動作レイヤーL3・L4 (IP・ポート)L7 に近い (接続・セッション)
制御対象接続の許可・遮断暗号化 + 相互の身元検証
防ぐもの許可されていない通信そのもの盗聴、改ざん、身元のなりすまし
たとえると扉を開け閉めする錠封印された封筒 + 身元確認
単独で十分かいいえいいえ

NetworkPolicy は 接続できるか を決め、mTLS は 接続された通信を暗号化し両側の身元を検証 します。2 つは代替物ではなく補完物です。default deny で通信を狭め (NetworkPolicy)、残った通信を暗号化して身元を掛ける (mTLS) ことが、in-transit セキュリティの 2 本の柱です。

mTLS とは何か #

TLS は、クライアントがサーバーの証明書を検証する一方向の身元確認です。ウェブブラウザがサーバーを信じる HTTPS がこれにあたります。mTLS (mutual TLS) はここから一歩進んで、両側が互いの証明書を検証 します。クライアントも自分の証明書を提示し、サーバーもその証明書を確認します。

Kubernetes の中で mTLS は 2 つを同時に提供します。

  • 暗号化 (encryption)。2 つの Pod の間のトラフィックが TLS で暗号化され、途中で傍受しても内容を読めません。
  • ワークロードの身元 (identity)。各ワークロードに証明書を発行し、IP ではなく証明書で「このサービスが本当に決済サービスなのか」を検証します。この身元はしばしば SPIFFE のような標準で表現されます。

アプローチ 1: Service Mesh のサイドカー mTLS #

Pod 間 mTLS を実装する最も広く知られた方式が Service Mesh です。代表的な実装は Istio と Linkerd です。

核心となるアイデアは 各 Pod の横にプロキシコンテナ (サイドカー) を付ける ことです。アプリケーションコンテナは普段どおり平文で通信していると思い込んでいますが、実際には同じ Pod の中のサイドカープロキシがトラフィックを横取りし、相手 Pod のサイドカーと mTLS でやり取りします。

[ app A ] --平文--> [ sidecar A ] ==mTLS 暗号化==> [ sidecar B ] --平文--> [ app B ]

この構造の特徴は次のとおりです。

  • アプリケーションコードの無変更。開発者が TLS を直接扱いません。メッシュがサイドカーの注入 (injection) で mTLS を透過的に処理します。
  • 自動の証明書管理。メッシュのコントロールプレーンが各ワークロードに証明書を発行し、短い周期で更新 (rotation) します。人が証明書を作る必要がありません。
  • L7 ポリシーと可観測性。mTLS のほかにも、トラフィックルーティング、リトライ、L7 権限ポリシー、分散トレーシングのような機能を一緒に提供します。

Istio でメッシュ全体に mTLS を強制するポリシーは、概念的には次のようになります。

apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata:
  name: default
  namespace: istio-system
spec:
  mtls:
    mode: STRICT       # 平文を拒否、mTLS のみ許可

STRICT モードは、サイドカーがあるワークロードの間の通信を mTLS でのみ受けます。導入の過渡期には PERMISSIVE (平文・mTLS の両方を許可) にしておき、段階的に STRICT へ移すのが一般的です。

サイドカー方式のコスト #

Service Mesh は強力ですが代償があります。Pod ごとにプロキシコンテナが 1 つずつ余分に付くので メモリ・CPU のオーバーヘッド が生じ、トラフィックがプロキシをもう 1 回経由するので 遅延 (latency) が増えます。コントロールプレーンという運用対象も新たに生まれます。そのため「暗号化だけが必要なのか、L7 機能まで必要なのか」が導入判断の分かれ道になります。

アプローチ 2: Cilium の透過的暗号化 #

暗号化が主な目的なら、メッシュのサイドカーなしに CNI レイヤーでノード間トラフィックをまるごと暗号化する 方法があります。これが Cilium の透過的暗号化 (transparent encryption) です。

Cilium は eBPF ベースの CNI で、NetworkPolicy を処理するデータプレーンでそのまま暗号化を有効にできます。2 つのバックエンドを提供します。

バックエンド特徴
WireGuard設定が単純で軽い。最新の推奨方式
IPsec古い標準。一部の規制・互換の要求に合致

透過的暗号化の核心は、名前のとおり 透過的 だという点です。アプリケーションも、Pod マニフェストも変えません。Cilium がノードを離れる Pod トラフィックを自動的に暗号化し、受け取るノードで復号します。サイドカーがないので Pod ごとのプロキシのオーバーヘッドもありません。

[ Pod A on node1 ] --> Cilium (暗号化) ==WireGuard トンネル==> Cilium (復号) --> [ Pod B on node2 ]

WireGuard 透過的暗号化は、Cilium の設定で 1 行程度で有効にする概念です。インストールツールによって異なりますが、Helm 値で表現すると次のような形になります。

# Cilium インストール時 (Helm values の概念)
encryption:
  enabled: true
  type: wireguard

有効にすると、ノードの間を行き交う Pod トラフィックが WireGuard で暗号化されます。ノード間の盗聴の脅威が消えることが核心的な効果です。

Cilium 透過的暗号化 vs mTLS #

ここで 1 つの区別が重要です。WireGuard・IPsec 透過的暗号化は ノード対ノード (node-to-node) 暗号化 です。トラフィックを暗号化しますが、mTLS のような ワークロード単位の相互身元検証 をそれ自体で提供するわけではありません。盗聴は防ぎますが、「この接続の相手ワークロードが本当に誰なのか」を証明書で検証することは別の話です。

そのため Cilium は別途 mTLS の方向 の機能を発展させてきました。SPIFFE ベースのワークロードの身元を導入し、暗号化に加えて 身元ベースのポリシー までサイドカーなしで処理しようとする流れです。整理すると次のとおりです。

  • WireGuard・IPsec 透過的暗号化: ノード間トラフィックの暗号化に集中。盗聴防止が目的。
  • Cilium mTLS・SPIFFE の身元: ワークロード単位の身元検証とポリシー。サイドカーのない mTLS の方向。

CKS の観点では、「サイドカーメッシュ (Istio・Linkerd) なのか、CNI 内蔵の暗号化 (Cilium) なのか」という 2 つの選択肢 があるという事実と、それぞれが暗号化と身元をどう扱うのかを区別できれば十分です。

Cilium での L7 制御 1 行 #

Cilium は標準の NetworkPolicy を拡張した CiliumNetworkPolicy で L7 レベルの制御も表現します。たとえば特定のサービスに対して HTTP GET /metrics だけを許可するポリシーを、概念的に 1 つの断片だけ見てみます。

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: allow-metrics-only
spec:
  endpointSelector:
    matchLabels:
      app: backend
  ingress:
    - toPorts:
        - ports:
            - port: "8080"
              protocol: TCP
          rules:
            http:
              - method: "GET"
                path: "/metrics"

標準の NetworkPolicy がポートまでしか制御しなかったとすれば、CiliumNetworkPolicy は HTTP メソッド・パスのような L7 属性まで制御します。暗号化・身元とは別の軸ですが、in-transit セキュリティを語るときに Cilium が L3・L4 を越えてどこまで届くのかを示す例です。

CKS の観点: 何を知っておくべきか #

このテーマで CKS が求める深さは 細かなメッシュ構成ではなく概念と判断 です。次を明確にできれば十分です。

  • なぜ必要か。Pod 間通信はデフォルトで平文であり、ノード間の盗聴と横方向の移動にさらされます。転送中の暗号化でこの脅威を覆います。
  • 何が違うか。NetworkPolicy は接続の許可・遮断 (L3・L4)、mTLS は暗号化と相互の身元検証です。2 つは補完の関係です。
  • どんな方式があるか。サイドカーベースの Service Mesh (Istio・Linkerd) の mTLS、CNI 内蔵の透過的暗号化 (Cilium WireGuard・IPsec)、そして Cilium の mTLS・SPIFFE の方向。
  • trade-off は何か。メッシュは強力ですがサイドカーのオーバーヘッドと運用負担があり、透過的暗号化は軽いですがそれ自体ではワークロードの身元検証を提供しません。

試験ポイント #

  • Pod 間のデフォルト通信は 平文 です。同じクラスターだから暗号化されるわけではありません。
  • 転送中の暗号化 (encryption in transit) の代表的な実装が mTLS です。mTLS は暗号化と 相互の 身元検証を一緒に提供します。
  • 一方向 TLS と mTLS の違いを区別します。mTLS はクライアントも証明書を提示します。
  • NetworkPolicy (L3・L4) と mTLS (暗号化・身元) は別の層 です。NetworkPolicy は通信を許可・遮断するだけで内容を暗号化しません。2 つは一緒に使います。
  • Service Mesh (Istio・Linkerd) は サイドカープロキシ でアプリケーションの変更なしに mTLS を透過的に処理し、証明書を自動で発行・更新します。代償はオーバーヘッドと運用負担です。
  • Cilium は WireGuard・IPsec 透過的暗号化 でノード間の Pod トラフィックをサイドカーなしに暗号化します。これはノード間暗号化であり、ワークロードの身元検証は SPIFFE ベースの mTLS の方向で別途です。
  • Istio の PeerAuthenticationSTRICT は mTLS のみを許可し PERMISSIVE は平文・mTLS を一緒に許可する、という違いを覚えます。

まとめ #

この記事で押さえたこと:

  • Pod 間通信はデフォルトで平文。ノード間の盗聴と横方向の移動にさらされるので、転送中の暗号化が必要です。
  • mTLS は暗号化と相互のワークロードの身元検証を一緒に提供します。一方向 TLS と区別します。
  • NetworkPolicy と mTLS は補完物。一方は接続を制御し、一方は制御された接続を暗号化して身元を掛けます。
  • 2 つのアプローチ。サイドカーベースの Service Mesh (Istio・Linkerd) と CNI 内蔵の透過的暗号化 (Cilium WireGuard・IPsec)、そして Cilium の mTLS・SPIFFE の方向。
  • CKS は詳細な構成より 概念と trade-off を問います。なぜ暗号化するのか、何が違うのか、どんなコストがあるのかを説明できる必要があります。

これで Minimize Microservice Vulnerabilities ドメイン (#9〜#12) を締めくくります。PSA で危険な Pod を防ぎ、Secrets を暗号化し、ワークロードを隔離し、通信を暗号化する 4 つの分岐で、マイクロサービスの攻撃面を減らしました。

次: Minimal images #

ワークロードを取り巻く制御はここまでです。次のドメイン Supply Chain Security は コンテナイメージ自体を安全にする 仕事へ進みます。

#13 Minimal images: distroless、scratch (Supply Chain) では、イメージに入るパッケージを最小に減らして攻撃面そのものを狭める方法、distroless と scratch のベースイメージの違い、シェルとパッケージマネージャーを除いたイメージがなぜより安全なのか、そしてマルチステージビルドで最終イメージを軽くするパターンまで、直接作ってみながら整理します。

X