我正在尝试使用 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 的工作原理,也许我上面的配置或调试中有些东西很突出。