0x01 docker安全特性
- kernel namespaces隔离机制
- 控制组
- 服务端防护
- kernel capability能力机制
- 其他安全特性
docker安全基线有些可以直接根据docker安全特性进行配置。企业在现实条件允许的情况下,可从中筛选出符合自己内部docker容器的基线配置方案,首要原则是不影响生产环境和容器应用的运行。
0x02 docker安全基线
-
容器最小化(基础)
- 基线要求
仅在容器中运行业务所需的必要服务,不能开启sshd等风险应用服务,防止容器或者因为应用服务本身存在的安全问题或者漏洞而沦陷。
- 配置适用性
Linux OS(Ubuntu、Centos)
- 检测方式
(1)可以检查容器创建模版dockerfile文件中是否存在添加ssh服务的指令。
cat dockerfile | grep ssh
(2)容器ssh服务是否开启,可通过使用ps命令查看是否启动ssh进程,如启用则不合理。
ps | grep sshd lsof -i:22
- 加固方式
(1)限制并删除dockerfile文件中不必要的构建服务。
(2)可使用如下的方式管理已部署并运行容器:
docker exec -it <container_id> bash
(3)进入docker容器关闭不必要的服务:
service sshd stop
-
Docker remote api 访问控制(基础)
- 基线要求
docker客户端可以通过remote api与docker服务端进行交互,客户端使用RESTful风格的url请求操纵docker管理相关命令。然而,在大多数部署swarm docker集群上,swarm master主机通过每台节点宿主机agent的2375端口统一管理docker swarm集群节点,存在远程未授权访问风险。因此,需要使用统一的认证方式验证容器api请求身份的合法性。
- 配置适用性
Linux OS(Ubuntu、Centos)
- 检测方式
可以使用如下三种方式检测docker remote api是否启用:
(1) 启用docker服务时是否启用remote api服务:
docker daemon -H tcp://10.10.10.10:2375 -H unix://var/run/docker.sock
(2)查看容器宿主机是否开启非常规端口,并使用类似如下请求验证容器remote api是否对外暴露,如果返回docker container列表证明remote api已经暴露:
curl http://host:port/containers/json/
(3)查看宿主机/etc/default/docker(centos下为/etc/sysconfig/docker)文件中的DOCKER_OPTS设置,判断是否开启remote api服务:
cat /etc/default/docker | grep DOCKER_OPTS
- 加固方式
在通常情况下,建议不要启用docker的remote api服务。如果必须使用的话,可以采用如下的加固方式,该方式为自签名CA设置:
- docker服务端CA自签名证书的TLS认证配置可参考docker官方文档:
https://docs.docker.com/engine/security/https/
客户端与服务端通讯的证书生成后,可以通过以下命令启动docker daemon
docker -d --tlsverify --tlscacert=ca.pem --tlscert=server-cert.pem --tlskey=server-key.pem -H=tcp://10.10.10.10:2375 -H ubix://var/run/docker.sock
客户端连接时需要设置以下环境变量:
export DOCKER_TLS_VERIFY=1 export DOCKER_TLS_PATH=~/.docker export DOCKER_HOST=tcp://10.10.10.10:2375 export DOCKER_API_VERSION=1.12
- 设置ACL,在宿主机中使用iptables对容器的访问连接进行有效的控制
Ubuntu环境:
#创建iptables防火墙规则,如果没有则新建iptables.rules文件。 vim /etc/iptables.rules #打开iptables.ruls,并在iptables.rules文件中添加下列规则 * filter: HOST_ALLOW1 - [0:0] #只允许同网段宿主机访问该容器宿主机 -A HOST_ALLOW1 -s 10.10.10/1/32 -j ACCEPT #隔离其他网段 -A HOST_ALLOW1 -j DROP #对输入2375端口的tcp请求使用HOST_ALLOW1模版处理 -A INPUT -p tcp -m tcp -d 10.10.10.10 --port 2375 -j HOST_ALLOW1 COMMIT #保存策略,使配置生效 iptables-restore < /etc/iptables.rules
CentOS环境:
运行如下修改防火墙策略命令,并重新启用iptables防火墙
#只允许同网段宿主机访问该容器宿主机 iptables -A HOST_ALLOW1 -s 10.10.10.1/32 -j ACCEPT #隔离其他网段 iptables -A HOST_ALLOW1 -j DROP #对输入2375端口的tcp请求使用HOST_ALLOW1模版处理 iptables -A INPUT -p tcp -m tcp -d 10.10.10.10 --port 2375 -j HOST_ALLOW1 #保存防火墙配置 service iptables save #重新启用防火墙 service iptables restart
通常建议上述两种加固方式结合使用
-
限制流量流向(基础)
- 基线要求
通常情况下,需要过滤docker容器源ip地址范围与外界进行通讯,防止容器源ip暴露在公网中,同时限制容器之间的通讯。
- 配置适用性
Linux OS(Ubuntu、centOS)
- 检测方式
查看iptables,查看容器网络访问控制。
查看是否安装iptables防火墙
whereis iptables
验证是否开启iptables防火墙:
service iptables status
- 加固方式
若未安装iptables防火墙可使用如下方式安装:
apt-get install iptables
已安装iptables防火墙的实例可以使用以下iptables过滤器限制Docker容器的源ip地址范围与外界通讯。
Ubuntu环境:
#创建iptables防火墙规则,如果没有则新建iptables.rules文件。 vim /etc/iptables.rules #打开iptables.rules,并在iptables.rules文件中添加下列规则 *filter :INPUT DROP [0:0] :FORWARD ACCEPT [0:0] :OUTPUT ACCEPT [0:0] #限制宿主机访问ip来源范围,并返回提示信息。 -A FORWARD -s <source_ip_range> -j REJECT --reject-with icmp-admin-prohibited #docker0 连接docker客户端,eth0连接外网 -A FORWARD -i docker0 -o eth0 -j DROP #放行docker客户端到外网已建立的连接 -A FORWARD -i docker0 -o eth0 -m state --state ESTABLISHED -j ACCEPT COMMIT #保存策略,使配置生效 iptables-restore < /etc/iptables.rules
CentOs环境
运行如下修改防火墙策略命令,并重新启用iptables防火墙
#限制宿主机访问ip来源范围,并返回提示消息。 iptables -A FORWARD -s <source_ip_range> -j REJECT --reject-with icmp-admin-prohibited #docker0 连接docker客户端,eth0连接外网 iptables -A FORWARD -i docker0 -o eth0 -m state --state ESTABLISHED -j ACCEPT #保存防火墙配置 service iptables save #重新启用防火墙 service iptables restart
-
使用普通用户启动Docker服务(基础)
- 基线要求
由于docker容器中uid=0的用户在宿主机中的权限等于root,为了避免宿主机沦陷影响docker容器,西药讲容器和宿主机的各个权限进行合理映射,防止容器中断风险。
- 配置适用性
Linux OS(Ubuntu、centOS)
Docker 1.10之后版本
- 检测方式
在宿主机上使用如下方式:
ps -aux | grep docker
查看docker进程在宿主机上的启动用户,看是否为root,如果为root则不安全。
- 加固方式
使用user namespace进行权限隔离,有以下两种加固途径:
(1)使用docker默认的权限映射
运行docker daemon进程的时候加入参数—userns-remap=default,或者在/etc/default/docker(centOS为/etc/sysconfig/docker)中的DOCKER_OPT选项中追加配置—users-remap=default(注意:使用默认映射代表容器root用户映射到宿主机docker默认指定的用户uid上)
DOCKER_OPT=“--userns-remaps=default”
(2)自定义权限映射
在宿主机新建一个用户,用于映射docker容器root用户
sudo useradd docker_map
在启动docker daemon的时候加入如下参数
--userns-remap=<uid> --userns-remap=<uid>:<gid> --userns-remap=<username> --userns-remap=<username>:<groupname>
-
文件系统限制(基础)
- 基线要求
挂载的容器根目录绝对只读,而且不同容器对应的文件目录权限分离,最好是每个容器在宿主上有自己单独分区。
- 配置适用性
Linux OS(Ubuntu、centOS)
- 检测方式
使用如下命令
docker inspect <container_id>
依次查看主机上所有容器目录挂在位置,并确定挂载的容器容器根目录是否只读。(注意:挂载数据卷信息在“Mount Key”值下面)
- 加固方式
在宿主机中新建con1和con2用户
sudo useradd con1 sudo useradd con2
使用如下的容器挂载启动方式启动容器,隔离容器目录权限。
su con1 docker run -v dev:/home/mc_server/con1 -it debian8:standard /bin/bash su con2 docker run -v dev:/home/mc_server/con2 -it debian8:standard /bin/bash
-
镜像安全(基础)
- 基线要求
镜像安全要求客户端使用证书认证,对下载镜像进行检查,通过与CVE数据库同步镜像扫描,一旦发现漏洞则通知用户处理,或者组织镜像继续构建。根据docker官方文档描述,有关docker镜像安全原则有如下两种:
(1)镜像标签和内容信任
(2)镜像安全漏洞扫描
在企业拥有内部镜像源的条件下,镜像标签和内容信任是构建镜像安全环境的首要目标。
单个镜像记录具有以下标示符:
[REGISTRY_HOST[:REGISTRY PORT]/]REPOSITORY[:TAG]
一个特定的镜像REPOSITORY可以有多个标签。内容信任与镜像的TAG部分相关联。每个镜像仓库都有一组给镜像发布者使用的用于签署镜像标签的密钥。镜像发布者通过密钥可以自行决定签署哪些标签。镜像使用者可以启用内容信任以确保他们使用的镜像已被签名。如果使用者启用内容信任,则只能使用受信任的镜像进行pull,run或build。未签名的镜像标签对他们来说是“不可见的”。对于尚未启用内容信任的消费者,可以正常使用所有的docker镜像。
- 配置适用性
Linux OS(Ubuntu、centOS)
- 检测方式
如果从公网获取镜像源,则需要验证baseimage的md5值。如果是使用公司内部镜像源,则可跳过此步。
可以通过检查DOCKER_CONTENT_TRUST全局变量判断docker宿主机是否开启镜像内容信任机制。
echo $DOCKER_CONTENT_TRUST
- 加固方式
公司内部自建镜像源,并自行创建业务所需要使用的镜像。
如需对镜像进行签名推送和获取,可以使用以下命令:
#添加环境变量,启用docker内容信任 export DOCKER_CONTENT_TRUST=1 #使用tag标签推送可信内容 docker push <dtr-domain>/<repository>/<image>:<tag> #使用tag标签获取镜像内容 docker pull <username>/<image>:<tag>
同时,建议使用Notary工具管理镜像发布和管理的内容信任,地址如下:
https://github.com/docker/notary
-
Docker client端与Docker Daemon的通信安全(基础)
- 基线要求
Docker采用C/S架构方式进行通讯,为了防止通讯过程中的链路劫持、绘画劫持等中间人攻击问题,服务端和客户端需要通过加密认证的方式进行通讯。
- 配置适用性
Linux OS(Ubuntu、centOS)
- 检测方式
(1)检查/etc/default/docker(ubuntu)或者/etc/sysconfig/docker(centOS)文件中DOCKER_OPTS选项是否开启tls证书验证:
Ubuntu环境
cat /etc/default/docker | grep DOCKER_OPTS
centOS环境
cat /etc/sysconfig/docker | grep DOCKER_OPTS
(2)查看docker daemon运行进程,判断tls证书校验是否开启:
ps -aux | grep docker
- 加固方式
Docker daemon在0.10后支持—tlsverify来提供加密的远程连接。
如果宿主机使用的是Ubuntu 12.04 LTS并已经安装了Docker其他环境下可能有些不同。
服务端CA的TLS认证配置可参考docker官方文档:
https://docs.docker.com/engine/security/https/
客户端只需要在/etc/default/dockr(centOS为/etc/sysconfig/docker)加入下面配置样例:
DOCKER_OPTS="--tlsverify -H=unix:///var/run/docker.sock -H=0.0.0.0:4243 --tlscacert=/root/docker/ca.perm --tlscert=/root/dockr/cert.pem --tlskey=/root/docker/key.pem"
之后重启docker
service docker restart
客户端只要使用–tlsverify可以连接远程docker实例了
docker --tlsverify -H tcp://example.com:4243 images
-
资源限制(基础)
- 基线要求
为了防止容器服务耗尽系统资源而引发的拒绝服务等风险,可以使用特定的命令行参数来启用一些针对容器运行环境的资源限制。
- 配置适用性
Linux OS(Ubuntu、centOS)
- 检测方式
(1)查看容器运行状态,检查宿主机上容器资源占用率。
docker stats <container_id>
(2)查看docker运行进程判断是否制定资源限制,包括CPU,内存
ps -aux | grep docker
- 加固方式
可以参考如下样例启动docker容器。
docker run -tid -name ec2 --cpuset-cpus 3 --cpu-shares 2048 0memory 2048m -rm --blkio-weight 100 --pids-limit 512
上述样例需求为:启动ec2容器、实例限制cpu使用第2核、100%使用cpu资源(2048)、内存限制2048M、block块使用权重为100、pid进程数量限制为512个、无需保存容器产生的数据。
更多限制可以参考Docker官方说明:
docker -h docker run --help
-
宿主机及时升级内核漏洞(基础)
- 基线要求
使用Docker容器对外提供相应服务时,应该考虑宿主机故障或者崩溃以及升级内核等问题。为了不影响在线业务,docker容器应该支持热迁移,保障业务持续稳定的运行。同时及时做好升级过程中的规划和备份,回迁方案。
- 配置适用性
Linux OS(Ubuntu、centOS)
- 检测方式
Docker宿主机内核版本,检测升级机制。测试样例如下:
uname -a
检查内核信息,并确认宿主机系统内核是否存在内核漏洞。
- 加固方式
及时升级宿主机操作系统内核对应的漏洞补丁版本。
sudo apt-get update sudo apt-get dist-update
-
避免Docker容器中信息泄漏(基础)
- 基线要求
用户通常使用dockerfile或者docker-compose文件创建容器,如果这些文件中存在账号密码等认证信息,一旦Docker容器对外开放,则这些宿主机上的敏感信息也会随之泄漏。
- 配置适用性
Linux OS(Ubuntu、centOS)
- 检测方式
因此可以通过以下样例方式检查容器dockerfile的内容,其他敏感内容可根据实际需要匹配查找:
#设置dockerfile环境变量,假设dockerfile文件路径为/docker/dockerfile export $dockerfile=/docker/dockerfile #检查dockerfile是否存在ssh密钥敏感信息 grep authorized_keys $dockerfile #检查是否存在操作系统用户敏感信息 grep "etc/group" $dockerfile #检查是否存在sudo用户敏感信息 grep "etc/sudoers.d" $dockerfile #检查是否存在ssh密钥对信息 grep ".ssh/*id_rsa" $dockerfile
- 加固方式
删除容器创建模版文件中的敏感信息。
-
SUID和GUID限制(基础)
- 基线要求
suid和guid程序在受攻击导致任意代码执行的情况下(如缓冲区溢出)时将非常危险,因为它们将运行在进程文件所有者或组的上下文中。因此,使用docker特定的命令行参数可减少赋予容器的能力,组织suid和guid生效。
- 配置适用性
Linux OS(Ubuntu、centOS)
- 检测方式
docker容器是否引入SUID和SGID机制。
检查系统中是否存在使用suid和guid的程序
find / -perm -4000 -exec ls -l {} \; 2>/dev/null find / -perm -2000 -exec ls -l {} \; 2>/dev/null
- 加固方式
suid和guid程序在受攻击导致任意代码执行(如缓冲区溢出)时将非常危险,因为它们将运行在进程文件所有者或组的上下文中。如果可能的话,使用特定的命令行参数减少赋予容器的能力,组织suid和guid生效。
docker run -it -rm -cap-drop SETUID --cap-drop SETGID
还有种做法,可以考虑在挂载文件系统时使用nosuid属性来移除掉suid能力。最后一种做法是,删除系统中不需要的suid和guid程序。
然后,可以使用类似于下面的命令将移除suid和guid文件权限:
sudo chmod u-s filename sudo chmod -R g-s directory
-
安装安全加固(增强)
- 基线要求
可根据实际情况,使用安全的linux内核以及内核不定。如SELinux,AppArmor,GRSEC等,这些都是docker官方推荐的安全固件。
- 配置适用性
Linux OS(Ubuntu、centOS)
Docker版本大于1.3
- 检测方式
若先前在宿主机已经安装并配置SELinux,可在容器使用setenforce 1 来启用它。SELinux功能默认是禁用的,需要使用—selinux-enable来启用。容器的标签限制可使用新增的—security-opt加载SELinux或者AppArmor的策略进行设置。例如:
docker run --security-opt=secdriver:name:value -i -t centos bash
AppArmor的选项
--security-opt="apparmor:PROFILE"(设置AppArmor)
GRSEC的选项
gradm -F -L /etc/grsec/learning.logs
- 加固方式
SELinux的相关选项如下表:
--security-opt="label:user:USER"(设置标签用户) --security-opt="lable:role:ROLE"(设置标签角色) --security-opt="label:type:TYPE"(设置标签类型) --security-opt="label:level:LEVEL"(设置标签级别) --security-opt="label:disable"(完全禁用标签限制)
建议根据实际情况使用选项隔离对应的容器安全类型
-
限制系统命令调用(增强)
- 基线要求
宿主机在开放某些系统调用情况下,对于未完全安全隔离的容器能够利用这些系统调用,存在恶意调用的风险。因此,对于不必要的系统调用应该采取增强隔离的访问控制。
- 配置适用性
Linux OS(Ubuntu、centOS)
- 检测方式
根据应用部署实际情况,参考Linux系统调用列表
http://www.ibm.com/developerworks/cn/linux/kernel/syscall/part1/appendix.html
确定需要被隔离的系统调用
- 加固方式
使用基于Seccomp的系统调用安全控制。 Seccomp是Secure computing mode的缩写,它是Linux内核提供的一个操作,用于限制一个进程可以执行的系统调用.Docker也提供了配置文件接口用于指定系统调用的控制策略。 虽然Docker使用Seccomp来限制一个容器可以执行的系统调用.但部分linux系统版本使用default Docker binary不支持Seccomp.建议使用static Docker binary来安装Docker(在Ubuntu 14.x以后的版本中,default Docker binary中就默认支持Seccomp了)。 Seccomp的配置文件需要自定义路径,可以按照如下样例创建Seccomp配置文件禁用系统调用。
{ "defaultAction": "SCMP_ACT_ALLOW", "syscalls": [{ "name": "chmod", "action": "SCMP_ACT_ERRNO" }] }
配置以后按如下方式启动docker。
docker run -rm -it --security-opt seccomp:/xxx/config.json
上述配置样例表示禁用chmod系统调用。
-
能力限制(增强)
- 基线要求
能力限制要求尽可能降低docker对资源的控制能力。
- 配置适用性
Linux OS(Ubuntu、centOS)
Docker版本大于1.2
- 检测方式
检查Docker默认的内核能力限制是否符合实际需求。
Docker默认的能力包括:
能力 解释 dac_override 绕过文件读、写、执行权限内核检测的能力 chown 能够改变文件读、写、执行权限内核检测的能力 fowner 绕过内核文件拥有者检测的能力 kill 绕过中断信号检查机制的能力 setgid 改变文件组id setpcap 改变网络属性 net_bind_service 能够binding到指定端口 net_raw 利用原始套接字能力 sys_chroot 改变进程根目录能力 mknod 创建和管理特殊设备文件能力 setfcap 改变特殊文件属性能力 audit_write 内核日志记录和审计能力 更多能力机制可参考
http://man7.org/linux/man-pages/man7/capabilities_7.html
- 加固方式
在命令行启动容器时,可以通过—cap-add=[]或—cap-drop=[]进行控制。例如:
docker run --cap-drop setuid --cap-drop setgid -it <container_name> /bin/bash
-
多租户环境(增强)
- 基线要求
由于docker容器内核的共享机制,无法在多租户环境中安全地实现责任分离。因此,要求容器运行在没有其他目的且不用于敏感操作的宿主上。
- 配置适用性
Linux OS(Ubuntu、centOS)
- 检测方式
判断是否需要在多租户环境下管理和使用容器。
- 加固方式
(1)建议将容器运行在没有其它目的,且不用于敏感操作的宿主上。可以考虑将所有服务迁移到docker控制的容器城。可能的话,设置守护进程使用–icc=false,并根据需要在docker run时指定-link,或通过–export=port暴露容器的一个端口,而不需要在宿主上发布。将相互信任的容器的组映射到不同机器上。 (2)在条件允许情况下,可以使用基于kubernetes的Hypernetes服务管理不同租户并在管理操作流程中执行身份验证任务,该服务使用openstack keystone接口统一管理租户授权。 https://github.com/hyperhq/hypernetes
-
完全虚拟化(增强)
- 基线要求
在内核漏洞曝光发现的情况下,这将有一定的可能性扩大到宿主上。因此,为了防止漏洞风险影响扩大到宿主上,应该使用完全虚拟化的方案使得容器和内核隔离。
- 配置适用性
Linux OS(Ubuntu、centOS)
- 检测方式
根据实际部署情况,判断Docker容器是否运行在KVM等类似虚拟化宿主机中。
- 加固方式
使用类似Docker-in-Docker工具,Docker镜像可以嵌套来提供KVM虚拟层。
这种方式的优点是可以防止针对容器的攻击扩大到宿主机中。
在安装docker cli宿主机中运行
dcoker run --privileged -d docker:dind
运行成功后就可以得到docker in docker的容器运行环境。
关于docker in docker 的使用和配置具体可以参考如下链接:
https://github.com/jpetazzo/dind
-
日志分析(增强)
- 基线要求
(1)建议将容器运行在没有其它目的,且不用于敏感操作的宿主上。可以考虑将所有服务迁移到docker控制的容器城。可能的话,设置守护进程使用–icc=false,并根据需要在docker run时指定-link,或通过–export=port暴露容器的一个端口,而不需要在宿主上发布。将相互信任的容器的组映射到不同机器上。 (2)在条件允许情况下,可以使用基于kubernetes的Hypernetes服务管理不同租户并在管理操作流程中执行身份验证任务,该服务使用openstack keystone接口统一管理租户授权。 https://github.com/hyperhq/hypernetes
- 配置适用性
Linux OS(Ubuntu、centOS)
- 检测方式
一般情况,可以在宿主上使用以下命令在容器外部访问日志文件:
docker run -v /dev/log:/dev/log <container_name> /bin/sh
使用docker内置命令:
docker logs ...(-f to follow log output)
日志文件也可以到处成一个压缩包实现持久存储
docker export
- 加固方式
收集并归档与Docker相关的安全日志来达到审核和监控的目的,一般建议使用rsyslog或stdout+ELK的方式进行日志收集、存储与分析,因为Docker本身要求轻量,所以不建议像虚拟机或者物理机上安装安全agent,这时实时威胁检测和事件响应功能就要依赖实时日志传输和分析了。 部署elk+logspout日志分析系统总体思路为: 在docker容器内运行一个elk stack,并且使用logspout工具来自动将容器日志路由到logstash。 具体配置过程如下: 首先下载最新版本的Docker和Docker Compose:
git clone https://github.com/nathanleclaire/elk cd elk docker-compose –f docker-compose-quickstart.yml up
构建elk镜像。
docker-compose build docker-compose up
如果能访问80端口并有kibana界面说明部署成功。
0x03 参考文献
《从自身漏洞与架构缺陷,谈Docker安全建设》 http://www.yunweipai.com/archives/21610.html
《docker 生产环境之使用可信镜像—docker中的内容信任》 https://blog.csdn.net/kikajack/article/details/79600066