我们的服务有时需要使用客户端 IP 来用于特定业务或安全用途。但通常的情况是,从客户端到真正的服务间可能经过多个不同的网络、负载均衡或代理服务,在流量经过每一层时都可能丢失掉原始的客户端 IP,使我们的服务只能拿到前一级网络的 IP,这不是我们想要的效果。
由于技术栈的复杂性,获取客户端 IP 需要很多不同的手段甚至结合使用。
NAT 技术中的客户端 IP 处理
在一些由用户自主建设或租用的传统 IDC 基础设施中,我们的服务可能位于一个网关后的独立 LAN 网络中,此时如果客户端尝试从外部网络连接我们的服务,流量将通过网关进行转发。
而除了本地部署的基础设施,我们在使用云服务时也可能遇到这种情况,公有云平台提供的 VPC 网络也是一个独立的 LAN 网络,它与其他 VPC 和互联网隔离,因此也需要网关帮助其对外访问互联网和接入外部连接。
这里的网关可能是路由器或防火墙等设备,它们通常可以为内部服务提供 DNAT (Destination NAT) 的地址转换服务,即在网关持有一至多个公开 IP 地址,并将公开 IP 上的特定端口转发至一个内网 IP 上的特定端口,流量的转发和端口映射由网关负责。在使用这种网络技术时,原始的 IP 包头中的来源地址将被网关修改为其自己的地址,并在其内存中建立外部端口和内部端口间的映射关系,而由于原始 IP 数据包已被修改,我们在内部网络中的服务只能获得网关的IP地址,而不是真实客户端地址。
以往网络设备或软件提供的 DNAT 地址转换能力较为原始,由于其几乎只工作在三层,而没有对更内层负载的感知和操作能力,几乎只能用来完成服务暴露的工作亦无法向后传递客户端 IP,同时限于设备或软件性能它对于同时维持的活跃连接数量和最大新建连接数也有上限,通常难以在不升级硬件配置情况下进行扩展,难以适应现在多变的环境。
为此,我们需要使用负载均衡,它们具备对流量更深入的操纵能力。
负载均衡对客户端 IP 的处理
通常来说负载均衡主要分为两种,通过工作层次的不同区分,它们工作在四层和七层,分别对应 TCP 数据流和以 HTTP 为代表的应用流量。
区别于 IP 网关,它不会对 IP 包头进行任何修改,对于 IP 数据包层面,它只会做透明转发。因此与上面的 DNAT 相比,我们会发现,IP 数据包中包含的来源 IP 可以正常传递到负载均衡后的组件中。
对于四层负载均衡,仅在完成基础的流量转发时,其后的服务就可以正确的获得客户端 IP,无需任何特殊处理。而对于特殊情况,它也可以使用 Proxy Protocol,在原始流量负载的数据前添加一个新的部分,其中包含客户端 IP,在负载均衡组件后的其他反向代理服务器或服务本身则可以解析 Proxy Protocol 数据,获得客户端 IP。
对于七层负载均衡,其具有更深度的流量处理能力,它可以直接在 HTTP 协议维度上传递来源 IP,一个较为普遍的做法是使用 X-Forwarded-For HTTP 头,即负载均衡组件会从流量的 IP 数据包中提取其来源 IP,之后解析并改写 HTTP 请求,向其请求头中插入值为客户端 IP 的新的 XFF 字段。
HTTPS 较为特殊,由于其负载(payload)是加密的,负载均衡组件无法直接操作其 HTTP 头,此时将采取这样的办法:
如果没有特殊要求,可以将 HTTPS 流量视作普通的 TLS 流量,直接以四层方式进行转发。此时客户端 IP 仍可以通过 IP 包头传递到负载均衡后的服务上。
如果需要以七层方式工作,则我们需要将 TLS 证书托管至负载均衡组件,使用它完成 TLS 终结 (tls termination),即在负载均衡层面剥离 TLS 加密,在负载均衡后面的 LAN 上使用 HTTP 明文或建立新的 HTTPS 连接到服务,而不是直接转发,此时负载均衡组件又可以对原始 HTTP 流量进行处理,可以继续使用 XFF 的方式传递 IP。
通过对流量的精细化处理,负载均衡有很多办法向后面服务传递客户端 IP。负载均衡组件的代表实现有:基于云的 ELB 服务、基于硬件设备的 F5 BIG-IP、基于 Linux 内核的 Linux Virtual Server (LVS)、基于用户软件的 NGINX 等。
CDN 服务中的客户端 IP 传递
有时,我们还会在服务前使用公有云平台提供的 CDN 服务以加速用户访问,它的工作模式像是一个工作在七层的反向代理服务器,但广义来说它们也属于负载均衡的范畴。
CDN 服务一般会提供 TLS 终结能力,并可以将客户端 IP 放入 HTTP 请求发送到服务。以 AWS CloudFront CDN 服务为例,它支持以 XFF 方式传递客户端 IP,这与七层负载均衡类似。
API 网关的应用
负载均衡组件通常只能提供流量本身较为基础的控制能力,或是一些基于云或硬件的负载均衡器提供的 API 很难与我们的实际业务需求结合,此时我们需要使用更灵活的组件为我们的服务应用策略,我们可以使用 API 网关,比如 Apache APISIX 或 API7 Enterprise。
APISIX 和 API7 Enterprise 支持 Proxy Protocol 协议用于以从负载均衡上获得客户端 IP。
除此之外,它们也提供了名为 real-ip 的插件,与 NGINX 提供的 ngx_http_realip_module 模块类似,从一个来源获取客户端 IP,并将其用于向后传递并记录日志。APISIX 和 API7 Enterprise 上的 real-ip 插件是 NGINX 上功能的增强版本,它允许动态开启或关闭真实来源 IP 的功能、同时它也扩充了客户端 IP 的来源、不会被 ngx_http_realip_module 本身仅支持 HTTP 头和 Proxy Protocol 所限制,可以使用任何 NGINX 变量或 APISIX 扩展变量,比如 query 参数或 POST 表单字段。
总结
通过对多种层次的技术的组合应用,我们就可以正确的将客户端 IP 通过每一层组件传递至服务以用于业务和安全用途。
如果你想了解更多有关 API 管理的解决方案,欢迎联系我们。