npm-scripts can't be executed by root on npm 7, 8
Environment
- Platform: Ubuntu 20.04
- Docker Version: 20.10.10
- Node.js Version: 16.13.0
- Image Tag:
node:16
Expected Behavior
When execute npm run by root user, the npm-script is also executed by root.
It is the behavior of Node 14.
Current Behavior
When execute npm run by root user, the npm-script is executed by node user.
Steps to Reproduce
Clone the example repository.
$ git clone https://github.com/hata6502/node-docker-test.git
$ cd node-docker-test
Then, run the following docker run command.
# Node 14
$ docker run --rm -it -v $(pwd):/usr/src/app node:14 bash -c "cd /usr/src/app && npm i && npm test"
# Node 16
$ docker run --rm -it -v $(pwd):/usr/src/app node:16 bash -c "cd /usr/src/app && npm i && npm test"
Result
$ sudo docker run --rm -it -v $(pwd):/usr/src/app node:14 bash -c "cd /usr/src/app && npm i && npm test"
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN [email protected] No description
npm WARN [email protected] No repository field.
npm WARN [email protected] No license field.
up to date in 1.119s
found 0 vulnerabilities
> [email protected] test /usr/src/app
> sleep 10 && touch /root/.npm/test.txt
$ sudo docker run --rm -it -v $(pwd):/usr/src/app node:16 bash -c "cd /usr/src/app && npm i && npm test"
npm WARN ancient lockfile
npm WARN ancient lockfile The package-lock.json file was created with an old version of npm,
npm WARN ancient lockfile so supplemental metadata must be fetched from the registry.
npm WARN ancient lockfile
npm WARN ancient lockfile This is a one-time fix-up, please be patient...
npm WARN ancient lockfile
up to date, audited 1 package in 170ms
found 0 vulnerabilities
> [email protected] test
> sleep 10 && touch /root/.npm/test.txt
touch: cannot touch '/root/.npm/test.txt': Permission denied
The npm-script is executed successfully on Node 14, but a permission error is occured on Node 16.
Show the detail of process by ps -au.
# Node 14
$ docker run --rm -it -v $(pwd):/usr/src/app node:14 bash -c "cd /usr/src/app && npm i; npm test & sleep 5 && ps -au"
# Node 16
$ docker run --rm -it -v $(pwd):/usr/src/app node:16 bash -c "cd /usr/src/app && npm i; npm test & sleep 5 && ps -au"
Result
$ sudo docker run --rm -it -v $(pwd):/usr/src/app node:14 bash -c "cd /usr/src/app && npm i; npm test & sleep 5 && ps -au"
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN [email protected] No description
npm WARN [email protected] No repository field.
npm WARN [email protected] No license field.
up to date in 1.066s
found 0 vulnerabilities
> [email protected] test /usr/src/app
> sleep 10 && touch /root/.npm/test.txt
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.3 0.0 17948 2868 pts/0 Ss+ 12:11 0:00 bash -c cd /usr/src/app && npm i; npm test & sleep 5 && ps -au
root 19 2.8 0.2 698104 46664 pts/0 Sl+ 12:11 0:00 npm
root 31 0.0 0.0 4288 772 pts/0 S+ 12:11 0:00 sh -c sleep 10 && touch /root/.npm/test.txt
root 32 0.0 0.0 4196 652 pts/0 S+ 12:11 0:00 sleep 10
root 33 0.0 0.0 36640 2820 pts/0 R+ 12:11 0:00 ps -au
$ sudo docker run --rm -it -v $(pwd):/usr/src/app node:16 bash -c "cd /usr/src/app && npm i; npm test & sleep 5 && ps -au"
npm WARN ancient lockfile
npm WARN ancient lockfile The package-lock.json file was created with an old version of npm,
npm WARN ancient lockfile so supplemental metadata must be fetched from the registry.
npm WARN ancient lockfile
npm WARN ancient lockfile This is a one-time fix-up, please be patient...
npm WARN ancient lockfile
up to date, audited 1 package in 164ms
found 0 vulnerabilities
> [email protected] test
> sleep 10 && touch /root/.npm/test.txt
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.4 0.0 3740 3016 pts/0 Ss+ 12:11 0:00 bash -c cd /usr/src/app && npm i; npm test & sleep 5 && ps -au
root 19 7.2 0.4 703008 68172 pts/0 Sl+ 12:11 0:00 npm test
node 31 0.0 0.0 2392 756 pts/0 S+ 12:11 0:00 sh -c sleep 10 && touch /root/.npm/test.txt
node 32 0.0 0.0 2300 684 pts/0 S+ 12:11 0:00 sleep 10
root 33 0.0 0.0 7644 2712 pts/0 R+ 12:11 0:00 ps -au
The npm-script is executed by root user on Node 14,
but it is executed by node user on Node 16.
Additional Information
This problem may be not reproduced on Docker Desktop for Mac.
This problem seems to be due to npm v7 and v8.
The behavior of npm run with root, is changed between npm v6 and v7.
- https://docs.npmjs.com/cli/v6/using-npm/scripts#user
- https://docs.npmjs.com/cli/v7/using-npm/scripts#user
When git clone https://github.com/hata6502/node-docker-test.git is executed by Ubuntu desktop user,
the UID is almost 1000.
node-docker-test directory is owned by node user on Docker because node's UID is also 1000.
So, npm-scripts is executed by the working-directory owner: node.
This behavior may be difficult for Ubuntu Desktop user.
I resolved it by sudo chown -R root:root node-docker-test/, but is it the correctly solution?
https://github.com/nodejs/docker-node/blob/main/docs/BestPractices.md#non-root-user might be related
I'm not sure why I just ran into this because I'm pretty sure I had been using node 16, but I think the documentation should probably suggest that starting docker with -u node:node or -u 1000:1000 is appropriate because node will change its user to node even if ran as root in a root container.
i'm having the same problem, running docker run node npm i with a "preinstall": "npx npm-force-resolutions" etc in scripts fails either with:
(clickme) output of docker run node npm i (clickme)
> [email protected] preinstall > npx npm-force-resolutions 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 1000:1000 "/root/.npm" 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 1000:1000 "/root/.npm"
= output of docker run node whoami
root
= output of docker run node npx -c whoami
node
(clickme) the output of docker run npm config ls -l shows that the default no .npmrc is never overridden for th enode user (clickme)
; "default" config from default values _auth = (protected) access = null all = false allow-same-version = false also = null audit = true audit-level = null auth-type = "legacy" before = null bin-links = true browser = null ca = null cache = "/root/.npm" cache-max = null cache-min = 0 cafile = null call = "" cert = null ci-name = null cidr = null color = true commit-hooks = true depth = null description = true dev = false diff = [] diff-dst-prefix = "b/" diff-ignore-all-space = false diff-name-only = false diff-no-prefix = false diff-src-prefix = "a/" diff-text = false diff-unified = 3 dry-run = false editor = "vi" engine-strict = false fetch-retries = 2 fetch-retry-factor = 10 fetch-retry-maxtimeout = 60000 fetch-retry-mintimeout = 10000 fetch-timeout = 300000 force = false foreground-scripts = false format-package-lock = true fund = true git = "git" git-tag-version = true global = false global-style = false globalconfig = "/usr/local/etc/npmrc" heading = "npm" https-proxy = null if-present = false ignore-scripts = false include = [] include-staged = false include-workspace-root = false init-author-email = "" init-author-name = "" init-author-url = "" init-license = "ISC" init-module = "/root/.npm-init.js" init-version = "1.0.0" init.author.email = "" init.author.name = "" init.author.url = "" init.license = "ISC" init.module = "/root/.npm-init.js" init.version = "1.0.0" json = false key = null legacy-bundling = false legacy-peer-deps = false link = false local-address = null location = "user" lockfile-version = null loglevel = "notice" logs-max = 10 ; long = false ; overridden by cli maxsockets = 15 message = "%s" metrics-registry = "https://registry.npmjs.org/" node-options = null node-version = "v17.6.0" noproxy = [""] npm-version = "8.5.1" offline = false omit = [] only = null optional = null otp = null pack-destination = "." package = [] package-lock = true package-lock-only = false parseable = false prefer-offline = false prefer-online = false prefix = "/usr/local" preid = "" production = null progress = true proxy = null read-only = false rebuild-bundle = true registry = "https://registry.npmjs.org/" save = true save-bundle = false save-dev = false save-exact = false save-optional = false save-peer = false save-prefix = "^" save-prod = false scope = "" script-shell = null searchexclude = "" searchlimit = 20 searchopts = "" searchstaleness = 900 shell = "sh" shrinkwrap = true sign-git-commit = false sign-git-tag = false sso-poll-frequency = 500 sso-type = "oauth" strict-peer-deps = false strict-ssl = true tag = "latest" tag-version-prefix = "v" timing = false tmp = "/tmp" umask = 0 unicode = false update-notifier = true usage = false user-agent = "npm/{npm-version} node/{node-version} {platform} {arch} workspaces/{workspaces} {ci}" userconfig = "/root/.npmrc" version = false versions = false viewer = "man" which = null workspace = [] workspaces = null yes = null ; "cli" config from command line options long = true
(clickme) the output form the next commands confirm that (clickme)
the node user, indeed, runs in the in the root users env
node user, indeed, runs in the in the root users env= output of docker run node sh -c 'find ~'
/root /root/.bashrc /root/.profile /root/.gnupg /root/.gnupg/random_seed /root/.gnupg/pubring.kbx /root/.gnupg/private-keys-v1.d /root/.gnupg/crls.d /root/.gnupg/crls.d/DIR.txt /root/.gnupg/pubring.kbx~ /root/.gnupg/trustdb.gpg /root/.npm /root/.npm/_logs /root/.npm/_logs/2022-03-02T09_59_07_157Z-debug-0.log(note: no
.npmrchere either)
= output of docker run node npx -c 'find ~'
/root find: '/root': Permission denied
= output of docker run node npx -c 'find /home/node'
/home/node /home/node/.bashrc /home/node/.profile /home/node/.bash_logout
= output of docker run node npx -c 'env | sort'
COLOR=1 EDITOR=vi HOME=/root HOSTNAME=63da0c0a688b INIT_CWD=/opt/project NODE=/usr/local/bin/node NODE_VERSION=17.6.0 PATH=/opt/project/node_modules/.bin:/opt/node_modules/.bin:/node_modules/.bin:/usr/local/lib/node_modules/npm/node_modules/@npmcli/run-script/lib/node-gyp-bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin PWD=/opt/project TERM=xterm YARN_VERSION=1.22.17 npm_command=exec npm_config_cache=/root/.npm npm_config_call=env | sort npm_config_global_prefix=/usr/local npm_config_globalconfig=/usr/local/etc/npmrc npm_config_init_module=/root/.npm-init.js npm_config_local_prefix=/opt/project npm_config_metrics_registry=https://registry.npmjs.org/ npm_config_node_gyp=/usr/local/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js npm_config_noproxy= npm_config_prefix=/usr/local npm_config_user_agent=npm/8.5.1 node/v17.6.0 linux x64 workspaces/false npm_config_userconfig=/root/.npmrc npm_execpath=/usr/local/lib/node_modules/npm/bin/npx-cli.js npm_lifecycle_event=npx npm_lifecycle_script=env | sort npm_node_execpath=/usr/local/bin/node npm_package_json=/opt/project/package.json npm_package_name=node-project npm_package_version=1.0.0
(logs from on :latest, tested also on :slim,:bullseye,:buster,:alpine,:lts,:10,:14 ... etc)
I am having this same problem as rood and a "node" user and regardless of whether npm is therefore trying to write to /tmp/npm_cache or /home/node/.npm/_cacache/tmp
This issue is resolved by installing npm v9 🎉 https://github.blog/changelog/2022-10-24-npm-v9-0-0-released/
We believe the changes made lay the ground-work for future improvements to the default npm experience long-term. Notably, Docker users should find this release to to be beneficial as we simplifie file permissions