hello folks,
here comes an improved version of the transparent proxy patch, basically solving the previous TODO issues.
please try it if you are interested in transparent stunnel.
any feedback il welcome.
the code is heavly based on the examples distributed along with the cttproxy patch, many thanks to Balzs Scheidler for its great work and to Lennert Buytenhek for the skaidrus example program.
this code is (re)distributed under the GPL version 2
MAtteo
---README--- cttproxy patch to stunnel
MAtteo HCE Valsasna (matteo dot valsasna at uninsubria dot it) based on example code in cttproxy patch
ports stunnel transparent proxy to cttproxy kernel patch
allow stunnel server to connect to the application server faking the stunnel client's IP source address. the server sees connections coming from the stunnel client's IP address instead of that of the stunnel server, and thus can apply admission control, bandwidth control, logging policies as if the client connected directly.
should work 2.4 and 2.6 kernels and 2.0.x cttproxy patch
tested with kernel 2.6.12.5 and cttproxy-2.6.12-2.0.2 patch
setup:
* application server(s): here run the application(s) you want to access trough stunnel
must route all traffic, or at least all traffic for transproxyed connections, trough the stunnel server
* stunnel server: must not be the same host as the application server
grab recent kernel source patch your kernel source with the appropriate cttproxy patch (from http://www.balabit.com/products/oss/tproxy/)
configure kernel with: CONFIG_IP_NF_TPROXY=m CONFIG_IP_NF_MATCH_TPROXY=m CONFIG_IP_NF_TARGET_TPROXY=m
make and install kernel reboot and choose patched kernel
load tproxy kernel modules # modprobe iptable_tproxy # modprobe ipt_tproxy
enable ip forwarding (this must act as a router) # echo 1 > /proc/sys/net/ipv4/ip_forward
grab stunnel source and extract it $ tar xzf stunnel-4.11.tar.gz
patch it $ patch -p0 < cttproxy.patch
configure selecting tproxy support $ ./configure --enable-tproxy
make and install $ make # make install
run stunnel in server mode with transparent = yes remote = <application server>:<port> in conf file
* stunnel client run plain stunnel, client = yes remote = stunnel server
* application client may or may not be the same host as the stunnel client
connect to stunnel client to talk securely to application server
---PATCH---diff -crb /tmp/stunnel-4.11/configure stunnel-4.11/configure *** /tmp/stunnel-4.11/configure 2005-07-09 23:00:44.000000000 +0200 --- stunnel-4.11/configure 2005-08-25 16:59:57.000000000 +0200 *************** *** 463,469 **** # include <unistd.h> #endif"
! ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CYGPATH_W PACKAGE VERSION ACLOCAL AUTOCONF AUTOMAKE AUTOHEADER MAKEINFO install_sh STRIP ac_ct_STRIP INSTALL_STRIP_PROGRAM mkdir_p AWK SET_MAKE am__leading_dot AMTAR am__tar am__untar build build_cpu build_vendor build_os host host_cpu host_vendor host_os CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT DEPDIR am__include am__quote AMDEP_TRUE AMDEP_FALSE AMDEPBACKSLASH CCDEPMODE am__fastdepCC_TRUE am__fastdepCC_FALSE EGREP LN_S ECHO AR ac_ct_AR RANLIB ac_ct_RANLIB CPP CXX CXXFLAGS ac_ct_CXX CXXDEPMODE am__fastdepCXX_TRUE am__fastdepCXX_FALSE CXXCPP F77 FFLAGS ac_ct_F77 LIBTOOL ssldir RANDOM_FILE USE_DH USE_IPv6 LIBOBJS LTLIBOBJS' ac_subst_files=''
# Initialize some variables set by options. --- 463,469 ---- # include <unistd.h> #endif"
! ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CYGPATH_W PACKAGE VERSION ACLOCAL AUTOCONF AUTOMAKE AUTOHEADER MAKEINFO install_sh STRIP ac_ct_STRIP INSTALL_STRIP_PROGRAM mkdir_p AWK SET_MAKE am__leading_dot AMTAR am__tar am__untar build build_cpu build_vendor build_os host host_cpu host_vendor host_os CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT DEPDIR am__include am__quote AMDEP_TRUE AMDEP_FALSE AMDEPBACKSLASH CCDEPMODE am__fastdepCC_TRUE am__fastdepCC_FALSE CPP LN_S RANLIB ac_ct_RANLIB EGREP ECHO AR ac_ct_AR CXX CXXFLAGS ac_ct_CXX CXXDEPMODE am__fastdepCXX_TRUE am__fastdepCXX_FALSE CXXCPP F77 FFLAGS ac_ct_F77 LIBTOOL ssldir RANDOM_FILE USE_DH USE_IPv6 USE_TPROXY LIBOBJS LTLIBOBJS' ac_subst_files=''
# Initialize some variables set by options. *************** *** 1037,1042 **** --- 1037,1043 ---- --disable-rsa Disable RSA support --enable-dh Enable DH support --enable-ipv6 Enable IPv6 support + --enable-tproxy Enable linux 2.6 tproxy patch support --disable-libwrap Disable TCP wrappers library support
Optional Packages: *************** *** 23166,23171 **** --- 23344,23367 ---- fi;
+ # use linux 2.[46] tproxy patch + echo "$as_me:$LINENO: checking whether to enable transparent proxyng using linux 2.46 tproxy patch" >&5 + echo $ECHO_N "checking whether to enable transparent proxyng using linux 2.46 tproxy patch... $ECHO_C" >&6 + # Check whether --enable-tproxy or --disable-tproxy was given. + if test "${enable_tproxy+set}" = set; then + enableval="$enable_tproxy" + echo "$as_me:$LINENO: result: yes" >&5 + echo "${ECHO_T}yes" >&6; cat >>confdefs.h <<_ACEOF + #define USE_TPROXY 1 + _ACEOF + + else + echo "$as_me:$LINENO: result: no" >&5 + echo "${ECHO_T}no" >&6 + + fi; + + # Disable use of libwrap (TCP wrappers) # it should be the last check! echo "$as_me:$LINENO: checking whether to disable TCP wrappers library support" >&5 *************** *** 23987,23992 **** --- 24184,24190 ---- s,@RANDOM_FILE@,$RANDOM_FILE,;t t s,@USE_DH@,$USE_DH,;t t s,@USE_IPv6@,$USE_IPv6,;t t + s,@USE_TPROXY@,$USE_TPROXY,;t t s,@LIBOBJS@,$LIBOBJS,;t t s,@LTLIBOBJS@,$LTLIBOBJS,;t t CEOF diff -crb /tmp/stunnel-4.11/configure.ac stunnel-4.11/configure.ac *** /tmp/stunnel-4.11/configure.ac 2005-07-09 22:58:05.000000000 +0200 --- stunnel-4.11/configure.ac 2005-08-25 16:56:33.000000000 +0200 *************** *** 14,21 **** --- 14,24 ---- if test "$GCC" = "yes" then CFLAGS="$CFLAGS -Wall -Wshadow -Wcast-align -Wpointer-arith" fi + AC_PROG_CPP AC_PROG_INSTALL + AC_PROG_LN_S AC_PROG_MAKE_SET + AC_PROG_RANLIB
# Checks for typedefs, structures, and compiler characteristics # AC_C_CONST *************** *** 254,259 **** --- 257,271 ---- ) AC_SUBST(USE_IPv6)
+ # use linux 2.[46] tproxy patch + AC_MSG_CHECKING([whether to enable transparent proxyng using linux 2.[46] tproxy patch]) + AC_ARG_ENABLE(tproxy, + [ --enable-tproxy Enable linux 2.6 tproxy patch support], + [AC_MSG_RESULT([yes]); AC_DEFINE(USE_TPROXY)], + [AC_MSG_RESULT([no])] + ) + AC_SUBST(USE_TPROXY) + # Disable use of libwrap (TCP wrappers) # it should be the last check! AC_MSG_CHECKING([whether to disable TCP wrappers library support]) diff -crb /tmp/stunnel-4.11/src/client.c stunnel-4.11/src/client.c *** /tmp/stunnel-4.11/src/client.c 2005-07-02 08:55:58.000000000 +0200 --- stunnel-4.11/src/client.c 2005-08-26 16:01:34.000000000 +0200 *************** *** 44,49 **** --- 44,65 ---- #define SHUT_RDWR 2 #endif
+ #ifdef USE_TPROXY + #define LINUX_NETFILTER + #define LINUX_TPROXY + #define TPROXY_MAJOR_VERSION(x) ((x >> 24) & 0xff) + #define TPROXY_MINOR_VERSION(x) ((x >> 16) & 0xff) + #define TPROXY_PATCH_VERSION(x) (x & 0xffff) + #endif + + /* 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> *************** *** 77,82 **** --- 93,103 ---- static int connect_wait(CLI *, int, int); static void reset(int, char *);
+ #ifdef USE_TPROXY + static int determine_source_address(struct sockaddr_in *destination, + struct sockaddr_in *source); + #endif + int max_clients; #ifndef USE_WIN32 int max_fds; *************** *** 921,926 **** --- 942,954 ---- int s; /* destination socket */ u16 i;
+ // struct in_addr *local=NULL; + #ifdef USE_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,959 **** 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", --- 973,1066 ---- if(alloc_fd(s)) return -1;
! if(c->bind_addr.num ! #ifdef USE_TPROXY ! /* if we're using the tproxy patch, transparent proxy is ! handled below */ ! && !c->opt->option.transparent ! #endif ! ) { ! /* explicit local bind, not tproxy 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"); }
+ #ifdef USE_TPROXY + if (c->opt->option.transparent){ + /* transparent proxy support on 2.[46] linux kernels */ + /* added by MAtteo HCE Valsasna (matteo.valsasna@uninsubria.it), 20050826 */ + /* requires tproxy kernel patch (http://www.balabit.com/products/oss/tproxy */ + /* based on examples in cttproxy patch */ + /* this patch is released under the GNU General Public Licence, Version 2 */ + + 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, "TPROXY_VERSION: %d.%d.%d", + TPROXY_MAJOR_VERSION(itp.v.version), + TPROXY_MINOR_VERSION(itp.v.version), + TPROXY_PATCH_VERSION(itp.v.version)); + + /* conservative: accepting only tproxy version 2.0.x */ + if (TPROXY_MAJOR_VERSION(itp.v.version) != 2 || + TPROXY_MINOR_VERSION(itp.v.version) != 0) + { + s_log(LOG_ERR, "unsupported tproxy version: %x (2.0.x required)", itp.v.version); + return -1; + } + + tproxy_sin.sin_family = AF_INET; + if (determine_source_address(&addr.in, &tproxy_sin) < 0){ + sockerror("determine_source_address"); + return -1; + } + + /* do not request a specific source port */ + tproxy_sin.sin_port = htons(0); + + /* bind to local source address */ + /* needed for tproxy magic */ + 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; + } + } + #endif + /* try to connect for the 1st time */ s_ntop(c->connecting_address, &addr); s_log(LOG_DEBUG, "%s connecting %s", *************** *** 1020,1023 **** --- 1127,1175 ---- log_error(LOG_DEBUG, get_last_socket_error(), txt); }
+ + #ifdef USE_TPROXY + + /* from skaidrus */ + static int determine_source_address(struct sockaddr_in *destination, + struct sockaddr_in *source) + { + struct sockaddr_in addr; + socklen_t addrlen; + char textaddr[32]; + int sock; + + sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock < 0) { + syslog(LOG_CRIT, "determine_source_address: error " + "%d while creating socket", errno); + abort(); + } + + addr = *destination; + addr.sin_port = htons(80); + if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + syslog(LOG_ALERT, "determine_source_address: error %d " + "while looking up route for %d", errno, + inet_aton(textaddr, &(addr.sin_addr))); + close(sock); + return -1; + } + + addrlen = sizeof(*source); + if (getsockname(sock, (struct sockaddr *)source, &addrlen) < 0) { + syslog(LOG_CRIT, "determine_source_address: error %d " + "while retrieving source address", errno); + abort(); + } + source->sin_port = 0; + + close(sock); + + return 0; + } + + #endif + + /* End of client.c */