npm / npm

This repository is moving to: https://github.com/npm/cli

Home Page:http://npm.community

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

npm gets permissions of node_modules all wrong

Cyberuben opened this issue · comments

I'm opening this issue because:

  • npm is crashing.
  • npm is producing an incorrect install.
  • npm is doing something I don't understand.
  • Other (see below for feature requests):

What's going wrong?

When installing modules in a freshly cloned git repo using npm install on npm version 5.0.0, I get the weird behavior of the node_modules folder having the wrong owner. Using ls -la I can see the following info is returned:

drwxr-xr-x 372  502 dialout 12288 May 28 11:16 node_modules/

When starting the application, I get several dependency errors. When installing the dependency that is reported to be missing, another entry appears.

When doing the same using npm at version 4.6.1, I get the following output:

drwxr-xr-x 373 root root 12288 May 28 11:19 node_modules/

Which is expected behavior, and no packages are missing

Note: The missing dependencies seem to be devDependencies from a few of the packages I depend on. There is something mentioned in the Breaking changes section of the NPM update changelog, but not about these packages not being installed when running npm install

How can the CLI team reproduce the problem?

npm install -g npm@5.0.0
cd /path/to/your/repo
npm install
ls -la

There is no npm-debug.log file, as there are no errors reported.

I'm positive I'm running npm as root:root.

root@node1:~# whoami
root
root@node1:~# groups
root

supporting information:

  • npm -v prints: 5.0.0
  • node -v prints: v7.10.0
  • npm config get registry prints: https://registry.npmjs.org/
  • Windows, OS X/macOS, or Linux?: Ubuntu 16.04 LTS
  • Network issues:
    • Geographic location where npm was run: OVH, France
    • I use a proxy to connect to the npm registry.
    • I use a proxy to connect to the web.
    • I use a proxy when downloading Git repos.
    • I access the npm registry via a VPN
    • I don't use a proxy, but have limited or unreliable internet access.
  • Container:
    • I develop using Vagrant on Windows.
    • I develop using Vagrant on OS X or Linux.
    • I develop / deploy using Docker.
    • I deploy to a PaaS (Triton, Heroku).

I just browsed through the npm code for keywords like mkdir, chown child_process to see if the problem would be obvious.

I didn't find anything in the npm code itself, but i found that Node.js uses the following syscall internally to execute mkdir: http://man7.org/linux/man-pages/man2/mkdir.2.html
This syscall creates a folder with the given name and mode, under the same user:group as the process that called it.

The newly created directory will be owned by the effective user ID of the process.

It looks like npm uses the module mkdirp to create folders, which in turn uses the built-in Node.js module fs, which in turn calls the above syscall.

If somehow npm runs under the user:group 502:dialout while creating the folder, this would be the cause of the problem. However, that must be a problem with npm itself and not with @Cyberuben's setup, because he is running npm as root:root.

@alexrsagen The place to look is in pacote, combined with https://github.com/npm/npm/blob/latest/lib/config/pacote.js#L45-L47

It's possible I brainfarted about how our permissions-dropping works. I should check with @iarna about what the intentions were, but I thought we were supposed to drop perms to SUDO_UID and SUDO_GID for both node_modules (the issue being reported here), and ~/.npm (which we definitely want to do).

If it's not supposed to drop perms like this, the fix is just to path pacote to not use uid and gid for extractions (but still use them for caching package data).

The fact that some packages are missing might well be a separate bug -- it might be useful to see what it is you tried to install that's missing. (I've seen some cases like #16744 where it's literally a completely invalid semver, and I have no idea how npm4 ever installed it).

In the meantime: you should be able to blow away SUDO_UID and SUDO_GID and have this work as you're describing. That should work as a stopgap until we get this issue resolved. I just think there's a good chance it was never intended for npm4 to work that way. heh.

The list of dependencies I'm trying to install is as following:

"dependencies": {
    "argon2": "^0.15.0",
    "body-parser": "^1.17.2",
    "config": "^1.26.1",
    "connect-flash": "^0.1.1",
    "connect-redis": "^3.3.0",
    "cookie-parser": "^1.4.3",
    "es6-request": "^2.1.7",
    "express": "^4.15.3",
    "express-session": "^1.15.3",
    "fs-extra": "^3.0.1",
    "gm": "^1.23.0",
    "googleapis": "^19.0.0",
    "kue": "^0.11.5",
    "markdown": "^0.5.0",
    "moment": "^2.18.1",
    "moment-duration-format": "^1.3.0",
    "morgan": "^1.8.2",
    "multer": "^1.3.0",
    "nodemailer": "^4.0.1",
    "numeral": "^2.0.6",
    "nunjucks": "^3.0.1",
    "passport": "^0.3.2",
    "passport-local": "^1.0.0",
    "passport-remember-me": "0.0.1",
    "pg": "^6.2.3",
    "realworks": "0.0.7",
    "realworks-local-media": "0.0.2",
    "realworks-postgres-logger": "0.0.2",
    "realworks-postgres-property": "0.0.1",
    "recaptcha": "^1.2.1",
    "redis": "^2.7.1",
    "serve-favicon": "^2.4.3",
    "speakingurl": "^13.0.0",
    "sql-template-strings": "^2.2.2",
    "striptags": "^3.0.1"
  }

What happens when require()ing fs-extra under npm 5.0.0, is that it complains the module rimraf is not installed. When looking in node_modules, it really isn't there. When installing this module manually (npm install rimraf in project folder) it complains about another package, and if I repeat another and so on

I'm also observing user:group of 501:20 when installing by root (in a linux container), without SUDO_UID and SUDO_GID. But bigger problem is that we get the folowing syscall eventually:

4556 chown("/usr/local/lib/node_modules/.staging/invariant-a05eb0c3", 302912903, 1215288109) = -1 EINVAL (Invalid argument)

Which don't look like sensible user and group id.

Reproduced on npm@5.0.1

I've also been experiencing this bug while trying to use npm5 in a Docker container on CircleCI 2.0. npm is being run by root and neither SUDO_UID nor SUDO_GID are set.

After reading this issue I employed the tried and true debugging method of deleting code at random until something works, and I discovered that the following change to lib/config/pacote.js fixes the problem for me:

--- pacote.js	2017-06-02 15:03:57.000000000 -0700
+++ pacote.fixed.js	2017-06-02 15:04:13.000000000 -0700
@@ -42,9 +42,7 @@
     userAgent: npm.config.get('user-agent')
   }

-  if (ownerStats.uid || ownerStats.gid) {
-    Object.assign(opts, ownerStats)
-  }
+  Object.assign(opts, ownerStats)

   npm.config.keys.forEach(function (k) {
     const authMatch = k[0] === '/' && k.match(

At least in my case, the problem seems to have been that since ownerStats.uid and ownerStats.gid were both 0, opts.uid and opts.gid were never being set.

I'm seeing 501:dialout on /usr/lib/node_modules/npm in my Alpine Docker images too: mhart/alpine-node#94

@rgrove's suggestion above also seems on point for what we are experiencing: trying to do global installs while logged in as root results in files set to the uid/gid of the package maintainer (whomever created the source tarball). I.E., since uid/gid is 0/0 in that case, the code rgrove referenced above in the pacote.js file does not apply the uid/gid to the options at all. As a result the options (ultimately) are passed to the extraction process as uid/gid undefined/undefined, and the extraction process then just leaves them set to the ids in the tarball.

rgrove's suggestion in combination with the recent npmc patch for file/directory permissions (80c33cf) seems to correct both the file owners and permissions errors for us when installing global packages as root.

I’m still seeing this in 5.0.4, even though the patch (80c33cf) seems to be in 5.0.4…?

@haggholm 80c33cf doesn't appear to address the issue I described in #16766 (comment).

I think both changes are needed, as @DanRagle said in #16766 (comment), but I'm not confident enough in my knowledge of npm's internals to say for sure that what I proposed in my comment is the correct fix. It would be helpful if someone with a deeper understanding of npm and pacote could weigh in.

@rgrove @Cyberuben @mhart @haggholm I've pushed a new canary, npmc@5.0.4-canary.7, which I believe fixes this. Could any of y'all verify this for me? Fingers crossed. :) (npm i -g npmc@latest && npmc i)

@zkat npmc@5.0.4-canary.7 works like a charm for me. Thanks! 🎉

@zkat doesn't seem to work for me. At least in the sense that /usr/lib/node_modules/npmc is still owned by 501:dialout

You can reproduce with:

docker run mhart/alpine-node sh -c 'npm i -g npmc@latest && npmc i && ls -la /usr/lib/node_modules'

Oh wait, that's probably because npm installed npmc, right...?

@mhart yes. You need to use npmc itself ;)

Right, duh 😸

Yes, this indeed works:

docker run mhart/alpine-node sh -c 'npm i -g npmc@latest && npmc i -g npm@latest && ls -la /usr/lib/node_modules'

looks like we're done here. thanks all!

Any idea when this patch is getting merged to mainline?

@jspiro the release should be ~ wednesday next week.

I’m still getting bad permissions when installing packages globally. I don’t know precisely what the cases are, but it seems to happen when running gyp; I find myself having to fix the permissions, change to the global node_modules/..., and run node rebuild.

5.6.0 an this error still occurs for npm global builds for root. npm...

I'm still seeing global install permissions bugs in 5.7.1.

commented

Like @cadavre; errors on global root install for packages:

root@domoticz:~# npm install -g node-red-admin
npm WARN deprecated node-uuid@1.4.8: Use uuid module instead
/usr/bin/node-red-admin -> /usr/lib/node_modules/node-red-admin/node-red-admin.js

> bcrypt@0.8.7 install /usr/lib/node_modules/node-red-admin/node_modules/bcrypt
> node-gyp rebuild

gyp WARN EACCES user "root" does not have permission to access the dev dir "/root/.node-gyp/8.11.0"
gyp WARN EACCES attempting to reinstall using temporary dev dir "/tmp/.node-gyp"
gyp ERR! configure error
gyp ERR! stack Error: EACCES: permission denied, mkdir '/usr/lib/node_modules/node-red-admin/node_modules/bcrypt/build'
gyp ERR! System Linux 4.9.59-v7+
gyp ERR! command "/usr/bin/node" "/usr/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js" "rebuild"
gyp ERR! cwd /usr/lib/node_modules/node-red-admin/node_modules/bcrypt
gyp ERR! node -v v8.11.0
gyp ERR! node-gyp -v v3.6.2
gyp ERR! not ok
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: bcrypt@0.8.7 (node_modules/node-red-admin/node_modules/bcrypt):
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: bcrypt@0.8.7 install: `node-gyp rebuild`
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: Exit status 1

+ node-red-admin@0.1.3
updated 1 package in 9.898s

root@domoticz:~# npm -v
5.6.0

root@domoticz:~# node -v
v8.11.0

root@domoticz:~# ls -ahl /root/.node-gyp/
total 12K
drwxr-xr-x 3 root root    4.0K Mar 29 20:22 .
drwx------ 7 root root    4.0K Mar 29 20:22 ..
drwxr-xr-x 3  502 dialout 4.0K Mar 29 20:22 8.11.0

commented

@cadavre I had the same issue. Updating to npm 5.8.0 didn't solve my problem.

Although, the following command saved me:

npm config set unsafe-perm=true

or just

npm i npm -g --unsafe-perm=true

Wow, this is still an issue in 6.1.0 Sadly for me the only satisfying resolution was to switch to yarn.