MEDIUM: http-ana: Add IPv6 support for forwardfor and orignialto options

A network may be specified to avoid header addition for "forwardfor" and
"orignialto" option via the "except" parameter. However, only IPv4
networks/addresses are supported. This patch adds the support of IPv6.

To do so, the net_addr structure is used to store the parameter value in the
proxy structure. And ipcmp2net() function is used to perform the comparison.

This patch should fix the issue #1145. It depends on the following commit:

  * c6ce0ab MINOR: tools: Add function to compare an address to a network address
  * 5587287 MINOR: tools: Add net_addr structure describing a network addess
This commit is contained in:
Christopher Faulet 2021-02-26 09:19:15 +01:00
parent 9553de7fec
commit 5d1def623a
5 changed files with 86 additions and 48 deletions

View File

@ -7973,7 +7973,7 @@ option forwardfor [ except <network> ] [ header <name> ] [ if-none ]
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.
private networks or 127.0.0.1. IPv4 and IPv6 are both supported.
Alternatively, the keyword "if-none" states that the header will only be
added if it is not present. This should only be used in perfectly trusted
@ -8773,7 +8773,7 @@ option originalto [ except <network> ] [ header <name> ]
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.
private networks or 127.0.0.1. IPv4 and IPv6 are both supported.
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

View File

@ -41,6 +41,7 @@
#include <haproxy/stats-t.h>
#include <haproxy/tcpcheck-t.h>
#include <haproxy/thread-t.h>
#include <haproxy/tools-t.h>
#include <haproxy/uri_auth-t.h>
/* values for proxy->mode */
@ -341,9 +342,8 @@ struct proxy {
unsigned int fe_sps_lim; /* limit on new sessions per second on the frontend */
unsigned int fullconn; /* #conns on backend above which servers are used at full load */
unsigned int tot_fe_maxconn; /* #maxconn of frontends linked to that backend, it is used to compute fullconn */
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. */
struct net_addr except_xff_net; /* don't x-forward-for for this address. */
struct net_addr except_xot_net; /* don't x-original-to for this address. */
char *fwdfor_hdr_name; /* header to use - default: "x-forwarded-for" */
char *orgto_hdr_name; /* header to use - default: "x-original-to" */
int fwdfor_hdr_len; /* length of "x-forwarded-for" header */

View File

@ -2107,20 +2107,35 @@ stats_error_parsing:
free(curproxy->fwdfor_hdr_name);
curproxy->fwdfor_hdr_name = strdup(DEF_XFORWARDFOR_HDR);
curproxy->fwdfor_hdr_len = strlen(DEF_XFORWARDFOR_HDR);
curproxy->except_xff_net.family = AF_UNSPEC;
/* 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") == 0) {
unsigned char mask;
int i;
/* suboption except - needs additional argument for it */
if (!*(args[cur_arg+1]) || !str2net(args[cur_arg+1], 1, &curproxy->except_net, &curproxy->except_mask)) {
if (*(args[cur_arg+1]) &&
str2net(args[cur_arg+1], 1, &curproxy->except_xff_net.addr.v4.ip, &curproxy->except_xff_net.addr.v4.mask)) {
curproxy->except_xff_net.family = AF_INET;
curproxy->except_xff_net.addr.v4.ip.s_addr &= curproxy->except_xff_net.addr.v4.mask.s_addr;
}
else if (*(args[cur_arg+1]) &&
str62net(args[cur_arg+1], &curproxy->except_xff_net.addr.v6.ip, &mask)) {
curproxy->except_xff_net.family = AF_INET6;
len2mask6(mask, &curproxy->except_xff_net.addr.v6.mask);
for (i = 0; i < 16; i++)
curproxy->except_xff_net.addr.v6.ip.s6_addr[i] &= curproxy->except_xff_net.addr.v6.mask.s6_addr[i];
}
else {
ha_alert("parsing [%s:%d] : '%s %s %s' expects <address>[/mask] as argument.\n",
file, linenum, args[0], args[1], args[cur_arg]);
err_code |= ERR_ALERT | ERR_FATAL;
goto out;
}
/* flush useless bits */
curproxy->except_net.s_addr &= curproxy->except_mask.s_addr;
cur_arg += 2;
} else if (strcmp(args[cur_arg], "header") == 0) {
/* suboption header - needs additional argument for it */
@ -2158,20 +2173,34 @@ stats_error_parsing:
free(curproxy->orgto_hdr_name);
curproxy->orgto_hdr_name = strdup(DEF_XORIGINALTO_HDR);
curproxy->orgto_hdr_len = strlen(DEF_XORIGINALTO_HDR);
curproxy->except_xot_net.family = AF_UNSPEC;
/* loop to go through arguments - start at 2, since 0+1 = "option" "originalto" */
cur_arg = 2;
while (*(args[cur_arg])) {
if (strcmp(args[cur_arg], "except") == 0) {
unsigned char mask;
int i;
/* suboption except - needs additional argument for it */
if (!*(args[cur_arg+1]) || !str2net(args[cur_arg+1], 1, &curproxy->except_to, &curproxy->except_mask_to)) {
if (*(args[cur_arg+1]) &&
str2net(args[cur_arg+1], 1, &curproxy->except_xot_net.addr.v4.ip, &curproxy->except_xot_net.addr.v4.mask)) {
curproxy->except_xot_net.family = AF_INET;
curproxy->except_xot_net.addr.v4.ip.s_addr &= curproxy->except_xot_net.addr.v4.mask.s_addr;
}
else if (*(args[cur_arg+1]) &&
str62net(args[cur_arg+1], &curproxy->except_xot_net.addr.v6.ip, &mask)) {
curproxy->except_xot_net.family = AF_INET6;
len2mask6(mask, &curproxy->except_xot_net.addr.v6.mask);
for (i = 0; i < 16; i++)
curproxy->except_xot_net.addr.v6.ip.s6_addr[i] &= curproxy->except_xot_net.addr.v6.mask.s6_addr[i];
}
else {
ha_alert("parsing [%s:%d] : '%s %s %s' expects <address>[/mask] as argument.\n",
file, linenum, args[0], args[1], args[cur_arg]);
err_code |= ERR_ALERT | ERR_FATAL;
goto out;
}
/* flush useless bits */
curproxy->except_to.s_addr &= curproxy->except_mask_to.s_addr;
cur_arg += 2;
} else if (strcmp(args[cur_arg], "header") == 0) {
/* suboption header - needs additional argument for it */

View File

@ -673,12 +673,8 @@ int http_process_request(struct stream *s, struct channel *req, int an_bit)
/* Add an X-Forwarded-For header unless the source IP is
* in the 'except' network range.
*/
if ((!sess->fe->except_mask.s_addr ||
(((struct sockaddr_in *)cli_conn->src)->sin_addr.s_addr & sess->fe->except_mask.s_addr)
!= sess->fe->except_net.s_addr) &&
(!s->be->except_mask.s_addr ||
(((struct sockaddr_in *)cli_conn->src)->sin_addr.s_addr & s->be->except_mask.s_addr)
!= s->be->except_net.s_addr)) {
if (ipcmp2net(cli_conn->src, &sess->fe->except_xff_net) &&
ipcmp2net(cli_conn->src, &s->be->except_xff_net)) {
unsigned char *pn = (unsigned char *)&((struct sockaddr_in *)cli_conn->src)->sin_addr;
/* Note: we rely on the backend to get the header name to be used for
@ -692,23 +688,26 @@ int http_process_request(struct stream *s, struct channel *req, int an_bit)
}
}
else if (cli_conn && conn_get_src(cli_conn) && cli_conn->src->ss_family == AF_INET6) {
/* FIXME: for the sake of completeness, we should also support
* 'except' here, although it is mostly useless in this case.
/* Add an X-Forwarded-For header unless the source IP is
* in the 'except' network range.
*/
char pn[INET6_ADDRSTRLEN];
if (ipcmp2net(cli_conn->src, &sess->fe->except_xff_net) &&
ipcmp2net(cli_conn->src, &s->be->except_xff_net)) {
char pn[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6,
(const void *)&((struct sockaddr_in6 *)(cli_conn->src))->sin6_addr,
pn, sizeof(pn));
inet_ntop(AF_INET6,
(const void *)&((struct sockaddr_in6 *)(cli_conn->src))->sin6_addr,
pn, sizeof(pn));
/* Note: we rely on the backend to get the header name to be used for
* x-forwarded-for, 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.
*/
chunk_printf(&trash, "%s", pn);
if (unlikely(!http_add_header(htx, hdr, ist2(trash.area, trash.data))))
goto return_int_err;
/* Note: we rely on the backend to get the header name to be used for
* x-forwarded-for, 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.
*/
chunk_printf(&trash, "%s", pn);
if (unlikely(!http_add_header(htx, hdr, ist2(trash.area, trash.data))))
goto return_int_err;
}
}
}
@ -717,20 +716,15 @@ int http_process_request(struct stream *s, struct channel *req, int an_bit)
* asks for it.
*/
if ((sess->fe->options | s->be->options) & PR_O_ORGTO) {
struct ist hdr = ist2(s->be->orgto_hdr_len ? s->be->orgto_hdr_name : sess->fe->orgto_hdr_name,
s->be->orgto_hdr_len ? s->be->orgto_hdr_len : sess->fe->orgto_hdr_len);
/* FIXME: don't know if IPv6 can handle that case too. */
if (cli_conn && conn_get_dst(cli_conn) && cli_conn->dst->ss_family == AF_INET) {
/* Add an X-Original-To header unless the destination IP is
* in the 'except' network range.
*/
if (cli_conn->dst->ss_family == AF_INET &&
((!sess->fe->except_mask_to.s_addr ||
(((struct sockaddr_in *)cli_conn->dst)->sin_addr.s_addr & sess->fe->except_mask_to.s_addr)
!= sess->fe->except_to.s_addr) &&
(!s->be->except_mask_to.s_addr ||
(((struct sockaddr_in *)cli_conn->dst)->sin_addr.s_addr & s->be->except_mask_to.s_addr)
!= s->be->except_to.s_addr))) {
struct ist hdr;
if (ipcmp2net(cli_conn->dst, &sess->fe->except_xot_net) &&
ipcmp2net(cli_conn->dst, &s->be->except_xot_net)) {
unsigned char *pn = (unsigned char *)&((struct sockaddr_in *)cli_conn->dst)->sin_addr;
/* Note: we rely on the backend to get the header name to be used for
@ -738,16 +732,33 @@ int http_process_request(struct stream *s, struct channel *req, int an_bit)
* 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)
hdr = ist2(s->be->orgto_hdr_name, s->be->orgto_hdr_len);
else
hdr = ist2(sess->fe->orgto_hdr_name, sess->fe->orgto_hdr_len);
chunk_printf(&trash, "%d.%d.%d.%d", pn[0], pn[1], pn[2], pn[3]);
if (unlikely(!http_add_header(htx, hdr, ist2(trash.area, trash.data))))
goto return_int_err;
}
}
else if (cli_conn && conn_get_dst(cli_conn) && cli_conn->dst->ss_family == AF_INET6) {
/* Add an X-Original-To header unless the source IP is
* in the 'except' network range.
*/
if (ipcmp2net(cli_conn->dst, &sess->fe->except_xot_net) &&
ipcmp2net(cli_conn->dst, &s->be->except_xot_net)) {
char pn[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6,
(const void *)&((struct sockaddr_in6 *)(cli_conn->dst))->sin6_addr,
pn, sizeof(pn));
/* Note: we rely on the backend to get the header name to be used for
* x-forwarded-for, 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.
*/
chunk_printf(&trash, "%s", pn);
if (unlikely(!http_add_header(htx, hdr, ist2(trash.area, trash.data))))
goto return_int_err;
}
}
}
/* If we have no server assigned yet and we're balancing on url_param

View File

@ -1229,10 +1229,8 @@ struct proxy *alloc_new_proxy(const char *name, unsigned int cap, const char *fi
curproxy->no_options = defproxy->no_options;
curproxy->no_options2 = defproxy->no_options2;
curproxy->bind_proc = defproxy->bind_proc;
curproxy->except_net = defproxy->except_net;
curproxy->except_mask = defproxy->except_mask;
curproxy->except_to = defproxy->except_to;
curproxy->except_mask_to = defproxy->except_mask_to;
curproxy->except_xff_net = defproxy->except_xff_net;
curproxy->except_xot_net = defproxy->except_xot_net;
curproxy->retry_type = defproxy->retry_type;
if (defproxy->fwdfor_hdr_len) {