Skip to content

Docker 安全实践:构建安全、可靠、合规的容器化应用

在生产环境中,Docker 镜像的安全性直接关系到系统的整体安全边界。一个臃肿、权限过高、包含已知漏洞的镜像,可能成为攻击者入侵系统的跳板。因此,必须遵循“最小化、最小权限、纵深防御”的安全原则。

以下是现代 Node.js、TypeScript、Nest、Bun 项目应遵循的核心安全实践。

一、最小化镜像:移除非必要工具

问题
许多基础镜像(如 node:18)包含了大量开发工具(curl, vim, bash, apt 等)。这些工具对运行时无用,却增加了攻击面。

风险

  • 攻击者可利用 curl 发起反向连接
  • 使用 vim 修改配置文件或植入后门
  • 通过包管理器安装恶意软件

解决方案

1. 使用 Alpine 镜像

dockerfile
FROM node:18-alpine

Alpine 是基于 musl libc 的轻量级 Linux 发行版,镜像体积小,预装工具少。

2. 多阶段构建 + 精简运行时

dockerfile
# 构建阶段
FROM node:18 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# 运行阶段:使用更小的基础镜像
FROM node:18-alpine
WORKDIR /app
COPY --from=builder --chown=node:node /app/dist ./dist
COPY --from=builder --chown=node:node /app/package*.json ./

USER node
CMD ["node", "dist/main.js"]

3. 显式删除不必要的包(Alpine)

dockerfile
RUN apk del .build-deps && \
    rm -rf /var/cache/apk/*

二、扫描漏洞:主动发现已知 CVE

即使使用官方镜像,也可能包含有漏洞的依赖库(如 openssl, glibc)。必须在 CI/CD 中集成漏洞扫描。

1. 使用 docker scan(集成 Snyk)

bash
docker scan myapp:latest
  • 免费扫描基础镜像和依赖中的 CVE
  • 提供修复建议
  • 可集成到 GitHub Actions

2. 使用 Trivy(推荐)

Trivy 是一款开源、快速、全面的漏洞扫描器,支持 Docker、Kubernetes、文件系统等。

bash
# 扫描镜像
trivy image myapp:latest

# 扫描文件系统(如 node_modules)
trivy fs .

# CI/CD 中自动扫描
trivy image --exit-code 1 --severity CRITICAL myapp:latest

GitHub Actions 示例

yaml
- name: Scan with Trivy
  uses: aquasecurity/trivy-action@master
  with:
    image-ref: 'myapp:latest'
    format: 'table'
    exit-code: '1'
    severity: 'CRITICAL,HIGH'

三、禁止 root 运行:最小权限原则

默认情况下,容器以 root 用户运行,拥有最高权限,极易导致容器逃逸。

正确做法:使用非 root 用户

dockerfile
# 创建专用用户(可选)
# RUN adduser -D -s /bin/sh appuser

# 切换到内置 node 用户(UID 1000)
USER node

# 或指定 UID/GID
# USER 1001:1001

优势

  • 无法修改系统文件
  • 无法挂载设备
  • 即使被攻破,影响范围受限

配合文件权限

dockerfile
COPY --chown=node:node . .

四、只读文件系统:防止恶意写入

如果应用不需要写入磁盘(如日志由 stdout 收集),应将根文件系统设为只读。

启动时启用只读模式

bash
docker run \
  --read-only \
  --tmpfs /tmp \
  --tmpfs /app/logs \
  myapp:latest
  • --read-only:根文件系统不可写
  • --tmpfs:挂载内存临时文件系统,用于 /tmp、日志目录等

适用场景

  • API 服务(无本地持久化)
  • 静态文件服务
  • Serverless 风格应用

注意:若需写日志,应重定向到 stdout,由日志收集器(如 Fluentd、Logstash)处理。

五、极致精简:distrolessscratch 镜像

对于最高安全要求的场景,应使用无操作系统的镜像。

1. Google Distroless

仅包含应用及其运行时依赖,无 shell、无包管理器、无任何多余工具。

dockerfile
# 多阶段构建后,复制到 distroless
FROM gcr.io/distroless/nodejs:18
COPY --from=builder /app /app
WORKDIR /app
# 无需 USER,distroless 默认非 root
CMD ["."]

优点

  • 攻击面极小
  • 镜像体积极小(~50MB)
  • 符合零信任安全模型

2. scratch 镜像(适用于编译型语言)

scratch 是空镜像,常用于 Go、Rust 等静态编译语言。

dockerfile
FROM scratch
COPY myapp /
CMD ["/myapp"]

Node.js 应用通常不适用 scratch,因为需要 node 运行时。

六、综合安全检查清单

项目是否符合
使用 node:alpinedistroless
移除 curl, vim, bash 等工具
使用 USER node 或自定义非 root 用户
CI/CD 中集成 trivy 漏洞扫描
避免使用 latest 标签,使用 sha 或语义版本
生产环境使用 --read-only 文件系统
敏感配置通过 secrets 注入,而非环境变量
日志输出到 stdout/stderr,不写本地文件

七、结语

容器安全不是“事后补救”,而是从设计之初就应内建的原则。通过:

  • 最小化镜像 减少攻击面
  • 漏洞扫描 主动发现风险
  • 非 root 用户 限制权限
  • 只读文件系统 防止篡改
  • distroless/scratch 实现极致安全

你可以构建出既高效又安全的生产级容器应用。这不仅是技术选择,更是对用户数据和系统稳定性的责任。