Go to file
Travis Burtrum 8af039d3eb Add ALPN protocol based probe 2016-01-05 00:32:10 -05:00
scripts Fix daemon start syntax 2015-10-19 13:40:51 -05:00
.gitignore cscope: add cscope tagging support 2013-09-16 22:02:29 +02:00
COPYING added license file 2014-03-30 18:09:16 +02:00
ChangeLog Add ALPN protocol based probe 2016-01-05 00:32:10 -05:00
Makefile Add SNI hostname based probe 2015-07-12 23:10:53 -04:00
README.MacOSX v1.10: 2013-07-10 23:14:15 +02:00
README.md typo: "transparent proying" -> "transparent proxying" 2015-03-15 00:44:39 +01:00
TODO initiated TODO list 2013-10-06 12:09:52 +02:00
basic.cfg Added log_level option to configuration file, which switches off log at each connection 2015-12-15 15:51:18 +01:00
common.c Changed connection log to include the name of the probe that triggered. 2015-12-15 16:06:14 +01:00
common.h libcap support: print out process capabilities at startup if verbose 2014-02-09 21:39:27 +01:00
echosrv.c remove unneeded executable permissions on source files 2013-09-16 21:56:38 +02:00
example.cfg Add ALPN protocol based probe 2016-01-05 00:32:10 -05:00
genver.sh Use portable way of getting modified time 2014-11-22 23:46:50 +09:00
probe.c Add ALPN protocol based probe 2016-01-05 00:32:10 -05:00
probe.h Add ALPN protocol based probe 2016-01-05 00:32:10 -05:00
sslh-fork.c sslh-fork: close all listening sockets in shoveler 2014-01-09 10:16:42 -05:00
sslh-main.c Add ALPN protocol based probe 2016-01-05 00:32:10 -05:00
sslh-select.c Fix typos and type warnings 2013-09-28 20:49:46 +02:00
sslh.pod changed configuration file to accomodate SNI in a cleaner way 2015-07-17 15:04:04 +02:00
t Disable Can't bind to address test since IP_FREEBIND allow us to do that 2014-07-15 16:22:37 +02:00
t_load v1.12: 08MAY2012 2013-07-10 23:15:38 +02:00
tls.c Add ALPN protocol based probe 2016-01-05 00:32:10 -05:00
tls.h Add ALPN protocol based probe 2016-01-05 00:32:10 -05:00


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 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.
    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.

  • 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 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.


  • In general:

      cp sslh-fork /usr/local/sbin/sslh
      cp basic.cfg /etc/sslh.cfg
              vi /etc/sslh.cfg
  • For Debian:

      cp scripts/etc.init.d.sslh /etc/init.d/sslh
  • For CentOS:

      cp scripts/etc.rc.d.init.d.sslh.centos /etc/rc.d/init.d/sslh

You might need to create links in /etc/rc.d so that the server start automatically at boot-up, e.g. under Debian:

update-rc.d sslh defaults


If you use the scripts provided, sslh will get its configuration from /etc/sslh.cfg. Please refer to example.cfg for an overview of all the settings.

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 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 --openvpn 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 --> httpd

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 for specifying the key and certificate
    • -d for specifying 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 and FreeBSD you can use the --transparent option to request transparent proxying. 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 (see appropriate chapter) or run it as root (but don't do that).

The firewalling tables also need to be adjusted as follow. The example connects 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 dev lo table 100

Tranparent proxying with IPv6 is similarly set up as follows:

    # ip6tables -t mangle -N SSLH
    # ip6tables -t mangle -A  OUTPUT --protocol tcp --out-interface eth0 --sport 22 --jump SSLH
    # ip6tables -t mangle -A OUTPUT --protocol tcp --out-interface eth0 --sport 4443 --jump SSLH
    # ip6tables -t mangle -A SSLH --jump MARK --set-mark 0x1
    # ip6tables -t mangle -A SSLH --jump ACCEPT
    # ip -6 rule add fwmark 0x1 lookup 100
    # ip -6 route add local ::/0 dev lo table 100

Note that these rules will prevent from connecting directly to ssh on the port 22, as packets coming out of sshd will be tagged. If you need to retain direct access to ssh on port 22 as well as through sslh, you can make sshd listen to 22 AND another port (e.g. 2222), and change the above rules accordingly.


Given you have no firewall defined yet, you can use the following configuration to have ipfw properly redirect traffic back to sslh



#! /bin/sh

# ssl
ipfw add 20000 fwd,443 log tcp from 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,443 log tcp from 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,443 log tcp from 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,443 log tcp from 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 or localhost), you'll need to use explicit IP addresses (or names):

sslh --listen --ssh --ssl

This will not work:

sslh --listen --ssh --ssl


If using transparent proxying, just use the standard ssh rules. If you can't or don't want to use transparent proxying, you can set fail2ban rules to block repeated ssh connections from a same IP address (obviously this depends on the site, there might be legimite reasons you would get many connections to ssh from the same IP address...)

See example files in scripts/fail2ban.

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.