获取真实客户端IP
1.网站接入(HTTP/HTTPS协议)
当接入高防防护服务后,网站服务器访问日志中的IP地址都将记录为高防防护的反向代理服务IP,无法取得客户端的真实IP地址。
为解决这个问题,我们在高防防护转发的HTTP头信息中增加了 X-Forwarded-For HEAD信息,记录的是请求过程中使用的代理IP列表,包括与我们代理直连的IP,WEB服务器日志或服务器程序就可以根据自己的方法来获取真实的客户端IP。
1.1.服务器日志类记录方法
- Nginx服务器
源站是Nginx搭建的服务器,配置文件中指定日志格式时使用$http_x_forwarded_for变量,相应的配置如下:
在nginx.conf中,配置log_format,定义访问日志记录的格式中用到$http_x_forwarded_for,示例如下:
log_format main '$http_x_forwarded_for - $remote_user [$time_local] "$request" ''$status $body_bytes_sent "$http_referer" ''"$http_user_agent" ';
上述示例配置了main的日志模板,在日志记录指令中指定这个模板就可以输出。
- Apache服务器
源站是Apache服务器的,日志记录方案与nginx类似,配置指令不同,参考如下:
LogFormat "%{X-Forwarded-For}i %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\""
1.2.服务器程序类读取方法
防御过程中,我们透传客户端IP采用http的HEAD头,不同的服务器程序读取HEAD头的方式不一样,下面分别介绍常用程序的读取方法
- ASP程序
Request.ServerVariables("HTTP_X_FORWARDED_FOR")
- PHP
$_SERVER["HTTP_X_FORWARDED_FOR"]
- JSP
request.getHeader("HTTP_X_FORWARDED_FOR")
2.端口接入(TCP协议)
对于TCP业务的源站需要加载TTM模块后才能获取到客户端真实源IP,您可以使用本文介绍的方法下载并安装TTM模块。
这种方式需要在防护业务中配置开启TTM功能(默认为开启)。配置路径:防护业务->编辑/添加 TCP协议防护业务。
2.1.TTM模块原理
客户端的数据包经过高防IP转发后,会将数据包的源地址和端口号,改成高防IP回源的地址和端口号。为了将客户端真实源IP和端口号发送给服务端,高防IP转发报文时会将客户端真实源IP和端口号添加到TCP报文的tcp option字段。源站加载TTM模块后,TTM模块通过hook Linux内核TCP协议栈的相关函数,从TCP报文的tcp option字段中解析出客户端真实源IP和端口号,详细描述如下:
- Linux TCP协议栈在建连阶段收到客户端三次握手的ACK报文后,会调用tcp_v4_syn_recv_sock函数。TTM模块hook了tcp_v4_syn_recv_sock函数,hook后的tcp_v4_syn_recv_sock_ttm函数首先会调用原来的tcp_v4_syn_recv_sock函数,然后调用get_ttm_data_from_ack函数,从ACK报文的tcp option字段中,提取出客户端真实源IP和端口号,并存储在sock的sk_user_data变量中,其中每条流对应一个sock。
- 客户端应用程序在用户态调用getpeername或者accept接口时,最终都会调用到inet_getname函数,TTM模块hook了inet_getname函数,hook后的inet_getname_ttm函数首先会调用原有的inet_getname函数,然后判断sock中的sk_user_data变量是否为空,如果sk_user_data变量不为空则从该变量中提取出客户端真实源IP和端口号,替换原有inet_getname返回的高防IP回源地址和端口号,这样客户端应用程序调用getpeername或者accept接口获取到的就是客户端真实源IP和端口号。
2.2.TTM模块支持的操作系统
- Linux
注意
- 建议先在测试环境进行测试,待确认功能正常且运行稳定后再部署上线到正式环境。
- TTM模块目前仅支持IPv4,且只支持64位操作系统。
- 非TCP协议不支持获取客户端真实源IP和端口号。
- 如果源站已经加载了类似的模块,hook了Linux协议栈的tcp_v4_syn_recv_sock和inet_getname函数,加载TTM模块后,会导致原有模块的功能不生效。
- 七层业务(HTTP/HTTPS协议)可以在http header中直接通过X_forwarded_for字段来获取客户端真实源IP。
2.3.TTM模块安装步骤
- 下载Linux对应版本的TTM模块,并进行加载
系统 | 版本号 | 下载地址 |
---|---|---|
CentOS | 3.10.0-514.26.2.el7.x86_64 | https://sdk.bce.baidu.com/console-sdk/3.10.0-514.26.2.el7.zip |
CentOS | 3.10.0-693.el7.x86_64 | https://sdk.bce.baidu.com/console-sdk/3.10.0-693.el7.zip |
CentOS | 3.10.0-957.1.3.el7.x86_64 | https://sdk.bce.baidu.com/console-sdk/3.10.0-957.1.3.el7.zip |
wget https://sdk.bce.baidu.com/console-sdk/3.10.xxx.zip
unzip 3.10.xxx.zip
cd 3.10.xxx
mv bce_ttm.ko /lib/modules/$(uname -r)/kernel/net/ipv4/
insmod /lib/modules/$(uname -r)/kernel/net/ipv4/bce_ttm.ko
注意:
如果没有/lib/modules/$(uname -r)/kernel/net/ipv4/目录,也可以将bce_ttm.ko放在其它任意目录,以下操作步骤的路径也需要替换成bce_ttm.ko所在目录
- 查看TTM模块加载情况
lsmod |grep bce_ttm
- 如果需要机器重启后能自动加载TTM模块,可执行以下命令
echo 'insmod /lib/modules/$(uname -r)/kernel/net/ipv4/bce_ttm.ko' >> /etc/rc.local
- 如果不再使用TTM模块,可以执行如下命令进行卸载
rmmod bce_ttm
2.4.制作TTM模块
如果TTM下载列表里没有对应Linux版本的TTM模块,也可以按照以下步骤手动制作TTM模块并进行加载。
- 安装编译环境
yum -y install gcc kernel-headers kernel-devel
- 下载bce_ttm模块源文件,并进行解压
wget -c "https://codeload.github.com/baidu/ttm/zip/master" -O bce_ttm.zip
unzip bce_ttm.zip
- 编译TTM模块,编译后会在当前目录生成bce_ttm.ko文件
cd ttm-master/
make
- 加载TTM模块,加载方法同TTM模块安装步骤
2.5.特殊说明
高防后端连接百度智能云BLB或者其他负载均衡器的情况,需要关闭BLB或者其他负载均衡设备的TOA模块(与高防的TTM冲突),同时要确保BLB或者其他负载均衡设备开启FULL NAT转发模式。