docker-node icon indicating copy to clipboard operation
docker-node copied to clipboard

npm-scripts can't be executed by root on npm 7, 8

Open hata6502 opened this issue 4 years ago • 5 comments

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.

hata6502 avatar Nov 05 '21 12:11 hata6502

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.

hata6502 avatar Nov 05 '21 14:11 hata6502

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?

hata6502 avatar Nov 05 '21 14:11 hata6502

https://github.com/nodejs/docker-node/blob/main/docs/BestPractices.md#non-root-user might be related

nschonni avatar Nov 05 '21 15:11 nschonni

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.

terencehonles avatar Mar 04 '22 21:03 terencehonles

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

= 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 .npmrc here 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)

razielanarki avatar Mar 09 '22 13:03 razielanarki

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

greghall76 avatar Oct 07 '22 15:10 greghall76

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

hata6502 avatar Jul 13 '23 07:07 hata6502