上一篇 下一篇 分享链接 返回 返回顶部

Docker 安全加固实战:从宿主机到镜像、运行时的完整防护清单

发布人:慈云数据-客服中心 发布时间:11小时前 阅读量:2

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 安全不是某一个参数、某一个扫描工具或某一条命令能够完全解决的,而是一套贯穿应用生命周期的体系化工程。建议按照以下优先级落地:

  1. 先保护 Docker Daemon:禁止公网暴露 Docker API,严格控制 docker 用户组。
  2. 再治理镜像来源:只使用可信镜像,固定版本,扫描漏洞,避免敏感信息入镜像。
  3. 强化容器运行时:禁用 privileged,使用非 root 用户,限制 capabilities,开启只读文件系统。
  4. 收敛网络暴露面:只开放必要端口,使用自定义网络隔离服务,避免管理端口暴露公网。
  5. 完善日志和审计:限制日志大小,审计 Docker Socket、关键配置和异常容器行为。
  6. 接入 CI/CD 安全门禁:在构建、扫描、签名、发布各环节加入自动化控制。
  7. 定期复查基线:使用 Docker Bench、Trivy 等工具持续发现风险。

结语

Docker 的便利性不应以牺牲安全为代价。对于个人开发环境,默认配置也许足够;但在生产环境中,Docker 需要像操作系统、数据库、中间件一样进行严格加固。真正可靠的 Docker 安全方案,应同时覆盖宿主机、守护进程、镜像供应链、容器运行参数、网络边界、日志审计和持续交付流程。

如果只能记住一句话,那就是:容器安全的核心是最小权限原则。不需要的权限不要给,不必要的端口不要开,不可信的镜像不要用,不清楚用途的挂载不要加。只要持续坚持这些原则,并配合自动化扫描和审计,Docker 环境的整体安全性就能得到显著提升。

目录结构
全文