mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-08-07 23:56:57 +02:00
[MEDIUM] add ability to connect to a server from an IP found in a header
Using get_ip_from_hdr2() we can look for occurrence #X or #-X and extract the IP it contains. This is typically designed for use with the X-Forwarded-For header. Using "usesrc hdr_ip(name,occ)", it becomes possible to use the IP address found in <name>, and possibly specify occurrence number <occ>, as the source to connect to a server. This is possible both in a server and in a backend's source statement. This is typically used to use the source IP previously set by a upstream proxy.
This commit is contained in:
parent
090466c91a
commit
bce7088275
@ -4127,6 +4127,7 @@ server <name> <address>[:port] [param*]
|
||||
|
||||
|
||||
source <addr>[:<port>] [usesrc { <addr2>[:<port2>] | client | clientip } ]
|
||||
source <addr>[:<port>] [usesrc { <addr2>[:<port2>] | hdr_ip(<hdr>[,<occ>]) } ]
|
||||
source <addr>[:<port>] [interface <name>]
|
||||
Set the source address for outgoing connections
|
||||
May be used in sections : defaults | frontend | listen | backend
|
||||
@ -4155,6 +4156,29 @@ source <addr>[:<port>] [interface <name>]
|
||||
The default value of zero means the system will select a free
|
||||
port.
|
||||
|
||||
<hdr> is the name of a HTTP header in which to fetch the IP to bind to.
|
||||
This is the name of a comma-separated header list which can
|
||||
contain multiple IP addresses. By default, the last occurrence is
|
||||
used. This is designed to work with the X-Forwarded-For header
|
||||
and to automatically bind to the the client's IP address as seen
|
||||
by previous proxy, typically Stunnel. In order to use another
|
||||
occurrence from the last one, please see the <occ> parameter
|
||||
below. When the header (or occurrence) is not found, no binding
|
||||
is performed so that the proxy's default IP address is used. Also
|
||||
keep in mind that the header name is case insensitive, as for any
|
||||
HTTP header.
|
||||
|
||||
<occ> is the occurrence number of a value to be used in a multi-value
|
||||
header. This is to be used in conjunction with "hdr_ip(<hdr>)",
|
||||
in order to specificy which occurrence to use for the source IP
|
||||
address. Positive values indicate a position from the first
|
||||
occurrence, 1 being the first one. Negative values indicate
|
||||
positions relative to the last one, -1 being the last one. This
|
||||
is helpful for situations where an X-Forwarded-For header is set
|
||||
at the entry point of an infrastructure and must be used several
|
||||
proxy layers away. When this value is not specified, -1 is
|
||||
assumed. Passing a zero here disables the feature.
|
||||
|
||||
<name> is an optional interface name to which to bind to for outgoing
|
||||
traffic. On systems supporting this features (currently, only
|
||||
Linux), this allows one to bind all traffic to the server to
|
||||
@ -4230,6 +4254,11 @@ source <addr>[:<port>] [interface <name>]
|
||||
# with Tproxy version 4.
|
||||
source 0.0.0.0 usesrc clientip
|
||||
|
||||
backend transparent_http
|
||||
# Connect to the servers using the client's IP as seen by previous
|
||||
# proxy.
|
||||
source 0.0.0.0 usesrc hdr_ip(x-forwarded-for,-1)
|
||||
|
||||
See also : the "source" server option in section 5, the Tproxy patches for
|
||||
the Linux kernel on www.balabit.com, the "bind" keyword.
|
||||
|
||||
@ -5576,6 +5605,7 @@ slowstart <start_time_in_ms>
|
||||
Supported in default-server: Yes
|
||||
|
||||
source <addr>[:<pl>[-<ph>]] [usesrc { <addr2>[:<port2>] | client | clientip } ]
|
||||
source <addr>[:<port>] [usesrc { <addr2>[:<port2>] | hdr_ip(<hdr>[,<occ>]) } ]
|
||||
source <addr>[:<pl>[-<ph>]] [interface <name>] ...
|
||||
The "source" parameter sets the source address which will be used when
|
||||
connecting to the server. It follows the exact same parameters and principle
|
||||
|
@ -63,6 +63,11 @@
|
||||
#define MAX_HTTP_HDR ((BUFSIZE+79)/80)
|
||||
#endif
|
||||
|
||||
// max # of headers in history when looking for header #-X
|
||||
#ifndef MAX_HDR_HISTORY
|
||||
#define MAX_HDR_HISTORY 10
|
||||
#endif
|
||||
|
||||
// max # of loops we can perform around a read() which succeeds.
|
||||
// It's very frequent that the system returns a few TCP segments at a time.
|
||||
#ifndef MAX_READ_POLL_LOOPS
|
||||
|
@ -92,6 +92,8 @@ void http_return_srv_error(struct session *s, struct stream_interface *si);
|
||||
void http_capture_bad_message(struct error_snapshot *es, struct session *s,
|
||||
struct buffer *buf, struct http_msg *msg,
|
||||
struct proxy *other_end);
|
||||
unsigned int get_ip_from_hdr2(struct http_msg *msg, const char *hname, int hlen,
|
||||
struct hdr_idx *idx, int occ);
|
||||
|
||||
void http_init_txn(struct session *s);
|
||||
void http_end_txn(struct session *s);
|
||||
|
@ -249,6 +249,9 @@ struct proxy {
|
||||
struct sockaddr_in source_addr; /* the address to which we want to bind for connect() */
|
||||
#if defined(CONFIG_HAP_CTTPROXY) || defined(CONFIG_HAP_LINUX_TPROXY)
|
||||
struct sockaddr_in tproxy_addr; /* non-local address we want to bind to for connect() */
|
||||
char *bind_hdr_name; /* bind to this header name if defined */
|
||||
int bind_hdr_len; /* length of the name of the header above */
|
||||
int bind_hdr_occ; /* occurrence number of header above: >0 = from first, <0 = from end, 0=disabled */
|
||||
#endif
|
||||
int iface_len; /* bind interface name length */
|
||||
char *iface_name; /* bind interface name or NULL */
|
||||
|
@ -106,6 +106,9 @@ struct server {
|
||||
struct sockaddr_in source_addr; /* the address to which we want to bind for connect() */
|
||||
#if defined(CONFIG_HAP_CTTPROXY) || defined(CONFIG_HAP_LINUX_TPROXY)
|
||||
struct sockaddr_in tproxy_addr; /* non-local address we want to bind to for connect() */
|
||||
char *bind_hdr_name; /* bind to this header name if defined */
|
||||
int bind_hdr_len; /* length of the name of the header above */
|
||||
int bind_hdr_occ; /* occurrence number of header above: >0 = from first, <0 = from end, 0=disabled */
|
||||
#endif
|
||||
int iface_len; /* bind interface name length */
|
||||
char *iface_name; /* bind interface name or NULL */
|
||||
|
@ -837,8 +837,19 @@ static void assign_tproxy_address(struct session *s)
|
||||
/* FIXME: what can we do if the client connects in IPv6 ? */
|
||||
s->from_addr = *(struct sockaddr_in *)&s->cli_addr;
|
||||
break;
|
||||
case SRV_TPROXY_DYN:
|
||||
if (s->srv->bind_hdr_occ) {
|
||||
/* bind to the IP in a header */
|
||||
s->from_addr.sin_port = 0;
|
||||
s->from_addr.sin_addr.s_addr = htonl(get_ip_from_hdr2(&s->txn.req,
|
||||
s->srv->bind_hdr_name,
|
||||
s->srv->bind_hdr_len,
|
||||
&s->txn.hdr_idx,
|
||||
s->srv->bind_hdr_occ));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
s->from_addr = *(struct sockaddr_in *)0;
|
||||
memset(&s->from_addr, 0, sizeof(s->from_addr));
|
||||
}
|
||||
}
|
||||
else if (s->be->options & PR_O_BIND_SRC) {
|
||||
@ -851,8 +862,19 @@ static void assign_tproxy_address(struct session *s)
|
||||
/* FIXME: what can we do if the client connects in IPv6 ? */
|
||||
s->from_addr = *(struct sockaddr_in *)&s->cli_addr;
|
||||
break;
|
||||
case PR_O_TPXY_DYN:
|
||||
if (s->be->bind_hdr_occ) {
|
||||
/* bind to the IP in a header */
|
||||
s->from_addr.sin_port = 0;
|
||||
s->from_addr.sin_addr.s_addr = htonl(get_ip_from_hdr2(&s->txn.req,
|
||||
s->be->bind_hdr_name,
|
||||
s->be->bind_hdr_len,
|
||||
&s->txn.hdr_idx,
|
||||
s->be->bind_hdr_occ));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
s->from_addr = *(struct sockaddr_in *)0;
|
||||
memset(&s->from_addr, 0, sizeof(s->from_addr));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -3451,15 +3451,56 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
|
||||
}
|
||||
#endif
|
||||
if (!*args[cur_arg + 1]) {
|
||||
Alert("parsing [%s:%d] : '%s' expects <addr>[:<port>], 'client', or 'clientip' as argument.\n",
|
||||
Alert("parsing [%s:%d] : '%s' expects <addr>[:<port>], 'client', 'clientip', or 'hdr_ip(name,#)' as argument.\n",
|
||||
file, linenum, "usesrc");
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
}
|
||||
if (!strcmp(args[cur_arg + 1], "client")) {
|
||||
newsrv->state &= ~SRV_TPROXY_MASK;
|
||||
newsrv->state |= SRV_TPROXY_CLI;
|
||||
} else if (!strcmp(args[cur_arg + 1], "clientip")) {
|
||||
newsrv->state &= ~SRV_TPROXY_MASK;
|
||||
newsrv->state |= SRV_TPROXY_CIP;
|
||||
} else if (!strncmp(args[cur_arg + 1], "hdr_ip(", 7)) {
|
||||
char *name, *end;
|
||||
|
||||
name = args[cur_arg+1] + 7;
|
||||
while (isspace(*name))
|
||||
name++;
|
||||
|
||||
end = name;
|
||||
while (*end && !isspace(*end) && *end != ',' && *end != ')')
|
||||
end++;
|
||||
|
||||
newsrv->state &= ~SRV_TPROXY_MASK;
|
||||
newsrv->state |= SRV_TPROXY_DYN;
|
||||
newsrv->bind_hdr_name = calloc(1, end - name + 1);
|
||||
newsrv->bind_hdr_len = end - name;
|
||||
memcpy(newsrv->bind_hdr_name, name, end - name);
|
||||
newsrv->bind_hdr_name[end-name] = '\0';
|
||||
newsrv->bind_hdr_occ = -1;
|
||||
|
||||
/* now look for an occurrence number */
|
||||
while (isspace(*end))
|
||||
end++;
|
||||
if (*end == ',') {
|
||||
end++;
|
||||
name = end;
|
||||
if (*end == '-')
|
||||
end++;
|
||||
while (isdigit(*end))
|
||||
end++;
|
||||
newsrv->bind_hdr_occ = strl2ic(name, end-name);
|
||||
}
|
||||
|
||||
if (newsrv->bind_hdr_occ < -MAX_HDR_HISTORY) {
|
||||
Alert("parsing [%s:%d] : usesrc hdr_ip(name,num) does not support negative"
|
||||
" occurrences values smaller than %d.\n",
|
||||
file, linenum, MAX_HDR_HISTORY);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
struct sockaddr_in *sk = str2sa(args[cur_arg + 1]);
|
||||
if (!sk) {
|
||||
@ -3725,9 +3766,50 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
|
||||
}
|
||||
|
||||
if (!strcmp(args[cur_arg + 1], "client")) {
|
||||
curproxy->options &= ~PR_O_TPXY_MASK;
|
||||
curproxy->options |= PR_O_TPXY_CLI;
|
||||
} else if (!strcmp(args[cur_arg + 1], "clientip")) {
|
||||
curproxy->options &= ~PR_O_TPXY_MASK;
|
||||
curproxy->options |= PR_O_TPXY_CIP;
|
||||
} else if (!strncmp(args[cur_arg + 1], "hdr_ip(", 7)) {
|
||||
char *name, *end;
|
||||
|
||||
name = args[cur_arg+1] + 7;
|
||||
while (isspace(*name))
|
||||
name++;
|
||||
|
||||
end = name;
|
||||
while (*end && !isspace(*end) && *end != ',' && *end != ')')
|
||||
end++;
|
||||
|
||||
curproxy->options &= ~PR_O_TPXY_MASK;
|
||||
curproxy->options |= PR_O_TPXY_DYN;
|
||||
curproxy->bind_hdr_name = calloc(1, end - name + 1);
|
||||
curproxy->bind_hdr_len = end - name;
|
||||
memcpy(curproxy->bind_hdr_name, name, end - name);
|
||||
curproxy->bind_hdr_name[end-name] = '\0';
|
||||
curproxy->bind_hdr_occ = -1;
|
||||
|
||||
/* now look for an occurrence number */
|
||||
while (isspace(*end))
|
||||
end++;
|
||||
if (*end == ',') {
|
||||
end++;
|
||||
name = end;
|
||||
if (*end == '-')
|
||||
end++;
|
||||
while (isdigit(*end))
|
||||
end++;
|
||||
curproxy->bind_hdr_occ = strl2ic(name, end-name);
|
||||
}
|
||||
|
||||
if (curproxy->bind_hdr_occ < -MAX_HDR_HISTORY) {
|
||||
Alert("parsing [%s:%d] : usesrc hdr_ip(name,num) does not support negative"
|
||||
" occurrences values smaller than %d.\n",
|
||||
file, linenum, MAX_HDR_HISTORY);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
struct sockaddr_in *sk = str2sa(args[cur_arg + 1]);
|
||||
if (!sk) {
|
||||
@ -5051,6 +5133,13 @@ int check_config_validity()
|
||||
curproxy->options2 &= ~cfg_opts2[optnum].val;
|
||||
}
|
||||
}
|
||||
|
||||
if (curproxy->bind_hdr_occ) {
|
||||
curproxy->bind_hdr_occ = 0;
|
||||
Warning("config : %s '%s' : ignoring use of header %s as source IP in non-HTTP mode.\n",
|
||||
proxy_type_str(curproxy), curproxy->id, curproxy->bind_hdr_name);
|
||||
err_code |= ERR_WARN;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -5074,6 +5163,13 @@ int check_config_validity()
|
||||
proxy_type_str(curproxy), curproxy->id);
|
||||
cfgerr++;
|
||||
}
|
||||
|
||||
if (curproxy->mode != PR_MODE_HTTP && newsrv->bind_hdr_occ) {
|
||||
newsrv->bind_hdr_occ = 0;
|
||||
Warning("config : %s '%s' : server %s cannot use header %s as source IP in non-HTTP mode.\n",
|
||||
proxy_type_str(curproxy), curproxy->id, newsrv->id, newsrv->bind_hdr_name);
|
||||
err_code |= ERR_WARN;
|
||||
}
|
||||
newsrv = newsrv->next;
|
||||
}
|
||||
|
||||
|
@ -6534,6 +6534,59 @@ void http_capture_bad_message(struct error_snapshot *es, struct session *s,
|
||||
es->src = s->cli_addr;
|
||||
}
|
||||
|
||||
/* return the IP address pointed to by occurrence <occ> of header <hname> in
|
||||
* HTTP message <msg> indexed in <idx>. If <occ> is strictly positive, the
|
||||
* occurrence number corresponding to this value is returned. If <occ> is
|
||||
* strictly negative, the occurrence number before the end corresponding to
|
||||
* this value is returned. If <occ> is null, any value is returned, so it is
|
||||
* not recommended to use it that way. Negative occurrences are limited to
|
||||
* a small value because it is required to keep them in memory while scanning.
|
||||
* IP address 0.0.0.0 is returned if no match is found.
|
||||
*/
|
||||
unsigned int get_ip_from_hdr2(struct http_msg *msg, const char *hname, int hlen, struct hdr_idx *idx, int occ)
|
||||
{
|
||||
struct hdr_ctx ctx;
|
||||
unsigned int hdr_hist[MAX_HDR_HISTORY];
|
||||
unsigned int hist_ptr;
|
||||
int found = 0;
|
||||
|
||||
ctx.idx = 0;
|
||||
if (occ >= 0) {
|
||||
while (http_find_header2(hname, hlen, msg->sol, idx, &ctx)) {
|
||||
occ--;
|
||||
if (occ <= 0) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
return 0;
|
||||
return inetaddr_host_lim(ctx.line+ctx.val, ctx.line+ctx.val+ctx.vlen);
|
||||
}
|
||||
|
||||
/* negative occurrence, we scan all the list then walk back */
|
||||
if (-occ > MAX_HDR_HISTORY)
|
||||
return 0;
|
||||
|
||||
hist_ptr = 0;
|
||||
hdr_hist[hist_ptr] = 0;
|
||||
while (http_find_header2(hname, hlen, msg->sol, idx, &ctx)) {
|
||||
hdr_hist[hist_ptr++] = inetaddr_host_lim(ctx.line+ctx.val, ctx.line+ctx.val+ctx.vlen);
|
||||
if (hist_ptr >= MAX_HDR_HISTORY)
|
||||
hist_ptr = 0;
|
||||
found++;
|
||||
}
|
||||
if (-occ > found)
|
||||
return 0;
|
||||
/* OK now we have the last occurrence in [hist_ptr-1], and we need to
|
||||
* find occurrence -occ, so we have to check [hist_ptr+occ].
|
||||
*/
|
||||
hist_ptr += occ;
|
||||
if (hist_ptr >= MAX_HDR_HISTORY)
|
||||
hist_ptr -= MAX_HDR_HISTORY;
|
||||
return hdr_hist[hist_ptr];
|
||||
}
|
||||
|
||||
/*
|
||||
* Print a debug line with a header
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user