前言
最近我连续几台服务器被挂了挖矿木马,CPU、带宽、磁盘 IO 被拉满,服务器直接卡死无法连接。
排查后发现,核心诱因是 Docker 权限过高 + 服务漏洞暴露,导致攻击者通过容器突破权限控制。

PS:本来想写一篇文章介绍排查过程的,不过还是嫌麻烦没写,放在本文一起讲吧~
重装系统后,我在部署 Docker 时注意到官方提示的 Rootless(无根)模式 —— 这一模式能从根本上降低容器逃逸风险,遂深入研究并落地配置,现将完整过程整理分享,希望能帮到同样关注 Docker 安全的开发者。
Rootless 模式是什么
普通情况下,Docker 守护进程(dockerd)是用 root 权限运行的,哪怕你用普通用户执行 docker run,底层还是 root 权限,这有安全风险(比如容器逃逸可能拿到主机 root)。
Rootless 模式让 Docker 守护进程以普通用户权限运行,哪怕容器出问题,也无法获取主机的 root 权限,安全性大幅提升。
有好处自然有代价,rootless 的代价是配置复杂,且部分功能受限(比如无法端口映射 < 1024)。
不过没关系,这些也可以通过配置解决。先从安装开始吧。
安装docker
本来安装是很简单的,不过加个定语:在国内网络环境,那就非常复杂了。
本文介绍最简单的安装方式:使用docker官方脚本+清华镜像。
export DOWNLOAD_URL="https://mirrors.tuna.tsinghua.edu.cn/docker-ce"
# 如您使用 curl
curl -fsSL https://ghfast.top/https://raw.githubusercontent.com/docker/docker-install/master/install.sh | sh
# 如您使用 wget
wget -O- https://ghfast.top/https://raw.githubusercontent.com/docker/docker-install/master/install.sh | sh
注意 raw.githubusercontent.com 这个域名也是无法访问的,可以使用 ghproxy 来加速。
安装完成提示
安装完成会有一个提示,这也是开启 Rootless 模式的关键入口:
================================================================================
To run Docker as a non-privileged user, consider setting up the
Docker daemon in rootless mode for your user:
dockerd-rootless-setuptool.sh install
Visit https://docs.docker.com/go/rootless/ to learn about rootless mode.
To run the Docker daemon as a fully privileged service, but granting non-root
users access, refer to https://docs.docker.com/go/daemon-access/
WARNING: Access to the remote API on a privileged Docker daemon is equivalent
to root access on the host. Refer to the 'Docker daemon attack surface'
documentation for details: https://docs.docker.com/go/attack-surface/
================================================================================
我就是在这里开始使用 rootless 模式的。
提示核心解读:
- 推荐通过
dockerd-rootless-setuptool.sh install开启 Rootless 模式,让普通用户无 root 权限运行 Docker; - 若坚持 root 权限运行 Docker,可参考文档给普通用户授权(如加入 docker 组),但风险更高;
- 重点警告:暴露 Docker 远程 API(如 2375 端口)= 直接开放主机 root 权限,这是服务器被入侵的高频诱因!
安装必要依赖
我直接运行 dockerd-rootless-setuptool.sh install 的时候,提示要缺乏依赖
$ dockerd-rootless-setuptool.sh install
[ERROR] Missing system requirements. Run the following commands to
[ERROR] install the requirements and run this tool again.
########## BEGIN ##########
sudo sh -eux <<EOF
# Install newuidmap & newgidmap binaries
apt-get install -y uidmap
EOF
########## END ##########
输入提示的这行命令:
sudo sh -eux <<EOF
# Install newuidmap & newgidmap binaries
apt-get install -y uidmap
EOF
安装完成后,再次执行 dockerd-rootless-setuptool.sh install
以后操作 docker 服务,要加上 --user
systemctl --user start docker.service
配置
rootless 模式下:
- 所有 Docker 命令都要在安装 Rootless 的普通用户下执行(不要用 root);
- 如果重启服务器后 Docker 没自动启动,执行:
systemctl --user enable --now docker; - 数据备份要找
~/.local/share/docker目录(而非/var/lib/docker)。
镜像加速器
默认情况下,Rootless Docker 的配置文件存放在当前用户的 XDG 配置目录 下,路径是:~/.config/docker/daemon.json
# 先创建目录(如果不存在)
mkdir -p ~/.config/docker
# 编辑配置文件(用 nano 或 vim 都可以)
nano ~/.config/docker/daemon.json
配置加速器
{
"registry-mirrors": ["https://你的阿里云镜像加速地址.mirror.aliyuncs.com"],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "3"
}
}
注意:之前大部分稳定好用的加速器都停止服务了,现在就没法推荐啥,大家各凭本事吧。
解决 “无法绑定 1-1023 端口” 的问题
需要给当前用户 “绑定低端口” 的权限:
# 给当前用户授权绑定 1-1023 端口(仅对当前会话生效)
sudo sysctl net.ipv4.ip_unprivileged_port_start=0
# 永久生效(重启后也有效)
echo "net.ipv4.ip_unprivileged_port_start=0" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p # 立即生效
执行后,你就能正常映射 80、443 等端口了。
重启生效
Rootless 模式的 Docker 重启命令和系统级不同,执行:
# 重启当前用户的 Docker 服务
systemctl --user restart docker
# 验证配置是否生效
docker info
# 能在 "Registry Mirrors" 部分看到你配置的镜像加速地址就是成功的
正常输出示例:
Rootless: true
Registry Mirrors:
https://你的阿里云镜像加速地址.mirror.aliyuncs.com/
Rootless 模式不支持的配置项
部分系统级配置在 Rootless 下无效(因为没有 root 权限),比如:
iptables: false(网络规则由 slirp4netns 管理,而非 iptables);storage-driver: overlay2(默认已启用,无需手动配置);- 远程 API 相关配置(如
hosts: ["tcp://0.0.0.0:2375"],Rootless 下不建议开启)。 - 任何涉及系统级目录(如
/var/run/docker.sock)的配置。
volume问题
切换到 rootless 之后,我还发现了 swag 的 config 无法读写了。
swag 的 compose.yaml 配置是这样:
services:
swag:
image: linuxserver/swag
container_name: swag
cap_add:
- NET_ADMIN
environment:
- PUID=1000
- PGID=1000
volumes:
- ./config:/config
rootless Docker 里,容器内的 UID=1000 ≠ 宿主机的 UID=1000
所以 SWAG 在容器里 chown 了 /config,宿主机看到的是一个“映射后的陌生 UID(100999)”
解决方法
rootless 官方推荐使用命名卷,但我要经常修改 config 里的文件,这个肯定不现实。
那么还有一个方法,使用 ACL 放行。
先安装相关工具:
sudo apt update
sudo apt install -y acl
在 swag 目录下执行
setfacl -m u:ecs-user:rwx config
setfacl -R -m u:ecs-user:rwx config
setfacl -d -m u:ecs-user:rwx config
验证:
getfacl config | sed -n '1,20p'
看到类似:
user::rwx
user:ecs-user:rwx
group::r-x
mask::rwx
other::r-x
小结
Rootless 模式虽比普通 Docker 配置稍繁琐,但能从根本上降低容器逃逸风险,尤其适合对外提供服务的生产服务器。核心总结:
- 安装:结合清华镜像源解决国内下载问题,优先用普通用户安装 Rootless 模式;
- 权限:禁止暴露 Docker 远程 API,给普通用户授权低端口绑定权限即可满足日常使用;
- 配置:牢记 Rootless 模式的配置文件、数据目录均在用户目录下,与系统级 Docker 区分开;
- 安全:即便开启 Rootless,运行容器时仍需注意服务安全(如 Redis 加密码、安全组限制端口访问)。
此次踩坑让我深刻意识到:服务器安全无小事,哪怕是 Docker 这样的基础工具,也需从权限层面做好最小化管控,才能避免被挖矿木马等恶意程序趁虚而入。
程序设计实验室
微信公众号