HaProxy 1.8 – Using Stick Tables and Passing Rates as Request Headers

haproxyrate-limiting

I have read this article about stick tables: https://www.haproxy.com/blog/introduction-to-haproxy-stick-tables/

I would like to add rate limiting to my haproxy config. But before I want to add a hard limit, I want to know how many requests each of my customers make in order to see what a good limit threshold would be and to be able to contact the customers exceeding this limit, before it is added as a hard limit.

My backends are prepared to store and log these kinds of information on a custom-level. All I need to do in step 1 is to let haproxy pass on the following values:

  • Request rate per ip (http_req_rate) which describes the number of http requests per X time (Used to prevent http flooding)
  • Connection rate per ip (conn_rate) which describes the number of new connections (When using keep alive, this number will differ from the http_req_rate) (Used to prevent tcp syn flood)
  • Current connection count per ip (conn_cur) (Used to prevent connection table exhaustion)

I have made the following config which does the job:

(global, listen, default and frontend sections not relevant)

// # round robin balancing between the various backends
backend app
  # Bind the 3 stick-tables counting to 3 slots named track-sc0, -sc1 and -sc2
  http-request track-sc0 src table sticktable_st_src_http_req_rate
  http-request track-sc1 src table sticktable_st_src_conn_cur
  http-request track-sc2 src table sticktable_st_src_conn_rate

  # Save current values in variables to be able to parse them on to backends
  http-request set-var(req.http_req_rate) sc_http_req_rate(0)
  http-request set-var(req.conn_cur) sc_conn_cur(1)
  http-request set-var(req.conn_rate) sc_conn_rate(2)

  # Finally copy the variables into request headers for the real backends to read the values
  http-request set-header X-HaProxy-http_req_rate %[var(req.http_req_rate)]
  http-request set-header X-HaProxy-conn_cur      %[var(req.conn_cur)]
  http-request set-header X-HaProxy-conn_rate     %[var(req.conn_rate)]

  ...
  server localtest 192.168.1.130:80 check

backend sticktable_st_src_http_req_rate
  stick-table type ip size 1m expire 10s store http_req_rate(10s)

backend sticktable_st_src_conn_cur
  stick-table type ip size 1m expire 10s store conn_cur

backend sticktable_st_src_conn_rate
  stick-table type ip size 1m expire 10s store conn_rate(10s)

Even though this actually does the job, it becomes a litte messy when having 3-5 backend sections which all needs this.

Another thing is that it does not look very clever, to

1) define three backends
2) use these three backends in 3 track commands
3) copy these into temp variables (because I do not know how to parse them directly into request headers
4) refer the temp vars when setting the request headers.

Can this config in any way be compressed or simplified to make it apear more dry?

HaProxy version 1.8

Thanks

Best Answer

If you just want to have a look to the stick table values, you can use the Runtime API (stat socket): https://www.haproxy.com/blog/dynamic-configuration-haproxy-runtime-api/ and use the "show table < table-name >" command.

Regarding your config:
1. you don't need to have a separate stick table for each counter. You can store all the counters in the same table.
2. You don't need to use intermediate variables

backend app
  # Enable counters tracking
  http-request track-sc0 src table sticktable_st_src_rates
  # Put counters in HTTP headers
  http-request set-header X-HaProxy-http_req_rate %[sc_http_req_rate(0)]
  http-request set-header X-HaProxy-conn_cur      %[sc_conn_cur(0)]
  http-request set-header X-HaProxy-conn_rate     %[sc_conn_rate(0)]

  ...
  server localtest 192.168.1.130:80 check

backend sticktable_st_src_rates
  stick-table type ip size 1m expire 10s store http_req_rate(10s),conn_cur,conn_rate(10s)