2026年7月1日
2026年7月1日
Dockerコンテナのセキュリティ強化ガイド(実行時の権限を絞る設定と確認方法)
Docker コンテナのセキュリティは「コンテナに与える権限をどこまで減らせるか」でほぼ決まります。この記事では、すでに動いているコンテナの設定を点検する方法と、root 以外での実行、Linux ケーパビリティの削除、読み取り専用ファイルシステム、ポート公開の範囲、イメージの脆弱性スキャンまでを、docker run と Docker Compose の両方の書き方で説明します。読み終えると、自分の環境のコンテナがどこまで絞られているかを確認し、優先順位を付けて強化できるようになります。
まず現状を点検する
設定を変える前に、今のコンテナがどの権限で動いているかを確認します。次のコマンドはコンテナを止めずに実行できます。
# 実行ユーザー(空なら root で動いている)
docker inspect -f '{{.Config.User}}' コンテナ名
# 特権モードかどうか(true なら最優先で見直す)
docker inspect -f '{{.HostConfig.Privileged}}' コンテナ名
# 追加・削除されたケーパビリティ
docker inspect -f 'add={{.HostConfig.CapAdd}} drop={{.HostConfig.CapDrop}}' コンテナ名
# マウントされているもの(docker.sock が含まれていないか)
docker inspect -f '{{range .Mounts}}{{.Source}} -> {{.Destination}}{{"\n"}}{{end}}' コンテナ名
# コンテナ内で実際に動いているユーザー
docker exec コンテナ名 id
点検で優先して直すべきものは次の3つです。
- Privileged が true:ホストのデバイスへのアクセスなど、ほぼすべての制限が外れます。本当に必要な用途はごく限られます。
- /var/run/docker.sock のマウント:このソケットに触れるコンテナは、新しいコンテナを起動してホストのファイルシステムをマウントできるため、実質的にホストの root 権限と同じです。
- ホストのディレクトリを広くマウントしている:/ や /etc をマウントしていると、コンテナ内の侵害がそのままホストに及びます。
root 以外のユーザーで実行する
コンテナ内のプロセスが root で動いていると、アプリケーションの脆弱性を突かれたときにコンテナ内のファイルを自由に書き換えられ、別の脆弱性と組み合わさったときの被害も大きくなります。自作のイメージでは Dockerfile で専用ユーザーを作り、USER で切り替えます。
FROM node:22-slim
WORKDIR /app
COPY --chown=node:node . .
RUN npm ci --omit=dev
# node イメージにはあらかじめ node ユーザーがある
USER node
CMD ["node", "server.js"]
既存のイメージを変えられない場合は、起動時に --user 1000:1000(Compose では user: "1000:1000")で指定できます。ただし、イメージによっては起動処理の中で root 権限を前提にしているため、そのままでは起動しないことがあります。
WordPress の公式イメージ(Apache 版)は、起動時は root で動き、Apache のワーカーが www-data に切り替わる作りです。この種のイメージに後から USER を指定すると、ファイルのコピーや 80 番ポートの待ち受けで失敗することがあります。強化する場合は、検証環境でログを見ながら1項目ずつ試してください。
ケーパビリティを削り、権限昇格を防ぐ
Linux では root の権限が「ケーパビリティ」という単位に分かれています。Docker は既定でその一部だけをコンテナに与えていますが、多くのアプリケーションはさらに少なくても動きます。基本の考え方は「すべて外してから必要なものだけ戻す」です。
docker run -d --name app \
--user 1000:1000 \
--cap-drop ALL \
--security-opt no-new-privileges:true \
--read-only --tmpfs /tmp \
--pids-limit 200 --memory 512m --cpus 1.0 \
-p 127.0.0.1:8080:8080 \
example/app:1.4.2
Compose で書くと次のようになります。
services:
app:
image: example/app:1.4.2
user: "1000:1000"
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
read_only: true
tmpfs:
- /tmp
pids_limit: 200
mem_limit: 512m
cpus: 1.0
ports:
- "127.0.0.1:8080:8080"
それぞれの意味は次のとおりです。
- cap_drop: ALL:ケーパビリティをすべて外します。起動に失敗したら、ログのエラーから必要なものを特定し
cap_addで個別に戻します。1024 未満のポートで待ち受けるなら NET_BIND_SERVICE が必要になる、といった具合です。 - no-new-privileges:setuid のプログラムなどを使ってコンテナ内で権限を上げることを防ぎます。副作用が少ないので、まず入れたい設定です。
- read_only と tmpfs:コンテナのファイルシステムを書き込み禁止にし、一時ファイルが必要な場所だけ tmpfs にします。侵入者が実行ファイルを置きにくくなります。WordPress の uploads のように書き込みが必要な場所は、名前付きボリュームとして明示的にマウントします。
- pids_limit とメモリ・CPU の上限:1つのコンテナの暴走や fork 爆弾で、ホスト全体が止まるのを防ぎます。
Compose のリソース上限は書き方が複数あり(deploy.resources.limits など)、Compose のバージョンによって解釈が異なる場合があります。設定後に docker stats で上限が反映されているか確認してください。
Docker は既定で seccomp プロファイル(危険なシステムコールの制限)を適用し、対応環境では AppArmor のプロファイルも適用します。--security-opt seccomp=unconfined のように無効化している設定を見つけたら、理由を確認して外せないか検討します。
ポート公開とネットワークの範囲を絞る
-p 8080:80 のように書くと、すべてのネットワークインターフェースでポートが公開されます。リバースプロキシ経由でしか使わないコンテナなら、127.0.0.1:8080:80 のようにループバックだけに限定します。
注意したいのは、Docker が公開ポートのために iptables(または nftables)のルールを直接追加するため、ufw などのホストのファイアウォールで閉じたつもりでも外部から届く場合があることです。公開範囲は Docker の設定側で絞り、外部からポートスキャンをして実際の状態を確認してください。確認は自分が管理するサーバーに対してだけ行います。
データベースのように外部に公開する必要がないコンテナは、ports を書かず、Compose のネットワーク内でだけ通信させます。用途ごとにネットワークを分けると、1つのコンテナが侵害されたときに届く範囲を狭くできます。
イメージを信頼できる状態に保つ
実行時の設定を絞っても、イメージ自体に脆弱性のあるパッケージが含まれていれば攻撃の入り口になります。
- 公式イメージや提供元が明確なイメージを使う:Docker Hub の Docker Official Image や、ソフトウェア提供元が公開しているイメージを選びます。
- タグを固定する:
latestは中身が変わるため、1.4.2のようにバージョンを固定し、更新は意図して行います。より厳密にするなら、ダイジェスト(@sha256:...)で固定します。 - 定期的にスキャンする:Trivy(
trivy image イメージ名)や Docker Scout(docker scout cves イメージ名)で既知の脆弱性を確認できます。機能や無料で使える範囲は変わることがあるため、各ツールの公式ドキュメントを参照してください(2026年10月現在)。 - 再ビルドの習慣を付ける:ベースイメージの更新を取り込むには、イメージを作り直してコンテナを入れ替える必要があります。コンテナ内で apt upgrade をしても、作り直したときに元に戻ります。
パスワードや API キーは Dockerfile の ENV や ARG に書かないでください。イメージの履歴(docker history)から読み取れる場合があります。Compose の secrets や、実行時に渡す環境変数ファイル(git には含めない)を使います。
ホスト側の対策
- Docker 本体とカーネルを更新する:コンテナはホストのカーネルを共有するため、カーネルの脆弱性はすべてのコンテナに影響します。
- docker グループのメンバーを絞る:docker コマンドを実行できるユーザーは、実質的に root と同等の操作ができます。
- rootless モードや userns-remap を検討する:Docker デーモン自体を一般ユーザーで動かす rootless モードや、コンテナ内の root をホストの一般ユーザーに対応付ける userns-remap があります。どちらも使えない機能や制約があるため、検証環境で動作を確認してから導入します。
- ベンチマークで点検する:CIS Docker Benchmark や、それをもとにした点検スクリプトを使うと、設定の抜けを洗い出せます。
設定を変える前には、ボリュームのデータと Compose ファイルのバックアップを取り、検証環境で起動・動作確認をしてから本番に反映します。
まとめ
- まず
docker inspectで実行ユーザー、Privileged、ケーパビリティ、マウントを点検する - Privileged と docker.sock のマウントは最優先で見直す
- root 以外で実行し、
cap_drop: ALLから必要なものだけ戻し、no-new-privileges を付ける - read_only と tmpfs、リソース上限で被害の範囲を狭める
- ポートは必要なインターフェースだけに公開し、Docker のルールがファイアウォールより優先される場合があることに注意する
- イメージはタグを固定し、スキャンと再ビルドを定期的に行う