Docker容器镜像安全最佳实践指南

Docker容器镜像安全最佳实践指南_第1张图片

文章目录:


0x02 Docker 容器安全最佳实践

  • 1.主机安全配置

    • 1.1 更新docker到最新版本

    • 1.2 为容器创建一个单独的分区

    • 1.3 只有受信任的用户才能控制docker守护进程

    • 1.4 审计docker守护进程

    • 1.5 审计docker相关的文件和目录

  • 2.docker守护进程配置

    • 2.1 限制默认网桥上容器之间的网络流量

    • 2.2 设置日志级别为info

    • 2.3 允许 docker 更改iptables

    • 2.4 不使用不安全的镜像仓库

    • 2.5 建议不使用aufs存储驱动程序

    • 2.6 docker守护进程配置TLS身份认证

    • 2.7 配置合适的 ulimit 资源控制

    • 2.8 启用用户命名空间

    • 2.9 使用默认cgroup

    • 2.10 启用docker客户端命令的授权

    • 2.11 配置集中和远程日志记录

    • 2.12 禁用docker resitry v1版本支持

    • 2.13 启用实时恢复

    • 2.14 禁用 userland 代理

    • 2.15 限制容器获取新的权限

  • 3.docker 守护进程文件配置

    • 3.1 设置 docker.service 文件所属和权限

    • 3.2 设置docker.socket文件所属和权限

    • 3.3 设置/etc/docker目录所有权为root:root

    • 3.4 设置仓库证书文件所有权为root:root

    • 3.5 设置TLS CA证书文件所有权为root:root

    • 3.6 设置docker服务器证书文件所有权为root:root

    • 3.7 设置docker服务器证书密钥文件所有权为root:root

    • 3.8 设置/var/run/docker.sock文件所有权为root:docker

    • 3.9 设置daemon.json文件所有权为root:root

    • 3.10 设置 /etc/default/docker 文件所有权为 root:root

  • 4.容器镜像和构建文件

    • 4.1 创建容器的用户

    • 4.2 容器使用可信的基础镜像

    • 4.3 容器中不安装没有必要的软件包

    • 4.4 扫描镜像漏洞并且构建包含安全补丁的镜像

    • 4.5 启用docker内容信任

    • 4.6 将HEALTHCHECK说明添加到容器镜像

    • 4.7 不在dockerfile中单独使用更新命令

    • 4.8 镜像中删除setuid和setgid权限

    • 4.9 在dockerfile中使用copy而不是add

    • 4.10 涉密信息不存储在dockerfile

    • 4.11 仅安装已经验证的软件包

    • 4.12 容器内部项目指定运行用户

  • 5.容器运行时保护

    • 5.1 设置SElinux安全选项

    • 5.2 linux内核特性在容器内受限

    • 5.3 不使用特权容器

    • 5.4 敏感的主机系统目录不要挂载在容器上

    • 5.5 SSH 不在容器中运行

    • 5.6 特权端口禁止映射到容器内

    • 5.7 只映射必要的端口

    • 5.8 不共享主机的网络命名空间

    • 5.9 确保容器的内存使用合理

    • 5.10 正确设置容器上的CPU优先级

    • 5.11 确保进入容器的流量绑定到特定的网卡

    • 5.12 容器重启策略on-failure设置为5

    • 5.13 确保主机的进程命名空间不共享

    • 5.14 主机的IPC命令空间不共享

    • 5.15 主机设备不直接共享给容器

    • 5.16 设置默认的ulimit配置(在需要时)

    • 5.17 设置主机的UTS命令空间不共享

    • 5.18 不要使用docker的默认网桥docker0


原文地址: https://www.bilibili.com/read/cv15554240

0x02 Docker 容器安全最佳实践

1.主机安全配置

1.1 更新docker到最新版本

描述: Docker频繁发布更新,旧版本可能存在安全漏洞,应及时更新。
加固说明: 通过及时了解Docker更新,Docker中的漏洞可以得到修复。攻击者可能会尝试获得访问权限或提升权限时利用已知的漏洞。不安装常规的Docker更新可能会让现有的Docker受到攻击。可能会导致提升权限,未经授权的访问或其他安全漏洞。所以需要跟踪新版本并根据需要进行更新。
判断方法: 和最新版本进行比对,查看是否为最新。
检测加固: 检查docker版本是否为最新docker version,跟踪Docker发布并根据需要进行更新。

# 1.检查docker版本是否为最新,和最新版本进行比对,查看是否为最新。
$ docker version

# 2.跟踪Docker发布并根据需要进行更新,例如此处在ubuntu上更新docker。
$ apt update && apt-cache madison docker-ce
docker-ce | 5:20.10.12~3-0~ubuntu-focal | https://download.docker.com/linux/ubuntu focal/stable amd64 Packages
$ apt install docker-ce docker-ce-cli

操作影响: 有些使用Docker的第三方产品可能依赖较老版本的Docker。
默认值: 不适用
备注: Docker频频曝出漏洞问题,应密切关注容器安全相关漏洞

1.2 为容器创建一个单独的分区

描述: 默认情况下,所有Docker容器、镜像及数据和元数据都存储在 /var/lib/docker 目录下。
加固说明: /var/lib/docker作为docker默认目录,其存储所有Docker相关文件,包括镜像文件。该目录写满时,会导致Docker、甚至主机可能无法使用。因此,建议为Docker创建一个单独的分区。
判断方法: 应该返回/var/lib/docker挂载点的分区详细信息。
检测加固: 新安装docker时为/var/lib/docker挂载点创建一个单独的分区,对于先前安装的系统可使用LVM创建分区。

# 1.查看docker数据目录
# $ df -h | grep docker
$ docker info -f '{{.DockerRootDir}}'
/app/docker
$ docker info | grep "Docker Root Dir:"
Docker Root Dir: /app/docker

# 2.配置 docker data 数据挂载点,修改后重载systemd守护进行以及重新docker服务。
$ vim /etc/docker/daemon.json
# 在json格式的 {} 中加入如下字段及内容。
"data-root": "/app/docker",
$ systemctl daemon-reload && systemctl restart

操作影响: None
默认值: /var/lib/docker将根据可用性挂载在/或/var分区下。

1.3 只有受信任的用户才能控制docker守护进程

描述: Docker守护进程绑定到unix socks需要root权限运行。对于添加到docker组的用户为提供了完整的root访问权限。
加固说明: Docker允许在Docker主机和容器之间共享目录,而不会限制容器的访问权限。这意味着可以启动容器并将主机上的根目录映射到容器。容器将能够不受任何限制地更改的主机文件系统。
判断方法: 判断是否必须要加入docker组的用户
检测加固: 检查docker用户组里的用户.从'docker'组中删除任何不受信任的用户,不要在主机上创建敏感目录到容器卷的映射。

# 建议docker组中不包含root或者其他高权限用户。
# 建立 docker 组:$ sudo groupadd docker
$ sudo usermod -aG docker $USER   # 将当前低权限用户加入 docker 组

# 检查系统中用户组里的用户是否必须要加入docker组的用户
$ grep "docker" /etc/group
docker:x:998:root,app

操作影响: 作为普通用户构建和执行容器的权限将受到限制
默认值: 不适用

1.4 审计docker守护进程

描述:审计所有活动的Docker守护进程
加固说明:除了审核常规的Linux文件系统和系统调用外,还要审Docker守护进程。Docker守护进程以root特权运行。因此有必要审核其活动和使用情况。
判断方法:应该列出Docker守护进程的规则
检测加固:验证是否存在Docker守护进程的审计规则,并为Docker守护进程添加审计规则

# 安装审计进程
sudo apt install auditd

# Docker守护进程的审核规则
auditctl -l | grep /usr/bin/docker

# 采用命令向 /etc/audit/audit.rules 添加审计规则
auditctl -w /usr/bin/docker -k docker
# 重新启动审计守护进程
systemctl restart auditd.service

操作影响:审计生成相当大的日志文件,确保定期归档它们,另外建议创建一个单独的审计分区以避免写满根文件系统。
默认值:默认安装后,Docker守护进程没有审计

1.5 审计docker相关的文件和目录

描述: 在条件允许的情况,审计docker相关的文件和目录,例如 docker.service、/etc/default/docker、/etc/docker docker.socket、daemon.json、/var/lib/docker
加固说明 除了正常的Linux文件系统和系统调用审核外,还可以审核所有与Docker相关的文件和目录。Docker守护进程以root权限运行。
判断方法: 如果以上文件存在,验证是否存在与之对应的审核规则,应该根据其位置列出docker相关的规则。
检测加固方法: 添加审计规则

# 在 /etc/audit/audit.rules 文件中添加以下行,然后重新启动审计守护进程
$ touch /etc/audit/rules.d/docker-audit.rules
$ cat > /etc/audit/rules.d/docker-audit.rules <<'EOF'
-w /usr/bin/docker -k docker
-w /usr/lib/systemd/system/docker.service -k docker 
-w /usr/lib/systemd/system/docker.socket -k docker
-w /usr/bin/docker-containerd -k docker
-w /usr/bin/docker-runc -k docker
-w /etc/docker -k docker
-w /etc/docker/daemon.json -k docker
EOF

操作影响: 审核会生成相当大的日志文件。确保定期归档。另外需要创建一个单独的审计分区,以避免填写根文件系统。
默认值: 默认情况下,Docker相关的文件和目录不会被审计,文件 docker.service 可能在系统上不可用


2.docker守护进程配置

2.1 限制默认网桥上容器之间的网络流量

描述: 默认情况下网桥上同一主机上的容器之间允许所有网络通信。如果不需要所有网络通信,建议限制容器间通信。将需要通信的特定容器链接在一起。或者创建自定义网络,并只加入需要与该自定义网络通信的容器。
加固说明: 每个容器都有可能读取同一主机上容器网络上的所有数据包。这可能会导致意外和不必要的信息泄露给其他容器。因此,限制默认网桥上的容器间通信。
判断方法: 它应该返回默认网桥的com.docker.network.bridge.enable_icc:false。
检测加固: 在守护进程模式下运行docker并传递--icc=false作为参数或创建自定义网络,注意--icc参数仅适用于默认网桥,如果使用自定义网络,则应采用分段网络的方法。

# 运行以下命令并确认默认网桥已被配置为限制集装箱间通信。
docker network ls -q | xargs docker network inspect --format '{{.Name}}: {{.Options}}'

# 设置不同容器之间是不允许网络互通的。

操作影响: 默认网桥上的容器间通信将被禁用。如果需要在同一主机上的容器之间进行通信,则需要使用容器链接来明确定义它,或者必须定义自定义网络。
默认值: 默认情况下,默认网桥上允许所有容器间通信。

2.2 设置日志级别为info

描述: 将Docker守护进程日志级别设置为info。
加固说明: 设置适当的日志级别,配置Docker守护进程以记录需要查看的事件。info及以上的基准日志级别将捕获除调试日志之外的所有日志。若无必须,不应该在'debug'日志级别运行Docker守护进程
检测加固:

# 确保--log-level参数不存在或存在,然后将其设置 info。
ps -ef | grep dockerd
grep "log-level" | /etc/docker/daemon.json

# 运行Docker守护进程参数如下。
Dockerd --log-level=info

操作影响: None.
默认值: 默认情况下,Docker守护进程设置为info的日志级别。

2.3 允许 docker 更改iptables

描述: iptables用于建立、维护和检查Linux内核中的IP包过滤规则表。允许Docker守护进程更改iptables
加固说明: Docker会根据用户为容器选择网络选项的方式自动对iptables进行必要的更改。建议让Docker自动更改iptables,以避免可能妨碍容器与外界通信的网络配置错误。
检测加固: 不要使用'--iptables=false'参数运行Docker守护进程。

# 确保'--iptables'参数不存在或不设置为'false'
ps -ef | grep dockerd

操作影响: Docker守护进程需要在启动之前启用iptables规则。在Docker守护进程操作期间任何重新启动iptables都可能导致丢失docker创建的规则。使用iptables-persistent持久iptables规则可以帮助减轻这种操作影响。
默认值: 默认情况下,'iptables'设置为'true'。

2.4 不使用不安全的镜像仓库

描述: Docker在默认情况下私有仓库被认为是相对安全的,所以我们需要保证私有镜像仓库的安全。
加固说明: 一个安全的镜像仓库建议使用TLS,在 /etc/docker/certs.d//目录下,将镜像仓库的CA证书副本放置在Docker主机上。不安全的镜像仓库是没有有效的镜像仓库证书或不使用TLS的镜像仓库。不应该在生产环境中使用任何不安全的镜像仓库。不安全的镜像仓库中的镜像可能会被篡改,从而导致生产系统可能受到损害。
检测加固:

# 使用镜像扫描工具检测, 验证本地或者远程镜像仓库是否存在不安全的基础镜像。
# 查看镜像所属以及构建操作
docker images 
docker history

操作影响: None.
默认值: 默认情况下,Docker假定所有的本地镜像仓库都是安全的。
备注: Hub仓库中中各基础发行版官方的镜像也可能是不安全的,我们需要用镜像扫描工具进行扫描验证。

2.5 建议不使用aufs存储驱动程序

描述:不要使用'aufs'作为Docker实例的存储驱动
加固说明:aufs代码太烂没能加入Linux内核主线,在Docker中只是保留了历史遗留支持,
检测加固:

# 执行以下命令并验证aufs不被用作存储驱动,此时命令结果不应该返回aufs。
docker info | grep -e StorageDriver:\s*aufs\s*$

# 在启动dockerd不要设置--storage-driveraufs参数,不要刻意的使用'aufs'作为存储驱动。
dockerd --storage-driveraufs

操作影响:aufs'是允许容器共享可执行文件和共享库内存的存储驱动程序。如果使用相同的程序或库运行数千个容器可以选用。
默认值:默认情况下,在大多数平台上使用overlay2和devicemapper作为Docker存储驱动程序。默认存储驱动程序可能因操作系统而异。应该首选操作系统最佳支持的存储驱动程序。
备注:在许多使用最新Linux内核的发行版中,'aufs'不再被支持。

2.6 docker守护进程配置TLS身份认证

描述:可以让Docker守护进程监听特定的IP和端口以及除默认Unix套接字以外的任何其他Unix套接字。配置TLS身份验证以限制通过IP和端口访问Docker守护进程。
加固说明:默认情况下,Docker守护进程绑定到非联网的Unix套接字,并以root权限运行。如果将默认的docker守护进程更改为绑定到TCP端口或任何其他Unix套接字,那么任何有权访问该端口或套接字的人都可以完全访问Docker守护进程,进而可以访问主机系统。因此,不应该将Docker守护进程绑定到另一个IP/端口或Unix套接字。如果必须通过网络套接字暴露Docker守护进程,建议为守护进程和 Docker Swarm API配置TLS身份验证。
检测加固: 按照Docker文档或其他参考中提到的步骤进行操作.

# 执行如下命令,确保存在以下参数:'--tlsverify'·'--tlscacert'·'--tlscert'·'--tlskey'
ps -ef | grep dockerd

操作影响: 需要管理Docker守护进程和Docker客户端的证书和密钥。
默认值: 默认情况下,未配置TLS认证
备注

2.7 配置合适的 ulimit 资源控制

描述: 根据业务环境设置默认的ulimit选项
加固说明:ulimit提供对shell可用资源的控制。设置系统资源控制可以防止资源耗尽带来的问题,如fork炸弹。有时候合法的用户和进程也可能过度使用系统资源,导致系统资源耗尽。为Docker守护进程设置默认ulimit将强制执行所有容器的ulimit。不需要单独为每个容器设置ulimit。但默认的ulimit可能在容器运行时被覆盖。因此,要控制系统资源,需要自定义默认的ulimit。
检测加固:

# 确保根据需要设置'--default-ulimit'参数
ps -ef| grep dockerd

# 在守护进程模式下运行docker,并根据相应的ulimits传递'--default-ulimit'作为参数。
dockerd --default-ulimit nproc=1024:2408 --default-ulimit nofile=100:200

# 在 daemon.json 中设置
"default-ulimits": {
    "nofile": {
        "Name": "nofile",
        "Hard": 1024000,
        "Soft": 1024000
    },
    "nproc": {
        "Name": "nproc",
        "Hard": 1024000,
        "Soft": 1024000
    },
    "core": {
        "Name": "core",
        "Hard": -1,
        "Soft": -1
  }
},

操作影响: 如果ulimits未正确设置,则可能无法实现所需的资源控制,甚至可能导致系统无法使用
默认值: 默认情况下,不设置ulimit
备注: 慎用

2.8 启用用户命名空间

描述: 在Docker守护进程中启用用户命名空间支持,可对用户进行重新映射。该建议对镜像中没有指定用户是有帮助的。如果在容器镜像中已经定义了非root运行,可跳过此建议,因为该功能比较新,可能会给带来不可预测的问题。
加固说明: Docker守护进程中对Linux内核用户命名空间支持为Docker主机系统提供了额外的安全性。它允许容器具有独特的用户和组ID,这些用户和组ID在主机系统所使用的传统用户和组范围之外。root用户希望有容器内的管理权限,可映射到主机系统上的非root的UID上。
检测加固:可参考Docke文档了解具体的配置方式。操作可能因平台而异在RedHat上,子UID和子GID映射创建不会自动工作。必须。确保存在/etc/subuid 、/etc/subgid 并使用--userns-remap标志启动docker守护进程

# 执行命令将查找容器的PID,然后列出与容器进程关联的主机用户。如果容器进程以root身份运行,则不符合安全要求。
~$ ps -o pid,user,command `docker inspect -format='{{.State.Pid}}' $(docker ps -aq) | cut -d '=' -f 2`
    PID USER     COMMAND
  22429 root     /bin/bash /usr/local/bin/start.sh
  22619 10000    /home/chart/chartm
  22638 10000    nginx: master process nginx -g daemon off;


# 手动创建映射,手动设置docker启用标志启动
touch /etc/subuid /etc/subgid
dockerd --userns-remap=default

操作影响: 注意用户命名空间重新映射使得一些Docker功能不兼容,可查看Docker文档和参考链接以获取详细信息。
默认值: 默认情况下,用户命名空间不会重新映射。

2.9 使用默认cgroup

描述: 查看--cgroup-parent 选项允许设置用于所有容器的默认 cgroup parent。如果没有特定用例,则该设置应保留默认值。
加固说明: 系统管理员可定义容器应运行的cgroup。若系统管理员没有明确定义cgroup,容器也会在docker cgroup下运行。应该监测和确认使用情况。通过加到与默认不同的cgroup,导致不合理地共享资源,从而可能会主机资源耗尽。
检测加固:默认设置够用的话可保留。

# 执行如下命令,确保'--cgroup-parent'参数未设置或设置为适当的非默认cgroup。
ps -ef | grep dockerd

# 如果要特别设置非默认cgroup,在启动时将-cgroup-parent参数传递给docker守护进程。
dockerd --cgroup-parent=/foobar

加固方法: 默认设置够用的话,可保留。如果要特别设置非默认cgroup,
操作影响: None
默认值: 如果未设置此选项,则默认为 /docker为了 fs cgroup 驱动程序和 system.slice用于 systemd cgroup 驱动程序。
如果 cgroup 有一个前导正斜杠 ( /),创建 cgroup 在根 cgroup 下,否则在 daemon 下创建 cgroup c组。假设守护进程在 cgroup 中运行 daemoncgroup, --cgroup-parent=/foobar在中创建一个 cgroup /sys/fs/cgroup/memory/foobar,而使用 --cgroup-parent=foobar 在中创建 cgroup /sys/fs/cgroup/memory/daemoncgroup/foobar

2.10 启用docker客户端命令的授权

描述: 使用本机Docker授权插件或第三方授权机制与Docker守护进程来管理对Docker客户端命令的访问。
加固说明: Docker默认是没有对客户端命令进行授权管理的功能。任何有权访问Docker守护进程的用户都可以运行任何Docker客户端命令。对于使用Docker远程API来调用守护进程的调用者也是如此。如果需要细粒度的访问控制,可以使用授权插件并将其添加到Docker守护进程配置中。使用授权插件,Docker管理员可以配置更细粒度访问策略来管理对Docker守护进程的访问。Docker的第三方集成可以实现他们自己的授权模型,以要求Docker的本地授权插件(即Kubernetes,CloudFoundry,Openshift)之外的Docker守护进程的授权。
检测加固:

# 如果使用docker本地授权,可使用--authorization-plugin参数加载授权插件。
ps -ef | grep dockerd

# 加固流程
第1步:安装/创建授权插件。
第2步:根据需要配置授权策略。
第3步:重启docker守护进程

操作影响: 使用授权插件可能会导致性能下降。
默认值: 默认情况下,未设置授权插件。
 

2.11 配置集中和远程日志记录

描述: Docker现在支持各种日志驱动程序, 存储日志的最佳方式是支持集中式和远程日志记录。
加固说明: 集中和远程日志确保所有重要的日志记录都是安全的,以满足容灾的要求。Docker支持多种类型的日志驱动程序,可根据自身情况选取。
检测加固:

# 运行docker info并确保日志记录驱动程序属性被设置为适当的。
docker info --format '{{.LoggingDriver}}'

# 可通过如下命令查看 --log-driver 的设置。
ps -ef | grep dockerd

# 加固流程
第1步:按照其文档设置所需的日志驱动程序。
第2步:使用该日志记录驱动程序启动docker守护进程。
dockerd --log-driver=syslog --log-optsyslog-address=tcp://10.10.107.233

# 或者
# 设置log-driver字段值为syslog并添到daemon.json 文件中
$ vim /etc/docker/daemon.json
"log-driver": "syslog",
  "log-level": "info",
  "log-opts": {
    "syslog-address": "tcp://10.10.107.233",
  },

操作影响: None
默认值: 默认情况下,容器日志为json文件格式

2.12 禁用docker resitry v1版本支持

描述: 最新的Docker registry版本是v2。v1版本存在很多安全问题,V1上的所有操作都应受到限制
加固说明: Docker镜像仓库v2在v1中引入了许多性能和安全性改进。它支持容器镜像来源验证和其他安全功能。因此对DockerV1仓库的操作应该受到限制。好在当前20.10.x已经不再针对仅支持旧版 v1 协议的注册表进行操作支持的,但是一些旧的版本是仍然支持的,此时可以采用如下方法禁用。
检测加固:

# 下面的命令应该列出--disable-legacy-registry作为传递给docker守护进程的选项。
ps -ef | grep dockerd
grep "disable-legacy-registry" /etc/docker/daemon.json

# 在 daemon.json 文件中配置 disable-legacy-registry 字段
vim /etc/docker/daemon.json
"disable-legacy-registry": true,

操作影响: 旧版镜像仓库操作将受到限制
默认值: 默认情况下,允许旧版镜像仓库操作
备注

2.13 启用实时恢复

描述: 使用--live-restore参数可以支持无守护进程的容器运行。它确保Docker daemon在关闭或恢复时不会停止容器,并在重新启动后重新连接到容器。
加固说明: 可用性作为安全一个重要的属性, 在Docker守护进程中设置'--live-restore'标志可确保当docker守护进程不可用时容器执行不会中断,这也意味着当更新和修复docker守护进程而不会导致容器停止工作。
检测加固:

# 确保LiveRestoreEnabled属性设置为true。
docker info --format '{{.LiveRestoreEnabled}}'

# 在 daemon.json 文件中配置live-restore字段
vim /etc/docker/daemon.json
"live-restore": true,

操作影响: None
默认值: 默认情况下--live-restore不启用
备注

2.14 禁用 userland 代理

描述: 当容器端口需要被映射时,docker守护进程都会启动用于端口转发的userland-proxy方式, 如果使用了DNAT方式则该功能可以禁用,是否使用用户态代理来实现容器间和出容器的回环通信。
加固说明: Docker引擎提供了两种机制将主机端口转发到容器,DNAT和userland-proxy。在大多数情况下,DNAT模式是首选,因为它提高了性能,并使用本地Linux iptables功能而需要附加组件, 如果DNAT可用,则应在启动时禁用userland-proxy以减少安全风险。
检测加固:

# 确保--userland-proxy参数设置为false。
ps -ef | grep dockerd

# 运行Docker守护进程如下:
dockerd --userland-proxy=false

# 或者设置userland-proxy字段值为false并添到daemon.json 文件中
$ vim /etc/docker/daemon.json
 "userland-proxy": false,
 "userland-proxy-path": "/usr/libexec/docker-proxy",

操作影响: 某些旧版Linux内核的系统可能无法支持DNAT,因此需要userland-prox服务。此外,某些网络设置可能会因删除userland-prox而受到操作影响。
默认值: 默认情况下,userland-prox已启用。
备注:建议使用较新内核的Linux发行版

2.15 限制容器获取新的权限

描述: 默认情况下,限制容器通过suid或sgid位获取附加权限。
加固说明: 一个进程可以在内核中设置no_new_priv。它支持fork,clone和execve。no_new_priv确保进程或其子进程不会通过suid或sgid位获得任何其他特权。这样,很多危险的操作就降低安全风险。在守护程序级别进行设置可确保默认情况下,所有新容器不能获取新的权限。
检测加固:

# 确保--no-new-privileges参数存在且未设置为false。
ps -ef | grep dockerd

# 运行Docker守护进程如下:
dockerd --no-new-privileges

# 设置no-new-privileges字段值为false并添到daemon.json 文件中
"no-new-privileges": true,

操作影响:no_new_priv会阻止像SELinux这样的LSM访问当前进程的进程标签。
默认值:默认情况下,容器不会获得新的权限。
备注


3.docker 守护进程文件配置

3.1 设置 docker.service 文件所属和权限

描述:验证'docker.service'文件所属和所属组是否正确设置为root。文件权限是否正确设置为'644'或更多限制。
加固说明:docker.service'文件包含可能会改变Docker守护进程行为的敏感参数。因此,它应该由root拥有和归属,以保持文件的完整性。
检测加固:

# 查看docker.service文件属性,文件所属为root,权限为644
ls -l /lib/systemd/system/docker.service
stat -c %a-%U:%G /lib/systemd/system/docker.service
# 644-root:root

# 设置文件所属与权限所属
chown root:root /usr/lib/systemd/system/docker.service
chmod 644 /usr/lib/systemd/system/docker.service

操作影响:None.
默认值:该文件可能不存在于系统上。在这种情况下,此建议不适用。默认情况下,如果文件存在,则该文件的所属和所属组正确设置为root 权限为644。
备注

3.2 设置docker.socket文件所属和权限

描述:验证docker.socket文件所属和所属组是否正确设置为root,文件权限是否正确设置为'644'或更多限制。
加固说明:docker.socket文件包含可能会改变Docker远程API行为的敏感参数。因此,它应该拥有root权限,以保持文件的完整性。
检测加固:

# 判断文件所属用户及用户组和权限
ls -al /usr/lib/systemd/system/docker.socket

# 所属和用户组应该为root,权限为644
chown root:root /usr/lib/systemd/system/docker.socket
chmod 644 /usr/lib/systemd/system/docker.socket

操作影响: None
默认值: 该文件可能不存在于系统上。在这种情况下,此建议不适用。默认情况下,如果文件存在,则该文件的所属和所属组正确设置为root, 文件权限正确设置为644。
备注

3.3 设置/etc/docker目录所有权为root:root

描述:验证/etc/docker目录所属和所属组是否正确设置为root, 权限是否正确设置为750或更多限制。
加固说明: 除了各种敏感文件之外/etc/docker目录还包含证书和密钥。
因此,它应该由root拥有和归组来维护目录的完整性。
检测加固:

# 执行以下命令以验证该目录是由root拥有和归属的(所属和所属组设置为root,权限设置为750)
stat -c %U:%G /etc/docker | grep  root:root

# 将目录的所属和所属组设置为root,权限设置为750
chown root:root /etc/docker
chmod 750 /etc/docker

操作影响: None.
默认值: 默认情况下所属和所属组为 root,权限为755。
备注

3.4 设置仓库证书文件所有权为root:root

描述: 验证所有仓库证书文件/etc/docker/certs.d/所属和所属组是否为root,权限为600或更多限制的权限
加固说明: 在/etc/docker/certs.d/目录包含Docker镜像仓库证书,这些证书文件必须由root和其组拥有,以维护证书的完整性
检测加固:

# 所属和所属组设置为root,权限设置为600
find /etc/docker/certs.d/ -type f -exec stat -c '%U:%G - %a - %n' {}  +
root:root - 600 - /etc/docker/certs.d/harbor.weiyigeek.top/server.key
root:root - 600 - /etc/docker/certs.d/harbor.weiyigeek.top/harbor.weiyigeek.top.crt
root:root - 600 - /etc/docker/certs.d/hub.weiyigeek.top/hub.weiyigeek.top.crt

# 将镜像仓库证书文件的所属和所属组设置为root。
chown root:root /etc/docker/certs.d//*
chmod -R 600  /etc/docker/certs.d//

操作影响: None.
默认值: 默认情况下,镜像仓库证书文件的所属和所属组正确设置为root 权限为444.
备注

3.5 设置TLS CA证书文件所有权为root:root

描述: 验证TLSCA证书文件是由root拥有和分组拥有的,权限为444。
加固说明: TLS CA证书文件应受到保护,不受任何篡改。它用于指定的CA证书验证。因此,它必须由root拥有,权限为444,以维护CA证书的完整性。
检测加固:

# 执行以下命令判断CA证书的所属和权限, 以验证TLS CA证书文件是否由root和其组拥有:
stat-c %U:%G <路径到TLS CA证书文件> | grep -v root:root

# 将TLS CA证书文件所属和所属组设置为root,权限为444。
chown root:root  -R <路径到TLS CA证书文件>
chmod 444 -R <路径到TLS CA证书文件>

操作影响: None.
默认值: 默认情况下,TLS CA证书文件的所有权和组属性正确设置为root。默认文件权限由系统或用户特定的umask值控制。
备注

3.6 设置docker服务器证书文件所有权为root:root

描述: 验证Docker服务器证书文件(与--tlscert'参数一起传递的文件)是否由root和其组拥有,权限为444。
加固说明: Docker服务器证书文件应受到保护,不受任何篡改。它用于验证Docker服务器。因此,它必须由root拥有以维护证书的完整性。
检测加固:

# 上面的命令所属和所属组为root,权限为444
ls -al 

# 将docker服务器证书文件的所属和所属组设置为root。
chown root:root -R <路径到Docker服务器证书文件>
chmod 444 -R  <路径到Docker服务器证书文件>

操作影响:None.
默认值:默认情况下,Docker服务器证书文件的所属和所属组正确设置为root, 默认文件权限由系统或用户特定的umask值控制。
备注

3.7 设置docker服务器证书密钥文件所有权为root:root

描述: 验证Docker服务器证书密钥文件(与--tlskey'参数一起传递的文件)是由由root拥有,权限设置为400。

加固说明: Docker服务器证书密钥文件应受到保护,不受任何篡改或不必要的读取。它保存Docker服务器证书的私钥。因此它必须由root拥有,权限为400 以维护Docker服务器证书的完整性。
检测加固:

# 所属和所属组为root,权限为400
ls -al <路径到Docker服务器证书密钥文件>

# 将Docker服务器证书密钥文件的所属和所属组设置为root,权限设置为400
chown root:root -R <路径到Docker服务器证书密钥文件>
chmod 400 -R <路径到docker服务器证书密钥文件>

操作影响 None.
默认值 默认情况下,Docker服务器证书密钥文件的所属和所属组正确设置为root。文件权限由系统或用户特定的umask值控制。
备注

3.8 设置/var/run/docker.sock文件所有权为root:docker

描述: 验证docker.sock文件由root拥有,而用户组为docker,权限为660。
加固说明:Docker守护进程以root用户身份运行。因此,默认的Unix套接字必须由root拥有。如果任何其他用户或进程拥有此套接字,那么该非特权用户或进程可能与Docker守护进程交互。另外,这样的非特权用户或进程可能与容器交互。这样非常不安全。另外,Docker安装程序会创建一个名为docker的用户组。可以将用户添加到该组,然后这些用户将能够读写默认的DockerUnix套接字。docker组成员由系统管理员严格控制。如果任何其他组拥有此套接字,那么该组的成员可能会与Docker守护进程交互。。
因此,默认的DockerUnix套接字文件必须由docker组拥有权限,以维护套接字文件的完整性。
检测加固:

# 所属和所属组为root,权限为660
ls -al /var/run/docker.sock
~$ stat -c %a-%U:%G /var/run/docker.sock
660-root:docker

# 将所有权设置为root和组所有权到docker作为默认Docker套接字文件。
chown root:docker /var/run/docker.sock
chmod 660 /var/run/docker.sock

操作影响 None.
默认值 默认情况下,Docker套接字文件的所属和所属组正确设置为root:docker,权限正确设置为'660'

3.9 设置daemon.json文件所有权为root:root

描述: 验证daemon.json文件所属和所属组是否正确设置为root,文件权限是否正确设置为644或更多限制。
加固说明: daemon.json文件包含可能会改变docker守护进程行为的敏感参数。因此,它应该由root拥有,以维护文件的完整性。
检测加固:

# 文件所属和所属组为root,权限为644
ls -l /etc/docker/daemon.json
stat -c %a-%U:%G /etc/docker/daemon.json
644-root:root

# 将文件的所属和所属组设置为root,权限为644。
chown root:root /etc/docker/daemon.json
chmod 644 /etc/docker/daemon.json

操作影响 None.
默认值: 该文件可能不存在于系统上。在这种情况下,此建议不适用.文件权限由系统或用户特定的umask值控制。
备注

3.10 设置 /etc/default/docker 文件所有权为 root:root

描述: 验证/etc/default/docker文件所属和所属组是否正确设置为root,文件权限是否正确设置为644或更多限制。
加固说明: /etc/default/docker文件包含可能会改变docker守护进程行为的敏感参数。因此,它应该由root拥有,以维护文件的完整性。
检测加固:

# 文件所属和所属组为root,权限为644
ls -l /etc/default/docker
stat -c %a-%U:%G /etc/default/docker

# 这将文件的所属和所属组设置为root。
chown root:root/etc/default/docker

操作影响: None.
默认值: 该文件可能不存在于系统上。在这种情况下,此建议不适用。
备注

4.容器镜像和构建文件

4.1 创建容器的用户

描述: 为容器镜像的Dockerfile中的容器创建非root用户。
加固说明: 如果可能,最好指定非root用户身份运行容器。虽然用户命名空间映射可用,但是如果用户在容器镜像中指定了用户,则默认情况下容器将作为该用户运行,并且不需要特定的用户命名空间重新映射。
检测加固

# 如果为空则表示容器以root身份运行。
docker ps -q |xargs docker inspect --format '{{.Id}}:User={{.Config.User}}'
# 确保容器镜像的Dockerfile包含以下指令:USER<用户名或ID>其中用户名或ID是指可以在容器基础镜像中找到的用户。如果在容器基础镜像中没有创建特定用户,则在USER指令之前添加user add命令以添加特定用户。
RUN user add -d /home/app -m -s/bin/bash app 
USER app

注意:如果镜像中有容器不需要的用户,应删除它们。删除这些用户后,提交镜像,然后生成新的容器实例以供使用。
操作影响: None.
默认值: 默认情况下,容器以root权限运行,并以容器中的用户root身份运行。

4.2 容器使用可信的基础镜像

描述: 确保容器镜像是从头开始编写的,或者是基于通过安全仓库下载的另一个已建立且可信的基本镜像。
加固说明: 官方存储库是由Docker社区或供应商优化的Docker镜像。可能还存在其他不安全的公共存储库。在从Docker和第三方获取容器镜像时,需谨慎使用。
检测方法: 检查Docker主机以查看执行以下命令使用的Docker镜像:docker images这将列出当前可用于Docker主机的所有容器镜像。再对在Docker主机上找到的每个Docker镜像,检查镜像的构建方式,以验证是否来自可信来源
判断方法: 判断镜像来源的合法性
加固方法: 配置和使用Docker内容信任。检查Docker镜像历史记录以评估其在网络上运行的风险。使用镜像扫描工具扫描Docker镜像以查找其依赖关系中的漏洞。
操作影响: None.
默认值: 无

4.3 容器中不安装没有必要的软件包

描述: 选用精简的镜像作为基础镜像,不安装不必要的软件
加固说明: 安装不必要的软件可能会增加容器的攻击风险。因此,除了容器的真正需要的软件之外,不要安装其他多余的软件。
检测方法: 进入容器中执行命令检查安装的软件包
判断方法: 查看软件包列表并确保它是合法的。
加固方法: 建议选用alpine镜像或官方Linux发行版精简的镜像
操作影响: None.
默认值: 不适用。

4.4 扫描镜像漏洞并且构建包含安全补丁的镜像

描述: 应该经常扫描镜像以查找漏洞。重建镜像安装最新的补丁。
加固说明: 安全补丁可以解决软件的安全问题。可以使用镜像漏洞扫描工具来查找镜像中的任何类型的漏洞,然后检查可用的补丁以减轻这些漏洞。修补程序将系统更新到最新的代码库。
检测方法: 运行镜像漏洞扫描工具扫描镜像
判断方法: 检查扫描结果中存在的安全隐患
加固方法:

  • 第1步:取出所有基本镜像(即给定一组Dockerfiles,提取在FROM指令中声明的所有镜像,并重新提取它们以检查更新

  • 第2步:重建每个镜像:docker build --no-cache

  • 第3步:使用更新的镜像重新启动所有容器,还可以在Dockerfile中使用ONBUILD指令来触发经常用作基本镜像的特定更新指令。
    操作影响: None.
    默认值: 默认情况下,容器和镜像不会自动更新。
    备注: 如果镜像漏洞扫描工具可以执行二进制级别分析,而不仅仅是版本字符串匹配,则会更好。

4.5 启用docker内容信任

描述: 默认情况下禁用内容信任,为了安全起见,可以启用。
加固说明: 内容信任为向远程Docker镜像仓库发送和接收的数据提供了使用数字签名的能力。这些签名允许客户端验证特定镜像标签的完整性和发布者。这确保了容器镜像的来源的合法性。
检测加固:

# DOCKER_CONTENT_TRUST 环境变量执行应该返回1
echo $DOCKER_CONTENT_TRUST

# 要在bash shell中启用内容信任或者,在配置文件中设置此环境变量
export DOCKER_CONTENT_TRUST=1

操作影响: 在设置了DOCKER_CONTENT_TRUST的环境中,需要在处理镜像时遵循信任过程-构建,创建,拉取,推送和运行。可以使用--disable-content-trust标志按照需要在标记镜像上运行单独的操作,一般用于测试目的,生成环境中应尽不要使用。
默认值: 默认情况下,内容信任被禁用。

4.6 将HEALTHCHECK说明添加到容器镜像

描述: 在Docker容器镜像中添加HEALTHCHECK指令以对正在运行的容器执行运行状况检查。
加固说明: 安全性最重要的一个特性就是可用性。将HEALTHCHECK指令添加到容器镜像可确保docker引擎定期检查运行的容器实例是否符合该指令,以确保实例仍在运行。根据报告的健康状况,docker引擎可以退出非工作容器并实例化新容器。
检测加固:

# 运行以下命令,并确保docker镜像对HEALTHCHECK指令设置, 应当返回设置值
docker inspect --format='{{.Config.Healthcheck}}' <镜像ID>
~$ docker inspect --format='{{.RepoTags}} - {{.Config.Healthcheck}}' $(docker images -q)
[weiyigeek/go-webserver:v2.0] - {[CMD-SHELL wget --spider http://localhost:8080 || exit 1] 5s 3s 0s 5}
[weiyigeek/go-webserver:v1.0 hub.weiyigeek.top/go-webserver:v1.0] - {[CMD-SHELL curl -fs http://localhost:8080 || exit 1] 5s 3s 0s 5}
[weiyigeek/go-webserver:v1.0 hub.weiyigeek.top/go-webserver:v1.0] - {[CMD-SHELL curl -fs http://localhost:8080 || exit 1] 5s 3s 0s 5}
[ubuntu-htop:v0.1] - 

# 按照Dockerfile文档,并使用HEALTHCHECK指令重建容器镜像。
HEALTHCHECK --interval=5s --timeout=3s --retries=5 \
  CMD curl -fs http://localhost:8080/ || exit 1

操作影响: None.
默认值: 默认情况下,HEALTHCHECK未设置。

4.7 不在dockerfile中单独使用更新命令

描述: 不要单独使用apt-get upgradeyum makecache等更新指令也不要在Dockerfile中使用更新指令。
加固说明: 在Dockerfile添加更新指令将缓存更新的层。稍后使用相同的指令构建任何镜像时,将使用先前缓存的更新图层。这可能会拒绝任何新版本进入到以后的版本。
检测加固:

# 查看Dockerfile,请确认没有上述更新指示。
docker images 
docker history  | grep "upgrade"

# 在安装软件包时,请使用最新的固定版本软件包。或可以在docker构建过程中使用--no-cache标志,以避免使用缓存的层。

操作影响: None.
默认值: 默认情况下,docker对更新无限制。

4.8 镜像中删除setuid和setgid权限

描述: 删除镜像中的setuid和setgid权限防止容器中的提权攻击。
加固说明: setuid和setgid可用于提升权限。虽然这些权限有时必须,应考虑为镜像中不需要的软件包删除这些权限。
检测加固:

# 在镜像上运行以下命令以列出具有setuid和setgid权限的可执行文件, 仔细检查列表确保它是合法的
docker run  find / -perm +6000 -typef -exec ls -ld {}\; 2>/dev/null

# 只在需要可执行的文件上允许setuid和setgid权限。可在构建时通过在Dockerfile中添加以下命令来删除这些权限,最好添加在Dockerfile的末尾:
RUN find /- perm +6000 -typef -exec chmod a -s {}\;||true

操作影响: 以上命令会导致依赖setuid或setgid权限(包括合法权限)的可执行文件无法执行,需要小心处理。
默认值: None

4.9 在dockerfile中使用copy而不是add

描述: 在Dockerfile中使用COPY指令而不是ADD指令。
加固说明: COPY指令只是将文件从本地主机复制到容器文件系统。ADD指令可能会从远程URL下载文件并执行诸如解包等操作。因此,ADD指令增加了从URL添加恶意文件的风险。
检测方法: 通过docker history Dockerfile
查找构建镜像过程中是否使用了ADD指令
判断方法: 不允许存在ADD指令
加固方法: 在Dockerfiles中使用COPY指令。
操作影响: 可能需ADD指令提供的功能,从远程URL获取文件。
默认值: none

4.10 涉密信息不存储在dockerfile

描述: 不要在Dockerfiles中存储任何涉密信息。
加固说明: 通过使用docker history历史命令,可以查看各种工具和实用程序。镜像发布者提供Dockerfiles来构建镜像。所以,Dockerfiles中的涉密信息可能会被暴露并被恶意利用。
检测方法: docker history  或查看Dockerfile查找是否有涉密信息
判断方法: 不应该有涉密的信息,如用户账号,私钥证书等。
加固方法: 不要在Dockerfiles中存储任何类型的涉密信息。
操作影响: 若必须使用,需要制定相应的措施
默认值: 默认情况下,在Dockerfiles中存储配置密码没有限制。

4.11 仅安装已经验证的软件包

描述: 在将软件包安装到镜像中之前,验证软件包可靠性。
加固说明: 验证软件包的可靠性对于构建安全的容器镜像至关重要。不合法的软件包可能具有恶意或者存在一些可能被利用的已知漏洞。
检测方法: docker history 或查看Dockerfile查看软件包的合法性
加固方法: 使用GPG密钥下载和验证所选择的软件包。
操作影响: None
默认值: 不适用

4.12 容器内部项目指定运行用户

Q:在说此项前我们先来了解容器内部应用程序运行用户设置建议采用gosu还是sudo?
答:我们通过实战来确定到底使用哪一个命令较好;

# gosu
docker run --rm gosu/alpine gosu root ps aux
PID   USER     TIME  COMMAND
    1 root      0:00 ps aux  # 通过gosu启动的是符合我们要求的(PID为1)容器内的唯一进程

# sudo
docker run --rm ubuntu:trusty sudo ps aux
#容器内出现了两个进程,sudo命令会创建第一个进程,然后该进程再创建了ps进程,而且ps进程的PID并不等于1,这是达不到我们要求的,此时在宿主机向该容器发送信号量收到信号量的是sudo进程;
USER        PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root          1  0.0  0.0  46012  1772 ?        Rs   12:05   0:00 sudo ps aux
root          6  0.0  0.0  15568  1140 ?        R    12:05   0:00 ps aux

小结:

  • gosu 启动命令时只有一个进程,所以docker容器启动时使用gosu,那么该进程可以做到PID等于1;

  • sudo 启动命令时先创建sudo进程,然后该进程作为父进程去创建子进程,1号PID被sudo进程占据;

正题回归

描述:为了安全容器中不要使用root账号(即最小权限),此时就需要一个能够提升自定账号权限的命令 gosu 便应运而生它与 sudo 类似但它比sudo更安全;

方便学习先来看看一个小例子(在镜像中创建非root账号):

首先我们以redis官方镜像的Dockerfile为例,来看看如何创建账号

#1.先添加我们的用户和组,以确保他们的id被一致地分配,不管添加了什么依赖项
RUN groupadd -r redis && useradd -r -g redis redis

#2.可见redis官方镜像使用groupadd和useradd创建了名为redis的组合账号,接下来就是用redis账号来启动服务了,理论上应该是以下套路;
* 用USER redis将账号切换到redis;
* 在docker-entrypoint.sh执行的时候已经是redis身份了,如果遇到权限问题,例如一些文件只有root账号有读、写、执行权限,用sudo xxx命令来执行即可;

但事实并非如此!
在Dockerfile脚本中未发现USER redis命令,意味着执行docker-entrypoint.sh文件的身份是root;

其次在docker-entrypoint.sh中没有发现su - redis命令,也没有sudo命令

这是怎么回事呢?难道容器内的redis服务是用root账号启动的?

#动手实践
docker run --name myredis -idt redis
docker exec -it myredis /bin/bash
apt-get update && apt-get install procps #更新软件源以及PS命令安装
root@122c2df16bbb:/data# ps -ef
UID         PID   PPID  C STIME TTY          TIME CMD
redis         1      0  0 09:22 ?        00:00:01 redis-server *:6379
root        287      0  0 09:36 ?        00:00:00 /bin/bash
root        293    287  0 09:39 ?        00:00:00 ps -ef

上面的结果展示了两个关键信息:
第一,redis服务是redis账号启动的并非root;
第二,redis服务的PID等于(重要),宿主机执行docker stop命令时,该进程可以收到SIGTERM信号量,于是redis应用可以做一些退出前的准备工作,例如保存变量、退出循环等,也就是优雅停机(Gracefully Stopping);

现在我们已经证实了redis服务并非root账号启动,而且该服务进程在容器内还是一号进程,但是我们在Dockerfile和docker-entrypoint.sh脚本中都没有发现切换到redis账号的命令,也没有sudo和su,这是怎么回事呢?

答案:在于redis的docker-entrypoint.sh文件之中

#!/bin/sh
set -e

# first arg is `-f` or `--some-option`
# or first arg is `something.conf`
if [ "${1#-}" != "$1" ] || [ "${1%.conf}" != "$1" ]; then
	set -- redis-server "$@"
fi

# allow the container to be started with `--user` | 首次运行进入
if [ "$1" = 'redis-server' -a "$(id -u)" = '0' ]; then
  # 当前文件夹下不属于redis用户的文件,全部授权为redis用户
	find . \! -user redis -exec chown redis '{}' +
	exec gosu redis "$0" "$@"
fi

exec "$@"

注意上图中的代码分析一下:

  • 1.假设启动容器的命令是docker run --name myredis -idt redis redis-server /usr/local/etc/redis/redis.conf

  • 2.容器启动后会执行docker-entrypoint.sh脚本此时的账号是root;

  • 3.当前账号是root因此进入后会执行第二个if条件中命令,其中位置参数含税表示如下;

    • $0表示当前脚本的名称即docker-entrypoint.sh;

    • $@表示外部传入的所有参数,即redis-server /usr/local/etc/redis/redis.conf;

  • 表示以redis账号的身份执行以下命令

# gosu redis "$0" "@"
docker-entrypoint.sh redis-server /usr/local/etc/redis/redis.conf
  • gosu redis "$0" "@"前面加上个exec,表示以gosu redis "$0" "@"这个命令启动的进程替换正在执行的 docker-entrypoint.sh 进程 保证了对应的进程ID为1

  • gosu redis "$0" "@"导致docker-entrypoint.sh再执行一次,但是当前的账号已经不是root了,所以会执行兜底逻辑 exec "$@";

  • 此时的$@是redis-server /usr/local/etc/redis/redis.conf,因此redis服务会启动并且启动服务的用户为redis;

最后我们在 Redis 的Dockerfile 中可以看见安装gosu的一些身影

# grab gosu for easy step-down from root
# https://github.com/tianon/gosu/releases
ENV GOSU_VERSION 1.14
RUN set -ex; \
	\
	fetchDeps=" \
		ca-certificates \
		dirmngr \
		gnupg \
		wget \
	"; \
	apt-get update; \
	apt-get install -y --no-install-recommends $fetchDeps; \
	rm -rf /var/lib/apt/lists/*; \
	\
	dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')"; \
	wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch"; \
	wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch.asc"; \
	export GNUPGHOME="$(mktemp -d)"; \
	gpg --batch --keyserver ha.pool.sks-keyservers.net --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4; \
	gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu; \
	gpgconf --kill all; \
	rm -r "$GNUPGHOME" /usr/local/bin/gosu.asc; \
	chmod +x /usr/local/bin/gosu; \
	gosu nobody true; \
	\
	apt-get purge -y --auto-remove $fetchDeps

5.容器运行时保护

5.1 设置SElinux安全选项

描述: SELinux是一个有效且易于使用的Linux访问控制机制。
加固说明: SELinux提供强制访问控制(MAC)系统,大大增强了默认的自由访问控制(DAC)模型。因此,可以通过在Linux主机上启用SELinux(如果适用)来增加额外的安全防护。
检测方法:

# 检查方式
$ ~$ docker ps -q -a | xargs docker inspect --format 'Name = {{.Name}}, SecurityOpt = {{json .HostConfig.SecurityOpt}}'
Name = /nginx, SecurityOpt = null
Name = /harbor-jobservice, SecurityOpt = null

判断方法: 上述命令应返回当前为容器配置的所有安全选项。
加固方法: 如果SELinux适用于你的Linux操作系统,请使用它,可能需要遵循以下步骤:

  • 1.设置SELinux状态。

  • 2.设置SELinux策略。

  • 3.为Docker容器创建或导入SELinux策略模板。

  • 4.启用SELinux的守护进程模式下启动Docker。docker daemon --selinux-enabled

  • 5.使用安全选项启动Docker容器。docker run --interactive—tty --security-optlabel=level TopSecretcentos /bin/bash

# 1.设置selinux-enabled字段值为true并添到daemon.json 文件中
"selinux-enabled": true,

操作影响: selinux配置文件中定义的一组操作限制。如果配置错误,容器可能无法完成工作。
默认值: 默认情况下,在容器上不应用SELinux安全选项。

5.2 linux内核特性在容器内受限

描述: 默认情况下,Docker使用一组受限制的Linux内核特性启动容器, 这意味着可以将任何进程授予所需的功能,而不是root访问, 使用Linux内核特性,这些进程不必以root权限运行。(为容器运行中的任何进程授予最小所需功能)
加固说明: Docker支持添加和删除功能,允许使用非默认配置文件。这可能会使Docker通过移除功能更加安全,或者通过增加功能来减少安全性。因此,建议除去容器进程明确要求的所有功能。
检测方法:

~$ docker ps -q -a  | xargs docker inspect --format '{{.Id}}:CapAdd={{.HostConfig.CapAdd}}CapDrop={{.HostConfig.CapDrop}}'
72b163c7e64e66fcecd09336bde28ee393cf07ca80c091c920eeb50c2f596bf8:CapAdd=CapDrop=
5d8e597549062d7709b667457e278e33f15221cb5c8e112bcbb648b3bca59f04:CapAdd=[CHOWN SETGID SETUID NET_BIND_SERVICE]CapDrop=[ALL]

~$ docker ps -q -a | xargs docker inspect --format 'Name = {{.Name}}, CapAdd={{json .HostConfig.CapAdd}}, CapDrop={{json .HostConfig.CapDrop}}'
Name = /nginx, CapAdd=["CHOWN","SETGID","SETUID","NET_BIND_SERVICE"], CapDrop=["ALL"]
Name = /harbor-jobservice, CapAdd=["CHOWN","SETGID","SETUID"], CapDrop=["ALL"]

判断方法: 验证添加和删除的Linux内核功能是否符合每个容器实例的容器进程所需的功能。

加固方法:

# 1.分别使用--cap-add和--cap-drop参数来增加所需的功能或者移除所需功能。
$ docker run --cap-add={Capability1,Capability2} --cap-drop=all  # 白名单机制
$ docker run -it --name captest --cap-add={chown,net_bind_service,net_admin} --cap-drop=all ubuntu:latest /bin/bash

# 2.在容器内部查询支持那些capability
$ capsh --print
Current: = cap_chown,cap_net_bind_service,cap_net_admin+eip
Bounding set =cap_chown,cap_net_bind_service,cap_net_admin

$ docker ps -q -a | xargs docker inspect --format '{{.Name}}, CapAdd={{json .HostConfig.CapAdd}}, CapDrop={{json .HostConfig.CapDrop}}'
captest, CapAdd=["chown","net_bind_service","net_admin"], CapDrop=["all"]

操作影响: 基于添加或删除的Linux内核功能,容器中功能会受到限制。
默认值: 默认情况下以下功能可用于容器,参考containerd项目地址 (https://github.com/containerd/containerd/blob/main/oci/spec.go)

func defaultUnixCaps() []string {
	return []string{
		"CAP_CHOWN",
		"CAP_DAC_OVERRIDE",
		"CAP_FSETID",
		"CAP_FOWNER",
		"CAP_MKNOD",
		"CAP_NET_RAW",
		"CAP_SETGID",
		"CAP_SETUID",
		"CAP_SETFCAP",
		"CAP_SETPCAP",
		"CAP_NET_BIND_SERVICE",
		"CAP_SYS_CHROOT",
		"CAP_KILL",
		"CAP_AUDIT_WRITE",
	}
}

5.3 不使用特权容器

描述: 使用--privileged标志将所有Linux内核功能提供给容器,从而覆盖-cap-add和-cap-drop标志。若无必须请不要使用它。
加固说明: --privileged标志给容器提供所有功能,并且还提升了cgroup控制器执行的所有限制, 若无必须不要使用它。
检测加固:

# 命令应为每个容器实例返回Privileged=
docker ps -q -a | xargs docker inspect --format '{{.Id}}:Privileged={{.HostConfig.Privileged}}'
72b163c7e64e66fcecd09336bde28ee393cf07ca80c091c920eeb50c2f596bf8:Privileged=false
5d8e597549062d7709b667457e278e33f15221cb5c8e112bcbb648b3bca59f04:Privileged=false

# 不要运行带有--privileged标志的容器。
~$ docker ps -q -a | xargs docker inspect --format '{{.Name}}:Privileged={{.HostConfig.Privileged}}'
/captest:Privileged=false
/cap_test:Privileged=false
/nginx:Privileged=false
/harbor-jobservice:Privileged=false

操作影响: 除默认值之外的Linux内核功能将无法在容器内使用。
默认值: False.

5.4 敏感的主机系统目录不要挂载在容器上

描述: 不应允许将敏感的主机系统目录(如/boot /dev /etc /lib /proc /sys /usr)作为容器卷进行挂载,特别是在读写模式下。
加固说明: 如果敏感目录以读写方式挂载,则可以对这些敏感目录中的文件进行更改。这些更改可能会降低安全性,且直接操作影响Docker宿主机。
检测方法: 命令将返回当前映射目录的列表,以及是否以每个容器实例的读写模式进行挂载。

# 查看创建运行的容器挂载的主机系统目录
~$ docker ps -q -a | xargs docker inspect --format '{{.Id}}:Volumes={{.Mounts}}'
72b163c7e64e66fcecd09336bde28ee393cf07ca80c091c920eeb50c2f596bf8:Volumes=[map[Destination:/var/run/docker.sock Mode: Propagation:rprivate RW:true Source:/var/run/docker.sock Type:bind]]
5d8e597549062d7709b667457e278e33f15221cb5c8e112bcbb648b3bca59f04:Volumes=[map[Destination:/etc/cert Mode:z Propagation:rprivate RW:true Source:/data/harbor/secret/cert Type:bind] map[Destination:/etc/nginx Mode:z Propagation:rprivate RW:true Source:/opt/harbor/common/config/nginx Type:bind] map[Destination:/harbor_cust_cert Mode: Propagation:rprivate RW:true Source:/opt/harbor/common/config/shared/trust-certificates Type:bind] map[Destination:/run Driver:local Mode: Name:2cbf86a1597dc82401969b05222e10b266d28c74382e97e1d402160c527a7633 Propagation: RW:true Source:/app/docker/volumes/2cbf86a1597dc82401969b05222e10b266d28c74382e97e1d402160c527a7633/_data Type:volume] map[Destination:/var/cache/nginx Driver:local Mode: Name:0955b631512898189af6c65b47098f6a791733f8ad560830726105608ba675ff Propagation: RW:true Source:/app/docker/volumes/0955b631512898189af6c65b47098f6a791733f8ad560830726105608ba675ff/_data Type:volume] map[Destination:/var/log/nginx Driver:local Mode: Name:afab7f34d4fa4dbcfae33ceadc0bc8b17fbfb79abdeabecda6cbe30cd861bef6 Propagation: RW:true Source:/app/docker/volumes/afab7f34d4fa4dbcfae33ceadc0bc8b17fbfb79abdeabecda6cbe30cd861bef6/_data Type:volume]]

# 排查那些容器挂载了主机敏感目录
~$ docker ps -q -a | xargs docker inspect --format '{{.Name}}: Volumes={{json .Mounts}}' | grep -E '"Source":"(/boot|/dev|/etc|/lib|/proc|/sys|/usr)'

加固方法: 不要将主机敏感目录挂载在容器上,尤其是在读写模式下。
操作影响: None.
默认值: Docker默认为读写卷,也可以以只读方式挂载一个目录。默认情况下,不会在容器上挂载敏感的主机目录。

5.5 SSH 不在容器中运行

描述: SSH服务不应该在容器内运行。
加固说明: 在容器内运行SSH可以增加安全管理的复杂性难以管理SSH服务器的访问策略和安全合规性难以管理各种容器的密钥和密码难以管理SSH服务器的安全升级可以在不使用SSH的情况下对容器进行shell访问,避免不必要地增加安全管理的复杂性。
检测方法: 对于每个容器实例,执行以下命令 docker exec $INSTANCE_ID ps -el
判断方法: 确保容器内没有SSH服务
加固方法: 从容器中卸载SSH服务
操作影响: None.
默认值: 默认情况下,SSH服务不在容器内运行

5.6 特权端口禁止映射到容器内

描述: 低于1024的TCP/IP端口号被认为是特权端口, 由于各种安全原因,普通用户和进程不允许使用它们。
加固说明: 默认情况下,如果用户没有明确声明容器端口进行主机端口映射,Docker会自动地将容器端口映射到主机上的49153-65535中。但是,如果用户明确声明它,Docker可以将容器端口映射到主机上的特权端口。这是因为容器使用不限制特权端口映射的NET_BIND_SERVICELinux内核功能来执行。特权端口接收和发送各种敏感和特权的数据。允许docker使用它们可能会带来严重的操作影响。

检测方法:

# 通过执行以下命令列出容器的所有运行实例及其端口映射
$ docker ps -q | xargs docker inspect --format '{{.Id}}:Ports={{.NetworkSettings.Ports}}'
5d8e597549062d7709b667457e278e33f15221cb5c8e112bcbb648b3bca59f04:Ports=map[4443/tcp:[map[HostIp:0.0.0.0 HostPort:4443]] 8080/tcp:[map[HostIp:0.0.0.0 HostPort:80]] 8443/tcp:[map[HostIp:0.0.0.0 HostPort:443]]]
b769645200f1eb51bb04ad65b9d9fd122b017ebb7ec707437beb9883ce117285:Ports=map[10514/tcp:[map[HostIp:127.0.0.1 HostPort:1514]]]

判断方法: 查看列表,并确保容器端口未映射到低于1024的主机端口号。
加固方法: 启动容器时,不要将容器端口映射到特权主机端口。另外,确保没有容器在Docker文件中特权端口映射声明。
操作影响: None.
默认值: 默认情况下,允许将容器端口映射到主机上的特权端口。
备注: 有些端口是必须使用的HTTP和HTTPS必须绑定80/tcp和443/tcp。

5.7 只映射必要的端口

描述: 容器镜像的Dockerfile定义了在容器实例上默认要打开的端口。端口列表可能与在容器内运行的应用程序相关。
加固说明: 一个容器可以运行在Docker文件中为其镜像定义的端口,也可以任意传递运行时参数以打开一个端口列表。此外,Docker文件可能会进行各种更改,暴露的端口列表可能与在容器内运行的应用程序不相关。推荐做法是不要打开不需要的端口。
检测方法: 通过执行以下命令列出容器的所有运行实例及其端口映射 docker ps -q | xargs docker inspect --format '{{.Id}}:Ports={{.NetworkSettings.Ports}}'
判断方法: 查看列表,并确保映射的端口是容器真正需要的端口。
加固方法: 修复容器镜像的Dockerfile,以便仅通过容器化应用程序公开所需的端口。也可以通过在启动容器时不使用-P(UPPERCASE)或--publish -a 标志来完全忽略Dockerfile中定义的端口列表。使用-p明确定义特定容器实例所需的端口。
操作影响: None.
默认值: 默认情况下,当使用'-P'或'--publish -a '标志运行容器时,打开在EXPOSE指令下的Dockerfile中列出的所有端口。

5.8 不共享主机的网络命名空间

描述: 当设置为--net=host时,容器上的网络模式将容器放置在单独的网络堆栈中。这个选择告诉Docker不使用Docker内部网络,那就意味着容器在可以完全访问主机的网络接口。
加固说明: 这有一定的安全风险,允许容器进程像任何其他root进程一样打开低端端口。还允许容器访问Docker主机上的D-bus等网络服务。因此容器进程可以潜在地执行恶意的事,关闭Docker主机。若无需要不要使用host模式。
检测方法:

# 当命令执行返回 NetworkMode=host时则意味着在启动容器时传递了--net=host选项。
~$ docker ps -q -all | xargs docker inspect --format '{{.Id}}:NetworkMode={{.HostConfig.NetworkMode}}'
5d8e597549062d7709b667457e278e33f15221cb5c8e112bcbb648b3bca59f04:NetworkMode=harbor_harbor

加固方法: 启动容器时不要通过'--net=host'选项。
操作影响: None.
默认值: 默认情况下,容器连接到Docker网桥。

5.9 确保容器的内存使用合理

描述: 默认情况下,Docker主机上的所有容器均等共享资源。通过使用Docker主机的资源管理功能,内存限制,可以控制容器可能消耗的内存量。
加固说明: 默认情况下,容器可以使用主机上的所有内存。可以使用内存限制机制来防止由于一个容器消耗了所有主机资源而导致拒绝服务,以致同一主机上的其他容器无法执行预期功能。
检测方法:

:~$ docker ps -q -a | xargs docker inspect --format '{{.Id}}:Memory={{.HostConfig.Memory}}'
5d8e597549062d7709b667457e278e33f15221cb5c8e112bcbb648b3bca59f04:Memory=0

判断方法: 如果上述命令返回0,则表示内存无限制。如果上述命令返回非零值,则表示已有内存限制策略。
加固方法: 建议使用--memory参数运行容器。
操作影响: 如果有设置适当的限制,容器可能将无法使用。
默认值: 默认情况下没有内存限制。

5.10 正确设置容器上的CPU优先级

描述: 默认情况下,Docker主机上的所有容器均可共享资源。通过使用
Docker主机的资源管理功能(如CPU共享),可以控制容器可能占用的主机CPU资源。
加固说明: 默认情况下,CPU时间在容器间平均分配。如果需要,为了控制容器实例之间的CPU时间,可以使用CPU共享功能。CPU共享允许将一个容器优先于另一个容器,并禁止较低优先级的容器更频繁占用CPU资源。可确保高优先级的容器更好地运行。
检测方法:

~$ docker ps -q -a|xargs docker inspect --format '{{.Id}}:CpuShares= {{.HostConfig.CpuShares}}'
5d8e597549062d7709b667457e278e33f15221cb5c8e112bcbb648b3bca59f04:CpuShares= 0

判断方法: 如果上述命令返回0或1024,则表示CPU无限制。如果上述命令返回非1024值以外的非零值,则表示CPU已经限制。
加固方法: 使用--cpu-shares参数启动容器。
操作影响: 如果没有设置适当的CPU共享,容器进程可能会不能执行。
默认值: 默认情况下没有执行CPU份额限制。

5.11 确保进入容器的流量绑定到特定的网卡

描述: 默认情况下,Docker容器可以连接到外部,但外部无法连接到容器。每个传出连接都源自主机自己的IP地址。所以只允许通过主机上的特定外部接口访问容器服务。
加固说明: 如果主机上有多个网卡,则容器可以接受任何网络接口上公开端口的连接。这可能不安全。很多时候,特定的端口暴露在外部,并且在这些端口上运行诸如入侵检测,入侵防护,防火墙,负载均衡等服务以筛选传入的公共流量。因此,只允许来自特定外部接口的传入连接。
检测方法: 通过执行以下命令列出容器的所有运行实例及其端口映射

~$ docker ps -q | xargs docker inspect --format '{{.Id}}:Ports={{.NetworkSettings.Ports.HostIp}}'
5d8e597549062d7709b667457e278e33f15221cb5c8e112bcbb648b3bca59f04:Ports=map[4443/tcp:[map[HostIp:0.0.0.0 HostPort:4443]] 8080/tcp:[map[HostIp:0.0.0.0 HostPort:80]] 8443/tcp:[map[HostIp:0.0.0.0 HostPort:443]]]

判断方法: 查看列表并确保公开的容器端口与特定接口绑定,而不是通配符IP地址-0.0.0.0。
加固方法: 将容器端口绑定到所需主机端口上的特定网卡。
默认值: 默认情况下,Docker将容器端口公开在0.0.0.0,可接受主机上任何可能的传入网络端口。
操作影响: None.

5.12 容器重启策略on-failure设置为5

描述: 在docker run命令中使用 --restart标志,可以指定重启策略,以便在退出时确定是否重启容器。基于安全考虑,应该设置重启尝试次数限制为5次。
加固说明: 如果无限期地尝试启动容器,可能会导致主机上的拒绝服务。这可能是一种简单的方法来执行分布式拒绝服务攻击,特别是在同一主机上有多个容器时。此外,忽略容器的退出状态并始终尝试重新启动容器导致未调查容器终止的根本原因。如果一个容器被终止,应该做的是去调查它重启的原因,而不是试图无限期地重启它。因此,建议使用故障重启策略并将其限制为最多5次重启尝试。
检测方法:

# 在 Docker run 上使用 --restart 标志,您可以指定容器在退出时应该或不应该如何重新启动的重新启动策略。
docker run --restart=on-failure:5  # 此处设置重试数为5次。

# 查看当前容器异常导致的重启次数
~$ docker ps -q  -a | xargs docker inspect -f "{{ .RestartCount }}".

# 查看重启策略及其重试次数
~$ docker ps -q  -a | xargs docker inspect --format '{{.Id}}:RestartPolicyName={{.HostConfig.RestartPolicy.Name}}, MaximumRetryCount={{.HostConfig.RestartPolicy.MaximumRetryCount}}'  # 
5d8e597549062d7709b667457e278e33f15221cb5c8e112bcbb648b3bca59f04:RestartPolicyName=always,MaximumRetryCount=0
b28b6bd4264d9aad4eff7214df6d368c44b5c252a6d61bb7fd85ebc75ffdc957:RestartPolicyName=always,MaximumRetryCount=0

判断方法: 如果上述命令返回RestartPolicyName=always,那么系统没有按需要进行配置。如果上述命令返回RestartPolicyName=no或仅RestartPolicyName=,则重新启动策略未被使用,容器不会重新启动。如果上述命令返RestartPolicyName=onfailure,则通过查看MaximumRetryCount验证重新启动尝试的次数是否设置为5或更少。
加固方法: 在docker run 或 docker-compos e中设定容器重启次数
操作影响: 容器只会尝试重新启动5次。
默认值: 默认情况下,容器未配置重新启动策略。

5.13 确保主机的进程命名空间不共享

描述: 进程PID命名空间隔离进程,不同PID命名空间中的进程可以具有相同的PID。这就是容器和主机之间的进程级隔离。
加固说明: PID名称空间提供了进程的隔离。PID命名空间删除了系统进程的视图,并允许重用包括PID的进程ID。如果主机的PID名称空间与容器共享,它基本上允许容器内的进程查看主机上的所有程。这就打破了主机和容器之间进程级别隔离的优点。若访问容器最终可以知道主机系统上运行的所有进程,甚至可以从容器内杀死主机系统进程。。因此不要将容器与主机的进程名称空间共享。
检测方法:

~$ docker ps -q -a | xargs docker inspect --format '{{.Id}}:PidMode={{.HostConfig.PidMode}}'
5d8e597549062d7709b667457e278e33f15221cb5c8e112bcbb648b3bca59f04:PidMode=

判断方法: 如果上述命令返回host,则表示主机PID名称空间与容器共享,存在安全风险。
加固方法: 不要使用'--pid=host'参数启动容器。
操作影响: 容器进程无法看到主机系统上的进程。在某些情况下,可能需要容器共享主机的进程命名空间。可以使用像strace或gdb这样的调试工具构建容器,在调试容器中的进程时要使用这些工具。
默认值: 默认情况下,所有容器都启用了PID命名空间,并且主机的进程命名空间不与容器共享。

5.14 主机的IPC命令空间不共享

描述: IPC命名空间提供命名共享内存段,信号量和消息队列的分离。主机上的IPC命名空间不应该与容器共享,并且应该保持独立。
加固说明: IPC命名空间提供主机和容器之间的IPC分离。如果主机的IPC名称空间与容器共享,它允许容器内的进程查看主机系统上的所有IPC。这打破了主机和容器之间IPC级别隔离。可通过访问容器操纵主机IPC。因此不要将主机的IPC命名空间与容器共享。
检测方法:

~$ docker ps -q -a | xargs docker inspect --format '{{.Id}}:IpcMode={{.HostConfig.IpcMode}}'
5d8e597549062d7709b667457e278e33f15221cb5c8e112bcbb648b3bca59f04:IpcMode=shareable

判断方法: 如果上述命令返回host,则意味着主机IPC命名空间与容器共享。如果上述命令不返回任何内容,则主机的IPC命名空间不会共享。
加固方法: 不要使用'--ipc=host'参数启动容器。
操作影响: 共享内存段用于加速进程间通信。它通常被高性能应用程序使用。如果这些应用程序被容器化为多个容器,则可能需要共享容器的IPC名称空间以实现高性能。在这种情况下,仍然应该共享容器特定的IPC命名空间而不是整个主机IPC命名空间。可以将容器的IPC名称空间与另一个容器共享
默认值: 默认情况下,所有容器都启用IPC命名空间,并且主机IPC命名空间不与任何容器共享。

5.15 主机设备不直接共享给容器

描述: 主机设备可以在运行时直接共享给容器。不要将主机设备直接共享给容器,特别是对不受信任的容器。
加固说明: 选项--device将主机设备共享给容器,因此容器可以直接访问这些主机设备。不允许容器以特权模式运行以访问和操作主机设备。默认情况下,容器将能够读取,写入和mknod这些设备。此外,容器可能会从主机中删除设备。因此,不要直接将主机设备共享给容器。如果必须的将主机设备共享给容器,适当地使用共享权限:r-readw -writem -mknod allowed
检测方法:

docker ps -q -a |xargs docker inspect --format '{{.Id}}:Devices={{.HostConfig.Devices}}'

判断方法: 验证从容器中访问主机设备,并且正确设置所需的权限。
加固方法: 不要将主机设备直接共享于容器。如果必须将主机设备共享给容器,使用正确的一组权限:
操作影响: 将无法直接在容器内使用主机设备。
默认值: 默认情况下,主机设备不共享于容器。如果不提供共享权限并选择将主机设备展示给容器,则主机设备将具有读取写入权限。

5.16 设置默认的ulimit配置(在需要时)

描述: 默认的ulimit是在Docker守护进程级别设置的, 如果需要可以在容器运行时重写默认的ulimit设置。
加固说明: ulimit提供对shell可用资源的控制。设置系统资源控制可以防止资源耗尽带来的问题,如fork炸弹。有时候合法的用户和进程也可能过度使用系统资源,导致系统资源耗尽应该遵守在Docker守护进程级别设置的默认ulimit。如果默认的ulimit设置不适合特定的容器实例,则可以将它们覆盖为例外。但是尽量不要这样做。如果有太多例外的话,可以直接修改默认的ulimit设置。
检测方法:

~$ docker ps -q -a | xargs docker inspect --format '{{.Id}}:Ulimits={{.HostConfig.Ulimits}}'
5d8e597549062d7709b667457e278e33f15221cb5c8e112bcbb648b3bca59f04:Ulimits=

判断方法: 对于每个容器实例,上述命令应该返回 Ulimits=,除非出现异常并且需要覆盖默认的ulimit设置。
加固方法: 如果需要,在docker run或docker-compose启动参数中设定正确合理的ulimit值。
操作影响: 如果ulimits未正确设置,则可能无法实现所需的资源控制,甚至导致系统无法使用。
默认值: 容器实例继承在Docker守护进程级别设置的默认ulimit

5.17 设置主机的UTS命令空间不共享

描述: UTS命名空间提供两个系统标识符的隔离:主机名和NIS域名。
加固说明: 它于设置在该名称空间中运行进程可见的主机名和域名。在容器中运行的进程通常不需要知道主机名和域名。与主机共享UTS命名空间提供了容器可更改主机的主机名。这是不安全的,因此名称空间不应与主机共享。
检测方法:

~$ docker ps -q -a | xargs docker inspect --format '{{.Id}}:UTSMode={{.HostConfig.UTSMode}}'
5d8e597549062d7709b667457e278e33f15221cb5c8e112bcbb648b3bca59f04:UTSMode=
b28b6bd4264d9aad4eff7214df6d368c44b5c252a6d61bb7fd85ebc75ffdc957:UTSMode=

判断方法: 如果上述命令返回host,则意味着主机UTS名称空间与容器共享,不符合要求。如果上述命令不返回任何内容,则主机的UTS名称空间不共享。
加固方法: 不要使用'--uts=host'参数启动容器。
操作影响: None.
默认值: 默认情况下,所有容器都启用了UTS命名空间,并且主机UTS命名空间不与任何容器共享。
备注:

5.18 不要使用docker的默认网桥docker0

描述: 不要使用Docker的默认bridge docker0。使用docker的用户定义的网络进行容器联网。
加固说明: Docker将以桥模式创建的虚拟接口连接到名为docker0的公共桥。这种默认网络模型易受ARP欺骗和MAC洪泛攻击的攻击,因为没有应用过滤。
检测方法: 运行以下命令,并验证容器是否在用户定义的网络上,而不是默认的docker0网桥。

~$ docker network ls -q | xargs docker network inspect --format '{{.Name}}:{{.Options}}'
bridge:map[com.docker.network.bridge.default_bridge:true com.docker.network.bridge.enable_icc:true com.docker.network.bridge.enable_ip_masquerade:true com.docker.network.bridge.host_binding_ipv4:0.0.0.0 com.docker.network.bridge.name:docker0 com.docker.network.driver.mtu:1500]
harbor_harbor:map[]

判断方法: 检查是否在使用bridge docker0默认网桥
加固方法: 遵循Docker文档并设置用户定义的网络。运行定义的网络中的所有容器。
操作影响: 必须管理用户定义的网络。
默认值: 默认情况下,docker在其docker0桥上运行容器。

至此完毕!

更多精彩文章,请查看,我在B站学云原生之Docker镜像安全最佳实践(https://www.bilibili.com/read/cv15553799)


欢迎各位志同道合的朋友一起学习交流,如文章有误请在下方留下您宝贵的经验知识,个人邮箱地址【master#weiyigeek.top】或者 个人公众号【WeiyiGeek】联系我。

更多文章来源于【WeiyiGeek Blog - 为了能到远方,脚下的每一步都不能少】 个人博客【https://weiyigeek.top 】

Docker容器镜像安全最佳实践指南_第2张图片

专栏书写不易,如果您觉得这个专栏还不错的,请给这篇专栏【点个赞、投个币、收个藏、关个注,转个发】,这将对我的肯定,谢谢!。

echo  "【点个赞】,动动你那粗壮的拇指或者芊芊玉手,亲!"

printf("%s", "【投个币】,万水千山总是情,投个硬币行不行,亲!")

fmt.Printf("【收个藏】,阅后即焚不吃灰,亲!")  

System.out.println("【关个注】,后续浏览查看不迷路哟,亲!")

console.info("【转个发】,让更多的志同道合的朋友一起学习交流,亲!")

Docker容器镜像安全最佳实践指南_第3张图片

你可能感兴趣的:(个人博客文章,docker,容器,安全)