容器通过namespace建立属于自己的一个相对隔离的环境,从上一篇《容器Namespace - 1》我们知道centos7默认没有启用user namespace。
上图显示容器的user namespace与宿主机是同一个ID:4026531837,接下来简单分析下docker创建namespace的代码。
vendor/github.com/containerd/containerd/oci/spec_unix.go,这个文件定义了缺省的namespace以及capability。
func defaultCaps() []string {
return []string{
"CAP_CHOWN",
"CAP_DAC_OVERRIDE",
"CAP_FSETID",
"CAP_FOWNER",
"CAP_MKNOD",
"CAP_NET_RAW",
"CAP_SETGID",
"CAP_SETUID",
"CAP_SETFCAP",
"CAP_SETPCAP",
"CAP_NET_BIND_SERVICE",
"CAP_SYS_CHROOT",
"CAP_KILL",
"CAP_AUDIT_WRITE",
}
}
func defaultNamespaces() []specs.LinuxNamespace {
return []specs.LinuxNamespace{
{
Type: specs.PIDNamespace,
},
{
Type: specs.IPCNamespace,
},
{
Type: specs.UTSNamespace,
},
{
Type: specs.MountNamespace,
},
{
Type: specs.NetworkNamespace,
},
}
}
创建启动容器过程中,会在下面的目录下生成run-time的config.json
/run/docker/containerd/daemon/io.containerd.runtime.v1.linux/moby/容器id/config.json
解析下这个文件(jq is a tool for processing JSON inputs):
cat config.json | jq . > /opt/config.json
/opt/config.json文件相关的namespace配置
"namespaces": [
{
"type": "mount"
},
{
"type": "network"
},
{
"type": "uts"
},
{
"type": "pid"
},
{
"type": "ipc"
}
],
创建容器namespace的逻辑在:
vendor/github.com/opencontainers/runc/libcontainer/nsenter/nsexec.c
if (config.namespaces)
join_namespaces(config.namespaces);
void join_namespaces(char *nslist)
{
struct namespace_t {
int fd;
int ns;
char type[PATH_MAX];
char path[PATH_MAX];
} *namespaces = NULL;
//准备namespace
fd = open(path, O_RDONLY);
if (fd < 0)
bail("failed to open %s", path);
ns->fd = fd;
ns->ns = nsflag(namespace);
strncpy(ns->path, path, PATH_MAX - 1);
for (i = 0; i < num; i++) {
struct namespace_t ns = namespaces[i];
// 通过setns设置
if (setns(ns.fd, ns.ns) < 0)
bail("failed to setns to %s", ns.path);
close(ns.fd);
}
free(namespaces);
}
详细的代码分析逻辑需要仔细再看看,这里只是个大概过程。
既然docker容器user namespace的管理员root是“map root to root”,它应该就具备super priviledge权限,我们来看看。
[root@centos opt]# unshare -m --mount-proc -u -i -n -p -U -r -f /bin/bash
[root@centos opt]# ip link show
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
[root@centos opt]# ip link set dev lo down
[root@centos opt]# ip link set dev lo up
[root@centos opt]#docker run -ti centos /bin/bash
[root@f45f03e236ec /]#ip link set lo down
RTNETLINK answers: Operation not permitted
为什么docker容器在设置loop接口状态时提示:“RTNETLINK answers: Operation not permitted”?
[root@f45f03e236ec /]# mount -t tmpfs -o size=20m tmpfs /tmp1
mount: permission denied // mount 也不允许
这个super user的权限是怎么限制的呢?
这个就需要讲讲linux系统的capability了。