首页 > 解决方案 > 如何避免更改 docker 中非 root 用户对 node_modules 的权限

问题描述

我当前文件的问题是,在我的entrypoint.sh文件中,我必须将整个项目目录的所有权更改为非管理用户 ( chown -R node /node-servers)。但是,当npm安装了很多包时,这会花费很多时间。有没有办法避免chown进入node_modules目录?

背景:我将所有内容都创建为 root 的原因Dockerfile是因为这样我可以匹配开发人员的本地用户的UIDGID。这使得安装卷更容易。缺点是我必须在一个entrypoint.sh文件中从root下级,并确保整个项目文件的权限都已更改为非管理员用户。

我的码头文件:

FROM node:10.24-alpine

#image already has user node and group node which are 1000, thats what we will use

# grab gosu for easy step-down from root
# https://github.com/tianon/gosu/releases
ENV GOSU_VERSION 1.14

RUN set -eux; \
    \
    apk add --no-cache --virtual .gosu-deps \
        ca-certificates \
        dpkg \
        gnupg \
    ; \
    \
    dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')"; \
    wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch"; \
    wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch.asc"; \
    \
# verify the signature
    export GNUPGHOME="$(mktemp -d)"; \
    gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4; \
    gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu; \
    command -v gpgconf && gpgconf --kill all || :; \
    rm -rf "$GNUPGHOME" /usr/local/bin/gosu.asc; \
    \
# clean up fetch dependencies
    apk del --no-network .gosu-deps; \
    \
    chmod +x /usr/local/bin/gosu; \
# verify that the binary works
    gosu --version; \
    gosu nobody true


COPY ./ /node-servers

# Setting the working directory
WORKDIR /node-servers

# Install app dependencies
# Install openssl
RUN apk add --update openssl ca-certificates && \
    apk --no-cache add shadow && \
    apk add libcap && \
    npm install -g && \
    chmod +x /node-servers/entrypoint.sh && \
    setcap cap_net_bind_service=+ep /usr/local/bin/node

# Entrypoint used to load the environment and start the node server
#ENTRYPOINT ["/bin/sh"]

我的 entrypoint.sh

# In Prod, this may be configured with a GID already matching the container
# allowing the container to be run directly as Jenkins. In Dev, or on unknown
# environments, run the container as root to automatically correct docker
# group in container to match the docker.sock GID mounted from the host
set -x
if [ -z ${HOST_UID+x} ]; then
    echo "HOST_UID not set, so we are not changing it"
else
    echo "HOST_UID is set, so we are changing the container UID to match"
    # get group of notadmin inside container
    usermod -u ${HOST_UID} node
    CUR_GID=`getent group node | cut -f3 -d: || true`
    echo ${CUR_GID}
    # if they don't match, adjust
    if [ ! -z "$HOST_GID" -a "$HOST_GID" != "$CUR_GID" ]; then
        groupmod -g ${HOST_GID} -o node
    fi
    if ! groups node | grep -q node; then
        usermod -aG node node
    fi
fi

# gosu drops from root to node user
set -- gosu node "$@"

[ -d "/node-servers" ] && chown -v -R node /node-servers

exec "$@"

标签: node.jsbashdocker

解决方案


你根本不需要chown在这里跑步。保留 root(或主机用户)拥有的文件。只要它们是世界可读的,应用程序仍然可以运行;但如果存在某种安全问题或其他错误,应用程序将无法意外覆盖其自己的源代码。

然后,您可以继续进一步简化它。在大多数情况下,Unix 中的用户是通过他们的数字用户 ID 来识别的;实际上并没有要求将用户列在/etc/passwd. 如果您不需要更改node用户 ID 并且不需要chown文件,则入口点脚本简化为“切换用户 ID 并运行主脚本”;但是 Docker 可以通过该docker run -u选项为您提供备用用户 ID。这意味着您也不需要安装gosu,这是很多 Dockerfile 内容。

所有这一切意味着您可以将 Dockerfile 缩减为:

FROM node:10.24-alpine

# Install OS-level dependencies (before you COPY anything in)
apk add openssl ca-certificates

# (Do not install gosu or its various dependencies)

# Set (and create) the working directory
WORKDIR /node-servers

# Copy language-level dependencies in
COPY package.json package-lock.json .
RUN npm ci

# Copy the rest of the application in
# (make sure `node_modules` is in .dockerignore)
COPY . .

# (Do not call setcap here)

# Set the main command to run
USER node
CMD npm run start

然后在运行容器时,可以使用 Docker 选项来指定当前用户和附加功能。

docker run                          \
  -d                                \ # in the background
  -u $(id -u)                       \ # as an alternate user
  -v "$PWD/data:/node-servers/data" \ # mounting a data directory
  -p 8080:80                        \ # publishing a port
  my-image

Docker默认授予该NET_BIND_SERVICE功能,因此您无需专门设置它。

如果您使用绑定挂载来覆盖应用程序代码,同样的权限设置也将起作用;再次,没有chown电话。

docker run ...                  \
  -u $(id -u)                   \
  -v "$PWD:/node-servers"       \ # run the application from the host, not the image
  -v /node-servers/node_modules \ # with libraries that will not be updated ever
  ...

推荐阅读