Dockerのuserns-remap設計|user namespaceリマップでコンテナのroot権限をホストから分離する方法

宮崎智広 この記事の監修:宮崎智広(Linux実務・教育歴20年以上・受講者3,100名超)
HOMELinux技術 リナックスマスター.JP(Linuxマスター.JP)Docker > Dockerのuserns-remap設計|user namespaceリマップでコンテナのroot権限をホストから分離する方法
「コンテナの中はrootで動かしているけど、もしコンテナを脱出されたらホストもroot権限で乗っ取られるのでは」
「ベースイメージが非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での権限調整が必須


「このままじゃマズい」と感じていませんか?
参考書を開く気力もない、同年代に取り残される不安——
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

なぜ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.jsonuserns-remapを追記します。値にdefaultを指定すると、dockerdが専用ユーザーdockremapを自動作成し、そのUID/GID範囲を使ってマッピングします。

# /etc/docker/daemon.json { "userns-remap": "default" }

3. /etc/subuid・/etc/subgidの割り当てを確認する

dockerdを再起動すると、/etc/subuid/etc/subgiddockremap用のUID/GID範囲が自動で追記されます。

# dockerd再起動でdockremapユーザーとマッピング範囲が作られる $ systemctl restart docker $ cat /etc/subuid dockremap:100000:65536 $ cat /etc/subgid dockremap:100000:65536

これは「コンテナ内のUID 0~65535を、ホスト側のUID 100000~165535へ丸ごとずらす」という意味です。
コンテナ内で見える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.

userns-remapが有効な状態では、--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 ディレクトリ
userns-remapは、非rootユーザー実行やrootlessモードと違い、アプリケーション側の対応状況に関係なくdockerd全体へ一律で効かせられる防御層です。
ただし有効化のタイミングによっては、既存のbind mountやvolumeで所有者のズレが顕在化しやすいため、本番投入前に検証サーバーで権限まわりを一通り確認しておくことを強くおすすめします。

現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、本記事で紹介したuserns-remapを含むDockerのセキュリティ設計をさらに深く学べる講座を用意しています。
Dockerマスター講座の詳細はこちら >>

無料メルマガで学習を続ける

Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。

登録無料・いつでも解除できます

暗記不要・1時間後にはサーバーが動く

3,100名以上が実践した「型」を無料で公開中

プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。

姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

Linux無料マニュアル(図解60P) 名前とメールで30秒登録
宮崎 智広

この記事を書いた人

宮崎 智広(みやざき ともひろ)

株式会社イーネットマーキュリー代表。現役のLinuxサーバー管理者として20年以上の実務経験を持ち、これまでに累計3,100名以上のエンジニアを指導してきたLinux教育のプロフェッショナル。「現場で本当に使える技術」を体系的に伝えることをモットーに、実践型のLinuxセミナーの開催や無料マニュアルの配布を通じてLinux人材の育成に取り組んでいる。

趣味は、キャンプにカメラ、トラウト釣り。好きな食べ物は、ラーメンにお酒。休肝日が作れない、酒量を減らせないのが悩み。最近、ドラマ「フライトエンジェル」を観て涙腺が崩壊しました。