Docker容器修改用户组无需登出登录时id命令不显示附加组问题
问题背景
使用如下Dockerfile构建镜像:
FROM ubuntu RUN apt update && apt -y upgrade RUN apt install -y sudo # Setup ops user use defaults uid 1000 gid 1000 RUN useradd -d /home/myuser -aG sudo \ && usermod -aG sudo root \ && echo "%sudo ALL=(ALL:ALL) NOPASSWD:ALL" > /etc/sudoers
构建完成后通过以下命令启动容器并进入交互终端:
docker run -dit -u 1000:1000 myimage "/bin/bash" docker exec -it 23u898908 "/bin/bash"
进入容器后执行命令得到如下输出:
myuser@23u898908$ id uid=1000(myuser) gid=1000(myuser) groups=1000(myuser) myuser@23u898908$ id myuser uid=1000(myuser) gid=1000(myuser) groups=1000(myuser),27(sudo) myuser@23u898908$ sudo ls file.txt
可以观察到:sudo权限实际生效可以正常执行,但无参数执行id时不显示附加的sudo组,只有指定用户名执行id myuser时才会显示sudo组。如果执行命令切换用户:
myuser@23u898908$sudo su -l myuser $id uid=1000(myuser) gid=1000(myuser) groups=1000(myuser),27(sudo)
切换用户后执行id即可正常显示所有所属组。预期效果是无需登出、重登或切换用户,直接执行id就能显示用户所有所属组,需要确认该现象是系统设计还是bug。
结论
这是Linux系统的标准机制,不是bug。
原理说明
进程的附属组列表是进程创建时就确定的属性,已经运行的进程不会自动同步/etc/group中的组配置变更。
使用-u 1000:1000参数启动容器时,Docker仅会将初始bash进程的UID设置为1000、主GID设置为1000,不会走完整的用户登录初始化流程,也不会主动查询/etc/group中该UID对应的所有附属组,因此sudo组的GID不会被加入到初始bash进程的附属组列表中。
不同命令输出差异的原因:
- 无参数执行
id:读取的是当前进程自身持有的附属组属性,因此仅显示主组myuser - 执行
id myuser:直接查询系统账号数据库(/etc/passwd、/etc/group)中myuser的静态配置,和当前进程属性无关,因此可以查到配置中的sudo组 sudo命令可以正常执行:sudo做权限校验时直接查询系统账号数据库的用户组归属,不依赖当前进程的附属组属性,只要/etc/group和sudoers配置正确即可正常提权- 执行
sudo su -l myuser后可以看到全量组:su -l会触发完整登录流程,创建新shell进程时会读取系统账号配置,将用户所有附属组加载到新进程的属性中,因此执行id可以看到sudo组。
实现预期效果的方案
- 方案1:启动容器时不直接用
-u参数指定用户,以root身份启动后通过登录式shell切换到目标用户,启动命令示例:
该方式会走完整的用户登录流程,自动加载所有附属组,进入容器后直接执行docker run -dit myimage "/bin/login -f myuser"id即可看到全量组信息。 - 方案2:如果必须使用
-u参数指定用户启动,可以在进入容器后执行exec sg myuser -c bash,会重新加载用户的所有组配置到当前会话,执行后再运行id即可显示全量组。
内容的提问来源于stack exchange,提问作者Timy Shark
相关产品推荐
相关产品推荐

