在CentOS上为Docker开启SELinux
在CentOS上为Docker开启SELinux
a:hover { text-decoration: underline }
ul,ol { padding-left: 30px }
ul li>:first-child,ol li>:first-child,ul li ul:first-of-type,ol li ol:first-of-type,ul li ol:first-of-type,ol li ul:first-of-type { margin-top: 0px }
ul ul,ul ol,ol ol,ol ul { margin-bottom: 0 }
dl { padding: 0 }
dl dt { font-size: 14px; font-weight: bold; font-style: italic; padding: 0; margin: 15px 0 5px }
dl dt:first-child { padding: 0 }
dl dt>:first-child { margin-top: 0px }
dl dt>:last-child { margin-bottom: 0px }
dl dd { margin: 0 0 15px; padding: 0 15px }
dl dd>:first-child { margin-top: 0px }
dl dd>:last-child { margin-bottom: 0px }
pre,code,tt { font-size: 12px; font-family: Consolas, “Liberation Mono”, Courier, monospace }
code,tt { margin: 0 0px; padding: 0px 0px; white-space: nowrap; border: 1px solid #eaeaea; background-color: #f8f8f8 }
pre>code { margin: 0; padding: 0; white-space: pre; border: none; background: transparent }
pre { background-color: #f8f8f8; border: 1px solid #ccc; font-size: 13px; line-height: 19px; overflow: auto; padding: 6px 10px }
pre code,pre tt { background-color: transparent; border: none }
kbd { background-color: #DDDDDD; background-image: linear-gradient(#F1F1F1, #DDDDDD); background-repeat: repeat-x; border-color: #DDDDDD #CCCCCC #CCCCCC #DDDDDD; border-style: solid; border-width: 1px; font-family: “Helvetica Neue”, Helvetica, Arial, sans-serif; line-height: 10px; padding: 1px 4px }
blockquote { border-left: 4px solid #DDD; padding: 0 15px; color: #777 }
blockquote>:first-child { margin-top: 0px }
blockquote>:last-child { margin-bottom: 0px }
hr { clear: both; margin: 15px 0; height: 0px; overflow: hidden; border: none; background: transparent; border-bottom: 4px solid #ddd; padding: 0 }
.highlight { background: #ffffff }
.highlight .c { color: #999988; font-style: italic }
.highlight .err { color: #a61717; background-color: #e3d2d2 }
.highlight .k { font-weight: bold }
.highlight .o { font-weight: bold }
.highlight .cm { color: #999988; font-style: italic }
.highlight .cp { color: #999999; font-weight: bold }
.highlight .c1 { color: #999988; font-style: italic }
.highlight .cs { color: #999999; font-weight: bold; font-style: italic }
.highlight .gd { color: #000000; background-color: #ffdddd }
.highlight .gd .x { color: #000000; background-color: #ffaaaa }
.highlight .ge { font-style: italic }
.highlight .gr { color: #aa0000 }
.highlight .gh { color: #999999 }
.highlight .gi { color: #000000; background-color: #ddffdd }
.highlight .gi .x { color: #000000; background-color: #aaffaa }
.highlight .go { color: #888888 }
.highlight .gp { color: #555555 }
.highlight .gs { font-weight: bold }
.highlight .gu { color: #aaaaaa }
.highlight .gt { color: #aa0000 }
.highlight .kc { font-weight: bold }
.highlight .kd { font-weight: bold }
.highlight .kp { font-weight: bold }
.highlight .kr { font-weight: bold }
.highlight .kt { color: #445588; font-weight: bold }
.highlight .m { color: #009999 }
.highlight .s { color: #d14 }
.highlight .na { color: #008080 }
.highlight .nb { color: #0086B3 }
.highlight .nc { color: #445588; font-weight: bold }
.highlight .no { color: #008080 }
.highlight .ni { color: #800080 }
.highlight .ne { color: #990000; font-weight: bold }
.highlight .nf { color: #990000; font-weight: bold }
.highlight .nn { color: #555555 }
.highlight .nt { color: #000080 }
.highlight .nv { color: #008080 }
.highlight .ow { font-weight: bold }
.highlight .w { color: #bbbbbb }
.highlight .mf { color: #009999 }
.highlight .mh { color: #009999 }
.highlight .mi { color: #009999 }
.highlight .mo { color: #009999 }
.highlight .sb { color: #d14 }
.highlight .sc { color: #d14 }
.highlight .sd { color: #d14 }
.highlight .s2 { color: #d14 }
.highlight .se { color: #d14 }
.highlight .sh { color: #d14 }
.highlight .si { color: #d14 }
.highlight .sx { color: #d14 }
.highlight .sr { color: #009926 }
.highlight .s1 { color: #d14 }
.highlight .ss { color: #990073 }
.highlight .bp { color: #999999 }
.highlight .vc { color: #008080 }
.highlight .vg { color: #008080 }
.highlight .vi { color: #008080 }
.highlight .il { color: #009999 }
.pl-c { color: #969896 }
.pl-c1,.pl-mdh,.pl-mm,.pl-mp,.pl-mr,.pl-s1 .pl-v,.pl-s3,.pl-sc,.pl-sv { color: #0086b3 }
.pl-e,.pl-en { color: #795da3 }
.pl-s1 .pl-s2,.pl-smi,.pl-smp,.pl-stj,.pl-vo,.pl-vpf { color: #333 }
.pl-ent { color: #63a35c }
.pl-k,.pl-s,.pl-st { color: #a71d5d }
.pl-pds,.pl-s1,.pl-s1 .pl-pse .pl-s2,.pl-sr,.pl-sr .pl-cce,.pl-sr .pl-sra,.pl-sr .pl-sre,.pl-src,.pl-v { color: #df5000 }
.pl-id { color: #b52a1d }
.pl-ii { background-color: #b52a1d; color: #f8f8f8 }
.pl-sr .pl-cce { color: #63a35c; font-weight: bold }
.pl-ml { color: #693a17 }
.pl-mh,.pl-mh .pl-en,.pl-ms { color: #1d3e81; font-weight: bold }
.pl-mq { color: #008080 }
.pl-mi { color: #333; font-style: italic }
.pl-mb { color: #333; font-weight: bold }
.pl-md,.pl-mdhf { background-color: #ffecec; color: #bd2c00 }
.pl-mdht,.pl-mi1 { background-color: #eaffea; color: #55a532 }
.pl-mdr { color: #795da3; font-weight: bold }
.pl-mo { color: #1d3e81 }
.task-list { padding-left: 10px; margin-bottom: 0 }
.task-list li { margin-left: 20px }
.task-list-item { list-style-type: none; padding-left: 10px }
.task-list-item label { font-weight: 400 }
.task-list-item.enabled label { cursor: pointer }
.task-list-item+.task-list-item { margin-top: 3px }
.task-list-item-checkbox { display: inline-block; margin-left: -20px; margin-right: 3px; vertical-align: 1px }
生产服务器使用的是CentOS 7系统,安装Docker也一直都是使用yum命令直接从CentOS自己的源安装。自从Docker项目改名为moby,进而诞生CE和EE两个不同版本后。不知是因为版权还是其他的什么原因,CentOS源中的Docker版本不再更新了,default维持在1.12.6,latest为1.13.1。
为了使用新版本的Docker,只能添加官方repo,然后安装docker-ce。安装完成后,在默认配置上与CentOS自带版相比,发现了两个不同之处:
- 存储驱动默认换成了overlay
- SELinux默认没有开启(指Docker服务配置)
分析
关于存储驱动的问题,这里暂时不做讨论,只是来看一下SELinux的问题。这里说的SELinux没有开启不是指在操作系统层面上将其disable掉了,而是说Docker服务的配置中没有将其enable。毕竟在Docker中开启SELinux是要在操作系统也就是Linux内核开启了SELinux的前提下进行的。首先,Docker没有开启SELinux,在现象上会有什么不同?
我们有两台服务器,第一台上Docker开启了SELinux,而第二台没有。两台上面都有运行nginx容器,执行如下命令对比一下:
[admin@server1 ~]$ ps -AZ | grep nginx
system_u:system_r:svirt_lxc_net_t:s0:c375,c378 2285 ? 00:00:22 nginx
system_u:system_r:svirt_lxc_net_t:s0:c245,c772 2407 ? 00:00:00 nginx
system_u:system_r:svirt_lxc_net_t:s0:c245,c772 2453 ? 00:00:02 nginx
system_u:system_r:svirt_lxc_net_t:s0:c375,c378 24771 ? 00:00:00 nginx
[admin@server2 ~]$ ps -AZ | grep nginx
system_u:system_r:spc_t:s0 4375 ? 00:00:00 nginx
system_u:system_r:spc_t:s0 4419 ? 00:00:00 nginx
system_u:system_r:spc_t:s0 4739 ? 00:00:01 nginx
system_u:system_r:spc_t:s0 4752 ? 00:00:01 nginx
system_u:system_r:spc_t:s0 9536 ? 00:00:00 nginx
system_u:system_r:spc_t:s0 9564 ? 00:00:00 nginx
system_u:system_r:spc_t:s0 19178 ? 00:00:00 nginx
system_u:system_r:spc_t:s0 19255 ? 00:00:00 nginx
通过对比,我们发现开启SELinux后的Docker服务:
- 容器内的进程运行在
svirt_lxc_net_t
domain下 - 不同容器内的进程,安全上下文sensitivity段是不相同的(SELinux的MCS隔离)
对于没有开启SELinux的Docker服务:
- 容器进程domain为
spc_t
- 所有容器进程的安全上下文sensitivity完全相同
根据以上对比,显而易见的是,在SElinux没有开启时容器进程拥有相同的sensitivity,所以就无法依赖SELinux实现容器间的安全隔离了。也就是说如果某个容器内的服务进程因为漏洞等原因被入侵,进而被黑客控制,SELinux不会阻止此进程去访问其他容器的资源。
对于svirt_lxc_net_t
和spc_t
,可能第一眼看来不会有那么多的想法,我们先来看看svirt_lxc_net_t
的进程是个什么体验:
[admin@server2 ~]$ runcon -u system_u -r system_r -t svirt_lxc_net_t -l s0:c125,c512 /bin/bash
bash: /home/admin/.bashrc: Permission denied
bash-4.2$ ls
ls: cannot open directory .: Permission denied
bash-4.2$ ps -Z
system_u:system_r:svirt_lxc_net_t:s0:c125,c512 23686 pts/0 00:00:00 bash
system_u:system_r:svirt_lxc_net_t:s0:c125,c512 23722 pts/0 00:00:00 ps
bash-4.2$
因为当前目录的安全上下文类型是user_home_t
,按照SELinux策略,svirt_lxc_net_t
是没有权限访问的。虽然能够感受到进程确实受到了限制,但是还不是很直观。接下来用sepolicy
命令分析一下安全策略:
[admin@server2 ~]$ sepolicy communicate -s svirt_lxc_net_t -t svirt_lxc_net_t
sysctl_net_unix_t
cifs_t
svirt_lxc_net_t
fusefs_t
cgroup_t
svirt_sandbox_file_t
usermodehelper_t
svirt_home_t
hugetlbfs_t
nfs_t
根据man page中的说明,这个命令用来分析source和target两个domain可以通过哪些type来通信,也就是哪些type对于source来说可写,对于target来说可读。我们把source和target指定为同一domain,来看看该domain能够读写哪些type。
我们看到了,一个很熟悉的svirt_sandbox_file_t
,正是给Docker容器挂载volume时经常看到、用到的。据了解svirt_lxc_net_t相关策略就是为容器、虚拟化技术而设计的,除了可以访问svirt_sandbox_file_t类型的文件还拥有网络能力,并且可以执行/usr下大多数的命令。
再用以上命令看一下spc_t
,因为行数太多,所以做一下统计:
[admin@server2 ~]$ sepolicy communicate -s spc_t -t spc_t | wc -l
3816
[admin@server2 ~]$ sepolicy communicate -s unconfined_t -t unconfined_t | wc -l
3816
根据当前的安全策略,3816个type,比起svirt_lxc_net_t
的10个,不可同日而语。而且我们发现,不受限domain unconfined_t
也是3816。那么spc_t
就等于unconfined_t
了吗?其实也不是。
根据Dan Walsh的Blog所讲,unconfined_t
是为管理员而设置的一个user domain,SELinux安全策略会阻止绝大多数的受限domain和unconfined_t
通信。spc_t
全称为Super Privileged Container,也就是特权容器。根据SELinux策略,Docker daemon container_runtime_t
可以通过transition转变为spc_t
,而且大多数重要的受限domain可以通过unix domain socket和spc_t
进行通信。
配置
上面的分析只是为了对SELinux对Docker的影响有更深入地了解,同时感受SELinux的重要性。其实在Docker服务配置中开启SELinux很简单:
- 一种方法是在dockerd启动时加上
--selinux-enabled
参数,在CentOS上可以修改systemd Unit文件docker.service - 另一种方法实在
/etc/docker/daemon.json
配置文件中加上:{ "selinux-enabled": true }
然后重启docker服务
需要注意的是,在SELinux开启之前创建的容器不会受到影响。如果要为这些容器应用SELinux,可以重建,或者尝试自己修改容器的配置文件和文件系统。