コンテナの「脱獄」を封じる:User Namespacesが防衛の最後の砦である理由
インフラの要塞化を語る際、多くのエンジニアは「不要なサービスを止めろ」「SSHのポートを変えろ」といった教科書的なアドバイスで思考を停止させる。しかし、我々のような最前線に立つ人間にとって、それらは「マナー」に過ぎない。真の防衛とは、OSのカーネル境界線という最も脆弱な地平において、いかに攻撃者の「特権」を虚無へと帰すかにかかっている。
今日議論したいのは、コンテナセキュリティにおける「User Namespaces(userns)」の実装だ。これは単なる設定項目ではなく、Linuxカーネルの深層における「IDの欺瞞」を通じた、究極の防御レイヤーである。
なぜコンテナのrootは危険なのか
DockerやKubernetesのデフォルト設定では、コンテナ内部のrootユーザーは、ホストOS上のrootと実質的に同一の権限を持つ可能性がある(Capabilitiesが適切にドロップされていない場合)。もしコンテナがプロセス分離の壁を破り、メモリ上のポインタ操作やvsyscallの脆弱性を突いてホストへエスケープした場合、攻撃者は即座にホストの支配権を掌握する。
これを防ぐのがUser Namespacesだ。これはコンテナ内のUID 0(root)を、ホスト上の全く別の非特権ユーザー(例えばUID 100000)にマッピングする。これにより、万が一コンテナがエスケープに成功しても、ホスト側からは「何の権限も持たないただのユーザー」としてしか認識されない。
User Namespacesの構成と実務的障壁
実装はシンプルだが、運用には知的な慎重さが求められる。まず、ホスト側でどの範囲のUIDをコンテナに割り当てるかを定義する必要がある。
/etc/subuid と /etc/subgid に以下の設定を記述する。
# ユーザー名:開始UID:UID数
# ホストのユーザーdockremapに、10万番目から65536個のIDを割り当てる
dockremap:100000:65536
次に、Dockerデーモン側でこのマッピングを有効化する。/etc/docker/daemon.json を編集し、以下の設定を追記する。
{
"userns-remap": "dockremap"
}
この設定を適用してDockerを再起動すると、コンテナ内で実行されるプロセスはすべて、ホスト上では非特権ユーザーとして動くようになる。
現場で直面する「泥沼」:ファイルシステムの権限問題
この技術を導入したエンジニアが最初に直面するのは、共有ボリュームのアクセス権限エラーだ。ホスト側でUID 1000のユーザーが作成したファイルに対し、コンテナ内のrootがアクセスできなくなる。これは当然だ。ホスト側から見れば、コンテナ内のrootはUID 100000の無名ユーザーであり、ファイル所有権の不一致が起きるからだ。
ここで「とりあえずchmod 777で凌ぐ」という安易な選択をした瞬間、あなたのセキュリティアーキテクチャは崩壊する。解決策は、コンテナ起動時にchownを行うか、あるいは適切にマウントポイントの所有権を再帰的にコンテナ内ユーザーへ適合させる運用スクリプトをCI/CDパイプラインに組み込むことだ。
アーキテクトへの問い:なぜこれをやるのか
現在のサイバー攻撃のトレンドは、単なるWebアプリケーションへのSQLインジェクションにとどまらない。コンテナのランタイム自体が持つ未公開のゼロデイ脆弱性や、カーネルのio_uringインターフェースを悪用した権限昇格などが狙われている。
もしあなたがKubernetes環境でマルチテナンシーを実現しているのであれば、Pod Security Admission(PSA)でrestrictedプロファイルを適用するのは前提だが、さらにその下の層でUser Namespacesを強制することで、コンテナ・ランタイムの脆弱性によるホスト浸透を物理的な限界まで抑制できる。
防衛の哲学
セキュリティとは、完璧を追い求めることではない。「どこが破られたら、どこまで被害を限定させるか」という、悲観的な設計思想に基づいた境界線引きの作業だ。
User Namespacesを有効にすることは、ホストとコンテナの間に「信頼の隔絶」を置くことと同義である。たとえ攻撃者がコンテナ内のプロセスでメモリダンプを試みたり、パケットを解析してネットワークの境界を探索しようとしても、彼らが見る景色は「制限された隔離空間」でしかない。
次回のアーキテクチャ設計では、この「IDの欺瞞」を標準装備として検討してほしい。OSの深淵で戦う準備ができている者だけが、真に堅牢なインフラを構築できるのだから。
コメント