我的开发人员希望仅允许局域网内的用户下载某些文件。我说好的,这很简单,然后我对 nginx 配置进行了如下更改:
location /restricteddir/download/file {
allow 192.168.0.0/16;
allow 10.0.0.0/8;
deny all;
}
好的,从外部我得到了 403,这很好,但是从内部(LAN)我得到了 404。为什么?
我检查了一下,文件的位置在磁盘上不存在。开发人员告诉我,该位置是由 php 动态生成的,因此文件位于 tmp 目录中,但是单击以下链接后:
https://example.com/report/download/file/id/somefile.txt
它应该开始下载但却出现 404 错误。
最后的物理位置是/restricteddir/download
由file/id/somefile.txt
php 生成的。
为了测试,我将位置改为,/restricteddir/download
然后访问https://example.com/restricteddir/download
也出现 404 错误。
我很困惑,禁用该目录的限制后,一切都正常了,我可以从中下载文件https://example.com/report/download/file/id/somefile.txt
并点击https://example.com/report/download/
,没有 404。那么错误在哪里?我应该在 nginx 配置中添加什么才能使其正常工作?
编辑1 这是该虚拟主机的完整配置:
#example.com:443
server {
listen 443 default;
ssl on;
ssl_certificate example.crt;
ssl_certificate_key example.key;
ssl_session_timeout 15m;
ssl_protocols SSLv2 SSLv3 TLSv1;
ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;
ssl_prefer_server_ciphers on;
server_name example.com;
access_log /var/log/nginx/example.com.access.log;
error_log /var/log/nginx/example.com.error.log;
location / {
root /sites/public;
index index.php;
if (!-f $request_filename) {
rewrite ^.*$ /index.php last;
break;
}
}
location /restricteddir/download/file {
allow 192.168.0.0/16;
allow 10.0.0.0/8;
deny all;
}
#PHP
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
include /fastcgi_params;
fastcgi_param SCRIPT_FILENAME /sites/public$fastcgi_script_name;
}
}
server {
listen 80 default;
server_name example.com;
access_log off;
rewrite ^(.*)$ https://example.com$1 permanent;
}
我在日志中发现了这一点:
2012/08/07 11:55:43 [error] 11121#0: *90 open() "/usr/share/nginx/html/restricteddir/download/file/id/somefile.txt" failed (2: No such file or directory), client: 10.200.2.70, server: example.com, request: "GET /restricteddir/download/file/id/somefile.txt HTTP/1.1", host: "example.com"
所以现在我知道 nginx 在错误的根目录中查找此位置,/usr/share/nginx/html/
而不是/sites/public
所以也许我应该这样写:
location /sites/public/restricteddir/download/file {
allow 192.168.0.0/16;
allow 10.0.0.0/8;
deny all;
}
编辑2
我将根指令移至服务器块,现在在日志中启用位置 /restricteddir/download/file 的限制后,我有:
2012/08/09 10:43:12 [error] 32120#0: *71 open() "/site/public/restricteddir/download/file/id/somefile.txt" failed (2: No such file or directory), client: 10.2.1.120, server: example.com, request: "GET /restricteddir/download/file/id/somefile.txt HTTP/1.1", host: "example.com"
所以现在 nginx 正在正确的地方寻找,但没有找到任何东西。好像这个文件不是由 php 生成的?我正在努力解决它,不知道出了什么问题……这是一个简单的任务,限制位置,为什么这不起作用?
编辑3
这是我的虚拟主机配置现在的样子:
#example.com:443
server {
listen 443 default;
ssl on;
ssl_certificate example.crt;
ssl_certificate_key example.key;
ssl_session_timeout 15m;
ssl_protocols SSLv2 SSLv3 TLSv1;
ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;
ssl_prefer_server_ciphers on;
server_name example.com;
access_log /var/log/nginx/example.com.access.log;
error_log /var/log/nginx/example.com.error.log;
root /sites/public;
location / {
index index.php;
if (!-f $request_filename) {
rewrite ^.*$ /index.php last;
break;
}
}
location /restricteddir/download/file {
allow 192.168.0.0/16;
allow 10.0.0.0/8;
deny all;
}
#PHP
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
include /fastcgi_params;
fastcgi_param SCRIPT_FILENAME /sites/public$fastcgi_script_name;
}
}
server {
listen 80 default;
server_name example.com;
access_log off;
rewrite ^(.*)$ https://example.com$1 permanent;
}
因此我只将根指令移入服务器块,之后 nginx 看起来很好但仍然出现 404。我很困惑,因为在我注释掉位置 /restricteddir/download/file 块后一切工作正常,我可以下载文件。
对我来说这并不奇怪,因为它只是简单的限制...为什么启用它后 nginx 找不到该文件...
答案1
两个建议:
- 将
root
指令移到块中server
,这样它将应用于location
其后的所有块。这似乎是您的意图。 - 请记住位置指令适用于URI不是*文件路径。因此,例如,的想法
location /sites/public
没有意义。
还有一些其他反馈:
在处理指令时,文件是否物理存在并不重要location
,无论如何它们都会匹配。
我发现的另一个不匹配之处是您的日志条目提到了“restricteddir”,但您的“完整配置”帖子没有提到这一点。它反而提到了“报告”目录。
答案2
首先,如果可能的话,我会避免使用以下if
代码,因为if 是邪恶的. 改用try_files
:
location / {
root /sites/public;
index index.php;
if (!-f $request_filename) {
rewrite ^.*$ /index.php last;
break;
}
}
这个更好:
location / {
try_files $uri $uri/ index.php?$args;
}
为了清楚起见,我将移出并root
移入该区块:index
location /
server
server {
…
server_name example.com;
access_log /var/log/nginx/example.com.access.log;
error_log /var/log/nginx/example.com.error.log;
root /sites/public;
index index.php;
这些文件是否完全由 PHP 动态生成,因此不直接存在于文件系统中,因此 nginx 无法在不将请求传递给 PHP 的情况下提供服务?如果是这样,您需要将内容传递给fastcgi_pass
fastcgi,以便 PHP 可以创建并提供服务,而不是 nginx 尝试提供不存在的内容。所以:
location /report/download/ {
allow 192.168.0.0/16;
allow 10.0.0.0/8;
deny all;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
include /fastcgi_params;
fastcgi_param SCRIPT_FILENAME /sites/public$fastcgi_script_name;
}
如果文件实际上存在于磁盘上,但位于不同的位置,即请求实际上https://example.com/report/download/file/id/somefile.txt
指向磁盘上的位置,/sites/public/restricteddir/download/file/id/somefile.txt
并且您希望 nginx 直接为其提供服务而不交给 fastcgi,那么您需要别名指令。
location /report/download/ {
allow 192.168.0.0/16;
allow 10.0.0.0/8;
deny all;
# The actual physical path on disk -
alias /sites/public/restricteddir/download/;
# or should this be the following? -
# alias /restricteddir/download/;
}
最后要检查的是:如果 nginx 和 fastcgi 以不同的用户身份运行,请确保两者都具有正确的操作系统文件权限来读取相关文件。如果 nginx 在正确的位置查找但仍找不到该文件,那么它的所有者和组可能是具有 660 权限的 fastcgi:fastcgi?要么是存在某种缓存,例如open_file_cache
在 nginx.conf 中