非 root 运行是什么
容器默认以 root 身份启动,容器里的 root 与宿主的 root 是同一个用户,只是被命名空间与权限机制圈定在容器视角内。一旦应用被攻破或者挂载了宿主目录,这份 root 权限的破坏力会被放大。把进程降级到非 root 用户运行,是容器安全成本最低、收益最直接的一项加固:镜像里用 USER 声明,或运行时用 --user 覆盖。
基本信息
| 镜像声明 | Dockerfile 里写 USER 10001:10001 或 USER app |
|---|---|
| 运行时覆盖 | docker run --user 1000:1000 镜像(覆盖镜像声明) |
| 检查方式 | docker image inspect -f '{{.Config.User}}' 镜像 |
| 常见内置用户 | node 镜像的 node、nginx 镜像的 nginx、python-slim 需自建 |
| 配套调整 | 卷与绑定目录的属主要与容器内 UID 对齐 |
| 适用平台 | Linux 容器;Windows 容器用 ContainerUser 概念不同 |
典型使用场景
- 对外暴露的 Web 服务:缩小被攻破后的影响面
- 挂载了宿主目录的服务:避免在宿主上写出 root 属主文件
- 多租户或共享集群:满足平台的安全基线要求
- 配合只读根文件系统:让运行时无法写入镜像层
- 审计整改项:扫描报告指出镜像以 root 运行时的直接修复动作
- 共享数据卷的多个容器:统一 UID 避免互相写不进去
镜像内建用户与运行时降权
镜像内建用户与运行时降权
# 构建期创建固定 UID 的专用用户,并把工作目录授权给它
RUN groupadd -g 10001 app && useradd -u 10001 -g 10001 -m -s /sbin/nologin app
WORKDIR /srv/app
COPY --chown=10001:10001 ./app /srv/app
USER 10001:10001
# 复用基础镜像自带账号(node 镜像已提供 node 用户)
USER node运行与验证
运行与验证
# 运行时覆盖镜像里的用户,快速验证降权后应用是否正常
docker run -d --user 1000:1000 -v appdata:/data --name api app/api:1.4.2
# 确认容器内进程真实身份(应为非 0 的 uid)
docker exec api id
docker inspect -f '{{.Config.User}}' app/api:1.4.2
# 宿主侧检查:挂载目录里的新文件属主是否符合预期
ls -ln ./data降权后的常见表现
| 现象 | 原因 | 处理方向 |
|---|---|---|
| 启动即报权限拒绝 | 目标目录属主仍是 root | 构建期 chown 或运行前调整宿主目录 |
| 写卷失败 | 卷内既有内容属主不一致 | 新建卷或统一 UID 后重建卷 |
| 监听 80 端口失败 | 非 root 不能绑定低位端口 | 改监听高位端口并由外部映射 |
| 无法读配置文件 | 文件权限 600 且属主为 root | 改成 644 或授权给该用户 |
| 日志目录不可写 | 目录未预先授权 | 构建期创建并授权日志目录 |
端口是常见卡点:非 root 进程绑定 1024 以下端口需要额外能力授权。更稳妥的做法是应用监听 8080 之类的端口,由 docker run -p 80:8080 做映射。
只改启动用户而不调整目录权限,问题往往在运行一段时间后才暴露(首次写日志、首次落盘)。上线前应覆盖写入路径做一次完整回归。