无法在 web 服务器上运行 Node

5

我试图从一个PHP网页中运行npm,但它从未运行。我总是得到一个代码为127的退出代码和没有输出。经过一些测试,我将问题缩小到npm中的shebang,它看起来像这样:

#!/usr/bin/env node

相当标准,但我写了一些测试代码:
<?php
$result = exec("/usr/bin/env node --version", $output, $exit);
var_dump($result);
var_dump($exit);
$result = exec("node --version", $output, $exit);
var_dump($result);
var_dump($exit);
$result = exec("/usr/bin/env gzip --version", $output, $exit);
var_dump($result);
var_dump($exit);

我在浏览器中看到了这个输出:

string(0) ""
int(127)
string(6) "v8.4.0"
int(0)
string(28) "Written by Jean-loup Gailly."
int(0)

我在PHP-FPM配置中启用了catch_workers_output,并在PHP日志中看到了以下内容:

[18-Aug-2017 15:15:35] WARNING: [pool web] child 27872 said into stderr: "/usr/bin/env: "
[18-Aug-2017 15:15:35] WARNING: [pool web] child 27872 said into stderr: "node"
[18-Aug-2017 15:15:35] WARNING: [pool web] child 27872 said into stderr: ": No such file or directory"
[18-Aug-2017 15:15:35] WARNING: [pool web] child 27872 said into stderr: ""

我还尝试从Web服务器上运行 exec("which node") ,并在PHP日志中看到了以下内容:

[18-Aug-2017 15:31:12] WARNING: [pool web] child 27873 said into stderr: "which: no node in ((null))"

我尝试运行var_dump(exec('echo $PATH')),并得到以下输出:
string(28) "/usr/local/bin:/bin:/usr/bin"

这似乎与env命令如何处理路径有关。我尝试在nginx.conf中使用fastcgi_param PATH指令手动设置它,但对上述输出没有产生任何变化。

仅运行env并打印输出结果如下,没有PATH条目:

Array
(
    [0] => USER=nginx
    [1] => PWD=/var/www/html
    [2] => SHLVL=1
    [3] => HOME=/var/cache/nginx
    [4] => _=/bin/env
)

我使用基于RHEL的发行版,已禁用SELinux,在Nginx 1.12.1上通过UNIX套接字运行PHP-FPM 5.6.31。 /usr/local/bin/node 是指向 /usr/local/nodejs/bin/node 的符号链接,具有775权限。出于测试目的,nginx用户拥有/usr/local/nodejs目录及其所有后代的所有权。有什么建议吗?
请注意,如果我从CLI(作为nginx用户)运行PHP代码,则按预期工作,因此这肯定与CGI / FPM环境有关。
$ su -s "/bin/sh" -c "/var/www/html/test.php" nginx
string(6) "v8.4.0"
int(0)
string(6) "v8.4.0"
int(0)
string(28) "Written by Jean-loup Gailly."
int(0)

当您在浏览器中运行 exec("which node", $output, $exit); 时会发生什么? - chiliNUT
@chiliNUT 没有输出,退出代码为1。这又没有意义,因为我可以只用 node 运行它,所以它必须在 $PATH 中。在 CLI 中运行良好($_SERVER["PATH"] 在 FPM 中返回 null,在 CLI 上似乎很好)。 - miken32
如果which返回代码1,我认为这意味着它没有找到它。但是,如果您可以仅使用node运行它,则必须在$path中...但我非常确定which只会浏览您的路径中的所有内容,直到找到它或放弃,所以这很奇怪。 - chiliNUT
1
根据_变量,shell被调用为/bin/env。当您运行exec("/bin/env node --version", $output, $exit);时会发生什么? - piarston
@piarston,一样的,/usr/bin/env是一个符号链接。 - miken32
2个回答

4

您需要在/etc/php5/fpm/pool.d/www.conf中设置PATH环境变量,以包括/usr/local/bin/node

在Ubuntu发行版中,它被注释掉了:

;env[PATH] = /usr/local/bin:/usr/bin:/bin 

so

env[PATH] = /usr/local/bin/node 

应该可以胜任这项工作。RHEL可能不会有太大的区别,但即使没有这行代码,您也可以随时添加它。

不要忘记重新启动php-fpm。


如果什么都不起作用,您始终可以在调用npm时明确设置路径:

exec('PATH=$PATH:/usr/local/bin/node npm');

但这只是最后的选择,我不建议这样做。

是的,那似乎解决了问题!我对FPM还很陌生,不知道可以在配置中设置环境变量。 - miken32

0

有一件事让我很好奇……为什么以ngnix用户身份运行脚本可以正常工作呢?

实际上,ngnix会向php-fpm请求进程,而执行您的脚本的是PHP-FPM。因此,可能是PHP-FPM服务缺少对/usr/bin/env node的权限。

为了简化问题,ngnix需要权限才能操作PHP-FPM。但实际上,PHP-FPM需要权限才能操作文件。

为了简化解决方案,可能通过将PHP-FPM用户(www-data?)添加到一个node二进制文件所在的组中,并确保ngnix用户已经在该组中来解决问题。

或者最好按照以下建议进行操作:https://serverfault.com/a/52703

编辑 #1:

我仍然会关注特权。

/usr/local/bin/node是指向具有775权限的/usr/local/nodejs/bin/node的符号链接。nginx用户拥有/usr/local/nodejs。

/usr/local目录下设置特定用户而不使用-x选项有点不标准。因为/usr/local原本是为任何用户提供存储库的。

请在不同情况下(从CLI、FPM、作为ngnix等)运行此脚本并检查您的权限:

<?php

$myuid = getmyuid();
$mygid = getmygid();

// you should change to check /usr/local/nodejs/bin/node
$path = '/usr/local/n/versions/node/7.2.0/bin/node'; 

$patharray = array_filter(explode('/', $path));

$dump = "%s \t %s \t %s%s%s \t %s" . PHP_EOL;

$fileOrFolder = __FILE__;
printf("uid \t gid \t rwx \t file" . PHP_EOL);
printf($dump, $myuid, $mygid, (is_readable($fileOrFolder) ? 'r' : '-'), (is_writable($fileOrFolder) ? 'w' : '-'), (is_executable($fileOrFolder) ? 'x' : '-'), $fileOrFolder);

$fileOrFolder = '';
while(count($patharray) > 0) {
    $fileOrFolder .= ('/' .  array_shift($patharray));

    $myuid = posix_getpwuid(fileowner($fileOrFolder));
    $mygid = posix_getgrgid(filegroup($fileOrFolder));

    printf($dump, $myuid['name'], $mygid['name'], (is_readable($fileOrFolder) ? 'r' : '-'), (is_writable($fileOrFolder) ? 'w' : '-'), (is_executable($fileOrFolder) ? 'x' : '-'), $fileOrFolder);

    if (is_dir($fileOrFolder) && !is_executable($fileOrFolder)) {
        echo PHP_EOL . "Ouh, cant go further cause of privelages" . PHP_EOL;
        break;
    }
}

我的输出证明,尽管是不同的用户,PHP仍然可以查看目标文件的所有内容:

@riddick:~/temp$ php phpowner.php
uid      gid     rwx     file
1000     1000    rw-     /home/yergo/temp/phpowner.php
root     root    r-x     /usr
root     root    r-x     /usr/local
root     root    r-x     /usr/local/n
root     root    r-x     /usr/local/n/versions
root     root    r-x     /usr/local/n/versions/node
root     root    r-x     /usr/local/n/versions/node/7.2.0
                 r-x     /usr/local/n/versions/node/7.2.0/bin
                 r-x     /usr/local/n/versions/node/7.2.0/bin/node

PHP-FPM 已配置为以用户/组“nginx”运行。 - miken32
请参考我的补充。我认为这仍然是一种权限不匹配的情况。 - yergo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接