See also : "option httpchk", "http-check disable-on-404"
-http-request { allow | deny | auth [realm <realm>] | redirect <rule> |
+http-request { allow | deny | tarpit | auth [realm <realm>] | redirect <rule> |
add-header <name> <fmt> | set-header <name> <fmt> }
[ { if | unless } <condition> ]
Access control for Layer 7 requests
the request and emits an HTTP 403 error. No further "http-request" rules
are evaluated.
+ - "tarpit" : this stops the evaluation of the rules and immediately blocks
+ the request without responding for a delay specified by "timeout tarpit"
+ or "timeout connect" if the former is not set. After that delay, if the
+ client is still connected, an HTTP error 500 is returned so that the
+ client does not suspect it has been tarpitted. Logs will report the flags
+ "PT". The goal of the tarpit rule is to slow down robots during an attack
+ when they're limited on the number of concurrent requests. It can be very
+ efficient against very dumb robots, and will significantly reduce the
+ load on firewalls compared to a "deny" rule. But when facing "correctly"
+ developped robots, it can make things worse by forcing haproxy and the
+ front firewall to support insane number of concurrent connections.
+
- "auth" : this stops the evaluation of the rules and immediately responds
with an HTTP 401 or 407 error code to invite the user to present a valid
user name and password. No further "http-request" rules are evaluated. An
txn->flags |= TX_CLDENY;
return rule;
+ case HTTP_REQ_ACT_TARPIT:
+ txn->flags |= TX_CLTARPIT;
+ return rule;
+
case HTTP_REQ_ACT_AUTH:
return rule;
do_stats = 0;
/* return a 403 if either rule has blocked */
- if (txn->flags & TX_CLDENY) {
+ if (txn->flags & (TX_CLDENY|TX_CLTARPIT)) {
+ if (txn->flags & TX_CLDENY) {
txn->status = 403;
s->logs.tv_request = now;
stream_int_retnclose(req->prod, http_error_message(s, HTTP_ERR_403));
if (s->listener->counters)
s->listener->counters->denied_req++;
goto return_prx_cond;
+ }
+ /* When a connection is tarpitted, we use the tarpit timeout,
+ * which may be the same as the connect timeout if unspecified.
+ * If unset, then set it to zero because we really want it to
+ * eventually expire. We build the tarpit as an analyser.
+ */
+ if (txn->flags & TX_CLTARPIT) {
+ channel_erase(s->req);
+ /* wipe the request out so that we can drop the connection early
+ * if the client closes first.
+ */
+ channel_dont_connect(req);
+ req->analysers = 0; /* remove switching rules etc... */
+ req->analysers |= AN_REQ_HTTP_TARPIT;
+ req->analyse_exp = tick_add_ifset(now_ms, s->be->timeout.tarpit);
+ if (!req->analyse_exp)
+ req->analyse_exp = tick_add(now_ms, 0);
+ session_inc_http_err_ctr(s);
+ s->fe->fe_counters.denied_req++;
+ if (s->fe != s->be)
+ s->be->be_counters.denied_req++;
+ if (s->listener->counters)
+ s->listener->counters->denied_req++;
+ return 1;
+ }
}
/* try headers filters */
} else if (!strcmp(args[0], "deny")) {
rule->action = HTTP_REQ_ACT_DENY;
cur_arg = 1;
+ } else if (!strcmp(args[0], "tarpit")) {
+ rule->action = HTTP_REQ_ACT_TARPIT;
+ cur_arg = 1;
} else if (!strcmp(args[0], "auth")) {
rule->action = HTTP_REQ_ACT_AUTH;
cur_arg = 1;