コンテナの「PID名前空間」という戦場:カーネルレベルで仕掛ける防御の極意
「コンテナは隔離されている」という言葉を、君はどの程度信じているだろうか。
多くのエンジニアが Docker や Kubernetes の抽象化されたレイヤーに満足し、その土台である Linux カーネルが提供する「名前空間(Namespaces)」の脆弱性を軽視している。特に PID(Process ID)名前空間の分離は、単なる「プロセスの見え方」の問題ではない。これは、攻撃者がコンテナからホストへ、あるいはコンテナからコンテナへと横移動(Lateral Movement)を試みる際の、最後の防衛線の一つだ。
本稿では、PID 名前空間の深淵に潜むリスクと、それを物理的・論理的に封じ込めるアーキテクチャについて、現場の泥臭い知見を交えて解説する。
—
1. なぜ「PID名前空間の分離」が最優先課題なのか
デフォルトのコンテナ設定では、ホストとコンテナの PID 名前空間を共有するケースが稀にある(--pid=host)。これは運用上の利便性――例えば、ホスト側の監視エージェントからコンテナ内のプロセスを直接シグナル送信したいといったニーズ――のために許容されることがあるが、セキュリティの観点からは自殺行為だ。
もしコンテナ内で ptrace を悪用したり、/proc ファイルシステムを介してホストのプロセス情報にアクセスできれば、攻撃者はカーネルの脆弱性を突くための「足場」を瞬時に特定する。
攻撃者の視点:プロセス隠蔽のトリック
攻撃者は自身のプロセスを kill するのではなく、LD_PRELOAD を使ったライブラリインジェクションや、プロセスリストのパースを回避するカーネルモジュール(Rootkit)を駆使する。PID 名前空間が分離されていない場合、ホスト側からは「見えているはずのプロセス」が、コンテナの特権設定によって隠蔽されるという、監視の死角(Blind Spot)が生まれる。
—
2. 実装:PID名前空間の堅牢化と隔離戦略
コンテナランタイムレベルで PID 名前空間を確実に分離する設定は、もはや必須要件だ。Kubernetes であれば、PodSecurityContext を用いて、ホストの名前空間へのアクセスを明確に拒否する。
Kubernetes マニフェストにおける堅牢化の例
apiVersion: v1
kind: Pod
metadata:
name: hardened-container
spec:
securityContext:
# ホストのPID名前空間へのアクセスを禁止(デフォルトだが明示的に定義)
shareProcessNamespace: false
containers:
- name: app-container
image: my-secure-app:latest
securityContext:
# プロセス間通信を制限し、特権昇格を防止
allowPrivilegeEscalation: false
runAsNonRoot: true
capabilities:
# 不要な機能を徹底的に剥奪
drop:
- "ALL"
この設定により、コンテナ内のプロセスは PID 1 として起動し、コンテナ外のプロセスツリーを覗き見ることは物理的に不可能になる。
—
3. 監査と監視:プロセスの「沈黙」を検知する
PID 名前空間を分離しただけでは不十分だ。我々が構築すべきは、プロセス挙動の「ホワイトリスト化」である。特にコンテナ内での不審な execve システムコールは、攻撃のシグナルだ。
Falco を用いたプロセス監視ルールの設計
シグネチャベースの検知ではなく、システムコールレベルのイベントを監視する。例えば、PID 名前空間内で実行された「予期せぬシェル起動」を検知するルールは以下の通りだ。
# Falcoルール例: コンテナ内で予期せぬシェルが起動されたら即座にアラート
- rule: Unexpected shell in container
desc: A shell was spawned inside a container
condition: >
spawned_process and container and
proc.name in (bash, sh, zsh, ksh) and
not proc.cmdline contains "sidecar-init"
output: "不審なシェル起動を検知 (user=%user.name container=%container.id process=%proc.cmdline)"
priority: WARNING
—
4. チーフホワイトハッカーからの提言:次の脅威へ
PID 名前空間の分離は「入口」に過ぎない。我々が直面している次の戦場は、「メモリ保護」と「コンテナランタイムの耐量子性」だ。
1. メモリフォレンジックの自動化: コンテナが侵害された際、揮発性メモリ内のシェルコードや難読化されたペイロードを抽出する自動フォレンジックパイプラインを構築しておくこと。gcore や criu を用いたスナップショットの取得は、インシデントハンドリングの質を劇的に変える。
2. ガードレイルの適用: LLM を用いたアプリケーションをコンテナで動かす場合、そのプロンプト注入を防止するガードレイル(NeMo Guardrails 等)をコンテナサイドカーとして配置せよ。コンテナの隔離と、アプリケーションの入力検証は、多層防御の要である。
最後に
セキュリティとは「ツールを入れること」ではなく、「システムの挙動を完全に制御下(Control Plane)に置くこと」である。
PID 名前空間を分離し、システムコールを監視し、カーネルの脆弱性を常に意識する。この泥臭い積み重ねこそが、洗練されたアーキテクトが唯一信頼できる「防壁」となる。君たちのインフラが、攻撃者にとって最も「割に合わない」ターゲットになることを期待している。
コメント