Nginx 的real_ip模块允许您$remote_addr
根据特定标头字段中发送的值设置变量。它对X-Forwarded-For
标头有特殊的理解,并且能够使用标头中最右侧的不受信任的值作为连接 IP 地址。
我想使用 real_ip 模块来设置$remote_addr
连接 IP 地址。我遇到的问题是,我知道从要查看的末尾回溯多少个跳数X-Forwarded-For
,但不知道中间代理的 IP 地址。据我了解,这意味着我不能用来set_real_ip_from
指定代理的 IP 地址。
我希望能够做的是配置 nginx 以选择列表中倒数第二个地址作为$remote_addr
。似乎只有当您拥有知道代理的 IP 地址的基础设施时,real_ip 模块才会起作用。
有没有办法用 real_ip 模块来实现这一点?我已经想出了一个基于正则表达式的解决方案,但如果可能的话,我更愿意使用 real_ip 模块。
我不认为这是nginx real_ip_header 和 X-Forwarded-For 似乎错误或类似问题。重述这个问题:
- 我知道多少跳从末端开始连接的IP地址将是。
- 我不知道可信 IP 地址作为连接 IP 和我的服务器之间的中介代理,所以我无法使用
set_real_ip_from
。
有关具体细节的更多详细信息:
我在 Google Cloud 内部运行 nginx,位于 Google Cloud HTTP 负载均衡器后面。Google Cloud用途标X-Forwarded-For
头用于指示 Google Cloud 网络的入口点。我知道列表中倒数第二个值X-Forwarded-For
是我想要的值,但我不知道最后一个值(代理)的 IP 地址是什么。即使我可以枚举 Google Cloud 的所有代理地址空间(没有指定 GCLB 仅在 GCP 的地址空间内运行),也会向可以在该地址空间内获取服务器的任何其他用户开放。
答案1
我最终使用了基于正则表达式的版本。从我的 nginx 配置来看:
http {
# Regexes are:
# (?<connecting_ip>\d+\.\d+\.\d+\.\d+), (?<proxy_ip>\d+\.\d+\.\d+\.\d+)$ # IPv4 only
# (?<connecting_ip_x>[0-9a-f:.]+),\s*(?<proxy_ip>[0-9a-f:.]+)$ # IPv6 and IPv4, and more robust
#
# The last IP address is the one from the GCP front end load balancer
# The second to last IP address in the list is the connecting IP address (i.e. user IP address)
# We capture both of them. X-Forwarded-For is separated by commas, hopefully whitespace as well
# but we don't want to trust that too much.
#
# Note that ~ at the start of the string in Nginx marks it as a regex. It's not part
# of the regex.
#
# Test cases for regex101
# 1.1.1.1, 2.2.2.2
# 1.1.1.1, 2.2.2.2, 3.3.3.3
# 1.1.1.1
# ::ffff:130.211.1.102, 2.2.2.2
# 2001:0db8:85a3:0000:0000:8a2e:0370:7334, 2.2.2.2
# 2001:41d0:8:e8ad::1, 2600:1901:0:2ad2::
# 1.1.1.1,2.2.2.2,3.3.3.3
#
# It would be better to use the real_ip module, if that is possible
# https://serverfault.com/q/947835/334330 might get answered for this.
# Get the IP address of the connecting IP. If we get a direct connection from
# GCP's health checkers, there won't be an X-Forwarded-For header. We shouldn't
# be getting any direct connections from other sources without XFF header.
map $http_x_forwarded_for $connecting_ip {
# Capture the proxy IP and connecting_ip_x, then assign the connecting_ip_x
"~(?<connecting_ip_x>[0-9a-f:.]+),\s*(?<proxy_ip>[0-9a-f:.]+)$" $connecting_ip_x;
default $remote_addr;
}
# ...
}
然后我在服务器定义中使用 $connecting_ip:
server {
# ...
location / {
proxy_set_header X-Real-IP $connecting_ip;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Host "";
proxy_redirect off;
proxy_next_upstream error;
}
}
答案2
这不能用ngx_http_realip_module
本身来完成,因为正如 中real_ip_recursive
所述描述,只有标头字段中的最后一个地址(即,当设置为 时,您的 GFE 的地址off
)或最后一个不受信任的地址(即,当设置为 时,客户端的地址on
)才会匹配以替换$remote_addr
,因此,即使您$http_x_forwarded_for
通过字符串操作从中获取了以前的代理的地址并设置$set_real_ip_from
为它,模块也不会改变$remote_addr
。
因此,看起来您将不得不像$remote_addr
您提到的那样使用正则表达式手动重新定义自己,或者使用其他模块。
话虽如此,值得注意的是,您的负载均衡器的 GFE 地址应始终与35.191.0.0/16
或匹配130.211.0.0/22
区域网域名称。