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,