在 chroot 内部的 fastcgi 下运行 php 时出现“未指定输入文件”

在 chroot 内部的 fastcgi 下运行 php 时出现“未指定输入文件”

我有一个在 chroot (Karmic Koala)内部nginx/1.4.6运行Ubuntu 14.04的场景。php/5.2.10Ubuntu 9.10

我的问题是,所有对 php 文件的请求都会导致可怕的“ No input file specified.

我将网站存储在 chroot 中,因此通过以下设置,可以从 chroot jail 内的 php 和 jail 外的 nginx 读取它:

From nginx' point of view:
/var/chroot/karmic/var/www/domains/dummysite/web:
.         www-data:www-data drwxr-xr-x
index.php www-data:www-data -rw-r--r--
test.jpg  www-data:www-data -rw-r--r--

在 chroot 里面

From php's point of view:
/var/www/domains/dummysite/web:
.         www-data:www-data drwxr-xr-x
index.php www-data:www-data -rw-r--r--
test.jpg  www-data:www-data -rw-r--r--

非常index.php简单!

<?php
    echo '<h1>Hello World</h1> Foo bar...';
?>

我已经使用以下命令从 lighttpd 使用 spawn-fcgi 启动了 php-fcgi:

LANG=C chroot /var/chroot/karmic /usr/bin/spawn-fcgi -C 12 -a 127.0.0.1 -p 9000 -u www-data -g www-data -f /usr/bin/php5-cgi -P /var/run/fastcgi-php.pid

Nginx 可以成功提供静态 test.jpg,但 php-fcgi 无法读取 index.php

# /etc/nginx/site-enabled/dummysite -> /etc/nginx/site-available/dummysite:

server {
    listen 80;
    root /var/chroot/karmic/var/www/domains/dummysite/web;
    server_name dummysite.wtf;

    location / {
        try_files $uri $uri/ /index.php?q=$uri&$args;
        index index.php index.html;
        allow all;
    }

    location ~ ^/index\.php {
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_intercept_errors on;
        fastcgi_param SCRIPT_NAME $fastcgi_script_name;
        fastcgi_param SCRIPT_FILENAME /var/www/domains/dummysite/web$fastcgi_script_name;
        fastcgi_param DOCUMENT_ROOT /var/www/domains/dummysite/web;
        include fastcgi_params;
    }
}

据我所知,这应该是正确的语法。我还尝试了一些变体,例如$document_root$fastcgi_script_name,不带SCRIPT_NAMEDOCUMENT_ROOT设置以及SCRIPT_FILENAME相对于DOCUMENT_ROOT或块rootserver

open_basedir我在 php 中没有设置任何限制。

尽管在 php 和 nginx 中启用了最大日志记录,但我在 php.log、nginx.error.log 或 dummysite.wtf.error.log 中都没有得到可用的信息。

我已直接使用该cgi-fcgi实用程序连接到 php-fcgi,这是我得到的响应:

env -i SCRIPT_NAME=index.php DOCUMENT_ROOT=/var/www/domains/dummysite/web SCRIPT_FILENAME=/var/www/domains/dummysite/web/index.php QUERY_STRING= REQUEST_METHOD=GET cgi-fcgi -bind -connect 127.0.0.1:9000
Status: 404 Not Found
X-Powered-By: PHP/5.2.10-2ubuntu6
Content-type: text/html

No input file specified.

相同的结果适用于所有设置SCRIPT_FILENAME

  • /index.php
  • /web/index.php
  • /dummysite/web/index.php
  • /域名/dummysite/web/index.php
  • /www/domains/dummysite/web/index.php
  • /var/www/domains/dummysite/web/index.php
  • /karmic/var/www/domains/dummysite/web/index.php
  • /chroot/karmic/var/www/domains/dummysite/web/index.php
  • /var/chroot/karmic/var/www/domains/dummysite/web/index.php

并且我已经尝试过各种各样的相同方法DOCUMENT_ROOT

答案1

总结

/etc/nginx/sites-enabled/dummysite -> /etc/nginx/sites-available/dummysite
...
location ~ \.php {
    root /var/www/domains/dummysite/web;
    fastcgi_pass 127.0.0.1:9000
    include fastcgi_params;
}
...

/etc/nginx/fastcgi_params
...
fastcgi_param SCRIPT_NAME $document_root$fastcgi_script_name;
...

长解决方案

好的,这就是我解决问题的方法(我认为它可能对某些人有用)。由于 php-cgi 不是很详细,我使用 strace 将 php 的文件操作捕获到磁盘。

 sudo strace -p <pid-of-first-php-process> -p <pid-of-2nd-php> ... -p <pid-of-nth-php> -e trace=all -s 4096

然后我直接使用以下命令调用 php-fcgi:

env -i SCRIPT_NAME=index.php DOCUMENT_ROOT=/var/www/domains/dummysite/web SCRIPT_FILENAME=/var/www/domains/dummysite/web/index.php QUERY_STRING= REQUEST_METHOD=GET cgi-fcgi -bind -connect 127.0.0.1:9000

strace 中有趣的几行是

[pid 24822] read(3, "\v\tSCRIPT_NAMEindex.php\r\33DOCUMENT_ROOT/var/www/domains/dummysite/web\17%SCRIPT_FILENAME/var/www/domains/dummysite/web/index.php\f\0QUERY_STRING\16\3REQUEST_METHODGET\0", 152) = 152
[pid 24822] lstat("/var", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
[pid 24822] lstat("/var/www", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
[pid 24822] lstat("/var/www/domains", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
[pid 24822] lstat("/var/www/domains/dummysite", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
[pid 24822] lstat("/var/www/domains/dummysite/web", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
[pid 24822] lstat("/var/www/domains/dummysite/web/index.php", {st_mode=S_IFREG|0644, st_size=31, ...}) = 0
[pid 24822] open("/index.php", O_RDONLY) = -1 ENOENT (No such file or directory)

好的,很明显,它成功尝试了 lstat [...]/web/index.php(具有正确的 0644 权限),但随后它尝试打开/index.php。这促使我尝试了SCRIPT_NAME,然后就成功了!

env -i SCRIPT_NAME=/var/www/domains/dummysite/web/index.php DOCUMENT_ROOT=/var/www/domains/dummysite/web SCRIPT_FILENAME=/var/www/domains/dummysite/web/index.php QUERY_STRING= REQUEST_METHOD=GET cgi-fcgi -bind -connect 127.0.0.1:9000
X-Powered-By: PHP/5.2.10-2ubuntu6
Content-type: text/html

<h1>Hello World!</h1> Foo bar...

所以我的第一的问题是我的 nginx 配置应该读

        fastcgi_param SCRIPT_NAME /var/www/domains/dummysite/web$fastcgi_script_name;
#           see what i did here   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

说实话,我不知道这是为什么。我以为的目的SCRIPT_NAME不是指向文件,而只是说明文件的名称。但我想我一定误解了它的用途。

但不幸的是,我的问题还没有解决。当我尝试时curl http://dummysite.wtf/,它仍然说No input file specified.

因此,再次strace进行救援!

[pid 24819] read(3, "\r\33DOCUMENT_ROOT/var/www/domains/dummysite/web\17%SCRIPT_FILENAME/var/www/domains/dummysite/web/index.php\v%SCRIPT_NAME/var/www/domains/dummysite/web/index.php\f\0QUERY_STRING\16\3REQUEST_METHODGET\f\0CONTENT_TYPE\16\0CONTENT_LENGTH\0177SCRIPT_FILENAME/var/chroot/karmic/var/www/domains/dummysite/web/index.php\v\nSCRIPT_NAME/index.php\v\1REQUEST_URI/\f\nDOCUMENT_URI/index.php\r-DOCUMENT_ROOT/var/chroot/karmic/var/www/domains/dummysite/web\17\10SERVER_PROTOCOLHTTP/1.1\21\7GATEWAY_INTERFACECGI/1.1\17\vSERVER_SOFTWAREnginx/1.4.6\v\tREMOTE_ADDR127.0.0.1\v\5REMOTE_PORT46644\v\tSERVER_ADDR127.0.0.1\v\2SERVER_PORT80\v\nSERVER_NAMEdummysite.wtf\17\3REDIRECT_STATUS200\17\vHTTP_USER_AGENTcurl/7.35.0\t\nHTTP_HOSTdummysite.wtf\v\3HTTP_ACCEPT*/*\0\0\0\0\0\0", 672) = 672

答案就在这里,SCRIPT_NAMESCRIPT_FILENAME出现DOCUMENT_ROOT了两次,第一次是正确的,第二次的值是错误的。事实证明,include fastcgi_paramsnginxserver块中的指令会自行插入这些变量,而当我将此 include 语句放在location块的最后时,它实际上覆盖了我之前的设置。

这是我修复此问题的方法:

/etc/nginx/sites-enabled/dummysite -> /etc/nginx/sites-available/dummysite
...
location ~ \.php {
    root /var/www/domains/dummysite/web;
#   ^ This line will set the $document_root variable used later on
    fastcgi_pass 127.0.0.1:9000
    include fastcgi_params;
}
...

在包含的 fastcgi_params 文件中,我将其中一行更改为以下内容

/etc/nginx/fastcgi_params
...
fastcgi_param   SCRIPT_NAME             $document_root$fastcgi_script_name;
# This is needed for chroot to work -> ^$document_root^ 
...

最后,辉煌的成功

$ curl http://dummysite.wtf
<h1>Hello World!</h1> Foo bar...

这是在现代软件(如 ubuntu 14.04 LTS 和 nginx 1.4.x)之上的 chrooted jail 中运行严重老旧的 php 版本的一种方法 :-)

相关内容