智汇华云 | 通过TProxy实现haproxy 透传用户IP
Posted 华云
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了智汇华云 | 通过TProxy实现haproxy 透传用户IP相关的知识,希望对你有一定的参考价值。
如今,实现IP透传最常见的技术便是linux TProxy技术,那么在IP透传过程中需要具体做哪些事情?本期华云数据“智汇华云”专栏将为带来“通过TProxy实现haproxy 透传用户IP”。
本期讲解人
廖桥生
华云数据网络开发工程师
1 IP透传
在所有代理类型的应用中都一个共同的问题,就是后端的目标服务器上看到的连接的源IP都不再是客户端原始的IP,而是前端的代理服务器的IP。
以haproxy来说,假设你有两台后端Web服务器提供服务,前端使用haproxy作为负载均衡设备。所有客户端的HTTP访问会先到达haproxy,haproxy作为代理,将这些请求按照指定的负载均衡规则分发到后边的两台Web服务器上。这个操作本身没有任何问题,因为haproxy就应该是这么工作的。但是对于某些对于客户端的访问IP有限制的敏感应用,问题来了: 后端服务器上的ACL无法限制哪些IP可以访问,因为在它看来,所有连接的源IP都是haproxy的IP。
图一
(图一例子中后端服务器只知道源IP为haproxy服务器的IP:192.168.1.4,而不知道客户端的真实IP)
启用IP透传后,haproxy转发客户端请求到后端服务时,源IP为客户端原始的IP了。
图二
(图二例子中后端服务器收到请求的源IP为客户端的真实IP,而不是haproxy服务器的IP:192.168.1.4)
实现IP透传最常见的技术便是linux TProxy技术,haproxy实现IP透传的技术之一便是linux TProxy。
2 TProxy技术
TProxy是linux内核支持透明代理的一种技术,TProxy允许代理"模仿"用户的访问IP,就像代理设备不存在一样,当HTTP请求到达后端的Web服务器时,在后端服务器上用netstat查看连接的时候,看到连接的源IP就是用户的真实IP,而不是haproxy的IP,TProxy名字中的T表示的就是transparent(透明)。从2.6.28以后TProxy已经进入官方内核,因此配置haproxy透传IP时需要注意你的linux内核版本。
2.1 TProxy主要功能
3. 重定向一部分经过路由选择的流量到本地路由进程(类似NAT中的REDIRECT)
2.2 TProxy如何工作?
1. / /* socket设置IP_TRANSPARENT标识 */
2. setsockopt(fd,SOL_IP, IP_TRANSPARENT,&opt,sizeof(opt))
3. /* 下句代码中的(struct sockaddr_in *)remote->sin_addr是客户端ip */
4. ((struct sockaddr_in *)&bind_addr)->sin_addr = ((struct sockaddr_in *)remote)->sin_addr
5. /* 将socket bind到client ip */
6. bind(fd, (struct sockaddr *)&bind_addr, get_addr_len(&bind_addr))
8. connect(fd, (struct sockaddr *)&conn->addr.to, get_addr_len(&conn->addr.to))
Ø 如何重定向一部分经过路由选择的流量到本地路由进程?
1. 后端server修改路由规则,将目的地为用户IP的回包,路由给haproxy服务器;
2. 由图三所示,haproxy服务器收到后端server的回包后,在IP层, 通过netfilter/iptables将回包路由到本地环路lo,TProxy从本地环路上抓取网络包(skb),从网络包提取出网络包中的源ip/port,目的ip/port,根据这些信息,从内核中查找出对应的套接字句柄sk,然后进行赋值:将该回包的skb->sk = sk(sk为haproxy进程创建的对应套接字),从而让tcp层能够根据skb->sk, 将该回包递交给haproxy进程进行处理,最终返回给客户端;
图三
一些背景知识:
1. linux内核分层处理后端server返回的网络包,网络包经由链路层(网卡驱动)、IP层、tcp层,最终递交到应用层的haproxy;
2. 数据包在各层传递过程中, 在linux内核中,统一表示为一个结构:struct sk_buff,或称之为socket buffer,简写为skb;
3. skb在递交tcp层时, 由skb->sk 标明该网络包对应的应用层socket套接字是哪个,tcp层将根据skb->sk这个信息,将网络包放入到某个应用进程创建的套接字中,供应用层处理该网络包;
4. 为此,处理“目的地为非本地IP网络包”的关键, 在于在IP层,将skb中的sk,指定为haproxy进程创建的socket套接字。
3 haproxy IP透传源码解析
3.1 确认haproxy的版本
通过haproxy -vv确认所运行的haproxy编译时指定USE_LINUX_TPROXY=1,如图四所示。
图四
如果OPTIONS中没有USE_LINUX_TPROXY=1,需要重新编译haproxy打开透传用户IP的代码,haproxy编译步骤如下:
1. wget http://www.haproxy.org/download/1.5/src/haproxy-1.5.14.tar.gz
2. tar zxvf haproxy-1.5.14.tar.gz
3. cd haproxy-1.5.14
4. yum install gcc gcc-c++ autoconf automake -y
5. make TARGET=linux2628 arch=x86_64 USE_LINUX_TPROXY=1
6. make install
3.2 haproxy IP透传如何透传用户IP
这一步其实非常简单。haproxy进程只需要拿到用户IP,然后在创建到后端server的tcp连接时,做三件件事情:
1. 将要启用 IP透传的后端服务器组对应的 backend 字段部分增加配置 "source 0.0.0.0 usesrc clientip",这样会将flags设置为1,使能IP透传,详细配置如下:
1. backend XXXX
2. mode tcp
3. balance XXX
4. source 0.0.0.0 usesrc clientip
5. . . .
2. 创建和后端server通信的socket,并调用setsockopt函数, 将socket设置为IP_TRANSPARENT或IP_FREEBIND
3. 调用bind函数,将用户IP绑定到该socket,绑定后后端server看到的该tcp连接IP,即为用户源IP。
haproxy相关代码:
1. int tcp_bind_socket(int fd, int flags, struct sockaddr_storage *local, struct sockaddr_storage *remote)
2. {
3. struct sockaddr_storage bind_addr;
4. int foreign_ok = 0;
5. int ret;
6. static int ip_transp_working = 1;
7. static int ip6_transp_working = 1;
8.
9. switch (local->ss_family) {
10. case AF_INET:
11. /* backend 字段部分增加配置 "source 0.0.0.0 usesrc clientip"后,flags变量值为1 */
12. if (flags && ip_transp_working) {
13. /* 调用setsockopt函数, 将socket设置IP_TRANSPARENT或IP_FREEBIND标识 */
14. if (0
15. #if defined(IP_TRANSPARENT)
16. || (setsockopt(fd, SOL_IP, IP_TRANSPARENT, &one, sizeof(one)) == 0)
17. #endif
18. #if defined(IP_FREEBIND)
19. || (setsockopt(fd, SOL_IP, IP_FREEBIND, &one, sizeof(one)) == 0)
20. #endif
21. #if defined(IP_BINDANY)
22. || (setsockopt(fd, IPPROTO_IP, IP_BINDANY, &one, sizeof(one)) == 0)
23. #endif
24. #if defined(SO_BINDANY)
25. || (setsockopt(fd, SOL_SOCKET, SO_BINDANY, &one, sizeof(one)) == 0)
26. #endif
27. )
28. foreign_ok = 1;
29. else
30. ip_transp_working = 0;
31. }
32. break;
33. }
34. if (flags) {
35. memset(&bind_addr, 0, sizeof(bind_addr));
36. bind_addr.ss_family = remote->ss_family;
37. switch (remote->ss_family) {
38. case AF_INET:
39. if (flags & 1)
40. /* 因为flags变量值为1,这儿只会将客户端IP赋值给bind_addr */
41. ((struct sockaddr_in *)&bind_addr)->sin_addr = ((struct sockaddr_in *)remote)->sin_addr;
42. if (flags & 2)
43. ((struct sockaddr_in *)&bind_addr)->sin_port = ((struct sockaddr_in *)remote)->sin_port;
44. break;
45. default:
46. /* we don't want to try to bind to an unknown address family */
47. foreign_ok = 0;
48. }
49. }
50. setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
51. if (foreign_ok) {
52. if (is_inet_addr(&bind_addr)) {
53. /* 这儿会将客户端IP绑定到该socket */
54. ret = bind(fd, (struct sockaddr *)&bind_addr, get_addr_len(&bind_addr));
55. if (ret < 0)
56. return 2;
57. }
58. }
59. else {
60. if (is_inet_addr(local)) {
61. ret = bind(fd, (struct sockaddr *)local, get_addr_len(local));
62. if (ret < 0)
63. return 1;
64. }
65. }
66. if (!flags)
67. return 0;
3.2 后端server如何返回给haproxy服务器
由于后端收到的包源IP为client IP,默认无法返回给haproxy服务器,因此需要配置haproxy服务器的IP设置client IP端的网关IP,后端server才能将回包返回给haproxy服务器
1. route add -net 10.10.0.0/16 gw 10.10.46.198
3.3 haproxy 服务器将非本地IP的IP包路由到本地环路
配置iptable规则,将发往非haproxy服务器本地(目的IP为客户端源IP)的tcp网络包打上标记(–set-mark 1)
1. iptables -t mangle -F
2. iptables -t mangle -N DIVERT,
3. iptables -t mangle -A PREROUTING -p tcp -m socket -j DIVERT
4. iptables -t mangle -A DIVERT -j MARK --set-mark 1,
5. iptables -t mangle -A DIVERT -j ACCEPT
haproxy 上配置路由将标记(mark 1)的包返回到本地环路lo
1. ip rule del from all fwmark 0x1 lookup 100
2. ip rule add fwmark 1 lookup 100
3. ip route add local 0.0.0.0/0 dev lo table 100
4 总结
综上所述,通过TProxy实现haproxy IP透传,需要做如下四件事情:
1. 确认linux内核版本大于2.6.28,确认haproxy编译时配置了USE_LINUX_TPROXY=1选项
2. 修改haproxy配置,需要启用IP透传的后端服务器组对应的backend 字段部分增加配置 "source 0.0.0.0 usesrc clientip"
3. 后端服务器配置路由规则,将客户端IP端的网关配置haproxy服务器IP
4. haproxy服务器上添加iptables规则和路由策略,将后端服务器的回包路由给本地环路lo
5 参考文献:
http://www.360doc.com/content/13/0821/17/13047933_308812287.shtml
https://blog.csdn.net/frockee/article/details/78641188
相关阅读
点击原文链接,了解华云数据更多信息
以上是关于智汇华云 | 通过TProxy实现haproxy 透传用户IP的主要内容,如果未能解决你的问题,请参考以下文章