APP下载

智汇华云 | 负载均衡源地址可见技术

消息来源:baojiabao.com 作者: 发布时间:2024-05-17

报价宝综合消息智汇华云 | 负载均衡源地址可见技术

摘要

在非网关型负载均衡器中,通常使用 FullNat 模式。在这种模式下,客户端访问后端服务器的源 IP 在负载均衡器上会被改变,导致在后端服务器上服务不能正确确定客户端的真实 IP 地址。在一些应用场景下,为了实现安全或者大数据分析等应用,需要感知客户端的真实 IP。本文介绍了一种 FullNat 模式下负载均衡的源地址可见方法。

概述

负载均衡有三种模式:DR,NAT,Tunnel。FullNat 模式在 NAT 模式下增加了源 IP NAT。FullNat 模式的优点:解决了 NAT 对 Director 和 RS 要求在同一个 vlan 的问题,适用更复杂的部署形式不要求配置 Director 作为网关,Director 与 RS 可以通过三层通讯。缺点:RS 看不到客户端真实 IP。

为了解决后端服务器感知客户端真实 IP,本文介绍了如下的方法。

四层源地址可见

四层流量通常是 TCP 和 UDP 协议报文。源地址可见的通常方法是在报文中某些字段携带客户端的真实 IP。在后端通过内核模块来获取客户端 IP。

TCP 源地址可见

TCP 流量是 TOA 来实现源地址可见。TOA 名字全称是 tcp option address,是 FullNat 模式下能够让后端服务器获取客户端 IP 的一种实现方式,它的基本原理比较简单。

客户端用户请求数据包到达负载均衡器时,负载均衡器在数据包的 tcp option 中插入源 IP 信息。

数据包到达后端服务器(装有 toa 内核模块)后,应用程序正常调用 getpeername 系统函数来获取连接的源端 IP 地址。

由于在 toa 代码中 hook(修改)了 inet_getname 函数(getpeername 系统调用对应的内核处理函数),该函数会从 tcp option 中获取负载均衡器填充的源 IP 信息。

这样后端服务器应用程序就获取到了真实客户端 IP,而且对应用程序来说是透明的。

TCP 头部格式如下:

在 option 选项部分携带客户端的 IP 地址。

IPv4 TOA 格式

opcode: opcode = 254

opsize: toa 大小 8 字节

port: 客户端端口

clientIP: 客户端 IP(4 字节)

注:opsize 大小包含了自身 opsize (2B) + port (2B) + ip (4B)

修改 option 的时机

负载均衡器需要对每个 tcp 数据包都要插入 toa 信息么?如果这样会影响到负载均衡器整体性能的,而且后端服务器也没必要对每个 tcp 数据包进行解析,当然也很影响服务器性能。其实只需要在第 3 次握手 ack 数据包中插入 toa 选项即可,后端服务器从 ack 数据包中解析并获取即可。

后端服务器上获取客户端 IP 获取。

TCP 协议栈中处理三次握手的 ack 数据包的函数是 tcp_v4_syn_recv_sock,完成连接的建立,并创建 newsock。在 TOA 内核模块中修改

1.hook tcp_v4_syn_recv_sock_toa 函数,从 TCP 的 skb 中获取 tcp option 的携带的 IP 信息,保存到 socket 中

2. Hook inet_getname,应用程序在调用 getpeername 时,会使用 inet_getname_toa 函数处理,从 socket 中将保存的 ip 信息返回

UDP 源地址可见

UDP 使用 UOA 来实现源地址可见。UDP 报文头部没有 option 字段,通常在 IP 头部的 option 中携带客户端 IP。另外 UDP 是没有连接的,没有三层握手,通常是在前面几个报文中携带信息。

七层源地址可见

七层的负载均衡通常通过反向代理来实现,如 Nginx 和 Haproxy。七层流量通常是 HTTP,通过在 HTTP 头中的 X-FORWARD-FOR 中携带客户端真实 IP,后端服务器应用从 HTTP 头的该字段中获取得到。

X-Forwarded-For 是一个 HTTP 扩展头部。HTTP / 1.1(RFC 2616)协议并没有对它的定义,它最开始是由 Squid 这个缓存代理软件引入,用来表示 HTTP 客户端真实 IP。如今它已经成为事实上的标准,被各大 HTTP 代理、负载均衡等转发服务广泛使用,并被写入 RFC 7239(Forwarded HTTP Extension)标准之中。

X-Forwarded-For 请求头格式非常简单,就这样:

X-Forwarded-For: client, proxy1, proxy2

可以看到,XFF 的内容由「英文逗号 + 空格」隔开的多个部分组成,最开始的是离服务端最远的设备 IP,然后是每一级代理设备的 IP。

如果一个 HTTP 请求到达服务器之前,经过了三个代理 Proxy1、Proxy2、Proxy3,IP 分别为 IP1、IP2、IP3,用户真实 IP 为 IP0,那么按照 XFF 标准,服务端最终会收到以下信息:

X-Forwarded-For: IP0, IP1, IP2

下面以 NGINX 为例,说明配置方法。

在 Nginx 配置文件中添加:

proxy_set_header X-Real-IP $remote_addr;

proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

$proxy_add_x_forwarded_for 会保存 X-Forwarded-For 中已有的值,并且追加 $remote_addr 的值,使用逗号隔开。

如果之前 X-Forwarded-For 中没有值,则修改后 X-Forwarded-For 中只有 $remote_addr 的值。

例子:

A(client)->B(Nginx1)->C(Nginx2)->D

A 为客户端,B 和 C 为 Nginx 反向代理,D 为服务端

A 访问 B 时,X-Forwarded-For 为空,$remote_addr 为 A 的 IP,故 B 转发到 C 时附带的 Header 头 X-Forwarded-For 即为 A 的 IP;

B 访问 C 时,X-Forwarded-For 为 A 的 IP,$remote_addr 为 B 的 IP,此时 C 转发到 D 附带的 Header 头 X-Forwarded-For 即为 A 的 IP,B 的 IP;

C 访问 D 时,D 就可以拿 C 传来的 X-Forwarded-For Header 头来分析源 IP。

2022-12-21 11:58:08

相关文章