Nginx 准确获取真实IP

41次阅读
没有评论

共计 3668 个字符,预计需要花费 10 分钟才能阅读完成。

背景

后端业务往往不期望关心细节,只期望能从上游 nginx 请求中获取到准确的真实IP,帮助进行更精准的日志分析和访问控制。

在实际的请求链路中,流量往往会经过多层代理。例如 用户 –> proxy1 –> proxy2 –>proxy* –> service。

不当的配置,往往会导致没能正确获取用户的真实IP。例如可能从 proxy2 中只获取到了 proxy1 的IP。

本文会聊聊 nginx 如何实现准确获取用户真实IP。

术语

在 nginx 中,有几个术语和 IP 相关:

  1. Remote Address
    • Remote Address 是 Nginx 在处理请求时直接获取到的客户端 IP 地址。
    • 它是 Nginx 内部的一个变量,可以在 Nginx 配置文件中直接使用 $remote_addr 来引用。
  2. X-Real-IP
    • X-Real-IP 是一个 自定义的HTTP 请求头字段。目前并不属于任何标准,代理和 Web 应用之间可以约定用任何自定义头来传递信息。
    • 通常被 HTTP 代理用来表示与它产生 TCP 连接的设备 IP,这个设备可能是其他代理,也可能是真正的请求端。
    • 在 Nginx 配置文件中,可以使用 $http_x_real_ip 变量来获取 X-Real-IP 的值。
  3. X-Forwarded-For:
    • X-Forwarded-For 也是一个 HTTP 请求头字段。
    • 当客户端的请求经过多层代理时,每个代理服务器都会在 X-Forwarded-For 头部追加客户端的 IP 地址。
    • 在 Nginx 配置文件中,可以使用 $http_x_forwarded_for 变量来获取 X-Forwarded-For 的值。

实践

若链路简单,例如:用户 –> proxy1 –> service。这样的话在 nginx 中 利用上述三种方式,其实都可以获取到用户真实IP。

下文以此图为示例,分析如何在每层代理都可以获取到用户真实IP:

​​Nginx 准确获取真实IP​​

若在 三台 proxy 上均使用 $remote_addr 获取IP,日志的IP会是:

[proxy1] 7.7.7.7
[proxy2] 192.168.2.1
[proxy3] 192.168.2.2

$remote_addr​ 只能获取到上一跳设备的真实IP。

在此案例中,下游的 proxy 已经无法获取上游客户端的真实IP。

若在 三台 proxy 上均使用 $http_x_forwarded_for 获取IP,日志的IP会是:

[proxy1] 7.7.7.7
[proxy2] 7.7.7.7, 192.168.2.1
[proxy3] 7.7.7.7, 192.168.2.1, 192.168.2.2

即每层代理都会在 $http_x_forwarded_for​ 追加上游的 IP。此时下游无法直接获取用户真实IP。当然后端服务可以获取 XFF 的第一个IP用于判断,不过我们尽量在代理层直接处理好细节最佳。

http_x_real_ip

由于 X-Real-IP​ 是一个 自定义的HTTP 请求头字段,它的值我们可以自行定义。

由于 proxy 1​中的 $remote_addr​ 可以获取到用户真实IP,那么我们可以将它传递给 $http_x_real_ip​ 。

这样就可以用 $http_x_real_ip​ 来准确获取用户真实IP了。

Nginx 准确获取真实IP

此时 proxy 1​ nginx 的配置范例为:

http {
    # 保留真实IP
    proxy_set_header    X-Real-IP  $remote_addr; #将 $remote_addr 传递给 X-Real-IP

    # 在日志中使用 $http_x_real_ip 保留客户端真实IP
    log_format  main  '$http_x_real_ip - $scheme [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for" "$host" "$upstream_addr" "$upstream_cache_status" $request_time $upstream_response_time';

    access_log  /data/logs/nginx/access.log  main;
}

由于 proxy 1​ 已经将真实IP传递给了 $http_x_real_ip​,在 proxy 2​ 和 proxy 3​ 中,不需要再进行传递即可直接获取。

即配置需剔除掉 proxy_set_header X-Real-IP $remote_addr;

此时后端服务可以从 $http_x_real_ip​ 获取到真实IP。

http_x_forwarded_for

上文已经说到, $http_x_forwarded_for​ 可以详细记录整个链路中出现的所有IP,且第一个IP即为用户的真实IP。

那么我们可以利用 nginx 的 ngx_http_realip_module​ 模块,将 $http_x_forwarded_for​ 中的真实IP进行提取,并传递给 $remote_addr​。

由于 proxy 1​ 的 $remote_addr​ 已经为真实IP,所以我们只需要在 proxy 3​中进行配置检索,即可将用户IP传递给后端服务。proxy 2​可配可不配,取决于自己需求。

proxy 1​ 和 proxy 2​、proxy 3​ 都需要配置 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;​用于传递。

X-Forwarded-For​ 头部字段中, IP 地址的添加顺序是这样的:

  • 当客户端第一次访问时, X-Forwarded-For​ 头部为空。
  • 当请求经过第一个代理服务器时, X-Forwarded-For​ 头部会被设置为代理服务器的 IP 地址。
  • 当请求经过第二个代理服务器时, X-Forwarded-For​ 头部会被设置为 “代理服务器2 IP, 代理服务器1 IP”。
  • 以此类推, X-Forwarded-For​ 头部会将经过的每个代理服务器的 IP 地址依次添加到最前面。

Nginx 准确获取真实IP

此时 proxy 3​ nginx 的配置范例为:

http {
    # 保留真实IP
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    # 在日志中使用 $http_x_real_ip 保留客户端真实IP
    log_format  main  '$http_x_real_ip - $scheme [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for" "$host" "$upstream_addr" "$upstream_cache_status" $request_time $upstream_response_time';

    access_log  /data/logs/nginx/access.log  main;

    server {
      ...
        location / {
            set_real_ip_from *.*.*.*/*;
            real_ip_header  X-Forwarded-For;
            real_ip_recursive of;
            ...
        }
    }
}

  1. set_real_ip_from *.*.*.*/*;
    • 这个指令用于指定一个或多个 IP 地址或 CIDR 块,这些地址或网段中的客户端 IP 地址将被视为真实的客户端 IP 地址。
    • 通常情况下,当 Nginx 位于负载均衡器或代理服务器之后时,客户端的真实 IP 地址会被这些中间设备覆盖掉,变成了中间设备的 IP 地址。这个指令就是用来告诉 Nginx 哪些 IP 地址或网段是可信的,Nginx 应该将这些地址视为客户端的真实 IP 地址。
  2. real_ip_header X-Forwarded-For;
    • 这个指令用于指定从哪个 HTTP 头部字段中获取客户端的真实 IP 地址。
    • 在负载均衡或代理的场景下,客户端的真实 IP 地址通常会被写入 X-Forwarded-For​ 头部字段。这个指令就是告诉 Nginx 从这个头部字段中获取客户端的真实 IP 地址。
  3. real_ip_recursive;
    • 这个指令用于控制 Nginx 在解析 X-Forwarded-For​ 头部字段时的行为。
    • 当设置为 on​ 时,Nginx 会递归地解析 X-Forwarded-For​ 头部字段,取最后一个非私有 IP 地址作为客户端的真实 IP 地址,传递给​$remote_addr​。
    • 当设置为 off​ 时,Nginx 会直接使用 X-Forwarded-For​ 头部字段中的第一个 IP 地址作为客户端的真实 IP 地址,传递给​$remote_addr​。

此时后端服务可以从 $remote_addr​ 获取到真实IP。

总结

文章中描述的是通常情况下对于真实IP的获取方法。

若链路中有恶意伪造 X-Forwarded-For​ 请求头的现象时,我们尽量配置好 set_real_ip_from​限定可信的IP源,可以在一定程度上防止 $remote_addr​ 被篡改。

正文完
 
pengyinwei
版权声明:本站原创文章,由 pengyinwei 2024-04-26发表,共计3668字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处:https://www.opshub.cn
评论(没有评论)