diff --git a/doc/configuration.txt b/doc/configuration.txt index 84465e82b..24bf1bc86 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -4070,6 +4070,44 @@ connslots(backend) dynamic connections. Also, if any of the server maxconn, or maxqueue is 0, then this acl clearly does not make sense - in which case the value returned will be -1. +fe_sess_rate +fe_sess_rate(frontend) + Returns true when the session creation rate on the current or the named + frontend matches the specified values or ranges, expressed in new sessions + per second. This is used to limit the connection rate to acceptable ranges in + order to prevent abuse of service at the earliest moment. This can be + combined with layer 4 ACLs in order to force the clients to wait a bit for + the rate to go down below the limit. + + Example : + # This frontend limits incoming mails to 10/s with a max of 100 + # concurrent connections. We accept any connection below 10/s, and + # force excess clients to wait for 100 ms. Since clients are limited to + # 100 max, there cannot be more than 10 incoming mails per second. + frontend mail + bind :25 + mode tcp + maxconn 100 + acl too_fast fe_sess_rate ge 10 + tcp-request inspect-delay 100ms + tcp-request content accept if ! too_fast + tcp-request content accept if WAIT_END + +be_sess_rate +be_sess_rate(backend) + Returns true when the sessions creation rate on the backend matches the + specified values or ranges, in number of new sessions per second. This is + used to switch to an alternate backend when an expensive or fragile one + reaches too high a session rate, or to limite abuse of service (eg. prevent + sucking of an online dictionary). + + Example : + # Redirect to an error page if the dictionary is requested too often + backend dynamic + mode http + acl being_scanned be_sess_rate gt 100 + redirect location /denied.html if being_scanned + 2.3.5.2) Matching contents at Layer 4 ------------------------------------- diff --git a/src/backend.c b/src/backend.c index 73fb1129f..d386805eb 100644 --- a/src/backend.c +++ b/src/backend.c @@ -2096,11 +2096,51 @@ acl_fetch_connslots(struct proxy *px, struct session *l4, void *l7, int dir, return 1; } +/* set test->i to the number of connections per second reaching the frontend */ +static int +acl_fetch_fe_sess_rate(struct proxy *px, struct session *l4, void *l7, int dir, + struct acl_expr *expr, struct acl_test *test) +{ + test->flags = ACL_TEST_F_VOL_TEST; + if (expr->arg_len) { + /* another proxy was designated, we must look for it */ + for (px = proxy; px; px = px->next) + if ((px->cap & PR_CAP_FE) && !strcmp(px->id, expr->arg.str)) + break; + } + if (!px) + return 0; + + test->i = read_freq_ctr(&px->fe_sess_per_sec); + return 1; +} + +/* set test->i to the number of connections per second reaching the backend */ +static int +acl_fetch_be_sess_rate(struct proxy *px, struct session *l4, void *l7, int dir, + struct acl_expr *expr, struct acl_test *test) +{ + test->flags = ACL_TEST_F_VOL_TEST; + if (expr->arg_len) { + /* another proxy was designated, we must look for it */ + for (px = proxy; px; px = px->next) + if ((px->cap & PR_CAP_BE) && !strcmp(px->id, expr->arg.str)) + break; + } + if (!px) + return 0; + + test->i = read_freq_ctr(&px->be_sess_per_sec); + return 1; +} + /* Note: must not be declared as its list will be overwritten */ static struct acl_kw_list acl_kws = {{ },{ { "nbsrv", acl_parse_int, acl_fetch_nbsrv, acl_match_int, ACL_USE_NOTHING }, { "connslots", acl_parse_int, acl_fetch_connslots, acl_match_int, ACL_USE_NOTHING }, + { "fe_sess_rate", acl_parse_int, acl_fetch_fe_sess_rate, acl_match_int, ACL_USE_NOTHING }, + { "be_sess_rate", acl_parse_int, acl_fetch_be_sess_rate, acl_match_int, ACL_USE_NOTHING }, { NULL, NULL, NULL, NULL }, }};