作者介绍了如何在HAProxy中使用某些IP地址实现查询速度限制(速率限制)。Mail.ru云解决方案团队翻译了他的文章-我们希望有了它,您不必花很多时间和精力。事实是,这是保护服务器免受DoS攻击的最流行的方法之一,但是很难在Internet上找到明确的说明来具体配置它。通过反复试验,作者强迫HAProxy限制IP地址列表中请求的频率,该列表是实时更新的。配置HAProxy不需要任何先验知识,因为下面概述了所有必要步骤。开源和免费的HAProxy是高度可访问的负载平衡器和代理服务器。近年来,由于它以最少的资源提供高性能而变得非常流行。与替代程序不同,HAProxy Community Edition的非营利版本提供了足够的功能来实现可靠的负载平衡。起初很难弄清楚这个程序。但是,她有非常细致而详尽的技术文档。作者说,这是他使用过的所有开源程序中最详细的文档。因此,这是分步指南。配置负载均衡器
为了节省时间并且不受基础结构设置的干扰,请拍摄Docker和Docker Compose映像并快速启动主要组件。第一个任务是使用多个Apache后端服务器提高HAProxy负载均衡器的工作实例。克隆存储库
$ git clone git@github.com:stargazer/haproxy-ratelimiter.git
$ cd haproxy-ratelimiter
您可以查看Dockerfile
并docker-compose.yml
带有安装参数。他们的讨论不在本文讨论范围之内,因此,让我们详细了解一下他们创建了一个有效的HAProxy实例,称为loadbalancer
两个后端服务器api01
和api02
。要配置HAProxy,我们将首先使用该文件haproxy-basic.cfg
,然后切换到haproxy-ratelimiting.cfg
。为简单起见,已将配置文件haproxy-basic.cfg
减少到最低限度并清除了多余的文件。让我们看一下:defaults
mode http
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
frontend proxy
bind *:80
use_backend api
backend api
balance roundrobin
server api01 api01:80
server api02 api02:80
本节frontend proxy
将HAProxy设置为侦听端口80,并将所有请求转发到api
后端的服务器池。类别backend api
指定后端池api
有两个后端服务器api01
,并api02
与相应的地址。通过负载平衡算法选择用于服务每个传入请求的服务器roundrobin
,也就是说,实际上依次使用了两个可用服务器。让我们启动所有三个容器
$ sudo docker-compose up
现在我们有了一个容器loadbalancer
,可以将请求重定向到两个后端api01
和服务器api02
。如果我们在地址栏中输入,我们将得到其中之一的答复http://localhost/
。刷新页面几次并查看日志很有趣docker-compose
。api01_1 | 192.168.192.3--[08 /一月/ 2019:11:38:09 +0000]“ GET / HTTP / 1.1” 200 45
api02_1 | 192.168.192.3--[08 /一月/ 2019:11:38:10 +0000]“ GET / HTTP / 1.1” 304-
api01_1 | 192.168.192.3--[08 /一月/ 2019:11:38:10 +0000]“ GET / HTTP / 1.1” 304-
api02_1 | 192.168.192.3--[08 /一月/ 2019:11:38:11 +0000]“ GET / HTTP / 1.1” 304-
api01_1 | 192.168.192.3--[08 /一月/ 2019:11:38:11 +0000]“ GET / HTTP / 1.1” 304-
api02_1 | 192.168.192.3--[08 /一月/ 2019:11:38:11 +0000]“ GET / HTTP / 1.1” 304-
如您所见,两个服务器api
依次处理请求。现在我们有了一个具有非常简单的负载平衡配置的HAProxy实例,并且对它的工作方式有了一些想法。添加请求数量限制
要设置对负载均衡器的请求数限制,您需要修改HAProxy实例中的配置文件。确保容器loadbalancer
使用配置文件haproxy-ratelimiter.cfg
。只需修改Dockerfile即可替换配置文件。FROM haproxy:1.7
COPY haproxy-ratelimiter.cfg /usr/local/etc/haproxy/haproxy.cfg
设定限制
所有设置都注册在配置文件中haproxy-ratelimiter.cfg
。让我们仔细研究它。defaults
mode http
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
frontend proxy
bind *:80
# ACL function declarations
acl is_abuse src_http_req_rate(Abuse) ge 10
acl inc_abuse_cnt src_inc_gpc0(Abuse) gt 0
acl abuse_cnt src_get_gpc0(Abuse) gt 0
# Rules
tcp-request connection track-sc0 src table Abuse
tcp-request connection reject if abuse_cnt
http-request deny if abuse_cnt
http-request deny if is_abuse inc_abuse_cnt
use_backend api
backend api
balance roundrobin
server api01 api01:80
server api02 api02:80
backend Abuse
stick-table type ip size 100K expire 30m store gpc0,http_req_rate(10s)
HAProxy提供了一组低级原语,这些原语提供了更大的灵活性,并且适用于各种用例。它的计数器经常使我想起CPU中的累积寄存器(加法器)。它们存储中间结果,采用不同的语义作为输入,但最后它们只是数字。为了很好地理解所有内容,从配置文件的最后开始就很有意义。表 Abuse
backend Abuse
stick-table type ip size 100K expire 30m store gpc0,http_req_rate(10s)
在这里,我们设置了一个虚拟后端Abuse
(“滥用”)。虚构的,因为它仅用于stick-table,其余配置可通过name引用Abuse
。棒表是存储在过程存储器中的表,您可以在其中为每个记录确定寿命。我们的表具有以下特征:type ip
:将请求按IP地址作为键保存在表中。因此,来自相同IP地址的请求将引用同一记录。本质上,这意味着我们正在跟踪IP地址和相关数据。
size 100K
:该表最多包含10万条记录。
expire 30m
:记录保留期为不活动30分钟。
store gpc0,http_req_rate(10s)
:计数器gpc0
和最近10秒的IP地址请求数与条目一起存储。在此帮助下,gpc0
我们将跟踪滥用情况下IP地址被发现的次数。实际上,正计数器值表示该IP地址已被标记为可疑。我们将此计数器称为abuse indicator
。
通常,该表Abuse
使您可以跟踪IP地址是否标记为可疑,以及该地址当前的请求频率。因此,我们拥有记录的历史以及实时信息。现在,让我们继续该部分frontend proxy
,看看那里有什么新内容。ACL功能和规则
frontend proxy
bind *:80
# ACL function declarations
acl is_abuse src_http_req_rate(Abuse) ge 10
acl inc_abuse_cnt src_inc_gpc0(Abuse) gt 0
acl abuse_cnt src_get_gpc0(Abuse) gt 0
# Rules
tcp-request connection track-sc0 src table Abuse
tcp-request connection reject if abuse_cnt
http-request deny if abuse_cnt
http-request deny if is_abuse inc_abuse_cnt
use_backend api
访问控制列表(ACL)是仅在设置规则时调用的函数声明。让我们仔细看看这三个ACL条目。请记住,它们都明确引用了一个Abuse
使用IP地址作为键的表,因此每个功能都适用于请求的IP地址:acl is_abuse src_http_req_rate(Abuse) ge 10
:如果当前请求频率大于或等于十,则该函数is_abuse
返回True
。
acl inc_abuse_cnt src_inc_gpc0(Abuse) gt 0
:如果增量值大于零,则函数inc_abuse_cnt
返回。由于初始值为零,因此该函数始终返回。换句话说,它增加了值,本质上表示此IP地址存在滥用。True
gpc0
gpc0
True
abuse indicator
acl abuse_cnt src_get_gpc0(Abuse) gt 0
:如果该值大于零,则函数abuse_cnt
返回。换句话说,他说这个IP地址是否已经被发现滥用。True
gpc0
如前所述,ACL只是简单的声明,即函数声明。它们只有在触发某些规则后才适用于传入请求。查看同一部分中定义的规则很有意义frontend
。规则将一个一个地应用于每个传入请求,并从我们刚定义的ACL中运行功能。让我们看看每个规则的作用:tcp-request connection track-sc0 src table Abuse
:向表添加查询Abuse
。由于密钥是表中的IP地址,因此此规则将添加到IP地址列表中。
tcp-request connection reject if abuse_cnt
: TCP-, IP- , abuse. , TCP- IP-.
http-request deny if abuse_cnt
: , IP- . IP-, abuse.
http-request deny if is_abuse inc_abuse_cnt
: , is_abuse
inc_abuse_cnt
True
. , , IP- , IP- .
本质上,我们引入了两种类型的检查:实时检查和查询历史记录中的黑名单检查。如果发现滥用该IP地址,第二条规则将拒绝所有新的TCP连接。第三个规则禁止对来自黑名单的IP地址的HTTP请求进行服务,无论该地址当前的请求频率如何。第四个规则确保在克服请求频率的阈值后立即拒绝来自IP地址的HTTP请求。因此,第二条规则主要适用于新的TCP连接,第三条规则和第四条规则适用于已建立的连接,第一条规则是历史检查,第二条是实时检查。让我们尝试一下过滤器!
现在,我们可以再次组装并启动容器了。$ sudo docker-compose down
$ sudo docker-compose build
$ sudo docker-compose up
负载平衡器应在两个后端服务器之前启动。让我们将浏览器定向到http://localhost/
。如果我们快速刷新该页面十次,则将在十秒钟的间隔内超过十个请求的阈值-并且我们的请求将被拒绝。如果我们继续刷新页面,则即使在建立TCP连接之前,新请求也会立即被拒绝。问题
为什么每十秒钟限制十个请求?
该表Abuse
确定http_req_rate(10s)
,即请求的频率是在十秒的窗口中测量的。在指定的时间间隔内,is_abuse
ACL 的功能True
以≥10的请求速率返回。因此,滥用被认为是十秒钟内发出十个或更多请求的频率。例如,在本文中,我们决定设置一个下限,以使检查限制器的操作更加容易。http-request和tcp-request连接规则之间有什么区别?
从文档中:http-request:http-request语句定义了一组适用于网络层7 [OSI模型]的规则
从文档中:tcp-request连接:根据网络层4的条件对传入连接执行操作
HTTP-, TCP-?
想象一下,对服务器的HTTP请求从同一IP地址发送多个TCP连接。HTTP请求的频率将很快超过阈值。然后,第四条规则生效,该规则丢弃请求并将IP地址列入黑名单。现在,完全有可能来自同一IP地址的HTTP连接保持打开状态(请参阅持久HTTP连接),并且HTTP请求的频率已降至阈值以下。第三个规则保证了HTTP请求的持续阻止,因为它是abuse indicator
在此IP上触发的。现在,假设几分钟后,同一IP尝试建立TCP连接。它们会被立即删除,因为第二条规则适用:它会看到标记的IP地址并立即删除连接。结论
首先,可能很难理解使用HAProxy处理请求的速度的局限性。为了正确地做所有事情,您需要一个相当“低级”的思维和一些非直觉的行动。本部分中的文档可能过于技术性,并且缺少基本示例。我们希望本指南能够弥补这一不足,并向所有想走这条路的人指明方向。还有什么要读的:- Mail.ru云解决方案平台中如何实现容错架构。
- 十大Kubernetes技巧和窍门。
- 我们关于数字转换的电报频道。