hi folks:
as suggested in http://www.stunnel.org/faq/transparent.html#ToC3, I modified the stunnel code to use the transparent proxy funcionality on 2.6 (and possibly 2.4) linux kernels with the cttproxy patch (http://www.balabit.com/products/oss/tproxy/, I used )cttproxy-2.6.12-2.0.2. on a clean 2.6.12.5 kernel)
the patch is higly incomplete, but it works (see TODO).
please try it if you are interested in transparent stunnel.
any feedback il welcome.
MAtteo
the code is heavly based on the examples distributed along with the cttproxy patch, many thanks to Balázs Scheidler for its great work and for making it GPL.
with this patch, I was able to run a stunnel in server mode with the transparent option, and the application server I connected to saw the IP address of the stunnel client.
of course, this code il also redistributed under the GPL.
conditions for running stunnel in transparent mode:
if you want to try this patch, you MUST modify the hardcoded IP address in client.c to the IP address of the stunnel server (look for DOIT! in the source code). any improvements wellcome.
the stunnel server must run a linux kernel patched with the cttproxy patch
the stunnel server and the application server cannot be the same host
the application server must believe that it has to route all packets of transparently tunneled connections via the stunnel server. thus the two server should be on the same subnet.
I was also able to use linux policy routing to avoid modifying the general routing on the application server: transparently tunneled connections are directed to a different IP on the application server, where a policy rule applies a specific routing table to packets with that source address (see at the end of this message).
TODO:
stunnel must explicitly bind to an *existing* local address (not loopback) before calling setsockopt(SOL_IP, IP_TPROXY, TPROXY_ASSIGN)"). now the address is hardcoded in the patch, it should automatically find a local address to bind to (or get it from the config file)
now the code will only run with tproxy patch version 2.0.2 it should be less strict about tproxy version (make it a configurable option in stunnel?). which tproxy version will it run with?
auto detect or configure kernel version (2.2 vs 2.4 or 2.6 with cttproxy), or better add it in .configure
----begin patch----- *** /tmp/stunnel-4.11/src/client.c 2005-07-02 08:55:58.000000000 +0200 --- stunnel-4.11/src/client.c 2005-08-22 12:19:57.000000000 +0200 *************** *** 44,49 **** --- 44,60 ---- #define SHUT_RDWR 2 #endif
+ #define LINUX_NETFILTER + #define LINUX_TPROXY + + /* transparent proxy */ + #ifdef LINUX_NETFILTER + #include <linux/netfilter_ipv4.h> + #endif + #ifdef LINUX_TPROXY + #include <linux/netfilter_ipv4/ip_tproxy.h> + #endif + /* TCP wrapper */ #ifdef USE_LIBWRAP #include <tcpd.h> *************** *** 921,926 **** --- 932,944 ---- int s; /* destination socket */ u16 i;
+ // struct in_addr *local=NULL; + #ifdef LINUX_TPROXY + int f; + struct in_tproxy itp; + struct sockaddr_in tproxy_sin; + #endif + /* setup address_list */ if(c->opt->option.delayed_lookup) { resolved_list.num=0; *************** *** 945,960 **** if(alloc_fd(s)) return -1;
! if(c->bind_addr.num) { /* explicit local bind or transparent proxy */ ! memcpy(&bind_addr, &c->bind_addr.addr[0], sizeof(SOCKADDR_UNION)); ! if(bind(s, &bind_addr.sa, addr_len(bind_addr))<0) { ! sockerror("bind transparent"); ! closesocket(s); ! return -1; ! } ! } ! ! /* try to connect for the 1st time */ s_ntop(c->connecting_address, &addr); s_log(LOG_DEBUG, "%s connecting %s", c->opt->servname, c->connecting_address); --- 963,1053 ---- if(alloc_fd(s)) return -1;
! if(c->bind_addr.num && !c->opt->option.transparent) { ! /* explicit local bind, not transparent mode */ ! ! memcpy(&bind_addr, &c->bind_addr.addr[0], sizeof(SOCKADDR_UNION)); ! if(bind(s, &bind_addr.sa, addr_len(bind_addr))<0) { ! sockerror("bind transparent"); ! closesocket(s); ! return -1; ! } ! sockerror("local bind succeeded"); ! } ! ! if (c->opt->option.transparent){ ! ! /* transparent proxy support on 2.[46] linux kernels */ ! /* added by MAtteo HCE Valsasna (matteo.valsasna@uninsubria.it), 20050822 */ ! /* requires tproxy kernel patch (http://www.balabit.com/products/oss/tproxy */ ! /* based on examples in cttproxy patch */ ! ! /* FIXME: should detect 2.2 kernel or 2.[46] + cttproxy patch and use appropriate code */ ! ! memcpy(&bind_addr, &c->bind_addr.addr[0], sizeof(SOCKADDR_UNION)); ! ! /* check tproxy version */ ! itp.op = TPROXY_VERSION; ! itp.v.version = 0x00000000; ! ! getsockopt(s, SOL_IP, IP_TPROXY, &itp, &f); ! s_log(LOG_NOTICE, "pre getsockopt(s, SOL_IP, IP_TPROXY): %x", itp.v.version); ! ! /* FIXME: should accept a range of tproxy versions - which? */ ! ! if (itp.v.version != 0x02000002) { ! itp.v.version = 0x02000002; ! if (setsockopt(s, SOL_IP, IP_TPROXY, &itp, sizeof(itp)) == -1) ! { ! sockerror("setsockopt(SOL_IP, IP_TPROXY, TPROXY_VERSION)"); ! getsockopt(s, SOL_IP, IP_TPROXY, &itp, &f); ! s_log(LOG_NOTICE, "err getsockopt(s, SOL_IP, IP_TPROXY): %x", itp.v.version); ! return -1; ! } ! } ! else{ ! s_log(LOG_NOTICE, "getsockopt(SOL_IP, IP_TPROXY, TPROXY_VERSION) returned right version"); ! } ! ! /* bind to local address */ ! /* FIXME: must become dynamic */ ! tproxy_sin.sin_family = AF_INET; ! /* DOIT! add a local IP address here */ ! inet_aton("192.168.179.71", &tproxy_sin.sin_addr); ! /* do not request a specific source port */ ! tproxy_sin.sin_port = htons(0); ! ! if (bind(s, (struct sockaddr *) &tproxy_sin, sizeof(tproxy_sin)) == -1) ! { ! sockerror("bind"); ! return -1; ! } ! s_log(LOG_NOTICE, "bound to local: %s", inet_ntoa(tproxy_sin.sin_addr)); ! ! /* assign foreign address */ ! itp.op = TPROXY_ASSIGN; ! itp.v.addr.faddr = bind_addr.in.sin_addr; ! itp.v.addr.fport = 0; ! ! if (setsockopt(s, SOL_IP, IP_TPROXY, &itp, sizeof(itp)) == -1) ! { ! s_log(LOG_NOTICE, "error assigning foreign address: %s", inet_ntoa(bind_addr.in.sin_addr)); ! sockerror("setsockopt(SOL_IP, IP_TPROXY, TPROXY_ASSIGN)"); ! return -1; ! } ! s_log(LOG_NOTICE, "assigned foreign address: %s", inet_ntoa(bind_addr.in.sin_addr)); ! ! /* set connect flag on socket */ ! itp.op = TPROXY_FLAGS; ! itp.v.flags = ITP_CONNECT|ITP_ONCE; ! if (setsockopt(s, SOL_IP, IP_TPROXY, &itp, sizeof(itp)) == -1) ! { ! sockerror("setsockopt(SOL_IP, IP_TPROXY, TPROXY_FLAGS)"); ! return -1; ! } ! } ! ! /* try to connect for the 1st time */ s_ntop(c->connecting_address, &addr); s_log(LOG_DEBUG, "%s connecting %s", c->opt->servname, c->connecting_address);
----end patch-----
EXTRAS
source routing on the application server
intended usage: the application server is a squid proxy with pubblic adresses. its default gw is an HW-based router (not the transparent stunnel server).
we want normal traffic (i.e. not handled by transparent stunnel) to continue to use normal routing. only traffic handled by stunnel should be routed back to the stunnel server.
a new IP address (10.0.0.1) is added on the application server specifically for this purpose. stunnel server will forward connections to this address, normal clients will use another address. then, only traffic originating from this address needs to be routed back to the stunnel server.
the stunnel server has IP 10.0.0.254 (but it can have more, of course)
in /etc/iproute2/rt_tables add a line defining a new routing table: 10 inr.tproxy
# add a rule to the new routing table ip r a table inr.tproxy default via 10.0.0.254
# create ip policy routing rule: packets with source address = # 10.0.0.1 should be routed according to the inr.tproxy table
ip rule add type unicast from 10.0.0.1 table inr.tproxy priority 220 # apply policy routing changes ip route flush cache