特権コンテナという「パンドラの箱」:Kubernetesハーデニングの急所
現場のインフラエンジニアやクラウドアーキテクトと話していると、「動けば正義」という魔物が巣食ったマニフェストにしばしば遭遇する。privileged: true。このたった一行の記述が、強固なはずのKubernetesクラスタをいとも簡単に紙細工へと変える。
コンテナ技術の本質は、Linuxカーネルの機能(cgroups、namespaces、capabilities)を利用した「プロセスの隔離」に過ぎない。仮想マシンとは異なり、コンテナはホストOSのカーネルを共有している。ここで privileged なコンテナを稼働させるということは、ホストOSの全権をそのコンテナ内のプロセスに明け渡すと同義なのだ。
攻撃者がWebアプリケーションの脆弱性(RCEなど)を突き、特権コンテナ内でコード実行の足がかりを得た瞬間、彼らはもはや「ゲスト」ではない。ホストの /dev や /proc への自由なアクセスを手に入れ、カーネルモジュールのロード、ホストのネットワーク名前空間の乗っ取り、そしてクラスタ全体へのピボット(横展開)を完了させる。
この悪夢を防ぐための現代的な防衛線が、Kubernetes Pod Security Admission (PSA) である。かつての PodSecurityPolicy (PSP) が複雑怪奇な設定と暗黙的な挙動の末に廃止された教訓を活かし、公式に組み込まれたこのAdmission Controllerは、クラスタの安全性を担保する最後の砦となる。
—
Pod Security Standards (PSS) の3つの階層と現実的な設計
Pod Security Admissionを導入するにあたり、私たちはGoogleやRed Hatが定めた3つの標準プロファイル(PSS)を理解し、ワークロードの性質に応じて厳格に使い分ける必要がある。
1. Privileged: 制限なし。CSIドライバやネットワークプラグインなど、ホストに直接触れるインフラコンポーネント以外での使用は完全に御法度である。
2. Baseline: 一般的なアプリケーション向けの標準的な制限。特権エスカレーションを防ぎつつ、多くの既存アプリが動くように設計されている。
3. Restricted: 最もセキュアなプロファイル。rootユーザーでの実行禁止、読み取り専用ルートファイルシステムの強制、不必要なLinux Capabilitiesの剥奪などを行う。
実務において、既存のレガシーシステムをいきなり Restricted で縛り上げるのは、運用チームとの全面戦争を引き起こすだけだ。私たちは、まずは Baseline をベースライン(文字通り)とし、段階的に締め付けるアプローチを取る。
—
実装ハンズオン:Namespace単位でのPSA強制と監査モード
Pod Security Admissionは、APIサーバーのプラグインとして動作し、NamespaceのアノテーションやラベルをトリガーにしてPodの作成を拒否、あるいは警告(Audit/Warn)する。
以下の設定例では、特定のNamespaceに対して Baseline プロファイルを強制しつつ、違反した場合にはログに記録しつつも拒否はしない audit モードと、将来的には完全にブロックする enforce モードを組み合わせる方法を示す。
1. 名前空間のマニフェスト定義
apiVersion: v1
kind: Namespace
metadata:
name: secure-workloads
labels:
# Pod Security Admissionの適用モードを設定
# enforce: ポリシーに違反したPodの作成を拒否する
pod-security.kubernetes.io/enforce: "baseline"
# 違反を監査ログに記録する(ドライランのような挙動)
pod-security.kubernetes.io/audit: "restricted"
# ユーザーがkubectl等で操作した際に警告メッセージを表示する
pod-security.kubernetes.io/warn: "restricted"
# 例外的に古いバージョンを許容する必要がある場合のバージョン指定(latestは避けるべき)
pod-security.kubernetes.io/enforce-version: "v1.28"
このラベルを付与したNamespaceに対して、開発者がうっかり特権コンテナを含んだマニフェストを適用しようとした瞬間、APIサーバーは以下のようなエラーを返し、デプロイを即座に阻止する。
Warning: arestricted/pod-security.kubernetes.io/warn:
allowPrivilegeEscalation != false, securityContext.privileged != true,
securityContext.allowPrivilegeEscalation != false (container "app" must set securityContext.allowPrivilegeEscalation=false)
—
攻撃者が狙う盲点:PSAをすり抜ける設定ミス
PSAを導入したからといって、ホワイトハッカーの視点から見れば、まだ油断できないポイントが無数に残されている。特にインフラ担当者が陥りがちな「安全の錯覚」をいくつか挙げておこう。
盲点1: ボリュームマウントの罠 (hostPath)
Baseline プロファイルは一見安全に見えるが、特定の hostPath ボリュームのマウントを完全に禁止しているわけではない(一部の安全なパスを除き、制限はあるものの、設定次第で危うい構成が組めてしまう)。
攻撃者は、ホスト上のDockerソケット (/var/run/docker.sock) や、Kubernetesのシークレットが保存されているディレクトリを hostPath 経由でコンテナ内にマウントできないか常にスキャンしている。PSAのポリシーが Baseline であっても、不必要なボリュームマウントが許可されていないか、常にオーディットを行うべきだ。
盲点2: Cluster-Admin権限との複合リスク
PSAは「Podの仕様」を制限するものであり、「誰がそのPodを作ったか(RBAC)」を直接制限するものではない。
もし攻撃者が、特定のNamespaceに対して ClusterRole または強力な Role を持つサービスアカウント(ServiceAccount)のトークンを奪取した場合、PSAの網を潜り抜けるようなマニフェスト(例えば、PSAの対象外となっているシステム名前空間や、ラベル付けを忘れた名前空間へのデプロイ)を悪用する可能性がある。
—
現場で即座に使える!不審な特権ポッドの検出スクリプト
現在稼働中のクラスタ内に、すでに特権コンテナや危険な設定を持つポッドが潜んでいないかを網羅的にスキャンするためのPythonスクリプト(Kubernetes Client Library使用)を共有する。インテリジェンス収集や日々のセキュリティ監査に役立ててほしい。
from kubernetes import client, config
import sys
def audit_privileged_pods():
# kubeconfigから設定を読み込む(クラスタ内から実行する場合は config.load_incluster_config() を使用)
try:
config.load_kube_config()
except Exception as e:
print(f"[-] Kubernetes設定の読み込みに失敗しました: {e}", file=sys.stderr)
sys.exit(1)
v1 = client.CoreV1Api()
print("[*] クラスタ内の全Namespaceにおけるポッドのセキュリティ監査を開始します...\n")
try:
ret = v1.list_pod_for_all_namespaces(watch=False)
except Exception as e:
print(f"[-] ポッドのリスト取得に失敗しました: {e}", file=sys.stderr)
sys.exit(1)
violation_count = 0
for pod in ret.items:
namespace = pod.metadata.namespace
pod_name = pod.metadata.name
# コンテナスペックの確認(initContainersも含む)
containers = pod.spec.containers + (pod.spec.init_containers or [])
for container in containers:
security_context = container.security_context
if security_context:
# 特権コンテナのチェック
is_privileged = security_context.privileged
# 特権エスカレーション許可のチェック
allow_escalation = security_context.allow_privilege_escalation
if is_privileged:
violation_count += 1
print(f"[!] 【危険】特権コンテナ検出!")
print(f" Namespace: {namespace} | Pod: {pod_name} | Container: {container.name}")
print(f" 理由: securityContext.privileged が True に設定されています。\n")
elif allow_escalation:
# Baseline/Restrictedの観点から推奨されない設定
print(f"[-] 【警告】特権エスカレーションが許可されています")
print(f" Namespace: {namespace} | Pod: {pod_name} | Container: {container.name}")
print(f" 理由: allowPrivilegeEscalation が True です。\n")
# hostPathマウントのチェック
if pod.spec.volumes:
for volume in pod.spec.volumes:
if volume.host_path:
print(f"[-] 【情報】hostPathボリュームが使用されています")
print(f" Namespace: {namespace} | Pod: {pod_name} | Volume: {volume.name} (Path: {volume.host_path.path})")
print(f"\n[*] 監査完了。検出された重大な特権コンテナ数: {violation_count}")
if __name__ == "__main__":
audit_privileged_pods()
—
最高峰の防衛者としての結論
Kubernetesのセキュリティは、単一のツールや設定で完結するものではない。Pod Security Admissionは強力な「予防線」であるが、それはあくまでレイヤー防御の一つに過ぎない。
真にセキュアなインフラストラクチャを構築するためには、PSAによるポリシー強制に加え、Falcoなどのランタイムセキュリティツールによる挙動監視、eBPFを活用したシステムコールレベルの異常検知、そして最小権限の原則に基づいたRBAC設計を組み合わせる必要がある。
「便利さ」と「安全性」のトレードオフにおいて、セキュリティアーキテクトが妥協してはならない。コンテナの特権化という誘惑を断ち切り、堅牢なクラスター環境を構築することこそが、現代のインフラエンジニアに課された最も重要な責務である。
コメント