|
|
|
|
移动端

深度使用Linux防火墙:允许我们构建Spectrum的黑客行为

今天我们来介绍spectrum:一项Cloudflare新业务,可为任何基于TCP协议的服务提供DDoS防护、负载均衡和内容加速功能。

作者:龚德阳来源:云技术实践|2018-05-04 08:43

人工智能+区块链的发展趋势及应用调研报告


今天我们来介绍spectrum:一项Cloudflare新业务,可为任何基于TCP协议的服务提供DDoS防护、负载均衡和内容加速功能。

在我们开始构建Spectrum之后不久,我们遇到了一个主要的技术障碍:Spectrum要求我们接受任意有效的TCP端口上的连接,从1到65535,但在我们的Linux边界服务器上,不可能“接受任意端口的入站连接“。这不是Linux的特定限制,它是BSD套接字API的一个特性,且是大多数操作系统上网络应用程序的基础,为了交付Spectrum,我们需要解决两个重叠的问题:

1、如何接受从1到65535的所有端口号上的TCP连接?

2、如何配置单台Linux服务器去接受大量IP地址上的连接?(我们的anycast范围中有数千个IP地址)

将数百万个IP分配给服务器

Cloudflare的边界服务器配置几乎相同,在我们早期的时候,我们曾经为本地环回网络接口分配特定的/32(和/128)段IP地址。在我们只有数十个IP地址时一切运行良好,但随着我们规模不断增长开始出现问题。

接下来是“AnyIP”技巧。AnyIP允许我们将整个IP前缀(子网)分配给本地环回接口,从特定IP地址扩展。AnyIP已被普遍使用:你的计算机将127.0.0.0/8分配给本地环回接口。从计算机的角度来看,自127.0.0.1到127.255.255.254范围内的所有IP地址都属于本地计算机。

这个技巧适用于超过127.0.0.1/8的块,要在本地分配整个192.0.2.0/24网段,请运行:

  1. ip route add local 192.0.2.0/24 dev lo 

然后,你可将其中的一个IP地址绑定到8080端口:

  1. nc -l 192.0.2.1 8080 

如果是IPv6的情况下工作起来会有点困难:

  1. ip route add local 2001:db8::/64 dev lo 

可悲的是,你不能像IPv4示例那样将端口绑定到这些附加的IPv6地址,为了让它工作,你必须使用IP_FREEBIND套接字选项,这需要更高的权限,考虑到完整性,还有一个net.ipv6.ip_nonlocal_bind内核参数,但我们不建议你做调整。

这个AnyIP技巧允许我们为每台服务器本地分配数百万个IP地址:

  1. $ ip addr show 
  2. 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 
  3.    inet 1.1.1.0/24 scope global lo 
  4.       valid_lft forever preferred_lft forever 
  5.    inet 104.16.0.0/16 scope global lo 
  6.       valid_lft forever preferred_lft forever 

绑定到所有端口

第二个主要问题是为任意端口号打开TCP套接字的能力,在Linux中,通常在任意支持BSD sockets API的系统中,你只能通过一个绑定系统调用来绑定到特定TCP端口,不能在单一操作中绑定到多个端口。

一个简单的解决方案是绑定65535次,每次为65535个可能的端口,确实,这可能是一种选择,但会带来不良后果:

监听套接字的后果

在内部,Linux内核在一个以端口号为索引的哈希表中存储监听套接字,LHTABLE,使用32个buckets。

/*是,真的,这就是你所需要的。*/

#定义INET_LHTABLE_SIZE 32

假如我们打开了65k个端口,那么对这张表的查询速度降大幅减慢,而且每个哈希表bucket将包含2000个条目。

解决我们问题的另一种方法是使用iptables强大的NAT功能,我们可以将入站包的目标地址重定向到某个特定的地址/端口,同时将我们的应用程序绑定到这个地址/端口。

可我们不想这样做,因为它需要启用iptables conntrack模块。 从以往经验来看,我们发现了一些性能优势案例,而conntrack无法应对我们遇到的一些大型DDoS攻击场景。

另外,使用NAT方案,我们将丢失目标IP地址信息。为了解决这个问题,SO_ORIGINAL_DST套接字选项存在一个不为人知的地方,但代码看起来并不令人鼓舞。

庆幸的是,有一种方法可以实现我们的目标,不涉及绑定到所有65k端口,或使用conntrack。

拯救防火墙

在我们进一步讨论前,让我们重新审视下操作系统中网络数据包的一般流程。

通常,入站数据包路径中有两个不同的层:

IP防火墙

网络堆栈

这些概念是截然不同的。IP防火墙通常是一个无状态的软件(现在让我们忽略conntrack和IP碎片重新封装)。 防火墙分析IP数据包并决定是否接受或丢弃它们。 请注意:在这一层,我们谈论的是数据包和端口号-而不是应用程序或套接字。

然后才是网络堆栈。这只野兽保持了很多状态。其主要任务是将入站IP数据包转发到套接字中,然后由用户空间应用程序处理。网络堆栈管理对象共享用户空间。它重新封装TCP流,处理路由,并知道哪些IP地址是本地的。

魔法光效尘埃

在某个时候,我们偶然发现了TPROXY iptables模块。官方文件很容易被忽略:

TPROXY

这个对象只在mangle表中有效

PREROUTING链和用户定义的链只是从这个链条调用。它将数据包重定向到本地套接字,而无需以任何方式更改数据包标签头。它还可以更改标记值,然后可以在高级路由规则中使用该标记值。

在内核中可以找到另一份文档:

  1. docs/networking/tproxy.txt(https://www.kernel.org/doc/Documentation/networking/tproxy.txt) 

我们越想,就越好奇…

所以…TPROXY究竟是做什么的呢?

魔法揭秘

TPROXY代码非常简单:

  1. case NFT_LOOKUP_LISTENER: 
  2.  sk = inet_lookup_listener(net, &tcp_hashinfo, skb, 
  3.                     ip_hdrlen(skb) + 
  4.                       __tcp_hdrlen(tcph), 
  5.                     saddr, sport, 
  6.                     daddr, dport, 
  7.                     in->ifindex, 0); 

让我为您解读:在iptables模块中,它是防火墙的一部分,我们调用inet_lookup_listener。该函数接受src/dst端口/IP 4-tuple,并返回能够接受该连接的监听套接字。这是网络堆栈的套接字调度的核心功能。

重述:防火墙代码调用一个套接字调度事务。

稍后TPROXY会实际执行套接字调度。

skb->sk = sk;

这一行将一个套接字结构的sock分配给入站包——完成分发。

把兔子从帽子里拽出来

通过使用TPROXY,我们可以很容易地执行绑定到所有端口的技巧。这是配置:

  1. Set 192.0.2.0/24 to be routed locally with AnyIP. 
  2. # Make it explicit that the source IP used for this network 
  3. when connecting locally should be in 127.0.0.0/8 range. 
  4. # This is needed since otherwise the TPROXY rule would match 
  5. # both forward and backward traffic. We want it to catch 
  6. forward traffic only
  7. sudo ip route add local 192.0.2.0/24 dev lo src 127.0.0.1 
  8.  
  9. Set the magical TPROXY routing 
  10. sudo iptables -t mangle -I PREROUTING 
  11.        -d 192.0.2.0/24 -p tcp 
  12.        -j TPROXY --on-port=1234 --on-ip=127.0.0.1 

除了设置这个地方以外,您还需要启动一个带有魔力的IP_TRANSPARENT套接字选项的TCP服务器。下面的示例需要监听tcp://127.0.0.1:1234。IP_TRANSPARENT的帮助页面显示信息:

  1. IP_TRANSPARENT (since Linux 2.6.24) 
  2. Setting this boolean option enables transparent proxying on 
  3. this socket.  This socket option allows the calling applica‐ 
  4. tion to bind to a nonlocal IP address and operate both as a 
  5. client and a server with the foreign address as the local 
  6. end‐point.  NOTE: this requires that routing be set up in 
  7. a way that packets going to the foreign address are routed 
  8. through the TProxy box (i.e., the system hosting the 
  9. application that employs the IP_TRANSPARENT socket option). 
  10. Enabling this socket option requires superuser privileges 
  11. (the CAP_NET_ADMIN capability). 
  12.  
  13. TProxy redirection with the iptables TPROXY target also 
  14. requires that this option be set on the redirected socket. 

这是一个简单的Python服务器:

  1. import socket 
  2.  
  3. IP_TRANSPARENT = 19 
  4.  
  5. s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
  6. s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 
  7. s.setsockopt(socket.IPPROTO_IP, IP_TRANSPARENT, 1) 
  8.  
  9. s.bind(('127.0.0.1', 1234)) 
  10. s.listen(32) 
  11. print("[+] Bound to tcp://127.0.0.1:1234"
  12. while True
  13.    c, (r_ip, r_port) = s.accept() 
  14.    l_ip, l_port = c.getsockname() 
  15.    print("[ ] Connection from tcp://%s:%d to tcp://%s:%d" % (r_ip, r_port, l_ip, l_port)) 
  16.    c.send(b"hello world "
  17. c.close() 

运行服务器后,您可以从任意的IP地址连接到它:

  1. $ nc -v 192.0.2.1 9999 
  2. Connection to 192.0.2.1 9999 port [tcp/*] succeeded! 
  3. hello world 

最重要的是,服务器将提示请求确实被重定向到192.0.2.1的9999端口,即使那个IP地址和端口没真正监听。:

  1. $ nc -v 192.0.2.1 9999 
  2. Connection to 192.0.2.1 9999 port [tcp/*] succeeded! 
  3. hello world 

这就是在不使用conntrack.前提下如何绑定到Linux的任意端口的方法。

总结

在这篇文章中,我们介绍了如何使用一个不起眼的iptables模块,它最初设计用于实现透明代理,因为它稍有不同。借助它,我们可以使用标准的BSD套接字API执行我们认为不可能实现的任务,从而避免需要任何定制的内核补丁。

TPROXY模块极不寻常-在Linux防火墙的上下文中,它执行通常由Linux网络堆栈完成的事情。官方文档相当缺乏,我不相信许多Linux用户理解这个模块的全部功能。

可以这么说,TPROXY允许我们的Spectrum产品在最初版本内核上平稳运行,同时让我们认识到,尝试理解iptables和网络堆栈式多么重要!

将IP地址分配给本地环回地址,附加适当的rp_filter和BGP配置,可以在我们的边界服务器处理任意IP范围。

【编辑推荐】

  1. 能让程序员假装很忙的三个Linux命令行工具
  2. 在Linux下9个有用的touch命令示例
  3. 每个Linux新手都应该知道的10个命令
  4. Yoda:您的Linux系统命令行个人助理
  5. 如何使用Linux防火墙隔离本地欺骗地址
【责任编辑:武晓燕 TEL:(010)68476606】

点赞 0
分享:
大家都在看
猜你喜欢

读 书 +更多

鸟哥的Linux私房菜——服务器架设篇(第二版)

本书是对连续三年蝉联畅销书排行榜前10名的《Linux鸟哥私房菜——服务器架设篇》的升级版,新版本根据目前服务器与网络环境做了大幅度修订...

订阅51CTO邮刊

点击这里查看样刊

订阅51CTO邮刊