应用无法获取用户真实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。

相关推荐
蝎子莱莱爱打怪5 小时前
GitLab CI/CD + Docker Registry + K8s 部署完整实战指南
后端·docker·kubernetes
闲云一鹤8 小时前
nginx 快速入门教程 - 写给前端的你
前端·nginx·前端工程化
蝎子莱莱爱打怪3 天前
Centos7中一键安装K8s集群以及Rancher安装记录
运维·后端·kubernetes
何中应4 天前
Nginx转发请求错误
前端·后端·nginx
芝士雪豹只抽瑞克五4 天前
Nginx 高性能Web服务器笔记
服务器·nginx
阿里云云原生4 天前
Kubernetes 官方再出公告,强调立即迁移 Ingress NGINX
kubernetes
失重外太空啦4 天前
nginx
运维·nginx
至此流年莫相忘4 天前
Kubernetes实战篇之配置与存储
云原生·容器·kubernetes
天蓝不会忘记024 天前
lvs,haproxy,keepalived,nginx,tomcat介绍和实验
nginx·tomcat·lvs