从下游代理解析用户IP
Resolve user IP from downstream proxies
我们以前 运行 互联网和我们的微服务之间的单个 Nginx 反向代理,配置如下:
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
其中的请求通过 header 管道通过,例如:
User -> ALB [nginx] -> App Servers
IP: 1.2.3.4 IP: 172.31.1.1 IP: n/a
Forwarded-For: 1.2.3.4 Real-IP: 1.2.3.4
Forwarded-For: 1.2.3.4, 172.31.1.1
但是现在我们需要扩展弹性 LB 后面的 ALB,我们发现额外的代理层存在问题,例如:
User -> ELB -> ALB [nginx] -> App Servers
IP: 1.2.3.4 IP: 172.31.1.2 IP: 172.31.1.1 IP: n/a
Forwarded-For: 1.2.3.4 Forwarded-For: 1.2.3.4, 172.31.1.2 Real-IP: 172.31.1.2
Forwarded-For: 1.2.3.4, 172.31.1.2
但如您所见,目前只是将 X-Real-IP:
设置为 ELB 的 IP。
我们需要能够剥离受信任的代理并在 X-Real-IP
header 中发送正确的用户 IP,并记录用户 IP 而不是 ELB IP。
GeoIP 模块具有 geoip_proxy
指令,这些指令定义了在确定“真实”IP 时要忽略的受信任代理,我想知道 nginx 中是否有类似的东西或其他一些方法来完成这个?
TIA
简短的回答是,没有一个简单的配置指令可以做到这一点,也没有 100% 可靠的方法来只信任某些代理。
让我们来构建一个例子。我的 IP 是 1.2.3.4
,但我是恶意的,我想假装我是 5.6.7.8
。我通过浏览器扩展注入我自己的 X-Forwarded-For: header 并且 nginx 框得到:
X-Forwarded-For: 5.6.7.8, 1.2.3.4, 172.31.1.1, 172.31.1.2
1。简单,但有缺陷
map $proxy_add_x_forwarded_for $x_real_ip {
"~^([^,]+).*" ;
default $remote_addr;
}
所有这一切只是剥离 X-Forwarded-For:
header 的第一个 IP 地址,如果您不介意用户通过 [=73= 欺骗其他 IP 地址,这一切都很好】 注射。在上面的例子中,这个方法将 return 欺骗 5.6.7.8
.
2。有点复杂,但大多数情况下可以接受
理想情况下,我们只想剥离两个受信任的代理并使用第一个不受信任的 IP。为此,我们将不得不对正则表达式进行一些创意:
map $proxy_add_x_forwarded_for $x_real_ip {
"~(?:^|, )([^,]+), (?:10\.|172\.(?:1[6-9]|2[0-9]|3[01])\.).*" ;
default $remote_addr;
}
大约一半的正则表达式只是处理 172.16.0.0/12 网络没有在八位字节边界上干净地分割这一事实,但它确实起到了作用。对于上面的例子,它正确 returns 1.2.3.4
.
但是,如果外部攻击者不知何故知道这个 kludgey 配置正在使用 并且 也知道可信网络是什么,他们可以设置以下 header 来绕过它:
X-Forwarded-For: 5.6.7.8, 172.16.0.1
最终在代理处为:
X-Forwarded-For: 5.6.7.8, 172.16.0.1, 1.2.3.4, 172.31.1.1, 172.31.1.2
由于正则表达式本质上是读取 left-to-right 和 return 将 IP 发送到第一个受信任 IP 的左侧,在这种特定情况下,它将 return 欺骗 5.6.7.8
知识产权。但是,这是一个极端情况,对于我的特定用途 YMMV 来说是可以接受的。
警告: 您可能会收到一条错误消息,提示“您应该增加 map_hash_bucket_size”,这意味着您需要增加该值以适应那个 bigass 正则表达式。但是,the docs on that 有点繁琐,并且谈论“对齐”很重要,所以如果您没有在其他地方设置该值,我建议将消息中引用的值加倍。在我的例子中,我将它从 64 翻倍到 128。
3。一个真正合适的、防弹的解决方案
恕我直言,这需要实际解析 header 并应用真正的逻辑,因此需要将其修补到 nginx 中或写入模块中。基本上移植 GeoIP 模块用于 geoip_proxy
和 geoip_proxy_recursive
.
的相同逻辑
或者,您可以制作您的应用程序 proxy-aware 并在那里实现逻辑。如果您知道如何正确处理 IP 和子网,那应该是小菜一碟。不幸的是,在这种情况下我没有可用的选项。
谢谢: 如果不是 IRC 上的 membear 提醒我正则表达式捕获组在 map{}
块内有效,我可能仍在旋转我的轮子.
不要脸的外挂:我原来写过a blog post这个,才想起来在这里问过。它大部分是相同的,但也有那个大正则表达式的更详细的细分。
我们以前 运行 互联网和我们的微服务之间的单个 Nginx 反向代理,配置如下:
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
其中的请求通过 header 管道通过,例如:
User -> ALB [nginx] -> App Servers
IP: 1.2.3.4 IP: 172.31.1.1 IP: n/a
Forwarded-For: 1.2.3.4 Real-IP: 1.2.3.4
Forwarded-For: 1.2.3.4, 172.31.1.1
但是现在我们需要扩展弹性 LB 后面的 ALB,我们发现额外的代理层存在问题,例如:
User -> ELB -> ALB [nginx] -> App Servers
IP: 1.2.3.4 IP: 172.31.1.2 IP: 172.31.1.1 IP: n/a
Forwarded-For: 1.2.3.4 Forwarded-For: 1.2.3.4, 172.31.1.2 Real-IP: 172.31.1.2
Forwarded-For: 1.2.3.4, 172.31.1.2
但如您所见,目前只是将 X-Real-IP:
设置为 ELB 的 IP。
我们需要能够剥离受信任的代理并在 X-Real-IP
header 中发送正确的用户 IP,并记录用户 IP 而不是 ELB IP。
GeoIP 模块具有 geoip_proxy
指令,这些指令定义了在确定“真实”IP 时要忽略的受信任代理,我想知道 nginx 中是否有类似的东西或其他一些方法来完成这个?
TIA
简短的回答是,没有一个简单的配置指令可以做到这一点,也没有 100% 可靠的方法来只信任某些代理。
让我们来构建一个例子。我的 IP 是 1.2.3.4
,但我是恶意的,我想假装我是 5.6.7.8
。我通过浏览器扩展注入我自己的 X-Forwarded-For: header 并且 nginx 框得到:
X-Forwarded-For: 5.6.7.8, 1.2.3.4, 172.31.1.1, 172.31.1.2
1。简单,但有缺陷
map $proxy_add_x_forwarded_for $x_real_ip {
"~^([^,]+).*" ;
default $remote_addr;
}
所有这一切只是剥离 X-Forwarded-For:
header 的第一个 IP 地址,如果您不介意用户通过 [=73= 欺骗其他 IP 地址,这一切都很好】 注射。在上面的例子中,这个方法将 return 欺骗 5.6.7.8
.
2。有点复杂,但大多数情况下可以接受
理想情况下,我们只想剥离两个受信任的代理并使用第一个不受信任的 IP。为此,我们将不得不对正则表达式进行一些创意:
map $proxy_add_x_forwarded_for $x_real_ip {
"~(?:^|, )([^,]+), (?:10\.|172\.(?:1[6-9]|2[0-9]|3[01])\.).*" ;
default $remote_addr;
}
大约一半的正则表达式只是处理 172.16.0.0/12 网络没有在八位字节边界上干净地分割这一事实,但它确实起到了作用。对于上面的例子,它正确 returns 1.2.3.4
.
但是,如果外部攻击者不知何故知道这个 kludgey 配置正在使用 并且 也知道可信网络是什么,他们可以设置以下 header 来绕过它:
X-Forwarded-For: 5.6.7.8, 172.16.0.1
最终在代理处为:
X-Forwarded-For: 5.6.7.8, 172.16.0.1, 1.2.3.4, 172.31.1.1, 172.31.1.2
由于正则表达式本质上是读取 left-to-right 和 return 将 IP 发送到第一个受信任 IP 的左侧,在这种特定情况下,它将 return 欺骗 5.6.7.8
知识产权。但是,这是一个极端情况,对于我的特定用途 YMMV 来说是可以接受的。
警告: 您可能会收到一条错误消息,提示“您应该增加 map_hash_bucket_size”,这意味着您需要增加该值以适应那个 bigass 正则表达式。但是,the docs on that 有点繁琐,并且谈论“对齐”很重要,所以如果您没有在其他地方设置该值,我建议将消息中引用的值加倍。在我的例子中,我将它从 64 翻倍到 128。
3。一个真正合适的、防弹的解决方案
恕我直言,这需要实际解析 header 并应用真正的逻辑,因此需要将其修补到 nginx 中或写入模块中。基本上移植 GeoIP 模块用于 geoip_proxy
和 geoip_proxy_recursive
.
或者,您可以制作您的应用程序 proxy-aware 并在那里实现逻辑。如果您知道如何正确处理 IP 和子网,那应该是小菜一碟。不幸的是,在这种情况下我没有可用的选项。
谢谢: 如果不是 IRC 上的 membear 提醒我正则表达式捕获组在 map{}
块内有效,我可能仍在旋转我的轮子.
不要脸的外挂:我原来写过a blog post这个,才想起来在这里问过。它大部分是相同的,但也有那个大正则表达式的更详细的细分。