Files
docker_practice/07_dockerfile/7.11_user.md

5.2 KiB
Raw Blame History

7.11 USER 指定当前用户

本节涵盖了相关内容与详细描述,主要探讨以下几个方面:

基本语法

如下代码块所示,展示了相关示例:

USER <用户名>[:<用户组>]
USER <UID>[:<GID>]

USER 指令切换后续指令 (RUN、CMD、ENTRYPOINT) 的执行用户。


为什么要使用 USER

笔者强调:以非 root 用户运行容器是最重要的安全实践之一。

flowchart LR
    subgraph Root ["root 用户运行的风险:"]
        direction TB
        R_C["容器内 root"] -- 可能逃逸 --> R_H["宿主机 root"]
        R_C -- 漏洞利用 --> R_Control["完全控制宿主机"]
    end
    
    subgraph NonRoot ["非 root 用户运行:"]
        direction TB
        NR_C["容器内普通用户"] -- 逃逸后 --> NR_H["宿主机普通用户"]
        NR_C -- 权限受限,危害降低 --> NR_Safe["无法控制系统"]
    end

基本用法

本节涵盖了相关内容与详细描述,主要探讨以下几个方面:

创建并切换用户

如下代码块所示,展示了相关示例:

FROM node:20-alpine

## 1. 创建用户和组

RUN addgroup -g 1001 appgroup && \
    adduser -u 1001 -G appgroup -D appuser

## 2. 设置目录权限

WORKDIR /app
COPY --chown=appuser:appgroup . .

## 3. 切换用户

USER appuser

## 4. 后续命令以 appuser 身份运行

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

使用 UID/GID

如下代码块所示,展示了相关示例:

## 也可以使用数字

USER 1001:1001

用户必须已存在

USER 指令只能切换到已存在的用户:

## ❌ 错误:用户不存在

USER nonexistent
## Error: unable to find user nonexistent

## ✅ 正确:先创建用户

RUN useradd -r -s /bin/false appuser
USER appuser

概述

总体概述了以下内容。

创建用户的方式

Debian/Ubuntu

RUN groupadd -r appgroup && \
    useradd -r -g appgroup appuser

Alpine

RUN addgroup -g 1001 -S appgroup && \
    adduser -u 1001 -S -G appgroup appuser
选项 说明
-r (useradd) / -S (adduser) 创建系统用户
-g 指定主组
-G 指定附加组
-u 指定 UID
-s /bin/false 禁用登录 shell

运行时切换用户

本节涵盖了相关内容与详细描述,主要探讨以下几个方面:

使用 gosu (推荐)

在 ENTRYPOINT 脚本中切换用户时,不要使用 susudo,应使用 gosu

FROM debian:bookworm

## 创建用户

RUN groupadd -r redis && useradd -r -g redis redis

## 安装 gosu

RUN apt-get update && apt-get install -y gosu && rm -rf /var/lib/apt/lists/*

COPY docker-entrypoint.sh /usr/local/bin/
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["redis-server"]

docker-entrypoint.sh

#!/bin/bash
set -e

## 以 root 执行初始化

chown -R redis:redis /data

## 用 gosu 切换到 redis 用户运行服务

exec gosu redis "$@"

为什么不用 su/sudo

问题 su/sudo gosu
TTY 要求 需要 不需要
信号传递 不正确 正确
子进程 exec 替换
容器中使用

运行时覆盖用户

使用 -u--user 参数:

## 以指定用户运行

$ docker run -u 1001:1001 myimage

## 以 root 运行(调试时)

$ docker run -u root myimage

文件权限处理

切换用户后,确保应用有权访问文件:

FROM node:20-alpine

## 创建用户

RUN adduser -D -u 1001 appuser

WORKDIR /app

## 方式1使用 --chown

COPY --chown=appuser:appuser . .

## 方式2手动 chown减少层数

## COPY . .

## RUN chown -R appuser:appuser /app

USER appuser
CMD ["node", "server.js"]

最佳实践

本节涵盖了相关内容与详细描述,主要探讨以下几个方面:

1。始终使用非 root 用户

如下代码块所示,展示了相关示例:

## ✅ 推荐

RUN adduser -D appuser
USER appuser
CMD ["myapp"]

## ❌ 避免

CMD ["myapp"]  # 以 root 运行

2。使用固定 UID/GID

便于在宿主机和容器间共享文件:

## 使用常见的非 root UID

RUN addgroup -g 1000 -S appgroup && \
    adduser -u 1000 -S -G appgroup appuser
USER 1000:1000

3。多阶段构建中的 USER

如下代码块所示,展示了相关示例:

## 构建阶段可以用 root

FROM node:20 AS builder
WORKDIR /app
COPY . .
RUN npm install && npm run build

## 生产阶段用非 root

FROM node:20-alpine
RUN adduser -D appuser
WORKDIR /app
COPY --from=builder --chown=appuser:appuser /app/dist .
USER appuser
CMD ["node", "server.js"]

常见问题

本节涵盖了相关内容与详细描述,主要探讨以下几个方面:

Q权限被拒绝

运行以下命令:

permission denied: '/app/data.log'

解决:确保目录权限正确

RUN mkdir -p /app/data && chown appuser:appuser /app/data

Q无法绑定低于 1024 的端口

非 root 用户无法绑定 80、443 等端口。

解决

  1. 使用高端口 (如 8080)
  2. 在运行时映射端口:docker run -p 80:8080