{T}

Docker 安全性与命令实践指南

概述

本文讲解 Docker 容器安全配置(USER 权限、最小权限原则)、Docker 命令体系(镜像管理、容器生命周期、调试命令),以及生产级安全 Dockerfile 配置方案。

前置知识

学习目标

  1. 理解容器以 root 运行的安全风险
  2. 掌握 USER 指令和 --chown 的安全配置方法
  3. 熟练使用 Docker 镜像/容器/Compose 命令体系
  4. 能够编写符合生产安全标准的 Dockerfile

一、为什么需要设置 USER

默认行为的安全风险

Docker 容器默认以 root 用户运行所有进程,存在以下风险:

风险说明
文件系统访问通过 Volume 可访问宿主机文件
系统文件破坏root 权限可修改系统关键文件
容器逃逸权限过大增加逃逸攻击面
审计不合规违反最小权限原则

Node.js 镜像内置用户

node:18-alpine 镜像预创建了 node 用户:

bash
# 查看镜像用户
docker run --rm node:18-alpine cat /etc/passwd | grep node
# node:x:1000:1000:Linux User,,,:/home/node:/bin/bash
  • 用户名:node,UID/GID:1000
  • 主目录:/home/node

二、安全 Dockerfile 配置

完整安全配置示例

dockerfile
# ==================== 构建阶段 ====================
ARG NODE_VERSION=18
FROM node:${NODE_VERSION}-alpine AS builder

# 切换到非 root 用户
USER node
WORKDIR /home/node/app

# 复制文件并设置所有者
COPY --chown=node:node package*.json ./
RUN npm install
COPY --chown=node:node . .
RUN npm run build

# ==================== 生产阶段 ====================
FROM node:${NODE_VERSION}-alpine AS production

USER node
WORKDIR /home/node/app

COPY --from=builder --chown=node:node /home/node/app/.output ./.output

EXPOSE 3000
ENV NODE_ENV=production
CMD ["node", ".output/server/index.mjs"]

关键配置说明

配置作用
USER node后续 RUN/CMD 以 node 用户执行
WORKDIR /home/node/app工作目录在 node 用户主目录下,确保写权限
COPY --chown=node:node文件所有者设为 node,避免权限不足
ARG NODE_VERSION构建时变量,便于版本统一管理

安全 vs 不安全对比

dockerfile
# 不安全:默认 root 运行
FROM node:18-alpine
WORKDIR /app
COPY . .
RUN npm install
CMD ["node", "app.js"]

# 安全:指定普通用户
FROM node:18-alpine
USER node
WORKDIR /home/node/app
COPY --chown=node:node . .
RUN npm install
CMD ["node", "app.js"]

三、Linux 权限基础

bash
# 权限数字:r=4, w=2, x=1
chmod 755 /app        # rwxr-xr-x
chmod 644 config.js   # rw-r--r--

# 修改所有者
chown node:node /app
chown -R node:node /home/node/app
权限值含义
7 (rwx)读+写+执行
6 (rw-)读+写
5 (r-x)读+执行
4 (r--)只读

四、Docker 命令体系

镜像管理

bash
docker build -t app:1.0 .           # 构建镜像
docker images                        # 列出镜像
docker rmi app:1.0                   # 删除镜像
docker image prune                   # 清理无用镜像
docker tag app:1.0 registry/app:1.0 # 打标签
docker push registry/app:1.0        # 推送到仓库
docker pull nginx:alpine            # 拉取镜像

容器生命周期

bash
docker run -d --name app -p 3000:3000 app:1.0  # 创建并启动
docker start app                    # 启动已停止容器
docker stop app                     # 优雅停止(SIGTERM)
docker restart app                  # 重启
docker rm app                       # 删除容器
docker rm -f app                    # 强制删除运行中容器

容器调试

bash
docker ps                           # 运行中容器
docker ps -a                        # 所有容器
docker logs -f app                  # 实时日志
docker logs --tail 100 app          # 最近100行
docker exec -it app sh              # 进入容器
docker inspect app                  # 详细信息
docker stats app                    # 资源占用
docker cp app:/app/log.txt ./       # 复制文件出来

Docker Compose 批量管理

bash
docker compose up -d                # 后台启动所有服务
docker compose down                 # 停止并删除
docker compose down -v              # 同时删除数据卷
docker compose ps                   # 查看状态
docker compose logs -f app          # 查看指定服务日志
docker compose exec app sh          # 进入指定服务
docker compose pull                 # 拉取最新镜像
docker compose up -d --build        # 重新构建并启动
docker compose config               # 验证配置文件

五、生产级安全配置实战

完整 docker-compose.yml

yaml
version: '3.8'
services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
      args:
        NODE_VERSION: 18
    container_name: my-app-prod
    restart: always
    ports:
      - "3000:3000"
    env_file:
      - .env.production
    volumes:
      - app-logs:/home/node/app/logs    # 日志持久化
    networks:
      - app-network
    healthcheck:
      test: ["CMD", "wget", "--spider", "-q", "http://localhost:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 10s
    # 安全加固
    read_only: true                     # 只读文件系统
    tmpfs:
      - /tmp                            # 临时文件用 tmpfs
    security_opt:
      - no-new-privileges:true          # 禁止提权

volumes:
  app-logs:

networks:
  app-network:
    driver: bridge

安全配置清单

配置项作用
USER node非 root 运行
read_only: true容器文件系统只读
no-new-privileges禁止进程提权
tmpfs: /tmp临时写入用内存文件系统
healthcheck自动检测应用健康状态
env_file敏感配置不入代码库

常见问题

问题原因解决方案
Permission denied文件所有者非当前 USER使用 --chown=node:node
无法写入日志read_only 文件系统挂载 Volume 或使用 tmpfs
npm install 失败node 用户无 /root/.npm 缓存权限设置 ENV npm_config_cache=/tmp/.npm
端口绑定失败非 root 无法绑定 <1024 端口使用 >1024 端口(如 3000)

最佳实践

  • 所有生产镜像必须指定非 root USER
  • WORKDIR 设在用户主目录下(/home/node/app
  • 使用 COPY --chown 而非先 COPY 再 RUN chown(减少层数)
  • 敏感信息通过 env_file 或 Docker Secret 注入
  • 定期清理无用镜像:docker image prune -f
  • 使用 docker compose config 验证配置后再部署

延伸阅读


上一篇:Docker 镜像构建优化实战 下一篇:NestJS 容器化部署实战