Istio 中实现客户端源 IP 的保持

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Istio 中实现客户端源 IP 的保持相关的知识,希望对你有一定的参考价值。

参考技术A 对于很多后端服务业务,我们都希望得到客户端资源 IP。云上的负载均衡器,比如,腾讯云 CLB 支持将客户端源IP传递到后端服务。但在使用 istio 的时候,由于 istio ingressgateway 以及 sidecar 的存在,后端服务如果需要获取客户端源 IP,特别是四层协议,情况会变得比较复杂。

很多业务场景,我们都希望得到客户端资源 IP。云上负载均衡器,比如,腾讯云 CLB支持将客户端 IP 传递到后端服务。TKE/TCM 也对该能力做了很好的集成。

但在使用 istio 的时候,由于中间链路上,istio ingressgateway 以及 sidecar 的存在,后端服务如果需要获取客户端 IP,特别是四层协议,情况会变得比较复杂。

对于应用服务商来说,它只能看到 Envoy 过来的连接。

先看看一些常见 Loadbalancer/Proxy 的源 IP 保持方法。我们的应用协议一般都是四层、或者七层协议。

七层的客户端源 IP 保持方式比较简单,最具代表性的是 HTTP 头 XFF(X-Forwarded-For) ,XFF 保存原始客户端的源 IP,并透传到后端,应用可以解析 XFF 头,得到客户端的资源 IP。常见的七层代理组件,比如 nginx、Haproxy,包括 Envoy 都支持该功能。

IPVS/iptables 都支持 DNAT,客户端通过 VIP 访问 LB,请求报文到达 LB 时,LB 根据连接调度算法选择一个后端 Server,将报文的目标地址 VIP 改写成选定 Server 的地址,报文的目标端口改写成选定 Server 的相应端口,最后将修改后的报文发送给选出的 Server。由于 LB 在转发报文时,没有修改报文的来源 IP,所以,后端 Server 可以看到客户端的资源 IP。

Nginx/Haproxy 支持透明代理( Transparent Proxy )。当开启该配置时,LB 与后端服务建立连接时,会将 socket 的源 IP 绑定为客户端的 IP 地址,这里依赖内核TPROXY以及 socket 的 IP_TRANSPARENT 选项。

此外,上面两种方式,后端服务的响应必须经过 LB,再回到 Client,一般还需要策略路由的配合。

TOA( TCP Option Address )是基于四层协议(TCP)获取真实源 IP 的方法,本质是将源 IP 地址插入 TCP 协议的 Options 字段。这需要内核安装对应的TOA内核模块。

Proxy Protocol是 Haproxy 实现的一个四层源地址保留方案。它的原理特别简单,Proxy 在与后端 Server 建立 TCP 连接后,在发送实际应用数据之前,首先发送一个 Proxy Protocol 协议头(包括客户端源 IP/端口、目标IP/端口等信息)。这样,后端 server 通过解析协议头获取真实的客户端源 IP 地址。

Proxy Protocol 需要 Proxy 和 Server 同时支持该协议。但它却可以实现跨多层中间代理保持源 IP。这有点类似七层 XFF 的设计思想。

istio 中,由于 istio ingressgateway 以及 sidecar 的存在,应用要获取客户端源 IP 地址,会变得比较困难。但 Envoy 本身为了支持透明代理,它支持 Proxy Protocol ,再结合 TPROXY,我们可以在 istio 的服务中获取到源 IP。

istio 东西向服务访问时,由于 Sidecar 的注入,所有进出服务的流量均被 Envoy 拦截代理,然后再由 Envoy 将请求转给应用。所以,应用收到的请求的源地址,是 Envoy 访问过来的地址 127.0.0.6 。

可以看到,httpbin 看到的源 IP 是 127.0.0.6 。从 socket 信息,也可以确认这一点。

我们修改 httpbin deployment,使用 TPROXY(注意 httpbin 的 IP 变成了 172.17.0.59 ):

可以看到,httpbin 可以得到 sleep 端的真实 IP。

socket 的状态:

第一行是 httpbin 的接收端 socket,第二行是 envoy 的发送端 socket。

httpbin envoy 日志:

可以看到,

httpbin envoy 连接 httpbin 的 local address 为 sleep 的 IP 地址。

对于南北向流量,客户端先请求 CLB,CLB 将请求转给 ingressgateway,再转到后端服务,由于中间多了 ingressgateway 一跳,想要获取客户端源 IP,变得更加困难。

我们以 TCP 协议访问 httpbin:

通过 ingressgateway 访问 httpbin:

可以看到,httpbin 看到的地址是 ingressgateway 的地址:

虽然我们在 httpbin envoy 开启了透明代理,但 ingressgateway 并不能把 client 的源地址传到 httpbin envoy 。基于 envoy 实现的 Proxy Protocol ,可以解决这个问题。

通过 EnvoyFilter 在 ingressgateway 和 httpbin 同时开启 Proxy Protocol 支持。

再次通过 LB 访问 httpbin:

httpbin 得到了客户端的源 IP。

可以看到,

可以看到,

值得注意的是, httpbin envoy 的 upstream_local_address 保留了客户端的 IP,这样,httpbin 看到的源地址 IP,就是客户端的真实 IP。

TPROXY 的内核实现参考net/netfilter/xt_TPROXY.c。

istio-iptables 会设置下面的 iptables 规则,给数据报文设置标记。

值得一提的是,TPROXY 不用依赖 NAT,本身就可以实现数据包的重定向。另外,结合策略路由,将非本地的数据包通过本地 lo 路由:

TPROXY 的更多详细介绍参考这里。

这里使用了 Version 1(Human-readable header format) ,如下:

可以看到,header 包括 client 和 ingressgateway 的 IP:PORT 信息。更加详细的介绍参考这里。

ingressgateway 作为发送端,使用 ProxyProtocolUpstreamTransport ,构建 Proxy Protocol 头部:

httpbin envoy 作为接收端,配置ListenerFilter( envoy.filters.listener.proxy_protocol )解析 Proxy Protocol 头部:

这里值得注意的, envoy.filters.listener.proxy_protocol 在解析 proxy protocol header 时, local_address 为发送端的 dst_addr(172.17.0.54:8000) , remote_address 为发送端的 src_addr(106.52.131.116) 。顺序刚好反过来了。

经过 proxy_protocol 的处理,连接的 downstream_remote_address 被修改为client的源地址。

对于 sidecar.istio.io/interceptionMode: TPROXY , virtualInbound listener 会增加 envoy.filters.listener.original_src :

envoy.filters.listener.original_src 通过 tcp option 实现修改 upstream_local_address 为 downstream_remote_address ,实现透传client IP。

另外, httbin envoy 作为 ingressgateway 的接收端, virtualInbound listener 还配置了 ListenerFilter( envoy.filters.listener.original_dst ),来看看它的作用。

对于 istio,由 iptable 截持原有 request,并转到15006(in request),或者15001(out request)端口,所以,处理 request 的 socket 的 local address ,并不请求的 original dst address 。 original_dst ListenerFilter 负责将 socket 的 local address 改为 original dst address 。

对于 virtualOutbound listener ,不会直接添加 envoy.filters.listener.original_dst ,而是将 use_original_dst 设置为 true,然后 envoy 会自动添加 envoy.filters.listener.original_dst 。同时, virtualOutbound listener 会将请求,转给请求原目的地址关联的 listener 进行处理。

对于 virtualInbound listener ,会直接添加 envoy.filters.listener.original_dst 。与 virtualOutbound listener 不同的是,它只是将地址改为 original dst address ,而不会将请求转给对应的 listener 处理(对于入请求,并不存在 dst address 的 listener)。实际上,对于入请求是由 FilterChain 完成处理。

参考 istio 生成 virtualInbound listener 的代码:

基于 TPROXY 以及 Proxy Protocol,我们可以在 istio 中,实现四层协议的客户端源 IP 的保持。

文章来自https://www.cnblogs.com/tencent-cloud-native/p/16355019.html

IstioCon 2021如何在Istio中进行源地址保持?

北京时间2月23日,在全球首届社区峰会IstioCon 2021中,华为云原生开源团队核心成员徐中虎发表了《Preserve Original Source Address within Istio》主题演讲。


演讲嘉宾:徐中虎

 

徐中虎,华为云原生开源团队核心成员,Istio社区指导委员会委员(亚洲唯一),Istio Maintainer以及核心贡献者,Kubernetes项目核心贡献者。畅销书《云原生服务网格Istio:原理、实践、架构与源码解析》作者之一,聚焦在Kubernetes、容器、Service Mesh等云原生技术。对于分布式系统高性能、高可靠、大规模扩展性有深入的研究和丰富的经验。


演讲主要包含三部分的内容:



2)用于安全策略控制,常用的黑白名单设置;



常见的源IP保持方式及背景知识


【IstioCon 2021】如何在Istio中进行源地址保持?


我们常用的源IP保持的方式从OSI网络模型L3-L7主要分为:


1. L3

【IstioCon 2021】如何在Istio中进行源地址保持?
(1)当用户请求到达Director Server,此时请求的数据报文会先到内核空间的PREROUTING链。此时报文的源IP为CIP,目标IP为VIP。
(2) PREROUTING检查发现数据包的目标IP是本机,将数据包送至INPUT链, LVS主要工作在INPUT链。
(3) IPVS比对数据包请求的服务是否为集群服务,若是,修改数据包的目标IP地址为后端服务器IP,然后将数据包发至POSTROUTING链。此时报文的源IP为CIP,目标IP为RIP ,在这个过程完成了目标IP的转换(DNAT),而源地址始终没变。
(4) POSTROUTING链通过选路,将数据包发送给Real Server。


【IstioCon 2021】如何在Istio中进行源地址保持?
HAProxy透明传输模式也是一种L3层的源地址保持方式,但是它工作在用户空间,是典型的两次连接模型。
1)用户首先与HAProxy建立一条连接,当用户请求到达HAProxy时,HAProxy获取客户端的IP地址。

2)HAProxy工作在透明传输模式下,根据负载均衡策略选择一个真实的后端服务,然后绑定客户端的IP地址,与后端服务建立新的连接。透明代理依赖socket的IP_TRANSPARENT

3)真实服务器响应数据包必须经过HAProxy代理,因此,透明传输还得依赖个性化路由的配置以及TPROXY。


2. L4



【IstioCon 2021】如何在Istio中进行源地址保持?


3. L7




1)服务网格东西向服务访问时,由于Sidecar的注入,所有进出服务的流量均被Envoy拦截代理,当svcA访问svcB时,svcB的Sidecar Envoy与svcB建立一条连接,源地址是127.0.0.1,目的地址也是127.0.0.1(未来是PodIP)。单纯从TCP通信的角度来看,svcB只能获取到客户端地址是127.0.0.1。


【IstioCon 2021】如何在Istio中进行源地址保持?

集群内服务访问


【IstioCon 2021】如何在Istio中进行源地址保持?

Ingress服务访问





1)源地址过滤器作用在Envoy的Inbound监听器,其工作原理是获取下游连接的客户端地址,并以客户端地址为源地址与上游服务建立新的连接。 本质上是IP_TRANSPARENT透明传输,这一点对TCP源地址的获取非常重要。 前面我们介绍到透明传输还必须解决IP包回程的问题,这一点我们留在后面再讲。
2)代理协议过滤器也作用于监听器,主要职责为解析Proxy Protocol协议,可在服务端获取源地址,特别适合Ingress服务源地址获取。
3)Proxy Protocol Transport Socket,是Envoy与上游通过Proxy Protocol进行源IP保持的一种方式,其主要构造代理协议头,并主动发送给上游支持代理协议的服务。
4)HTTP XFF(x-forwarded-for)自动注入,envoy可以根据LDS HTTP连接管理配置自动添加最近的下游客户端的IP地址到XFF中。


1. HTTP类协议源IP保持

【IstioCon 2021】如何在Istio中进行源地址保持?

那么可以看到,东西向七层HTTP类协议可以非常容易的通过添加XFF的方式,方便服务端获取客户端真实IP。其典型的配置如下:


【IstioCon 2021】如何在Istio中进行源地址保持?


【IstioCon 2021】如何在Istio中进行源地址保持?

Ingress HTTP


【IstioCon 2021】如何在Istio中进行源地址保持?


两种模式下,都需要配置Ingress gateway使其支持XFF自动添加, 另外可以通过xff_num_trusted_hops额外配置Envoy信任的代理跳数,详细参考https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_conn_man/headers#x-forwarded-for。

【IstioCon 2021】如何在Istio中进行源地址保持?


2. TCP源IP保持

【IstioCon 2021】如何在Istio中进行源地址保持?


2)配置TPROXY和个性化路由,确保svcB发送到10.244.0.20的数据包被Envoy接收。主要是对数据包打标记,然后根据自定义路由,将非本地的数据包通过本地lo路由。

-A PREROUTING -p tcp -m mark --mark 0x539 -j CONNMARK --save-mark --nfmask 0xffffffff --ctmask 0xffffffff
-A OUTPUT -p tcp -m connmark --mark 0x539 -j CONNMARK --restore-mark --nfmask 0xffffffff --ctmask 0xffffffff
ip -f inet rule add fwmark 1337 lookup 133
ip -f inet route add local default dev lo table 133


【IstioCon 2021】如何在Istio中进行源地址保持?


【IstioCon 2021】如何在Istio中进行源地址保持?



在svcB上配置envoy.filters.listener.proxy_protocol,保证Envoy监听器获取原始客户端IP。

【IstioCon 2021】如何在Istio中进行源地址保持?


本文小结



本文根据本人在IstioCon 2021的分享整理而来,相关的案例demo都可以在

https://github.com/hzxuzhonghu/istio-demo 找到,希望对大家有所启发。


【IstioCon 2021】如何在Istio中进行源地址保持?


即可获取演讲PPT



扫描二维码 | 加入 Istio 技术交流群




戳阅读原文,观看演讲视频!

以上是关于Istio 中实现客户端源 IP 的保持的主要内容,如果未能解决你的问题,请参考以下文章

在 Istio 中实现 Redis 集群的数据分片读写分离和流量镜像

使用 istio 后如何获取客户端真实 IP

IstioCon 2021如何在Istio中进行源地址保持?

IstioCon 2021如何在Istio中进行源地址保持?

教程|使用Istio在Kubernetes集群中实现金丝雀部署

Istio实战-非侵入的流量治理