坑系列之阿里SLB上获取客户IP

栏目: 服务器 · Nginx · 发布时间: 5年前

内容简介:好久没更新了,正好上周遇到一个获取不到客户端IP的BUG,开发环境用nginx做反代都是work的。上到生产环境就获取不到。思来想去就是生产上多了一个SLB负载均衡。但这是一个老的功能,之前也都是好的,突然就拿的不对了,非常之诡异。为了确认不是代码的问题,我们使用tcpdump在服务结点上抓包。发现报文头的X-Real-IP是一个VPC的内网地址,说明在我们的nginx中获取的

好久没更新了,正好上周遇到一个获取不到客户端IP的BUG,开发环境用nginx做反代都是work的。上到生产环境就获取不到。思来想去就是生产上多了一个SLB负载均衡。但这是一个老的功能,之前也都是好的,突然就拿的不对了,非常之诡异。

故障重现

为了确认不是代码的问题,我们使用tcpdump在服务结点上抓包。

GET /api/foo/bar HTTP/1.1
remoteip: 122.xx.xx.xx
x-forwarded-for: 122.xx.xx.xx, 10.130.0.1
accept: application/json, text/plain, */*
dnt: 1
user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36
referer: https://app.example.com/login
accept-language: zh-CN,zh;q=0.9
x-forwarded-host: app.example.com,app.example.com
x-forwarded-port: 80,80
x-forwarded-proto: http,http
x-request-id: d74323d45afd4609977eb233d59f9a9e
x-trace-id: d74323d45afd4609977eb233d59f9a9e
x-real-ip: 10.130.0.1
x-locale: zh_CN
host: app.example.com
Accept-Encoding: gzip
Content-Length: 0
Connection: Keep-Alive

发现报文头的X-Real-IP是一个VPC的内网地址,说明在我们的nginx中获取的 $remote_addr 就是 10.130.0.1 ,是SLB的地址。

nginx配置如下:

http {
		server {
		    listen       80;
		    server_name  192.168.50.88;
		    root  /usr/local/var/www/html;
		
		    location /api {
		        proxy_set_header X-Real-IP $remote_addr;  #将remode_addr写入Http Header
			    	proxy_pass http://backend_hosts;
		    }
		}
		
		upstream backend_hosts {
		    server 127.0.0.1:8080;
		}
}

配置很简单,没有使用realip模块,直接将 remote_addr 认定为客户端ip。大家知道 remote_addr 不是http头,不容易伪造。它是服务端与客户端建立socket连接时,从客户端直接获取的。但是为什么这里获取的ip却是SLB自身的IP呢?

故障分析

再仔细分析抓包内容,发现其实报文中是包含客户端原始IP的,分别在 x-forwarded-forremoteip 上。这里 x-forwarded-for 的值引起了我们的注意,如果是用nginx原始的 $proxy_add_x_forwarded_for 参数,客户端IP应该会放在最后,但是这里在第一位,说明SLB对这个头做过处理。

找到devops询问是否更改过SLB的配置,发现确实做过调整。为了直接在SLB实现http到https的重定向,将原本的4层负载均衡(tcp)换成了7层负载均衡(http)。试着将SLB恢复原有配置,可以获取客户端IP。最终问题定位到SLB的配置上。

再次阅读 SLB手册 ,发现以下描述:

负载均衡提供获取客户端真实IP地址的功能,该功能默认是开启的。

四层负载均衡(TCP协议)服务可以直接在后端ECS上获取客户端的真实IP地址,无需进行额外的配置。

七层负载均衡(HTTP/HTTPS协议)服务需要对应用服务器进行配置,然后使用X-Forwarded-For的方式获取客户端的真实IP地址。

真实的客户端IP会被负载均衡放在HTTP头部的X-Forwarded-For字段,格式如下:

X-Forwarded-For: 用户真实IP, 代理服务器1-IP, 代理服务器2-IP,…

当使用此方式获取客户端真实IP时,获取的第一个地址就是客户端真实IP。

查看SLB配置页面确实也如文档所说

坑系列之阿里SLB上获取客户IP

至于 remote_addr 获取到SLB的IP也就很容易理解了,当没有上级代理没有透传tcp连接时, remote_addr 获取的就是上一层代理的ip地址。

故障恢复

既然定位到问题了,那么需要着手解决,改回4层LB是不现实的。

阿里云其实提供了两个方案:

  1. 按照文档上说的,获取X-Forwarded-For的第一段IP即为客户真实IP
  2. 通过抓包发现SLB会添加一个remoteip的头,直接使用就行

我们偷个懒,直接用第二种,在nginx将remoteip塞到X-Real-IP上,这样不用打hotfix即可修复问题。

花絮

其实在故障恢复的过程中,本想在本地复现的。过程就是用nginx搭建一个4层负载代理到7层负载最终到服务。如下图所示

+------------------+      +----------------+      +---------------+    +----------------+
|                  |      |                |      |               |    |                |
|                  |      |                |      |               |    |                |
|      Client      +------>    TCP LB      +----->+     HTTP LB   +---->     SERVER     |
|                  |      |                |      |               |    |                |
|                  |      |                |      |               |    |                |
+------------------+      +----------------+      +---------------+    +----------------+

tcp负载的配置如下:

stream {
    upstream tcp_proxy {
        server 127.0.0.1:80;
    }
    server {
        listen 88;
        proxy_connect_timeout 1s;
        proxy_timeout 300s;
        proxy_pass tcp_proxy;
    }
}

最终发现nginx的tcp代理有个巨大的坑,就是无法透传 remote_addr ,如果tcp代理跳过http直连服务,获取到的remote_addr就是127.0.0.1这个本机地址。

翻了翻文档,发现还真有 官方说明

简而言之,就是要买nginx-plus,里面有个 proxy_bind $remote_addr transparent; 可以实现透传功能,满满的套路。

总结

DevOps有的时候真的会影响到业务,不同环境不同配置造成难以预料的影响。虽然我们的服务都已经实现了容器化。但是对于这些PaaS组件如何统一配置并且将配置代码化,让多个环境(包括开发,测试,staging)保持一致还是挺值得研究的话题。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

JSP应用开发技术

JSP应用开发技术

柳永坡 / 人民邮电出版社 / 2005-9 / 52.00元

本书全面系统地介绍了JSP应用开发技术,包括JSP预备知识和环境配置、JSP编程基础、JSP应用开发进阶、在JSP中使用数据库、Servlet技术、标签库和表达式语言、Web编程模式和应用框架等几个方面的内容。本书不但由浅入深地介绍了JSP程序设计的原理、方法和技术,还提供了大量的JSP应用开发实例,给出了相应的实用技巧、操作步骤及优化思路。 本书着重于JSP技术的应用性和可操作性,......一起来看看 《JSP应用开发技术》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

MD5 加密
MD5 加密

MD5 加密工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具