使用 uWSGI 和 nginx 的 cgi-bin 脚本的问题

使用 uWSGI 和 nginx 的 cgi-bin 脚本的问题

我正在尝试使用 uWSGI 在 nginx 系统上运行 cgi-bin 脚本,但遇到了问题,并且正在调试以找出问题所在。该系统是运行 Rasbian bookworm 的 Raspberry Pi,我已将 cgi-bin 的 nginx 位置块添加到默认站点,/etc/nginx/sites-available/default如下所示...

location ^~ /cgi-bin/ {
   root /var/www/cgi-bin;
   include uwsgi_params;
   uwsgi_pass unix:/tmp/uwsgi.sock;
}

据我所知,该位置块定义了一个表达式,以便随后处理以 cgi-bin 开头的任何内容。uWSGI 的基本配置包括我的系统提供的一个文件,该文件设置了一些默认值,/etc/nginx/uwsgi_params该文件在这里...

uwsgi_param  QUERY_STRING       $query_string;
uwsgi_param  REQUEST_METHOD     $request_method;
uwsgi_param  CONTENT_TYPE       $content_type;
uwsgi_param  CONTENT_LENGTH     $content_length;

uwsgi_param  REQUEST_URI        $request_uri;
uwsgi_param  PATH_INFO          $document_uri;
uwsgi_param  DOCUMENT_ROOT      $document_root;
uwsgi_param  SERVER_PROTOCOL    $server_protocol;
uwsgi_param  REQUEST_SCHEME     $scheme;
uwsgi_param  HTTPS              $https if_not_empty;

uwsgi_param  REMOTE_ADDR        $remote_addr;
uwsgi_param  REMOTE_PORT        $remote_port;
uwsgi_param  SERVER_PORT        $server_port;
uwsgi_param  SERVER_NAME        $server_name;

上面的内容非常简单,我在执行此操作时尝试了一些方法。我创建了一个 systemd 单元,它以特定的配置运行 uWSGI,您可以在此处看到...

[uwsgi]
uid = uwsgi
gid = uwsgi
plugins = cgi,logfile
socket = /tmp/uwsgi.sock
cgi = /cgi-bin=/var/www/cgi-bin
chmod-socket = 666
chown-socket = www-data:www-data
req-logger = file:/var/log/uwsgi/errors.log
logger = file:/var/log/uwsgi/errors.log
chdir = /var/www/cgi-bin

我尝试了上述路径配置的其他一些变体。在查看了下面的方法后,我决定不再担心这些路径和这些细节。

一切启动,目录权限cgi-bin设置正确,一切运行正常。当我尝试使用 URL 运行脚本时,http://this.system/cgi-bin/nph-proxy.cgi我在 nginx 中收到 502 错误,然后此错误出现在我为 uWSGI 设置的日志中...

-- unavailable modifier requested: 0 --

日志没有显示此处出错的详细信息,而这正是我的主要问题。我找不到任何方法来获取更详细的日志或有关此错误的更多上下文,因此我继续进行进一步调查。有些帖子说一些 Django 用户由于缺少插件而遇到问题。我已经验证了 CGI 的插件和这些错误日志存在并且正在加载。

这与 nginx 错误日志中的请求错误相对应......

2023/07/24 18:02:18 [error] 32579#32579: *5 upstream prematurely closed connection while reading response header from upstream, client: 192.168.1.4, server: _, request: "GET /cgi-bin/test.cgi HTTP/1.1", upstream: "uwsgi://unix:/tmp/uwsgi.sock:", host: "redacted.home"

因为没有选项可以获取更详细的日志,所以我决定看看其他方法。第一个是得到一个朋友借给我的 GPT-4 实例来创建一个简单的 CGI Perl 脚本,该脚本在运行时只需创建一个文件并将其环境变量写入其中……

#!/usr/bin/perl

use CGI;
my $cgi = CGI->new;

# Open the debug file
open(my $fh, '>', '/var/www/cgi-bin/test.log') or die "Could not open file 'debug_file.txt' $!";

# Print the environment variables to the file
foreach my $key (sort keys %ENV) {
    print $fh "$key=$ENV{$key}\n";
}

# Close the file
close $fh;

# Generate a simple HTTP response
print $cgi->header('text/plain');
print "Debug information written to '/path/to/debug_file.txt'.\n";

exit;

这个简单的 Perl 脚本从未创建过任何文件,因此我想尝试一些其他的方法来看看发生了什么。

我决定使用工具 socat 和命令socat UNIX-LISTEN:/tmp/uwsgi.sock,user=www-data STDOUT来监听 uWSGI 使用的套接字,亲自查看请求。输出有点乱码,无法很好地粘贴在这里,但我能够看到 nginx 在此请求中传递的一些详细信息...

SERVER_PROTOCO HTTP/1.1
REQUEST_SCHEME http
REQUEST_URI /cgi-bin/test.cgi
PATH_INFO /cgi-bin/test.cgi

socat 的输出中还有其他内容,但总体而言,这里没有什么特别突出的内容,无论是明显的还是我可以更改的。不过,我认为 nginx 提供的路径可能是罪魁祸首。调整路径等并没有改变任何事情,正如我接下来对发生的事情的调查所揭示的那样。

我还决定使用它strace来监控 uWSGI 工作进程,strace允许查看进程进行的系统调用,虽然有点限制,但可以提供一些见解。我在启动服务时从日志文件中获取了进程 ID。我使用了这个 strace 命令,strace -fvv -s 9999 -p 32699它可以跟踪任何分叉,非常详细,并且不会截断小于 9999 字节的字符串。一旦运行,它就会像这样等待...

strace: Process 32699 attached
epoll_wait(10,

当我向目录中的 nginx 发出 HTTP 请求来获取我的测试脚本时cgi-bin,我们可以看到工作进程执行了一些工作。我们可以看到 uWSGI 工作进程向操作系统询问请求,以及有关请求的一些信息。接下来,脚本将进行与将错误写入日志相关的系统调用。

epoll_wait(10, [{EPOLLIN, {u32=9, u64=9}}], 1, -1) = 1
accept4(9, {sa_family=AF_UNIX}, [110->2], SOCK_NONBLOCK) = 12
read(12, "\0v\1\0\f\0QUERY_STRING\0\0\16\0REQUEST_METHOD\3\0GET\f\0CONTENT_TYPE\0\0\16\0CONTENT_LENGTH\0\0\v\0REQUEST_URI\26\0/cgi-bin/nph-proxy.cgi\t\0PATH_INFO\26\0/cgi-bin/nph-proxy.cgi\r\0DOCUMENT_ROOT\20\0/var/www/cgi-bin\17\0SERVER_PROTOCOL\10\0HTTP/1.1\16\0REQUEST_SCHEME\4\0http\v\0REMOTE_ADDR\v\000192.168.1.4\v\0REMOTE_PORT\5\00040434\v\0SERVER_PORT\2\080\v\0SERVER_NAME\1\0_\t\0HTTP_HOST\n\0pidns.home\17\0HTTP_USER_AGENT\n\0curl/8.0.1\v\0HTTP_ACCEPT\3\0*/*", 4100) = 378
write(2, "-- unavailable modifier requested: 0 --\n", 40) = 40
close(12)                               = 0

在这里,我将上述内容分解为更容易阅读的内容......

REQUEST_URI /cgi-bin/nph-proxy.cgi
PATH_INFO /cgi-bin/nph-proxy.cgi
DOCUMENT_ROOT /var/www/cgi-bin
SERVER_PROTOCOL HTTP/1.1
REQUEST_SCHEME http
SERVER_PORT 80
SERVER_NAME (possibly empty)

我注意到,strace 发现请求中的某些路径看起来不太正确。proc 文件系统还指出了所使用的工作目录存在一些问题,该目录设置为/- 我已修复该问题,并在上​​面显示的 nginx 和 uWSGI 配置文件中进行了更新。

我认为这说明了很多问题关于 strace 没有显示的内容即文件操作,或任何运行脚本的系统调用。尽管对 nginx 和 uWSGI 服务的配置进行了各种调整,但从未尝试打开任何文件或执行任何操作。

文档显示,遇到此错误的用户必须安装并启用某些插件才能消除错误。然而,这些人正在运行 Python/Django 应用程序,而我只是使用 CGI 和日志文件扩展。运行 CGI 应用程序除了启用 CGI 插件和cgi在配置文件中有一个变量之外,没有提到任何内容。

鉴于错误发生时进程除了获取请求之外没有执行任何其他操作,进程从未尝试对请求中的详细信息执行任何操作,并且某些用户在缺少插件等时会遇到此错误 - 我认为可以假设这些unsupported modifiers基本上是 nginx 提供的变量。此处的默认值将包含现代 Web 应用程序所需的一切,例如 clean-url 和 CGI​​ 中不存在的其他内容。

我怀疑解决这个问题的方法是减少 nginx 传递的内容,但我在文档中找不到有关此内容的任何信息。如果有人更熟悉 CGI 的工作原理,也许我上面的配置或调试中有些东西很突出。

相关内容