背景
使用 istio 为工作负载注入 sidecar 后,envoy 会代理工作负载的所有网络请求,导致工作负载收到的流量来自 envoy 的转发,因此工作负载看到的客户端 IP 是 envoy 的,默认是 127.0.0.6
,不是真实的客户端地址。
某些 app 是需要获取到真实客户端地址的,比如黑白名单、地理位置判断等等。
解决方案
早期的 istio 版本是比较难解决的,但新版本中已经很容易解决,本文中的 istio 是 1.11.2 版本,只需要使用下面的 HTTP Header 即可获取到客户端真实的 IP 地址:
- $http_x_original_forwarded_for
- $http_remoteip
两个的区别是,$http_x_original_forwarded_for
会包含所有中间代理的地址,而
$http_remoteip
仅包含最近一个代理的地址。
举个例子:当工作负载前方有 CDN、WAF、SLB 等代理时,$http_x_original_forwarded_for
会包含这些代理的所有地址,逐个从左到右增加到列表中。而$http_remoteip
仅包含最近的一个代理地址,即 SLB 的地址。
但正常情况下,真实的客户端 IP 是在最前端的,即应该在$http_x_original_forwarded_for
的左边第一位,所以还需要做处理,可参考下面的方式进行处理:
map $http_x_original_forwarded_for $clientRealIp { "" $http_remoteip; # if $http_x_original_forwarded_for is null, then $clientRealIp=$http_remoteip
~^(?P<firstAddr>[0-9\.]+),?.*$ $firstAddr; # if $http_x_original_forwarded_for is not null, get the first ip add and put it to $clientRealIp. }
$clientRealIp
将是真实的客户端 ip 地址。
结果验证
通过下面的命令,虚拟 3 个 ip 到X-Forwarded-For
中进行测试
curl -H 'X-Forwarded-For: 56.5.6.7, 72.9.5.6, 98.1.2.3' http://test-bmp-admin-front.aitsp.ltd/bmpadmin/user/login
可以看到 nginx 日志中将打印最前面的 56.5.6.7
的 ip 地址
image
如果本机直接访问页面,将打印本机的公网 ip 地址,如下图:
image