tldr;使用fs.readFileSync的脚本在使用npm
而不是node
调用时抛出EACCESS
在一个古老的(2016年)Docker图像上,我需要运行一个包含Bower(bower install --allow-root
)的postinstall
NPM脚本,但无论何时,我都会得到EACCES: permission denied, open '/root/.config/configstore/bower-github.json'
。我发现执行npx bower
的结果是一样的。
通常情况下,我会很容易地处理这些问题,因为当有人使用sudo
执行不应该执行的命令时,通常会出现这些问题。解决这些问题的方法通常是将所有者更改回当前用户,或者使用sudo和--allow-root
(example 1,example 2)运行bower命令。
- 但这不是问题之一。*我已经是root用户了!
完整错误与任何类似问题类似:
root@eaa32456c249:/var/www/myproj# npx bower --allow-root
/var/www/myproj/node_modules/bower/lib/node_modules/configstore/index.js:54
throw err;
^
Error: EACCES: permission denied, open '/root/.config/configstore/bower-github.json'
You don't have access to this file.
at Object.openSync (node:fs:585:3)
at Object.readFileSync (node:fs:453:35)
at Configstore.get (/var/www/myproj/node_modules/bower/lib/node_modules/configstore/index.js:35:38)
at new Configstore (/var/www/myproj/node_modules/bower/lib/node_modules/configstore/index.js:28:48)
at readCachedConfig (/var/www/myproj/node_modules/bower/lib/config.js:19:23)
at defaultConfig (/var/www/myproj/node_modules/bower/lib/config.js:11:12)
at Object.<anonymous> (/var/www/myproj/node_modules/bower/lib/index.js:16:32)
at Module._compile (node:internal/modules/cjs/loader:1101:14)
at Object.Module._extensions..js (node:internal/modules/cjs/loader:1153:10)
at Module.load (node:internal/modules/cjs/loader:981:32) {
errno: -13,
syscall: 'open',
code: 'EACCES',
path: '/root/.config/configstore/bower-github.json'
我无法进一步提升权限,添加--allow-root
也没有任何作用。我甚至检查了有问题的模块,发现总是失败的调用很简单:
readFileSync(this.path, 'utf8');
其中this.path
当然是'/root/.config/configstore/bower-github.json'
。
然后我编写了这个小的测试模块来做同样的事情,它运行起来没有问题:
root@eaa32456c249:/var/www/myproj# cat test.js
const execSync = require('child_process').execSync;
const fs = require('fs');
const path = '/root/.config/configstore/bower-github.json';
console.log('exec whoami: ', execSync('whoami').toString());
try {
const result = execSync('ls -l ' + path, { encoding: 'utf8' });
console.log('exec ls -l: ', result);
} catch (err) {}
try {
const parsed = JSON.parse(fs.readFileSync(path, 'utf8', { encoding: 'utf8' }));
console.log('parsed: ', parsed);
} catch (err) {
console.error(err.message);
}
root@eaa32456c249:/var/www/myproj# node test.js
exec whoami: root
exec ls -l: -rw-r--r-- 1 root root 3 Dec 8 22:55 /root/.config/configstore/bower-github.json
parsed: {}
一个谜!
1条答案
按热度按时间elcex8rz1#
**tldr;**NPM版本〉6(但小于9!)将以根包目录的所有者身份运行。换句话说,如果你想以根目录的身份运行,在你项目的根目录上执行
chown root.root -R .
。更新NPM 9恢复了这一变更,意味着上述内容仅适用于NPM 6和7。参见PR。
在让这个问题酝酿了很长一段时间之后,我想我可能会检查代码是以谁的身份运行的,所以我打开了有问题的模块(
node_modules/configstore/index.js
),并将以下内容添加到失败调用之前的行中:的确有一些可疑的事情正在发生,正如这些文字所示:
因此,以
root
的身份运行npx bower
会使bower
以uid=1000的用户身份运行?运行npm run postinstall
会导致相同的问题。好的......让我们仔细看看这个。如果我使用
node
手动运行bower
CLI模块会怎么样?它工作-我仍然是根!所以很明显,
npx
和npm
都在不知何故做一些时髦的掩盖下,关于谁的命令运行!NPM、
root
和CWD的所有者深入挖掘以找到解决方案
在我发现上述事实后,我做了一些谷歌搜索,并遇到了this NPM issue,而实际上没有一个直接的解释,使我进入节点试图作为文件的所有者执行的线索。
简单地执行
chown root.root -R node_modules
没有任何作用,所以我继续搜索,然后读取this article,其中包含以下代码片段:如果使用root权限调用npm,则它会将uid更改为用户帐户或用户config指定的uid,默认值为nobody。设置unsafe-perm标志以使用root权限运行脚本。
好的,让我们尝试将
unsafe-perm
设置为true。没有成功,我仍然以uid=1000运行。然后我冒险进入实际的NPM库,搜索与uid
相关的内容,并在以下内容中找到了答案:对于NPM 8,它使用这段代码来确定以谁的身份运行:
正如
infer-owner
模块的the docs所述:根据路径最近的现有父路径的所有者推断路径的所有者
注意
cwd
部分。它不查看node_modules
,而是当前工作目录!所以我在项目的根目录上运行了chown root.root -R .
,你瞧,它成功了!附录
此行为仅适用于NPM版本7和8。NPM版本〈7(随节点12-14一起提供)在以root身份运行时工作得非常好。因此,最快的修复方法可能只是使用节点14。从NPM 9开始,旧的行为又回来了(可能随节点20一起提供)。