Introduction

到目前为止,本模块已向您介绍了 Docker 的基础知识以及与容器相关的潜在漏洞。本房间将向您介绍保护容器的各种方法。

学习目标

  • 保护 Docker 守护进程以防止未经授权的交互。
  • 正确分配容器的权限(功能)。
  • 防止有故障或恶意的容器耗尽系统。
  • 利用 Seccomp 和 AppArmor 等安全功能来定义容器与操作系统的行为方式。
  • 通过检查 Docker 映像是否存在漏洞来养成良好的卫生习惯 - 实施检查代码是否存在漏洞的框架和工具。

先决条件

为了完成本房间,您必须熟悉 Docker 的各个组件。我强烈建议在继续之前完成 Docker 简介 房间。

Protecting the Docker Daemon

您可能还记得 容器漏洞 房间中提到,Docker 守护程序负责处理请求,例如管理容器以及将图像拉取或上传到 Docker 注册表。Docker 可以远程管理,并且通常在 CI(持续集成)和 CD(持续开发)管道中完成。例如,在另一台主机上的容器中推送和运行新代码以检查错误。

如果攻击者可以与 Docker 守护程序交互,他们就可以与容器和图像交互。例如,他们启动自己的(恶意)容器或访问运行具有敏感信息(例如数据库)的应用程序的容器。

默认情况下,Docker 守护程序不会暴露给网络,必须手动配置。但是,暴露 Docker 守护程序是一种常见做法(尤其是在 CI/CD 管道等云环境中)。

实施安全通信和身份验证方法(例如下面列出的方法)对于防止未经授权访问 Docker 守护程序非常重要。

SSH

开发人员可以使用 SSH 身份验证与运行 Docker 的其他设备进行交互。为此,Docker 使用可以视为配置文件的上下文。这些配置文件允许开发人员保存和交换其他设备的配置。例如,开发人员可能有一个用于开发的 Docker 设备上下文,以及另一个用于生产的 Docker 设备上下文。

注意:您必须具有对远程设备的 SSH 访问权限,并且您在远程设备上的用户帐户必须具有执行 Docker 命令的权限。

作为开发人员,您需要在设备上创建 Docker 上下文。请参阅下面的代码片段以在 Docker 中创建上下文。

创建新的 Docker 上下文

client@thm:~# docker context create
--docker host=ssh://myuser@remotehost
--description="Development Environment"
development-environment-host

Successfully created context "development-environment-host"

完成后,您可以切换到此上下文,其中所有与 Docker 相关的命令现在都将在远程主机上执行。

使用我们新创建的 Docker 上下文

cmnatic@thm:~# docker context use development-environment-host

Current context is now "development-environment-host"

要退出此上下文并使用您自己的 Docker 引擎,您可以通过 docker context use default 恢复为“默认”。

注意:这并不完全安全。例如,弱 SSH 密码可能导致攻击者能够进行身份验证。强烈建议使用强密码。下面列出了一些强密码提示:

  • 大量字符(即 12-22+)
  • 特殊字符,如 !、@、#、$
  • 大写字母和数字不时出现(即 sUp3rseCreT!PaSSw0rd!)

Docker 上下文允许您直接通过 SSH 与 Docker 守护程序交互,这是一种安全且加密的通信方式。

TLS 加密

Docker 守护程序也可以使用 HTTP/S 进行交互。例如,如果 Web 服务或应用程序要与远程设备上的 Docker 交互,这将非常有用。

为了安全地执行此操作,我们可以利用加密协议 TLS 来加密设备之间发送的数据。在 TLS 模式下配置时,Docker 将仅接受已针对您希望远程执行 Docker 命令的设备签名的设备的远程命令。

注意:创建和管理 TLS 证书超出了本房间的范围,因为您通常需要考虑诸如环境的到期日期和加密强度等因素。创建证书后,您可以告诉 Docker 使用生成的证书在 TLS 模式下运行。

在您发出命令的主机(服务器)上:

在 TLS 模式下运行 Docker

server@thm:~# dockerd --tlsverify --tlscacert=myca.pem --tlscert=myserver-cert.pem --tlskey=myserver-key.pem -H=0.0.0.0:2376

在您发出命令的主机(客户端)上:

告诉 Docker(本地)使用 TLS 进行身份验证

client@thm:~# docker --tlsverify --tlscacert=myca.pem --tlscert=client-cert.pem --tlskey=client-key.pem -H=SERVERIP:2376 info

注意:请务必记住,这并不能保证安全。例如,任何拥有有效证书和私钥的人都可以成为“受信任”的设备。我在下表中解释了生成 TLS 证书和密钥时使用的参数:

Argument Description
--tlscacert 此参数指定证书颁发机构的证书。证书颁发机构是颁发用于识别设备的证书的受信任实体。
--tlscert 此参数指定用于识别设备的证书。
--tlskey 此参数指定用于解密发送到设备的通信的私钥。

Implementing Control Groups

控制组(也称为 cgroups)是 Linux 内核的一项功能,有助于限制和优先处理进程可以利用的系统资源数量。

例如,可以限制诸如应用程序之类的进程仅使用一定数量的 RAM 或处理能力,或者使其优先于其他进程。这通常可以提高系统稳定性,并允许管理员更好地跟踪系统资源使用情况。

在 Docker 环境中,实施 cgroups 有助于实现隔离和稳定性。由于 cgroups 可用于确定容器使用的资源数量(或优先级),因此有助于防止有故障或恶意的容器耗尽系统。当然,最好的机制是防止这种情况发生,但防止容器破坏整个系统是极好的第二道防线。

Docker 上默认不启用此行为,必须在启动容器时为每个容器启用。下表提供了用于指定容器可以使用的资源限制的开关:

Type of Resource Argument Example
CPU --cpus (in core count) docker run -it --cpus="1" mycontainer
Memory --memory (以 k、m、g 表示千字节、兆字节或千兆字节) docker run -it --memory="20m" mycontainer

您还可以在容器运行后更新此设置。为此,请使用 docker update 命令、新的内存值和容器名称。例如:docker update --memory="40m" mycontainer

您可以使用 docker inspect containername 命令查看有关容器的信息(包括设置的资源限制)。如果资源限制设置为 0,则表示未设置任何资源限制。

使用 Docker inspect 列出为容器设置的资源限制。

cmnatic@thm:~# docker inspect mycontainer
--cropped for brevity--
"Memory": 0,
"CpuQuota": 0,
"CpuRealtimePeriod": 0,
"CpuRealtimeRuntime": 0,
"CpusetCpus": "",
"CpusetMems": "",
"CpuCount": 0,
"CpuPercent": 0,
--cropped for brevity--

Docker 使用命名空间来创建隔离环境。例如,命名空间是一种在不影响其他进程的情况下执行不同操作的方式。可以将它们想象成办公室里的房间;每个房间都有各自的用途。这个办公室的一个房间里发生的事情不会影响另一个办公室发生的事情。这些命名空间通过将进程彼此隔离来提供安全性。

Preventing “Over-Privileged” Containers

首先,我们需要了解特权容器在此上下文中的含义。特权容器是指对主机具有不受限制访问权限的容器。

容器化的全部意义在于将容器与主机“隔离”。通过在“特权”模式下运行 Docker 容器,可以绕过将容器与主机隔离的正常安全机制。虽然特权容器可以有合法用途,例如运行 Docker-In-Docker(容器中的容器)或用于调试目的,但它们极其危险。

在“特权”模式下运行 Docker 容器时,Docker 会将所有可能的功能分配给容器,这意味着容器可以执行和访问主机上的任何内容(例如文件系统)。

depicting the level of access a non-privileged and privileged container has to the host

您可能会问,什么是能力?能力是 Linux 的一项安全功能,它决定了进程在细粒度上可以做什么和不能做什么。传统上,进程可以拥有完全的 root 权限,也可以没有任何权限,这可能很危险,因为我们可能不希望进程拥有完全的 root 权限,因为这意味着它将不受限制地访问系统。

能力使我们能够微调进程拥有的权限。我在下表中列出了一些标准能力、它们转换为哪些权限以及它们可能在何处使用:

Capability Description Use Case
CAP_NET_BIND_SERVICE 此功能允许服务绑定到端口,特别是 1024 以下的端口,这通常需要 root 权限。 允许 Web 服务器绑定到端口 80,无需 root 访问权限。
CAP_SYS_ADMIN 此功能提供各种管理权限,包括能够挂载/卸载文件系统、更改网络设置、执行系统重启、关机等。 您可能会在自动执行管理任务的过程中发现此功能。例如,修改用户或启动/停止服务。
CAP_SYS_RESOURCE 此功能允许进程修改可用资源的最大限制。例如,进程可以使用更多内存或带宽。 此功能可以精细地控制进程可以消耗的资源数量。这可以是增加资源量,也可以是减少资源量。

总而言之,特权容器是被分配了完全权限(即完全 root 访问权限)的容器。攻击者可以使用此方法逃离容器。如果您想要做家庭作业,此过程已在 容器漏洞 房间中进行了演示。

建议单独为容器分配功能,而不是使用 --privileged 标志运行容器(这将分配所有功能)。例如,您可以在运行容器时包含 --cap-add=NET_BIND_SERVICE,为在端口 80 上运行 Web 服务器的容器分配 NET_BIND_SERVICE 功能。

为容器分配 NET_BIND_SERVICE 功能

cmnatic@thm:~# docker run -it --rm --cap-drop=ALL --cap-add=NET_BIND_SERVICE mywebserver

最后,可以使用命令“capsh –print”来确定分配给进程的功能。

使用 capsh 列出当前分配的功能

cmnatic@thm:~# capsh --print
Current: =
Bounding set = cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner
-- cut for brevity ---
cap_sys_rawio,cap_sys_chroot,cap_sys_ptrace,cap_sys_pacct,cap_sys_admin,
cap_sys_boot,cap_sys_nice,cap_sys_resource,cap_sys_time,cap_sys_tty_config,cap_mknod
-- cut for brevity ---
Ambient set =
Current IAB:
Securebits: 00/0x0/1'b0
secure-noroot: no (unlocked)
secure-no-suid-fixup: no (unlocked)
secure-keep-caps: no (unlocked)
secure-no-ambient-raise: no (unlocked)
uid=1000(cmnatic) euid=1000(cmnatic)
gid=1000(cmnatic)
groups=4(adm),20(dialout),24(cdrom),25(floppy),27(sudo),1000(cmnatic)

经常检查分配给容器的功能非常重要。当容器具有特权时,它会与主机共享相同的命名空间,这意味着容器可以访问主机上的资源 - 从而打破“隔离”环境。

Seccomp & AppArmor 101

Seccomp 是 Linux 的一个重要安全功能,它限制了程序可以执行和不能执行的操作。为了解释这一点,我们可以想象一下办公室入口处的保安。保安负责确保只有授权人员才能进入大楼,并且他们必须做他们应该做的事情。在这种情况下,Seccomp 就是保安。

Seccomp 允许您创建并执行应用程序可以执行的操作(系统调用)的规则列表。例如,允许应用程序进行系统调用以读取文件,但不允许它进行系统调用以打开新的网络连接(例如反向 shell)。

这些配置文件很有用,因为它们降低了攻击者执行恶意命令的能力,同时保持了应用程序的功能。例如,Web 服务器的 Seccomp 配置文件可能如下所示:

{
"defaultAction": "SCMP_ACT_ALLOW",
"architectures": [
"SCMP_ARCH_X86_64",
"SCMP_ARCH_X86",
"SCMP_ARCH_X32"
],
"syscalls": [
{ "names": [ "read", "write", "exit", "exit_group", "open", "close", "stat", "fstat", "lstat", "poll", "getdents", "munmap", "mprotect", "brk", "arch_prctl", "set_tid_address", "set_robust_list" ], "action": "SCMP_ACT_ALLOW" },
{ "names": [ "execve", "execveat" ], "action": "SCMP_ACT_ERRNO" }
]
}

此 Seccomp 配置文件:

允许读取和写入文件

允许创建网络套接字

但不允许执行(例如,execve)

要创建 Seccomp 配置文件,您只需使用您最喜欢的文本编辑器创建配置文件即可。此房间将使用 nano。下面提供了一个示例 Seccomp 配置文件 (profile.json)。此配置文件将允许读取和写入文件,但不允许网络连接。

{
"defaultAction": "SCMP_ACT_ALLOW",
"architectures": ["SCMP_ARCH_X86_64"],
"syscalls": [
{
"name": "socket",
"action": "SCMP_ACT_ERRNO",
"args": []
},
{
"name": "connect",
"action": "SCMP_ACT_ERRNO",
"args": []
},
{
"name": "bind",
"action": "SCMP_ACT_ERRNO",
"args": []
},
{
"name": "listen",
"action": "SCMP_ACT_ERRNO",
"args": []
},
{
"name": "accept",
"action": "SCMP_ACT_ERRNO",
"args": []
}
{
"name": "read",
"action": "SCMP_ACT_ALLOW",
"args": []
},
{
"name": "write",
"action": "SCMP_ACT_ALLOW",
"args": []
}
]
}

现在,我们的 Seccomp 配置文件已创建,我们可以在运行时使用 –security-opt seccomp 标志和 Seccomp 配置文件的位置将其应用到我们的容器中。例如:

在运行容器时应用我们的 Seccomp 配置文件

cmnatic@thm:~# docker run --rm -it --security-opt seccomp=/home/cmnatic/container1/seccomp/profile.json mycontainer

Docker 已在运行时应用了默认的 Seccomp 配置文件。但是,这可能不适合你的特定用例,特别是如果你希望在保持功能的同时进一步强化容器。你可以在此处了解有关将 Seccomp 与 Docker 结合使用的更多信息。

AppArmor

AppArmor 是 Linux 中类似的安全功能,因为它可以防止应用程序执行未经授权的操作。但是,它的工作方式与 Seccomp 不同,因为它不包含在应用程序中,而是包含在操作系统中。

此机制是一种强制访问控制 (MAC) 系统,它根据操作系统级别的一组规则确定进程可以执行的操作。要使用 AppArmor,我们首先需要确保它已安装在我们的系统上:

检查 AppArmor 是否已安装

cmnatic@thm:~# sudo aa-status
apparmor module is loaded.
34 profiles are loaded.

通过输出“apparmor module is loaded”,我们可以确认 AppArmor 已安装并启用。要将 AppArmor 配置文件应用于我们的容器,我们需要执行以下操作:

创建 AppArmor 配置文件
将配置文件加载到 AppArmor
使用新配置文件运行我们的容器
首先,让我们创建我们的 AppArmor 配置文件。您可以使用您最喜欢的文本编辑器来执行此操作。请注意,有一些工具可以帮助您根据您的 Dockerfile 生成 AppArmor 配置文件。但是,这超出了本房间的范围,并且可能“不可靠”。

下面提供了一个“Apache”Web 服务器的示例 AppArmor 配置文件 (profile.json):

可以读取位于 /var/www/、/etc/apache2/mime.types 和 /run/apache2 中的文件。
读取和写入 /var/log/apache2。
绑定到端口 80 的 TCP 套接字,但不绑定到其他端口或协议(如 UDP)。
无法从 /bin、/lib、/usr 等目录读取。

/usr/sbin/httpd {

capability setgid,
capability setuid,

/var/www/** r,
/var/log/apache2/** rw,
/etc/apache2/mime.types r,

/run/apache2/apache2.pid rw,
/run/apache2/*.sock rw,

# Network access
network tcp,

# System logging
/dev/log w,

# Allow CGI execution
/usr/bin/perl ix,

# Deny access to everything else
/** ix,
deny /bin/**,
deny /lib/**,
deny /usr/**,
deny /sbin/**
}

现在我们已经创建了 AppArmor 配置文件,我们需要将其导入 AppArmor 程序以便被识别。

将我们的 AppArmor 配置文件导入 AppArmor

cmnatic@thm:~# sudo apparmor_parser -r -W /home/cmnatic/container1/apparmor/profile.json

现在我们的 AppArmor 配置文件已导入,我们可以在运行时使用 –security-opt apparmor 标志和 AppArmor 配置文件的位置将其应用于我们的容器。例如:

在运行容器时应用我们的 AppArmor 配置文件

cmnatic@thm:~# docker run --rm -it --security-opt apparmor=/home/cmnatic/container1/apparmor/profile.json mycontainer

和 Seccomp 一样,Docker 在运行时已经应用了默认的 AppArmor 配置文件。但是,这可能不适合您的特定用例,特别是如果您希望在保持功能的同时进一步强化容器。您可以在此处了解有关将 AppArmor 与 Docker 结合使用的更多信息。

有什么区别
好吧,简而言之:

AppArmor 决定应用程序可以访问哪些资源(即 CPU、RAM、网络接口、文件系统等)以及它可以对这些资源采取哪些操作。
Seccomp 位于程序本身内,它限制了进程可以进行的系统调用(即 CPU 和操作系统的哪些部分起作用)。
重要的是要注意,这不是“非此即彼”的情况。Seccomp 和 AppArmor 可以结合起来为容器创建安全层。

Reviewing Docker Images

检查 Docker 镜像是一个非常重要的习惯。您会对在设备上运行未知代码持谨慎态度,那么为什么您会考虑在生产环境中运行它呢?

不幸的是,有许多恶意 Docker 镜像造成破坏的例子。例如,在 2020 年,Palo Alto 发现了加密挖掘 Docker 镜像,这些镜像被拉取(并可能运行)了超过 200 万次。

Docker Hub 上的镜像通常带有附加到存储库的 Dockerfile。例如,Docker Hub 显示 Dockerfile 的层(因此执行了命令)。

depicting the layers of an image on DockerHub.

在上图中,我们可以看到 DockerHub 上镜像的各个层。这些层是镜像构建过程中执行的步骤。

此外,Docker Hub 上镜像的开源代码存储库通常会包含在内,以便您查看整个 Dockerfile。

depicting the Dockerfile of an image stored in the code repository of the application.

在上图中,我们可以看到 Dockerfile 的代码。这使我们能够审核代码并准确了解容器中正在执行的操作。通过分析代码,我们可以检查漏洞或恶意操作。

Dive 等工具允许您通过检查在构建过程中在镜像的每一层执行和更改的内容来对 Docker 镜像进行逆向工程。

Reverse Engineering Docker Images using Dive

Compliance & Benchmarking

合规性和基准测试在保护资产(更不用说容器)方面发挥着至关重要的作用。让我们首先解释一下合规性。合规性是遵守法规和标准的过程,例如 NIST SP 800-190,这是美国国家标准与技术研究院制定的一套标准,为容器安全提供指导和最佳实践:

Compliance Framework Description URL
NIST SP 800-190 该框架概述了与容器相关的潜在安全问题并提供了解决这些问题的建议。 https://csrc.nist.gov/publications/detail/sp/800-190/final
ISO 27001 该框架是信息安全的国际标准。该标准指导信息安全管理系统的实施、维护和改进。 https://www.iso.org/standard/27001

请注意,您可能必须遵守与您的行业相关的其他框架。例如,金融或医疗行业。所有行业都有法规。例如,在医疗领域,HIPPA 适用于处理医疗数据。

另一方面,基准测试是一种用于查看组织遵守最佳实践情况的过程。基准测试使组织能够了解他们在哪些方面很好地遵循了最佳实践以及哪些方面需要进一步改进:

Benchmarking Tool Description URL
CIS Docker Benchmark 该工具可以评估容器是否符合CIS Docker Benchmark框架。 https://www.cisecurity.org/benchmark/docker
OpenSCAP 该工具可以评估容器与多个框架的合规性,包括 CIS Docker Benchmark、NIST SP-800-190 等。 https://www.open-scap.org/
Docker Scout 此工具是 Docker 本身提供的基于云的服务,用于扫描 Docker 镜像和库中的漏洞。此工具列出了存在的漏洞并提供了解决这些漏洞的步骤。 https://docs.docker.com/scout/
Anchore 该工具可以评估容器与多个框架的合规性,包括 CIS Docker Benchmark、NIST SP-800-190 等。 https://github.com/anchore/anchore-engine
Grype 此工具是一款现代且快速的 Docker 镜像漏洞扫描程序 https://github.com/anchore/grype

下面的终端提供了使用 Docker Scout 工具分析 Docker 镜像的示例。请注意,这需要事先安装。您可以阅读 Docker Scout 文档以了解更多信息。

使用 Docker Scout 扫描我们的“nginx”镜像以查找漏洞。

cmnatic@thm:~# docker scout cves local://nginx:latest
✓ SBOM of image already cached, 215 packages indexed
✗ Detected 22 vulnerable packages with a total of 45 vulnerabilities

## Overview
│ Analyzed Image
────────────────────┼──────────────────────────────
Target │ local://nginx:latest
digest │ 4df6f9ac5341
platform │ linux/amd64
vulnerabilities │ 0C 1H 18M 28L
size │ 91 MB
packages │ 215

## Packages and Vulnerabilities
0C 1H 1M 3L glibc 2.35-0ubuntu3.1
pkg:deb/ubuntu/glibc@2.35-0ubuntu3.1?os_distro=jammy&os_name=ubuntu&os_version=22.04
✗ HIGH CVE-2023-4911
https://scout.docker.com/v/CVE-2023-4911
Affected range : <2.35-0ubuntu3.4
Fixed version : 2.35-0ubuntu3.4
CVSS Score : 7.8
CVSS Vector : CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H

✗ MEDIUM CVE-2023-5156
https://scout.docker.com/v/CVE-2023-5156
Affected range : <2.35-0ubuntu3.5
Fixed version : 2.35-0ubuntu3.5
CVSS Score : 7.5
CVSS Vector : CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

✗ LOW CVE-2016-20013
https://scout.docker.com/v/CVE-2016-20013
Affected range : >=0
Fixed version : not fixed
CVSS Score : 7.5
CVSS Vector : CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

✗ LOW CVE-2023-4813
https://scout.docker.com/v/CVE-2023-4813
Affected range : <2.35-0ubuntu3.5
Fixed version : 2.35-0ubuntu3.5
CVSS Score : 5.9
CVSS Vector : CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H

✗ LOW CVE-2023-4806
https://scout.docker.com/v/CVE-2023-4806
Affected range : <2.35-0ubuntu3.5
Fixed version : 2.35-0ubuntu3.5
CVSS Score : 5.9
CVSS Vector : CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H

Practical

按下绿色的“启动机器”按钮,部署连接到此任务的机器。机器将以分屏视图启动。如果未显示,您可以按下页面右上角的蓝色“显示分屏视图”按钮。您的任务是使用机器上的 Grype 漏洞扫描程序来分析一些 Docker 镜像。

Grype 可用于分析 Docker 镜像和容器文件系统。您可以参考下表作为备忘单来回答此任务中的问题。

Example Description Command
Scanning a Docker image Scan a Docker image for vulnerabilities. grype imagename –scope all-layers
Scanning an exported container filesystem Scan an exported container filesystem (i.e. from docker image save). grype /path/to/image.tar

请注意,对于这个房间,您可以放心地忽略“无法检查漏洞数据库更新”警告信息。