Skip to content

Docker Compose 生产安全加固:缩小服务暴露面

Compose 能稳定启动服务,不等于服务已经适合公网或生产环境。真正的安全边界来自可信镜像、最小端口、有效认证、受控权限、可审计日志和经过演练的恢复方案。

本文面向单机长期运行的 Compose 服务。高可用、跨主机调度和强租户隔离通常需要额外平台能力,不能通过增加几行 Compose 配置自动获得。

上线前判断

先确认服务是否具备生产所需的认证、权限模型、稳定版本、升级说明和安全维护记录。官方明确标记为演示、开发或 All-in-One 体验的部署,不应因为配置了 TLS 就改称生产方案。

固定镜像来源

镜像使用维护方明确发布的 registry 和版本 tag,避免长期跟随 latest。关键服务可以在验证后固定 digest,但仍要记录可读版本,便于判断发布说明和安全公告。

bash
docker compose config --images
docker image inspect example/app:1.2.3 --format '{{json .RepoDigests}}'

不要运行来源不明的镜像,也不要把“镜像能拉取”当作供应链可信证明。更新时重新核对维护者、tag、架构和配置兼容性。

缩小端口和网络暴露

只供同机代理访问的 HTTP 服务绑定回环地址:

yaml
services:
  app:
    image: example/app:1.2.3
    ports:
      - "127.0.0.1:8080:8080"

数据库、缓存和内部 Worker 通常只加入 Compose 网络,不设置 ports。局域网服务应绑定明确网卡并通过防火墙限制来源;公网入口只开放代理需要的 80/443。将地址改成 0.0.0.0 不会自动提供认证、TLS 或访问控制。Docker Engine 早于 28.0.0 时,同一二层网络中的其他主机可能访问绑定在 localhost 的已发布端口;应升级 Engine 并从外部网络实际验证隔离效果。参见 Docker 端口发布说明

管理凭据和密钥

示例密码不能进入正式环境。敏感值不要写在 Compose、镜像、命令行或版本库中;优先使用 Compose secrets、受限权限的环境文件或外部密钥系统。应用使用加密密钥保护数据库字段时,密钥必须与数据一起纳入恢复计划,但应分开存储。

bash
chmod 600 .env
docker compose config --quiet

docker compose config 可能展开环境变量,排障输出不能直接贴到公开 Issue 或聊天记录。

收紧容器权限

项目支持时使用非 root 用户、只读根文件系统、临时 tmpfs 和最少 capabilities。不要为了绕过权限问题直接使用 privileged: true、挂载整个宿主机根目录或执行 chmod 777。这些选项需要项目级兼容验证,不能机械加入所有 Compose。

挂载 /var/run/docker.sock 等价于向容器交出高权限 Docker 控制面。需要容器发现功能时,优先使用受限代理,并把管理界面限制在可信网络。

控制资源与日志

为服务设置与实际 Compose 实现兼容的 CPU、内存和进程限制,避免单个容器拖垮主机。日志使用轮转策略,防止磁盘被无限增长的 stdout/stderr 占满:

yaml
services:
  app:
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "5"

同时监控磁盘、容器重启次数、健康状态、证书期限和备份结果。只有日志没有告警,故障仍可能长期无人发现。

HTTPS 与源站隔离

公网服务使用反向代理终止 TLS,并让应用端口只接受代理访问。完整的 Caddy、Nginx、转发头和长连接配置见 Docker 服务反向代理与 HTTPS。应用只有在代理会清洗并重写 X-Forwarded-* 时才能信任这些请求头。

备份与更新

安全事件和错误更新都需要恢复能力。按 Docker 备份与恢复数据 建立异机副本,按 Docker Compose 更新、回滚与卸载服务 固定版本和回滚条件。备份文件本身包含用户数据和凭据时必须加密并限制访问。

安全验收

上线前检查实际监听地址、外部端口、匿名访问、管理入口、默认账号、密钥权限、镜像版本、容器权限、日志轮转和恢复记录。以下 app 换成当前项目的服务名:

bash
docker compose ps
docker compose config --quiet
docker inspect "$(docker compose ps -q app)" --format '{{json .HostConfig.PortBindings}}'
docker inspect "$(docker compose ps -q app)" --format '{{json .HostConfig.Privileged}}'

如果 docker compose ps -q app 为空,先检查服务名和启动状态,不要对空容器 ID 执行 inspect。再从非可信网络验证内部端口不能访问,从正常入口验证 TLS、登录、退出和权限拒绝行为。配置文件看起来正确不能替代网络侧验收。

安全边界

Rootless Docker 可以降低 daemon 与宿主机 root 权限绑定的风险,但不会修复应用漏洞、弱密码或错误的公网暴露。单机 Compose 也不自动提供多节点容灾、Web 应用防火墙、集中身份管理和合规审计;需要这些能力时应引入相应平台并重新评估架构。

常见问题

只绑定 127.0.0.1 后外部无法访问

这是预期结果。通过同机反向代理、VPN 或 SSH 隧道提供受控入口,不要直接改成全网卡监听来绕过设计。

容器改成非 root 后无法写入卷

核对镜像声明的 UID/GID 和宿主机目录属主,在测试环境修正明确目录权限。不要放宽整个数据树或宿主机目录。

日志轮转后仍然磁盘不足

继续检查应用自身日志、数据库、镜像、构建缓存和卷增长。Docker 日志限制只控制所选 logging driver 的文件。

加固完成后

把端口、账号、密钥、镜像、权限、日志、备份和更新负责人记录成可重复检查的上线清单。每次版本或网络入口变化后重新执行,而不是只在首次部署时检查一次。

总结

生产安全的重点不是堆叠配置,而是缩小真实暴露面并保留验证证据。可信镜像、回环源站、有效认证、最小权限、受控日志和可恢复数据共同构成单机 Compose 的安全基线。

参考资料

全部公开文章由同一个站点构建和发布。