1. 导言

绝大多数业务场景都是需要知道客户端IP的

在k8s中运行的业务项目,如何获取到客户端真实IP?

本文总结了通行的2种方式

要答案的直接看方式一、方式二和总结

SEO 关键字

nginx ingress客户端真实ip

kubernets获取客户端真实ip

rke获取客户端真实ip

rancher获取客户端真实ip

本文由 www.iamle.com 流水理鱼 原创,wx公众号同名

1.1 流量链路介绍

7层转发链路 Client(客户端) > Nginx > K8s Ingress(Nginx ingress)

4层转发链路 Client(客户端) > 公有云LB > K8s Ingress(Nginx ingress)

ps: 实际业务会串联更多层级的转发。WAF、CDN、Api Gateway一般是http 7层转发,LB一般是4层tcp转发

1.2 准备whoami探针

whomai是一个go编写的调试探针工具,回显http头信息

在k8s中部署一个containous/whoami用来作为探针,配置好ingress公网和访问,这样客户端web访问可以看到基本的http头信息,方便调试

kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: whoami
namespace: default
labels:
app: whoami
spec:
replicas: 1
selector:
matchLabels:
app: whoami
template:
metadata:
labels:
app: whoami
spec:
containers:
- image: containous/whoami
imagePullPolicy: Always
name: whoami
ports:
- containerPort: 80
name: 80tcp02
protocol: TCP
dnsPolicy: ClusterFirst
restartPolicy: Always
EOF

ps:ingress自行增加

客户端web访问,回显http头示例

Hostname: whoami-65b8cc4b-6vwns
IP: 127.0.0.1
IP: 10.42.2.12
RemoteAddr: 10.42.1.0:47850
GET / HTTP/1.1
Host: whoami.iamle.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.162 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7,zh-TW;q=0.6,la;q=0.5
Cookie: _ga=GA1.2.30707523.1570429261;
Dnt: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: cross-site
Sec-Fetch-User: ?1
Upgrade-Insecure-Requests: 1
X-Forwarded-For: 8.8.8.8, 10.0.0.1
X-Forwarded-Host: whoami.iamle.com
X-Forwarded-Port: 443
X-Forwarded-Proto: https
X-Original-Forwarded-For: 8.8.8.8
X-Original-Uri: /
X-Real-Ip: 8.8.8.8
X-Request-Id: 3852c9780589ffba4c1f9f2785691d5f
X-Scheme: https

2. 两种方式 7层http头X-Forwarded-For透传 和 4层Proxy Protocol透传

获得客户端真实IP有针对7层和针对4层两种方式

2.1 7层http头X-Forwarded-For透传介绍

http工作在网络第7层,http中有个X-Forwarded-For字段

大部分CDN、WAF、LB用X-Forwarded-For字段来存客户端IP,也有用X-Real-Ip字段,cloudflare、百度云加速还扩展了CF-Connecting-IP字段

标准数据为

X-Forwareded-For:Client,proxy1,proxy2,proxy3……

第一个ip是客户端ip,后面的proxy为路过一层就加一层的ip

这里的proxy可以是WAF、CDN、LB、Api Gateway等

2.2 4层Proxy Protocol透传

tcp工作在网络第4层,Proxy Protocol就是在tcp中增加一个小的报头,用来存储额外的信息

代理协议即 Proxy Protocol,是haproxy的作者Willy Tarreau于2010年开发和设计的一个Internet协议,通过为tcp添加一个很小的头信息,来方便的传递客户端信息(协议栈、源IP、目的IP、源端口、目的端口等),在网络情况复杂又需要获取客户IP时非常有用。

其本质是在三次握手结束后由代理在连接中插入了一个携带了原始连接四元组信息的数据包。

目前 proxy protocol有两个版本,v1仅支持human-readable报头格式(ASCIII码),v2需同时支持human-readable和二进制格式,即需要兼容v1格式

proxy protocol的接收端必须在接收到完整有效的 proxy protocol 头部后才能开始处理连接数据。因此对于服务器的同一个监听端口,不存在兼容带proxy protocol包的连接和不带proxy protocol包的连接。如果服务器接收到的第一个数据包不符合proxy protocol的格式,那么服务器会直接终止连接。

Proxy protocol是比较新的协议,但目前已经有很多软件支持,如haproxy、nginx、apache、squid、mysql等等,要使用proxy protocol需要两个角色sender和receiver,sender在与receiver之间建立连接后,会先发送一个带有客户信息的tcp header,因为更改了tcp协议头,需receiver也支持proxy protocol,否则不能识别tcp包头,导致无法成功建立连接。

nginx是从1.5.12起开始支持的

3. 方式一 X-Forwarded-For配置

适用于7层http转发

3.1 NGINX Ingress Controller X-Forwarded-For配置

查看NGINX Ingress Controller的ConfigMaps配置文档,可以找到以下配置项

use-forwarded-headers

如果为true,NGINX会将传入的 X-Forwarded-* 头传递给upstreams。当NGINX位于另一个正在设置这些标头的 L7 proxy / load balancer 之后时,请使用此选项。

如果为false,NGINX会忽略传入的 X-Forwarded-* 头,用它看到的请求信息填充它们。如果NGINX直接暴露在互联网上,或者它在基于 L3/packet-based load balancer 后面,并且不改变数据包中的源IP,请使用此选项。

ps: NGINX Ingress Controller直接暴露互联网也就是Edge模式不能开启为true,否则会有伪造ip的安全问题。也就是k8s有公网ip,直接让客户端访问,本配置不要设为true!

forwarded-for-header

设置标头字段以标识客户端的原始IP地址。 默认: X-Forwarded-For

ps:如果 NGINX Ingress Controller 在CDN,WAF,LB等后面,设置从头的哪个字段获取IP,默认是X-Forwarded-For

这个配置应该和use-forwarded-headers配合使用

compute-full-forwarded-for

将远程地址附加到 X-Forwarded-For 标头,而不是替换它。 启用此选项后,upstreams应用程序将根据其自己的受信任代理列表提取客户端IP

修改configmap nginx-configuration配置

kubectl -n ingress-nginx edit cm nginx-configuration

在apiVersion: v1下,kind: ConfigMap上加入

data:
compute-full-forwarded-for: "true"
forwarded-for-header: "X-Forwarded-For"
use-forwarded-headers: "true"

或者直接apply附加配置

kubectl apply -f - <<EOF
apiVersion: v1
data:
compute-full-forwarded-for: "true"
forwarded-for-header: X-Forwarded-For
use-forwarded-headers: "true"
kind: ConfigMap
metadata:
labels:
app: ingress-nginx
name: nginx-configuration
namespace: ingress-nginx
EOF

ps:如果nginx-configuration不在namespace ingress-nginx中就在namespace kube-system中找

3.2 Nginx作为边缘节点(Edge)配置

作为Edge需要重写remote_addr,保证了客户端IP不会被伪造

必须:X-Forwarded-For 重写为 $remote_addr

非必须扩展:X-Real-IP 重写为 $remote_addr

upstream wwek-k8s {
server 8.8.8.8:443;
server 8.8.8.7:443;
server 8.8.8.6:443;
} map $http_upgrade $connection_upgrade {
default Upgrade;
'' close;
}
server {
if ($http_x_forwarded_proto = '') {
set $http_x_forwarded_proto $scheme;
}
location / { proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Port $server_port;
#proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Real-IP $remote_addr; proxy_pass https://wwek-k8s;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_read_timeout 900s;
proxy_buffering off;
}
}

3.3 X-Forwarded-For是否可以伪造

客户端是否能伪造IP,取决于边缘节点(Edge)是如何处理X-Forwarded-For字段的。

客户端直接连接的首个proxy节点都叫做边缘节点(Edge),不管是网关、CDN、LB等只要这一层是直接接入客户端访问的,那么他就是一个边缘节点。

不重写-不安全的边缘节点(Edge)

边缘节点如果是透传http头中的X-Forwarded-For字段,那么这个就是不安全的,客户端可以在http中实现包含X-Forwarded-For字段值,这个值又被透传了。

#不安全
X-Forwareded-For:Client(Edge不重写,只透传),proxy1,proxy2,proxy3……

重写-安全的边缘节点(Edge)

边缘节点(Edge)如果重写remote_addr到X-Forwarded-For,那么这就是安全的。边缘节点(Edge)获取的remote_addr就是客户端的真实IP

#安全
X-Forwareded-For:Client(Edge获取的remote_addr),proxy1,proxy2,proxy3……

4. 方式二 Proxy Protocol 配置实例

适用于4层tcp转发

公有云的负载均衡LB一般都支持Proxy Protocol

查看NGINX Ingress Controller的ConfigMaps配置文档,可以找到如何配置Proxy Protocol

use-proxy-protocol

启用或禁用roxy Protocol,以接收通过代理服务器和负载均衡器(例如HAProxy和Amazon Elastic Load Balancer(ELB))传递的客户端连接(真实IP地址)信息。

NGINX Ingress Controller 作为receiver角色 Proxy Protocol配置

kubectl -n ingress-nginx edit cm nginx-configuration

在apiVersion: v1下,kind: ConfigMap上加入

data:
use-proxy-protocol: "true"

或者直接apply附加配置

kubectl apply -f - <<EOF
apiVersion: v1
data:
use-proxy-protocol: "true"
kind: ConfigMap
metadata:
labels:
app: ingress-nginx
name: nginx-configuration
namespace: ingress-nginx
EOF

ps: 注意需要上一层LB支持Proxy Protocol,才能这么配置,否则会导致无法链接

5. 总结

7层http头X-Forwarded-For透传

链路proxy有透传X-Forwarded-For

访问链路上多层proxy,任意一个节点不支持Proxy Protocol

4层协议Proxy Protocol透传

上下游可控都支持Proxy Protocol协议

链路proxy中丢失了http头

https反向代理http(某些情况下由于Keep-alive导致不是每次请求都传递x-forword-for

应该用那种方式?

7层用X-Forwarded-For,4层用Proxy Protocol

如果链路的边缘节点(Edge)X-Forwarded-For字段是安全的,建议用X-Forwarded-For

如果链路proxy全路径都支持Proxy Protocol,那么建议用Proxy Protocol

如果有4层tcp业务应用,那么获取客户端IP就的用Proxy Protocol

总之搞清楚了这2种方式的原理按照场景选择

5. 参考

Kubernets中获取客户端真实IP总结的更多相关文章

  1. Tomcat 8.5中获取客户端真实IP及协议

    获取客户端真实IP ServletRequest接口提供了getRemoteAddr方法用于获取客户端IP,但是当客户端通过代理服务器访问后端服务器的时候,服务器调用getRemoteAddr方法会返 ...

  2. Java Web 获取客户端真实IP

    Java Web 获取客户端真实IP 发生的场景:服务器端接收客户端请求的时候,一般需要进行签名验证,客户端IP限定等情况,在进行客户端IP限定的时候,需要首先获取该真实的IP.一般分为两种情况: 方 ...

  3. 获取客户端真实IP地址

    Java-Web获取客户端真实IP: 发生的场景:服务器端接收客户端请求的时候,一般需要进行签名验证,客户端IP限定等情况,在进行客户端IP限定的时候,需要首先获取该真实的IP. 一般分为两种情况: ...

  4. nginx 代理模式下,获取客户端真实IP

    最近做博友推荐,发现个小问题,用$_SERVER['REMOTE_ADDR'];得到的都是服务器的地址192.168.96.52,搜索了一下,发现问题,改为$_SERVER['HTTP_X_REAL_ ...

  5. Java 获取客户端真实IP地址

    本文基于方法 HttpServletRequest.getHeader 和 HttpServletRequest.getRemoteAddr 介绍如何在服务器端获取客户端真实IP地址. 业务背景 服务 ...

  6. Nginx反向代理后应用程序获取客户端真实IP

    Nginx反向代理后,Servlet应用通过request.getRemoteAddr()取到的IP是Nginx的IP地址,并非客户端真实IP,通过request.getRequestURL()获取的 ...

  7. 伪造IP及获取客户端真实IP地址

    Fiddler支持自定义规则,可以实现对HTTP请求数据发送给Server前或HTTP应答数据发送给浏览器前进行修改.下面的例子将演示如何向所有HTTP请求数据中增加一个头.1)打开Fiddler,点 ...

  8. 获取客户端真实ip

    // 获取客户端真实ip() protected function getIP() { global $ip; if (getenv("HTTP_CLIENT_IP")) $ip ...

  9. 某云负载均衡获取客户端真实IP的问题

    某云负载均衡真实IP的问题,我们这边已经遇到过两次了.而且每次和售后沟通的时候都大费周折,主要是要给售后说明白目前文档的获取真实IP是有问题的,他们觉得文档上说明的肯定没问题,售后要是不明白,他们不会 ...

随机推荐

  1. BLAKE及BLAKE2算法详解

    1 简介 哈希算法 (Hash Algorithm) 是将任意长度的数据映射为固定长度数据的算法,也称为消息摘要.一般情况下,哈希算法有两个特点: 原始数据的细微变化(比如一个位翻转)会导致结果产生巨 ...

  2. 前端每日实战:152# 视频演示如何用纯 CSS 创作一个圆点错觉效果

    效果预览 按下右侧的"点击预览"按钮可以在当前页面预览,点击链接可以全屏预览. https://codepen.io/comehope/pen/gBwzKR 可交互视频 此视频是可 ...

  3. 前端开发: webStorm手机真机调试

    目录 1. 做好准备工作 2. 开始设置 3. 配置路径 4. 匹配路径设置 5. 设置完成,即可体验 文章首发微信公众号飙码人生 2018-05-21 18:21:15 1. 做好准备工作 安装we ...

  4. 峰哥说技术:10-Spring Boot静态资源处理

    Spring Boot深度课程系列 峰哥说技术—2020庚子年重磅推出.战胜病毒.我们在行动 10  峰哥说技术:Spring Boot静态资源处理 今天我们聊聊关于 Spring Boot 中关于静 ...

  5. java算法--循环队列

    循环队列 我们再用队列得时候不知道发没发现这样一个问题. 这是一个只有三个位置得队列,在进行三次加入(addqueue)操作和三次取出(get)操作之后再进行加入操作时候的样子.明显可以看到,队列已经 ...

  6. 大型Java进阶专题(四) 设计模式之工厂模式

    前言 ​ 今天开始我们专题的第三课了,开始对设计模式进行讲解,本章节介绍:了解设计模式的由来,介绍设计模式能帮我们解决那些问题以及剖析工厂模式的历史由来及应用场景.本章节参考资料书籍<Sprin ...

  7. 微信小程序接入LeanCloud

    大家在做小程序或者客户端开发的时候肯定会想使得数据进行联网,但这样就必须有对应的后台服务器以及数据库,再加上linux运维等各种细节,往往会对新手比较劝退,在这里给大家推荐一种bass(后端即服务), ...

  8. 吐血干货,直播首屏耗时400ms以下的优化实践

    导读: 直播行业的竞争越来越激烈,进过18年这波洗牌后,已经度过了蛮荒暴力期,剩下的都是在不断追求体验.最近在帮做直播优化首开,通过多种方案并行,把首开降到500ms以下,希望能对大家有借鉴. 背景: ...

  9. 手动生成WebService代理类

    方式一: 手动生成WebService代理类需要把一句生成语句,如 wsdl.exe /l:cs /out:D:/ProxyServices.cs http://localhost/WebServic ...

  10. Java抽象类和接口的区别及联系

    抽象类 注:先将抽象类中的两种方法解释完,再综合解释抽象类 抽象方法 应用场景:其下所有子类都应该有该方法但是大部分子类具体的执行步骤是有所不同的. 必须重写:也可以说"必须实现" ...