Docker デーモンをルート以外のユーザで実行(Rootless モード)¶
Rootless モード(Rootless mode)は Docker デーモンとコンテナを root 以外のユーザが実行できるようにするもので、デーモンやコンテナ・ランタイムにおける潜在的な脆弱性を回避します。
Rootless モードは Docker デーモンのインストールに root 権限を必要としないだけでなく、 事前準備 においても不要です。
Rootless モードは Docker Engine v19.03 から導入されました。
注釈
Rootless モードは実験的な機能であり、いくつかの制約があります。詳細は 既知の制約 をご覧ください。
どのように動作するか¶
Rootless モードは Docker デーモンとコンテナをユーザ名前空間(user namespace)の中で実行します。これは userns-remap モード と非常に似ていますが、こちらはデーモン自身は root 権限として動作しています。一方の Rootless モードでは、デーモンとコンテナの両方の実行に root 権限がありません。
Rootless モードは SETUID
ビットやファイル・ケーパビリティを必要としません、しかし、 newuidmap
と newgidmap
は除外します。これらはユーザ名前空間内で複数の UID/GID を利用可能にするために必要です。
事前準備¶
- ホスト上に
newuidmap
とnewgidmap
のインストールが必要です。各コマンドは多くのディストリビューションでuidmap
パッケージとして提供されています。 /etc/subuid
と/etc/subgid
はユーザに対して、少なくともサブオーディネイト UID/GID を 65,536 含むべきです。以下の例は、ユーザtestuser
は 65,546 サブオーディネイト UID/GID (231072~296607)を持っています。
$ id -u
1001
$ whoami
testuser
$ grep ^$(whoami): /etc/subuid
testuser:231072:65536
$ grep ^$(whoami): /etc/subgid
testuser:231072:65536
ディストリビューション固有のヒント¶
注釈
私たちは Ubuntu kernel の利用を推奨します。
Ubuntu¶
- 何も準備する必要がありません
overlay2
ストレージ・ドライバがデフォルトで有効です( Ubuntu 固有のカーネル・パッチ )- 動作するのが分かっているのは Ubuntu 16.04、18.04、20.04 です。
Debian GNU/Linux¶
/etc/sysctl.conf
(または/etc/sysctl.d
)にkernel.unprivileged_userns_clone=1
を追加し、sudo sysctl --system
を実行。overlay2
ストレージ・ドライバ(推奨)を使うために、sudo modprobe overlay permit_mounts_in_userns=1
を実行( Debian 10 で導入された、Debian 独自のカーネル・パッチ 。 )。設定を残し続けるためには、/etc/modprobe.d
に設定を追加。- Debian 9 と 10 での動作が分かっています。
overlay2
をサポートしているのは Debian 10 からで、前述のmodprobe
設定が必要。
Arch Linux¶
/etc/sysctl.conf
(または/etc/sysctl.d
)にkernel.unprivileged_userns_clone=1
を追加し、sudo sysctl --system
を実行。
openSUSE¶
sudo modprobe ip_tables iptable_mangle iptable_nat iptable_filte
が必要。設定状況によっては、他のディストリビューションのような依存関係が必要な場合があります。- openSUSE 15 での動作が分かっています。
Fedora 31 以降¶
- Fedora 31 では cgroup v2 の利用がデフォルトですが、containerd ランタイムでは未サポートです。
sudo grubby --update-kernel=ALL --args="systemd.unified_cgroup_h
を実行し、 cgroup v1 を使います。 sudo dnf install -y iptables
が必要になる場合もあります。
CentOS 8¶
sudo dnf install -y iptables
が必要になる場合があります。
CentOS 7¶
/etc/sysctl.conf
(または/etc/sysctl.d
)にuser.max_user_namespaces=28633
を追加し、sudo sysctl --system
を実行。- デフォルトでは
systemctl --user
が動作しません。systemd を使わずに直接デーモンを実行します:dockerd-rootless.sh --experimental --storage-driver vfs
- CentOS 7.7 での動作が分かっています。以前のリリースでは、追加の設定手順が必要です。
- CentOS 7.6 以前のリリースでは COPR パッケージ vbatts/shadow-utils-newxidmap をインストールします。
- CentOS 7.5 以前のリリースでは、
sudo grubby --update-kernel=ALL --args="user_namespace.enable=1"
の実行と、有効化のために再起動が必要です。
既知の制限¶
vfs
グラフドライバをサポートしています。しかしながら、 Ubuntu と Debian 10 ではoverlay2
とoverlay
もサポートしています。以下の機能は非サポートです。
- cgroups(cgroups に依存する
docker top
を含みます) - AppArmor
- Checkpoint
- オーバレイ・ネットワーク
- SCTP ポートの公開(exposing)
- cgroups(cgroups に依存する
ping
コマンドを使うには、 ping パケットのルーティング をご覧ください。特権 TCP/UDP ポート(ポート 1024 以下)を公開するには、 特権ポートの公開 をご覧ください。
docker inspect
で表示するIPAddress
とは、RootlessKit のネットワーク名前空間内で名前区間化されたものです。つまり、nsenter
化しなければ、そのネットワーク名前空間にはホスト側から到達できない IP アドレスです。また、ホスト・ネットワーク(
docker run --net=host
)も RootlessKit 内に名前空間化されています。
インストール¶
インストール用のスクリプトは https://get.docker.com/rootless にあります。
$ curl -fsSL https://get.docker.com/rootless | sh
スクリプトを root 以外のユーザで実行します。root ユーザとして Rootless Docker をインストールするには、 手動インストール の手順をご覧ください。
スクリプトによって表示される環境変数が必要になります:
$ curl -fsSL https://get.docker.com/rootless | sh
...
# Docker binaries are installed in /home/testuser/bin
# WARN: dockerd is not in your current PATH or pointing to /home/testuser/bin/dockerd
# Make sure the following environment variables are set (or add them to ~/.bashrc):
export PATH=/home/testuser/bin:$PATH
export PATH=$PATH:/sbin
export DOCKER_HOST=unix:///run/user/1001/docker.sock
#
# To control docker service run:
# systemctl --user (start|stop|restart) docker
#
手動インストール¶
インストーラを使わず手動でバイナリをインストールするには、 https://download.docker.com/linux/static/stable/x86_64/ から docker-<version>.tar.gz
と一緒に docker-rootless-extras-<version>.tar.gz
を展開します。
既に Docker を root として実行中の場合は、 docker-rootless-extras-<version>.tar.gz
のみを展開します。アーカイブは $PATH
に含まれるいずれかのディレクトリに展開します。たとえば、 /usr/local/bin
や $HOME/bin
です。
nightly チャネル¶
nightly バージョンの Rootless Docker をインストールするには、インストール・スクリプトで CHANNEL="nightly"
を使って実行します。
$ curl -fsSL https://get.docker.com/rootless | CHANNEL="nightly" sh
単体のバイナリは、こちらからダウンロードできます。
使い方¶
デーモン¶
systemctl --user
を使い、デーモンのライフサイクルを管理します。
$ systemctl --user start docker
システム起動時にデーモンを起動するには、 systemd サービスを有効化し、実行し続ける設定にします。
$ systemctl --user enable docker
$ sudo loginctl enable-linger $(whoami)
systemd を使わずにデーモンを直接起動するには、 dockerd
の代わりに dockerd-rootless.sh
の実行が必要です。
$ dockerd-rootless.sh --experimental --storage-driver vfs
Rootless モードは実験的なため、 docker-rootless.sh
には --experimental
が必要です。
また、 Ubuntu や Debian 10 カーネルを使っていない場合は、 --storage-driver vfs
の指定も必要です。systemd を使ってデーモンを管理している場合、これらのフラグについて考慮する必要はありません。systemd ユニットファイル内で各フラグが自動的に追加されるためです。
ディレクトリのパスについて説明します:
- ソケットのパスは、デフォルトで
$XDG_RUNTIME_DIR/docker.sock
に設定されます。$XDG_RUNTIME_DIR
は通常/run/user/$UID
に設定されます。 - データ・ディレクトリは、デフォルトで
~/.local/share/docker
に設定されます。 - 実行ディレクトリは、デフォルトで
$XDG_RUNTIME_DIR/docker
に設定されます。
その他の説明です:
dockerd-rootless.sh
スクリプトのdockerd
実行は、自分自身のユーザ、マウント、ネットワークの各名前空間を使います。名前空間に入る場合は、nsenter -U --preserve-credentials -n -m -t $(cat $XDG_RUNTIME_DIR/docker.pid)
を実行します。docker info
を実行すると、SecutiryOptions
がrootless
と表示します。docker info
を実行すると、Cgroup Driver
がnone
と表示します。
クライアント¶
ソケットのパスを明確に指定する必要があります。
$DOCKER_HOST`
でソケットのパスを指定するには:
$ export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/docker.sock
$ docker run -d -p 8080:80 nginx
docker context
でソケットのパスを指定するには:
$ docker context create rootless --description "for rootless mode" --docker "host=unix://$XDG_RUNTIME_DIR/docker.sock"
rootless
Successfully created context "rootless"
$ docker context use rootless
rootless
Current context is now "rootless"
$ docker run -d -p 8080:80 nginx
ベストプラクティス¶
Rootless Docker in Docker¶
「完全な root」で動作する Docker 内において Rootless Docker を実行するには、 docker:<version>-dind
イメージの代わりに docker:<version>-dind-rootless
イメージを使います。
$ docker run -d --name dind-rootless --privileged docker:19.03-dind-rootless --experimental
docker:<version>-dind-rootless
イメージは非 root ユーザ(UID 1000)として実行します。しかしながら、 seccomp、AppArmor、mount mask を無効化するために --privileged
が必要です。
Docker API ソケットを TCP を通して公開¶
Docker API ソケットを TCP を通して公開するには、 dockerd-rootless.sh
の起動に DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS="-p 0.0.0.0:2376:2376/tcp"
の指定が必要です。
$ DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS="-p 0.0.0.0:2376:2376/tcp" \
dockerd-rootless.sh --experimental \
-H tcp://0.0.0.0:2376 \
--tlsverify --tlscacert=ca.pem --tlscert=cert.pem --tlskey=key.pem
Docker API ソケットを SSH を通して公開¶
Docker API ソケットを SSH を通して公開するには、リモートホスト上で $DOCKER_HOST`
の指定が必要です。
$ ssh -l <REMOTEUSER> <REMOTEHOST> 'echo $DOCKER_HOST'
unix:///run/user/1001/docker.sock
$ docker -H ssh://<REMOTEUSER>@<REMOTEHOST> run ...
ping パケットのルーティング¶
いくつかのディストリビューションで、デフォルトでは ping
が動作しません。
/etc/sysctl.conf
(または /etc/sysctl.d
)に net.ipv4.ping_group_range = 0 2147483647
を追加し、 ping
の使用を許可するために sudo sysctl --system
を実行します。
特権ポートの公開¶
特権ポート(1024 以下)を公開するには、 rootlesskit
バイナリに対して CAP_NET_BIND_SERVICE
を設定します。
$ sudo setcap cap_net_bind_service=ep $HOME/bin/rootlesskit
/etc/sysctl.conf
(または /etc/sysctl.d
)に net.ipv4.ip_unprivileged_port_start=0
を追加し、 sudo sysctl --system
を実行します。
リソースの制限¶
Docker 19.03 では、rootless モードでは cgroups に関連する docker run
のフラグ、 --cpus
、 --memory
、 -pids-limit
を無視します。
ただし従来からの ulimit
や cpulimit は利用できます。
これらはコンテナー単位というよりもプロセス単位での処理動作を規定するものですが、コンテナー・プロセスごとに無効化することができます。
例:
- CPU の使用を 0.5 コアに制限するには(
docker run --cpus 0.5
のように):docker run --cpus 0.5): docker run <イメージ> cpulimit --limit=50 --include-children <コマンド>
- 最大 VSZ を 64MiB に制限するには(
docker run --memory 64m
のように):docker run <イメージ> sh -c "ulimit -v 65536; <コマンド>"
- UID 2000 の名前空間ごとに最大 100 プロセスに制限する(
docker run --pids-limit=100
のように):docker run --user 2000 --ulimit nproc=100 <IMAGE> <コマンド>
ネットワーク・スタックの変更¶
デフォルトでは、 dockerd-rootless.sh
は slirp4netns (インストール済みであれば)または VPNKit をネットワーク・スタックとして使います。
これらのネットワーク・スタックはユーザ空間内で実行されるため、パフォーマンスのオーバヘッドを招く場合があります。詳しい情報は RootlessKit ドキュメント(英語) をご覧ください。
最適な性能を得るために、代わりに lxc-user-nic
を利用することもできます。 lxc-user-nic
を使うには、 /etc/lxc/lxc-usernet を編集し、 $DOCKERD_ROOTLESS_ROOTLESSKIT_NET=lxc-user-nic
を指定します。
トラブルシューティング¶
Docker デーモン起動時のエラー¶
[rootlesskit:parent] error: failed to start the child: fork/exec /proc/self/exe: operation not permitted
このエラーが発生するのは、ほとんどが /proc/sys/kernel/unprivileged_userns_clone
の値を 0 に設定している時です。
$ cat /proc/sys/kernel/unprivileged_userns_clone
0
この問題を解決するには、 /etc/sysctl.conf
(あるいは /etc/sysctl.d
)に kernel.unprivileged_userns_clone=1
を追加し、 sudo sysctl --system
を実行します。
[rootlesskit:parent] error: failed to start the child: fork/exec /proc/self/exe: no space left on device
このエラーが発生するのは、ほとんどが /proc/sys/user/max_user_namespaces
が小さすぎる時です。
$ cat /proc/sys/user/max_user_namespaces
0
この問題を解決するには、 /etc/sysctl.conf
(あるいは /etc/sysctl.d
)に user.max_user_namespaces=28633
を追加し、 sudo sysctl --system
を実行します。
[rootlesskit:parent] error: failed to setup UID/GID map: failed to compute uid/gid map: No subuid ranges found for user 1001 (“testuser”)
XDG_RUNTIME_DIR を取得できない
このエラーが発生するのは、 $XDG_RUNTIME_DIR
の設定がない時です。
systemd がないホスト上では、ディレクトリの作成とパスの設定が必要になります。
$ export XDG_RUNTIME_DIR=$HOME/.docker/xrd
$ rm -rf $XDG_RUNTIME_DIR
$ mkdir -p $XDG_RUNTIME_DIR
$ dockerd-rootless.sh --experimental
注釈
このディレクトリは、ログアウト時に毎回削除する必要があります。
systemd ホスト上では、ホストへのログインに pam_systemd
を使います(以下をご覧ください)。この値は自動的に /run/user/$UID
に指定され、ログアウト後にクリーンアップされます。
systemctl --user がエラー「Failed to connect to bus: No such file or directory」で失敗
このエラーが発生するのは、ほとんどが root ユーザから非 root ユーザに sudo
で切り替える時です。
# sudo -iu testuser
$ systemctl --user start docker
Failed to connect to bus: No such file or directory
sudo -iu <ユーザ名>
の代わりに、 pam_systemd
を使ってログインする必要があります。たとえば、
- グラフィック・コンソールを通してログイン
ssh <ユーザ名>@localhost
machinectl shell <ユーザ名>@
デーモンが自動的に起動しない
デーモンを自動的に起動するには sudo loginctl enable-linger $(whoami)
を実行する必要があります。 使い方 をご覧ください。
dockerd がエラー「rootless mode is supported only when running in experimental mode」
このエラーが発生するのは、 --experimental
を付けずにデーモンを起動した時です。 使い方 をご覧ください。
docker pull
errors¶
docker: failed to register layer: Error processing tar file(exit status 1): lchown <FILE>: invalid argument
このエラーが発生するのは、 /etc/subuid
か /etc/subgid
で利用可能なエントリ数が十分ではない時です。エントリに必要な数は、イメージによって様々です。しかしながら、ほとんどのイメージでは 65,536 で十分です。 事前準備 をご覧ください。
docker run
errors¶
--cpus、 --memory 、 --pids-limit が無視される
この挙動は Docker 19.03 で想定された挙動です。詳しい情報は リソースの制限 をご覧ください。
デーモンからのエラー応答「groups: cgroup mountpoint does not exist: unknown.」
このエラーが発生するのは、ほとんどが cgroup v2 がホスト上で動作している時です。ホストが cgroup v1 を使うように切り替えるための情報は、 Fedora 31 以降 のセクションをご覧ください。
ネットワークのエラー¶
docker run -p で「cannot expose privileged port」のエラー
docker run -p
はホスト側のポートとして特権ポート(1024未満)を指定するとエラーになります。
$ docker run -p 80:80 nginx:alpine
docker: Error response from daemon: driver failed programming external connectivity on endpoint focused_swanson (9e2e139a9d8fc92b37c36edfa6214a6e986fa2028c0cc359812f685173fa6df7): Error starting userland proxy: error while calling PortManager.AddPort(): cannot expose privileged port 80, you might need to add "net.ipv4.ip_unprivileged_port_start=0" (currently 1024) to /etc/sysctl.conf, or set CAP_NET_BIND_SERVICE on rootlesskit binary, or choose a larger port number (>= 1024): listen tcp 0.0.0.0:80: bind: permission denied.
このエラーに遭遇したら、代わりに特権のないポートの利用を検討ください。たとえば 80 の代わりに 8080 を使います。
$ docker run -p 8080:80 nginx:alpine
特権ポートの公開を許可するには、 特権ポートの公開 をご覧ください。
ping できません
/proc/sys/net/ipv4/ping_group_range
が 1 0
の設定であれば、 ping は機能しません。
$ cat /proc/sys/net/ipv4/ping_group_range
1 0
詳細は ping パケットのルーティング をご覧ください。
docker inspect で表示された IPAddress に到達できません(unreachable)
これは予想されうる挙動で、デーモンは RootlessKit のネットワーク名前空間内にいるからです。かわりに docker run -p
を使います。
--net=host がホスト・ネットワーク名前空間上でポートをリッスンしません
これは予想されうる挙動で、デーモンは RootlessKit のネットワーク名前空間内にいるからです。かわりに docker run -p
を使います。
参考
- Run the Docker daemon as a non-root user (Rootless mode)
- https://docs.docker.com/engine/security/rootless/