天天看点

使用User Namespaces,隔离容器应用

作者:神奇的工程师

容器内的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了。

使用User Namespaces,隔离容器应用
使用User Namespaces,隔离容器应用

以nginx容器为例,虽然容器内是以root用户运行nginx master进程,并使用普通用户运行了2个nginx worker进程。

容器内的root用户,被映射到宿主机是普通用户296608。容器内的普通用户(nginx),被映射到宿主机是普通用户296709。

使用User Namespaces,隔离容器应用
使用User Namespaces,隔离容器应用

如果仍希望使用root用户来启动容器呢?可以使用--userns=host​来解决。

此时,容器root用户映射到宿主root用户,容器普通用户映射到宿主普通用户,等同于不使用User Namespace的默认情况。

使用User Namespaces,隔离容器应用

总结

用一张图来看看user namespaces的作用。

使用User Namespaces,隔离容器应用

Docker使用user namespaces后,容器应用不论是以root用户还是普通用户身份运行,在宿主机上实际都是普通用户权限,这就大大提高了安全性。

继续阅读