コンテナの「脱獄」を許すな:AppArmor/SELinuxで実現する強制アクセス制御(MAC)の実践
「コンテナは安全だ」という神話はもう捨ててくれ。DockerやKubernetesを使っていればセキュリティは万全だと思っているなら、それは大きな勘違いだ。
現場でインシデントレスポンスを担当していると、よく見る光景がある。Webアプリケーションの脆弱性(例えば、最近流行りのOSコマンドインジェクションやファイル読み込み脆弱性)を突かれた際、攻撃者がコンテナ内でやりたい放題暴れているログだ。デフォルト設定のコンテナは、ホストOSから見れば「ただのプロセス」に過ぎない。一度侵入を許せば、攻撃者は/etc/shadowを覗き見たり、ホストのソケットを叩いてコンテナを破壊したりする。
これを防ぐ最後の砦が、強制アクセス制御(MAC)、すなわちAppArmorやSELinuxだ。今回は、現場で即戦力となる「コンテナを檻に閉じ込める」ための技術を解説する。
—
1. なぜ「パーミッション」だけでは不十分なのか
Linuxの標準的なDAC(任意アクセス制御)は、ユーザーやグループの権限で管理される。しかし、一度Webサーバーのプロセス(www-dataなど)が乗っ取られると、そのプロセスが持っている権限でファイルシステム全体が攻撃者の遊び場になる。
そこで登場するのがMACだ。これは「プロセスが何をできるか」をポリシーファイルで厳格に定義する仕組みである。たとえアプリケーションに致命的な脆弱性があり、root権限でコードが実行されたとしても、ポリシーで禁止されていれば攻撃者は一歩も動けない。これが「要塞化」の真髄だ。
—
2. AppArmorによるプロファイル作成の実践
今回は、Pythonで書かれたWebアプリケーションを実行するコンテナを例に、AppArmorで制限をかける手順を示す。
AppArmorプロファイルの定義例
まずは、コンテナ内のプロセスが「読み込み専用」かつ「特定のディレクトリ以外アクセス不可」にするためのプロファイルを作成する。/etc/apparmor.d/containers/web-app に以下のように記述する。
# プロファイル名
profile docker-web-app flags=(attach_disconnected, complain) {
# 基本的なライブラリへのアクセスは許可
/usr/bin/python3.10 mr,
/usr/lib/** mr,
# アプリケーションのコードベースのみ読み込み許可
/app/src/** r,
# 一時ファイルのみ書き込み許可(ログやセッション用)
/tmp/web-app/ w,
# ネットワークアクセスは制限なし(必要に応じて制限可能)
network inet stream,
network inet6 stream,
# それ以外は全て拒否(暗黙のDeny)
deny /** w,
}
このポリシーを適用することで、もし攻撃者が os.system('/bin/bash') を実行しようとしても、AppArmorがカーネルレベルでその実行を即座にブロックし、システムログに「Permission Denied」を叩き出す。
—
3. 実務でハマるポイントと解決策
よくある失敗は、厳格にしすぎてアプリケーション自体が動かなくなることだ。これを防ぐために、以下のステップで進めるのがプロの流儀だ。
1. complainモードで運用開始: 上記プロファイルの complain フラグは、違反があってもブロックせずログだけを出すモードだ。まずはこれで数日間動かし、必要なパスを全て洗い出す。
2. auditログの監視: /var/log/audit/audit.log を監視し、本来必要なパスまで拒否されていないか確認する。
3. enforceモードへの切り替え: 運用が安定したら、complain を削除して enforce モード(デフォルト)に切り替える。これで真の要塞化が完了する。
—
4. コンテナ実行時の適用方法
作成したプロファイルをDockerコンテナに紐付けるには、--security-opt オプションを使う。デプロイメントパイプライン(CI/CD)にも必ず組み込んでほしい。
# Docker起動時にAppArmorプロファイルを指定
docker run --rm \
--security-opt "apparmor=docker-web-app" \
-v /var/run/docker.sock:/tmp/dummy \
my-secure-app:latest
もしKubernetesを使用しているなら、SecurityContext で同様の設定が可能だ。
# KubernetesのPod設定例
spec:
containers:
- name: web-app
securityContext:
appArmorProfile:
type: Localhost
localhostProfile: docker-web-app
—
セキュリティチーフからの提言
多くのエンジニアが「攻撃されたらどうしよう」と不安を抱えているが、それは「境界防御」しか考えていないからだ。「侵入されることを前提に、その後の行動を極限まで制限する」というMACの考え方は、ゼロトラスト時代の必須スキルだ。
今回紹介したAppArmorの設定は、一度書いてしまえばサーバーを再起動しても保護が持続する。複雑なWAFの設定に頭を抱える前に、まずはOSレベルでプロセスの行動を縛り上げてほしい。それが、明日あなたが休日に呼び出される確率を劇的に下げる最善の策だ。
もし設定中に「どこまで許可すべきか」と迷ったら、それはアプリケーションの設計を見直す良い機会だ。不要なファイルやパスへのアクセスを必要とするコードこそが、そもそも脆弱性の温床なのだから。
コメント