381 lines
21 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Docker Security
{{#include ../../../banners/hacktricks-training.md}}
## **基本的 Docker 引擎安全性**
**Docker 引擎** 利用 Linux 内核的 **Namespaces****Cgroups** 来隔离容器,提供基本的安全层。通过 **Capabilities dropping**、**Seccomp** 和 **SELinux/AppArmor** 提供额外的保护,增强容器隔离。一个 **auth plugin** 可以进一步限制用户操作。
![Docker Security](https://sreeninet.files.wordpress.com/2016/03/dockersec1.png)
### 安全访问 Docker 引擎
Docker 引擎可以通过 Unix 套接字本地访问,也可以通过 HTTP 远程访问。对于远程访问,使用 HTTPS 和 **TLS** 确保机密性、完整性和身份验证是至关重要的。
Docker 引擎默认在 `unix:///var/run/docker.sock` 上监听。在 Ubuntu 系统上Docker 的启动选项在 `/etc/default/docker` 中定义。要启用对 Docker API 和客户端的远程访问,通过添加以下设置来通过 HTTP 套接字暴露 Docker 守护进程:
```bash
DOCKER_OPTS="-D -H unix:///var/run/docker.sock -H tcp://192.168.56.101:2376"
sudo service docker restart
```
然而,由于安全问题,不建议通过 HTTP 暴露 Docker 守护进程。建议使用 HTTPS 来保护连接。保护连接的主要方法有两种:
1. 客户端验证服务器的身份。
2. 客户端和服务器相互验证对方的身份。
证书用于确认服务器的身份。有关这两种方法的详细示例,请参阅 [**this guide**](https://sreeninet.wordpress.com/2016/03/06/docker-security-part-3engine-access/)。
### 容器镜像的安全性
容器镜像可以存储在私有或公共仓库中。Docker 提供了几种容器镜像的存储选项:
- [**Docker Hub**](https://hub.docker.com): Docker 的公共注册服务。
- [**Docker Registry**](https://github.com/docker/distribution): 一个开源项目,允许用户托管自己的注册表。
- [**Docker Trusted Registry**](https://www.docker.com/docker-trusted-registry): Docker 的商业注册表产品,具有基于角色的用户身份验证和与 LDAP 目录服务的集成。
### 镜像扫描
容器可能存在 **安全漏洞**这可能是由于基础镜像或在基础镜像上安装的软件造成的。Docker 正在进行一个名为 **Nautilus** 的项目该项目对容器进行安全扫描并列出漏洞。Nautilus 通过将每个容器镜像层与漏洞库进行比较来识别安全漏洞。
有关更多 [**information read this**](https://docs.docker.com/engine/scan/)。
- **`docker scan`**
**`docker scan`** 命令允许您使用镜像名称或 ID 扫描现有的 Docker 镜像。例如,运行以下命令以扫描 hello-world 镜像:
```bash
docker scan hello-world
Testing hello-world...
Organization: docker-desktop-test
Package manager: linux
Project name: docker-image|hello-world
Docker image: hello-world
Licenses: enabled
✓ Tested 0 dependencies for known issues, no vulnerable paths found.
Note that we do not currently have vulnerability data for your image.
```
- [**`trivy`**](https://github.com/aquasecurity/trivy)
```bash
trivy -q -f json <container_name>:<tag>
```
- [**`snyk`**](https://docs.snyk.io/snyk-cli/getting-started-with-the-cli)
```bash
snyk container test <image> --json-file-output=<output file> --severity-threshold=high
```
- [**`clair-scanner`**](https://github.com/arminc/clair-scanner)
```bash
clair-scanner -w example-alpine.yaml --ip YOUR_LOCAL_IP alpine:3.5
```
### Docker 镜像签名
Docker 镜像签名确保容器中使用的镜像的安全性和完整性。以下是简要说明:
- **Docker 内容信任** 利用 Notary 项目,基于更新框架 (TUF),来管理镜像签名。有关更多信息,请参见 [Notary](https://github.com/docker/notary) 和 [TUF](https://theupdateframework.github.io)。
- 要激活 Docker 内容信任,请设置 `export DOCKER_CONTENT_TRUST=1`。此功能在 Docker 版本 1.10 及更高版本中默认关闭。
- 启用此功能后仅可以下载签名的镜像。初始镜像推送需要为根密钥和标记密钥设置密码Docker 还支持 Yubikey 以增强安全性。更多详细信息可以在 [这里](https://blog.docker.com/2015/11/docker-content-trust-yubikey/) 找到。
- 尝试在启用内容信任的情况下拉取未签名的镜像会导致 "No trust data for latest" 错误。
- 在第一次之后的镜像推送中Docker 会要求输入存储库密钥的密码以签署镜像。
要备份您的私钥,请使用以下命令:
```bash
tar -zcvf private_keys_backup.tar.gz ~/.docker/trust/private
```
在切换 Docker 主机时,必须移动根密钥和存储库密钥以维持操作。
## 容器安全特性
<details>
<summary>容器安全特性摘要</summary>
**主要进程隔离特性**
在容器化环境中,隔离项目及其进程对于安全和资源管理至关重要。以下是关键概念的简化解释:
**命名空间**
- **目的**:确保进程、网络和文件系统等资源的隔离。特别是在 Docker 中,命名空间使容器的进程与主机和其他容器分开。
- **`unshare` 的使用**`unshare` 命令(或底层系统调用)用于创建新的命名空间,提供额外的隔离层。然而,虽然 Kubernetes 本身并不阻止这一点,但 Docker 确实会。
- **限制**:创建新命名空间并不允许进程恢复到主机的默认命名空间。要穿透主机命名空间,通常需要访问主机的 `/proc` 目录,使用 `nsenter` 进行进入。
**控制组 (CGroups)**
- **功能**:主要用于在进程之间分配资源。
- **安全方面**CGroups 本身不提供隔离安全,除了 `release_agent` 特性,如果配置错误,可能会被利用进行未经授权的访问。
**能力丢弃**
- **重要性**:这是进程隔离的重要安全特性。
- **功能**:通过丢弃某些能力来限制根进程可以执行的操作。即使进程以根权限运行,缺乏必要的能力也会阻止其执行特权操作,因为系统调用将因权限不足而失败。
这些是进程丢弃其他能力后的 **剩余能力**
```
Current: cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap=ep
```
**Seccomp**
它在 Docker 中默认启用。它有助于**进一步限制进程可以调用的系统调用**。\
**默认的 Docker Seccomp 配置文件**可以在 [https://github.com/moby/moby/blob/master/profiles/seccomp/default.json](https://github.com/moby/moby/blob/master/profiles/seccomp/default.json) 找到。
**AppArmor**
Docker 有一个可以激活的模板:[https://github.com/moby/moby/tree/master/profiles/apparmor](https://github.com/moby/moby/tree/master/profiles/apparmor)
这将允许减少能力、系统调用、对文件和文件夹的访问...
</details>
### Namespaces
**Namespaces** 是 Linux 内核的一个特性,它**将内核资源进行分区**,使得一组**进程****看到**一组**资源**,而**另一**组**进程**看到**不同**的资源。该特性通过为一组资源和进程使用相同的命名空间来工作,但这些命名空间指向不同的资源。资源可以存在于多个空间中。
Docker 利用以下 Linux 内核命名空间来实现容器隔离:
- pid namespace
- mount namespace
- network namespace
- ipc namespace
- UTS namespace
有关命名空间的**更多信息**,请查看以下页面:
{{#ref}}
namespaces/
{{#endref}}
### cgroups
Linux 内核特性**cgroups**提供了**限制资源如 CPU、内存、IO、网络带宽**等的能力适用于一组进程。Docker 允许使用 cgroup 特性创建容器,从而实现对特定容器的资源控制。\
以下是一个用户空间内存限制为 500m内核内存限制为 50mCPU 共享为 512blkio-weight 为 400 的容器。CPU 共享是控制容器 CPU 使用的比例。它的默认值为 1024范围在 0 到 1024 之间。如果三个容器的 CPU 共享都是 1024则在 CPU 资源争用的情况下,每个容器最多可以占用 33% 的 CPU。blkio-weight 是控制容器 IO 的比例。它的默认值为 500范围在 10 到 1000 之间。
```
docker run -it -m 500M --kernel-memory 50M --cpu-shares 512 --blkio-weight 400 --name ubuntu1 ubuntu bash
```
要获取容器的 cgroup您可以执行
```bash
docker run -dt --rm denial sleep 1234 #Run a large sleep inside a Debian container
ps -ef | grep 1234 #Get info about the sleep process
ls -l /proc/<PID>/ns #Get the Group and the namespaces (some may be uniq to the hosts and some may be shred with it)
```
有关更多信息,请查看:
{{#ref}}
cgroups.md
{{#endref}}
### 能力
能力允许**对可以允许的根用户能力进行更细粒度的控制**。Docker使用Linux内核能力特性来**限制可以在容器内执行的操作**,无论用户类型如何。
当运行docker容器时**进程会放弃敏感能力,以防止进程逃离隔离**。这试图确保进程无法执行敏感操作并逃逸:
{{#ref}}
../linux-capabilities.md
{{#endref}}
### Docker中的Seccomp
这是一种安全特性允许Docker**限制可以在容器内使用的系统调用**
{{#ref}}
seccomp.md
{{#endref}}
### Docker中的AppArmor
**AppArmor**是一个内核增强,用于将**容器**限制在**有限**的**资源**集内,并具有**每个程序的配置文件**
{{#ref}}
apparmor.md
{{#endref}}
### Docker中的SELinux
- **标记系统**SELinux为每个进程和文件系统对象分配一个唯一的标签。
- **策略执行**:它执行安全策略,定义进程标签可以对系统内其他标签执行的操作。
- **容器进程标签**当容器引擎启动容器进程时通常会分配一个受限的SELinux标签通常为`container_t`
- **容器内文件标记**:容器内的文件通常标记为`container_file_t`
- **策略规则**SELinux策略主要确保具有`container_t`标签的进程只能与标记为`container_file_t`的文件进行交互(读取、写入、执行)。
该机制确保即使容器内的进程被攻陷,它也仅限于与具有相应标签的对象进行交互,从而显著限制此类攻陷可能造成的损害。
{{#ref}}
../selinux.md
{{#endref}}
### AuthZ & AuthN
在Docker中授权插件在安全性中发挥着关键作用通过决定是否允许或阻止对Docker守护进程的请求来实现。这一决定是通过检查两个关键上下文来做出的
- **身份验证上下文**:这包括有关用户的全面信息,例如他们是谁以及他们如何进行身份验证。
- **命令上下文**:这包括与所发出请求相关的所有相关数据。
这些上下文有助于确保只有经过身份验证的用户的合法请求被处理从而增强Docker操作的安全性。
{{#ref}}
authz-and-authn-docker-access-authorization-plugin.md
{{#endref}}
## 来自容器的DoS
如果您没有正确限制容器可以使用的资源则被攻陷的容器可能会对其运行的主机造成DoS。
- CPU DoS
```bash
# stress-ng
sudo apt-get install -y stress-ng && stress-ng --vm 1 --vm-bytes 1G --verify -t 5m
# While loop
docker run -d --name malicious-container -c 512 busybox sh -c 'while true; do :; done'
```
- 带宽 DoS
```bash
nc -lvp 4444 >/dev/null & while true; do cat /dev/urandom | nc <target IP> 4444; done
```
## 有趣的 Docker 标志
### --privileged 标志
在以下页面中,您可以了解 **`--privileged` 标志意味着什么**
{{#ref}}
docker-privileged.md
{{#endref}}
### --security-opt
#### no-new-privileges
如果您正在运行一个容器,攻击者设法以低权限用户身份获得访问权限。如果您有一个 **配置错误的 suid 二进制文件**,攻击者可能会滥用它并 **在容器内提升权限**。这可能允许他逃离容器。
启用 **`no-new-privileges`** 选项运行容器将 **防止这种权限提升**
```
docker run -it --security-opt=no-new-privileges:true nonewpriv
```
#### 其他
```bash
#You can manually add/drop capabilities with
--cap-add
--cap-drop
# You can manually disable seccomp in docker with
--security-opt seccomp=unconfined
# You can manually disable seccomp in docker with
--security-opt apparmor=unconfined
# You can manually disable selinux in docker with
--security-opt label:disable
```
对于更多 **`--security-opt`** 选项,请查看: [https://docs.docker.com/engine/reference/run/#security-configuration](https://docs.docker.com/engine/reference/run/#security-configuration)
## 其他安全考虑
### 管理机密:最佳实践
避免直接在 Docker 镜像中嵌入机密或使用环境变量至关重要,因为这些方法会通过 `docker inspect``exec` 等命令将您的敏感信息暴露给任何可以访问容器的人。
**Docker 卷** 是一种更安全的替代方案,推荐用于访问敏感信息。它们可以作为内存中的临时文件系统使用,从而降低与 `docker inspect` 和日志记录相关的风险。然而,根用户和具有 `exec` 访问权限的用户仍然可能访问这些机密。
**Docker secrets** 提供了一种更安全的方法来处理敏感信息。对于在镜像构建阶段需要机密的实例,**BuildKit** 提供了一种高效的解决方案,支持构建时机密,提升构建速度并提供额外功能。
要利用 BuildKit可以通过三种方式激活
1. 通过环境变量: `export DOCKER_BUILDKIT=1`
2. 通过命令前缀: `DOCKER_BUILDKIT=1 docker build .`
3. 通过在 Docker 配置中默认启用: `{ "features": { "buildkit": true } }`,然后重启 Docker。
BuildKit 允许使用 `--secret` 选项来使用构建时机密,确保这些机密不会包含在镜像构建缓存或最终镜像中,使用命令如下:
```bash
docker build --secret my_key=my_value ,src=path/to/my_secret_file .
```
对于运行中的容器所需的秘密,**Docker Compose 和 Kubernetes** 提供了强大的解决方案。Docker Compose 在服务定义中使用 `secrets` 键来指定秘密文件,如 `docker-compose.yml` 示例所示:
```yaml
version: "3.7"
services:
my_service:
image: centos:7
entrypoint: "cat /run/secrets/my_secret"
secrets:
- my_secret
secrets:
my_secret:
file: ./my_secret_file.txt
```
此配置允许在使用 Docker Compose 启动服务时使用秘密。
在 Kubernetes 环境中,秘密是原生支持的,并且可以通过像 [Helm-Secrets](https://github.com/futuresimple/helm-secrets) 这样的工具进一步管理。Kubernetes 的基于角色的访问控制 (RBAC) 增强了秘密管理的安全性,类似于 Docker Enterprise。
### gVisor
**gVisor** 是一个应用内核,使用 Go 编写,实施了 Linux 系统表面的相当大一部分。它包括一个名为 `runsc` 的 [Open Container Initiative (OCI)](https://www.opencontainers.org) 运行时,提供了 **应用程序与主机内核之间的隔离边界**`runsc` 运行时与 Docker 和 Kubernetes 集成,使得运行沙箱容器变得简单。
{{#ref}}
https://github.com/google/gvisor
{{#endref}}
### Kata Containers
**Kata Containers** 是一个开源社区,致力于构建一个安全的容器运行时,使用轻量级虚拟机,感觉和表现像容器,但提供 **使用硬件虚拟化技术的更强工作负载隔离** 作为第二道防线。
{{#ref}}
https://katacontainers.io/
{{#endref}}
### 总结提示
- **不要使用 `--privileged` 标志或在容器内挂载** [**Docker socket**](https://raesene.github.io/blog/2016/03/06/The-Dangers-Of-Docker.sock/)**。** Docker socket 允许生成容器,因此这是完全控制主机的简单方法,例如,通过使用 `--privileged` 标志运行另一个容器。
- **不要在容器内以 root 身份运行。使用** [**不同用户**](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#user) **和** [**用户命名空间**](https://docs.docker.com/engine/security/userns-remap/)**。** 容器内的 root 与主机上的 root 是相同的,除非通过用户命名空间重新映射。它仅受到 Linux 命名空间、能力和 cgroups 的轻微限制。
- [**丢弃所有能力**](https://docs.docker.com/engine/reference/run/#runtime-privilege-and-linux-capabilities) **(`--cap-drop=all`),仅启用所需的能力** (`--cap-add=...`)。许多工作负载不需要任何能力,添加它们会增加潜在攻击的范围。
- [**使用“no-new-privileges”安全选项**](https://raesene.github.io/blog/2019/06/01/docker-capabilities-and-no-new-privs/) **以防止进程获得更多权限,例如通过 suid 二进制文件。**
- [**限制容器可用的资源**](https://docs.docker.com/engine/reference/run/#runtime-constraints-on-resources)**。** 资源限制可以保护机器免受拒绝服务攻击。
- **调整** [**seccomp**](https://docs.docker.com/engine/security/seccomp/)**、** [**AppArmor**](https://docs.docker.com/engine/security/apparmor/) **(或 SELinux** 配置文件,以将容器可用的操作和系统调用限制到最低要求。
- **使用** [**官方 docker 镜像**](https://docs.docker.com/docker-hub/official_images/) **并要求签名**,或基于它们构建自己的镜像。不要继承或使用 [后门](https://arstechnica.com/information-technology/2018/06/backdoored-images-downloaded-5-million-times-finally-removed-from-docker-hub/) 镜像。还要将 root 密钥、密码短语存放在安全的地方。Docker 计划通过 UCP 管理密钥。
- **定期** **重建** 镜像以 **应用安全补丁到主机和镜像。**
- 明智地管理您的 **秘密**,使攻击者难以访问它们。
- 如果您 **暴露 docker 守护进程,请使用 HTTPS**,并进行客户端和服务器身份验证。
- 在您的 Dockerfile 中,**优先使用 COPY 而不是 ADD**。ADD 会自动提取压缩文件,并可以从 URL 复制文件。COPY 没有这些功能。尽可能避免使用 ADD以免受到通过远程 URL 和 Zip 文件的攻击。
- 为每个微服务 **使用单独的容器**
- **不要在容器内放置 ssh**,可以使用 “docker exec” 连接到容器。
- 拥有 **更小的** 容器 **镜像**
## Docker 突破 / 权限提升
如果您 **在 docker 容器内** 或者您有权访问 **docker 组中的用户**,您可以尝试 **逃逸并提升权限**
{{#ref}}
docker-breakout-privilege-escalation/
{{#endref}}
## Docker 身份验证插件绕过
如果您可以访问 docker socket 或者有权访问 **docker 组中的用户,但您的操作受到 docker 身份验证插件的限制**,请检查您是否可以 **绕过它:**
{{#ref}}
authz-and-authn-docker-access-authorization-plugin.md
{{#endref}}
## 加固 Docker
- 工具 [**docker-bench-security**](https://github.com/docker/docker-bench-security) 是一个脚本,检查在生产中部署 Docker 容器的数十个常见最佳实践。所有测试都是自动化的,并基于 [CIS Docker 基准 v1.3.1](https://www.cisecurity.org/benchmark/docker/)。\
您需要从运行 docker 的主机或具有足够权限的容器中运行该工具。查找 **如何在 README 中运行它:** [**https://github.com/docker/docker-bench-security**](https://github.com/docker/docker-bench-security)。
## 参考
- [https://blog.trailofbits.com/2019/07/19/understanding-docker-container-escapes/](https://blog.trailofbits.com/2019/07/19/understanding-docker-container-escapes/)
- [https://twitter.com/\_fel1x/status/1151487051986087936](https://twitter.com/_fel1x/status/1151487051986087936)
- [https://ajxchapman.github.io/containers/2020/11/19/privileged-container-escape.html](https://ajxchapman.github.io/containers/2020/11/19/privileged-container-escape.html)
- [https://sreeninet.wordpress.com/2016/03/06/docker-security-part-1overview/](https://sreeninet.wordpress.com/2016/03/06/docker-security-part-1overview/)
- [https://sreeninet.wordpress.com/2016/03/06/docker-security-part-2docker-engine/](https://sreeninet.wordpress.com/2016/03/06/docker-security-part-2docker-engine/)
- [https://sreeninet.wordpress.com/2016/03/06/docker-security-part-3engine-access/](https://sreeninet.wordpress.com/2016/03/06/docker-security-part-3engine-access/)
- [https://sreeninet.wordpress.com/2016/03/06/docker-security-part-4container-image/](https://sreeninet.wordpress.com/2016/03/06/docker-security-part-4container-image/)
- [https://en.wikipedia.org/wiki/Linux_namespaces](https://en.wikipedia.org/wiki/Linux_namespaces)
- [https://towardsdatascience.com/top-20-docker-security-tips-81c41dd06f57](https://towardsdatascience.com/top-20-docker-security-tips-81c41dd06f57)
- [https://www.redhat.com/sysadmin/privileged-flag-container-engines](https://www.redhat.com/sysadmin/privileged-flag-container-engines)
- [https://docs.docker.com/engine/extend/plugins_authorization](https://docs.docker.com/engine/extend/plugins_authorization)
- [https://towardsdatascience.com/top-20-docker-security-tips-81c41dd06f57](https://towardsdatascience.com/top-20-docker-security-tips-81c41dd06f57)
- [https://resources.experfy.com/bigdata-cloud/top-20-docker-security-tips/](https://resources.experfy.com/bigdata-cloud/top-20-docker-security-tips/)
{{#include ../../../banners/hacktricks-training.md}}