客户端 IP 解析
AISIX 网关会把每个请求归属到一个客户端地址。调用方直接连接时,该地址就是连接的 TCP 对端。网关运行在负载均衡器或 Ingress 后方时,TCP 对端是前置设备,网关需要由该设备告知原始客户端是谁。本文介绍网关接受这类信息的两种方式,即 X-Forwarded-For 和 PROXY protocol,以及如何配置网关信任哪些设备提供这类信息。
客户端地址的用途
网关在所有需要识别调用方的地方都使用解析出的客户端地址:
- 用量事件的
client_source_ip字段,以及 OTLP Trace 的client.address属性。 - 模型的
allowed_cidrs、透传路由的来源允许列表,以及 MCP 的source_cidrs。 - 基于客户端 IP 的哈希路由。
访问日志还会记录连接的 peer,即连接本身的地址和源端口。
默认配置下,网关不信任前置的任何设备,客户端地址始终是 TCP 对端。
信任网关前置的设备
两种方式都由同一个启动配置项 proxy.real_ip.trusted_proxies 控制,它是一组 CIDR 或单个 IP 地址。当网关看到的 TCP 对端地址落在其中某一项内时,该设备即受信任。该列表默认为空。
proxy:
real_ip:
trusted_proxies:
- "10.0.0.0/8"
- "127.0.0.1/32"
仅通过环境变量部署时,用逗号分隔列表:
AISIX_PROXY__REAL_IP__TRUSTED_PROXIES=10.0.0.0/8,127.0.0.1/32
只列出确实向网关转发流量的设备地址。受信任的对端可以声明任意客户端地址,因此如果某一项覆盖了调用方,这些调用方就能自行决定网关把它们归属到哪个地址。
位于 HTTP 代理或 Ingress 后方
HTTP 反向代理或 Ingress 控制器通常会把接收请求时的来源地址追加到 X-Forwarded-For 请求头中。当 TCP 对端在 trusted_proxies 内时,网关会读取该请求头来确定客户端。来自其他对端的请求归属到该对端本身,其请求头会被忽略。
proxy.real_ip 下另有两个配置项控制请求头的读取方式:
| 配置项 | 默认值 | 作用 |
|---|---|---|
header | x-forwarded-for | 要读取的请求头。 |
recursive | false | 为 false 时,客户端是请求头中最右侧的地址。为 true 时,网关从右向左遍历请求头,跳过所有位于 trusted_proxies 内的地址,取第一个不受信任的地址。网关前有多级受信任代理串联时使用 true。 |
如果请求头不存在或其中没 有可用地址,客户端即为 TCP 对端。
位于四层负载均衡器后方
会改写连接源地址的四层(TCP)负载均衡器不解析 HTTP,因此无法添加 HTTP 请求头。它可以在每个连到网关的连接开头发送一个 PROXY protocol 头,写明原始客户端。网关支持读取 PROXY protocol v1 和 v2 头。
检测 PROXY 头
每个代理监听器都会在 TLS 握手和 HTTP 之前,检查每个新连接开头的字节是否为 PROXY protocol 头。这适用于由 proxy.addr 和 proxy.tls 设置的监听器,以及 proxy.listeners 中的每一项,无论是否启用 TLS。该功能没有开关,也没有其他配置项。Admin、指标/状态和诊断监听器从不读取 PROXY 头。
后续处理取决于连接:
| 连接 | 结果 |
|---|---|
| 没有 PROXY 头 | 与此前完全相同。平台探针以及其他直接访问同一端口的调用方不受影响。 |
来自 trusted_proxies 内对端的有效 PROXY 头 | 在该连接的剩余生命周期内,用 PROXY 头中的源地址和端口替换 TCP 对端。 |
来自 trusted_proxies 外对端的 PROXY 头 | 关闭连接。 |
| 来自受信任对端的格式错误或不完整的 PROXY 头 | 关闭连接。 |
v2 的 LOCAL 头(负载均衡器用于自身健康检查)、传输协议或地址族未指定或地址族为 UNIX 套接字的 v2 头,以及 v1 的 UNKNOWN 头都不携带客户端地址。网关会跳过这些头并保留 TCP 对端。v2 头中的 TLV(Type-Length-Value)扩展会被忽略。
PROXY 头被采纳时,其源地址就是访问日志中该连接的 peer,也是客户端地址的用途中所列各项的解析起点。
启用 PROXY protocol
-
确定网关看到的、来自负载均衡器的连接的 TCP 对端地址。通常是负载均衡器自身的地址段;在 Kubernetes 上则可能是节点地址,参见在 Kubernetes 上保留负载均衡器地址。
-
把该地址段加入
proxy.real_ip.trusted_proxies,并下发到所有网关:proxy:real_ip:trusted_proxies:- "10.0.0.0/16" # 负载均衡器的地址段。 -
在负载均衡器的目标组或后端上开启 PROXY protocol。
请先下发网关配置。不信任该负载均衡器的网关会关闭所有携带 PROXY 头的连接,因此如果在网关信任发送方之前就开启 PROXY 头,来自该负载均衡器的流量会被全部拒绝。
受信任的地址在不携带 PROXY 头的连接上同样被信任提供 X-Forwarded-For。开启 PROXY 头后,来自负载均衡器的每个连接都会携带该头,因此这一点只影响从受信任地址段内到达网关的其他流量。
使用 Helm Chart 配置
使用 api7/aisix Helm Chart 时,在 config 下设置同一个键:
config:
proxy:
real_ip:
trusted_proxies:
- "10.0.0.0/16"
Chart 如何渲染 config,参见设置其他网关配置。
在 Kubernetes 上保留负载均衡器地址
当负载均衡器通过 externalTrafficPolicy: Cluster(Kubernetes 默认值)的 Kubernetes Service 访问网关 时,kube-proxy 会把连接的源地址改写为节点地址,以便把连接转发到任意节点上的 Pod。此时网关看到的 TCP 对端是节点地址,而不是负载均衡器地址。
这种情况下,trusted_proxies 必须覆盖节点网络,否则所有携带 PROXY 头的连接都会被关闭。
使用 externalTrafficPolicy: Local 时,kube-proxy 只把连接交给接收该连接的节点上的 Pod,并保留源地址,因此 TCP 对端就是负载均衡器地址。Chart 通过 service.externalTrafficPolicy 设置该策略。
与 X-Forwarded-For 配合使用
对携带 PROXY 头的连接,X-Forwarded-For 处理依然生效。网关在读取 X-Forwarded-For 之前,会用 PROXY 头中的地址作为对端去匹配 trusted_proxies。因此,负载均衡器后方的客户端如果自身地址不受信任,就无法让网关采信它发送的 X-Forwarded-For 请求头。
如果四层负载均衡器后方还有一个设置 X-Forwarded-For 的 HTTP 代理,请把负载均衡器和该代理的地址段都加入 trusted_proxies。
诊断被关闭的连接
网关每因 PROXY 头关闭一个连接,就会记录一条 WARN 日志。两种日志都带有监听器的 label 和连接来源的 TCP peer:
| 消息 | 原因 |
|---|---|
PROXY protocol header from a peer outside proxy.real_ip.trusted_proxies; closing the connection | TCP 对端不在 trusted_proxies 内。如果 peer 中的地址是你的负载均衡器,或者在 Kubernetes 上是你的某个节点,请把它加入列表。 |
malformed PROXY protocol header; closing the connection | 无法解析来自受信任对端的 PROXY 头。reason 字段说明原因,例如 PROXY 头不完整或超过网关接受的长度。 |
开头字节为 PROXY 加一个空格的连接始终会被当作 v1 PROXY 头读取。使用名为 PROXY 的自定义 HTTP 方法的请求无法在代理监听器上处理。
双栈监听器
在绑定到 [::] 的代理监听器上,操作系统会把 IPv4 客户端报告为 IPv4 映射的 IPv6 地址,例如 ::ffff:192.0.2.10。网关在接受连接时会把它还原为普通 IPv4 形式(此处为 192.0.2.10),对 PROXY 头中的地址也做同样处理。因此,IPv4 客户端在用量事件、Trace 和访问日志中以普通 IPv4 地址报告,trusted_proxies、allowed_cidrs、透传路由来源允许列表、MCP source_cidrs 和基于客户端 IP 的哈希路由中的 IPv4 条目都能匹配它们。
这些列表中的 IPv4 地址段请以 IPv4 形式填写。以 ::ffff: 形式填写的条目(例如 ::ffff:10.0.0.0/104)无法匹配 IPv4 客户端。