From b6de2904f05cfb3334f36bee61052441c5809874 Mon Sep 17 00:00:00 2001 From: Ruben van Staveren Date: Fri, 20 Jun 2014 14:11:25 +0200 Subject: [PATCH 1/9] FreeBSD way of doing transparent proxy: work in progress --- common.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/common.c b/common.c index a297176..7af9ee1 100644 --- a/common.c +++ b/common.c @@ -120,8 +120,13 @@ int bind_peer(int fd, int fd_from) * got here */ res = getpeername(fd_from, from.ai_addr, &from.ai_addrlen); CHECK_RES_RETURN(res, "getpeername"); - res = setsockopt(fd, IPPROTO_IP, IP_TRANSPARENT, &trans, sizeof(trans)); - CHECK_RES_DIE(res, "setsockopt"); + if (from.ai_addr->sa_family==AF_INET) { /* IPv4 */ + res = setsockopt(fd, IPPROTO_IP, IP_BINDANY, &trans, sizeof(trans)); + CHECK_RES_RETURN(res, "setsockopt IP_BINDANY"); + } else { /* IPv6 */ + res = setsockopt(fd, IPPROTO_IPV6, IPV6_BINDANY, &trans, sizeof(trans)); + CHECK_RES_RETURN(res, "setsockopt IPV6_BINDANY"); + } res = bind(fd, from.ai_addr, from.ai_addrlen); CHECK_RES_RETURN(res, "bind"); @@ -143,6 +148,8 @@ int connect_addr(struct connection *cnx, int fd_from) fprintf(stderr, "connecting to %s family %d len %d\n", sprintaddr(buf, sizeof(buf), a), a->ai_addr->sa_family, a->ai_addrlen); + + /* XXX Needs to match ai_family from fd_from when being transparent! */ fd = socket(a->ai_family, SOCK_STREAM, 0); if (fd == -1) { log_message(LOG_ERR, "forward to %s failed:socket: %s\n", From dedb3672d785210ae80b44eab36ea65dd46461e8 Mon Sep 17 00:00:00 2001 From: Ruben van Staveren Date: Tue, 22 Jul 2014 19:36:29 +0200 Subject: [PATCH 2/9] Have USELIBWRAP redefineable --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 800af29..2e9de22 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ VERSION=$(shell ./genver.sh -r) USELIBCONFIG=1 # Use libconfig? (necessary to use configuration files) -USELIBWRAP= # Use libwrap? +USELIBWRAP?= # Use libwrap? USELIBCAP= # Use libcap? COV_TEST= # Perform test coverage? PREFIX=/usr/local From 7d23a5523670075f6077d94efc4e1a1d9a8ebedc Mon Sep 17 00:00:00 2001 From: Ruben van Staveren Date: Tue, 22 Jul 2014 19:36:40 +0200 Subject: [PATCH 3/9] When transparent, make sure both connections use the same address family --- common.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/common.c b/common.c index 7af9ee1..139481e 100644 --- a/common.c +++ b/common.c @@ -139,11 +139,22 @@ int bind_peer(int fd, int fd_from) * of new file descriptor. */ int connect_addr(struct connection *cnx, int fd_from) { - struct addrinfo *a; + struct addrinfo *a, from; + struct sockaddr_storage ss; char buf[NI_MAXHOST]; int fd, res; + memset(&from, 0, sizeof(from)); + from.ai_addr = (struct sockaddr*)&ss; + from.ai_addrlen = sizeof(ss); + + res = getpeername(fd_from, from.ai_addr, &from.ai_addrlen); + CHECK_RES_RETURN(res, "getpeername"); + for (a = cnx->proto->saddr; a; a = a->ai_next) { + /* When transparent, make sure both connections use the same address family */ + if (transparent && a->ai_family != from.ai_addr->sa_family) + continue; if (verbose) fprintf(stderr, "connecting to %s family %d len %d\n", sprintaddr(buf, sizeof(buf), a), From e246536be2a704b130902d956c8b55544552ec3c Mon Sep 17 00:00:00 2001 From: Ruben van Staveren Date: Fri, 20 Jun 2014 14:11:25 +0200 Subject: [PATCH 4/9] FreeBSD way of doing transparent proxy: work in progress --- common.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/common.c b/common.c index a297176..7af9ee1 100644 --- a/common.c +++ b/common.c @@ -120,8 +120,13 @@ int bind_peer(int fd, int fd_from) * got here */ res = getpeername(fd_from, from.ai_addr, &from.ai_addrlen); CHECK_RES_RETURN(res, "getpeername"); - res = setsockopt(fd, IPPROTO_IP, IP_TRANSPARENT, &trans, sizeof(trans)); - CHECK_RES_DIE(res, "setsockopt"); + if (from.ai_addr->sa_family==AF_INET) { /* IPv4 */ + res = setsockopt(fd, IPPROTO_IP, IP_BINDANY, &trans, sizeof(trans)); + CHECK_RES_RETURN(res, "setsockopt IP_BINDANY"); + } else { /* IPv6 */ + res = setsockopt(fd, IPPROTO_IPV6, IPV6_BINDANY, &trans, sizeof(trans)); + CHECK_RES_RETURN(res, "setsockopt IPV6_BINDANY"); + } res = bind(fd, from.ai_addr, from.ai_addrlen); CHECK_RES_RETURN(res, "bind"); @@ -143,6 +148,8 @@ int connect_addr(struct connection *cnx, int fd_from) fprintf(stderr, "connecting to %s family %d len %d\n", sprintaddr(buf, sizeof(buf), a), a->ai_addr->sa_family, a->ai_addrlen); + + /* XXX Needs to match ai_family from fd_from when being transparent! */ fd = socket(a->ai_family, SOCK_STREAM, 0); if (fd == -1) { log_message(LOG_ERR, "forward to %s failed:socket: %s\n", From 42425a837368d964c0856e4b7cede057870451f8 Mon Sep 17 00:00:00 2001 From: Ruben van Staveren Date: Tue, 22 Jul 2014 19:36:29 +0200 Subject: [PATCH 5/9] Have USELIBWRAP redefineable --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 800af29..2e9de22 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ VERSION=$(shell ./genver.sh -r) USELIBCONFIG=1 # Use libconfig? (necessary to use configuration files) -USELIBWRAP= # Use libwrap? +USELIBWRAP?= # Use libwrap? USELIBCAP= # Use libcap? COV_TEST= # Perform test coverage? PREFIX=/usr/local From e2fc09148251bfbe0705380d9125a46beaa118e4 Mon Sep 17 00:00:00 2001 From: Ruben van Staveren Date: Tue, 22 Jul 2014 19:36:40 +0200 Subject: [PATCH 6/9] When transparent, make sure both connections use the same address family --- common.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/common.c b/common.c index 7af9ee1..139481e 100644 --- a/common.c +++ b/common.c @@ -139,11 +139,22 @@ int bind_peer(int fd, int fd_from) * of new file descriptor. */ int connect_addr(struct connection *cnx, int fd_from) { - struct addrinfo *a; + struct addrinfo *a, from; + struct sockaddr_storage ss; char buf[NI_MAXHOST]; int fd, res; + memset(&from, 0, sizeof(from)); + from.ai_addr = (struct sockaddr*)&ss; + from.ai_addrlen = sizeof(ss); + + res = getpeername(fd_from, from.ai_addr, &from.ai_addrlen); + CHECK_RES_RETURN(res, "getpeername"); + for (a = cnx->proto->saddr; a; a = a->ai_next) { + /* When transparent, make sure both connections use the same address family */ + if (transparent && a->ai_family != from.ai_addr->sa_family) + continue; if (verbose) fprintf(stderr, "connecting to %s family %d len %d\n", sprintaddr(buf, sizeof(buf), a), From 36cf99697b40702b760b2c8c7995212bc4f5caa4 Mon Sep 17 00:00:00 2001 From: Ruben van Staveren Date: Tue, 22 Jul 2014 20:30:52 +0200 Subject: [PATCH 7/9] Add instruction for FreeBSD --- README.md | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2b7f44d..37f3220 100644 --- a/README.md +++ b/README.md @@ -217,13 +217,15 @@ transparent proxying, just don't use it (or use the libcap method). Transparent proxy support ------------------------- -On Linux (only?) you can use the `--transparent` option to +On Linux and FreeBSD you can use the `--transparent` option to request transparent proying. This means services behind `sslh` (Apache, `sshd` and so on) will see the external IP and ports as if the external world connected directly to them. This simplifies IP-based access control (or makes it possible at all). +Linux: + `sslh` needs extended rights to perform this: you'll need to give it `CAP_NET_ADMIN` capabilities (see appropriate chapter) or run it as root (but don't do that). @@ -241,6 +243,42 @@ this scheme -- let me know if you manage that: # ip rule add fwmark 0x1 lookup 100 # ip route add local 0.0.0.0/0 dev lo table 100 +FreeBSD: + +Given you have no firewall defined yet, you can use the following configuration +to have ipfw properly redirect traffic back to sslh + +/etc/rc.conf +firewall_enable="YES" +firewall_type="open" +firewall_logif="YES" +firewall_coscripts="/etc/ipfw/sslh.rules" + + +/etc/ipfw/sslh.rules +#! /bin/sh + +# ssl +ipfw add 20000 fwd 192.0.2.1,443 log tcp from 192.0.2.1 8443 to any out +ipfw add 20010 fwd 2001:db8::1,443 log tcp from 2001:db8::1 8443 to any out + +# ssh +ipfw add 20100 fwd 192.0.2.1,443 log tcp from 192.0.2.1 8022 to any out +ipfw add 20110 fwd 2001:db8::1,443 log tcp from 2001:db8::1 8022 to any out + +# xmpp +ipfw add 20200 fwd 192.0.2.1,443 log tcp from 192.0.2.1 5222 to any out +ipfw add 20210 fwd 2001:db8::1,443 log tcp from 2001:db8::1 5222 to any out + +# openvpn (running on other internal system) +ipfw add 20300 fwd 192.0.2.1,443 log tcp from 198.51.100.7 1194 to any out +ipfw add 20310 fwd 2001:db8::1,443 log tcp from 2001:db8:1::7 1194 to any out + + + + + + This will only work if `sslh` does not use any loopback addresses (no `127.0.0.1` or `localhost`), you'll need to use explicit IP addresses (or names): From 0d8e2438de261523952af08263f4c0c2a763a4b9 Mon Sep 17 00:00:00 2001 From: Ruben van Staveren Date: Tue, 22 Jul 2014 21:43:03 +0200 Subject: [PATCH 8/9] Correct markdown --- README.md | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 37f3220..9ea4216 100644 --- a/README.md +++ b/README.md @@ -248,35 +248,34 @@ FreeBSD: Given you have no firewall defined yet, you can use the following configuration to have ipfw properly redirect traffic back to sslh -/etc/rc.conf -firewall_enable="YES" -firewall_type="open" -firewall_logif="YES" -firewall_coscripts="/etc/ipfw/sslh.rules" + /etc/rc.conf + firewall_enable="YES" + firewall_type="open" + firewall_logif="YES" + firewall_coscripts="/etc/ipfw/sslh.rules" /etc/ipfw/sslh.rules -#! /bin/sh -# ssl -ipfw add 20000 fwd 192.0.2.1,443 log tcp from 192.0.2.1 8443 to any out -ipfw add 20010 fwd 2001:db8::1,443 log tcp from 2001:db8::1 8443 to any out + #! /bin/sh -# ssh -ipfw add 20100 fwd 192.0.2.1,443 log tcp from 192.0.2.1 8022 to any out -ipfw add 20110 fwd 2001:db8::1,443 log tcp from 2001:db8::1 8022 to any out - -# xmpp -ipfw add 20200 fwd 192.0.2.1,443 log tcp from 192.0.2.1 5222 to any out -ipfw add 20210 fwd 2001:db8::1,443 log tcp from 2001:db8::1 5222 to any out - -# openvpn (running on other internal system) -ipfw add 20300 fwd 192.0.2.1,443 log tcp from 198.51.100.7 1194 to any out -ipfw add 20310 fwd 2001:db8::1,443 log tcp from 2001:db8:1::7 1194 to any out + # ssl + ipfw add 20000 fwd 192.0.2.1,443 log tcp from 192.0.2.1 8443 to any out + ipfw add 20010 fwd 2001:db8::1,443 log tcp from 2001:db8::1 8443 to any out + # ssh + ipfw add 20100 fwd 192.0.2.1,443 log tcp from 192.0.2.1 8022 to any out + ipfw add 20110 fwd 2001:db8::1,443 log tcp from 2001:db8::1 8022 to any out + # xmpp + ipfw add 20200 fwd 192.0.2.1,443 log tcp from 192.0.2.1 5222 to any out + ipfw add 20210 fwd 2001:db8::1,443 log tcp from 2001:db8::1 5222 to any out + # openvpn (running on other internal system) + ipfw add 20300 fwd 192.0.2.1,443 log tcp from 198.51.100.7 1194 to any out + ipfw add 20310 fwd 2001:db8::1,443 log tcp from 2001:db8:1::7 1194 to any out +General notes: This will only work if `sslh` does not use any loopback From ece6e28e453e8361b583570d95594fefb40f7647 Mon Sep 17 00:00:00 2001 From: Ruben van Staveren Date: Thu, 24 Jul 2014 17:29:53 +0200 Subject: [PATCH 9/9] #ifdef IP_BINDANY/IPV6_BINDANY cases --- common.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/common.c b/common.c index 139481e..43285bf 100644 --- a/common.c +++ b/common.c @@ -120,13 +120,20 @@ int bind_peer(int fd, int fd_from) * got here */ res = getpeername(fd_from, from.ai_addr, &from.ai_addrlen); CHECK_RES_RETURN(res, "getpeername"); +#ifndef IP_BINDANY /* use IP_TRANSPARENT */ + res = setsockopt(fd, IPPROTO_IP, IP_TRANSPARENT, &trans, sizeof(trans)); + CHECK_RES_DIE(res, "setsockopt"); +#else if (from.ai_addr->sa_family==AF_INET) { /* IPv4 */ res = setsockopt(fd, IPPROTO_IP, IP_BINDANY, &trans, sizeof(trans)); - CHECK_RES_RETURN(res, "setsockopt IP_BINDANY"); + CHECK_RES_RETURN(res, "setsockopt IP_BINDANY"); +#ifdef IPV6_BINDANY } else { /* IPv6 */ res = setsockopt(fd, IPPROTO_IPV6, IPV6_BINDANY, &trans, sizeof(trans)); - CHECK_RES_RETURN(res, "setsockopt IPV6_BINDANY"); + CHECK_RES_RETURN(res, "setsockopt IPV6_BINDANY"); +#endif /* IPV6_BINDANY */ } +#endif /* IP_TRANSPARENT / IP_BINDANY */ res = bind(fd, from.ai_addr, from.ai_addrlen); CHECK_RES_RETURN(res, "bind"); @@ -160,7 +167,7 @@ int connect_addr(struct connection *cnx, int fd_from) sprintaddr(buf, sizeof(buf), a), a->ai_addr->sa_family, a->ai_addrlen); - /* XXX Needs to match ai_family from fd_from when being transparent! */ + /* XXX Needs to match ai_family from fd_from when being transparent! */ fd = socket(a->ai_family, SOCK_STREAM, 0); if (fd == -1) { log_message(LOG_ERR, "forward to %s failed:socket: %s\n",