容器内的root用户映射到宿主机也是root用户。这就意味着,容器应用提升到root权限后,也相当于获得了宿主机的root权限。
为了加强用户权限管控,可以为Docker开启User Namespaces机制。
通过 user namespace 技术,可以把容器中的 root 用户映射为宿主机的一个普通用户(只有普通权限的用户)。应用在容器中以为自己是以root身份运行,具有root权限。实际上它在宿主机上使用的只是普通用户身份,拥有有限的权限。
User Namespaces和subuid
User Namespaces (用户命名空间)是在Linux内核3.8版本引入,作为Namespaces安全机制的一部分。这也意味着目前流行的Linux和Docker版本都是支持的。
这里,首先需要理解subuid和subgid的概念。
Linux系统是使用uid来标识一个用户。root用户的uid为0。
Linux可以通过/etc/subuid文件为用户配置一些从属用户。默认情况下,用户的subuid从100000开始,每组65536个。这里我已经创建了一个用户u,因为它前面还有几个用户,所以其subuid是从296608开始的65536个。
同理,Linux系统也可以通过/etc/subgid配置从属组。
再看看subuid在用户命名空间中的意义。
初始状态下,Linux使用的是一个默认用户空间。为用户指定了从属用户,在对应的子用户空间中,子控件的用户可以被映射到这些从属用户。
这样一来,用户u对应的子用户空间中,uid=0的root用户,实际映射的是宿主机上uid=296608的用户。
为Docker启用User Namespaces
可以修改/etc/subuid和/etc/subguid,为用户u设置好从属用户和从属用户组。
然后在/etc/docker/daemon.json中配置用户命名空间:
{
"userns-remap":"u"
}
或者,把userns-remap设置为default,让Docker自动完成配置:
{
"userns-remap": "default"
}
默认的用户命名空间最大数量(max_user_namespaces)可能为0,会Docker导致无法创建用户命名空间,出现错误提示:nsenter: failed to unshare namespaces: Invalid argument。
查看/proc/sys/user/max_user_namespaces,可以获得该配置。如果为0,可以通过下列命令增大。
sysctl user.max_user_namespaces=15000
然后,重启docker: systemctl restart docker.service。
此时,Docker容器应用就运行在一个特定用户空间了。
可以在宿主机上看到生成的新目录/var/lib/docker/296608.296608。此时,docker的根目录变成了这个新目录,而不是原来的/var/lib/docker。此时,使用docker ls 也看不到原来的images了。
以nginx容器为例,虽然容器内是以root用户运行nginx master进程,并使用普通用户运行了2个nginx worker进程。
容器内的root用户,被映射到宿主机是普通用户296608。容器内的普通用户(nginx),被映射到宿主机是普通用户296709。
如果仍希望使用root用户来启动容器呢?可以使用--userns=host来解决。
此时,容器root用户映射到宿主root用户,容器普通用户映射到宿主普通用户,等同于不使用User Namespace的默认情况。
总结
用一张图来看看user namespaces的作用。
Docker使用user namespaces后,容器应用不论是以root用户还是普通用户身份运行,在宿主机上实际都是普通用户权限,这就大大提高了安全性。