一 容器安全介绍
容器的安全性分为两个层面
一个是架构层面
比如
1、容器与宿主机共享linux内核
2、用Namespace隔离资源的使用
3、通过shim/runC的方式来启动容器
但作为使用者,架构层面的东西我们是无法更改的,想要做到容器安全,只能是在容器启动时控制它的内部权限
具体控制主分为两个方面
1、控制容器的capabilities
2、让容器以非root用户启动
二 linux capabilities
默认启动容器,虽然你看到的是root用户,但是很多操作都没有权限,除非开启privileged才算是真正的root用户拥有所有权限,如下docker run –name –privileged ….
示例如下:
1、dockerfile
FROM centos:7
RUN yum install -y iptables
2、制作镜像并且启动容器
[root@test04 ~]# docker build -t test_cap:v1.0 ./
[root@test04 ~]# docker container run -ti --rm --name test test_cap:v1.0 sh
sh-4.2# iptables -L -n
报错权限错误: Permission denied (you must be root)
3、但是我们在容器里查看当前用户,会发现命名是root用户,为何会没有权限呢???
sh-4.2# whoami
root
为什么?
在linux内核2.2以前,一个进程的权限只有root与非root之分,root用户永远一切权限,非root用户不能执行任何特权操作。但是很多时候,我们是需要让某一普通用户拥有一些特权权限的。
于是从2.2内核之后,诞生linux capablilities, Linux capabilities的本质 就是把Linux root 用户原来所有的特权做了细化,可以更加细粒度地给进程赋予不同权限。
注意:capability代表的是特权,是对root这个用的特权进行了细化,而不是普通权限。
对于任意一个进程,在做任意一个特权操作时,只要赋予它这个特权操作对应的capability即可,这样你整体的权限还是普通用户的,但某些特殊操作你也有想要的权限。
比如:你在某一个进程里
1、执行iptables命令,那该进程就需要有CAP_NET_ADMIN这个capability
2、如果你要mount一个文件系统,那么对应的进程就需要有CAP_SYS_ADMIN这个capability,(CAP_SYS_ADMIN这个capability里允许了大量的特权操作,包括文件系统、交换空间、还有对各种设备的
操作,以及系统调试相关的调用等)
在宿主机上,默认root用户拥有所有的capability,普通用户没有任何capability,如果我们去掉root的某个capability,它肯定也就缺失了某个特权,可以做一个简单的测试
在物理机上,对root用户启动的进程,手动去掉CAP_NET_ADMIN这个capabilities,你会发现在该进程内就是无法执行iptables操作了,哪怕你是root用户
# capsh --keep=1 --user=root --drop=cap_net_admin -- -c 'iptables -L;sleep 100'
报错没有权限:Perhaps iptables or your kernel needs to be upgraded.
三 控制容器的capability
我们启动容器时docker run –name –privileged ….指定了privileges的参数,其实就是让容器获取了所有的capabilities
如果容器没有一些特权,很多人会直接指定–privileged参数启动容器,这是不合理的,还是应该用capability来详细控制,只赋予容器它想要的那一个特权就好
那为何在物理机上,用root用户执行任何操作都可以,但是到了容器里,同样是root用户,一些操作却受到限制呢???
1、在普通的linux节点上,非root用户启动的进程默认就没有任何的linux capabilities
2、在普通的linux节点上,root用户启动的进程默认就包含了所有的linux capabilities,而在容器内可不是这样,容器在启动时除非你指定–privileged才会包含所有,否则容器进程里的root默认只包含了一部分linux capabilities
# 启动容器查看1号进程status里关于cap的参数
[root@test04 bin]# docker container run -ti --rm --name test test_cap:v1.0 sh
sh-4.2# cat /proc/1/status |grep -i cap
CapInh: 0000000000000000
CapPrm: 00000000a80425fb
CapEff: 00000000a80425fb
CapBnd: 00000000a80425fb
CapAmb: 0000000000000000
启动容器,赋予cap_net_admin
docker container run -ti --rm --cap-add NET_ADMIN --name test test_cap:v1.0 sh
# 有权限执行iptables命令
sh-4.2# iptables -L -n
Chain INPUT (policy ACCEPT)
target prot opt source destination
Chain FORWARD (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source
四 让容器以非root用户启动
4.1 以root用户启动容器的安全问题
为何要让用户以非root身份运行?这里面涉及到安全问题!
如果容器内的进程是以root用户启动的,它虽然与宿主机的root用户不同,但依然有权限修改宿主机的一些文件,哪怕这些文件是000权限
[root@xxx ~]# docker container run -ti --rm -v /etc:/etc --name test centos:7 sh
sh-4.2# ls -l /etc/shadow
---------- 1 root root 808 Aug 25 16:10 /etc/shadow
sh-4.2# echo "egon666" >> /etc/shadow
sh-4.2# exit
exit
[root@xxx ~]# tail -1 /etc/shadow # 回到宿主机查看,发现确实修改了
egon666
你可能会说,我们控制不把宿主机的关键目录挂载给容器不就可以了嘛,确实是这样,但是,这里需要强调点,你就明白了
1、上例告诉了你,容器内的root用户虽然不完全等同与宿主机的root用户,但同样是拥有一些危险的权限的
2、容器与宿主机是共享内核的,也就是说,一些关键数据你不挂载给容器,容器也在用宿主机的
综合1与2,一旦容器内的应用有安全漏洞,攻击者就会顺着该漏洞逃逸出容器,对宿主机造成危害,这其实很好理解:容器本身就是宿主机上的进程,该进程出现了安全问题,攻击者就是会利用该进程的权限
那如何解决呢?有以下 几种方案