diff --git a/Makefile b/Makefile index e06db53..800af29 100644 --- a/Makefile +++ b/Makefile @@ -3,6 +3,7 @@ VERSION=$(shell ./genver.sh -r) USELIBCONFIG=1 # Use libconfig? (necessary to use configuration files) USELIBWRAP= # Use libwrap? +USELIBCAP= # Use libcap? COV_TEST= # Perform test coverage? PREFIX=/usr/local @@ -31,6 +32,11 @@ ifneq ($(strip $(USELIBCONFIG)),) CPPFLAGS+=-DLIBCONFIG endif +ifneq ($(strip $(USELIBCAP)),) + LIBS:=$(LIBS) -lcap + CPPFLAGS+=-DLIBCAP +endif + all: sslh $(MAN) echosrv .c.o: *.h diff --git a/README b/README new file mode 100644 index 0000000..cb2f588 --- /dev/null +++ b/README @@ -0,0 +1,229 @@ +===== sslh -- A ssl/ssh multiplexer. ===== + +Sslh accepts connections on specified ports, and forwards +them further based on tests performed on the first data +packet sent by the remote client. + +Probes for HTTP, SSL, SSH, OpenVPN, tinc, XMPP are +implemented, and any other protocol that can be tested using +a regular expression, can be recognised. A typical use case +is to allow serving several services on port 443 (e.g. to +connect to ssh from inside a corporate firewall, which +almost never block port 443) while still serving HTTPS on +that port. + +Hence sslh acts as a protocol demultiplexer, or a +switchboard. Its name comes from its original function to +serve SSH and HTTPS on the same port. + +==== Compile and install ==== + +sslh uses libconfig (http://www.hyperrealm.com/libconfig/) +and libwrap. + +For Debian, these are contained in packages libwrap0-dev and +libconfig8-dev. + +For OpenSUSE, these are contained in packages libconfig9 and +libconfig-dev in repository +http://download.opensuse.org/repositories/multimedia:/libs/openSUSE_12.1/ + +For Fedora, you'll need packages libconfig and +libconfig-devel: +yum install libconfig libconfig-devel + +If you can't find libconfig, or just don't want a +configuration file, set 'USELIBCONFIG=' in the Makefile. + +After this, the Makefile should work: + +make install + +There are a couple of configuration options at the beginning +of the Makefile: + + USELIBWRAP compiles support for host access control (see + hosts_access(3)), you will need libwrap headers and + library to compile (libwrap0-dev in Debian). + + USELIBCONFIG compiles support for the configuration + file. You will need libconfig headers to compile + (libconfig8-dev in Debian). + +The Makefile produces two different executables: sslh-fork +and sslh-select. + +sslh-fork forks a new process for each incoming connection. +It is well-tested and very reliable, but incurs the overhead +of many processes. sslh-select uses only one thread, which +monitors all connections at once. It is more recent and less +tested, but only incurs a 16 byte overhead per connection. +Also, if it stops, you'll lose all connections, which means +you can't upgrade it remotely. + +If you are going to use sslh for a "small" setup (less than +a dozen ssh connections and a low-traffic https server) then +sslh-fork is probably more suited for you. If you are going +to use sslh on a "medium" setup (a few thousand ssh +connections, and another few thousand ssl connections), +sslh-select will be better. If you have a very large site +(tens of thousands of connections), you'll need a vapourware +version that would use libevent or something like that. + + +To install: + +make +cp sslh-fork /usr/local/sbin/sslh +cp scripts/etc.default.sslh /etc/default/sslh + +For Debian: +cp scripts/etc.init.d.sslh /etc/init.d/sslh +For CentOS: +cp scripts/etc.rc.d.init.d.sslh /etc/rc.d/init.d/sslh + +and probably create links in /etc/rc.d so that the server +start automatically at boot-up, e.g. under Debian: +update-rc.d sslh defaults + + + +==== Configuration ==== + +You can edit settings in /etc/default/sslh: + +LISTEN=ifname:443 +SSH=localhost:22 +SSL=localhost:443 + +A good scheme is to use the external name of the machine in +$LISTEN, and bind httpd to localhost:443 (instead of all +binding to all interfaces): that way, https connections +coming from inside your network don't need to go through +sslh, and sslh is only there as a frontal for connections +coming from the internet. + +Note that 'external name' in this context refers to the +actual IP address of the machine as seen from your network, +i.e. that that is not 127.0.0.1 in the output of +ifconfig(8). + +==== Libwrap support ==== + +Sslh can optionnaly perform libwrap checks for the sshd +service: because the connection to sshd will be coming +locally from sslh, sshd cannot determine the IP of the +client. + +==== OpenVPN support ==== + +OpenVPN clients connecting to OpenVPN running with +-port-share reportedly take more than one second between +the time the TCP connexion is established and the time they +send the first data packet. This results in sslh with +default settings timing out and assuming an SSH connexion. +To support OpenVPN connexions reliably, it is necessary to +increase sslh's timeout to 5 seconds. + +Instead of using OpenVPN's port sharing, it is more reliable +to use sslh's -o option to get sslh to do the port sharing. + +==== Using proxytunnel with sslh ==== + +If you are connecting through a proxy that checks that the +outgoing connection really is SSL and rejects SSH, you can +encapsulate all your traffic in SSL using proxytunnel (this +should work with corkscrew as well). On the server side you +receive the traffic with stunnel to decapsulate SSL, then +pipe through sslh to switch HTTP on one side and SSL on the +other. + +In that case, you end up with something like this: + +ssh -> proxytunnel -e --------ssh/ssl------> stunnel ---ssh---> sslh --> sshd + +Web browser --------http/ssl------> stunnel ---http---> sslh --> http:80 + +Configuration goes like this: + +On the server side, using stunnel3: +stunnel -f -p mycert.pem -d thelonious:443 -l /usr/local/sbin/sslh -- sslh -i --http localhost:80 --ssh localhost:22 + +stunnel options: -f for foreground/debugging, -p specifies +the key + certificate, -d specifies which interface and port +we're listening to for incoming connexions, -l summons sslh +in inetd mode. + +sslh options: -i for inetd mode, --http to forward http +connexions to port 80, and SSH connexions to port 22. + +==== capabilities support ==== + +On Linux (only?), you can compile sslh with USELIBCAP=1 to +make use of POSIX capabilities; this will save the required +capabilities needed for transparent proxying for unprivileged +processes. + +Alternatively, you may use filesystem capabilities instead +of starting sslh as root and asking it to drop privileges. +You will need CAP_NET_BIND_SERVICE for listening on port 443 +and CAP_NET_ADMIN for transparent proxying (see +capabilities(7)). + +You can use the setcap(8) utility to give these capabilities +to the executable: + +# setcap cap_net_bind_service,cap_net_admin+pe sslh-select + +Then you can run sslh-select as an unpriviledged user, e.g.: + +$ sslh-select -p myname:443 --ssh localhost:22 --ssl localhost:443 + +Caveat: CAP_NET_ADMIN does give sslh too many rights, e.g. +configuring the interface. If you're not going to use +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 +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). + +sslh needs extended rights to perform this: you'll need to +give it cap_net_admin capabilities manually or enable libcap +support (see appropriate chapter). + +The firewalling tables also need to be adjusted as follow +(example to connect to https on 4443 -- adapt to your needs +(I don't think it is possible to have httpd listen to 443 in +this scheme -- let me know if you manage that))): + +# iptables -t mangle -N SSLH +# iptables -t mangle -A OUTPUT --protocol tcp --out-interface eth0 --sport 22 --jump SSLH +# iptables -t mangle -A OUTPUT --protocol tcp --out-interface eth0 --sport 4443 --jump SSLH +# iptables -t mangle -A SSLH --jump MARK --set-mark 0x1 +# iptables -t mangle -A SSLH --jump ACCEPT +# ip rule add fwmark 0x1 lookup 100 +# ip route add local 0.0.0.0/0 dev lo table 100 + +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): + +sslh --listen 192.168.0.1:443 --ssh 192.168.0.1:22 --ssl 192.168.0.1:4443 + +This will not work: +sslh --listen 192.168.0.1:443 --ssh 127.0.0.1:22 --ssl 127.0.0.1:4443 + +==== Comments? Questions? ==== + +You can subscribe to the sslh mailing list here: +http://rutschle.net/cgi-bin/mailman/listinfo/sslh + +This mailing list should be used for discussion, feature +requests, and will be the prefered channel for +announcements. + diff --git a/README.md b/README.md index d0d8963..938c2f1 100644 --- a/README.md +++ b/README.md @@ -189,28 +189,29 @@ Configuration goes like this on the server side, using `stunnel3`: Capabilities support -------------------- -On Linux (only?), you can use POSIX capabilities to reduce a -server's capabilities to the minimum it needs (see -`capabilities(8)`. For sslh, this is `CAP_NET_ADMIN` (to -perform transparent proxy-ing) and `CAP_NET_BIND_SERVICE` (to -bind to port 443 without being root). +On Linux (only?), you can compile sslh with USELIBCAP=1 to +make use of POSIX capabilities; this will save the required +capabilities needed for transparent proxying for unprivileged +processes. -The simplest way to use capabilities is to give them to the -executable as root: +Alternatively, you may use filesystem capabilities instead +of starting sslh as root and asking it to drop privileges. +You will need CAP_NET_BIND_SERVICE for listening on port 443 +and CAP_NET_ADMIN for transparent proxying (see +capabilities(7)). - # setcap cap_net_bind_service,cap_net_admin+pe sslh-select +You can use the setcap(8) utility to give these capabilities +to the executable: -Then you can run `sslh-select` as an unpriviledged user, e.g.: +# setcap cap_net_bind_service,cap_net_admin+pe sslh-select - $ sslh-select -p myname:443 --ssh localhost:22 --ssl localhost:443 +Then you can run sslh-select as an unpriviledged user, e.g.: -This has 2 advantages over starting as root with `-u`: -- You no longer start as root (duh) -- This enables transparent proxying. +$ sslh-select -p myname:443 --ssh localhost:22 --ssl localhost:443 -Caveat: `CAP_NET_ADMIN` does give `sslh` too many rights, e.g. +Caveat: CAP_NET_ADMIN does give sslh too many rights, e.g. configuring the interface. If you're not going to use -transparent proxying, just don't use it. +transparent proxying, just don't use it (or use the libcap method). Transparent proxy support ------------------------- diff --git a/common.c b/common.c index 7703189..fefc33e 100644 --- a/common.c +++ b/common.c @@ -37,6 +37,10 @@ struct addrinfo *addr_listen = NULL; /* what addresses do we listen to? */ int allow_severity =0, deny_severity = 0; #endif +#ifdef LIBCAP +#include +#include +#endif /* check result and die, printing the offending address and error */ void check_res_dumpdie(int res, struct addrinfo *addr, char* syscall) @@ -514,6 +518,56 @@ void setup_syslog(const char* bin_name) { log_message(LOG_INFO, "%s %s started\n", server_type, VERSION); } +/* Ask OS to keep capabilities over a setuid(nonzero) */ +void set_keepcaps(int val) { +#ifdef LIBCAP + int res; + res = prctl(PR_SET_KEEPCAPS, val, 0, 0, 0); + if (res) { + perror("prctl"); + exit(1); + } +#endif +} + +/* set needed capabilities for effective and permitted, clear rest */ +void set_capabilities(void) { +#ifdef LIBCAP + int res; + cap_t caps; + cap_value_t cap_list[10]; + int ncap = 0; + + if (transparent) + cap_list[ncap++] = CAP_NET_ADMIN; + + caps = cap_init(); + +#define _cap_set_flag(flag) do { \ + res = cap_clear_flag(caps, flag); \ + CHECK_RES_DIE(res, "cap_clear_flag(" #flag ")"); \ + if (ncap > 0) { \ + res = cap_set_flag(caps, flag, ncap, cap_list, CAP_SET); \ + CHECK_RES_DIE(res, "cap_set_flag(" #flag ")"); \ + } \ + } while(0) + + _cap_set_flag(CAP_EFFECTIVE); + _cap_set_flag(CAP_PERMITTED); + +#undef _cap_set_flag + + res = cap_set_proc(caps); + CHECK_RES_DIE(res, "cap_set_proc"); + + res = cap_free(caps); + if (res) { + perror("cap_free"); + exit(1); + } +#endif +} + /* We don't want to run as root -- drop privileges if required */ void drop_privileges(const char* user_name) { @@ -526,10 +580,15 @@ void drop_privileges(const char* user_name) if (verbose) fprintf(stderr, "turning into %s\n", user_name); + set_keepcaps(1); + res = setgid(pw->pw_gid); CHECK_RES_DIE(res, "setgid"); res = setuid(pw->pw_uid); CHECK_RES_DIE(res, "setuid"); + + set_capabilities(); + set_keepcaps(0); } /* Writes my PID */