装完Docker,敲下第一条docker run,容器就跑起来了。大多数人到这一步就停了,不会去想这个容器在宿主机上到底以什么身份在跑。

打开网易新闻 查看精彩图片

答案有点扎心:默认情况下,是root。

Docker至今仍是家庭实验室玩家最常用的容器运行时,原因很实在——大量开源工具直接提供现成镜像,配一份Compose配置就能部署,管理命令也足够简单,新手也能很快上手。但它的默认权限设计,是这套便利背后最容易被忽略的一环。

root身份是怎么来的

熟悉Linux的人都知道,系统里有个叫root的管理员账户,权限不受限制。第一次安装Docker时,底层的守护进程dockerd会绑定到这个root用户拥有的Unix套接字上。

除非你在容器的Dockerfile里改动相关参数,否则由dockerd拉起的运行环境同样带着root权限。问题就出在这里。

虚拟机会模拟一套全新的内核和操作系统,容器不一样——它和宿主机共用同一个内核。再叠加dockerd对系统拥有完整的root级访问权限,Docker这种rootful设计的安全隐患就被放大了。

举个具体场景:某个容器镜像在最近一次更新中被塞进了恶意代码。在默认的Docker配置下,这段代码要突破容器的边界、对宿主机造成破坏,会容易得多。

需要提防的也不只是带毒的镜像。如果你不小心把某个系统目录挂载进容器,这个开源服务就获得了该目录下关键文件的完整超级用户权限。一次搞砸的容器实验,就足以让宿主机变得无法访问。

两种收紧权限的思路

让Docker实验更安全,技术上不止一条路。

  • 修改Dockerfile,强制容器以非root权限运行
  • 对使用预构建镜像的人,可以在docker run命令或compose.yml文件里手动覆盖用户

但这两招的共同毛病是:每加一个容器,就得重复一遍同样的步骤,容器一多就很难坚持。

更彻底的做法是启用rootless模式,它同时解决了dockerd守护进程以超级用户身份运行的问题。容器和守护进程都被锁在一个非特权用户之下,即便Docker环境出了故障,也拿不到宿主机的完整root权限。

启用只需要一次,之后新建的容器都会以rootless权限运行:

  • sudo apt install uidmap -y
  • curl -fsSL https://get.docker.com/rootless | sh
  • export PATH=/usr/bin:$PATH
  • export DOCKER_HOST=unix:///run/user/1000/docker.sock

rootless不是没有代价

在Docker工作站上坚持用rootless容器,大部分开源服务都能正常跑。但非root权限确实会带来一些小麻烦。

最直接的一条:rootless模式下无法为容器映射特权端口,也就是1024以下的端口全部对容器环境关闭。对常见的生产力应用、归档工具或备份服务来说不算大事,网络类工具就完全是另一回事了。

以Pi-hole为例,它的核心DNS服务需要访问53端口。当然,改几个配置文件仍然能在rootless环境里部署它,但绝不像在默认配置下敲一条docker run那么省事。

网络之外还有存储层面的约束,尤其是实验涉及OverlayFS挂载时。同样,通过cgroups v2限制容器资源会变得麻烦一些,自定义AppArmor配置文件也是同理。

所以取舍很清楚:如果你折腾的主要是生产力工具、各种冷门容器镜像和数据归档工具,在Docker上开启rootless模式是个值得做的选择。默认的root权限省事,但省下来的这部分,迟早要在别的地方还。