Kubernetesの「境界」を再定義する:mTLSとDefault Denyが防ぐのは「侵入」ではなく「横展開」だ
Kubernetesのネットワークモデルは、設計思想として「フラットなPodネットワーク」を採用している。しかし、実戦的なペネトレーションテストの現場において、このフラットさは攻撃者にとって「天国」に他ならない。一度最初のPod(Entry point)を掌握すれば、クラスター内は制限のないパケットが飛び交う広大な平原へと変貌するからだ。
今回は、サービスメッシュ(Istio)を用いたmTLSによる暗号化と、NetworkPolicyによるゼロトラスト構築を、単なる「設定ガイド」としてではなく、攻撃者の視点からその「崩し方」と「守り方」を深掘りする。
—
1. ゼロトラストの本質は「パケットの非対称性」にある
多くのエンジニアが誤解しているが、mTLSの真の目的は暗号化そのものではない。暗号化は副産物に過ぎない。真の目的は、「証明書によるアイデンティティの強制と、認可による通信経路の非対称化」だ。
攻撃者がPodのシェルを取ったとき、まず行うのは arp -a や nmap による隣接ノードの偵察、そしてEnvoyプロキシを介さない直接通信の試行である。これに対し、IstioでmTLSを STRICT モードに設定することで、証明書を持たない攻撃者のパケットは、TCPハンドシェイクの段階で拒絶されるか、セッション確立後のTLSハンドシェイクで断絶する。
Istio PeerAuthenticationによる強制適用
以下は、名前空間全体でmTLSを強制する設定だ。
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: default
namespace: secure-app
spec:
mtls:
# STRICTは、暗号化されていない通信を一切受け付けない
# これにより、サイドカーを介さない不正な横展開を遮断する
mode: STRICT
2. Default Denyの「盲点」を突く:階層化された防御
NetworkPolicyを適用する際、多くの現場で「取りこぼし」が発生する。特に多いのは、名前空間を跨いだ通信や、DNS(CoreDNS)へのクエリに対する制限の甘さだ。
攻撃者は、NetworkPolicyが「L3/L4レベルのパケットフィルタリング」であることを熟知している。したがって、彼らはアプリケーション層の脆弱性(SSRF等)を悪用し、許可されたバックエンドサービスを「踏み台」として内部探索を継続する。
堅牢なDefault Denyの構成例
単に deny-all を置くだけでは不十分だ。必要な通信のみを明示的に許可する「ホワイトリスト・アプローチ」が鉄則となる。
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
name: default-deny-all
spec:
podSelector: {} # 選択対象:すべて
policyTypes:
- Ingress
- Egress
# デフォルトで全トラフィックを拒否
このポリシーを適用した上で、必要な通信だけを以下のように定義する。これはDNSクエリを許可しつつ、特定のAPIサーバーのみに通信を制限する例だ。
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
name: allow-dns-and-specific-backend
spec:
podSelector:
matchLabels:
app: frontend
egress:
# DNSクエリを許可(UDP/53)
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: UDP
port: 53
# 特定のバックエンドPodのみへの通信を許可
- to:
- podSelector:
matchLabels:
app: backend
3. 次世代の脅威:耐量子暗号とガードレイル
昨今のセキュリティアーキテクチャにおいて避けて通れないのが、「量子計算機による暗号解読(Harvest Now, Decrypt Later)」への備えだ。IstioのEnvoyプロキシは、将来的に耐量子暗号(PQC: Post-Quantum Cryptography)アルゴリズム(Kyber等)をTLS 1.3の鍵交換に統合する方向へ進んでいる。
また、生成AIを利用したプロンプトインジェクションは、Kubernetesのネットワークレイヤーを超えてくる脅威である。これを防ぐには、サービスメッシュのEnvoyフィルタに「WAF」的な役割を持たせ、リクエストボディ内の異常なプロンプトを遮断する「インスペクション・ガードレイル」の導入が不可欠だ。
EnvoyFilterによる動的インスペクションの考え方
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: prompt-injection-guard
spec:
configPatches:
- applyTo: HTTP_FILTER
match:
context: SIDECAR_INBOUND
listener:
filterChain:
filter:
name: "envoy.filters.network.http_connection_manager"
patch:
operation: INSERT_BEFORE
value:
name: envoy.filters.http.lua
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua
inline_code: |
function envoy_on_request(request_handle)
-- ここでリクエストボディを解析し、危険な文字列パターンを検知
local body = request_handle:body():getBytes(0, 1000)
if string.find(body, "system_prompt_override") then
request_handle:respond({[":status"] = "403"}, "Forbidden: Injection Attempt")
end
end
終わりに:ペネトレーションテストの視点から
我々攻撃者は、完璧な防御など存在しないことを知っている。しかし、「攻撃コストを指数関数的に引き上げること」は可能だ。
mTLSとNetworkPolicyを組み合わせ、さらにEnvoyレベルでの深いトラフィックインスペクションを導入することは、侵入後の横展開を劇的に困難にする。セキュリティアーキテクトに求められるのは、パッチを当てることではなく、クラスター内を「攻撃者が迷路に迷い込み、一歩踏み出すたびに足跡が残る空間」に変えることだ。
技術は常に進化する。だが、敵が「経路」を探しているという事実は、これからも変わらない。その経路をいかに複雑で、かつ監視可能なものに作り変えるか。それが、このKubernetesという広大な戦場で生き残る唯一の術である。
コメント