查看原文
其他

使用BFE如何实现地址透传

BFE开源项目 BFE开源项目 2024-04-15

在经过负载均衡转发后,客户端地址如何透传到下游的服务是一个常见的问题。本文将说明在BFE中是如何解决这个问题的。


1. 问题说明


在经过BFE转发后,RS无法获得原始的客户端IP地址,而只能获得BFE实例的IP地址。

如图1所示,客户端的IP地址为1.2.3.4,在经过BFE转发后,BFE和RS建立了新的TCP连接,RS只能看到BFE的地址10.1.0.1

       很多应用都需要获取请求原始的客户端IP地址。需要提供机制将原始的客户端IP地址传递到RS。


图1 经过BFE转发后客户端地址的变化


2. BFE的客户端地址透传方案


BFE在扩展模块mod_header中默认提供了捎带客户端IP地址和端口的功能。只要在BFE启动时配置加载mod_header,在转发后请求中就会包含这两个信息。

 

在经过BFE转发后,在HTTP请求的头部会增加2个字段

  • X-Real-Ip:用于传递原始的客户端IP地址

  • X-Real-Port:用于传递原始的客户端端口

 

注:

mod_header的实现位于

https://github.com/bfenetworks/bfe/tree/develop/bfe_modules/mod_header

 

mod_header的说明文档位于

https://github.com/bfenetworks/bfe/blob/develop/docs/zh_cn/modules/mod_header/mod_header.md


3. 为什么不使用x-forward-for


曾经有一个朋友在《深入理解BFE》的Github讨论区中提了一个问题:为什么不使用X-Forwarded-For来传递客户端IP地址呢?

 

BFE使用独立定义的“X-Real-Ip”是为了避免“X-Forwarded-For”被伪造。

如果请求在到达BFE时已经包含了“X-Real-Ip”字段,BFE会将这个字段的值重写为BFE所见的客户端IP地址,从而避免这个字段被伪造。

但是BFE不能使用类似的“重写”方案来避免x-forward-for字段的伪造。

 

可以看看下面的两种可能的场景:

  • 场景一:如果请求中本来没有包含X-Forwarded-For头部
    BFE可以添加新的头部,

X-Forwarded-For: client-ip, bfe的地址

 

  • 场景二:如果请求中已经包含X-Forwarded-For头部,如,

X-Forwarded-For: client1, proxy1,proxy2, proxy3
       那么BFE需要将自己的地址添加到末尾

X-Forwarded-For: client1, proxy1,proxy2, proxy3,BFE的地址

    在这种情况下,client的地址可能为伪造。但是,BFE不能改写X-Forwarded-For的值,因为需要保留X-Forwarded-For的内容供其它可能的分析


4. 其它信息的透传


除了原始的客户端IP地址,可能还需要BFE向下游服务透传其它信息,如本次请求所访问的服务端IP地址(VIP),本次请求所使用的TLS/SSL协议版本等。在某些场景中,由于历史的原因,可能也需要对于透传客户端IP地址的头部名称做特殊的设置

     在mod_header中,可以对每个租户提供一张配置表,可以针对符合条件的请求,对头部做指定的动作。

图2 租户的mod_header配置表


    在配置表中,使用BFE的条件表达式来描述匹配的条件。对请求(Request)和响应(Response)的Header可能执行的操作包括设置、添加、删除等。针对同一个匹配条件,可以执行多个操作。

       为了便于操作,在mod_header中还提供了一些内置的变量,用于在配置图2中的action时使用。如:

  • %bfe_cip,代表客户端IP (CIP)

  • %bfe_vip,代表服务端IP (VIP)


    在下面这个配置的例子中,对于属于“example_product”的请求,如果请求的Path前缀为“/header”,则设置名为X-Bfe-Vip的Header,用于向下游服务传递服务端IP地址。

{

    "Version":  "20190101000000",

    "Config": {

        "example_product": [

            {

                "cond":  "req_path_prefix_in(\"/header\", false)",

                "actions": [

                    {

                        "cmd":  "REQ_HEADER_SET",

                        "params": [

                             "X-Bfe-Vip",

                             "%bfe_vip"

                        ]

                    },

                ],

                "last": true

            }

        ]

    }

}

 


5. BFE如何从上游获得客户端地址


在实际部署中,在BFE的上游经常会使用四层负载均衡,以实现多个BFE实例间的流量均衡,并解决BFE的高可用。

流量在经过四层负载均衡器的转发后,BFE同样也无法直接获得原始的客户端IP地址,而只能看到四层负载均衡器的内部地址。


图3 经过四层负载均衡器后地址发生变化


    由于很多四层负载均衡器不能像BFE这样修改HTTP请求头部(部分的原因是因为这样的功能对于四层负载均衡器来说太复杂;部分的原因是因为在HTTPS的场景下四层负载均衡器无法“看见”和修改请求的内容),所以需要使用更底层的机制。

      

常见的方案有两种:

  • 方案1:使用TOA(TCP OptionAddress)

通过在TCP Options中插入客户端地址方式,将客户端地址从四层负载均衡器携带到BFE。

LVS这样的开源负载均衡软件和F5这样的商用负载均衡产品都支持开启TOA。

如果使用TOA方案,在BFE所在的服务器,需要安装TOA插件。这样BFEsocket中读取到的客户端IP地址就是原始的客户端IP地址。

TOA的详情可以参考以下文章:

https://zhuanlan.zhihu.com/p/341560754

https://www.jianshu.com/p/e770a11481e9

 

  • 方案2:使用Proxy Protocol

Proxy Protocol是由HAProxy发明的。其主要思想是:在发送数据之前,首先发送一个特殊的头信息,用于传递客户端IP地址、端口等信息。

HAProxy这样的开源负载均衡软件和F5这样的商用负载均衡产品都支持开启Proxy Prococol。

    如果使用Proxy Prococol方案,需要修改BFE的配置。在bfe.conf中,需要将Layer4LoadBalancer设置为"PROXY"

[Server]

# type of layer-4 load balancer  (PROXY/NONE), default NONE

Layer4LoadBalancer = "PROXY"

可以参考

https://github.com/bfenetworks/bfe/blob/develop/docs/zh_cn/configuration/bfe.conf.md。

 

Proxy Protocol的详情可以参考以下文章:

https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt

       https://www.jianshu.com/p/cc8d592582c9


6. 总结


在向下游服务透传原始客户端IP地址等信息方面,BFE提供了完整的支持。

      在包含四层负载均衡器的场景中,可以使用TOA或Proxy Protocol将原始的客户端IP地址从客户端传递给BFE



注:本文章中包含多个外部资料链接。可以通过点击左下方的“阅读原文”来访问这些链接。



欢迎关注“BFE开源项目”微信公众号,获得本项目的更多更新。谢谢!




继续滑动看下一个
向上滑动看下一个

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存