mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-08-06 23:27:04 +02:00
[MINOR] add X-Original-To: header
I have attached a patch which will add on every http request a new header 'X-Original-To'. If you have HAProxy running in transparent mode with a big number of SQUID servers behind it, it is very nice to have the original destination ip as a common header to make decisions based on it. The whole thing is configurable with a new option 'originalto'. I have updated the sourcecode as well as the documentation. The 'haproxy-en.txt' and 'haproxy-fr.txt' files are untouched, due to lack of my french language knowledge. ;) Also the patch adds this header for IPv4 only. I haven't any IPv6 test environment running here and don't know if getsockopt() with SO_ORIGINAL_DST will work on IPv6. If someone knows it and wants to test it I can modify the diff. Feel free to ask me questions or things which should be changed. :) --Maik
This commit is contained in:
parent
3909a2ab00
commit
2850cb42b6
@ -128,6 +128,15 @@ Limits :
|
|||||||
option httpclose
|
option httpclose
|
||||||
option forwardfor
|
option forwardfor
|
||||||
|
|
||||||
|
- if the application needs to log the original destination IP, use the
|
||||||
|
"originalto" option which will add an "X-Original-To" header with the
|
||||||
|
original destination IP address. You must also use "httpclose" to ensure
|
||||||
|
that you will rewrite every requests and not only the first one of each
|
||||||
|
session :
|
||||||
|
|
||||||
|
option httpclose
|
||||||
|
option originalto
|
||||||
|
|
||||||
The web server will have to be configured to use this header instead.
|
The web server will have to be configured to use this header instead.
|
||||||
For example, on apache, you can use LogFormat for this :
|
For example, on apache, you can use LogFormat for this :
|
||||||
|
|
||||||
|
@ -603,6 +603,7 @@ monitor-uri X X X -
|
|||||||
[no] option dontlognull X X X -
|
[no] option dontlognull X X X -
|
||||||
[no] option forceclose X - X X
|
[no] option forceclose X - X X
|
||||||
option forwardfor X X X X
|
option forwardfor X X X X
|
||||||
|
option originalto X X X X
|
||||||
[no] option http_proxy X X X X
|
[no] option http_proxy X X X X
|
||||||
option httpchk X - X X
|
option httpchk X - X X
|
||||||
[no] option httpclose X X X X
|
[no] option httpclose X X X X
|
||||||
@ -2108,6 +2109,65 @@ option forwardfor [ except <network> ] [ header <name> ]
|
|||||||
See also : "option httpclose"
|
See also : "option httpclose"
|
||||||
|
|
||||||
|
|
||||||
|
option originalto [ except <network> ] [ header <name> ]
|
||||||
|
Enable insertion of the X-Original-To header to requests sent to servers
|
||||||
|
May be used in sections : defaults | frontend | listen | backend
|
||||||
|
yes | yes | yes | yes
|
||||||
|
Arguments :
|
||||||
|
<network> is an optional argument used to disable this option for sources
|
||||||
|
matching <network>
|
||||||
|
<name> an optional argument to specify a different "X-Original-To"
|
||||||
|
header name.
|
||||||
|
|
||||||
|
Since HAProxy can work in transparent mode, every request from a client can
|
||||||
|
be redirected to the proxy and HAProxy itself can proxy every request to a
|
||||||
|
complex SQUID environment and the destination host from SO_ORIGINAL_DST will
|
||||||
|
be lost. This is annoying when you want access rules based on destination ip
|
||||||
|
addresses. To solve this problem, a new HTTP header "X-Original-To" may be
|
||||||
|
added by HAProxy to all requests sent to the server. This header contains a
|
||||||
|
value representing the original destination IP address. Since this must be
|
||||||
|
configured to always use the last occurrence of this header only. Note that
|
||||||
|
only the last occurrence of the header must be used, since it is really
|
||||||
|
possible that the client has already brought one.
|
||||||
|
|
||||||
|
The keyword "header" may be used to supply a different header name to replace
|
||||||
|
the default "X-Original-To". This can be useful where you might already
|
||||||
|
have a "X-Original-To" header from a different application, and you need
|
||||||
|
preserve it. Also if your backend server doesn't use the "X-Original-To"
|
||||||
|
header and requires different one.
|
||||||
|
|
||||||
|
Sometimes, a same HAProxy instance may be shared between a direct client
|
||||||
|
access and a reverse-proxy access (for instance when an SSL reverse-proxy is
|
||||||
|
used to decrypt HTTPS traffic). It is possible to disable the addition of the
|
||||||
|
header for a known source address or network by adding the "except" keyword
|
||||||
|
followed by the network address. In this case, any source IP matching the
|
||||||
|
network will not cause an addition of this header. Most common uses are with
|
||||||
|
private networks or 127.0.0.1.
|
||||||
|
|
||||||
|
This option may be specified either in the frontend or in the backend. If at
|
||||||
|
least one of them uses it, the header will be added. Note that the backend's
|
||||||
|
setting of the header subargument takes precedence over the frontend's if
|
||||||
|
both are defined.
|
||||||
|
|
||||||
|
It is important to note that as long as HAProxy does not support keep-alive
|
||||||
|
connections, only the first request of a connection will receive the header.
|
||||||
|
For this reason, it is important to ensure that "option httpclose" is set
|
||||||
|
when using this option.
|
||||||
|
|
||||||
|
Examples :
|
||||||
|
# Original Destination address
|
||||||
|
frontend www
|
||||||
|
mode http
|
||||||
|
option originalto except 127.0.0.1
|
||||||
|
|
||||||
|
# Those servers want the IP Address in X-Client-Dst
|
||||||
|
backend www
|
||||||
|
mode http
|
||||||
|
option originalto header X-Client-Dst
|
||||||
|
|
||||||
|
See also : "option httpclose"
|
||||||
|
|
||||||
|
|
||||||
option http_proxy
|
option http_proxy
|
||||||
no option http_proxy
|
no option http_proxy
|
||||||
Enable or disable plain HTTP proxy mode
|
Enable or disable plain HTTP proxy mode
|
||||||
|
@ -130,6 +130,9 @@
|
|||||||
// X-Forwarded-For header default
|
// X-Forwarded-For header default
|
||||||
#define DEF_XFORWARDFOR_HDR "X-Forwarded-For"
|
#define DEF_XFORWARDFOR_HDR "X-Forwarded-For"
|
||||||
|
|
||||||
|
// X-Original-To header default
|
||||||
|
#define DEF_XORIGINALTO_HDR "X-Original-To"
|
||||||
|
|
||||||
/* Default connections limit.
|
/* Default connections limit.
|
||||||
*
|
*
|
||||||
* A system limit can be enforced at build time in order to avoid using haproxy
|
* A system limit can be enforced at build time in order to avoid using haproxy
|
||||||
|
@ -105,7 +105,8 @@
|
|||||||
#define PR_O_CONTSTATS 0x10000000 /* continous counters */
|
#define PR_O_CONTSTATS 0x10000000 /* continous counters */
|
||||||
#define PR_O_HTTP_PROXY 0x20000000 /* Enable session to use HTTP proxy operations */
|
#define PR_O_HTTP_PROXY 0x20000000 /* Enable session to use HTTP proxy operations */
|
||||||
#define PR_O_DISABLE404 0x40000000 /* Disable a server on a 404 response to a health-check */
|
#define PR_O_DISABLE404 0x40000000 /* Disable a server on a 404 response to a health-check */
|
||||||
/* unused: 0x80000000 */
|
#define PR_O_ORGTO 0x80000000 /* insert x-original-to with destination address */
|
||||||
|
/* unused: 0x80000000 - now used by PR_O_ORGTO */
|
||||||
|
|
||||||
/* bits for proxy->options2 */
|
/* bits for proxy->options2 */
|
||||||
#define PR_O2_SPLIC_REQ 0x00000001 /* transfer requests using linux kernel's splice() */
|
#define PR_O2_SPLIC_REQ 0x00000001 /* transfer requests using linux kernel's splice() */
|
||||||
@ -231,8 +232,12 @@ struct proxy {
|
|||||||
unsigned int fe_maxsps; /* max # of new sessions per second on the frontend */
|
unsigned int fe_maxsps; /* max # of new sessions per second on the frontend */
|
||||||
unsigned int fullconn; /* #conns on backend above which servers are used at full load */
|
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 */
|
struct in_addr except_net, except_mask; /* don't x-forward-for for this address. FIXME: should support IPv6 */
|
||||||
|
struct in_addr except_to; /* don't x-original-to for this address. */
|
||||||
|
struct in_addr except_mask_to; /* the netmask for except_to. */
|
||||||
char *fwdfor_hdr_name; /* header to use - default: "x-forwarded-for" */
|
char *fwdfor_hdr_name; /* header to use - default: "x-forwarded-for" */
|
||||||
int fwdfor_hdr_len; /* length of "x-forwarded-for" header */
|
int fwdfor_hdr_len; /* length of "x-forwarded-for" header */
|
||||||
|
char *orgto_hdr_name; /* header to use - default: "x-original-to" */
|
||||||
|
int orgto_hdr_len; /* length of "x-original-to" header */
|
||||||
|
|
||||||
unsigned down_trans; /* up-down transitions */
|
unsigned down_trans; /* up-down transitions */
|
||||||
unsigned down_time; /* total time the proxy was down */
|
unsigned down_time; /* total time the proxy was down */
|
||||||
|
@ -763,6 +763,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int inv)
|
|||||||
curproxy->lbprm.algo = defproxy.lbprm.algo;
|
curproxy->lbprm.algo = defproxy.lbprm.algo;
|
||||||
curproxy->except_net = defproxy.except_net;
|
curproxy->except_net = defproxy.except_net;
|
||||||
curproxy->except_mask = defproxy.except_mask;
|
curproxy->except_mask = defproxy.except_mask;
|
||||||
|
curproxy->except_mask_to = defproxy.except_mask_to;
|
||||||
|
|
||||||
if (defproxy.fwdfor_hdr_len) {
|
if (defproxy.fwdfor_hdr_len) {
|
||||||
curproxy->fwdfor_hdr_len = defproxy.fwdfor_hdr_len;
|
curproxy->fwdfor_hdr_len = defproxy.fwdfor_hdr_len;
|
||||||
@ -1722,6 +1723,51 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int inv)
|
|||||||
}
|
}
|
||||||
} /* end while loop */
|
} /* end while loop */
|
||||||
}
|
}
|
||||||
|
else if (!strcmp(args[1], "originalto")) {
|
||||||
|
int cur_arg;
|
||||||
|
|
||||||
|
/* insert x-original-to field, but not for the IP address listed as an except.
|
||||||
|
* set default options (ie: bitfield, header name, etc)
|
||||||
|
*/
|
||||||
|
|
||||||
|
curproxy->options |= PR_O_ORGTO;
|
||||||
|
|
||||||
|
free(curproxy->orgto_hdr_name);
|
||||||
|
curproxy->orgto_hdr_name = strdup(DEF_XORIGINALTO_HDR);
|
||||||
|
curproxy->orgto_hdr_len = strlen(DEF_XORIGINALTO_HDR);
|
||||||
|
|
||||||
|
/* loop to go through arguments - start at 2, since 0+1 = "option" "forwardfor" */
|
||||||
|
cur_arg = 2;
|
||||||
|
while (*(args[cur_arg])) {
|
||||||
|
if (!strcmp(args[cur_arg], "except")) {
|
||||||
|
/* suboption except - needs additional argument for it */
|
||||||
|
if (!*(args[cur_arg+1]) || !str2net(args[cur_arg+1], &curproxy->except_to, &curproxy->except_mask_to)) {
|
||||||
|
Alert("parsing [%s:%d] : '%s %s %s' expects <address>[/mask] as argument.\n",
|
||||||
|
file, linenum, args[0], args[1], args[cur_arg]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
/* flush useless bits */
|
||||||
|
curproxy->except_to.s_addr &= curproxy->except_mask_to.s_addr;
|
||||||
|
cur_arg += 2;
|
||||||
|
} else if (!strcmp(args[cur_arg], "header")) {
|
||||||
|
/* suboption header - needs additional argument for it */
|
||||||
|
if (*(args[cur_arg+1]) == 0) {
|
||||||
|
Alert("parsing [%s:%d] : '%s %s %s' expects <header_name> as argument.\n",
|
||||||
|
file, linenum, args[0], args[1], args[cur_arg]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
free(curproxy->orgto_hdr_name);
|
||||||
|
curproxy->orgto_hdr_name = strdup(args[cur_arg+1]);
|
||||||
|
curproxy->orgto_hdr_len = strlen(curproxy->orgto_hdr_name);
|
||||||
|
cur_arg += 2;
|
||||||
|
} else {
|
||||||
|
/* unknown suboption - catchall */
|
||||||
|
Alert("parsing [%s:%d] : '%s %s' only supports optional values: 'except' and 'header'.\n",
|
||||||
|
file, linenum, args[0], args[1]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
} /* end while loop */
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
Alert("parsing [%s:%d] : unknown option '%s'.\n", file, linenum, args[1]);
|
Alert("parsing [%s:%d] : unknown option '%s'.\n", file, linenum, args[1]);
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -41,6 +41,7 @@
|
|||||||
#include <proto/acl.h>
|
#include <proto/acl.h>
|
||||||
#include <proto/backend.h>
|
#include <proto/backend.h>
|
||||||
#include <proto/buffers.h>
|
#include <proto/buffers.h>
|
||||||
|
#include <proto/client.h>
|
||||||
#include <proto/dumpstats.h>
|
#include <proto/dumpstats.h>
|
||||||
#include <proto/fd.h>
|
#include <proto/fd.h>
|
||||||
#include <proto/log.h>
|
#include <proto/log.h>
|
||||||
@ -2216,7 +2217,52 @@ int http_process_request(struct session *s, struct buffer *req)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 10: add "Connection: close" if needed and not yet set.
|
* 10: add X-Original-To if either the frontend or the backend
|
||||||
|
* asks for it.
|
||||||
|
*/
|
||||||
|
if ((s->fe->options | s->be->options) & PR_O_ORGTO) {
|
||||||
|
|
||||||
|
/* FIXME: don't know if IPv6 can handle that case too. */
|
||||||
|
if (s->cli_addr.ss_family == AF_INET) {
|
||||||
|
/* Add an X-Original-To header unless the destination IP is
|
||||||
|
* in the 'except' network range.
|
||||||
|
*/
|
||||||
|
if (!(s->flags & SN_FRT_ADDR_SET))
|
||||||
|
get_frt_addr(s);
|
||||||
|
|
||||||
|
if ((!s->fe->except_mask_to.s_addr ||
|
||||||
|
(((struct sockaddr_in *)&s->frt_addr)->sin_addr.s_addr & s->fe->except_mask_to.s_addr)
|
||||||
|
!= s->fe->except_to.s_addr) &&
|
||||||
|
(!s->be->except_mask_to.s_addr ||
|
||||||
|
(((struct sockaddr_in *)&s->frt_addr)->sin_addr.s_addr & s->be->except_mask_to.s_addr)
|
||||||
|
!= s->be->except_to.s_addr)) {
|
||||||
|
int len;
|
||||||
|
unsigned char *pn;
|
||||||
|
pn = (unsigned char *)&((struct sockaddr_in *)&s->frt_addr)->sin_addr;
|
||||||
|
|
||||||
|
/* Note: we rely on the backend to get the header name to be used for
|
||||||
|
* x-original-to, because the header is really meant for the backends.
|
||||||
|
* However, if the backend did not specify any option, we have to rely
|
||||||
|
* on the frontend's header name.
|
||||||
|
*/
|
||||||
|
if (s->be->orgto_hdr_len) {
|
||||||
|
len = s->be->orgto_hdr_len;
|
||||||
|
memcpy(trash, s->be->orgto_hdr_name, len);
|
||||||
|
} else {
|
||||||
|
len = s->fe->orgto_hdr_len;
|
||||||
|
memcpy(trash, s->fe->orgto_hdr_name, len);
|
||||||
|
}
|
||||||
|
len += sprintf(trash + len, ": %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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 11: add "Connection: close" if needed and not yet set.
|
||||||
* Note that we do not need to add it in case of HTTP/1.0.
|
* Note that we do not need to add it in case of HTTP/1.0.
|
||||||
*/
|
*/
|
||||||
if (!(s->flags & SN_CONN_CLOSED) &&
|
if (!(s->flags & SN_CONN_CLOSED) &&
|
||||||
|
Loading…
Reference in New Issue
Block a user