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 diff --git a/README.md b/README.md index 2b7f44d..9ea4216 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,41 @@ 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 + +General notes: + + 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): diff --git a/common.c b/common.c index f35f283..100753a 100644 --- a/common.c +++ b/common.c @@ -120,8 +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"); +#ifdef IPV6_BINDANY + } else { /* IPv6 */ + res = setsockopt(fd, IPPROTO_IPV6, IPV6_BINDANY, &trans, sizeof(trans)); + 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"); @@ -134,15 +146,28 @@ 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), 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",