From 7ac51f61f52837f206e10bcdbd45ddd5be8241e4 Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Sun, 25 Mar 2007 16:00:04 +0200 Subject: [PATCH] [MEDIUM] add the "except" keyword to the "forwardfor" option Patch from Bryan Germann for 1.2.17. In some circumstances, it is useful not to add the X-Forwarded-For header, for instance when the client is another reverse-proxy or stunnel running on the same machine and which already adds it. This patch adds the "except" keyword to the "forwardfor" option, allowing to specify an address or network which will not be added to this header. --- doc/haproxy-en.txt | 9 +++++++-- doc/haproxy-fr.txt | 9 +++++++-- include/types/proxy.h | 1 + src/cfgparse.c | 24 +++++++++++++++++++++++- src/proto_http.c | 30 ++++++++++++++++++++++-------- 5 files changed, 60 insertions(+), 13 deletions(-) diff --git a/doc/haproxy-en.txt b/doc/haproxy-en.txt index dbe78f5f5..eecc6c3d6 100644 --- a/doc/haproxy-en.txt +++ b/doc/haproxy-en.txt @@ -2142,7 +2142,12 @@ Examples : Also, the 'forwardfor' option creates an HTTP 'X-Forwarded-For' header which contains the client's IP address. This is useful to let the final web server -know what the client address was (eg for statistics on domains). +know what the client address was (eg for statistics on domains). Starting with +version 1.3.8, it is possible to specify the "except" keyword followed by a +source IP address or network for which no header will be added. This is very +useful when another reverse-proxy which already adds the header runs on the +same machine or in a known DMZ, the most common case being the local use of +stunnel on the same system. Last, the 'httpclose' option removes any 'Connection' header both ways, and adds a 'Connection: close' header in each direction. This makes it easier to @@ -2155,7 +2160,7 @@ Example : log global option httplog option dontlognull - option forwardfor + option forwardfor except 127.0.0.1/8 option httpclose Note that some HTTP servers do not necessarily close the connections when they diff --git a/doc/haproxy-fr.txt b/doc/haproxy-fr.txt index 7f6d2318d..7a2faf584 100644 --- a/doc/haproxy-fr.txt +++ b/doc/haproxy-fr.txt @@ -2223,7 +2223,12 @@ Exemples : De plus, l'option 'forwardfor' ajoute l'adresse IP du client dans un champ 'X-Forwarded-For' de la requête, ce qui permet à un serveur web final de -connaître l'adresse IP du client initial. +connaître l'adresse IP du client initial. Depuis la version 1.3.8, il est +possible de préciser le mot-clé "except" suivi d'une adresse ou un réseau +IP source pour lequel l'entête ne sera pas ajouté. C'est très pratique dans le +cas où un autre reverse-proxy ajoutant déjà l'entête est installé sur la même +machine ou dans une DMZ connue. Le cas le plus fréquent est lié à l'utilisation +de stunnel en local. Enfin, l'option 'httpclose' apparue dans la version 1.1.28/1.2.1 supprime tout en-tête de type 'Connection:' et ajoute 'Connection: close' dans les deux sens. @@ -2237,7 +2242,7 @@ Exemple : log global option httplog option dontlognull - option forwardfor + option forwardfor except 127.0.0.1/8 option httpclose Notons que certains serveurs HTTP ne referment pas nécessairement la session diff --git a/include/types/proxy.h b/include/types/proxy.h index a709ada5b..b598e4b2c 100644 --- a/include/types/proxy.h +++ b/include/types/proxy.h @@ -113,6 +113,7 @@ struct proxy { unsigned int cum_feconn, cum_beconn; /* cumulated number of processed sessions */ unsigned int maxconn; /* max # of active sessions on the frontend */ unsigned int fullconn; /* #conns on backend above which servers are used at full load */ + struct in_addr except_net, except_mask; /* don't x-forward-for for this address. FIXME: should support IPv6 */ unsigned failed_conns, failed_resp; /* failed connect() and responses */ unsigned denied_req, denied_resp; /* blocked requests/responses because of security concerns */ unsigned failed_req; /* failed requests (eg: invalid or timeout) */ diff --git a/src/cfgparse.c b/src/cfgparse.c index 4c1f032ec..885a01bb5 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -85,7 +85,6 @@ static const struct { #endif { "redispatch", PR_O_REDISP, PR_CAP_BE, 0 }, { "keepalive", PR_O_KEEPALIVE, PR_CAP_NONE, 0 }, - { "forwardfor", PR_O_FWDFOR, PR_CAP_FE | PR_CAP_BE, 0 }, { "httpclose", PR_O_HTTP_CLOSE, PR_CAP_FE | PR_CAP_BE, 0 }, { "logasap", PR_O_LOGASAP, PR_CAP_FE, 0 }, { "abortonclose", PR_O_ABRT_CLOSE, PR_CAP_BE, 0 }, @@ -501,6 +500,8 @@ int cfg_parse_listen(const char *file, int linenum, char **args) /* set default values */ curproxy->state = defproxy.state; curproxy->options = defproxy.options; + curproxy->except_net = defproxy.except_net; + curproxy->except_mask = defproxy.except_mask; if (curproxy->cap & PR_CAP_FE) { curproxy->maxconn = defproxy.maxconn; @@ -1022,6 +1023,27 @@ int cfg_parse_listen(const char *file, int linenum, char **args) curproxy->options &= ~PR_O_HTTP_CHK; curproxy->options |= PR_O_SSL3_CHK; } + else if (!strcmp(args[1], "forwardfor")) { + /* insert x-forwarded-for field, but not for the + * IP address listed as an except. + */ + if (*(args[2])) { + if (!strcmp(args[2], "except")) { + if (!*args[3] || !str2net(args[3], &curproxy->except_net, &curproxy->except_mask)) { + Alert("parsing [%s:%d] : '%s' only supports optional 'except' address[/mask].\n", + file, linenum, args[0]); + return -1; + } + /* flush useless bits */ + curproxy->except_net.s_addr &= curproxy->except_mask.s_addr; + } else { + Alert("parsing [%s:%d] : '%s' only supports optional 'except' address[/mask].\n", + file, linenum, args[0]); + return -1; + } + } + curproxy->options |= PR_O_FWDFOR; + } else { Alert("parsing [%s:%d] : unknown option '%s'.\n", file, linenum, args[1]); return -1; diff --git a/src/proto_http.c b/src/proto_http.c index d3bc5f665..b01859080 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -1656,17 +1656,31 @@ int process_cli(struct session *t) */ if ((t->fe->options | t->be->beprm->options) & PR_O_FWDFOR) { if (t->cli_addr.ss_family == AF_INET) { - int len; - unsigned char *pn; - pn = (unsigned char *)&((struct sockaddr_in *)&t->cli_addr)->sin_addr; - len = sprintf(trash, "X-Forwarded-For: %d.%d.%d.%d", - pn[0], pn[1], pn[2], pn[3]); + /* Add an X-Forwarded-For header unless the source IP is + * in the 'except' network range. + */ + if ((!t->fe->except_mask.s_addr || + (((struct sockaddr_in *)&t->cli_addr)->sin_addr.s_addr & t->fe->except_mask.s_addr) + != t->fe->except_net.s_addr) && + (!t->be->except_mask.s_addr || + (((struct sockaddr_in *)&t->cli_addr)->sin_addr.s_addr & t->be->except_mask.s_addr) + != t->be->except_net.s_addr)) { + int len; + unsigned char *pn; + pn = (unsigned char *)&((struct sockaddr_in *)&t->cli_addr)->sin_addr; - if (unlikely(http_header_add_tail2(req, &txn->req, - &txn->hdr_idx, trash, len)) < 0) - goto return_bad_req; + len = sprintf(trash, "X-Forwarded-For: %d.%d.%d.%d", + pn[0], pn[1], pn[2], pn[3]); + + if (unlikely(http_header_add_tail2(req, &txn->req, + &txn->hdr_idx, trash, len)) < 0) + goto return_bad_req; + } } else if (t->cli_addr.ss_family == AF_INET6) { + /* FIXME: for the sake of completeness, we should also support + * 'except' here, although it is mostly useless in this case. + */ int len; char pn[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6,