Docker コンテナー内のプロセスをルートとして実行してはならない理由

公開: 2022-08-19

Docker ロゴを示すグラフィック

Docker コンテナー内のプロセスは、root として実行しないでください。 Dockerfile の一部として指定するか、 docker docker runを使用するときに、root 以外のユーザーとしてアプリケーションを実行する方が安全です。 これにより、コンテナー内の脅威に対する攻撃対象領域が減少し、リスクが最小限に抑えられます。

この記事では、コンテナー化されたアプリケーションをルートとして実行することの危険性について学びます。 非 root ユーザーを作成し、これが不可能な状況で名前空間をセットアップする方法も説明します。

ルートとして実行するのはなぜ危険なのですか?

デフォルトでは、コンテナは root として実行されます。 Docker デーモンはホスト上で root として実行され、実行中のコンテナーも root になります。

コンテナー内の root は独立したユーザーのように見えますが、実際にはホストの root アカウントと同じです。 分離は、Docker のコンテナー分離メカニズムによってのみ提供されます。 強力な物理的境界はありません。 ホストのカーネルで root ユーザーが実行するコンテナーの別のプロセス。 これは、アプリケーション、Docker ランタイム、または Linux カーネルの脆弱性により、攻撃者がコンテナーから抜け出し、マシンで root 権限の操作を実行できる可能性があることを意味します。

これが発生するリスクを軽減する組み込みの保護機能がいくつかあります。 コンテナー内のルートには特権がなく、機能が制限されています。 これにより、コンテナの起動時に機能を手動で追加するか特権モードを使用しない限り、コンテナはシステム管理コマンドを使用できなくなります。

この軽減策にもかかわらず、アプリケーションを root として実行できるようにすることは依然として危険です。 従来の環境で root の使用を制限するのと同じように、コンテナ内で root を不必要に使用するのは賢明ではありません。 違反が発生した場合に攻撃者に足がかりを与える過剰な特権環境を提供しています。

root 以外のユーザーとしてコンテナー化されたアプリケーションを実行する

コンテナー化されたアプリケーションを通常のユーザーとして実行することをお勧めします。 ほとんどのソフトウェアはルート アクセスを必要としないため、ユーザーを変更することで、コンテナーのブレイクアウトに対する即時の防御層が提供されます。

Dockerfile の最終段階の 1 つとして、新しいユーザー アカウントを作成する必要があります。 これは、 USER命令で実現できます。

 FROM ベースイメージ:最新
RUN apt install デモパッケージ
USER デモユーザー:デモグループ
ENTRYPOINT [「デモバイナリ」]

このイメージから開始されたコンテナーは、 demo-userとして実行されます。 ユーザーはdemo-groupグループのメンバーになります。 ユーザーをグループに含める必要がない場合は、グループ名を省略できます。

 USER デモユーザー

名前の代わりにユーザー ID (UID) とグループ ID (GID) を指定できます。

 ユーザー 950:950

通常、既知の UID と GID を割り当てるのが最も安全な方法です。 これにより、コンテナー内のユーザーが過剰な特権を持つホスト アカウントにマップされるのを防ぎます。

USERは、多くの場合、Dockerfile の最後から 2 番目のステージとして指定されます。 これは、イメージ ビルドの早い段階で root を必要とする操作を引き続き実行できることを意味します。 上記の例のapt install命令には、root が正当に必要です。 USER命令がその上に配置されている場合、 aptは必要な権限を欠いているdemo-userとして実行されます。 Dockerfile の手順はイメージ ビルドにのみ適用され、実行中のコンテナーには適用されないため、ユーザーの変更は Dockerfile で後で行うのが安全です。

コンテナーを実行するユーザーを変更すると、コンテナーがアクセスするファイルとフォルダーのアクセス許可を更新することが必要になる場合があります。 アプリケーションで使用されるすべてのパスに所有権を設定します。

 COPY initial-config.yaml /data/config.yaml

USER デモユーザー:デモグループ
RUN chown demo-user:demo-group /data

この例では、アプリケーションが構成ファイルを変更できるように、 /dataディレクトリをdemo-userが所有する必要があります。 以前のCOPYステートメントは、ルートとしてファイルをコピーします。 copy--chownフラグを使用すると、省略形を使用できます。

 COPY --chown=demo-user:demo-group initial-config.yaml /data/config.yaml

コンテナ起動時のユーザー変更

独自の Dockerfile でユーザーを簡単に変更できますが、多くのサードパーティ アプリケーションは引き続き root として実行されます。 docker runを呼び出すたびに--userフラグを設定することで、これらの使用に伴うリスクを軽減できます。 これにより、イメージの Dockerfile で設定されたユーザーが上書きされます。

 $ docker run -d --user demo-user:demo-group demo-image:latest
$ docker run -d --user demo-user demo-image:latest
$ docker run -d --user 950:950 demo-image:latest

--userフラグは、コンテナーのプロセスを指定されたユーザーとして実行します。 すべての docker docker runコマンドに個別に適用する必要があるため、Dockerfile USER命令よりも安全性が低くなります。 定期的に使用されるイメージのより良いオプションは、新しいユーザー アカウントを設定できる独自の派生イメージを作成することです。

 FROM image-that-runs-as-root:latest
USER デモユーザー
$ docker build . -t image-that-now-runs-as-non-root:latest

サードパーティ イメージのユーザーを変更すると、問題が発生する可能性があります。コンテナーがルートとして実行されることを期待している場合、またはルートが所有するファイル システム パスにアクセスする必要がある場合、アプリケーションを使用するとエラーが表示されます。 問題の原因となっているパスのアクセス許可を手動で変更してみてください。 または、特権のないユーザー アカウントでアプリケーションを実行する方法がベンダーにサポートされているかどうかを確認してください。

root として実行する必要があるアプリケーションの処理

ユーザー名前空間は、一部のルート権限を必要とするアプリケーションを処理するための手法です。 これにより、コンテナー内のルートをホスト上の非ルート ユーザーにマップできます。 コンテナー内のシミュレートされたルートには必要な権限がありますが、ブレイクアウトではホストへのルート アクセスは提供されません。

/etc/docker/daemon.jsonファイルにuserns-remapフィールドを追加すると、名前空間の再マッピングが有効になります。

 {
    "userns-remap": "デフォルト"
}

userns-remapの値としてdefaultを使用すると、docker はdockremapというホスト上に新しいユーザーを自動的に作成します。 コンテナー内のルートは、ホストのdockremapにマップされます。 必要に応じて、UID/GID またはユーザー名/グループ名の組み合わせを使用して、代わりに既存のユーザーとグループを指定できます。

 {
    "userns-remap": "デモユーザー"
}

変更を適用した後、Docker デーモンを再起動します。

 $ sudo サービス docker restart

nsuser-remap: defaultを使用している場合、 dockremapユーザーがホストに存在するはずです。

 $ id ドックリマップ

uid=140(dockremap) gid=119(dockremap) groups=119(dockremap)

ユーザーは/etc/subuidおよび/etc/subgid下位 ID ファイルにも表示されます。

 $ Dockremap:231500:65535

ユーザーには、231500 から始まる 65,535 個の下位 ID の範囲が割り当てられています。ユーザー名前空間内では、ID 2315000にマップされ、コンテナー内のルート ユーザーになります。 231500 は高い番号の UID であるため、ホストに対する権限がないため、コンテナーのブレークアウト攻撃はそれほど大きなダメージを与えることができません。

docker run --userns=hostでオプトアウトしない限り、開始するすべてのコンテナーは、再マップされたユーザー名前空間を使用して実行されます。 このメカニズムは、名前空間を持つユーザーの下位の UID と GID が所有する/var/lib/docker内に名前空間を持つディレクトリを作成することによって機能します。

 $ sudo ls -l /var/lib/docker/231500.231500

合計 14
drwx------ 5 231500 231500 13 7 月 22 日 19:00 aufs
drwx------ 3 231500 231500 13 Jul 22 19:00 コンテナー
...

ユーザー名前空間は、コンテナーの分離を強化し、ブレイクアウトを回避し、ルート権限を必要とするアプリケーションとの互換性を維持するための効果的な方法です。 ただし、いくつかのトレードオフがあります。この機能は新しい Docker インスタンスで最適に機能し、ホストからマウントされたボリュームはアクセス許可を調整する必要があり、一部の外部ストレージ ドライバーはユーザー マッピングをまったくサポートしていません。 このオプションを採用する前に、ドキュメントを確認する必要があります。

概要

コンテナー化されたアプリケーションをルートとして実行することは、セキュリティ リスクです。 見落としがちですが、コンテナーによって提供される分離は、カーネル ユーザーとコンテナー ユーザーを完全に分離するには十分ではありません。 コンテナー内のルートはホスト上のルートと同じであるため、侵害に成功するとマシンが制御される可能性があります。

イメージの作成者として、アプリケーションが root なしで実行されるように、Dockerfile にUSER命令を含める必要があります。 イメージ ユーザーはdocker run --userでこれをオーバーライドして、特定の UID と GID を割り当てることができます。 これは、イメージが通常 root を使用するケースを軽減するのに役立ちます。

--cap-drop=ALLを使用してコンテナーからすべての機能を削除し、 --cap-addフラグで必要な機能をホワイトリストに登録することで、セキュリティをさらに強化できます。 これらの手法を組み合わせることで、必要最小限の特権セットを持つ root 以外のユーザーとしてアプリケーションを実行し、セキュリティ体制を改善します。