Docker 安全加固实战:从宿主机到镜像、运行时的完整防护清单
Docker 安全加固方案|附完整命令
Docker 让应用交付变得更快、更标准化,但也带来了新的安全边界问题。很多团队在使用 Docker 时,重点关注镜像构建、容器编排和部署效率,却忽略了一个关键事实:容器不是虚拟机,容器共享宿主机内核。一旦容器逃逸、镜像被投毒、Docker Socket 暴露或权限配置不当,攻击者可能直接威胁宿主机、内网服务甚至整个生产环境。
本文将从 宿主机、Docker Daemon、镜像、容器运行时、网络、日志审计、漏洞扫描、CI/CD 流程 等多个角度,系统梳理 Docker 安全加固方案,并附上可直接执行的命令示例,适合用于企业生产环境基线建设、安全整改和运维检查。
一、Docker 安全风险概览
在正式加固之前,需要先明确 Docker 常见风险来源。
1. 容器权限过高
很多人为了方便,直接使用如下方式启动容器:
docker run -d --privileged nginx
--privileged 会给容器几乎完整的宿主机能力,包括访问设备、加载内核模块、修改网络配置等。除非是极少数底层运维场景,否则生产环境不应使用。
2. Docker Socket 暴露
Docker Socket 通常位于:
/var/run/docker.sock
如果将它挂载进容器:
docker run -v /var/run/docker.sock:/var/run/docker.sock ...
容器内进程就可以控制宿主机 Docker Daemon,相当于间接获得宿主机 root 权限。
3. 镜像来源不可信
使用未知来源镜像,可能引入后门、挖矿程序、恶意启动脚本、弱口令服务或高危漏洞组件。
4. 容器以 root 用户运行
Docker 默认容器内用户通常是 root。虽然容器内 root 不完全等于宿主机 root,但在挂载目录、内核漏洞、能力配置不当时,风险会显著放大。
5. 网络边界不清晰
容器端口随意暴露、容器互通无限制、管理端口对公网开放,都会扩大攻击面。
二、宿主机安全加固
Docker 的安全基础首先来自宿主机。宿主机一旦失守,容器层面的防护基本失效。
1. 保持系统更新
以 Ubuntu/Debian 为例:
sudo apt update
sudo apt upgrade -y
sudo apt autoremove -y
以 CentOS/RHEL 为例:
sudo yum update -y
或 Rocky Linux / AlmaLinux:
sudo dnf update -y
建议生产环境建立补丁管理流程,至少对内核、OpenSSL、containerd、runc、Docker Engine 等关键组件保持关注。
查看系统版本:
cat /etc/os-release
uname -a
查看 Docker 版本:
docker version
docker info
2. 最小化安装宿主机组件
Docker 宿主机不应安装无关服务,例如数据库、办公软件、调试工具、开发环境等。减少软件包数量可以降低漏洞暴露面。
查看监听端口:
sudo ss -tunlp
查看运行服务:
systemctl list-units --type=service --state=running
禁用不必要服务:
sudo systemctl disable 服务名
sudo systemctl stop 服务名
3. 配置防火墙
Ubuntu 可使用 UFW:
sudo ufw enable
sudo ufw default deny incoming
sudo ufw default allow outgoing
只开放必要端口,例如 SSH、HTTP、HTTPS:
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw status verbose
CentOS/RHEL 可使用 firewalld:
sudo systemctl enable firewalld --now
sudo firewall-cmd --set-default-zone=public
sudo firewall-cmd --add-service=ssh --permanent
sudo firewall-cmd --add-service=http --permanent
sudo firewall-cmd --add-service=https --permanent
sudo firewall-cmd --reload
sudo firewall-cmd --list-all
如果 Docker 自动修改 iptables,建议定期审计规则:
sudo iptables -L -n -v
sudo iptables -t nat -L -n -v
三、Docker Daemon 安全加固
Docker Daemon 是 Docker 的核心控制组件,必须重点保护。
1. 禁止 Docker API 暴露到公网
检查 Docker 是否监听 TCP:
sudo ss -tunlp | grep dockerd
如果发现类似 0.0.0.0:2375,属于高危配置。2375 是无 TLS 的 Docker API 端口,任何能访问该端口的人都可能控制 Docker。
检查 systemd 启动参数:
systemctl cat docker
如果存在:
-H tcp://0.0.0.0:2375
应立即移除。
编辑 Docker 配置:
sudo mkdir -p /etc/docker
sudo vi /etc/docker/daemon.json
建议基础配置如下:
{
"icc": false,
"userland-proxy": false,
"no-new-privileges": true,
"live-restore": true,
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "3"
}
}
重启 Docker:
sudo systemctl daemon-reload
sudo systemctl restart docker
验证配置:
docker info
2. 限制 Docker 用户组
加入 docker 用户组的用户可以不通过 sudo 直接控制 Docker,而控制 Docker 基本等价于拥有 root 权限。因此,必须严格限制该组成员。
查看 docker 组成员:
getent group docker
将不必要用户移出 docker 组:
sudo gpasswd -d 用户名 docker
如需临时执行 Docker 命令,建议使用 sudo:
sudo docker ps
3. 启用 rootless Docker
Rootless 模式可以让 Docker Daemon 和容器以非 root 用户运行,降低容器逃逸后的影响范围。
安装依赖:
sudo apt install -y uidmap dbus-user-session
安装 rootless Docker:
dockerd-rootless-setuptool.sh install
设置环境变量:
export PATH=/usr/bin:$PATH
export DOCKER_HOST=unix:///run/user/$(id -u)/docker.sock
让用户服务开机启动:
systemctl --user enable docker
loginctl enable-linger $(whoami)
验证:
docker info | grep -i rootless
需要注意,rootless 模式在端口绑定、存储驱动、网络性能等方面可能有一些限制,适合安全要求高的业务逐步试点。
四、镜像安全加固
镜像是容器运行的基础。如果镜像本身不安全,后续运行时防护会非常被动。
1. 使用官方或可信镜像
拉取镜像时优先使用官方镜像或企业内部镜像仓库。
docker pull nginx:1.26-alpine
避免使用不明确标签:
docker pull nginx:latest
latest 并不代表最新安全版本,也不利于版本追踪和回滚。建议固定版本:
docker pull nginx:1.26.2-alpine
2. 使用轻量基础镜像
推荐使用 Alpine、Distroless、Debian Slim 等基础镜像,减少系统组件和漏洞数量。
示例 Dockerfile:
FROM nginx:1.26-alpine
COPY ./dist /usr/share/nginx/html
EXPOSE 80
对于 Go、Java、Node.js 等应用,建议使用多阶段构建,避免将编译工具、源码、包管理缓存带入运行镜像。
Go 应用示例:
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY . .
RUN go build -o server ./cmd/server
FROM alpine:3.20
RUN addgroup -S app && adduser -S app -G app
WORKDIR /app
COPY --from=builder /app/server /app/server
USER app
EXPOSE 8080
CMD ["/app/server"]
3. 删除敏感信息
不要将 .env、SSH 私钥、云厂商 AK/SK、数据库密码、内部证书等写入镜像。
检查镜像历史:
docker history 镜像名:标签
如果曾在 Dockerfile 中执行:
RUN echo "password=123456" > /app/config
即使后续删除,敏感信息也可能留在镜像层中。因此敏感信息应通过环境变量、密钥管理系统或编排平台 Secret 注入。
4. 扫描镜像漏洞
可以使用 Trivy 扫描镜像:
trivy image nginx:1.26-alpine
只显示高危和严重漏洞:
trivy image --severity HIGH,CRITICAL nginx:1.26-alpine
扫描本地 Dockerfile 配置问题:
trivy config .
扫描文件系统:
trivy fs .
如果没有安装 Trivy,可使用:
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh
企业环境建议将漏洞扫描接入 CI/CD 流程,在镜像推送前完成安全检查。
五、容器运行时安全加固
容器启动参数是 Docker 安全加固中最关键的一环。很多风险并不来自 Docker 本身,而是来自不安全的运行方式。
1. 禁止使用 privileged 模式
不推荐:
docker run -d --privileged 镜像名
应按需添加能力,而不是一次性开放全部权限。
查看 Linux capabilities:
man capabilities
启动容器时删除所有能力,再按需添加:
docker run -d \
--cap-drop=ALL \
--cap-add=NET_BIND_SERVICE \
nginx:1.26-alpine
NET_BIND_SERVICE 允许进程绑定 1024 以下端口,适用于 Nginx、Apache 等 Web 服务。
2. 使用非 root 用户运行容器
在 Dockerfile 中指定用户:
RUN addgroup -S app && adduser -S app -G app
USER app
也可以启动时指定 UID/GID:
docker run -d \
--user 10001:10001 \
nginx:1.26-alpine
查看容器内用户:
docker exec -it 容器名 id
如果业务必须以 root 启动,也应尽量在进程启动后降权,或在容器内使用专用非特权用户运行主进程。
3. 启用只读根文件系统
很多应用运行时不需要修改根文件系统,可以启用只读模式:
docker run -d \
--read-only \
nginx:1.26-alpine
如果应用需要写临时文件,可以挂载 tmpfs:
docker run -d \
--read-only \
--tmpfs /tmp:rw,noexec,nosuid,size=64m \
nginx:1.26-alpine
如果需要持久化目录,应显式挂载指定路径:
docker run -d \
--read-only \
-v /data/app/uploads:/app/uploads:rw \
nginx:1.26-alpine
4. 限制容器资源
资源限制既能防止异常进程拖垮宿主机,也能降低拒绝服务攻击影响。
限制内存:
docker run -d \
--memory=512m \
--memory-swap=512m \
nginx:1.26-alpine
限制 CPU:
docker run -d \
--cpus="1.5" \
nginx:1.26-alpine
限制进程数:
docker run -d \
--pids-limit=256 \
nginx:1.26-alpine
查看容器资源使用:
docker stats
5. 禁止容器获取新权限
no-new-privileges 可以防止进程通过 setuid、setgid 等方式获得额外权限。
docker run -d \
--security-opt no-new-privileges:true \
nginx:1.26-alpine
也可以在 /etc/docker/daemon.json 中全局启用:
{
"no-new-privileges": true
}
6. 使用 seccomp 限制系统调用
Docker 默认启用 seccomp 配置,可减少危险系统调用。不要随意使用:
--security-opt seccomp=unconfined
查看容器安全选项:
docker inspect 容器名 --format '{{json .HostConfig.SecurityOpt}}'
如需自定义 seccomp,可基于 Docker 默认模板调整,而不是完全关闭。
7. 使用 AppArmor 或 SELinux
Ubuntu 默认常用 AppArmor:
sudo aa-status
查看 Docker 默认 AppArmor:
docker inspect 容器名 --format '{{.AppArmorProfile}}'
CentOS/RHEL 常用 SELinux,建议保持 enforcing 模式:
getenforce
sudo setenforce 1
永久启用 SELinux:
sudo vi /etc/selinux/config
设置:
SELINUX=enforcing
使用 Docker 挂载目录时,可添加 SELinux 标签:
docker run -d \
-v /data/nginx:/usr/share/nginx/html:ro,Z \
nginx:1.26-alpine
六、文件挂载与数据安全
1. 避免挂载敏感宿主机目录
高危挂载示例:
-v /:/host
-v /etc:/host/etc
-v /var/run/docker.sock:/var/run/docker.sock
-v /proc:/host/proc
-v /sys:/host/sys
这些挂载可能导致容器读取敏感配置、修改宿主机文件,甚至控制 Docker。
2. 使用只读挂载
如果容器只需要读取配置,应使用只读模式:
docker run -d \
-v /data/nginx/conf:/etc/nginx/conf.d:ro \
nginx:1.26-alpine
检查挂载信息:
docker inspect 容器名 --format '{{json .Mounts}}'
3. 合理设置宿主机目录权限
创建专用目录:
sudo mkdir -p /data/app
sudo chown -R 10001:10001 /data/app
sudo chmod 750 /data/app
启动容器:
docker run -d \
--user 10001:10001 \
-v /data/app:/app/data:rw \
应用镜像:版本
原则是:容器只访问它必须访问的目录,只拥有它必须拥有的权限。
七、Docker 网络安全加固
1. 避免不必要的端口暴露
不推荐直接暴露全部接口:
docker run -p 0.0.0.0:8080:80 nginx
如果只允许本机访问,可绑定到 127.0.0.1:
docker run -d \
-p 127.0.0.1:8080:80 \
nginx:1.26-alpine
查看端口映射:
docker ps
2. 使用自定义网络隔离服务
创建独立网络:
docker network create app-net
启动应用:
docker run -d --name web --network app-net nginx:1.26-alpine
启动后端服务:
docker run -d --name api --network app-net 应用镜像:版本
查看网络:
docker network ls
docker network inspect app-net
删除不用的网络:
docker network prune
3. 关闭容器间默认通信
在 Docker Daemon 中配置:
{
"icc": false
}
重启 Docker:
sudo systemctl restart docker
该配置会影响默认 bridge 网络上的容器互通,生产环境建议结合自定义网络和防火墙策略一起使用。
八、日志与审计
1. 限制容器日志大小
如果不限制日志,容器可能写满磁盘,造成宿主机不可用。
全局配置 /etc/docker/daemon.json:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "3"
}
}
重启:
sudo systemctl restart docker
单个容器配置:
docker run -d \
--log-driver=json-file \
--log-opt max-size=100m \
--log-opt max-file=3 \
nginx:1.26-alpine
查看日志:
docker logs 容器名
2. 审计 Docker 关键文件
建议监控以下路径:
/etc/docker/
/var/lib/docker/
/var/run/docker.sock
/usr/bin/docker
/usr/bin/dockerd
/usr/bin/containerd
查看 Docker Socket 权限:
ls -l /var/run/docker.sock
正常情况下通常类似:
srw-rw---- 1 root docker ... /var/run/docker.sock
如果权限被改成 666,属于高危:
sudo chmod 660 /var/run/docker.sock
sudo chown root:docker /var/run/docker.sock
3. 使用 Docker Bench for Security
Docker Bench for Security 是官方安全基线检查工具,可以快速发现常见配置问题。
运行:
docker run --rm --net host --pid host --userns host --cap-add audit_control \
-e DOCKER_CONTENT_TRUST=$DOCKER_CONTENT_TRUST \
-v /etc:/etc:ro \
-v /usr/bin/containerd:/usr/bin/containerd:ro \
-v /usr/bin/runc:/usr/bin/runc:ro \
-v /usr/lib/systemd:/usr/lib/systemd:ro \
-v /var/lib:/var/lib:ro \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
--label docker_bench_security \
docker/docker-bench-security
注意,该工具需要较高权限访问宿主机信息,应仅在可信环境中运行。
九、Docker Compose 安全配置示例
如果使用 Docker Compose,也应在配置文件中体现安全基线。
示例 docker-compose.yml:
services:
web:
image: nginx:1.26-alpine
container_name: web
user: "10001:10001"
read_only: true
tmpfs:
- /tmp:rw,noexec,nosuid,size=64m
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
security_opt:
- no-new-privileges:true
pids_limit: 256
mem_limit: 512m
cpus: "1.0"
ports:
- "127.0.0.1:8080:80"
volumes:
- ./html:/usr/share/nginx/html:ro
networks:
- app-net
restart: unless-stopped
networks:
app-net:
driver: bridge
启动:
docker compose up -d
检查:
docker compose ps
docker inspect web
十、CI/CD 中的 Docker 安全控制
生产环境 Docker 安全不能只依赖上线后检查,更应该前移到构建和发布阶段。
1. 构建阶段扫描 Dockerfile
trivy config .
2. 构建镜像
docker build -t registry.example.com/app/web:1.0.0 .
3. 扫描镜像漏洞
trivy image --exit-code 1 --severity CRITICAL,HIGH registry.example.com/app/web:1.0.0
--exit-code 1 表示发现符合条件的漏洞时让流水线失败。
4. 推送到私有镜像仓库
docker push registry.example.com/app/web:1.0.0
5. 禁止部署未扫描镜像
可以在流水线中设置规则:只有扫描通过、签名通过、版本号合规的镜像才能进入生产环境。
十一、镜像签名与可信分发
1. 启用 Docker Content Trust
Docker Content Trust 可以验证镜像签名。
临时启用:
export DOCKER_CONTENT_TRUST=1
拉取镜像:
docker pull nginx:1.26-alpine
也可以写入 shell 配置:
echo 'export DOCKER_CONTENT_TRUST=1' >> ~/.bashrc
source ~/.bashrc
需要注意,实际生产中很多内部镜像仓库会使用 Cosign、Notary v2、Harbor 签名能力等方案进行镜像可信分发。
2. 使用 Cosign 签名镜像
安装 Cosign 后签名:
cosign sign registry.example.com/app/web:1.0.0
验证签名:
cosign verify registry.example.com/app/web:1.0.0
在 Kubernetes 或企业发布平台中,可进一步使用准入控制策略拒绝未签名镜像。
十二、常用安全检查命令清单
1. 检查高权限容器
docker ps -q | xargs -I {} docker inspect {} --format '{{.Name}} Privileged={{.HostConfig.Privileged}}'
2. 检查容器是否使用 host 网络
docker ps -q | xargs -I {} docker inspect {} --format '{{.Name}} NetworkMode={{.HostConfig.NetworkMode}}'
3. 检查容器用户
docker ps -q | xargs -I {} docker inspect {} --format '{{.Name}} User={{.Config.User}}'
如果 User= 为空,通常表示使用默认用户,很多镜像默认就是 root。
4. 检查容器能力配置
docker ps -q | xargs -I {} docker inspect {} --format '{{.Name}} CapAdd={{.HostConfig.CapAdd}} CapDrop={{.HostConfig.CapDrop}}'
5. 检查挂载 Docker Socket 的容器
docker ps -q | xargs -I {} docker inspect {} --format '{{.Name}} {{json .Mounts}}' | grep docker.sock
6. 检查端口暴露
docker ps --format 'table {{.Names}}\t{{.Image}}\t{{.Ports}}'
7. 检查日志配置
docker info --format '{{.LoggingDriver}}'
docker inspect 容器名 --format '{{json .HostConfig.LogConfig}}'
十三、生产环境推荐启动模板
下面是一条相对安全的 Docker 启动命令模板,可根据业务实际调整:
docker run -d \
--name app \
--user 10001:10001 \
--read-only \
--tmpfs /tmp:rw,noexec,nosuid,size=64m \
--cap-drop=ALL \
--cap-add=NET_BIND_SERVICE \
--security-opt no-new-privileges:true \
--pids-limit=256 \
--memory=512m \
--memory-swap=512m \
--cpus="1.0" \
--log-driver=json-file \
--log-opt max-size=100m \
--log-opt max-file=3 \
-p 127.0.0.1:8080:8080 \
-v /data/app:/app/data:rw \
--restart unless-stopped \
registry.example.com/app/web:1.0.0
这条命令体现了几个核心原则:
- 使用非 root 用户运行;
- 根文件系统只读;
- 临时目录使用受限 tmpfs;
- 删除默认 Linux capabilities;
- 禁止获取新权限;
- 限制进程数、内存和 CPU;
- 控制日志大小;
- 仅绑定本地端口;
- 明确挂载目录;
- 使用固定版本镜像。
十四、Docker 安全加固最佳实践总结
Docker 安全不是某一个参数、某一个扫描工具或某一条命令能够完全解决的,而是一套贯穿应用生命周期的体系化工程。建议按照以下优先级落地:
- 先保护 Docker Daemon:禁止公网暴露 Docker API,严格控制 docker 用户组。
- 再治理镜像来源:只使用可信镜像,固定版本,扫描漏洞,避免敏感信息入镜像。
- 强化容器运行时:禁用 privileged,使用非 root 用户,限制 capabilities,开启只读文件系统。
- 收敛网络暴露面:只开放必要端口,使用自定义网络隔离服务,避免管理端口暴露公网。
- 完善日志和审计:限制日志大小,审计 Docker Socket、关键配置和异常容器行为。
- 接入 CI/CD 安全门禁:在构建、扫描、签名、发布各环节加入自动化控制。
- 定期复查基线:使用 Docker Bench、Trivy 等工具持续发现风险。
结语
Docker 的便利性不应以牺牲安全为代价。对于个人开发环境,默认配置也许足够;但在生产环境中,Docker 需要像操作系统、数据库、中间件一样进行严格加固。真正可靠的 Docker 安全方案,应同时覆盖宿主机、守护进程、镜像供应链、容器运行参数、网络边界、日志审计和持续交付流程。
如果只能记住一句话,那就是:容器安全的核心是最小权限原则。不需要的权限不要给,不必要的端口不要开,不可信的镜像不要用,不清楚用途的挂载不要加。只要持续坚持这些原则,并配合自动化扫描和审计,Docker 环境的整体安全性就能得到显著提升。