コンテナは「魔法の箱」じゃない:Pod Security Admissionで守るKubernetesの境界線
「コンテナの中にSSHして、rootで好き勝手やる」。これは開発初期のデバッグでは甘美な誘惑ですが、本番環境では「自ら開けたバックドア」に他なりません。
多くのエンジニアが「Kubernetesを使っているから安心」と勘違いしていますが、デフォルト設定のPodは驚くほど無防備です。今日は、Kubernetesの標準機能であるPod Security Admission (PSA)を使って、攻撃者に足場を渡さないための「鉄壁の防御」について、現場の知見を交えて解説します。
—
1. なぜ「Privilegedコンテナ」が最悪の悪夢なのか
攻撃者がWebアプリケーションの脆弱性(例:RCE)を突いてコンテナ内に侵入した際、もしそのコンテナが privileged: true で動いていたり、runAsRoot が許可されていたらどうなるか。
彼らはコンテナの境界を軽々と踏み越え、ホストOSのカーネルにアクセスします。具体的には、ホスト側の /var/run/docker.sock をマウントしたり、ホストのファイルシステムを直接操作して、ノード全体を掌握(Node Compromise)します。
PoCの視点:
攻撃者はまず whoami でroot権限を確認し、次に mount コマンドでホストのルートディレクトリが見えないかを探ります。もし見えたら、そこからホスト上の設定ファイルを改ざんし、他のPodへと横展開(Lateral Movement)していく。これが侵害の王道パターンです。
—
2. Pod Security Admission (PSA) で強制する「境界線」
Pod Security Standards (PSS) には3つのレベルがありますが、本番環境では迷わず restricted を適用すべきです。
- Privileged: 制限なし(論外)
- Baseline: 一般的な攻撃を防ぐ最低限のルール
- Restricted: 攻撃の余地を極限まで削る、プロダクション品質のポリシー
実装:Namespaceへの適用
特定のNamespaceに対し、restricted プロファイルを強制する設定です。kubectl で設定する際は、ラベルを付与するだけで即座に効果を発揮します。
対象のNamespaceにラベルを付与
warn: 違反時に警告のみ出す
enforce: 違反時にPodの作成を拒否する
kubectl label namespace my-app-prod \
pod-security.kubernetes.io/enforce=restricted \
pod-security.kubernetes.io/enforce-version=latest
—
3. 実務でハマる「動かない」を回避するPod定義
restricted を適用すると、今までのデプロイが弾かれるはずです。これはセキュリティが正しく機能している証拠ですが、サービスを止めないためにはPod定義を「セキュアな書き方」に修正する必要があります。
以下は、restricted ポリシーに適合する、セキュアなPod定義のテンプレートです。
apiVersion: v1
kind: Pod
metadata:
name: secure-app
spec:
# セキュリティコンテキストの適用
securityContext:
runAsNonRoot: true # 非rootユーザーでの実行を強制
runAsUser: 1000 # 任意のuidを指定
runAsGroup: 3000
fsGroup: 2000 # ボリュームマウント時の権限管理
seccompProfile:
type: RuntimeDefault # カーネルの呼び出しを制限
containers:
- name: app
image: my-secure-app:v1.0
securityContext:
allowPrivilegeEscalation: false # 特権昇格を禁止
capabilities:
drop: [“ALL”] # 全てのLinuxケーパビリティを剥奪
readOnlyRootFilesystem: true # ルートファイルシステムを読み取り専用に
volumeMounts:
- name: tmp-volume
mountPath: /tmp # 書き込みが必要な場所だけメモリ上にマウント
volumes:
- name: tmp-volume
emptyDir: {}
この設定のポイント
allowPrivilegeEscalation: false: アプリがsetuidビットを利用して権限を上げることを防ぎます。drop: ["ALL"]: 不要なシステムコールを遮断します。これだけで、多くのエクスプロイトコードは「動かないゴミ」と化します。readOnlyRootFilesystem: true: 攻撃者がバックドアを配置したり、設定ファイルを書き換えることを物理的に不可能にします。
—
4. 現場の教訓:完璧を目指して運用を止めるな
セキュリティは「導入して終わり」ではありません。PSAを導入する際は、いきなり enforce をかけるのではなく、まずは audit モードで運用し、どのPodが違反しているかをログ(kube-apiserver の監査ログ)で確認してください。
チーフエンジニアからのアドバイス:
「開発者の利便性」と「セキュリティ」は天秤にかけるものではありません。restricted な環境でも快適に開発できるよう、emptyDir の活用や、適切な SecurityContext の共通ライブラリ化(Helmチャートへの組み込みなど)を先回りして行うのが、頼れるエンジニアの仕事です。
「脆弱性が見つかってから直す」のは、すでに敗北しています。Kubernetesという強固な城壁の中で、さらに部屋の鍵をすべて閉めておく。その泥臭い積み重ねこそが、あなたのサービスを、そしてあなたのエンジニアとしての信頼を守り抜く鍵になるはずです。
さあ、今すぐ kubectl label で、最初の一歩を踏み出してください。
コメント