Kubernetesの「パンドラの箱」:/var/run/docker.sock マウントによるコンテナ脱出の真実
現場でKubernetesクラスタを運用していると、「とりあえずデバッグ用にDockerソケットをマウントしておこう」という誘惑に駆られる瞬間があるはずだ。しかし、断言しよう。それは「コンテナの中で、自分の首に巻く縄を編んでいる」のと同じだ。
今日は、攻撃者がこの「盲点」をどう突き、いかにしてクラスタ全体を掌握するのか。そして、それを防ぐために明日から何をすべきかを、現場の視点で叩き込む。
—
1. 攻撃のメカニズム:なぜ「ソケット」が最強の武器になるのか
KubernetesのPodは、本来ホストOSから隔離されている。しかし、/var/run/docker.sock をホストからマウントした瞬間、その隔離壁は砂上の楼閣と化す。
攻撃者は、Pod内のアプリケーションに存在する脆弱性(例えば、不適切な入力を受け付けるAPIエンドポイントや、RCEが可能なWebシェル)を利用して、コンテナ内でコマンドを実行する。その際、マウントされたDockerソケットを操作することで、ホスト側のDockerエンジンに対して「命令」を送れるようになるのだ。
PoC:コンテナ脱出の「悪夢」
攻撃者はPod内で以下のように振る舞う。
# 1. ホスト上のイメージ一覧を確認(攻撃の足掛かり)
docker ps
# 2. ホストのルートディレクトリをマウントした特権コンテナを起動し、即座にホストのシェルを奪取する
docker run -it --privileged -v /:/host alpine chroot /host
このコマンドを実行した瞬間、攻撃者はコンテナの制約を完全に脱出し、ホストOSの root 権限を手に入れる。あとはKubeletの認証情報を盗み出し、クラスタ全体を横展開(ラテラルムーブメント)するだけだ。これが、我々が「Dockerソケットのマウント」を厳禁とする理由である。
—
2. なぜ「設定」だけでは不十分なのか
多くのエンジニアが「PodSecurityPolicy (PSP) を入れているから大丈夫」と安心している。だが、PSPは既に非推奨であり、現在は Pod Security Admission (PSA) が主流だ。
重要なのは、ツールを入れることではなく、「開発者が誤ってマウント設定を記述できない環境を構築すること」にある。人間はミスをする。設定ファイルは、そのミスをシステムレベルで弾く必要がある。
—
3. 完全防御:セキュアな実装とポリシー設定
防御の要は「最小特権の原則」の強制だ。以下の設定をインフラ構成に組み込んでほしい。
(1) Kubernetes Pod Security Admission (PSA) の設定
Namespaceに対して restricted プロファイルを適用し、ホストパスのマウントを物理的に拒否する。
# namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: production-app
labels:
# 制限レベルを最大に設定。これにより、docker.sockのマウントは拒否される
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/enforce-version: latest
(2) OPA Gatekeeper を使ったガードレール
PSAだけでは不安な場合、Open Policy Agent (OPA) の Gatekeeper を用いて、より細かい制御を行う。これは「Dockerソケットをマウントしようとしたら、即座にデプロイを却下する」という強力なバリデータだ。
# constraint.rego
package k8sallowedhostpaths
violation[{"msg": msg}] {
input.review.object.spec.volumes[_].hostPath.path == "/var/run/docker.sock"
msg := "セキュリティ違反: /var/run/docker.sock のマウントは禁止されています。"
}
—
4. 現場のエンジニアへ:明日からのチェックリスト
どんなに強固なセキュリティ設定も、運用で穴が開いては意味がない。以下の3点をチームの「絶対ルール」にしてほしい。
1. Docker-in-Docker (DinD) は避ける: どうしても必要な場合は、kaniko や img のような「デーモンレス」のビルドツールへ移行せよ。これらは特権を必要としない。
2. Read-Only Root Filesystem を徹底: Podのセキュリティコンテキストで readOnlyRootFilesystem: true を設定せよ。これにより、攻撃者がランタイムでツールをダウンロードすることを防げる。
3. CI/CDパイプラインでのスキャン: kube-linter や checkov を導入し、マウント設定が含まれるマニフェストをコミット段階で弾くこと。
# セキュアなPod定義の例
securityContext:
runAsNonRoot: true
runAsUser: 1000
readOnlyRootFilesystem: true # 書き込みを禁止し、攻撃の定着を防ぐ
allowPrivilegeEscalation: false # 特権昇格を物理的に防ぐ
最後に:セキュリティは「性悪説」で設計せよ
セキュリティ対策において「信頼」は敵だ。自分たちの書いたコードも、同僚が作ったマニフェストも、「いつか悪用される可能性がある」という前提で設計する。その冷徹なまでの疑念こそが、大規模なインシデントから組織を守る唯一の盾となる。
さあ、今すぐクラスタ内の deployment.yaml を grep して、/var/run/docker.sock が残っていないか確認してほしい。それが今日の仕事だ。
コメント