TT Lab
はじめる
学ぶ 学習パス コース

コンテナセキュリティ

コンテナを開ける四つの扉

TT Labで続きを見る

一言でいうと

コンテナ脱出は、たいていカーネルの脆弱性ではなく、自分たちが直接開けておいた設定から 起こります。4つのパターンを知っていれば、レビューで30秒で見つけられます。

このレッスンは、防御のためのものです。各項目ごとに、「なぜ危険か」と 「何で防ぐか」をペアにして扱います。

なぜ必要なのか

コンテナが非rootかを確認するだけでは、ホストの境界を証明できません。 Dockerソケット、ホストのファイルシステム、PIDネームスペースや過剰なcapabilityを開けば、 コンテナ内の低い権限のプロセスでも、ホストの権限を迂回して獲得できます。 したがって、イメージのユーザーだけでなく、ランタイムに接続したリソースとカーネルの権限もあわせてレビューする必要があります。

扉1: Dockerソケットのマウント

volumes:
  - /var/run/docker.sock:/var/run/docker.sock

CIランナーやモニタリングエージェントでよく見かける行です。このソケットは、Docker デーモンのAPIであり、デーモンはホストのrootで動いています。ソケットにアクセスできれば、 ホストのルートを丸ごとマウントした特権コンテナを、新しく起動できます。 つまり、この1行は、事実上ホストのroot権限を与えるのと同じです。

防ぐ方法: ソケットをコンテナに渡しません。コンテナのビルドが必要なら、 デーモンが不要なビルダー(kaniko、buildah)を使い、情報の取得が目的なら、 読み取り専用のプロキシを前に置いて、許可するエンドポイントを許可リストで制限します。 LabHubのイメージビルドがkanikoを使う理由が、これです。

扉2: --privileged

--privilegedは、capabilityをすべて与え、デバイスへのアクセスを開き、 seccomp/AppArmorを事実上解除します。この状態では、ホストのディスクを /dev/sdaとして開いてマウントすることが可能です。

防ぐ方法: 必要なcapabilityだけを個別に与えます。たとえば、低いポートへの バインドが目的なら、NET_BIND_SERVICE1つで足ります。Kubernetesなら、 Pod Security Admissionのbaseline以上で、特権Podをそもそも拒否します。

扉3: ホストパスのマウント

volumes:
  - /:/host          # 최악
  - /etc:/etc-host   # 나쁨
  - /var/log:/logs   # 상황에 따라

このコードブロックの韓国語コメントは、順に、最悪、悪い、状況による、という意味です。

/は言うまでもなく、/etcだけを使っても、shadow・sudoers・cronファイルに 届きます。書き込み可能なら、そのままホストの侵害です。

防ぐ方法: マウントのパスを最小限の範囲に絞り、:roを付けます。 ホストのパスの代わりに名前付きボリュームを使い、Kubernetesでは、ポリシーエンジンで hostPath自体を禁止するのが確実です。

扉4: 共有されたネームスペース

--pid=hostは、ホストのすべてのプロセスを見えるようにし、--net=hostは、 ホストのネットワークスタックをそのまま使います。前者は/proc/<pid>/rootを通じて、 ほかのプロセスのファイルシステムにアクセスする道を開き、後者は、localhostにだけ 開けておいた管理ポートに届くようにします。

防ぐ方法: デフォルト(分離)を維持します。モニタリングのように、本当に必要な場合でも、 読み取り専用 + 最小限のcapabilityに絞ります。

一目でわかる一覧

設定 実質的な意味 代替
docker.sockのマウント ホストのroot kaniko/buildah、読み取り専用のプロキシ
--privileged すべてのcapability + デバイス 必要なcapだけ、PSA baseline
-v /:/host ホストのファイルシステム 範囲の縮小 + :ro、名前付きボリューム
--pid=host ほかのプロセスへのアクセス デフォルトの分離を維持

そして、防御の層

設定をうまく行っても、ランタイムの脆弱性は残ります。層を重ねておきます。

  1. UID: USER 10001。rootで動かさなければ、ほとんどの経路が塞がります。
  2. capability: すべてdropして、必要なものだけをaddします。
  3. NO_NEW_PRIVS: setuidで権限を上げる道を断ちます。
  4. 読み取り専用ルート: --read-only + 必要な場所だけtmpfs。
  5. seccomp: 危険なシステムコールを、カーネルの入口で遮断します。

LabHubのラボのPodは、この5つをすべて適用しています。そのため、皆さんがラボの 中でrootとして何をしても、ホストには届きません。

現場での姿

次の確認で見ること

続くクイズでは、Dockerソケット・ホストのマウント・PIDの共有が開く経路と、それを 防ぐ最小限のcapability・NoNewPrivs・seccompの役割を区別します。前のラボで確認した カーネルの権限の値を根拠に、単一の防御策で十分だという誤答を除いてください。