Skip to content

[BUG] Using npm run-script to call npm install or npm ci in ephemeral environment fails with EACCES #4451

@Hossy

Description

@Hossy

Is there an existing issue for this?

  • I have searched the existing issues

This issue exists in the latest npm version

  • I am using the latest npm

Current Behavior

In our build environment, we use docker to perform specific build steps without needing to manage an endless amount of tools on our build agents. The build agents and the steps they execute do not run as root. However, when executing docker run, the user within the ephemeral container is typically root (normal for Docker). We use npm run-script to execute a series of npm commands as part of the build.

The build agent executes docker run --rm -v $PWD:/app -v ~/.npmrc:/root/.npmrc -w /app node:16 sh -c 'npm run-script build'

$PWD contains the checked-out contents from source. We also pass a pre-configured .npmrc shared across all our builds. The contents of $PWD and .npmrc are owned by a non-root uid/gid.

build.sh:

#!/bin/sh

npm install \
  && npm run lint \
  && npm run autotest \
  && rm -rf node_modules \
  && npm install --production

When npm run-script is first executed within the node:16 container, it is running as root (normal for Docker). Once the entrypoint script determines it isn't a node command, npm run-script checks for the existence of the cache folder, which it obviously doesn't find and so creates it (owned by the current user, root) at /root/.npm.

npm then executes build.sh whose first task is to execute npm install (we also tried npm ci). The first thing npm install does is change the user context to the owner of the working directory (/app), which is the non-root user the build agent is using.
npm then checks to see if it can access /root/.npmrc and /root/.npm, which it cannot because the user context has changed.
This user does not exist in node:16's passwd file obviously, so the uid/gid numbers are shown in the error.

npm ERR! code EACCES
npm ERR! syscall lstat
npm ERR! path /root/.npm/_cacache/content-v2/sha1/f2/bb/2a7e83cbc87bb95c8e572828a06c9add6e0d
npm ERR! errno -13
npm ERR!
npm ERR! Your cache folder contains root-owned files, due to a bug in
npm ERR! previous versions of npm which has since been addressed.
npm ERR!
npm ERR! To permanently fix this problem, please run:
npm ERR!   sudo chown -R 900:900 "/root/.npm"

This behavior is not observed in versions earlier than node:16.
node:16 comes with npm 8.3.1, but this behavior is still observed when upgrading npm to 8.5.1.

Expected Behavior

Either:

  1. npm run-script shouldn't be messing with .npmrc or the cache directory since it doesn't need it
    or
  2. npm run-script should be following the same user context switching code path that npm install and npm ci are following.

Steps To Reproduce

  1. On a machine with Docker installed and running...
  2. Create a folder containing the following two files. Ensure the folder and files are owned by a non-root user.
    package.json:
{
  "name": "test-app",
  "scripts": {
    "build": "./build.sh"
  },
  "dependencies": {
    "config": "^3.1.0"
  }
}

build.sh (+x):

#!/bin/sh

id
ls -la
ls -la $HOME
npm install \
&& echo SUCCESS!
  1. Open a shell as a non-root user and change directories to the folder you just created. Not sure if it would interfere, but try not to use uid/gid 1000 which would map to the node user within node:16.
  2. Run docker run --rm -v $PWD:/app -w /app node:16 sh -c 'npm -v; node -v; npm config ls; echo -----------; npm run-script build'

Output:

bash-5.1$ docker run --rm -v $PWD:/app -w /app node:16 sh -c 'npm -v; node -v; npm config ls; echo -----------; npm run-script build'
8.3.1
v16.14.0
; node bin location = /usr/local/bin/node
; cwd = /app
; HOME = /root
; Run `npm config ls -l` to show all defaults.
-----------

> build
> ./build.sh

uid=900 gid=900 groups=900
total 8
drwxr-xr-x 2  900  900  42 Feb 21 20:01 .
drwxr-xr-x 1 root root  29 Feb 21 20:28 ..
-rwxr-xr-x 1  900  900  66 Feb 21 20:26 build.sh
-rw-r--r-- 1  900  900 119 Feb 21 18:48 package.json
ls: cannot open directory '/root': Permission denied
glob error [Error: EACCES: permission denied, scandir '/root/.npm/_logs'] {
  errno: -13,
  code: 'EACCES',
  syscall: 'scandir',
  path: '/root/.npm/_logs'
}
npm WARN logfile Error: EACCES: permission denied, scandir '/root/.npm/_logs'
npm WARN logfile  error cleaning log files [Error: EACCES: permission denied, scandir '/root/.npm/_logs'] {
npm WARN logfile   errno: -13,
npm WARN logfile   code: 'EACCES',
npm WARN logfile   syscall: 'scandir',
npm WARN logfile   path: '/root/.npm/_logs'
npm WARN logfile }
npm ERR! code EACCES
npm ERR! syscall mkdir
npm ERR! path /root/.npm/_cacache/tmp
npm ERR! errno EACCES
npm ERR! 
npm ERR! Your cache folder contains root-owned files, due to a bug in
npm ERR! previous versions of npm which has since been addressed.
npm ERR! 
npm ERR! To permanently fix this problem, please run:
npm ERR!   sudo chown -R 900:900 "/root/.npm"

npm ERR! A complete log of this run can be found in:
npm notice 
npm notice New minor version of npm available! 8.3.1 -> 8.5.1
npm notice Changelog: <https://github.com/npm/cli/releases/tag/v8.5.1>
npm notice Run `npm install -g npm@8.5.1` to update!
npm notice 

Output with npm upgrade to 8.5.1:

bash-5.1$ docker run --rm -v $PWD:/app -w /app node:16 sh -c 'npm install -g npm; npm -v; node -v; npm config ls; echo -----------; npm run-script build'

removed 1 package, changed 44 packages, and audited 218 packages in 13s

11 packages are looking for funding
  run `npm fund` for details

3 moderate severity vulnerabilities

To address all issues, run:
  npm audit fix

Run `npm audit` for details.
npm notice 
npm notice New minor version of npm available! 8.3.1 -> 8.5.1
npm notice Changelog: <https://github.com/npm/cli/releases/tag/v8.5.1>
npm notice Run `npm install -g npm@8.5.1` to update!
npm notice 
8.5.1
v16.14.0
; node bin location = /usr/local/bin/node
; cwd = /app
; HOME = /root
; Run `npm config ls -l` to show all defaults.
-----------

> build
> ./build.sh

uid=900 gid=900 groups=900
total 8
drwxr-xr-x 2  900  900  42 Feb 21 20:01 .
drwxr-xr-x 1 root root  40 Feb 21 20:29 ..
-rwxr-xr-x 1  900  900  66 Feb 21 20:26 build.sh
-rw-r--r-- 1  900  900 119 Feb 21 18:48 package.json
ls: cannot open directory '/root': Permission denied
glob error [Error: EACCES: permission denied, scandir '/root/.npm/_logs'] {
  errno: -13,
  code: 'EACCES',
  syscall: 'scandir',
  path: '/root/.npm/_logs'
}
npm WARN logfile Error: EACCES: permission denied, scandir '/root/.npm/_logs'
npm WARN logfile  error cleaning log files [Error: EACCES: permission denied, scandir '/root/.npm/_logs'] {
npm WARN logfile   errno: -13,
npm WARN logfile   code: 'EACCES',
npm WARN logfile   syscall: 'scandir',
npm WARN logfile   path: '/root/.npm/_logs'
npm WARN logfile }
npm ERR! code EACCES
npm ERR! syscall mkdir
npm ERR! path /root/.npm/_cacache/tmp
npm ERR! errno EACCES
npm ERR! 
npm ERR! Your cache folder contains root-owned files, due to a bug in
npm ERR! previous versions of npm which has since been addressed.
npm ERR! 
npm ERR! To permanently fix this problem, please run:
npm ERR!   sudo chown -R 900:900 "/root/.npm"

npm ERR! A complete log of this run can be found in:

Environment

  • npm: 8.3.1 (part of node:16), but we tried 8.5.1 as well
  • Node.js: 16.14.0
  • OS Name: Alpine
  • System Model Name:
  • npm config:
; node bin location = /usr/local/bin/node
; cwd = /app
; HOME = /root
; Run `npm config ls -l` to show all defaults.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Bugthing that needs fixingPriority 2secondary priority issueRelease 8.xwork is associated with a specific npm 8 releasews:arboristRelated to the arborist workspace

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions