当您不知道中间代理 IP 地址时使用 Nginx real_ip

当您不知道中间代理 IP 地址时使用 Nginx real_ip

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 区域网域名称

相关内容