]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: http: add support for "http-request tarpit" rule
authorWilly Tarreau <w@1wt.eu>
Thu, 27 Dec 2012 11:37:57 +0000 (12:37 +0100)
committerWilly Tarreau <w@1wt.eu>
Fri, 28 Dec 2012 13:47:19 +0000 (14:47 +0100)
The "reqtarpit" rule is not very handy to use. Now that we have more
flexibility with "http-request", let's finally make the tarpit rules
usable there.

There are still semantical differences between apply_filters_to_request()
and http_req_get_intercept_rule() because the former updates the counters
while the latter does not. So we currently have almost similar code leafs
for similar conditions, but this should be cleaned up later.

doc/configuration.txt
include/types/proto_http.h
src/proto_http.c

index a4973ecb705585bee6ea4c5a69867aa68cb577ea..5c15d6a8808199bfe8c50f42c04567cddd1f6320 100644 (file)
@@ -2606,7 +2606,7 @@ http-check send-state
 
   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
@@ -2628,6 +2628,18 @@ http-request { allow | deny | auth [realm <realm>] | redirect <rule> |
       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
index ef81a12a67cb446c077038f4bceef2b2d2232de6..12e446f73066e7a72f215e0e2fa1203dac0036bf 100644 (file)
@@ -240,6 +240,7 @@ enum {
        HTTP_REQ_ACT_UNKNOWN = 0,
        HTTP_REQ_ACT_ALLOW,
        HTTP_REQ_ACT_DENY,
+       HTTP_REQ_ACT_TARPIT,
        HTTP_REQ_ACT_AUTH,
        HTTP_REQ_ACT_ADD_HDR,
        HTTP_REQ_ACT_SET_HDR,
index 7fc2dce2b09420809fc77049d487e32a237392d2..aaa94766e43b6e5c76ff785b470e911dbabfd624 100644 (file)
@@ -3101,6 +3101,10 @@ http_req_get_intercept_rule(struct proxy *px, struct list *rules, struct session
                        txn->flags |= TX_CLDENY;
                        return rule;
 
+               case HTTP_REQ_ACT_TARPIT:
+                       txn->flags |= TX_CLTARPIT;
+                       return rule;
+
                case HTTP_REQ_ACT_AUTH:
                        return rule;
 
@@ -3419,7 +3423,8 @@ int http_process_req_common(struct session *s, struct channel *req, int an_bit,
                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));
@@ -3430,6 +3435,31 @@ int http_process_req_common(struct session *s, struct channel *req, int an_bit,
                        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 */
@@ -8059,6 +8089,9 @@ struct http_req_rule *parse_http_req_cond(const char **args, const char *file, i
        } 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;