Skip to content

安全实践:最小化镜像、漏洞扫描、禁止 root 运行

在生产环境中使用 Docker 容器时,安全性是一个至关重要的考虑因素。Docker 容器的安全性不仅关系到容器本身,还可能影响整个宿主机系统和其他容器。本篇文章将深入探讨 Docker 容器的安全实践,包括如何最小化镜像、扫描漏洞以及禁止以 root 用户身份运行容器。

一、镜像最小化原则

镜像最小化是容器安全的基础原则之一。一个最小化的镜像只包含运行应用程序所必需的组件,减少了潜在的攻击面。

1. 选择合适的基镜像

dockerfile
# 不推荐:使用完整的 Ubuntu 镜像
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y nodejs npm

# 推荐:使用 Alpine Linux 镜像
FROM node:18-alpine

2. 使用 distroless 镜像

Distroless 镜像只包含应用程序及其运行时依赖,不包含包管理器、shell 或其他不必要的工具:

dockerfile
# 使用 Google 的 distroless 镜像
FROM gcr.io/distroless/nodejs:18

COPY . /app
WORKDIR /app

# distroless 镜像没有 shell,直接指定入口点
ENTRYPOINT ["node", "server.js"]

3. 多阶段构建实现最小化

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

# 运行阶段 - 使用最小的基础镜像
FROM node:18-alpine
WORKDIR /app

# 从构建阶段复制必要的文件
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./package.json

# 移除不必要的工具
RUN apk del curl wget

# 创建非 root 用户
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001

USER nextjs

EXPOSE 3000
CMD ["node", "dist/server.js"]

二、漏洞扫描

即使使用了最小化镜像,仍然可能存在安全漏洞。定期扫描镜像是发现和修复安全问题的重要手段。

1. 使用 Docker Scout

Docker Scout 是 Docker 官方提供的镜像分析工具:

bash
# 扫描本地镜像
docker scout cves myapp:latest

# 比较两个镜像的安全性
docker scout compare myapp:latest myapp:dev

2. 使用 Trivy

Trivy 是一个开源的漏洞扫描工具:

bash
# 安装 Trivy
brew install aquasecurity/trivy/trivy

# 扫描镜像
trivy image myapp:latest

# 扫描并输出 JSON 格式报告
trivy image --format json --output report.json myapp:latest

3. 在 CI/CD 中集成漏洞扫描

yaml
# GitHub Actions 示例
name: Security Scan
on: [push]
jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Build Docker Image
        run: docker build -t myapp:${{ github.sha }} .
      
      - name: Scan for vulnerabilities
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: myapp:${{ github.sha }}
          format: 'sarif'
          output: 'trivy-results.sarif'
      
      - name: Upload Trivy scan results to GitHub Security tab
        uses: github/codeql-action/upload-sarif@v2
        if: always()
        with:
          sarif_file: 'trivy-results.sarif'

三、禁止 root 运行

以 root 用户身份运行容器是一个严重的安全隐患。攻击者可能利用容器逃逸技术获得宿主机的 root 权限。

1. 创建非 root 用户

dockerfile
FROM node:18-alpine

# 创建用户组和用户
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001

WORKDIR /app

# 复制文件并设置正确的权限
COPY --chown=nextjs:nodejs . .

# 切换到非 root 用户
USER nextjs

EXPOSE 3000
CMD ["node", "server.js"]

2. 使用 USER 指令

dockerfile
FROM node:18-alpine

WORKDIR /app

# 安装依赖
COPY package*.json ./
RUN npm ci --only=production

# 复制应用代码
COPY . .

# 创建应用用户
RUN adduser -D -s /bin/sh appuser

# 更改文件所有权
RUN chown -R appuser:appuser /app

# 切换到非 root 用户
USER appuser

EXPOSE 3000
CMD ["npm", "start"]

3. 在运行时强制非 root

bash
# 使用 --user 参数运行容器
docker run --user 1000:1000 myapp:latest

# 在 docker-compose.yml 中指定用户
version: '3.8'
services:
  web:
    image: myapp:latest
    user: "1000:1000"

四、其他安全最佳实践

1. 只读文件系统

dockerfile
# 在运行时设置只读文件系统
docker run --read-only myapp:latest

# 在 docker-compose.yml 中设置
version: '3.8'
services:
  web:
    image: myapp:latest
    read_only: true
    tmpfs:
      - /tmp
      - /var/run

2. 限制容器能力

bash
# 移除不必要的 Linux 能力
docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE myapp:latest

# 在 docker-compose.yml 中设置
version: '3.8'
services:
  web:
    image: myapp:latest
    cap_drop:
      - ALL
    cap_add:
      - NET_BIND_SERVICE

3. 使用安全扫描工具

dockerfile
# 在构建过程中集成安全检查
FROM node:18-alpine

# 安装安全扫描工具
RUN apk add --no-cache npm
RUN npm install -g nsp

WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production

# 运行安全检查
RUN nsp check

COPY . .
USER node
EXPOSE 3000
CMD ["npm", "start"]

五、安全配置示例

以下是一个综合了多种安全实践的 Dockerfile 示例:

dockerfile
# 使用最小化基础镜像
FROM node:18-alpine AS base

# 安装必要的依赖
RUN apk add --no-cache tini

# 创建应用用户
RUN addgroup -g 1001 -S nodejs && \
    adduser -S nextjs -u 1001

# 创建应用目录
WORKDIR /app

# 使用 tini 作为 init 系统
ENTRYPOINT ["/sbin/tini", "--"]

# 构建阶段
FROM base AS builder
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force

# 生产阶段
FROM base AS production

# 从构建阶段复制依赖
COPY --from=builder /app/node_modules ./node_modules
COPY . .

# 设置正确的权限
RUN chown -R nextjs:nodejs /app
WORKDIR /app

# 切换到非 root 用户
USER nextjs

# 暴露端口
EXPOSE 3000

# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD curl -f http://localhost:3000/health || exit 1

# 启动应用
CMD ["node", "server.js"]

一句话总结

Docker 容器安全需要从镜像最小化、漏洞扫描和禁止 root 运行等多个维度进行综合考虑,通过采用多阶段构建、使用安全扫描工具、创建非 root 用户等方式,可以显著提升容器的安全性。

在实际应用中,应该将这些安全实践集成到 CI/CD 流程中,确保每次构建和部署都符合安全标准。同时,定期更新基础镜像和依赖包,及时修复已知的安全漏洞,是维护容器安全的重要环节。