应用无法获取用户真实ip问题排查

现象

领导反馈生产环境的用户ip有问题。登陆到这个页面,发现是所有的用户ip都是172.30.94.97,这是个内部网络ip.

排查过程

1 登陆到应用前端nginx, 查看nginx的请求日志

shell 复制代码
172.30.94.97 - - [17/Jul/2024:02:02:54 +0000] "POST /***/notify/my-page HTTP/1.1" 200 182 "/report/home?type=2&id=2612&lang=zh_CN" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36" "10.72.44.200"
172.30.94.97 - - [17/Jul/2024:02:02:54 +0000] "POST /***/notify/my-page HTTP/1.1" 200 182 "/home?lang=zh_CN" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36" "10.49.140.102"
172.30.94.97 - - [17/Jul/2024:02:02:56 +0000] "POST /***/msg/notify/my-page HTTP/1.1" 200 59 "/user/message/info?lang=zh_CN" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36" "10.13.52.192"

发现第一列展示的ip正好是我们的Java应用代码拿到的iP,而真实的ip展示在最后一列

2 查看nginx的日志输出格式。第一列取的是remote_addr变量,说明这个变量是有问题的 。我们要取的是最后一列http_x_forwarded_for变量

shell 复制代码
log_format  main   '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

3 查看Java代码获取客户ip的逻辑。Java代码从6个Header变量中依次找可以用的ip。

java 复制代码
    public static String getClientIP(HttpServletRequest request, String... otherHeaderNames) {
        String[] headers = new String[]{"X-Forwarded-For", "X-Real-IP", "Proxy-Client-IP", "WL-Proxy-Client-IP", "HTTP_CLIENT_IP", "HTTP_X_FORWARDED_FOR"};
        if (ArrayUtil.isNotEmpty(otherHeaderNames)) {
            headers = (String[])ArrayUtil.addAll(new String[][]{headers, otherHeaderNames});
        }

        return getClientIPByHeader(request, headers);
    } 

4 查看nginx传递过来了哪些Header变量。nginx传递过来了X-Real-IP和X-Forwarded-For,其中X-Real-IP取的是有问题的remote_addr。正好我们Java代码取到的是这个变量。

shell 复制代码
location ~ ^/(admin-api|rpc-api)/ {
    client_max_body_size 32m;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_pass http://***:30001;
}

5 根据第二步的分析,将remote_addr修改为http_x_forwarded_for

6 重启nginx,问题解决

K8S网络拓扑

所有的外部流量一定会通过一个ingress controller进入到K8S的内部。ingress controller的一个常见实现是Nginx,正好我们的k8s选择的就是Nginx。也就是说我们的业务前端nginx前面还有一个nginx。所以我们的前端nginx的remote_addr拿到的是k8s入口ingress的内部ip地址。

总结

用户请求经过两层nginx转发才到达后端java业务应用。remote_addr仅存储上一个转发节点的ip,所以我们的业务应用一直拿的是ingress的ip。http_x_forwarded_for存储的是原始用户的请求ip。

相关推荐
Hui Baby17 小时前
K8S使用CRD和控制器简单应用
云原生·容器·kubernetes
BelongPanda1 天前
Linux Nginx 纯手动 Let‘s Encrypt 泛域名证书配置教程
linux·nginx
Zhu7581 天前
在k8s环境部署Apache Superset最新版
容器·kubernetes·apache
Zhu7581 天前
在k8s环境部署Apache zookeeper3.9.5,高可用,多pod
容器·kubernetes·apache
运维大师1 天前
【K8S 运维实战】07-Pod生命周期与故障排查
运维·容器·kubernetes
郝亚军1 天前
nginx的三个基础库:PCRE、OpenSSL、Zlib的安装
linux·服务器·nginx
CodexDave1 天前
MySQL事务隔离级别与MVCC机制解析
前端·数据库·mysql·nginx·性能优化·负载均衡
easy_coder2 天前
K8s Pod 创建失败深度解析:从 IPAM 400 错误到子网资源耗尽
云原生·容器·kubernetes
heimeiyingwang2 天前
【架构实战】可观测性三支柱:日志、指标、链路的融合
elasticsearch·架构·kubernetes
CodexDave2 天前
数据库连接池耗尽:排查顺序与三层兜底
服务器·前端·数据库·git·云原生·容器·kubernetes