如何限制HAProxy中的请求频率:分步说明


作者介绍了如何在HAProxy中使用某些IP地址实现查询速度限制(速率限制)。Mail.ru云解决方案团队翻译了他的文章-我们希望有了它,您不必花很多时间和精力。

事实是,这是保护服务器免受DoS攻击的最流行的方法之一,但是很难在Internet上找到明确的说明来具体配置它。通过反复试验,作者强迫HAProxy限制IP地址列表中请求的频率,该列表是实时更新的。

配置HAProxy不需要任何先验知识,因为下面概述了所有必要步骤。

开源和免费的HAProxy是高度可访问的负载平衡器和代理服务器。近年来,由于它以最少的资源提供高性能而变得非常流行与替代程序不同,HAProxy Community Edition的非营利版本提供了足够的功能来实现可靠的负载平衡。

起初很难弄清楚这个程序。但是,她有非常细致而详尽的技术文档作者说,这是他使用过的所有开源程序中最详细的文档。
因此,这是分步指南。

配置负载均衡器


为了节省时间并且不受基础结构设置的干扰,请拍摄DockerDocker Compose映像并快速启动主要组件。

第一个任务是使用多个Apache后端服务器提高HAProxy负载均衡器的工作实例。

克隆存储库


$ git clone git@github.com:stargazer/haproxy-ratelimiter.git
$ cd haproxy-ratelimiter

您可以查看Dockerfiledocker-compose.yml带有安装参数。他们的讨论不在本文讨论范围之内,因此,让我们详细了解一下他们创建了一个有效的HAProxy实例,称为loadbalancer两个后端服务器api01api02。要配置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地址存在滥用。Truegpc0gpc0Trueabuse indicator
  • acl abuse_cnt src_get_gpc0(Abuse) gt 0如果该值大于零,函数abuse_cnt返回换句话说,他说这个IP地址是否已经被发现滥用。Truegpc0

如前所述,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_abuseACL 的功能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处理请求的速度的局限性。为了正确地做所有事情,您需要一个相当“低级”的思维和一些非直觉的行动。本部分中的文档可能过于技术性,并且缺少基本示例。我们希望本指南能够弥补这一不足,并向所有想走这条路的人指明方向。

还有什么要读的

  1. Mail.ru云解决方案平台中如何实现容错架构
  2. 十大Kubernetes技巧和窍门
  3. 我们关于数字转换的电报频道

All Articles