Kubernetesの「特権コンテナ」という時限爆弾:Pod Security Admissionで封じ込める現実解
現場でKubernetesを運用していると、開発チームから「このライブラリを動かすために特権モード(privileged: true)が必要です」というリクエストが飛んでくることがある。
正直に言おう。そのリクエストを承認した瞬間、あなたのクラスタのセキュリティは崩壊したも同然だ。
なぜか? 特権コンテナは、ホストOSのカーネル機能に直接アクセスできる。つまり、コンテナの脱獄(Breakout)は容易であり、ホスト上の全Podへのアクセス、さらにはノードそのものの乗っ取りまで許してしまうからだ。今日は、この「緩い設定」を物理的に許さないための、Pod Security Admission(PSA)による強制適用について、実務的な防衛術を解説する。
—
なぜ「特権」がそこまで危険なのか(攻撃の視点)
攻撃者は、アプリケーションの脆弱性(例えば、未パッチのWebアプリのRCE)を突いてコンテナ内に侵入する。もしそのコンテナが特権モードで動いていれば、攻撃者は以下のことができてしまう。
1. ホストファイルシステムへのアクセス: /host のようなマウントポイントを探索し、SSHの秘密鍵や、他のPodがマウントしている機密情報(Secret)を盗み出す。
2. カーネルモジュールのロード: 悪意のあるコードをホストのカーネルに注入し、セキュリティツール(falcoなど)を無効化する。
3. 特権昇格: ホストのプロセスID空間(PID Namespace)を共有することで、ホスト上の他のプロセスを操作する。
これは単なる「設定の不備」ではなく、「攻撃者にクラスタの鍵を渡している」のと同じことなのだ。
—
Pod Security Admission (PSA) で強制的に縛り上げる
Kubernetes 1.25以降、Pod Security Policies(PSP)が削除され、標準機能である Pod Security Admission が正義となった。これを使えば、ネームスペース単位で「この中では特権コンテナは禁止!」と強制できる。
まずは、特定のネームスペース(ここでは production)に対して、「特権禁止」を強制するラベルを付与する設定だ。
# productionネームスペースに対して、Pod Security Admissionのポリシーを強制適用する
kubectl label namespace production \
pod-security.kubernetes.io/enforce=restricted \
pod-security.kubernetes.io/enforce-version=latest \
--overwrite
これにより、privileged: true を含むPodを作成しようとすると、APIサーバーが即座に拒否(Reject)するようになる。
—
実践:セキュアなPod設計のテンプレート
では、開発者がどう記述すべきか。以下は、特権を一切使わず、かつ最小権限(Least Privilege)で動かすためのベストプラクティスな deployment.yaml だ。
apiVersion: apps/v1
kind: Deployment
metadata:
name: secure-app
spec:
template:
spec:
securityContext:
# コンテナ全体で非rootユーザー実行を強制
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 3000
containers:
- name: app
image: my-secure-app:latest
securityContext:
# ここが最重要:特権昇格を一切許可しない
allowPrivilegeEscalation: false
# Linuxの機能を制限(不要なシステムコールを遮断)
capabilities:
drop:
- ALL
# 書き込み可能なルートファイルシステムを禁止(Read-Onlyにする)
readOnlyRootFilesystem: true
volumeMounts:
# 書き込みが必要な場所だけ一時ディレクトリをマウント
- mountPath: /tmp
name: tmp-volume
volumes:
- name: tmp-volume
emptyDir: {}
解説:この設定の狙い
allowPrivilegeEscalation: false: 子プロセスが親プロセスよりも高い権限を得ることを防ぐ。capabilities: drop: ["ALL"]: Linuxカーネルの機能をすべて剥奪する。ほとんどのアプリはこれで動く。readOnlyRootFilesystem: true: 万が一アプリが乗っ取られても、攻撃者がバックドアをファイルシステムに書き込むことを防ぐ。
—
運用現場での「泥臭い」インシデントハンドリング
「ポリシーを導入したらアプリが動かなくなった!」という叫びは、現場では日常茶飯事だ。その際、安易に privileged を戻してはいけない。
1. Auditログの確認: kubectl logs ではなく、APIサーバーのAuditログを見て、どの権限が拒否されたのか(例:CAP_NET_BIND_SERVICE が足りない等)を特定する。
2. ポリシーの段階的適用: いきなり enforce するのではなく、まずは warn モードで運用し、どのPodが違反しているかを洗い出すことから始める。
# 違反Podを検知するだけのモード(いきなり落とさない)
kubectl label namespace production \
pod-security.kubernetes.io/warn=restricted
最後に:セキュリティは「諦め」から始まる
我々エンジニアが追求すべきなのは、「何でも動く最強のプラットフォーム」ではなく、「攻撃者が侵入した瞬間に、その場から一歩も動けなくなるプラットフォーム」だ。
特権コンテナの排除は、そのための最も強力な第一歩となる。便利さと引き換えにセキュリティを捨てるような設計は、今日で卒業しよう。もし開発チームから文句を言われたら、この記事を見せて「私の署名で許可は出せない。セキュアな実装を考えよう」と伝えてやってほしい。
それが、プロのエンジニアとしての仕事だ。
コメント