2026年5月17日

2026年5月17日

Dockerコンテナをrootで実行しない方法|DockerfileのUSER・–user・rootlessモード

Docker のコンテナは、イメージで指定がなければ root(UID 0) でプロセスを動かします。コンテナの中の root は、ユーザー名前空間を使っていない通常の設定では、ホストの root と同じ UID です。アプリの脆弱性を突かれたり、コンテナの隔離を破る不具合が見つかったりしたときに、プロセスが root だと被害が大きくなります。

この記事では、コンテナのプロセスを root 以外のユーザーで動かす方法を説明します。コマンドと出力は2026年10月時点の Docker Engine 29 で確認しています。

今どのユーザーで動いているかを確かめる

# イメージの既定のユーザーで id を実行する
docker run --rm イメージ名 id

# 動いているコンテナの中のユーザー
docker exec コンテナ名 id

# ホストから見たプロセスのユーザー
docker top コンテナ名 -o user,pid,cmd

# イメージに設定されている USER(空なら root)
docker inspect --format '{{.Config.User}}' イメージ名

uid=0(root) と表示されたら root で動いています。

Dockerfile で一般ユーザーに切り替える

基本は、root のうちにパッケージのインストールなどを済ませ、最後に USER 命令で一般ユーザーに切り替えることです。USER より後の RUN・CMD・ENTRYPOINT は、そのユーザーで実行されます。USER の後で apt-get install を実行すると、権限がなくて失敗します。

Python の小さな Web アプリの例です。

FROM python:3.13-slim

# root のうちにパッケージを入れる
RUN apt-get update \
    && apt-get install -y --no-install-recommends curl \
    && rm -rf /var/lib/apt/lists/*

# UID/GID を固定した一般ユーザーを作る
RUN groupadd --gid 10001 app \
    && useradd --uid 10001 --gid app --no-create-home --shell /usr/sbin/nologin app

WORKDIR /app
COPY --chown=app:app app.py .

# ここから先の命令とコンテナのプロセスは app ユーザーで動く
USER app

EXPOSE 8000
CMD ["python", "app.py"]
# app.py
from http.server import HTTPServer, SimpleHTTPRequestHandler

HTTPServer(("0.0.0.0", 8000), SimpleHTTPRequestHandler).serve_forever()

ビルドして確かめます。

$ docker build -t myapp .
$ docker run --rm myapp id
uid=10001(app) gid=10001(app) groups=10001(app)

書き方のポイントは次のとおりです。

  • UID と GID を固定する:番号を決めておくと、ボリュームの所有者を合わせたり、Kubernetes などで UID を指定したりするときに扱いやすくなります。
  • COPY --chown:COPY したファイルは既定で root の所有になります。アプリが書き込む必要のあるファイルやディレクトリーだけ、--chown で一般ユーザーの所有にします。読むだけのファイルは root の所有のままでも動きます。
  • Alpine の場合:useradd ではなく addgroup -g 10001 -S app && adduser -u 10001 -S -G app app のように BusyBox のコマンドを使います。

公式イメージには、一般ユーザーを最初から用意しているものもあります。たとえば node イメージには node ユーザー(UID 1000)があり、Dockerfile に USER node と書くだけで切り替えられます。

実行時にユーザーを指定する(--user)

イメージを作り直さずに、起動時にユーザーを指定することもできます。

$ docker run --rm --user 1000:1000 python:3.13-slim id
uid=1000 gid=1000 groups=1000

イメージの中に存在しない UID でも指定できます(その場合、名前は表示されません)。Docker Compose では user で指定します。

services:
  app:
    image: myapp
    user: "10001:10001"

バインドマウントの権限を合わせる

ホストのディレクトリーをマウントして書き込む場合は、コンテナのユーザーの UID と、ホストのディレクトリーの所有者を合わせる必要があります。開発環境では、ホストの自分の UID で起動するのが手軽です。

docker run --rm --user "$(id -u):$(id -g)" -v "$PWD":/work -w /work python:3.13-slim \
  python -c "open('out.txt','w').write('ok')"

本番では、ホスト側のディレクトリーをイメージで決めた UID(例の 10001)の所有にしておきます。

sudo chown -R 10001:10001 /srv/myapp/data

1024番未満のポートを使う場合

Linux では通常、root 以外は 1024 番未満のポートで待ち受けできません。ただし現在の Docker は、コンテナのネットワーク名前空間で net.ipv4.ip_unprivileged_port_start=0 を設定するため、一般ユーザーのプロセスでもコンテナ内で 80 番を使えます(Docker Engine 29 で確認)。--network host で動かす場合はこの設定が適用されないので、8000 番などの大きい番号で待ち受け、-p 80:8000 のように公開します。ほかの環境に移しても同じように動くので、迷ったらこの構成にしておくと扱いやすくなります。

nginx の公式イメージは、マスタープロセスを root で起動し、ワーカーを nginx ユーザーで動かします。マスターも含めて root 以外で動かしたい場合は、nginx の開発元が配布している nginxinc/nginx-unprivileged イメージ(8080 番で待ち受け)を使う方法があります。

あわせて設定したい実行時のオプション

root 以外で動かすことに加えて、次のオプションで権限をさらに絞れます。

docker run -d --name myapp \
  --user 10001:10001 \
  --cap-drop ALL \
  --security-opt no-new-privileges \
  --read-only --tmpfs /tmp \
  -p 8000:8000 myapp
オプション 働き
--cap-drop ALL Linux のケーパビリティ(root の権限を分割したもの)をすべて外す。必要なものだけ --cap-add で戻す
--security-opt no-new-privileges setuid されたプログラムなどで権限が上がるのを防ぐ
--read-only コンテナのファイルシステムを読み取り専用にする。書き込みが必要な場所は --tmpfs やボリュームで用意する

アプリによっては書き込み先が足りずに起動しないことがあるので、docker logs でエラーを確認しながら調整します。

Docker 側の設定で root を切り離す

イメージの中身を変えられない場合や、ホストをさらに守りたい場合は、Docker デーモンの設定で対策できます。

userns-remap(ユーザー名前空間の再割り当て)

コンテナの中の UID を、ホストの別の UID の範囲に対応させます。コンテナの中では root(UID 0)でも、ホストから見ると権限のない UID になります。/etc/docker/daemon.json に書き、デーモンを再起動します。

{
  "userns-remap": "default"
}
sudo systemctl restart docker

default を指定すると dockremap というユーザーが作られ、/etc/subuid と /etc/subgid に書かれた範囲が使われます。有効にすると、それまでのイメージやコンテナは見えなくなり(別の保存場所が使われる)、--privileged やホストの名前空間を共有する一部のオプションが使えなくなるなどの制約があります。既存の環境で有効にする前に、公式ドキュメントの制約を確認してください。

rootless モード

Docker デーモン自体を一般ユーザーで動かす方法です。デーモンもコンテナも root の権限を持たないため、デーモンが乗っ取られた場合の影響も小さくできます。Docker の公式パッケージでは、一般ユーザーで dockerd-rootless-setuptool.sh install を実行して設定します(事前に uidmap パッケージなどが必要です)。対応する機能や手順はディストリビューションによって異なるので、公式ドキュメントの「Rootless mode」に沿って設定してください。

注意点

  • docker グループに入っているユーザーは、Docker を通じてホストの root と同等の操作ができます。コンテナを root 以外で動かしても、docker グループに入れるユーザーは信頼できる人だけにします。
  • Docker のソケット(/var/run/docker.sock)をコンテナにマウントすると、そのコンテナはホストを操作できるようになります。必要な場合以外はマウントしません。
  • --privileged は、ほぼすべての制限を外すオプションです。root 以外で動かしていても、--privileged と組み合わせると対策の意味が薄れます。

まとめ

  • docker run --rm イメージ id や docker top で、今のユーザーを確かめる
  • Dockerfile では root のうちにパッケージを入れ、UID を固定した一般ユーザーを作って最後に USER で切り替える
  • 書き込むファイルだけ COPY --chown で一般ユーザーの所有にする
  • 実行時は --user、Compose では user: で指定でき、バインドマウントは UID を合わせる
  • --cap-drop ALL・no-new-privileges・--read-only を組み合わせ、必要に応じて userns-remap や rootless モードを使う

関連記事:

お気軽にご相談ください

お見積りへ お問い合わせへ
▲