]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
[MEDIUM] introduce "timeout http-request" in frontends
authorWilly Tarreau <w@1wt.eu>
Sun, 6 Jan 2008 12:24:40 +0000 (13:24 +0100)
committerWilly Tarreau <w@1wt.eu>
Sun, 6 Jan 2008 12:24:40 +0000 (13:24 +0100)
In order to offer DoS protection, it may be required to lower the maximum
accepted time to receive a complete HTTP request without affecting the client
timeout. This helps protecting against established connections on which
nothing is sent. The client timeout cannot offer a good protection against
this abuse because it is an inactivity timeout, which means that if the
attacker sends one character every now and then, the timeout will not
trigger. With the HTTP request timeout, no matter what speed the client
types, the request will be aborted if it does not complete in time.

doc/configuration.txt
include/types/proto_http.h
include/types/proxy.h
src/cfgparse.c
src/client.c
src/proto_http.c
src/proxy.c
tests/test-timeout.cfg

index ea3d342c20a40542203862d3c150dc9022aff257..3cd48d366379265125e4950e166b572d891d6219 100644 (file)
@@ -603,6 +603,7 @@ timeout client              X          X         X         -
 timeout clitimeout          X          X         X         -  (deprecated)
 timeout connect             X          -         X         X
 timeout contimeout          X          -         X         X  (deprecated)
+timeout httpreq             X          X         X         -
 timeout queue               X          -         X         X
 timeout server              X          -         X         X
 timeout srvtimeout          X          -         X         X  (deprecated)
@@ -952,7 +953,8 @@ clitimeout <timeout>
   This parameter is provided for compatibility but is currently deprecated.
   Please use "timeout client" instead.
 
-  See also : "timeout client", "timeout server", "srvtimeout".
+  See also : "timeout client", "timeout http-request", "timeout server", and
+             "srvtimeout".
 
 
 contimeout <timeout>
@@ -2060,6 +2062,40 @@ timeout contimeout <timeout> (deprecated)
   See also : "timeout queue", "timeout server", "contimeout".
 
 
+timeout http-request <timeout>
+  Set the maximum allowed time to wait for a complete HTTP request
+  May be used in sections :   defaults | frontend | listen | backend
+                                 yes   |    yes   |   yes  |   no
+  Arguments :
+    <timeout> is the timeout value is specified in milliseconds by default, but
+              can be in any other unit if the number is suffixed by the unit,
+              as explained at the top of this document.
+
+  In order to offer DoS protection, it may be required to lower the maximum
+  accepted time to receive a complete HTTP request without affecting the client
+  timeout. This helps protecting against established connections on which
+  nothing is sent. The client timeout cannot offer a good protection against
+  this abuse because it is an inactivity timeout, which means that if the
+  attacker sends one character every now and then, the timeout will not
+  trigger. With the HTTP request timeout, no matter what speed the client
+  types, the request will be aborted if it does not complete in time.
+
+  Note that this timeout only applies to the header part of the request, and
+  not to any data. As soon as the empty line is received, this timeout is not
+  used anymore.
+
+  Generally it is enough to set it to a few seconds, as most clients send the
+  full request immediately upon connection. Add 3 or more seconds to cover TCP
+  retransmits but that's all. Setting it to very low values (eg: 50 ms) will
+  generally work on local networks as long as there are no packet losses. This
+  will prevent people from sending bare HTTP requests using telnet.
+
+  If this parameter is not set, the client timeout still applies between each
+  chunk of the incoming request.
+
+  See also : "timeout client".
+
+
 2.3) Using ACLs
 ---------------
 
index c5e051e2285883661c412514217eb24db46b06f4..64530616562fffb199c18ac1b08ff9a1bbac0a6e 100644 (file)
@@ -241,6 +241,7 @@ struct http_txn {
        char *srv_cookie;               /* cookie presented by the server, in capture mode */
        int status;                     /* HTTP status from the server, negative if from proxy */
        unsigned int flags;             /* transaction flags */
+       struct timeval exp;             /* expiration date for the transaction (generally a request) */
 };
 
 /* This structure is used by http_find_header() to return values of headers.
index 8a72d82d564e761c18498d70d2f23510f4b13857..5d397d403a213ffdc9b06f26533ae1f5e820eef3 100644 (file)
@@ -176,7 +176,8 @@ struct proxy {
                struct timeval queue;           /* queue timeout, defaults to connect if unspecified */
                struct timeval connect;         /* connect timeout (in milliseconds) */
                struct timeval server;          /* server I/O timeout (in milliseconds) */
-               struct timeval appsession;
+               struct timeval appsession;      /* appsession cookie expiration */
+               struct timeval httpreq;         /* maximum time for complete HTTP request */
        } timeout;
        char *id;                               /* proxy id */
        struct list pendconns;                  /* pending connections with no server assigned yet */
index 655a40e63442d79b4886193ba2a0d9177e9e7e63..b9cf62f07e44ab052ce9223392f49ba72785b889 100644 (file)
@@ -521,6 +521,7 @@ static void init_default_instance()
        tv_eternity(&defproxy.timeout.appsession);
        tv_eternity(&defproxy.timeout.queue);
        tv_eternity(&defproxy.timeout.tarpit);
+       tv_eternity(&defproxy.timeout.httpreq);
 }
 
 /*
@@ -603,6 +604,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int inv)
                tv_eternity(&curproxy->timeout.appsession);
                tv_eternity(&curproxy->timeout.queue);
                tv_eternity(&curproxy->timeout.tarpit);
+               tv_eternity(&curproxy->timeout.httpreq);
 
                curproxy->last_change = now.tv_sec;
                curproxy->id = strdup(args[1]);
@@ -663,6 +665,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int inv)
                if (curproxy->cap & PR_CAP_FE) {
                        curproxy->timeout.client = defproxy.timeout.client;
                        curproxy->timeout.tarpit = defproxy.timeout.tarpit;
+                       curproxy->timeout.httpreq = defproxy.timeout.httpreq;
                        curproxy->uri_auth  = defproxy.uri_auth;
                        curproxy->mon_net = defproxy.mon_net;
                        curproxy->mon_mask = defproxy.mon_mask;
index cfcfb0c3f8d9cb08c07311c684c3b56b3dda2f4e..d1d763db59a6f629c4c663fb73ec24e902feb2c9 100644 (file)
@@ -390,6 +390,7 @@ int event_accept(int fd) {
                tv_eternity(&s->req->cex);
                tv_eternity(&s->rep->rex);
                tv_eternity(&s->rep->wex);
+               tv_eternity(&s->txn.exp);
                tv_eternity(&t->expire);
 
                if (tv_isset(&s->fe->timeout.client)) {
@@ -403,6 +404,11 @@ int event_accept(int fd) {
                        }
                }
 
+               if (s->cli_state == CL_STHEADERS && tv_isset(&s->fe->timeout.httpreq)) {
+                       tv_add(&s->txn.exp, &now, &s->fe->timeout.httpreq);
+                       tv_bound(&t->expire, &s->txn.exp);
+               }
+
                task_queue(t);
 
                if (p->mode != PR_MODE_HEALTH)
index f387d0b4434a0cae0b3e0173cf0e9228246ff77a..16028cedafcaf52f812370264488c68b2c84ff46 100644 (file)
@@ -620,11 +620,12 @@ void process_session(struct task *t, struct timeval *next)
                s->req->flags &= BF_CLEAR_READ & BF_CLEAR_WRITE;
                s->rep->flags &= BF_CLEAR_READ & BF_CLEAR_WRITE;
 
-               t->expire = s->req->rex;
                tv_min(&t->expire, &s->req->rex, &s->req->wex);
                tv_bound(&t->expire, &s->req->cex);
                tv_bound(&t->expire, &s->rep->rex);
                tv_bound(&t->expire, &s->rep->wex);
+               if (s->cli_state == CL_STHEADERS)
+                       tv_bound(&t->expire, &s->txn.exp);
 
                /* restore t to its place in the task list */
                task_queue(t);
@@ -1585,7 +1586,8 @@ int process_cli(struct session *t)
                        }
 
                        /* 3: has the read timeout expired ? */
-                       else if (unlikely(tv_isle(&req->rex, &now))) {
+                       else if (unlikely(tv_isle(&req->rex, &now) ||
+                                         tv_isle(&txn->exp, &now))) {
                                /* read timeout : give up with an error message. */
                                txn->status = 408;
                                client_retnclose(t, error_message(t, HTTP_ERR_408));
index 82bb2ae14e0f7a0f0571344ee373a512209ce0bb..e2e6b15fcc4b66d8b7923feacb23f0bdc3efe3a9 100644 (file)
@@ -104,6 +104,10 @@ int proxy_parse_timeout(const char **args, struct proxy *proxy,
                tv = &proxy->timeout.tarpit;
                td = &defpx->timeout.tarpit;
                cap = PR_CAP_FE;
+       } else if (!strcmp(args[0], "http-request")) {
+               tv = &proxy->timeout.httpreq;
+               td = &defpx->timeout.httpreq;
+               cap = PR_CAP_FE;
        } else if (!strcmp(args[0], "server") || !strcmp(args[0], "srvtimeout")) {
                name = "server";
                tv = &proxy->timeout.server;
@@ -123,7 +127,9 @@ int proxy_parse_timeout(const char **args, struct proxy *proxy,
                td = &defpx->timeout.queue;
                cap = PR_CAP_BE;
        } else {
-               snprintf(err, errlen, "timeout '%s': must be 'client', 'server', 'connect', 'appsession', 'queue', or 'tarpit'",
+               snprintf(err, errlen,
+                        "timeout '%s': must be 'client', 'server', 'connect', "
+                        "'appsession', 'queue', 'http-request' or 'tarpit'",
                         args[0]);
                return -1;
        }
index 7053f5c28ff9598f06ee5fe628a208caad4e6c21..1c0842559d36e0d2eae1a6c7df3bd6f805a19c3c 100644 (file)
@@ -10,6 +10,7 @@ listen  sample1
         retries    1
         redispatch
         timeout    client  15m
+        timeout    http-request 6s
         timeout    tarpit  20s
         timeout    queue   60s
         timeout    connect 5s