HAPRoxy Rate Limiting – Stick-Table Entry Expiration

haproxyrate-limiting

I've been trying to implement rate limiting for some HTTP POST requests on my website. It works great, except for one detail: the expiration of my entry in my stick-table is always reset to 30 seconds, which means that if the client mistakenly makes a request 29 seconds after being blocked, it will be blocked, again, for 30 seconds.

Here's my config, stripped down to the bare minimal for ease of reading:

frontend http-in
        mode    http
        bind *:80

        ### Request limiting
        # Declare stick table
        stick-table type string size 100k expire 30s store gpc0

        # Inspect layer 7
        tcp-request inspect-delay 15s

        # Declare ACLs
        acl source_is_abuser sc0_get_gpc0 gt 0

        tcp-request content track-sc0 req.cook(frontend) if !source_is_abuser
        ### End Request limiting

        use_backend rate-limit if source_is_abuser

        default_backend mybackend

backend mybackend
        mode   http

        stick-table type string size 100k expire 30s store http_req_rate(30s)
        tcp-request content track-sc1 req.cook(frontend) if METH_POST

        acl post_req_rate_abuse sc1_http_req_rate gt 30
        acl mark_as_abuser sc0_inc_gpc0 gt 0

        tcp-request content accept if post_req_rate_abuse mark_as_abuser

        server myLocalhost 127.0.0.1:8081

backend rate-limit
        mode http
        errorfile 503 /usr/local/etc/haproxy/rate-limit.http

With this config, as soon as a client makes more than 1 request per second over 30 seconds, this client is marked as an abuser by mybackend. The following request are then, as expected, blocked by the http-in frontend.

However, every time the currently marked source_is_abuser client sends a request, the expiration counter of http-in 's stick-table is reset to 30 seconds. I would expect the expiration counter to keep going down, since the connection is supposedly only tracked when !source_is_abuser.

Any insight into what I am doing wrong?

Best Answer

To check the counter without updating its value, you can use table_http_req_rate. In your case you can put the below config in your frontend, so that you can deny the request before reaching the stick-table.

http-request deny if { key,table_http_req_rate(name_of_your_table) gt 30 }

More reference: Stick table doc