[MAJOR] support for source binding via cttproxy

Using the cttproxy kernel patch, it's possible to bind to any source
address. It is highly recommended to use the 03-natdel patch with the
other ones.

A new keyword appears as a complement to the "source" keyword : "usesrc".
The source address is mandatory and must be valid on the interface which
will see the packets. The "usesrc" option supports "client" (for full
client_ip:client_port spoofing), "client_ip" (for client_ip spoofing)
and any 'IP[:port]' combination to pretend to be another machine.

Right now, the source binding is missing from server health-checks if
set to another address. It must be implemented (think restricted firewalls).
The doc is still missing too.
This commit is contained in:
Willy Tarreau 2006-11-12 23:57:19 +01:00
parent 2152cb5b59
commit 77074d548b
7 changed files with 210 additions and 19 deletions

View File

@ -92,14 +92,60 @@ ADDLIB =
# set some defines when needed.
# Known ones are -DENABLE_POLL, -DENABLE_EPOLL, and -DUSE_MY_EPOLL
# - use -DTPROXY to compile with transparent proxy support.
# - use -DCONFIG_HAP_CTTPROXY to enable full transparent proxy support
DEFINE = -DTPROXY
#### build options
# do not change this one, enable USE_* variables instead.
OPTIONS =
ifneq ($(USE_CTTPROXY),)
OPTIONS += -DCONFIG_HAP_CTTPROXY
endif
ifneq ($(USE_TPROXY),)
OPTIONS += -DTPROXY
endif
ifneq ($(USE_POLL),)
OPTIONS += -DENABLE_POLL
endif
ifneq ($(USE_EPOLL),)
OPTIONS += -DENABLE_EPOLL
endif
ifneq ($(USE_MY_EPOLL),)
OPTIONS += -DUSE_MY_EPOLL
endif
ifneq ($(USE_NETFILTER),)
OPTIONS += -DNETFILTER
endif
ifneq ($(USE_EPOLL_WORKAROUND),)
OPTIONS += -DEPOLL_CTL_MOD_WORKAROUND
endif
ifneq ($(USE_GETSOCKNAME),)
OPTIONS += -DUSE_GETSOCKNAME
endif
ifneq ($(USE_REGPARM),)
OPTIONS += -DCONFIG_HAP_USE_REGPARM
endif
#### end of build options
# global options
TARGET_OPTS=$(COPTS.$(TARGET))
REGEX_OPTS=$(COPTS.$(REGEX))
CPU_OPTS=$(COPTS.$(CPU))
COPTS=-Iinclude $(ADDINC) $(CPU_OPTS) $(TARGET_OPTS) $(REGEX_OPTS) $(SMALL_OPTS) $(DEFINE)
COPTS=-Iinclude $(ADDINC) $(CPU_OPTS) $(TARGET_OPTS) $(REGEX_OPTS) $(SMALL_OPTS) $(DEFINE) $(OPTIONS)
LIBS=$(LIBS.$(TARGET)) $(LIBS.$(REGEX)) $(ADDLIB)
CFLAGS = -Wall $(COPTS) $(DEBUG)

View File

@ -53,6 +53,12 @@
#define PR_O_ABRT_CLOSE 0x00800000 /* immediately abort request when client closes */
#define PR_O_SSL3_CHK 0x01000000 /* use SSLv3 CLIENT_HELLO packets for server health */
#define PR_O_TPXY_ADDR 0x02000000 /* bind to this non-local address when connect()ing */
#define PR_O_TPXY_CIP 0x04000000 /* bind to the client's IP address when connect()ing */
#define PR_O_TPXY_CLI 0x06000000 /* bind to the client's IP+port when connect()ing */
#define PR_O_TPXY_MASK 0x06000000 /* bind to a non-local address when connect()ing */
#endif /* _TYPES_BACKEND_H */
/*

View File

@ -38,22 +38,27 @@
#define MODE_STARTING 128
#define MODE_FOREGROUND 256
/* list of last checks to perform, depending on config options */
#define LSTCHK_CAP_BIND 0x00000001 /* check that we can bind to any port */
#define LSTCHK_CTTPROXY 0x00000002 /* check that tproxy is enabled */
#define LSTCHK_NETADM 0x00000004 /* check that we have CAP_NET_ADMIN */
/* FIXME : this will have to be redefined correctly */
struct global {
int uid;
int gid;
int nbproc;
int maxconn;
int maxsock; /* max # of sockets */
int rlimit_nofile; /* default ulimit-n value : 0=unset */
int rlimit_memmax; /* default ulimit-d in megs value : 0=unset */
int mode;
char *chroot;
char *pidfile;
int logfac1, logfac2;
int loglev1, loglev2;
struct sockaddr_in logsrv1, logsrv2;
int uid;
int gid;
int nbproc;
int maxconn;
int maxsock; /* max # of sockets */
int rlimit_nofile; /* default ulimit-n value : 0=unset */
int rlimit_memmax; /* default ulimit-d in megs value : 0=unset */
int mode;
int last_checks;
char *chroot;
char *pidfile;
int logfac1, logfac2;
int loglev1, loglev2;
struct sockaddr_in logsrv1, logsrv2;
};
extern struct global global;

View File

@ -102,6 +102,9 @@ struct proxy {
int options; /* PR_O_REDISP, PR_O_TRANSP, ... */
int mode; /* mode = PR_MODE_TCP, PR_MODE_HTTP or PR_MODE_HEALTH */
struct sockaddr_in source_addr; /* the address to which we want to bind for connect() */
#ifdef CONFIG_HAP_CTTPROXY
struct sockaddr_in tproxy_addr; /* non-local address we want to bind to for connect() */
#endif
struct proxy *next;
struct sockaddr_in logsrv1, logsrv2; /* 2 syslog servers */
signed char logfac1, logfac2; /* log facility for both servers. -1 = disabled */

View File

@ -35,11 +35,16 @@
/* server flags */
#define SRV_RUNNING 1 /* the server is UP */
#define SRV_BACKUP 2 /* this server is a backup server */
#define SRV_MAPPORTS 4 /* this server uses mapped ports */
#define SRV_BIND_SRC 8 /* this server uses a specific source address */
#define SRV_CHECKED 16 /* this server needs to be checked */
#define SRV_RUNNING 0x0001 /* the server is UP */
#define SRV_BACKUP 0x0002 /* this server is a backup server */
#define SRV_MAPPORTS 0x0004 /* this server uses mapped ports */
#define SRV_BIND_SRC 0x0008 /* this server uses a specific source address */
#define SRV_CHECKED 0x0010 /* this server needs to be checked */
#define SRV_TPROXY_ADDR 0x0020 /* bind to this non-local address to reach this server */
#define SRV_TPROXY_CIP 0x0040 /* bind to the client's IP address to reach this server */
#define SRV_TPROXY_CLI 0x0060 /* bind to the client's IP+port to reach this server */
#define SRV_TPROXY_MASK 0x0060 /* bind to a non-local address to reach this server */
/* function which act on servers need to return various errors */
#define SRV_STATUS_OK 0 /* everything is OK. */
@ -60,6 +65,9 @@ struct server {
struct task *queue_mgt; /* the task associated to the queue processing */
struct sockaddr_in addr; /* the address to connect to */
struct sockaddr_in source_addr; /* the address to which we want to bind for connect() */
#ifdef CONFIG_HAP_CTTPROXY
struct sockaddr_in tproxy_addr; /* non-local address we want to bind to for connect() */
#endif
short check_port; /* the port to use for the health checks */
int health; /* 0->rise-1 = bad; rise->rise+fall-1 = good */
int rise, fall; /* time in iterations */

View File

@ -35,6 +35,9 @@
#include <proto/stream_sock.h>
#include <proto/task.h>
#ifdef CONFIG_HAP_CTTPROXY
#include <import/ip_tproxy.h>
#endif
/*
* This function recounts the number of usable active and backup servers for
@ -384,6 +387,42 @@ int connect_server(struct session *s)
s->proxy->id, s->srv->id);
return SN_ERR_RESOURCE;
}
#ifdef CONFIG_HAP_CTTPROXY
if (s->srv->state & SRV_TPROXY_MASK) {
struct in_tproxy itp1, itp2;
memset(&itp1, 0, sizeof(itp1));
itp1.op = TPROXY_ASSIGN;
switch (s->srv->state & SRV_TPROXY_MASK) {
case SRV_TPROXY_ADDR:
itp1.v.addr.faddr = s->srv->tproxy_addr.sin_addr;
itp1.v.addr.fport = s->srv->tproxy_addr.sin_port;
break;
case SRV_TPROXY_CLI:
itp1.v.addr.fport = ((struct sockaddr_in *)&s->cli_addr)->sin_port;
/* fall through */
case SRV_TPROXY_CIP:
/* FIXME: what can we do if the client connects in IPv6 ? */
itp1.v.addr.faddr = ((struct sockaddr_in *)&s->cli_addr)->sin_addr;
break;
}
/* set connect flag on socket */
itp2.op = TPROXY_FLAGS;
itp2.v.flags = ITP_CONNECT | ITP_ONCE;
if (setsockopt(fd, SOL_IP, IP_TPROXY, &itp1, sizeof(itp1)) == -1 ||
setsockopt(fd, SOL_IP, IP_TPROXY, &itp2, sizeof(itp2)) == -1) {
Alert("Cannot bind to tproxy source address before connect() for server %s/%s. Aborting.\n",
s->proxy->id, s->srv->id);
close(fd);
send_log(s->proxy, LOG_EMERG,
"Cannot bind to tproxy source address before connect() for server %s/%s.\n",
s->proxy->id, s->srv->id);
return SN_ERR_RESOURCE;
}
}
#endif
}
else if (s->proxy->options & PR_O_BIND_SRC) {
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof(one));
@ -395,6 +434,42 @@ int connect_server(struct session *s)
s->proxy->id, s->srv->id);
return SN_ERR_RESOURCE;
}
#ifdef CONFIG_HAP_CTTPROXY
if (s->proxy->options & PR_O_TPXY_MASK) {
struct in_tproxy itp1, itp2;
memset(&itp1, 0, sizeof(itp1));
itp1.op = TPROXY_ASSIGN;
switch (s->proxy->options & PR_O_TPXY_MASK) {
case PR_O_TPXY_ADDR:
itp1.v.addr.faddr = s->srv->tproxy_addr.sin_addr;
itp1.v.addr.fport = s->srv->tproxy_addr.sin_port;
break;
case PR_O_TPXY_CLI:
itp1.v.addr.fport = ((struct sockaddr_in *)&s->cli_addr)->sin_port;
/* fall through */
case PR_O_TPXY_CIP:
/* FIXME: what can we do if the client connects in IPv6 ? */
itp1.v.addr.faddr = ((struct sockaddr_in *)&s->cli_addr)->sin_addr;
break;
}
/* set connect flag on socket */
itp2.op = TPROXY_FLAGS;
itp2.v.flags = ITP_CONNECT | ITP_ONCE;
if (setsockopt(fd, SOL_IP, IP_TPROXY, &itp1, sizeof(itp1)) == -1 ||
setsockopt(fd, SOL_IP, IP_TPROXY, &itp2, sizeof(itp2)) == -1) {
Alert("Cannot bind to tproxy source address before connect() for proxy %s. Aborting.\n",
s->proxy->id);
close(fd);
send_log(s->proxy, LOG_EMERG,
"Cannot bind to tproxy source address before connect() for server %s/%s.\n",
s->proxy->id, s->srv->id);
return SN_ERR_RESOURCE;
}
}
#endif
}
if ((connect(fd, (struct sockaddr *)&s->srv_addr, sizeof(s->srv_addr)) == -1) &&

View File

@ -1153,6 +1153,30 @@ int cfg_parse_listen(const char *file, int linenum, char **args)
newsrv->state |= SRV_BIND_SRC;
newsrv->source_addr = *str2sa(args[cur_arg + 1]);
cur_arg += 2;
#ifdef CONFIG_HAP_CTTPROXY
if (!strcmp(args[cur_arg], "usesrc")) { /* address to use outside */
if (newsrv->source_addr.sin_addr.s_addr == INADDR_ANY) {
Alert("parsing [%s:%d] : '%s' requires an explicit 'source' address.\n",
file, linenum, "usesrc");
return -1;
}
if (!*args[cur_arg + 1]) {
Alert("parsing [%s:%d] : '%s' expects <addr>[:<port>], 'client', or 'clientip' as argument.\n",
file, linenum, "usesrc");
return -1;
}
if (!strcmp(args[cur_arg + 1], "client")) {
newsrv->state |= SRV_TPROXY_CLI;
} else if (!strcmp(args[cur_arg + 1], "clientip")) {
newsrv->state |= SRV_TPROXY_CIP;
} else {
newsrv->state |= SRV_TPROXY_ADDR;
newsrv->tproxy_addr = *str2sa(args[cur_arg + 1]);
}
global.last_checks |= LSTCHK_CTTPROXY | LSTCHK_NETADM;
cur_arg += 2;
}
#endif
}
else {
Alert("parsing [%s:%d] : server %s only supports options 'backup', 'cookie', 'check', 'inter', 'rise', 'fall', 'port', 'source', 'minconn', 'maxconn' and 'weight'.\n",
@ -1241,6 +1265,30 @@ int cfg_parse_listen(const char *file, int linenum, char **args)
curproxy->source_addr = *str2sa(args[1]);
curproxy->options |= PR_O_BIND_SRC;
#ifdef CONFIG_HAP_CTTPROXY
if (!strcmp(args[2], "usesrc")) { /* address to use outside */
if (curproxy->source_addr.sin_addr.s_addr == INADDR_ANY) {
Alert("parsing [%s:%d] : '%s' requires an explicit 'source' address.\n",
file, linenum, "usesrc");
return -1;
}
if (!*args[3]) {
Alert("parsing [%s:%d] : '%s' expects <addr>[:<port>], 'client', or 'clientip' as argument.\n",
file, linenum, "usesrc");
return -1;
}
if (!strcmp(args[3], "client")) {
curproxy->options |= PR_O_TPXY_CLI;
} else if (!strcmp(args[3], "clientip")) {
curproxy->options |= PR_O_TPXY_CIP;
} else {
curproxy->options |= PR_O_TPXY_ADDR;
curproxy->tproxy_addr = *str2sa(args[3]);
}
global.last_checks |= LSTCHK_CTTPROXY | LSTCHK_NETADM;
}
#endif
}
else if (!strcmp(args[0], "cliexp") || !strcmp(args[0], "reqrep")) { /* replace request header from a regex */
regex_t *preg;