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. beego的安装以及bee的安装和使用

    beego的安装以及bee的安装和使用 一.beego的安装 1.beego是什么 beego 是一个快速开发 Go 应用的 HTTP 框架,他可以用来快速开发 API.Web 及后端服务等各种应用, ...

  2. 支持IE6、IE7、IE8等低端浏览器的简化版vue

    最近研究Vue的底层原理,写了一个简化版的Vue,可以在支持IE6.IE7.IE8等低端浏览器运行.由于低端浏览器不支持对象属性定义,所以设置属性不支持直接赋值,需要调用虚拟机实例的set方法.目前只 ...

  3. python正则表达式之re模块方法介绍

    python正则表达式之re模块其他方法 1:search(pattern,string,flags=0) 在一个字符串中查找匹配 2:findall(pattern,string,flags=0) ...

  4. LVM简介及CentOS7 LVM操作实战

    LVM简介LVM是逻辑盘卷管理(LogicalVolumeManager)的简称,它是Linux环境下对磁盘分区进行管理的一种机制,LVM是建立在硬盘和 分区之上的一个逻辑层,来提高磁盘分区管理的灵活 ...

  5. A. Reorder the Array

    You are given an array of integers. Vasya can permute (change order) its integers. He wants to do it ...

  6. String字符串位置移动

    有规律的String字符串位置移动 1.自定义一个有规律的String字符串 String numstr = "1,2,3,x,y,4,5"; 2.按逗号拆分numstr字符串 S ...

  7. HDFS DataNode详解

    1. datanode介绍 1.1 datanode datanode是负责当前节点上的数据的管理,具体目录内容是在初始阶段自动创建的,保存的文件夹位置由配置选项{dfs.data.dir}决定 1. ...

  8. SpringBoot图文教程15—项目异常怎么办?「跳转404错误页面」「全局异常捕获」

    有天上飞的概念,就要有落地的实现 概念十遍不如代码一遍,朋友,希望你把文中所有的代码案例都敲一遍 先赞后看,养成习惯 SpringBoot 图文教程系列文章目录 SpringBoot图文教程1-Spr ...

  9. linux最常用命令记录(一)

    一.vim个人最常用设置: vim .vimrc 然后添加以下内容 set nu set tabstop=4 set encoding=utf-8 二.查看磁盘空间相关命令 1.df -h   查看硬 ...

  10. ASP.NETMVC中js非空验证实例

    页面代码 @using (Html.BeginForm("Edit", "Home", FormMethod.Post, new { @Id = "f ...