自从我写了关于 Docker 安全系列文章的前两篇 文章 已经有一段时间了。本文将更新 Docker 自那时以来添加的新功能,并介绍正在通过上游 Docker 合并过程的新功能。
调整 Capabilities
在之前的文章中,我介绍了基于 Linux Capabilities 的容器隔离。
Linux Capabilities 允许您将 root 权限分解为更小的权限组。目前,默认情况下,docker 容器仅获得以下 capabilities。
CHOWN, DAC_OVERRIDE, FSETID, FOWNER, MKNOD, NET_RAW, SETGID, SETUID, SETFCAP, SETPCAP, NET_BIND_SERVICE, SYS_CHROOT, KILL, AUDIT_WRITE
在某些情况下,您可能需要调整此列表,例如,如果您正在构建一个将运行 ntpd 或 crony 的容器,这需要能够修改主机系统时间。容器将无法运行,因为它需要 CAP_SYS_TIME。在旧版本的 docker 中,容器必须在 --privileged 模式下运行,这将关闭所有安全功能。
在 docker-1.3 中,添加了 --cap-add 和 --cap-drop。现在,为了运行 ntpd 容器,您可以直接运行
docker run -d --cap-add SYS_TIME ntpd
这将仅将 SYS_TIME capability 添加到您的容器。
另一个例子是,如果您的容器没有更改任何进程的 UID/GID,您可以从您的容器中删除这些 capabilities,从而使其更安全。
docker run --cap-drop SETUID --cap-drop SETGID --cap-drop FOWNER fedora /bin/sh
# pscap | grep 2912
5417 2912 root sh chown, dac_override, fsetid, kill, setpcap, net_bind_service, net_raw, sys_chroot, mknod, audit_write, setfcap
或者您可以删除所有 capabilities 并添加一个回来。
docker run --cap-drop ALL --cap-add SYS_TIME ntpd /bin/sh
# pscap | grep 2382
5417 2382 root sh sys_time
调整 SELinux 标签
与 capabilities 类似,我们添加了动态调整 SELinux 标签的功能。
如果您看过 SELinux 彩色书,您就会知道我们可以按类型和 MCS/MLS 级别分隔进程。我们使用类型来保护主机免受容器的侵害。但我们也可以调整类型来控制允许进入和退出容器的网络端口。目前,我们使用 svirt_net_lxc_t 运行所有容器。此类型允许监听所有网络端口,并允许连接到所有网络端口。我们可以通过调整 SELinux 类型标签来加强容器的安全性。
对于常规 SELinux 和 Apache httpd,默认情况下我们只允许 apache 进程监听 Apache 端口 (http_port_t)。
# sudo sepolicy network -t http_port_t
http_port_t: tcp: 80,81,443,488,8008,8009,8443,9000
我们还阻止所有出站端口连接。这有助于我们锁定 Apache 进程,即使黑客通过 ShellShock 等安全漏洞破坏了应用程序,我们也可以阻止应用程序成为垃圾邮件机器人,或允许该进程对其他系统发起攻击。这就像加州旅馆,“你可以随时入住,但你永远无法离开。”
然而,对于容器,如果您在容器内运行 Apache 服务器应用程序,并且应用程序被破坏,则 Apache 进程将能够连接到任何网络端口并成为垃圾邮件机器人,或通过网络攻击其他主机/容器。
使用 SELinux 创建一个新的策略类型来与您的容器一起运行非常简单。首先,您可以创建一个 SELinux TE(类型强制)文件。
# cat > docker_apache.te << _EOF
policy_module(docker_apache,1.0)
# 此模板接口创建 docker_apache_t 类型作为
# 可以作为 docker 容器运行的类型。该模板
# 为域提供运行所需的最小权限。
virt_sandbox_domain_template(docker_apache)
# 我知道容器内的 apache 守护程序将需要
# 一些 capabilities 才能运行。幸运的是,我已经有了 Apache 的策略,我可以
# 查询 SELinux 以获取 capabilities。
# sesearch -AC -s httpd_t -c capability
allow docker_apache_t self: capability { chown dac_override kill setgid setuid net_bind_service sys_chroot sys_nice sys_tty_config } ;
# 这些是允许容器监听
# 网络上 Apache 端口所需的规则。allow docker_apache_t self:tcp_socket create_stream_socket_perms;
allow docker_apache_t self:udp_socket create_socket_perms;
corenet_tcp_bind_all_nodes(docker_apache_t)
corenet_tcp_bind_http_port(docker_apache_t)
corenet_udp_bind_all_nodes(docker_apache_t)
corenet_udp_bind_http_port(docker_apache_t)
# Apache 需要解析针对 DNS 服务器的名称
sysnet_dns_name_resolve(docker_apache_t)
# 许可域允许进程不被 SELinux 阻止
# 在开发和测试您的策略时,您可能希望
# 在许可模式下运行容器。
# 当您对策略充满信心时,您需要删除此规则。
permissive docker_apache_t;
_EOF
# make -f /usr/share/selinux/devel/Makefile docker_apache.pp
# semodule -i docker_apache.pp
现在使用新类型运行容器
# docker run -d --security-opt type:docker_apache_t httpd
现在,此容器将以比普通容器更严格的 SELinux 安全性运行。请注意,您可能需要查看审核日志,以查看您的应用程序是否需要额外的 SELinux 允许规则。
您可以使用 audit2allow 命令添加这些规则,并将规则附加到现有的 .te 文件上,重新编译并安装。
# grep docker_apache_t /var/log/audit/audit.log | audit2allow >> docker_apache.te
# make -f /usr/share/selinux/devel/Makefile docker_apache.pp
# semodule -i docker_apache.pp
多级安全模式
目前,我们使用 MCS 分离来确保我们的容器不会相互干扰或交互,除非是通过网络。某些政府系统需要不同类型的策略 MLS(多级安全)。使用 MLS,您可以根据进程将要处理的数据级别标记进程。MLS 表示,如果您的容器将要处理绝密数据,那么它应该以绝密级别运行。我们已在 docker 中添加了选项,允许管理员设置容器以在特定级别运行,这应该满足 MLS 系统的需求。
docker run -d --security-opt label:level:TopSecret --security-opt label:type:docker_apache_t httpd
这将使 docker 容器能够以备用类型和级别运行,并防止容器使用与标签级别不相同的数据。目前尚未通过认证,但我们愿意帮助第三方为 MLS 用户构建解决方案。
调整命名空间
在其他安全演讲中,我已经讨论过命名空间如何被视为一种安全机制,因为它消除了进程查看系统上其他进程的能力(PID 命名空间)。网络命名空间可以消除从您的命名空间查看其他网络的能力。IPC(进程间通信)命名空间具有阻止容器使用其他容器 IPC 的能力。
Docker 现在具有放宽这些限制的能力。您可以与容器共享主机命名空间
--pid=host 让容器共享主机 pid 命名空间
--net=host 让容器共享主机 net 命名空间
--ipc=host 让容器共享主机 ipc 命名空间
请注意,由于与主机共享 PID 或 IPC 命名空间需要我们禁用 SELinux 分离才能使其工作。
docker run -ti --pid=host --net=host --ipc=host rhel7 /bin/sh
您可能想阅读文章 超级特权容器中关于此的更多信息。
2 条评论