我们的服务器使用 PHP 以及 MivaScript(.php 和 .mvc),它们均使用 fastcgi 在 nginx 中配置。
同时,我们希望通过 IP 地址锁定一些文件/目录。我了解到 nginx 每次请求只处理一个匹配的位置块,这似乎迫使我重复一些语句,而我更愿意避免这种情况。
例如,我从以下内容开始,告诉服务器如何处理.php 和.mvc 文件:
location ~ \.php$ {
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_pass unix:/var/run/php-fpm.sock;
}
location ~ \.mvc$ {
fastcgi_read_timeout 300;
fastcgi_pass www.example.com:1234;
fastcgi_param MvCONFIG_LIBRARY /var/www/sites/www.example.com/cgi-bin/libmivaconfig.so;
include fastcgi_params;
}
好的,太好了,现在服务器知道如何处理这些文件了。接下来我想告诉服务器拒绝访问某些文件:
location ~ ^(\/mm5\/admin.mvc|\/MyProtected\/PHPScripts\/) {
deny all;
}
这工作得很好,当我尝试访问这些页面时出现 403 错误。
但是现在,如果我尝试允许我的 IP 地址 - 服务器会“忘记”如何处理 .mvc 文件(我还没有测试过,但我想 .php 文件也会被忘记)。它不会显示网页,而是提示我下载文件。
location ~ ^(\/mm5\/admin.mvc|\/MyProtected\/PHPScripts\/) {
allow 1.2.3.4;
deny all;
}
我希望可以进行一些嵌套,告诉服务器如何处理 .mvc 文件,然后拒绝其中一些文件,但它仍然提示下载:
location ~ \.mvc$ {
fastcgi_read_timeout 300;
fastcgi_pass www.example.com:1234;
fastcgi_param MvCONFIG_LIBRARY /var/www/sites/www.example.com/cgi-bin/libmivaconfig.so;
include fastcgi_params;
location ~ ^(\/mm5\/admin.mvc) {
allow 1.2.3.4;
deny all;
}
}
即使上述尝试成功,我也必须在 .mvc 块和 .php 块下复制 IP 地址列表。
因此,我唯一能做的事情就是,这需要大量的重复,但却造成了如此混乱:
# restrict access to this .mvc file, and tell the server how to
# handle it for anyone who is permitted
# remember to copy your changes everywhere you need to!
location ~ ^(\/mm5\/admin.mvc) {
allow 1.2.3.4;
deny all;
fastcgi_read_timeout 300;
fastcgi_pass www.example.com:1234;
fastcgi_param MvCONFIG_LIBRARY /var/www/sites/www.example.com/cgi-bin/libmivaconfig.so;
include fastcgi_params;
}
# tell the server how to handle the rest of the world's .mvc pages
# remember to copy your changes everywhere you need to!
location ~ \.mvc$ {
fastcgi_read_timeout 300;
fastcgi_pass www.example.com:1234;
fastcgi_param MvCONFIG_LIBRARY /var/www/sites/www.example.com/cgi-bin/libmivaconfig.so;
include fastcgi_params;
}
# restrict access to this directory of PHP scripts, and tell the server how to
# handle them for anyone who is permitted
# remember to copy your changes everywhere you need to!
location ~ ^(\/MyProtected\/PHPScripts\/) {
allow 1.2.3.4;
deny all;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_pass unix:/var/run/php-fpm.sock;
}
# tell the server how to handle the rest of the world's PHP scripts
# remember to copy your changes everywhere you need to!
location ~ \.php$ {
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_pass unix:/var/run/php-fpm.sock;
}
一定有更好的方法!至少,我能以某种方式设置几个包含文件,其中包含允许的 IP 列表和 PHP/MVC 配置,然后将它们包含在我似乎需要复制它们的 999 个位置吗?
或者更好的是,希望我是把这个问题复杂化了,而有一个令人难以置信的简单、令人难以置信的明显的解决方案被我错过了?
答案1
还有另一种方法,您可以使用几个 map 指令来实现这一点。map 指令允许您根据另一个变量的值设置自定义变量的值。这些指令位于 http 块中,但在 server 块之外
因此,您从一个引用受限访问文件开始,调整正则表达式以捕获所有文件,无需将 php 和 mvc 拆分为单独的映射:
map $request_uri $fastcgi_restricted {
~ ^(\/regex\/pattern\/) 1;
}
现在,如果请求 uri 与正则表达式匹配,则自定义变量$fastcgi_restricted
将包含值1
。Map 指令支持在您指定的值均不匹配的情况下设置默认值,但这是可选的,因此您不需要它。
现在,在下面添加另一个地图,使用客户端 IP 作为要检查的变量(如果您有一个很长的 IP 访问列表,那么地理指令会比地图更好,但对于少数人来说,地图就可以了):
map $remote_addr $fastcgi_access {
default $fastcgi_restricted;
1.2.3.4 0;
}
现在,如果客户端请求 uri 与上面的正则表达式不匹配,则第二个自定义变量$fastcgi_access
将为空;如果它与上面的正则表达式匹配但客户端 IP 为 1.2.3.4,则它将为 0;如果它与正则表达式匹配但客户端 IP 为其他任何值,则它将为 1。因此,基本上,如果它设置为 1,我们不想处理该请求。
Nginx 将空变量或值为 0 的变量视为 false。因此,在您的服务器块中、在您添加的所有位置块的顶部和外部:
if ($fastcgi_access) {
return 403;
}
此时,你会得到一个对 Nginx 有基本了解的人的必然回应,那就是“if is evil”,如果你敢把它包含在你的配置中的任何地方,世界就会灭亡,通常会有一个链接指向这一页。
您可以忽略这些人,因为他们显然没有阅读他们链接到的页面。需要注意的要点是:
指令 if 在位置上下文中使用时出现问题
和
在某些情况下,也可以将 if 移动到服务器级别(这是安全的,因为其中只允许其他重写模块指令)。
这正是我在这里概述的,这大致相当于如何使用 geoip 模块根据地理位置限制访问。现在您应该能够从配置中删除额外的 php 和 mvc 块,因为对受限制文件的请求在到达那里之前将被拒绝。