《精通Docker第三版》完整目录:
第一章 Docker概览
第二章 创建容器镜像
第三章 存储和发布镜像
第四章 管理容器
第五章 Docker Compose
第六章 Windows容器
第七章 Docker Machine
第八章 Docker Swarm
第九章 Docker和Kubernetes
第十章 在公有云上运行Docker
第十一章 Portainer – 一个Docker的GUI
第十二章 Docker安全
第十三章 Docker工作流
第十四章 Docker进阶
本章中我们将来学习Docker安全的相关知识,这是当今每个人都最为关心的一个话题。我们会将本章划分为如下五个部分:
- 使用容器的考虑因素
- Docker命令
- 最佳实践
- Docker Bench Security应用
- 第三方安全服务
技术准备
本章中,我们将使用桌面Docker,并且我们将使用Docker Machine来在云端启动一个Docker主机。和此前章节一样,作者将使用个人偏好的操作系统macOS。同样,所使用的Docker命令在安装过 Docker 的三种操作系统均可使用。但是,部分支持命令可能仅能用于macOS和Linux操作系统。
观看如下视频来查看实时操作效果:
容器的考虑因素
首次发布Docker时,有大量关于Docker对比虚拟机的讨论。我还记得在杂志中阅读文章、Reddit上的讨论以及无休止的博客文章。早期的Docker alpha和beta版本,人们把Docker容器视作虚拟机,因为没有其实的东西可作参照了,我们把它视为微型虚拟机。
以前我会启动SSH,在容器中启动多个进程,甚至通过启动容器来创建容器镜像以及运行命令来安装我们的软件栈。这在第二章 创建容器镜像中已经讨论过,应当避免这么做,因为这是不良实践。
因为,我们不讨论容器和虚拟机的对比,来看一些运行容器而非虚拟机时一些考量因素。
优势
在启动Docker容器时,Docker引擎在后台做了大量的工作。Docker引擎所做的工作之一是在启动容器时,设置命名空间和控制组。什么意思呢?通过设置命名空间,Docker将每个容器的进程进行隔离,不仅是容器之间,与宿主机的系统同样隔离。控制组保障每个容器获取自己份额的资源,如CPU、内存和磁盘I/O。最重要的是,它们确保一个容器不会耗尽给定Docker主机上的所有资源。
在此前的章节中也看到了,在Docker控制的网络中启动容器表示你可以在应用级别隔离你的容器,所有应用A 的容器在网络层都无法访问应用 B 的容器。
此外,这种网络隔离可以通过使用默认的网络驱动器来在单个Docker主机上运行,或者可以使用Docker Swarm内置的多主机网络驱动器或Weave的Weave Net驱动器来横跨多个Docker主机。
最后,我所认为Docker相对典型的虚拟机的最大优势是你无需登录容器。Docker尽最大的努力来保持你无需登录容器即可管理它所运行的进程。通过docker container exec, docker container top, docker container logs和docker container stats等命令,你可以做所需做的所有事情,而不暴露不必使用的更多服务。
Docker主机
在处理虚拟机时,你可以控制谁使用哪台虚拟机。假设你只希望开发人员User 1使用开发虚拟机。但User 2是运维人员,它负责开发和生产环境,因此他需要能进入所有虚拟机。大部分虚拟机管理工具都允许你对虚拟机按角色进行赋权。
使用Docker时就存在一点劣势了,因为能够使用Docker主机的Docker引擎的任何人,不论是通过sudo赋权或是将用户加入到Docker Linux组中,都可以进入你运行的所有Docker容器。他们可以运行新容器,停止已有容器,他们也可以删除镜像。一定要当心主机上的Docker引擎访问权限赋给谁。他们掌管着通过你的容器王国的大门钥匙。了解了这个,推荐将Docker主机仅用于Docker,将其它服务与Docker主机进行分离。
镜像可信度
如果你运行的是虚拟机,极有可能会从头开始进行配置。因为下载文件在大小(以及启动所花费的精力),你不太可能下载一个随便什么人在网上创建的预置机器镜像。通常会使用来自可信的软件提供者的预建虚拟应用。
因此,你会清楚虚拟机内部有什么和没有什么,因为你负责构建和维护它。
Docker的一个优势是易于使用,但这种易于使用也容易让人忽略一个相当重要安全的考量:你是否知道容器内运行的是什么?
我们已经在前面章节中涉及到了镜像可信度的问题。比如,我们说到过不要发布或下载没有使用Dockerfile定义的镜像,不要把自定义代码呀密钥等等嵌入到会发布到Docker Hub上镜像中。
虽然容器有命名空间、控制组和网络隔离的保护,我们讨论了随意下载的镜像可能会为你的环境带有安全问题和风险。比如,一个完全没问题的容器运行着一个未打补丁的软件会为应用和数据的可用性带来风险。
Docker 命令
我们来看能帮助强化安全以及查看你可能使用的镜像信息的一些Docker命令。
我们会集中在两个命令上。第一个是docker container run命令,通过这一命令可以了解一些对你有优势的项目。第二个我们会来看docker container diff命令,我们可以来了解计划使用的镜像发生了什么。
run命令
就docker run而言,我们将主要集中在允许你以只读的方式在容器中进行所有设置的选择,而不是具体到目录或数据卷。这有助于限制恶意软件所导致的损失程序,这种软件通过更新它的二进行文件可能会劫持一个欠保护的应用。
我们来看如何启动一个只读容器,然后再分解其中进行的操作,如下:
1 |
$ docker container run -d --name mysql --read-only -v /var/lib/mysql -v /tmp -v /var/run/mysqld -e MYSQL_ROOT_PASSWORD=password mysql |
这里,我们运行了一个MySQL容器并将整个容器除以下文件夹外置为只读:
- /var/lib/mysql
- /var/run/mysqld
- /tmp
这些会以三个独立的数据卷进行创建,然后以可读/可写进行挂载。如果你不添加这些数据卷,MySQL将无法启动,因为它需要读写权限来完成这些操作:在/var/run/mysqld中创建套接字文件、 在/tmp中创建一些临时文件以及在/var/lib/mysql中创建数据库自身文件。
容器中的其它位置都不允许进行写入。如果你运行如下命令,会执行失败:
1 |
$ docker container exec mysql touch /trying_to_write_a_file |
以上命令会得到如下消息:
1 |
touch: cannot touch '/trying_to_write_a_file': Read-only file system |
这在你需要控制容器内何处可写(何处不可写)非常的有用。进行完善的测试,因为应用不能在某些位置写入可能会产生问题。
和前面的命令类似,使用docker container run我们设置了所有内容只读(除指定数据卷外),我们可进行相反的操作来设置单个数据卷(如果你使用-v 参数可以设置更多的数据卷)为只读。有关数据卷需要记住的一件事是,在使用数据卷并将其挂载到容器中时,它会在容器内部目录之上挂载一个空的数据卷,除非你使用–volumes-from参数或以其它方式在启动之后将数据加入到容器中:
1 |
$ docker container run -d -v /local/path/to/html/:/var/www/html/:ro nginx |
这将从Docker宿主机挂载/local/path/to/html/到/var/www/html/并将其设为只读。如果你不希望运行的容器写入到数据卷,并保持数据数据文件和配置文件的完整性,这会非常有用。
diff命令
我们再来看看docker diff命令,因为它与容器的安全方面有关,你可能会希望使用Docker Hub或其它相关仓库上托管的镜像。
记住有权限访问Docker主机和Docker守护进程的人都有权限访问所有运行中的Docker容器。所以,如果你没有设置监控,有人就可以对你的容器执行命令并进行恶意操作。
我们来看上一部分中所启动的MySQL容器:
1 |
$ docker container diff mysql |
你会看到没有任何文件返回。什么原因呢?
diff命令告诉我们在容器启动后镜像所发生的变化。在上一部分中,我们以镜像只读模式启动了MySQL容器,然后对已知MySQL会需要读取和写入的目录挂载了数据卷,也就是说我们所下载的镜像与容器中运行的那个没有任何文件上的变化。
运行如下命令停止并删除该MySQL容器,然后清理数据卷:
1 2 3 |
$ docker container stop mysql $ docker container rm mysql $ docker volume prune |
然后,再次启动容器,去除掉只读标记和数据卷,这次结果会有所不同,如下:
1 2 3 |
$ docker container run -d --name mysql -e MYSQL_ROOT_PASSWORD=password mysql $ docker container exec mysql touch /trying_to_write_a_file $ docker container diff mysql |
可以看到创建了两个目录并添加了一些文件:
1 2 3 4 5 6 7 8 |
A /trying_to_write_a_file C /run C /run/mysqld A /run/mysqld/mysqld.pid A /run/mysqld/mysqld.sock A /run/mysqld/mysqld.sock.lock A /run/mysqld/mysqlx.sock A /run/mysqld/mysqlx.sock.lock |
这对于定位容器中所进行的异常或非预期操作是一种很好的方式。
最佳实践
这一部分中,我们来看Docker的最佳实践,以及互联网安全中心指南,来恰如其分地保障你的Docker环境各方面的安全。
Docker最佳实践
在进入到互联网安全中心指南之前,我们来过一遍使用Docker的一些最佳实践,如下:
- 每个容器一个应用:将应用分布到各个容器中。Docker就是为这个而生的,随着时间的推荐它会让所有事情更简单。我们前面讨论的隔离是这里的关键。
- 只安装需要安装的:我们在前面章节已经讲到,只在容器镜像中安装需要用到的。如果你需要为容器所运行的进程安装更多进行支持的话,我们建议你重新审视下这么做的原因。这不仅保持你的镜像容量小和异于迁移,也减少了潜在的功击面。
- 审查谁对Docker主机拥有权限:记住对Docker主机拥有 root 或 sudo 权限的人可以对主机上的所有镜像和容器进行操作。
- 使用最新版本:保持使用Docker的最新版本。这会确保所有的安全漏洞都打过补丁,你也拥有那些最新的功能。在修复安全问题时,保持使用最新社区版可能会因功能变化或新特性而带来一些问题。如果这是你关注的问题,你可能想要了解Docker的LTS企业版以及Red Hat。
- 利用资源:如果需要帮助使用可用的资源。Docker的社区可以提供巨大的帮助。使用它们的网站、文档和Slack聊天室来有助于规划 Docker 环境和评估平台。有关如何使用Slack以及社区其它部等信息,参见第十四章 Docker进阶。
互联网安全中心基准
互联网安全中心(CIS)是一个独立的非营利组织,目标是提供安全的上网体验。它们发布基准和管控,被认为是 IT 各方面的最佳实践。
Docker的CIS基准可以免费下载。你会看到当前它是一份230页的 PDF,通过知识共享证书发布,涵盖Docker CE 17.06及之后的版本。
你会在实际运行扫描(本章的下一部分)时参照这一指南,并获取结果来确定什么需要(或应该)修复。指南划分为如下部分:
- 主机配置
- Docker守护进程配置
- Docker守护进程配置文件
- 容器镜像/运行时
- Docker安全运维
主机配置
指南的这一部分有关Docker主机的配置。这是你所有容器运行的Docker环境部分。因此,保持它的安全至关重要。这是对抗攻击人员的第一道防线。
Docker守护进程配置
指南的这一部分有保障运行中Docker守护进程安全的建议。你对Docker守护进程做的每一操作都影响所有容器。有一些开关参数可以附加到Docker守护进程上,前面有看到,在下一部分中运行工具时还有针对配置项的。
Docker守护进程配置文件
指南的这一部分处理Docker守护进程使用的文件和目录。包括从权限到所有权的方方面面。有时,这些区域会包含一些你不希望其他人知道的信息,可能会是普通文件格式。
容器镜像/运行时和构建文件
指南的这一部分包含加固容器文件以及构建文件的信息。
第一部分包含镜像,涵盖基础镜像和使用的构建文件。我们在前面提到过,你需要准确了解所使用的镜像,不仅是基础镜像,还有Docker体验的任何方面。指南的这一部分涵盖在创建你的基础镜像时应遵循的项目。
容器运行时
这一部分此前是下面一部分的内容,但在CIS指南中将其移为自己的部分。容器运行时包含大量的安全相关项。
注意你所使用的运行变量。某些情况下,在你使用它们时攻击者会利用它们。在你的容器中暴露过多,比如将应用密钥和数据库连接暴露为环境变量,不仅会破坏你的容器的安全,还会影响到Docker主机及其上运行的其它容器。
Docker安全运维
指南的这一部分涵盖部署相关的安全领域,这些项目与Docker最佳实践结合更紧密。因此,最好是遵循这些建议。
Docker Bench Security应用
这一部分中,我们将涉及你可以安装并运行的Docker基准安全应用。该工具会检查如下内容:
- 主机配置
- Docker守护进程配置
- Docker守护进程配置文件
- 容器镜像和构建文件
- 容器运行时
- Docker安全运维
- Docker Swarm配置
看起来很熟?因为这和前一节中所回顾的是相同的项,只是构建在未来会做大量工作的应用。它们向你展示你的配置内产生的警告,并提供其它配置项的信息,甚至是我们传给test的各项。
下面我们会来看如何运行该工具、一个实际实例,以及进程输出的意思。
在MacOS 和Windows的Docker上运行该工具
运行这个工具非常简单。已经为我们在Docker容器中进行了打包。虽然你可以获取源代码并自定义输出或以某些方式操作它(比如,邮件发送输出),你可能只需默认的即可。
该工具的 GitHub 项目地址为https://github.com/docker/docker-bench-security/,在macOS或Windows电脑上运行这个工具,你只需要将如下命令拷贝到终端即可。以下命令缺少了检查systemd所需的行,因为Docker for macOS和Docker for Windows的底层操作系统Moby Linux,并不运行systemd。我们一会儿再来看带有systemd的系统:
1 2 3 4 5 6 |
$ docker run -it --net host --pid host --cap-add audit_control \ -e DOCKER_CONTENT_TRUST=$DOCKER_CONTENT_TRUST \ -v /var/lib:/var/lib \ -v /var/run/docker.sock:/var/run/docker.sock \ -v /etc:/etc --label docker_bench_security \ docker/docker-bench-security |
镜像下载好之后,它启动启动并立即开始审计你的Docker主机,打印运行的结果,如以下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
$ docker run -it --net host --pid host --cap-add audit_control \ > -e DOCKER_CONTENT_TRUST=$DOCKER_CONTENT_TRUST \ > -v /var/lib:/var/lib \ > -v /var/run/docker.sock:/var/run/docker.sock \ > -v /etc:/etc --label docker_bench_security \ > docker/docker-bench-security Unable to find image 'docker/docker-bench-security:latest' locally latest: Pulling from docker/docker-bench-security cd784148e348: Pull complete 48fe0d48816d: Pull complete 164e5e0f48c5: Pull complete 378ed37ea5ff: Pull complete Digest: sha256:ddbdf4f86af4405da4a8a7b7cc62bb63bfeb75e85bf22d2ece70c204d7cfabb8 Status: Downloaded newer image for docker/docker-bench-security:latest # ------------------------------------------------------------------------------ # Docker Bench for Security v1.3.4 # # Docker, Inc. (c) 2015- # # Checks for dozens of common best-practices around deploying Docker containers in production. # Inspired by the CIS Docker Community Edition Benchmark v1.1.0. # ------------------------------------------------------------------------------ Initializing Mon Apr 29 15:49:40 UTC 2019 [INFO] 1 - Host Configuration [WARN] 1.1 - Ensure a separate partition for containers has been created [NOTE] 1.2 - Ensure the container host has been Hardened [INFO] 1.3 - Ensure Docker is up to date [INFO] * Using 18.09.2, verify is it up to date as deemed necessary [INFO] * Your operating system vendor may provide support and security maintenance for Docker [INFO] 1.4 - Ensure only trusted users are allowed to control Docker daemon [WARN] 1.5 - Ensure auditing is configured for the Docker daemon [WARN] 1.6 - Ensure auditing is configured for Docker files and directories - /var/lib/docker [WARN] 1.7 - Ensure auditing is configured for Docker files and directories - /etc/docker [INFO] 1.8 - Ensure auditing is configured for Docker files and directories - docker.service [INFO] * File not found [INFO] 1.9 - Ensure auditing is configured for Docker files and directories - docker.socket [INFO] * File not found [INFO] 1.10 - Ensure auditing is configured for Docker files and directories - /etc/default/docker [INFO] * File not found [INFO] 1.11 - Ensure auditing is configured for Docker files and directories - /etc/docker/daemon.json [INFO] * File not found [INFO] 1.12 - Ensure auditing is configured for Docker files and directories - /usr/bin/docker-containerd [INFO] * File not found [INFO] 1.13 - Ensure auditing is configured for Docker files and directories - /usr/bin/docker-runc [INFO] * File not found ... |
可以看到,有一些警告([WARN])、备注([NOTE])和信息([INFO]),但因类主机由Docker所管理,如同预期不和过度担心。
Ubuntu Linux上运行该工具
我们在深入了解审计的输出之前,我会先在DigitalOcean中启动一个常规的Ubuntu 16.04.5 LTS服务器,并使用Docker Machine安装一个全新的Docker,执行命令如下:
1 2 3 4 5 |
$ DOTOKEN=0cb54091fecfe743920d0e6d28a29fe325b9fc3f2f6fccba80ef4b26d41c7224 $ docker-machine create \ --driver digitalocean \ --digitalocean-access-token $DOTOKEN \ docker-digitalocean |
译者注:先运行eval $(docker-machine env docker-digitalocean)再运行如下的命令
安装之后,我会启动一些容器,每个容器都不涉及敏感设置。我会从 Docker Hub启动如下两个容器:
1 2 |
$ docker container run -d --name root-nginx -v /:/mnt nginx $ docker container run -d --name priv-nginx --privileged=true nginx |
然后,我们会基于Ubuntu 16.04构建一个自定义镜像,使用如下Dockerfile来运行SSH:
1 2 3 4 5 6 7 8 9 10 11 |
FROM ubuntu:16.04 RUN apt-get update && apt-get install -y openssh-server RUN mkdir /var/run/sshd RUN echo 'root:screencast' | chpasswd RUN sed -i 's/PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config RUN sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd ENV NOTVISIBLE "in users profile" RUN echo "export VISIBLE=now" >> /etc/profile EXPOSE 22 CMD ["/usr/sbin/sshd", "-D"] |
使用如下命令来构建和启动它:
1 2 |
$ docker image build --tag sshd . $ docker container run -d -P --name sshd sshd |
可以看到,在一个镜像中,我们在root-nginx容器中以可读可写权限挂账主机的根文件系统。我们还在priv-nginx中以扩展权限进行了运行,最后在sshd中运行SSH。
运行如下命令来在我们的 Ubuntu Docker主机上启动审计:
1 2 3 4 5 6 7 |
$ docker run -it --net host --pid host --cap-add audit_control \ -e DOCKER_CONTENT_TRUST=$DOCKER_CONTENT_TRUST \ -v /var/lib:/var/lib \ -v /var/run/docker.sock:/var/run/docker.sock \ -v /usr/lib/systemd:/usr/lib/systemd \ -v /etc:/etc --label docker_bench_security \ docker/docker-bench-security |
因为我们运行的操作系统支持systemd,这里挂载了 /usr/lib/systemd,所以我们可对其进行审计。
有大量的输出有待消化,它们的意思究竟是什么呢?我们来逐一进行分解。
掌握输出
我们看到了三类输出,如下:
- [PASS]:这些项没有任何问题。不需要在意它们,但读起来也不错,让你内心感觉到温暖。这种是越多越好。
- [WARN]:这些项需要修复。这是我们所不想看到的内容。
- [INFO]:如果你觉得它们是与你的设置和安全需求相关的话,这些项是你应该重审和修复的。
- [NOTE]:这些给出最佳实践建议。
前面提到,在审计中会包含七大部分,如下:
- 主机配置
- Docker守护进程配置
- Docker守护进程配置文件
- 容器镜像和构建文件
- 容器运行时
- Docker安全运维
- Docker Swarm配置
下面来了解各个部分所扫描的内容。这些扫描结果来自默认的Ubuntu Docker主机,至此还没有对系统做过任何修改。我们集中看每个部分的 [WARN]项。你在自己运行时可能会出现其它警告,但这是大部分人首次会看到的警告。
主机配置
我的主机配置有5项为[WARN] 状态,如下:
1 |
[WARN] 1.1 - Ensure a separate partition for containers has been created |
默认情况下,Docker使用主机上的/var/lib/docker存储所有文件,包含由默认驱动器创建的所有镜像、容器和数据卷。这表示这个文件夹会快速变大。因为我的主机运行的是单个分区(取决于容器所做的事),这可能会填满整个磁盘,会导致我的主机不可用:
1 2 3 4 |
[WARN] 1.5 - Ensure auditing is configured for the Docker daemon [WARN] 1.6 - Ensure auditing is configured for Docker files and directories - /var/lib/docker [WARN] 1.7 - Ensure auditing is configured for Docker files and directories - /etc/docker [WARN] 1.10 - Ensure auditing is configured for Docker files and directories - /etc/default/docker |
标出这些警告是因为没有安装auditd,以及Docker守护进程和相关联的文件没有审计规则,参见博客文章https://www.linux.com/learn/customized-file-monitoring-auditd/。
Docker守护进程配置
我的Docker守护进程配置标出了8个为[WARN]状态,如下所示:
1 |
[WARN] 2.1 - Ensure network traffic is restricted between containers on the default bridge |
默认情况下,Docker对相同主机上容器间的通讯不做任何限制。可以改变这一行为,更多有关Docker网络的信息,参见:https://docs.docker.com/engine/userguide/networking/。
1 |
[WARN] 2.5 - Ensure aufs storage driver is not used |
译者注:Alan测试时显示为 PASS
早期的Docker中大量的使用了AUFS,但现在它已不再被视作最佳实践,因为可能会带来主机内核的问题:
1 |
[WARN] 2.8 - Enable user namespace support |
默认用户命令空间没有被重映射。对它们进行映射如今会导致多个Docker功能的问题,参见https://docs.docker.com/engine/reference/commandline/dockerd/来了解已很有限制的更多信息:
1 |
[WARN] 2.11 - Ensure that authorization for Docker client commands is enabled |
默认Docker的安装不限制Docker守护进程的访问,你可以通过启用一个认证插件来限制访问为认证用户。更多细节,参见https://docs.docker.com/engine/extend/plugins_authorization/:
1 |
[WARN] 2.12 - Ensure centralized and remote logging is configured |
因为我只运行了单主机,没有使用服务,如rsyslog来将Docker主机的日志发送到中央服务器上,也没有在我的Docker守护进程上配置日志驱动器。参见https://docs.docker.com/engine/admin/logging/overview/了解更多信息:
1 |
[WARN] 2.14 - Ensure live restore is Enabled |
–live-restore标记启动Docker中无守护进程容器的全面支持,这表示在守护进程关闭时,不停止容器,它们将继续运行,并且在重启时与容器进行恰当的重新连接。默认因向后兼容性的问题并没有启动,更多信息,参见https://docs.docker.com/engine/admin/live-restore/:
1 |
[WARN] 2.15 - Ensure Userland Proxy is Disabled |
你的容器有两种方式路由到外部世界,通过回环NAT或userland代理。对大部分安装,更推荐回环NAT模式,因为它利用了iptable并且性能更佳。在无法使用它时,Docker使用userland代理。大部分现代操作系统上的Docker软件都支持回环NAT。有关如何禁用userland代理的详细信息,参见https://docs.docker.com/engine/userguide/networking/default_network/binding/:
1 |
[WARN] 2.18 - Ensure containers are restricted from acquiring new privileges |
这会停止容器内通过设置suid和sgid无法获取其它权限的进程,这会限制任意尝试访问授权二进制的危险操作。
Docker守护进程配置文件
这部分中没有 [WARN] 状态的项目,这在意料之中,因为Docker使用Docker Machine进行部署。
容器镜像和构建文件
针对容器镜像和构建文件我有三个状态为[WARN]的,你会看到多行警告的状态前缀为*:
1 2 3 4 |
[WARN] 4.1 - Ensure a user for the container has been created [WARN] * Running as root: sshd [WARN] * Running as root: priv-nginx [WARN] * Running as root: root-nginx |
我运行的容器中的进程都是以root用户运行的,这是大部分容器的默认动作。更多信息请参见https://docs.docker.com/engine/security/security/:
1 |
[WARN] 4.5 - Ensure Content trust for Docker is Enabled |
启动Docker的内容信任确保你所拉取的容器镜像的归属可信,因为在你推送的时候会进行数字签名,这意味着你可总可以运行所想要运行的镜像。有关内容信任的更多信息,参见https://docs.docker.com/engine/security/trust/content_trust/:
1 2 3 4 |
[WARN] 4.6 - Ensure HEALTHCHECK instructions have been added to the container image [WARN] * No Healthcheck found: [sshd:latest] [WARN] * No Healthcheck found: [ubuntu:16.04] [WARN] * No Healthcheck found: [nginx:latest] |
在构建你的镜像时,可以在HEALTHCHECK中构建,这样可以保证在容器由你的镜像启动时,Docker会定期检查你的容器的状态,并且在需要的情况下重启或重新发布容器。更多信息请参见https://docs.docker.com/engine/reference/builder/#healthcheck。
容器运行时
我们Docker主机上启动容器时对审所知甚少,有大量的漏洞存在,总共有11处:
1 2 3 |
[WARN] 5.2 - Ensure SELinux security options are set, if applicable [WARN] * No SecurityOptions Found: sshd [WARN] * No SecurityOptions Found: root-nginx |
以上的漏洞是一个误判,我们并没有运行SELinux,因为这是一台Ubuntu机器,SELinux仅用于Red Hat相关机器,而5.1显示的则是我们所要的[PASS]结果:
1 |
[PASS] 5.1 - Ensure AppArmor Profile is Enabled |
接下来的两个[WARN]状态由我们自己产生的,如下:
1 2 |
[WARN] 5.4 - Ensure privileged containers are not used [WARN] * Container running in Privileged mode: priv-nginx |
以下也是我们自己产生的:
1 2 |
[WARN] 5.6 - Ensure ssh is not run within containers [WARN] * Container running sshd: sshd |
这些可以被安全地忽略,你应该很少会启动以特权模式启动一个容器。仅当你的容器需要与Docker主机上运行的Docker引擎进行交互时才需要,例如,在你运行图形界面(如Portainer)时,在第十一章 Portainer: 一个Docker的GUI中进行了讲解。
我们还讨论了你不应该在容器中运行SSH,只有极少数的用例,如在特定网络中运行跳板机,但这些是特例。
接下来两个[WARN]状态的标记是因为,Docker中默认Docker主机上所有运行的容器平均共享资源;设置容器的内存和 CPU 优先级限制会确保你希望的优先级较高的容器不会被优先级较低的容器抢占资源。
1 2 3 4 5 6 7 8 |
[WARN] 5.10 - Ensure memory usage for container is limited [WARN] * Container running without memory restrictions: sshd [WARN] * Container running without memory restrictions: priv-nginx [WARN] * Container running without memory restrictions: root-nginx [WARN] 5.11 - Ensure CPU priority is set appropriately on the container [WARN] * Container running without CPU restrictions: sshd [WARN] * Container running without CPU restrictions: priv-nginx [WARN] * Container running without CPU restrictions: root-nginx |
本章前面已讨论过,如果可能,请以只读方式启动容器,并为已知需写入数据的进程挂载数据卷:
1 2 3 4 |
[WARN] 5.12 - Ensure the container's root filesystem is mounted as read only [WARN] * Container running with root FS mounted R/W: sshd [WARN] * Container running with root FS mounted R/W: priv-nginx [WARN] * Container running with root FS mounted R/W: root-nginx |
抛出以下标记的原因是我们没有告诉Docker把暴露的端口与Docker主机上指定的IP地址进行绑定:
1 2 |
[WARN] 5.13 - Ensure incoming container traffic is binded to a specific host interface [WARN] * Port being bound to wildcard IP: 0.0.0.0 in sshd |
因为我们的测试Docker主机只有单个NIC(网卡),这不是什么问题,但如果我的Docker有多个网络接口,那么这个容器会暴露在所有这些网络中,就会成为问题了,比如,我有内网和外网。详细信息参见https://docs.docker.com/engine/userguide/networking/:
1 2 3 4 |
[WARN] 5.14 - Ensure 'on-failure' container restart policy is set to '5' [WARN] * MaximumRetryCount is not set to 5: sshd [WARN] * MaximumRetryCount is not set to 5: priv-nginx [WARN] * MaximumRetryCount is not set to 5: root-nginx |
虽然我们没有使用–restart标记来启动我们的容器,MaximumRetryCount没有默认值。这表示如果容器不断失败,它还在那里尝试重启。这对Docker可能带来负面效果,将MaximumRetryCount添加为5表示容器会尝试重启5次,然后放弃:
1 2 3 4 |
[WARN] 5.25 - Ensure the container is restricted from acquiring additional privileges [WARN] * Privileges not restricted: sshd [WARN] * Privileges not restricted: priv-nginx [WARN] * Privileges not restricted: root-nginx |
默认情况下,Docker不对进程或其子进程通过suid或sgid获取新权限进行限制。有关如何停止这一行为的更多细节,参见http://www.projectatomic.io/blog/2016/03/no-new-privs-docker/:
1 2 3 4 |
[WARN] 5.26 - Ensure container health is checked at runtime [WARN] * Health check not set: sshd [WARN] * Health check not set: priv-nginx [WARN] * Health check not set: root-nginx |
再次,我们没有使用健康检查,也即Docker不会定期检查容器的状态。查看GitHub问题中引入这一功能的拉取请求,可浏览https://github.com/moby/moby/pull/22719/:
1 2 3 4 |
[WARN] 5.28 - Ensure PIDs cgroup limit is used [WARN] * PIDs limit not set: sshd [WARN] * PIDs limit not set: priv-nginx [WARN] * PIDs limit not set: root-nginx |
攻击者可能通过在容器中使用一条命令来触发fork炸弹。这会导致Docker主机的崩溃,唯一的恢复方式是重启主机。你可以使用–pids-limit来进行保护。更多信息,但看https://github.com/moby/moby/pull/18697/这一拉取请求。
Docker安全运维
这一部分包含有关最佳实践的[INFO],如下:
1 2 3 4 5 |
[INFO] 6 - Docker Security Operations [INFO] 6.1 - Avoid image sprawl [INFO] * There are currently: 4 images [INFO] 6.2 - Avoid container sprawl [INFO] * There are currently a total of 4 containers, with 4 of them currently running |
译者注:此处结果与原书略有不同
Docker Swarm配置
这一部分包含[PASS]信息,因为我们没有在主机上启用 Docker Swarm:
1 2 3 4 5 6 7 8 9 10 11 |
[INFO] 7 - Docker Swarm Configuration [PASS] 7.1 - Ensure swarm mode is not Enabled, if not needed [PASS] 7.2 - Ensure the minimum number of manager nodes have been created in a swarm (Swarm mode not enabled) [PASS] 7.3 - Ensure swarm services are binded to a specific host interface (Swarm mode not enabled) [PASS] 7.4 - Ensure data exchanged between containers are encrypted on different nodes on the overlay network [PASS] 7.5 - Ensure Docker's secret management commands are used for managing secrets in a Swarm cluster (Swarm mode not enabled) [PASS] 7.6 - Ensure swarm manager is run in auto-lock mode (Swarm mode not enabled) [PASS] 7.7 - Ensure swarm manager auto-lock key is rotated periodically (Swarm mode not enabled) [PASS] 7.8 - Ensure node certificates are rotated as appropriate (Swarm mode not enabled) [PASS] 7.9 - Ensure CA certificates are rotated as appropriate (Swarm mode not enabled) [PASS] 7.10 - Ensure management plane traffic has been separated from data plane traffic (Swarm mode not enabled) |
Docker Bench的总结
可以看出,对Docker主机上运行Docker Bench是种很好的方式,让你理解Docker主机如何一步步地符合CIS Docker基准;它显然比手动的过230页文档的每条测试要更易于管理。
第三方安全服务
结束本章之前,我们将来看一些第三方服务,它们帮助你评估镜像的漏洞,
Quay
Quay是一个CoreOS的镜像仓库服务,被Red Hat收购,类似Docker Hub/仓库,一个区别是Quay实际上会在镜像推送/构建时执行安全检查。
你可以通过所选镜像的仓库标签来查看扫描结果,在那里会有一列安全扫描。在以下截图中可以看到,我们创建的示例镜像没有安全问题:
点击Passed会进入一个镜像中所监测到的漏洞的详细分解。因为我们这里不存在漏洞(好事),所以这个界面没有什么内容。但是,点击左侧菜单的Packages图标会显示扫描所发现的一系列包。就我们的测试镜像而言,有29个无漏洞的包,都与包的版本确定信息显示在这里,还包含它们是如何引入到镜像中的:
你还会看到,Quay在扫描我们公开的镜像,托管在Quay所提供的免费开源方案中。安全扫描是Quay的所有方案的标准服务。
Clair
Clair是一个来自CoreOS的开源项目,它是一款提供Quay托管版本以及商业支持企业版的静态分析功能服务。
它通过创建以下漏洞数据库的本地镜像来运作:
- Debian安全漏洞追踪器: https://security-tracker.debian.org/tracker/
- Ubuntu CVE追踪器: https://launchpad.net/ubuntu-cve-tracker/
- Red Hat安全数据: https://www.redhat.com/security/data/metrics/
- Oracle Linux安全数据: https://linux.oracle.com/security/
- Alpine SecDB: https://git.alpinelinux.org/cgit/alpine-secdb/
- NIST NVD: https://nvd.nist.gov/
拥有了镜像数据资源后,它挂载镜像文件系统,然后执行所安装包的扫描,将它们与前面的数据源的签名进行比对。
Clair并不是一个直接的服务,它只有API驱动的接口,并且API默认并不带有漂亮的网页界面和命令行工具。它的API文档参见:https://coreos.com/clair/docs/latest/api_v1.html。
安装指南参见项目的GitHub页面:https://github.com/coreos/clair/。
同时,你可以在其集成页面找到支持Clair的工具列表:https://coreos.com/clair/docs/latest/integrations.html。
Anchore
我们要讲解的最后一个工具是Anchore。有多个版本,有云端的版本和本地资产安装的企业版,都带有全面的网页图形界面。有一个与Jenkins关联的版本,同时有开源命令行扫描器,我们下面就要来了解。
这个版本以Docker Compose进行发布,因此我们应先从创建所需的文件夹开始,并且我们还将从项目GitHub仓库下载该Docker Compose和基本配置文件。
1 2 3 4 |
$ mkdir anchore anchore/config $ cd anchore $ curl https://raw.githubusercontent.com/anchore/anchore-engine/master/scripts/docker-compose/docker-compose.yaml -o docker-compose.yaml $ curl https://raw.githubusercontent.com/anchore/anchore-engine/master/scripts/docker-compose/config.yaml -o config/config.yaml |
现在基本文件已就位,你可以使用如下命令拉取镜像并启动容器:
1 2 |
$ docker-compose pull $ docker-compose up -d |
在我们与Anchore部署交互之前,需要安装命令行客户端。如果你使用macOS,那么你需要运行如下命令,如果已安装 pip 请忽略第一条命令:
1 2 3 |
$ sudo easy_install pip $ pip install --user anchorecli $ export PATH=${PATH}:${HOME}/Library/Python/2.7/bin |
对于Ubuntu用户,你应该运行如下命令,这次哪果已安装 pip 请忽略前两条命令:
1 2 3 |
$ sudo apt-get update $ sudo apt-get install python-pip $ sudo pip install anchorecli |
安装后,你可以运行如下命令来检查安装的状态:
1 |
$ anchore-cli --u admin --p foobar system status |
这会显示安装的整体状态,初次启动可能会花费一到两分钟来显示所有内容:
1 2 3 4 5 6 |
$ anchore-cli --u admin --p foobar system status Service apiext (dockerhostid-anchore-engine, http://anchore-engine:8228): up Service catalog (dockerhostid-anchore-engine, http://anchore-engine:8082): up Engine DB Version: 0.0.9 Engine Code Version: 0.3.4 |
下一条命令显示Anchore处于数据库同步的什么位置:
1 |
$ anchore-cli --u admin --p foobar system feeds list |
从下面的输出看以看出,我的软件当前在同步CentOS 6数据库。这一进程会花费数小时,但在我们的例子中,我们将扫描一个基于Alpine Linux的镜像:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
$ anchore-cli --u admin --p foobar system feeds list Feed Group LastSync RecordCount vulnerabilities alpine:3.3 2019-05-01T01:33:04.758144 457 vulnerabilities alpine:3.4 2019-05-01T01:33:16.418498 681 vulnerabilities alpine:3.5 2019-05-01T01:33:32.340808 875 vulnerabilities alpine:3.6 2019-05-01T01:33:49.943712 1051 vulnerabilities alpine:3.7 2019-05-01T01:34:09.107003 1125 vulnerabilities alpine:3.8 None 0 vulnerabilities alpine:3.9 None 0 vulnerabilities amzn:2 None 0 vulnerabilities centos:5 None 0 vulnerabilities centos:6 None 0 vulnerabilities centos:7 None ... |
接下来我们需要选择一个镜像来扫描,我们来选取一个较老的镜像,如下:
1 |
$ anchore-cli --u admin --p foobar image add docker.io/russmckendrick/moby-counter:old |
初次运行扫描会花费一到两分钟,可以运行如下命令来检查状态:
1 |
$ anchore-cli --u admin --p foobar image list |
过一会儿后,状态应该会由analyzing变为analyzed:
1 |
$ anchore-cli --u admin --p foobar image get docker.io/russmckendrick/moby-counter:old |
这会显示镜像的总览,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
$ anchore-cli --u admin --p foobar image get docker.io/russmckendrick/moby-counter:old Image Digest: sha256:157cbbab5607b45c02c5f6a609f40bf95561b7fbdf21a63ca85cb8813a2ef700 Parent Digest: sha256:157cbbab5607b45c02c5f6a609f40bf95561b7fbdf21a63ca85cb8813a2ef700 Analysis Status: analyzed Image Type: docker Image ID: de9ae734622dc82cdc9b40654d536694bd6a04e058d17a6ea39c8ec867ecec18 Dockerfile Mode: Guessed Distro: alpine Distro Version: 3.7.0 Size: 28067464 Architecture: amd64 Layer Count: 6 Full Tag: docker.io/russmckendrick/moby-counter:old |
然后你可以通过运行如下命令来查看问题列表(如果有问题):
1 |
$ anchore-cli --u admin --p foobar image vuln docker.io/russmckendrick/moby-counter:old os |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
$ anchore-cli --u admin --p foobar image vuln docker.io/russmckendrick/moby-counter:old os Vulnerability ID Package Severity Fix Vulnerability URL CVE-2018-0495 libressl2.6-libcrypto-2.6.3-r0 Low 2.6.5-r0 http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-0495 CVE-2018-0495 libressl2.6-libssl-2.6.3-r0 Low 2.6.5-r0 http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-0495 CVE-2018-5407 libcrypto1.0-1.0.2n-r0 Low 1.0.2q-r0 http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-5407 CVE-2018-5407 libssl1.0-1.0.2n-r0 Low 1.0.2q-r0 http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-5407 CVE-2017-3738 libcrypto1.0-1.0.2n-r0 Medium 1.0.2n-r0 http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-3738 CVE-2017-3738 libssl1.0-1.0.2n-r0 Medium 1.0.2n-r0 http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-3738 CVE-2018-0732 libcrypto1.0-1.0.2n-r0 Medium 1.0.2o-r1 http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-0732 CVE-2018-0732 libressl2.6-libcrypto-2.6.3-r0 Medium 2.6.5-r0 http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-0732 CVE-2018-0732 libressl2.6-libssl-2.6.3-r0 Medium 2.6.5-r0 http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-0732 CVE-2018-0732 libssl1.0-1.0.2n-r0 Medium 1.0.2o-r1 http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-0732 CVE-2018-0733 libcrypto1.0-1.0.2n-r0 Medium 1.0.2o-r0 http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-0733 ... |
可以看到,每个列出的包都有当前版本,CVE问题的链接以及一个修复报出问题的版本号确认。
可以使用如下命令来删除Anchore容器:
1 2 |
$ docker-compose stop $ docker-compose rm |
总结
本章中,我们讲解了Docker安全的方方面面。首先,我们来看一些你在运行容器时安全方面必须考虑的事情(对比一般的虚拟机)。我们看了它的优势以及Docker主机,然后讨论了镜像的可信度。然后我们了解了可用于安全方面的Docker命令。
我们启动了一个只读容器,这样我们可以最小化攻击者在我们运行的容器中带来的潜在损害。因为不是所有的应用可以运行在只读容器中,然后我们来看了如何追踪镜像在启动后所产生的变化。在查看问题时,能够在运行时中发现文件中所做的改变总是很有用的。
然后,我们讨论了互联网安全中心的Docker指南。该指南有助于你设置Docker环境的方方面面。最后,我们来看了Docker Bench安全。我们了解了如何启动并支持它,并且我们快速浏览了一个输出内容的示例。我们分析了输出来了解它的含义。记住主机配置所涵盖的七项:主机配置、Docker守护进程配置、Docker守护进程配置文件、容器镜像和构建文件、容器运行时、容器安全运维和Docker Swarm配置。
下一章中,我们将来看Docker如何融入已有的工作流,以及一些使用容器方法的新的方式。
课后问题
- 在启动容器时,我们将它或其一部分设为只读?
- 每个容器上你应该运行多少个进程?
- 检查Docker安装是否符合CIS Docker基准的最好的方式是什么?
- 在运行Docker Bench Security应用时,应该挂载什么?
- 是非题:Quay仅支持私有镜像的扫描。
扩展阅读
更多信息,请访问网站https://www.cisecurity.org/;可在https://www.cisecurity.org/benchmark/docker/上找到Docker基准。