向容器添加非root用户
许多Docker镜像默认使用root用户,但在某些情况下,您可能更倾向于使用非root用户。如果您这样做,有一些关于本地文件系统(绑定)挂载的特殊注意事项您应该了解。具体来说:
-
Docker Desktop for Mac: 在容器内部,任何挂载的文件/文件夹将表现得好像它们是由您指定的容器用户拥有的一样。在本地,所有文件系统操作将使用您本地用户的权限。
-
Docker Desktop for Windows: 在容器内部,任何挂载的文件/文件夹将显示为
root
所有,但你指定的用户仍然能够读取/写入它们,并且所有文件都将可执行。在本地,所有文件系统操作将使用本地用户的权限。这是因为从根本上无法直接将Windows风格的文件权限映射到Linux。 -
Linux 上的 Docker CE/EE:在容器内部,任何挂载的文件/文件夹将具有与容器外部完全相同的权限 - 包括所有者用户 ID (UID) 和组 ID (GID)。因此,您的容器用户需要具有相同的 UID 或属于具有相同 GID 的组。用户/组的实际名称并不重要。机器上的第一个用户通常获得 UID 1000,因此大多数容器使用此作为用户的 ID,以尽量避免此问题。
为 VS Code 指定用户
如果您使用的镜像或Dockerfile已经提供了一个可选的非root用户(如node
镜像),但仍然默认使用root用户,您可以通过在devcontainer.json
中指定remoteUser
属性来选择让Visual Studio Code(服务器)和任何子进程(终端、任务、调试)使用该用户:
"remoteUser": "user-name-goes-here"
在Linux上,如果你在devcontainer.json
中引用了Dockerfile、镜像或Docker Compose,这也会自动更新容器用户的UID/GID以匹配你的本地用户,以避免在此环境中存在的绑定挂载权限问题(除非你设置了"updateRemoteUserUID": false
)。
由于此设置仅影响VS Code及其相关子进程,因此需要重新启动VS Code(或重新加载窗口)才能使其生效。但是,UID/GID更新仅在创建容器时应用,并且需要重新构建才能更改。
指定默认容器用户
在某些情况下,您可能需要容器中的所有进程以不同的用户身份运行(例如,由于启动要求),而不仅仅是VS Code。具体操作方式略有不同,取决于您是否使用Docker Compose。
-
Dockerfile 和镜像: 将
containerUser
属性添加到同一个文件中。"containerUser": "user-name-goes-here"
在Linux上,与
remoteUser
类似,这也会自动更新容器用户的UID/GID以匹配您的本地用户,以避免在此环境中存在的绑定挂载权限问题(除非您设置"updateRemoteUserUID": false
)。 -
Docker Compose: 更新(或扩展)你的
docker-compose.yml
,为适当的服务添加以下内容:user: user-name-or-UID-goes-here
创建一个非root用户
虽然来自 Dev Containers 扩展的任何镜像或 Dockerfile 都会包含一个 UID/GID 为 1000 的非 root 用户(通常称为 vscode
或 node
),但许多基础镜像和 Dockerfile 并不包含。幸运的是,您可以更新或创建一个 Dockerfile,将非 root 用户添加到您的容器中。
建议在生产环境中也以非root用户身份运行您的应用程序(因为这样更安全),所以即使您正在重用现有的Dockerfile,这也是一个好主意。例如,这个针对Debian/Ubuntu容器的代码片段将创建一个名为user-name-goes-here
的用户,赋予其使用sudo
的权限,并将其设置为默认用户:
ARG USERNAME=user-name-goes-here
ARG USER_UID=1000
ARG USER_GID=$USER_UID
# Create the user
RUN groupadd --gid $USER_GID $USERNAME \
&& useradd --uid $USER_UID --gid $USER_GID -m $USERNAME \
#
# [Optional] Add sudo support. Omit if you don't need to install software after connecting.
&& apt-get update \
&& apt-get install -y sudo \
&& echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME \
&& chmod 0440 /etc/sudoers.d/$USERNAME
# ********************************************************
# * Anything else you want to do like clean up goes here *
# ********************************************************
# [Optional] Set the default user. Omit if you want to keep the default as root.
USER $USERNAME
提示: 如果在构建过程中遇到关于GID或UID已存在的错误,您选择的镜像可能已经有一个非root用户,您可以直接利用。
无论哪种情况,如果您已经构建了容器并连接到它,请从命令面板(F1)运行Dev Containers: Rebuild Container以应用更改。否则,运行Dev Containers: Open Folder in Container...以连接到容器。
更改现有容器用户的UID/GID
虽然在使用Dockerfile或镜像时,remoteUser
属性会尝试在Linux上自动更新UID/GID,但你可以在Dockerfile中使用此代码片段手动更改用户的UID/GID。根据需要更新ARG
值。
ARG USERNAME=user-name-goes-here
ARG USER_UID=1000
ARG USER_GID=$USER_UID
RUN groupmod --gid $USER_GID $USERNAME \
&& usermod --uid $USER_UID --gid $USER_GID $USERNAME \
&& chown -R $USER_UID:$USER_GID /home/$USERNAME
请注意,在Alpine Linux上,您需要先安装shadow
包。
RUN apk add --no-cache shadow