标注的博客| 安全研究| 渗透测试| APT

首页

指南:安全部署docker的部署清单

作者 olanna 时间 2020-04-15
all

在当今日益增长的基于云的IT市场中,人们对虚拟化技术有着强烈的需求。不幸的是,大多数虚拟化解决方案都不够灵活,无法满足开发人员的需求,使用完整虚拟化解决方案所带来的开销成为基础设施可扩展性的负担。

Docker允许开发人员和系统管理员无缝地为业务操作所需的应用程序和服务部署容器,从而减少了这一开销。但是,由于Docker利用与主机系统相同的内核来减少对资源的需求,如果配置不充分,容器可能会面临重大的安全风险。

下表列出了可以采取的强化措施,以改善容器在各自环境中的安全状况。应该注意,建议的解决方案仅适用于在基于Linux的主机上部署Linux Docker容器,使用撰写本文时Docker的最新版本(1.4.0,commit 4595d4f,dating 11/12/14)。

4595d4f

以下部分内容基于Jérôme Petazzoni[1]和Daniel J Walsh[2]的出版物。本文件旨在补充他们的建议,以及如何在Docker中具体实施这些建议。

注意:大多数建议的命令行选项可以以类似的方式存储和使用在Dockerfile中,以便自动生成图像。

Docker1.3现在支持加密签名[3]以确定正式存储库图像的来源和完整性。不过,此功能仍在进行中,因为Docker将发出警告,但不会阻止映像实际运行。此外,它不适用于非官方图像。

通常,请确保仅从受信任的存储库检索图像,并且从不使用--unsecure registry=[]命令行选项。

--insecure-registry=[]

默认情况下,用于控制通过系统Docker守护进程公开的容器的Docker REST API只能通过Unix域套接字在本地访问。

在TCP端口上运行Docker(即在启动Docker守护进程时使用-H选项强制绑定地址)将允许任何有权访问该端口的人访问容器,可能在主机上以及本地用户属于Docker组的某些情况下获得根访问权限[5]。

当允许通过TCP访问守护进程时,请确保使用SSL[6]对通信进行了充分加密,并且访问控制有效地防止未经授权的方与之交互。

内核防火墙iptables规则可以应用于docker0,Docker的标准网桥接口,以强制执行这些控件。

docker0

例如,Docker容器的源IP范围可以使用以下iptables过滤器来限制与外部世界的通信[7]。iptables-t filter-A FORWARD-s<source_ip_range>-j REJECT--禁止使用icmp管理拒绝

iptables -t filter -A FORWARD -s <source_ip_range> -j REJECT --reject-with icmp-admin-prohibited

收集并存档与Docker相关的安全日志,以便审计和监控。

从主机[8]访问容器外部的日志文件可以使用以下命令:docker run-v/dev/log:/dev/log<container_name>/bin/sh

docker run -v /dev/log:/dev/log <container_name> /bin/sh

使用Docker命令内置:Docker日志。。。(-f跟踪日志输出)

docker logs ...

日志文件也可以导出为持久存储到tarball使用:docker export。。。

docker export ... --selinux-enabled

在Docker1.3版本[9]中引入,可以使用新添加的--security opt参数配置容器的标签限制,以加载SELinux或AppArmor策略,如下面Docker运行引用摘录所示。

--security-opt run

--security opt=“label:user:user”:设置容器的标签用户security opt=“label:role:role”:设置容器的标签角色security opt=“label:type:type”:设置容器的标签类型security opt=“label:level:level”:设置容器的标签级别security opt=“apparmor:PROFILE”:设置apparmor要应用于容器的配置文件

--security-opt="label:user:USER" --security-opt="label:role:ROLE" --security-opt="label:type:TYPE" --security-opt="label:level:LEVEL" --security-opt="apparmor:PROFILE"

示例:docker run--security opt=label:level:s0:c100,c200-i-t centos bash

docker run --security-opt=label:level:s0:c100,c200 -i -t centos bash

不要使用--privileged命令行选项。否则,这将允许容器访问主机上的所有设备,此外,还将为容器提供特定的LSM(即SELinux或AppArmor)配置,使其具有与主机上运行的进程相同的访问级别。

--privileged

避免使用--privileged有助于减少攻击面和主机受损的可能性。但是,这并不意味着守护进程将在没有根权限的情况下运行,这在最新版本中仍然是必需的。

--privileged

启动守护进程和容器的能力只应授予受信任的用户。

通过使用-u选项将容器内强制的特权最小化。示例:docker run-u<username>-it<container\name>/bin/bash

docker run -u <username> -it <container_name> /bin/bash

docker组的任何用户部分最终都可以从容器获取主机上的根目录

为了防止通过系统资源耗尽来进行拒绝服务(DoS)攻击,可以使用特定的命令行参数应用一些资源限制。

CPU使用率:docker run-it--rm--cpuset=0,1-c 2。。。

docker run -it --rm --cpuset=0,1 -c 2 ...

内存使用:docker run-it--rm-m 128m。。。

docker run -it --rm -m 128m ...

存储使用:docker-d—存储opt dm.basesize=5G

docker -d --storage-opt dm.basesize=5G

磁盘I/O:Docker当前不支持。通过systemd公开的BlockIO*属性可用于控制受支持操作系统上的磁盘使用配额。

SUID和GUID二进制文件在易受导致任意代码执行的攻击(例如缓冲区溢出)时可能会证明是危险的,因为它们将在进程的文件所有者或组的上下文中运行。

如果可能的话,禁止SUID和SGID通过减少使用特定命令行参数给容器的功能而生效。docker run-it--rm--cap drop SETUID--cap drop SETGID。。。

docker run -it --rm --cap-drop SETUID --cap-drop SETGID ...

或者,考虑通过安装带有nosuid属性的文件系统从系统中删除SUID功能。

nosuid

最后一个选项可能是从系统中完全删除不需要的SUID和GUID二进制文件。这些类型的二进制文件可以通过运行以下命令在Linux系统上找到:find/-perm-4000-exec ls-l{};2>/dev/nullfind/-perm-2000-exec ls-l{};2>/dev/null

find / -perm -4000 -exec ls -l {} \; 2>/dev/null find / -perm -2000 -exec ls -l {} \; 2>/dev/null

然后,可以使用类似于以下[11]的命令删除SUID和GUID文件权限:sudo chmod u-s filename sudo chmod-R g-s directory

sudo chmod u-s filename sudo chmod -R g-s directory

如果需要,请使用内置的--device选项装载设备(不要将-v与--privileged参数一起使用)。

--device --privileged

可以使用第三组选项为每个设备分配粒度权限:rwm分别覆盖读、写和mknod权限(默认包括所有权限)。

:rwm read write mknod

示例(用于使用具有只读权限的声卡):docker run--device=/dev/snd:/dev/snd:r。。。

docker run --device=/dev/snd:/dev/snd:r ...

此功能在版本1.2[12]中引入。

若要降低Docker容器受损时横向移动的可能性,请考虑隔离敏感服务(例如,在堡垒主机或虚拟机中运行SSH服务)。

此外,不要在容器中运行具有根权限的不受信任的应用程序。

这是Docker在使用本机容器库(即libcontainer)时自动处理的。

但是,在使用LXC容器库时,理想情况下应使用只读权限手动装载敏感装载点,包括:

/sys /proc/sys /proc/sysrq-trigger /proc/irq /proc/bus

使用系统提供的更新实用程序(例如apt-get、yum等)确保内核是最新的。过时的内核更容易受到公开披露的漏洞的攻击。

使用带有GRSEC或PAX的增强内核,例如,它提供了针对内存损坏错误的增强的安全性。

Docker不支持用户名称空间,但目前正在开发中[13]。LXC驱动程序当前支持UID映射,但在本机libcontainer库中不支持。

此功能将允许Docker守护进程在主机上以非特权用户的身份运行,但在容器中以根用户的身份运行。使用LXC驱动程序时,可以使用-LXC conf选项,如下面的Docker run命令示例所示。

-lxc-conf run docker run -lxc-conf="lxc.id_map = u 0 100000 65536" -lxc-conf="lxc.id_map = g 0 100000 65536" ...

上述命令中的指定参数将指示Docker将主机上的uid和gid 100000到65536映射到uid,并将gid 0到65536映射到特定用户帐户,该帐户在主机上的系统级定义,配置类似于:[14]

/etc/subgid:<username>:100000:65536 /etc/subuid:<username>:100000:65536

libseccomp库允许基于白名单方法限制Linux内核系统调用过程的使用。理想情况下,应该禁用对系统操作不重要的系统调用过程,以防止在受损容器中滥用或误用。

此功能目前正在进行中(可在LXC驱动程序中使用,而不是在libcontainer中,后者现在是默认的)。

要重新启动Docker守护进程以使用LXC驱动程序,请使用[15]:Docker-d-e LXC

docker -d -e lxc

有关如何生成seccomp配置的说明位于Docker GitHub存储库的“contrib”[16]文件夹中。稍后可以使用以下命令创建基于LXC的Docker容器:Docker run--LXC conf=“LXC.seccomp=$file”<rest of arguments>

docker run --lxc-conf="lxc.seccomp=$file" <rest of arguments> capabilities(7)

尽可能减少linux的功能。Docker默认功能包括:chown、dac_override、fowner、kill、setgid、setuid、setpcap、net_bind_service、net_raw、sys_chroot、mknod、setfcap和audit_write。

chown dac_override fowner kill setgid setuid setpcap net_bind_service net_raw sys_chroot mknod setfcap audit_write

从命令行启动容器时可以使用--cap add=[]或--cap drop=[]进行控制。

--cap-add=[] --cap-drop=[]

示例:docker run--cap drop setuid--cap drop setgid-ti<container\u name>/bin/sh

docker run --cap-drop setuid --cap-drop setgid -ti <container_name> /bin/sh

Docker版本1.2[17]中引入了此功能

由于Docker容器内核的共享性,在多租户环境下无法安全地实现职责分离。建议在没有其他用途且不用于敏感操作的主机上运行容器。考虑将所有服务移动到Docker控制的容器中。

如果可能,请将Docker后台程序设置为在必要时使用--icc=false并指定-link with Docker run,或设置--export=port以从容器中公开端口,而不在主机上发布端口,从而将容器间通信保持在最低限度。

--icc=false --export=port

将相互信任的容器组映射到不同的计算机[18]。

使用完整的虚拟化解决方案来包含Docker,如KVM。如果Docker映像中存在内核漏洞,这将防止从容器升级到主机。

Docker镜像可以嵌套以提供这个KVM虚拟化层,如Docker in Docker实用程序[19]中的Docker所示。

定期对主机系统和容器执行安全审核,以确定可能使系统暴露于危害的错误配置或漏洞。