「ベースイメージが非rootユーザーに対応しておらず、rootのまま動かさざるを得ない」
そんな不安を抱えたままDockerを本番運用しているサーバー管理者は少なくありません。
この記事では、コンテナ内のroot(UID 0)をホスト側の非特権UIDへマッピングし直す
userns-remapの仕組みを、daemon.jsonでの設定手順から動作確認、ボリューム権限エラーのトラブルシュートまで実機で解説します。動作確認環境: Rocky Linux 9.4 / Ubuntu 24.04 LTS(Docker Engine 26.x・Docker Compose Plugin v2.27)
この記事のポイント
・userns-remapはコンテナ内rootをホストの非特権UIDへ丸ごとマッピングし直すdockerd機能
・非rootユーザー実行やrootlessモードとは別レイヤーの防御で、両者は併用できる
・daemon.jsonに1行追加してdockerd再起動するだけで、既存コンテナすべてに一律適用される
・有効化後はbind mountや既存volumeのPermission deniedが起きやすく、chownでの権限調整が必須
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
なぜuserns-remapが必要なのか|コンテナのrootとホストのrootが同じUIDである危険性
Dockerのデフォルト設定では、コンテナ内で動くプロセスのUID 0(root)は、ホスト側から見てもそのままUID 0として扱われます。Linuxカーネルのnamespace機能でプロセス空間やファイルシステムは隔離されていますが、UIDの数値自体は隔離されていないためです。
つまり、コンテナ内でrootとして動くプロセスがカーネルの脆弱性やマウント設定のミスを突いてコンテナを脱出した場合、ホスト側でもroot権限を持ったまま活動できてしまいます。
1. 非rootユーザー実行・rootlessモードとの違い
「コンテナ内を非rootユーザーで動かす」対策は、DockerfileのUSER命令でアプリケーションプロセスの実行ユーザーを変える設計です。これはアプリケーション層の対策であり、ベースイメージやミドルウェアがrootでの起動を前提にしている場合は適用できません。
一方、userns-remapはdockerdそのものの機能で、コンテナ内が「見かけ上root」であっても、ホスト側のUIDは強制的に非特権な範囲へ差し替えます。
アプリケーション側の対応状況に関係なく、daemon全体に一律で効かせられる点が最大の違いです。
非rootユーザー実行・rootlessモード・userns-remapは互いに排他ではなく、重ねて使うことでコンテナ脱出時の被害を多層的に抑えられます。
基本的な使い方|userns-remapの設定手順
2. daemon.jsonでuserns-remapを有効化する
/etc/docker/daemon.jsonにuserns-remapを追記します。値にdefaultを指定すると、dockerdが専用ユーザーdockremapを自動作成し、そのUID/GID範囲を使ってマッピングします。# /etc/docker/daemon.json { "userns-remap": "default" }
3. /etc/subuid・/etc/subgidの割り当てを確認する
dockerdを再起動すると、/etc/subuidと/etc/subgidにdockremap用のUID/GID範囲が自動で追記されます。# dockerd再起動でdockremapユーザーとマッピング範囲が作られる $ systemctl restart docker $ cat /etc/subuid dockremap:100000:65536 $ cat /etc/subgid dockremap:100000:65536
コンテナ内で見えるroot(UID 0)は、ホストから見るとUID 100000の非特権ユーザーとして動くことになります。
4. 動作確認:ホスト側のUIDマッピングをdocker infoとpsで確認する
# Docker Root Dirがremap後のUID.GID名のディレクトリに変わっているか確認 $ docker info | grep "Docker Root Dir" Docker Root Dir: /var/lib/docker/100000.100000 # コンテナを起動してホスト側のプロセス所有者を確認 $ docker run -d --name remap-test nginx:1.27 $ ps -eo pid,user,cmd | grep nginx | grep -v grep 4821 100000 nginx: master process nginx -g daemon off; 4855 100000 nginx: worker process
idコマンドを実行するとuid=0(root)と表示されますが、ホスト側のプロセス一覧では実際にはUID 100000のユーザーとして動いていることが確認できます。コンテナが仮に脱出しても、ホスト上での権限は一般ユーザー相当にとどまります。
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、userns-remapをはじめとするDockerのセキュリティ設計を体系的に学べる講座を用意しています。
→ Dockerマスター講座の詳細はこちら >>
応用・実務Tips|特定コンテナだけremapを無効化する設計
5. --userns=hostで信頼できるコンテナだけ従来動作に戻す
userns-remapを有効化すると、原則としてホスト上の全コンテナに一律適用されます。ただし、監視エージェントなど「ホストのプロセス・デバイスに直接アクセスする必要がある」信頼済みコンテナでは、個別に無効化できます。
# このコンテナだけremapを無効化し、従来通りホストのUIDと一致させる $ docker run -d --userns=host --name host-agent monitoring-agent:latest
--userns=hostを指定したコンテナは、userns-remapの防御効果を受けません。信頼できるイメージに限定して使いましょう。6. Docker Composeでuserns_modeを指定する
Compose環境でも、サービス単位で従来動作に戻すことができます。# compose.yml(監視エージェントだけremapから除外する) services: app: image: myapp:latest host-agent: image: monitoring-agent:latest userns_mode: "host"
appサービスはdaemon側のuserns-remap設定に従いますが、host-agentサービスだけuserns_mode: "host"で除外されます。7. 既存運用からの移行時に起きるボリューム所有者のズレ
すでに稼働中のホストでuserns-remapを後から有効化すると、既存のnamed volume内のファイルは「remap前のUID(実UID 0など)」のまま残ります。remap後のコンテナは非特権UIDとして動くため、既存ファイルの所有者と一致せず、読み書きできなくなる事故が起きやすいポイントです。
本番環境に投入する前に、検証サーバーで既存ボリュームを複製し、所有者のズレが起きないか事前に確認しておくことを推奨します。
トラブルシュート|userns-remap有効化後によくあるエラーと対処
8. 手順1: bind mountのPermission deniedを疑う
$ docker run --rm -v /data/app:/data alpine touch /data/test touch: /data/test: Permission denied
/data/appが実UID 0(root)所有のままだと、remap後のコンテナ内root(ホストから見ると非特権UID)では書き込めません。/etc/subuidで確認した開始UIDに合わせて、ホスト側ディレクトリの所有者を変更します。# subuidの開始UID(100000)に所有者を合わせる $ chown -R 100000:100000 /data/app $ docker run --rm -v /data/app:/data alpine touch /data/test $ ls -n /data/app -rw-r--r-- 1 100000 100000 0 Aug 20 12:00 test
9. 手順2: 既存named volumeの所有者UIDのズレを疑う
# volumeの実体パスを確認し、所有者UIDを調べる $ docker volume inspect app_data --format '{{ .Mountpoint }}' /var/lib/docker/100000.100000/volumes/app_data/_data $ ls -ln /var/lib/docker/100000.100000/volumes/app_data/_data -rw-r--r-- 1 0 0 1240 Jul 10 09:12 config.yml
0(remap前のroot)のままであれば、remap後のUID(この例では100000)へchownで合わせ直す必要があります。10. 手順3: --privilegedやdocker-in-dockerが動かない場合を疑う
$ docker run --privileged --rm alpine sh docker: Error response from daemon: privileged mode is incompatible with user namespace mappings.
--privilegedコンテナやDocker-in-Docker構成の一部が起動できません。これらを使う既存コンテナが混在する環境では、そのサービスにだけ
--userns=host(Composeならuserns_mode: "host")を指定して除外する設計にしましょう。11. よくある原因チェックリスト
・daemon.json未反映:設定後にdockerdを再起動し忘れていないか(systemctl restart docker)・subuid/subgidの重複:複数のremap対象ユーザーを手動指定した場合、UID範囲が他ユーザーと重複していないか
・bind mountの所有者未調整:ホスト側ディレクトリの所有者をremap後のUIDに合わせているか
・既存volumeの移行漏れ:remap有効化前に作成したvolume内ファイルの所有者を確認したか
・privileged/DinDとの併用:特権が必要なコンテナだけ
--userns=hostで個別除外しているか本記事のまとめ
| やりたいこと | コマンド・設定 |
|---|---|
| userns-remapを有効化する | daemon.jsonに"userns-remap": "default"を追記してdockerdを再起動する |
| 割り当てられたUID範囲を確認する | cat /etc/subuid /etc/subgid |
| マッピング後のroot dirを確認する | docker info | grep "Docker Root Dir" |
| 特定コンテナだけ従来通りrootで動かす | docker run --userns=host イメージ名 |
| Composeで個別サービスを除外する | サービスにuserns_mode: "host"を指定する |
| bind mountのPermission deniedを解消する | remap後のUIDに合わせてchown -R 100000:100000 ディレクトリ |
ただし有効化のタイミングによっては、既存のbind mountやvolumeで所有者のズレが顕在化しやすいため、本番投入前に検証サーバーで権限まわりを一通り確認しておくことを強くおすすめします。
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、本記事で紹介したuserns-remapを含むDockerのセキュリティ設計をさらに深く学べる講座を用意しています。
→ Dockerマスター講座の詳細はこちら >>
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら
- 前のページへ:Docker Composeのextendsで共通設定を複数ファイルに分割管理する方法|環境別オーバーライドとの使い分け
- この記事の属するカテゴリ:Dockerへ戻る

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。
登録無料・いつでも解除できます