• Ukieweb

    佳的博客

    曾梦想仗剑天涯,后来工作忙没去。

haproxy 限制流量 限制连接数

场景

我们的 haproxy 作为负载均衡,当前端大量的连接过来,后端处理不过来,会把后面服务器搞死 504。以至于不能正常服务。所以我们可以在 haproxy 拦截一部分流量,使后端不至于崩掉

您可以设置速率限制以防范应用程序层DDoS攻击

1. 限制速率的几种方法

每种方法配合使用 HAProxy 灵活的配置语言结合: 访问控制列表(ACL), stick tables 和 maps,以构成不同的解决方案

例如如下方法

  • 限制连接数

  • 流入的字节数

  • 流出的字节数

  • 最大错误数

当看到客户端超过速率限制时,HAProxy 可以采取许多措施

  • 暂存它们

  • 将它们发送到其他服务器池

  • 禁止它们较长时间

2. 设置最大连接数 Setting the Maximum Connections

在讲速率限制之前,我们可以启用 queuing queuing  可以将多余的连接存储在 HAProxy 中,直到腾出服务器处理它们为止。打开 queuing 还可以在保持大量连接时,而不会显着增加内存CPU使用率

maxconn 参数来限制并发连接数的数量,

2.1 设置最多给每个服务器发送 30 个连接

所有服务器达到最大连接数后其余的连接将在 Haproxy 内排队

backend servers
    server s1 192.168.30.10:80 check  maxconn 30
    server s2 192.168.31.10:80 check  maxconn 30
    server s3 192.168.31.10:80 check  maxconn 30

如上面配置,三台服务器每个上面上都有30个连接,则新连接必须排队等待。 这意味着服务器本身不会过载!!

2.2 给在队列中连接指定超时时间

设置超时的目的是:

让客户端收到 503 服务不可用,总比把你的服务器压死的好吧。当然,也可以更早的让客户收到错误信息,而不是等待更长的时间并可能导致更难解决的错误。

backend servers
    timeout queue 10s
    server s1 192.168.30.10:80 check  maxconn 30
    server s2 192.168.31.10:80 check  maxconn 30
    server s3 192.168.31.10:80 check  maxconn 30

3. 滑动窗口限制速率 Sliding Window Rate Limiting

您想限制用户在一定时间可以发出的请求数

3.1 限制每个客户端最近10秒钟最多允许20个请求。【更多示例点击看查】

frontend website
    bind :80
    
    stick-table  type ip  size 100k  expire 30s  store http_req_rate(10s)
    http-request track-sc0 src
    http-request deny deny_status 429 if { sc_http_req_rate(0) gt 20 }
    
    default_backend servers

这里我们允许每个客户端最多有 20 个连接数拒绝额外的请求,并返回 429 状态码。sc_http_req_rate 方法返回客户端当前请求速率

其他的处理方法还有:

  • 把客户端转发到其他的后端

  • 丢弃连接

配置解释

stick-table 指令创建一个键/值存储,用于存储计数器(如: 每个客户端HTTP 请求速率)。

  • 是由 type 参数指定的,这里是客户端 IP 地址,用于存储和汇总该客户端的请求数量。

  • expire 参数设置,设置客户端经过一段不活动的时间后将其删除,如果没有设置,那么在存储空间已满时将清除最旧的记录

  • size  保存条目数量,这里我们允许 100,000 条记录。

  • http-request track-sc0 src 行将客户机添加为 stick table 中的记录。当有 ip 加入计数就开始。

  • http-request deny 行设置 速率限制阈值 以及 超过该阈值时应采取的措施

4. 固定窗口限制速率 Rate Limit by Fixed Time Window

假设您希望每天最多允许1000个请求。在零点重置。

在这里你需要使用 http_req_cnt 不是上面的 http_req_rate,它会一直递增直到重置达到到期为止, 然后,您将使用 HAProxy Runtime API 在恰好午夜清除所有记录。

frontend website
    bind :80
    
    stick-table  type ip  size 100k  expire 24h  store http_req_cnt
    http-request track-sc0 src
    http-request deny deny_status 429 if { sc_http_req_cnt(0) gt 1000 }
    
    default_backend servers

当一个客户端发送超过 1000 个请求,他们将被拒绝

你需要通过向您的 HAProxy 配置global 部分添加 stats 套接字指令来启用 Runtime API 以在每天结束时重置此状态的方法。

global
    stats socket /run/haproxy.sock mode 660 level admin

接下来,安装 socat 实用程序,并使用它调用 clear table Runtime API 命令来清除 stick 表中的所有记录

$ echo "clear table website" | sudo socat stdio /run/haproxy.sock

您可以设置一个 cron 作业以每天自动执行此操作。 如果您需要一次性清除一条记录,则可以包含客户的 IP 地址,如下所示

$ echo "clear table website key 192.168.50.10" | sudo socat stdio /run/haproxy.sock

5. 通过 URL 限制速率 Rate Limit by URL

有些页面需要比其他页面更多的处理时间,这时候你可能需要通过以 URL 维度设置阈值

1. 首先需要在  /etc/haproxy 下面新建一个 rates.map 文件(格式如下),这个文件关联 URL 路径他们的速率限制

/urla  10
/urlb  20
/urlc  30

2. 然后 haproxy 配置文件中配置

frontend website

    bind :80
    
    stick-table  type binary  len 8  size 100k  expire 10s  store http_req_rate(10s)
    
    # Track client by base32+src (Host header + URL path + src IP)
    http-request track-sc0 base32+src
    
    # Check map file to get rate limit for path
    http-request set-var(req.rate_limit)  path,map_beg(/etc/haproxy/rates.map,20)
    
    # Client's request rate is tracked
    http-request set-var(req.request_rate)  base32+src,table_http_req_rate()
    
    # Subtract the current request rate from the limit
    # If less than zero, set rate_abuse to true
    acl rate_abuse var(req.rate_limit),sub(req.request_rate) lt 0   
    
    # Deny if rate abuse
    http-request deny deny_status 429 if rate_abuse
    
    default_backend servers

我们没有指定 stick 表中的IP地址,而是指定了一种二进制类型。其中填充 HTTP Host headerURL 路径客户端的源IP地址哈希。当调用 http-request track-sc0 base32+src 行时,您将获得所有这些。这样,您可以在许多不同的网页上区分客户的请求率

第一个 http-request set-var 行在 rate.map 文件中找到当前请求的URL路径请求速率阈值。如果找不到请求的 URL,则使用默认值 20。它将结果存储在名为 req.rate_limit变量中。

第二个 http-request set-var 行将客户该页面当前请求率存在 req.request_rate 的变量

我们 req.rate_limit 减(subtract) 去 req.rate_limit ,并确保差值大于零。  如果不是,我们将拒绝该请求,因为它们已超过该页面的阈值

5. 通过 URL 参数 限制速率 Rate Limit by URL Parameter

这个和通过 URL路径 进行速率限制相比有些许变化。 如果您的客户端URL中包含一个API令牌以标识自己,通过URL参数进行速率限制

frontend website

    bind :80
    
    stick-table type string size 100k expire 24h store http_req_rate(24h)
    
    # check for token parameter
    acl has_token url_param(token) -m found    
    
    # check if exceeds limit
    acl exceeds_limit url_param(token),table_http_req_rate() gt 1000
    
    # start tracking based on token parameter
    http-request track-sc0 url_param(token) unless exceeds_limit
    
    # Deny if missing token or exceeds limit
    http-request deny deny_status 429 if !has_token or exceeds_limit
    
    default_backend servers

在这里,我们使用 24小时的滑动窗口,在此期间,客户最多可以提出1000 个请求

stick table类型string

http-request track-sc0 行将名为 tokenURL参数 存储为表中的键。url 形如:http://yourwebsite.com/api/v1/does_a_thing?token=abcd1234

请注意:我们已在 http-request track-sc0 行的末尾添加了unless exceeds_limit 子句,因为在超出限制后没有必要继续增加计数器。 它还可以防止永久阻止客户端

has_token  ACL 确保URL中包含令牌

exceeds_limit ACL 查找最近 24 小时内的当前请求计数

http-request deny 行拒绝 哪些 没有 token 或者 超过 limit 限制的请求。

6. http_req_rate  和  http_req_cnt 区别

http_req_rate

过去最近24小时内的滑动窗口。

http_req_cnt

从用户发送第一个请求时开始,并从那时起递增直到到期;

但是,除非每隔 24 小时通过 Runtime API 手动清除该表,当客户端保持活动状态时,http_req_cnt 可能会长时间保留,因为每当有记录更新时,过期时间就会重置




摘自:

https://www.haproxy.com/blog/four-examples-of-haproxy-rate-limiting/




0
0
下一篇:Kubernetes 中网络插件 calico 与 flannel 比较

0 条评论

老佳啊

85后,大专学历,中原人士,家里没矿。

由于年轻时长的比较帅气,导致在别人眼里,我一直不谈恋爱的原因是清高,实则是自己的小自卑。最大的人生目标就是找一个相知相爱相容的人,共度余生。

和人相处时如果能感受到真诚,会非常注重彼此的关系,对别人没有什么心机,即使有利益冲突,一般也会以和为贵,因为在这个世界上,物质的东西,从来不会吸引到我。

特别迷恋那些大山大水,如果现在还能隐居,可能早就去了。对那些宏伟的有底蕴的人文景观比较不感冒。

从事于IT行业,却一直对厨房念念不忘,由于身材魁梧,总觉得自己上辈子是个将军,可惜这辈子没当兵,也不会打架。