bigywater.tech
Docker · 88

USER 指令:非 root 运行与监听 1024 以下端口

给镜像加一行 USER 就算「非 root」了吗?COPY 进来的文件归谁?uid 在 /etc/passwd 里没有会怎样?USER 之后的构建步骤会遇到什么?这篇逐项实测。

CapEff = 0
非 root 用户的有效 capability 集合为空(c40 里 root 有 14 个)
COPY 属主 root
USER app 之后不带 --chown 的 COPY,文件属主仍是 0:0
unknown uid 12345
--user 12345 在 passwd 里没有记录时 whoami 的报错

运行时

实测 · 本机执行输出
  docker inspect 的 Config.User = app
  默认 CMD 的输出:uid=10001(app) gid=10001(app) groups=10001(app),10001(app) ; HOME=/home/app ; app
  CapEff / CapBnd:CapEff: 0000000000000000 CapBnd: 00000000a80425fb
  写 /tmp、写 /、写 /etc/x、写 $HOME:/tmp/x 成功;  /x 失败;  /etc/x 失败;  /home/app/x 成功

USER app 后,进程的 uid/gid 是 10001,有效 capability 为空,只能写 /tmp 和 HOME 这类 app 自己的目录。边界集合 CapBnd 仍然是 14 个,但没有有效权限就用不上(c40)。

文件属主

实测 · 本机执行输出
  0:0 /srv ; 10001:10001 /srv/owned.txt ; 0:0 /srv/plain.txt ; 0:0 /srv/root.txt ; 10001:10001 /srv/work
  USER app 之后 WORKDIR /srv/work 创建出来的目录属主:10001:10001
  以 app 身份往 /srv/work 里写文件:成功
  以 app 身份修改 COPY 进来的 /srv/plain.txt(root 属主)和 /srv/owned.txt(app 属主):sh: can't create /srv/plain.txt: Permission denied ; owned可写

COPY 默认把文件归 root,即使前面有 USER。要让应用能改自己的文件,要写 COPY --chown=用户:组;WORKDIR 在 USER 之后创建的目录则属于该用户。

--user

实测 · 本机执行输出
  --user root:uid=0(root) gid=0(root) groups=0(root),0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),11(floppy),20(dialout),26(tape),27(video) ; root
  --user 12345(passwd 里没有):uid=12345 gid=0(root) groups=0(root) ; HOME=/ ; whoami: unknown uid 12345
  --user 12345:0(加上 root 组):uid=12345 gid=0(root) groups=0(root)
  --user app:10001;--user 10001:app

--user 可以用名字或数字;uid 在 /etc/passwd 里没有时,id 仍然能用,但没有用户名、HOME 是 /,gid 默认 0(root 组)——这个组默认 0 值得注意。

构建中的 USER

实测 · 本机执行输出
  USER 10002 之后的 RUN:构建退出码 0;构建输出里的相关行:["/bin/sh: can't create /who: Permission denied", '#7 [4/4] RUN mkdir /out 2>&1 || echo "mkdir /out 失败"', "mkdir: can't create directory '/out': Permission denied", 'mkdir /out 失败']
  这个镜像里 /who、/out 存在吗?ls: /who: No such file or directory ; ls: /out: No such file or directory
  RUN 里的 id -u(构建时的用户):构建时 USER 10002 生效,所以写 / 失败

USER 从它出现的那一行开始对后面的 RUN 生效。需要 root 的步骤(装包、建目录)要放在 USER 之前。

分步演示

非 root 用户
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / Docker Desktop(Engine 29.8.2,容器跑在 Docker Desktop 自带的 Linux 虚拟机里,内核与宿主机的 macOS 不同);镜像为 linux/arm64;需要看虚拟机内部时用特权容器 + nsenter;计时用 time / date +%s%N,数字每次运行会有小幅抖动。用户用 busybox 的 addgroup / adduser 建立(不需要联网);属主取自 ls -ldn 和 stat;capability 取自 /proc/self/status;构建期的 USER 效果通过让 RUN 失败来观察;实验后已删除镜像
    • 没有验证:Debian/Ubuntu 的 useradd 写法、gosu / su-exec 在入口脚本里降权、USER 与卷的权限(见 c23)、rootless 模式、Kubernetes 的 runAsNonRoot、HOME 目录是否存在时的行为差异
    京ICP备2026067502号-1京公网安备11010502063487号