![当位于一个或多个反向代理之后时,通过 X-Forwarded-For 检查 Varnish ACL](https://linux22.com/image/699428/%E5%BD%93%E4%BD%8D%E4%BA%8E%E4%B8%80%E4%B8%AA%E6%88%96%E5%A4%9A%E4%B8%AA%E5%8F%8D%E5%90%91%E4%BB%A3%E7%90%86%E4%B9%8B%E5%90%8E%E6%97%B6%EF%BC%8C%E9%80%9A%E8%BF%87%20X-Forwarded-For%20%E6%A3%80%E6%9F%A5%20Varnish%20ACL.png)
我在反向代理后面运行 Varnish(在本地主机上运行,用于 SSL 卸载)。代理设置 X-Forwarded-For 标头,如果标头已存在,则将自身添加到标头中。
当我进行 ACL 检查时,我当然希望检查原始客户端的 IP,而不是我的代理的 IP,所以我不能使用该client.ip
字段。使用std
vmod 我可以执行以下操作:
vcl 4.0;
import std;
sub vcl_recv {
if (std.ip(regsub(req.http.X-Forwarded-For, ", 127.0.0.1$", ""), "0.0.0.0") ~ my_acl) {
...do stuff...
}
}
换句话说,我在运行代理 IP (127.0.0.1) 并将std.ip
其与我的 ACL 进行比较之前,先从标头中修剪代理的 IP (127.0.0.1)。这很有效,除了...
如果在到达我的代理之前已经设置了 X-Forwarded-For 标头,则此操作会失败。在这种情况下,XFF 标头包含三个或更多 IP 地址。修剪最后一个 IP 地址后,仍会留下多个 IP 地址,并std.ip
因此而受阻,导致请求延迟几秒钟,当然也无法检查 ACL。
我需要确保在删除代理后,XFF 标头仅包含一个 IP(IPv4 或 IPv6)。这应该是客户端的 IP。
例如:
X-Forwarded-For: 10.10.1.1, 10.10.2.2, 2001:a031:100a:dead:beef:1234:1234:1234, 127.0.0.1
应该成为
X-Forwarded-For: 2001:a031:100a:dead:beef:1234:1234:1234, 127.0.0.1
由于我无法信任来自外部的任何 XFF 标头,因此我想丢弃代理看到的客户端 IP 以外的任何内容。我的代理不支持修改 XFF 标头,因此我需要在 Varnish 中执行此操作。
当我可以与标题进行交互时,Varnish 流程中的第一个点是在vcl_recv()
,此时 Varnish 已将其添加client.ip
到列表末尾。
我希望使用正则表达式将最后两项(IPv4 或 IPv6)捕获到编号的捕获组 ($1) 中,然后简单地用捕获组替换标头。如下所示:
vcl 4.0;
import std;
sub vcl_recv {
set req.http.X-Forwarded-For = regsub(req.http.X-Forwarded-For, "([a-f0-9:.]+, [a-f0-9:.]+)$", "\1");
}
第三个参数(替换正则表达式匹配的字符的字符串)不起作用。即使正则表达式仅捕获最后两个 IP 地址,生成的标头也与之前完全相同。
我怎样才能丢弃 XFF 标头中最后两个 IP 地址以外的任何内容?
答案1
您的正则表达式似乎没问题。我唯一看到的是您请求单独替换组。
如果你 ^(.*)
在正则表达式的开头添加并将第二个参数替换为\2
set req.http.X-Forwarded-For = regsub(req.http.X-Forwarded-For, "^(.*)([a-f0-9:.]+, [a-f0-9:.]+)$", "\2");
}
话虽如此,我认为更改 XFF 标头不是一个好主意。添加标头应该比更改和删除已通过中间代理传输的信息更标准。