一.现象

在docker中有一个运行选项是selinux-enabled。这个选项的作用是啥?

简而言之,它提供了对docker容器中进程的selinux的控制支持。下面举例说明。

首先按照官方文档的说明在docker的启动参数中加入selinux-enabled的支持,当然,前提是你系统的selinux已经打开(https://docs.docker.com/engine/reference/commandline/dockerd/)。

修改完参数,重新部署完毕后,执行以下指令,这条指令启动一个image为alpine的容器,并且将Host的根目录挂载到容器的/host目录。

[wlh@xiaomi ~]$ sudo docker run -ti -v /:/host alpine sh
/ # cd /host/home/
/host/home # ls
ls: can't open '.': Permission denied
/host/home #

这里可以看到容器没有权限访问Host的/home目录。

然后,作为对比,我们再执行一条指令。

[wlh@xiaomi ~]$ sudo docker run -ti -v /:/host --security-opt label:disable alpine sh
[sudo] password for wlh:
/ # cd /host/home/
/host/home # ls
lost+found wlh
/host/home #

这一次我们可以访问到宿主机的/home目录了。

二. 推理

那么造成访问权限不同的原因是什么呢?

这是第一次访问被拒绝的容器的sh进程的信息(用ps auxZ|grep sh获取)

system_u:system_r:container_t:s0:c157,c808 root  1.0  0.0    pts/    Ss+  :   : sh

这是第二次访问通过的容器的sh进程的信息(用ps auxZ|grep sh获取)

system_u:system_r:spc_t:s0      root        0.0  0.0         pts/    Ss+  :   : sh

我们可以看到,它们的selinux label是不同的。这些label决定了它们对主机上资源的访问权限。

第二个容器因为有--security-opt label:disable标签,所以其进程的selinux label和docker关闭了selinux-enabled时是一样的。

在如同第一个容器的默认情况下,每次创建一个新的容器,docker会根据/etc/selinux/targeted/contexts/lxc_contexts

文件的内容给容器的进程打上相应的label:

process = "system_u:system_r:container_t:s0"
content = "system_u:object_r:virt_var_lib_t:s0"
file = "system_u:object_r:container_file_t:s0"
ro_file="system_u:object_r:container_ro_file_t:s0"
sandbox_kvm_process = "system_u:system_r:svirt_qemu_net_t:s0"
sandbox_kvm_process = "system_u:system_r:svirt_qemu_net_t:s0"
sandbox_lxc_process = "system_u:system_r:container_t:s0"

这里应该是按照sandbox_lxc_process来打Label的。(process和sandbox_lxc_process的内容是一致的,不确定是哪个)

并且随机生成一个Category,在我们的例子中,Category是c157,c808。因此整个Label为:

system_u:system_r:container_t:s0:c157,c808 

docker每创建一个容器,都会给它分配不同的Category,以此来确保容器之间的访问隔离。也就是说容器A无法访问容器B的资源。

三.验证

回到我们前文举得例子,执行指令验证容器一的label能否访问home目录(sesearch指令可以通过sudo yum install setools-console.x86_64安装):

宿主机的home目录的selinux label为:

system_u:object_r:home_root_t:s0 home

下面我们验证一下selinux的规则对容器访问的隔离。简单说明一下这里指令的含义:

-A表示allow,意思是只输出allow的规则

-s表示的是source,意思是我想要查询的规则的source是container_t

-t表示的是type,意思是我想要查询的规则的object是home_root_t。

连在一起的意思是请返回允许domain 为container_t 的进程访问type为home_root_t的资源的规则。

[root@xiaomi contexts]# sesearch -A -s container_t -t home_root_t |grep read
[root@xiaomi contexts]# sesearch -A -s spc_t -t home_root_t |grep read|grep dir
allow files_unconfined_type file_type:dir { add_name append audit_access create execmod execute getattr ioctl link lock map mounton open quotaon read relabelfrom relabelto remove_name rename reparent rmdir search setattr swapon unlink write };
allow named_filetrans_domain home_root_t:dir { add_name getattr ioctl lock open read remove_name search write };
allow named_filetrans_domain mountpoint:dir { add_name getattr ioctl lock open read remove_name search write };

从中可以看出容器一的lable是不具备读取宿主机home目录的权限的,而容器二的label是具备读取宿主机home目录的权限的。(关于这里为什么grep出来的结果不包含spc_t/home_root_t这里不展开了,和selinux的attribute机制有关,简而言之file_type是一个attribute,它是一组type的集合,其中包含了home_root_t)

因此,容器一访问宿主机的home目录失败了,容器二访问宿主机的home目录成功了。

四.如何确保访问?

那么问题来了,在一个开启了selinux的docker服务中,如果我们想要创建一个容器,给它挂载一个宿主机的目录,我们如何确保容器对该目录的访问呢?

做法是在容器启动时在需要挂载的目录后面加上z或者Z,例如:

  docker run -v /var/db:/var/db:Z rhel7 /bin/sh

这里加上大写Z之后,docker服务会将docker容器一模一样的selinux lable打到挂载的目录上

[wlh@xiaomi ~]$ sudo docker run -ti -v /home/wlh:/host alpine sh
[sudo] password for wlh:
/ # cd /host/
/host # ls
ls: can't open '.': Permission denied
/host # exit
[wlh@xiaomi ~]$ sudo docker run -ti -v /home/wlh:/host:Z alpine sh
/ # cd /host/
/host # ls
file1 file2
/host # exit
[wlh@xiaomi ~]$ ls -Z /home/
system_u:object_r:lost_found_t:s0 lost+found system_u:object_r:container_file_t:s0:c498,c862 wlh
[wlh@xiaomi ~]$

从中上面的例子可以看出,第一次启动容器没有添加大写Z,一如所料我们没有权限访问/home/wlh目录下的内容。

而加了大写Z后,我们有权限访问了,与此同时/home/wlh的selinux label也被改变了。

[root@xiaomi contexts]# sesearch -A -s container_t -t container_file_t |grep read|grep dir
allow container_domain container_file_t:dir { add_name create getattr ioctl link lock map open read relabelfrom relabelto remove_name rename reparent rmdir search setattr unlink write };
allow svirt_sandbox_domain container_file_t:dir { add_name create execmod getattr ioctl link lock map mounton open read relabelfrom relabelto remove_name rename reparent rmdir search setattr unlink write };

访问selinux policy,确实有规则负责这个事情。

而加上小写的z的话,情况为:

[wlh@xiaomi ~]$ sudo docker run -ti -v /home/wlh:/host:z alpine sh
[sudo] password for wlh:
/ # cd /host/
/host # ls
file1 file2/host # exit
[wlh@xiaomi ~]$ ls -Z /home/
system_u:object_r:lost_found_t:s0 lost+found system_u:object_r:container_file_t:s0 wlh

注意它和大写的Z的区别,小写的z没有添加category,这应该意味着如果添加的是小写z,那么该目录可以被多个容器共享。

而如果添加的是大写的Z,那么容器挂载的目录由当前容器独占的,其他容器无法访问。(当然,除非其他容器也用大写Z抢占了该目录。例如容器A用大写Z的方式挂载了/home目录,此时它是可以正常访问的,接着容器B用大写Z的方式挂在了/home目录,那么/home目录的category会发生变化,由容器A的category转变为容器B的category,此时容器A失去了/home目录的访问权,转而容器B获得/home目录的访问权)

五.总结

根据前文的描述,我想selinux-enabled选项的基本功能已经比较明晰了,它主要是利用selinux机制限制docker容器内的进程访问宿主机/其它容器的资源。如果这个选项没有开启,那么容器自身的隔离机制是用户安全的唯一屏障,某些恶意程序在某些情况下可能会突破docker容器本身的资源隔离机制(以rootfs为主),访问到宿主机的资源。开启了这个选项后,即使恶意程序突破了rootfs的限制进入到宿主机的文件系统中,它所造成的破坏也是有限的,因为selinux机制限制了这个进程对宿主机文件的访问。

ref: https://medium.com/lucjuggery/docker-selinux-30-000-foot-view-30f6ef7f621(这个例子简单的说明了如何开启selinux-enabled并且有一个简单的小例子说明selinux-enabled是干什么的)

  https://prefetch.net/blog/2017/09/30/using-docker-volumes-on-selinux-enabled-servers/(这篇文章主要说明了大写Z和小写v的作用)

  http://www.projectatomic.io/blog/2015/06/using-volumes-with-docker-can-cause-problems-with-selinux/(同上)

docker selinux-enabled作用的更多相关文章

  1. nsenter into docker. selinux(semanage,restorecon)

    Docker容器运行后,如何进入容器进行操作呢?起初我是用SSH.如果只启动一个容器,用SSH还能应付,只需要将容器的22端口映射到本机的一个端口即可.当我启动了五个容器后,每个容器默认是没有配置SS ...

  2. Change the default MySQL data directory with SELinux enabled

    转载:https://rmohan.com/?p=4605 Change the default MySQL data directory with SELinux enabled This is a ...

  3. Docker selinux

    编辑/etc/sysconfig/docker文件,把OPTIONS='--selinux-enabled'的--selinux-enabled注释掉,也可以通过这个错误. 最大的问题就是Linux的 ...

  4. 进击的docker 二 : docker 快速入门

    1.安装docker 1.1.安装环境 [root@docker ~]# cat /etc/redhat-release CentOS Linux release (Core) [root@docke ...

  5. Docker - 在CentOS 7中安装Docker

    1-确认系统信息 # cat /etc/redhat-release CentOS Linux release 7.2.1511 (Core) # uname -a Linux CentOS-7 3. ...

  6. docker 中运行 mysql

    在宿主机Host的终端执行: 1. 搜索docker镜像仓库中提供的mysql docker search mysql 2.下载mysql镜像 docker pull mysql 3.改变目录权限,非 ...

  7. Docker容器学习梳理 - 基础知识(2)

    之前已经总结了Docker容器学习梳理--基础知识(1),但是不够详细,下面再完整补充下Docker学习的一些基础. Docker是个什么东西 Docker是一个程序运行.测试.交付的开放平台,Doc ...

  8. Docker HUB 的重要性

    1. 昨天晚上和今天早上 学习了下 mysql 的 主从配置(docker化部署) ,但是发现很多 -e 的参数不清楚. 然后在docker HUB 上面发现了具体的内容. 认识到 工作学习生活中 肯 ...

  9. Dockerfile & Docker Swarm & Docker Stack & Docker Compose

    Dockerfile 通俗地讲,它是为了指导单个镜像从无到有的构建过程.如果你镜像是从Docker registry上面拉下来的,那就用不到这个文件:如果你是自己的应用,想打包成镜像,那就需要这个文件 ...

  10. Jenkins - 以Docker方式安装启动Jenkins

    1 - 官网信息 操作步骤:https://jenkins.io/zh/doc/book/installing/#docker Docker映像地址:https://hub.docker.com/r/ ...

随机推荐

  1. ubuntu16.04中docker安装curl拒绝连接问题

    在Ubuntu16.04中安装docker ce,安装步骤按照官网说明https://docs.docker.com/engine/installation/linux/docker-ce/ubunt ...

  2. 使用hibernate建立mysql连接以及生成映射类和配置文件*.cfg.xml

    建立数据库连接 找到window—open perspective—myeclipse database explore空白出右键new注意 driver template 和driver class ...

  3. $.ajax相关用法

      ? $.ajax({             type: "GET",             url: "Services/EFService.svc/Member ...

  4. button的默认type居然是submit

    今天使用了html中的button标签,用js写了一点代码来完成onclick实践,当我点下它的时候,它不仅执行了我写的function,还把表单给提交了,一查它的button居然是sumbit. 然 ...

  5. 【自己D自己】WC2019总结

    好吧写着写着写成自黑文了. 这是我时隔一个月写的,寒假非常自闭,肝童年游戏赛尔号来着…… 没玩过的无视 作为一个 $BJ$ 蒟蒻,第一次飞到广州二中这么远的地方(我没出过国,去广州算是很远的一次了). ...

  6. css 三列布局

    前面的话 前面已经介绍过单列定宽单列自适应和两列自适应的两列布局.本文介绍三列布局,分为两侧定宽中间自适应.两列定宽一侧自适应.中间定宽两侧自适应.一侧定宽两列自适应和三列自适应这五种情况 两侧定宽中 ...

  7. 开发人员为组件添加自定义的className

    在开发过程当中需要给组件写上自己的样式,这个时候怎么办呢? 直接给组件添加className这样是无效的 当给组件添加className之后 在写组件的时候需要对使用你的组件的开发人员提供自定义cla ...

  8. 【Hihocoder1634】Puzzle Game(DP)

    题意:有一个n*m的矩阵,每个矩阵里有一个数字a[i][j].现在要求将其中一个格子的值改为p,使得修改后矩阵的最大子矩阵和最小,求这个最小值 n,m<=150,abs(a[i][j])< ...

  9. 【HDOJ5951】Winning an Auction(博弈DP)

    题意:A和B两个人做一个拍卖游戏.每一轮两人分别给出一个价格,出价高者获得该轮的物品,出价相同则奇数轮A优先,偶数轮B优先. 两个人的目标都是最大化自己的商品数量,给定轮数n与两人分别的总资金a,b, ...

  10. hdu 1867 A+B again for you

    A + B for you again Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Othe ...