以非root用户身份运行Docker守护程序(无根模式)
预计阅读时间:17分钟
无根模式允许以非root用户身份运行Docker守护程序和容器,以减轻守护程序和容器运行时中的潜在漏洞。
只要满足先决条件,即使在Docker守护程序安装期间,无根模式也不需要root特权。
无根模式是Docker Engine v19.03中引入的一项实验功能。无根模式从Docker Engine v20.10的实验中毕业。
这个怎么运作
无根模式在用户名称空间内执行Docker守护程序和容器。这与userns-remap
mode非常相似,除了模式之外,userns-remap
守护进程本身以root特权运行,而在无根模式下,守护程序和容器都在没有root特权的情况下运行。
无根模式不使用具有SETUID
位或文件功能的二进制文件,除了newuidmap
和newgidmap
,它们是允许在用户名称空间中使用多个UID / GID所必需的。
先决条件
-
您必须在主机上安装
newuidmap
和newgidmap
。这些命令由uidmap
大多数发行版的软件包提供。 -
/etc/subuid
且/etc/subgid
应至少包含该用户的65,536个从属UID / GID。在以下示例中,用户testuser
具有65,536个从属UID / GID(231072-296607)。
$ id -u
1001
$ whoami
testuser
$ grep ^$(whoami): /etc/subuid
testuser:231072:65536
$ grep ^$(whoami): /etc/subgid
testuser:231072:65536
特定于发行版的提示
注意:我们建议您使用Ubuntu内核。
-
无需准备。
-
overlay2
默认情况下启用存储驱动程序(特定于Ubuntu的内核补丁)。 -
已知可在Ubuntu 16.04、18.04和20.04上运行。
-
添加
kernel.unprivileged_userns_clone=1
到/etc/sysctl.conf
(或/etc/sysctl.d
)并运行sudo sysctl --system
。 -
要使用
overlay2
存储驱动程序(推荐),请运行sudo modprobe overlay permit_mounts_in_userns=1
(Debian 10中引入了Debian特定的内核补丁)。将配置添加到/etc/modprobe.d
持久性。
-
fuse-overlayfs
建议安装。运行sudo pacman -S fuse-overlayfs
。 -
添加
kernel.unprivileged_userns_clone=1
到/etc/sysctl.conf
(或/etc/sysctl.d
)并运行sudo sysctl --system
-
fuse-overlayfs
建议安装。运行sudo zypper install -y fuse-overlayfs
。 -
sudo modprobe ip_tables iptable_mangle iptable_nat iptable_filter
是必须的。根据配置,其他发行版可能也需要这样做。 -
已知可以在openSUSE 15上工作。
-
fuse-overlayfs
建议安装。运行sudo dnf install -y fuse-overlayfs
。 -
您可能需要
sudo dnf install -y iptables
。 -
启用SELinux后,您可能会遇到
can't open lock file /run/xtables.lock: Permission denied
错误。解决此问题的方法是sudo dnf install -y policycoreutils-python-utils && sudo semanage permissive -a iptables_t
。此问题已在moby / moby#41230中进行了跟踪。 -
已知可在CentOS 8和Fedora 33上工作。
-
添加
user.max_user_namespaces=28633
到/etc/sysctl.conf
(或/etc/sysctl.d
)并运行sudo sysctl --system
。 -
systemctl --user
默认情况下不起作用。dockerd-rootless.sh
不使用systemd直接运行。
已知限制
- 仅支持以下存储驱动程序:
overlay2
(仅在以5.11或更高版本的内核,Ubuntu风格的内核或Debian风格的内核运行时)fuse-overlayfs
(仅在与内核4.18或更高版本一起运行且fuse-overlayfs
已安装的情况下)btrfs
(仅在使用内核4.18或更高版本运行,或~/.local/share/docker
通过user_subvol_rm_allowed
mount选项安装时)vfs
- 仅当与cgroup v2和systemd一起运行时,才支持Cgroup。请参阅限制资源。
- 不支持以下功能:
- AppArmor
- 检查站
- 叠加网络
- 暴露SCTP端口
- 要使用该
ping
命令,请参阅路由ping数据包。 - 要公开特权TCP / UDP端口(<1024),请参阅公开特权端口。
IPAddress
显示在中,docker inspect
并在RootlessKit的网络名称空间中命名。这意味着如果不nsenter
进入网络名称空间,则无法从主机访问IP地址。- 主机网络(
docker run --net=host
)也位于RootlessKit中。
安装
笔记
如果系统范围的Docker守护程序已在运行,请考虑将其禁用:
$ sudo systemctl disable --now docker.service docker.socket
如果您安装了带有RPM / DEB软件包的Docker 20.10或更高版本,则应在dockerd-rootless-setuptool.sh
中/usr/bin
。
dockerd-rootless-setuptool.sh install
以非root用户身份运行以设置守护程序:
$ dockerd-rootless-setuptool.sh install
[INFO] Creating /home/testuser/.config/systemd/user/docker.service
...
[INFO] Installed docker.service successfully.
[INFO] To control docker.service, run: `systemctl --user (start|stop|restart) docker.service`
[INFO] To run docker.service on system startup, run: `sudo loginctl enable-linger testuser`
[INFO] Make sure the following environment variables are set (or add them to ~/.bashrc):
export PATH=/usr/bin:$PATH
export DOCKER_HOST=unix:///run/user/1000/docker.sock
如果dockerd-rootless-setuptool.sh
不存在,则可能需要docker-ce-rootless-extras
手动安装软件包,例如,
$ sudo apt-get install -y docker-ce-rootless-extras
如果你没有运行包管理器一样的权限apt-get
和dnf
,考虑使用提供的安装脚本在https://get.docker.com/rootless。
$ curl -fsSL https://get.docker.com/rootless | sh
...
[INFO] Creating /home/testuser/.config/systemd/user/docker.service
...
[INFO] Installed docker.service successfully.
[INFO] To control docker.service, run: `systemctl --user (start|stop|restart) docker.service`
[INFO] To run docker.service on system startup, run: `sudo loginctl enable-linger testuser`
[INFO] Make sure the following environment variables are set (or add them to ~/.bashrc):
export PATH=/home/testuser/bin:$PATH
export DOCKER_HOST=unix:///run/user/1000/docker.sock
二进制文件将安装在~/bin
。
如果遇到错误,请参阅故障排除。
卸载
要删除Docker守护程序的systemd服务,请运行dockerd-rootless-setuptool.sh uninstall
:
$ dockerd-rootless-setuptool.sh uninstall
+ systemctl --user stop docker.service
+ systemctl --user disable docker.service
Removed /home/testuser/.config/systemd/user/default.target.wants/docker.service.
[INFO] Uninstalled docker.service
[INFO] This uninstallation tool does NOT remove Docker binaries and data.
[INFO] To remove data, run: `/usr/bin/rootlesskit rm -rf /home/testuser/.local/share/docker`
要删除数据目录,请运行rootlesskit rm -rf ~/.local/share/docker
。
要删除二进制文件,docker-ce-rootless-extras
请在软件包管理器中安装了Docker的情况下删除软件包。如果您使用https://get.docker.com/rootless(不带软件包安装)安装了Docker,请删除以下二进制文件~/bin
:
$ cd ~/bin
$ rm -f containerd containerd-shim containerd-shim-runc-v2 ctr docker docker-init docker-proxy dockerd dockerd-rootless-setuptool.sh dockerd-rootless.sh rootlesskit rootlesskit-docker-proxy runc vpnkit
用法
守护进程
systemd单位文件安装为 ~/.config/systemd/user/docker.service
。
使用systemctl --user
管理守护程序的生命周期:
$ systemctl --user start docker
要在系统启动时启动守护程序,请启用systemd服务并持续进行以下操作:
$ systemctl --user enable docker
$ sudo loginctl enable-linger $(whoami)
/etc/systemd/system/docker.service
即使使用User=
指令,也不支持将Rootless Docker作为全系统范围的服务()启动。
要在不使用systemd的情况下直接运行守护程序,您需要运行dockerd-rootless.sh
而不是dockerd
。
必须设置以下环境变量:
$HOME
:主目录$XDG_RUNTIME_DIR
:临时目录,只有预期的用户可以访问,例如~/.docker/run
。该目录应在每次主机关闭时删除。该目录可以位于tmpfs上,但是不应位于之下/tmp
。在此目录下/tmp
可能容易受到TOCTOU攻击。
关于目录路径的说明:
- 套接字路径
$XDG_RUNTIME_DIR/docker.sock
默认设置为。$XDG_RUNTIME_DIR
通常设置为/run/user/$UID
。 - 数据目录
~/.local/share/docker
默认设置为。数据目录不应位于NFS上。 - 守护程序配置目录
~/.config/docker
默认设置为。此目录~/.docker
与客户端使用的目录不同。
客户
您需要明确指定套接字路径或CLI上下文。
要指定套接字路径,请使用$DOCKER_HOST
:
$ export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/docker.sock
$ docker run -d -p 8080:80 nginx
要指定CLI上下文,请使用docker context
:
$ docker context use rootless
rootless
Current context is now "rootless"
$ docker run -d -p 8080:80 nginx
最佳实务
Docker中的无根Docker
要在“根” Docker中运行无根Docker,请使用docker:<version>-dind-rootless
映像代替docker:<version>-dind
。
$ docker run -d --name dind-rootless --privileged docker:20.10-dind-rootless
该docker:<version>-dind-rootless
映像以非root用户身份运行(UID 1000)。但是,--privileged
禁用seccomp,AppArmor和安装掩码是必需的。
通过TCP公开Docker API套接字
为了揭露通过TCP泊坞窗API插座,你需要推出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 \
-H tcp://0.0.0.0:2376 \
--tlsverify --tlscacert=ca.pem --tlscert=cert.pem --tlskey=key.pem
通过SSH公开Docker API套接字
要通过SSH公开Docker API套接字,您需要确保$DOCKER_HOST
已在远程主机上设置了该套接字。
$ ssh -l <REMOTEUSER> <REMOTEHOST> 'echo $DOCKER_HOST'
unix:///run/user/1001/docker.sock
$ docker -H ssh://<REMOTEUSER>@<REMOTEHOST> run ...
路由ping数据包
在某些发行版中,ping
默认情况下不起作用。
添加net.ipv4.ping_group_range = 0 2147483647
到/etc/sysctl.conf
(或
/etc/sysctl.d
)并运行sudo sysctl --system
以允许使用ping
。
公开特权端口
要公开特权端口(<1024),请设置CAP_NET_BIND_SERVICE
为rootlesskit
二进制。
$ sudo setcap cap_net_bind_service=ep $HOME/bin/rootlesskit
或添加net.ipv4.ip_unprivileged_port_start=0
到/etc/sysctl.conf
(或
/etc/sysctl.d
)并运行sudo sysctl --system
。
限制资源
仅当与cgroup v2和systemd一起运行时,才支持使用与cgroup相关的docker run
标志(例如--cpus
,)限制资源。请参阅更改cgroup版本以启用cgroup v2。--memory
--pids-limit
如果docker info
显示none
为Cgroup Driver
,则不满足条件。当不满足这些条件时,无根模式将忽略与cgroup相关的docker run
标志。有关变通办法,请参阅在不使用cgroup的情况下限制资源。
如果docker info
显示systemd
为Cgroup Driver
,则满足条件。但是,通常,默认情况下,仅memory
和pids
控制器被委派给非root用户。
$ cat /sys/fs/cgroup/user.slice/user-$(id -u).slice/user@$(id -u).service/cgroup.controllers
memory pids
要允许委派所有控制器,您需要按以下方式更改systemd配置:
# mkdir -p /etc/systemd/system/user@.service.d
# cat > /etc/systemd/system/user@.service.d/delegate.conf << EOF
[Service]
Delegate=cpu cpuset io memory pids
EOF
# systemctl daemon-reload
笔记
委派
cpuset
需要systemd 244或更高版本。
在没有cgroup的情况下限制资源
即使cgroup不可用,您仍然可以使用传统的ulimit
and cpulimit
,尽管它们以进程粒度而不是容器粒度运行,并且可以被容器进程任意禁用。
例如:
- 要将CPU使用率限制为0.5个内核(类似于
docker run --cpus 0.5
):docker run <IMAGE> cpulimit --limit=50 --include-children <COMMAND>
-
要将最大VSZ限制为64MiB(类似于
docker run --memory 64m
):docker run <IMAGE> sh -c "ulimit -v 65536; <COMMAND>"
- 要将每个命名空间UID 2000的最大进程数限制为100(类似于
docker run --pids-limit=100
),请执行以下操作:docker run --user 2000 --ulimit nproc=100 <IMAGE> <COMMAND>
故障排除
启动Docker守护程序时发生错误
[rootlesskit:parent]错误:无法启动子级:fork / exec / proc / self / exe:不允许操作
的错误主要是在的值/proc/sys/kernel/unprivileged_userns_clone
设置为0时发生的:
$ cat /proc/sys/kernel/unprivileged_userns_clone
0
要解决此问题,请添加 kernel.unprivileged_userns_clone=1
至
/etc/sysctl.conf
(或/etc/sysctl.d
)并运行sudo sysctl --system
。
[rootlesskit:parent]错误:无法启动子级:fork / exec / proc / self / exe:设备上没有剩余空间
的错误主要是由于的值/proc/sys/user/max_user_namespaces
太小:
$ cat /proc/sys/user/max_user_namespaces
0
要解决此问题,请添加 user.max_user_namespaces=28633
至
/etc/sysctl.conf
(或/etc/sysctl.d
)并运行sudo sysctl --system
。
[rootlesskit:parent]错误:无法设置UID / GID映射:未能计算uid / gid映射:未找到用户1001(“ testuser”)的子uid范围
未配置/etc/subuid
和时,/etc/subgid
会发生此错误。请参阅先决条件。
无法获得XDG_RUNTIME_DIR
$XDG_RUNTIME_DIR
未设置时发生此错误。
在非系统主机上,您需要创建一个目录,然后设置路径:
$ export XDG_RUNTIME_DIR=$HOME/.docker/xrd
$ rm -rf $XDG_RUNTIME_DIR
$ mkdir -p $XDG_RUNTIME_DIR
$ dockerd-rootless.sh
注意:每次注销时必须删除目录。
在systemd主机上,使用登录到主机pam_systemd
(请参见下文)。该值将自动设置为,/run/user/$UID
并在每次注销时清除。
systemctl --user
失败并显示“无法连接到总线:没有这样的文件或目录”
当您使用以下命令从root用户切换到非root用户时,通常会发生此错误sudo
:
# sudo -iu testuser
$ systemctl --user start docker
Failed to connect to bus: No such file or directory
取而代之的是sudo -iu <USERNAME>
,您需要使用登录pam_systemd
。例如:
- 通过图形控制台登录
ssh <USERNAME>@localhost
machinectl shell <USERNAME>@
守护程序不会自动启动
您需要sudo loginctl enable-linger $(whoami)
启用守护程序以自动启动。请参阅用法。
iptables失败:iptables -t nat -N DOCKER:致命:无法打开锁定文件/run/xtables.lock:权限被拒绝
在主机上启用SELinux时可能会发生此错误。
已知的解决方法是运行以下命令来禁用SELinux iptables
:
$ sudo dnf install -y policycoreutils-python-utils && sudo semanage permissive -a iptables_t
此问题已在moby / moby#41230中进行了跟踪。
docker pull
错误
泊坞窗:无法注册层:错误处理tar文件(退出状态1):lchown <FILE>:无效参数
当可用条目数不足/etc/subuid
或
/etc/subgid
不足时,会发生此错误。所需的条目数量因图像而异。但是,对于大多数图像来说,有65,536个条目就足够了。请参阅
先决条件。
泊坞窗:无法注册层:ApplyLayer退出状态1 stdout:stderr:lchown <FILE>:不允许操作
大多数~/.local/share/docker
位于NFS上时,会发生此错误。
一种解决方法是按以下方式指定非NFSdata-root
目录~/.config/docker/daemon.json
:
{"data-root":"/somewhere-out-of-nfs"}
docker run
错误
--cpus
,--memory
以及--pids-limit
被忽略
这是cgroup v1模式下的预期行为。要使用这些标志,需要将主机配置为启用cgroup v2。有关更多信息,请参见限制资源。
网路错误
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.
遇到此错误时,请考虑使用非特权端口。例如,用8080代替80。
$ docker run -p 8080:80 nginx:alpine
要允许公开特权端口,请参阅公开特权端口。
ping不起作用
当平不起作用/proc/sys/net/ipv4/ping_group_range
设置为1 0
:
$ cat /proc/sys/net/ipv4/ping_group_range
1 0
有关详细信息,请参阅路由ping数据包。
IPAddress
中显示的docker inspect
内容无法访问
这是预期的行为,因为该守护进程在RootlessKit的网络名称空间中被命名。使用docker run -p
代替。
--net=host
不侦听主机网络名称空间上的端口
这是预期的行为,因为该守护进程在RootlessKit的网络名称空间中被命名。使用docker run -p
代替。
网络速度慢
如果安装了slirp4netns v0.4.0或更高版本,则无根模式的Docker将slirp4netns用作默认网络堆栈。如果未安装slirp4netns,则Docker将退回到VPNKit。
安装slirp4netns可以提高网络吞吐量。有关基准测试结果,请参见RootlessKit文档。
同样,更改MTU值可以提高吞吐量。可以通过添加Environment="DOCKERD_ROOTLESS_ROOTLESSKIT_MTU=<INTEGER>"
到MTU值~/.config/systemd/user/docker.service
然后运行来指定MTU值systemctl --user daemon-reload
。
docker run -p
不传播源IP地址
这是因为默认情况下,具有无根模式的Docker使用RootlessKit的内置端口驱动程序。
可以通过添加Environment="DOCKERD_ROOTLESS_ROOTLESSKIT_PORT_DRIVER=slirp4netns"
到源IP地址~/.config/systemd/user/docker.service
然后运行来传播源IP地址systemctl --user daemon-reload
。
请注意,此配置会降低吞吐量。有关基准测试结果,请参见RootlessKit文档。
调试技巧
进入dockerd
名称空间
该dockerd-rootless.sh
脚本dockerd
在其自己的用户,安装和网络名称空间中执行。
为了进行调试,您可以通过运行输入名称空间
nsenter -U --preserve-credentials -n -m -t $(cat $XDG_RUNTIME_DIR/docker.pid)
。