yrutschle/sslh
{ "createdAt": "2013-07-10T21:25:31Z", "defaultBranch": "master", "description": "Applicative Protocol Multiplexer (e.g. share SSH and HTTPS on the same port)", "fullName": "yrutschle/sslh", "homepage": "https://www.rutschle.net/tech/sslh/README.html", "language": "C", "name": "sslh", "pushedAt": "2025-11-14T13:54:51Z", "stargazersCount": 4930, "topics": [], "updatedAt": "2025-11-25T17:48:14Z", "url": "https://github.com/yrutschle/sslh"}sslh — A ssl/ssh multiplexer
Section titled “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, TLS/SSL (including SNI and ALPN), SSH, OpenVPN, tinc, XMPP, SOCKS5, 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. With the SNI and ALPN probe, it makes a good
front-end to a virtual host farm hosted behind a single IP
address.
sslh has the bells and whistles expected from a mature
daemon: privilege and capabilities dropping, inetd support,
systemd support, transparent proxying, support for HAProxy’s
proxyprotocol, chroot, logging, IPv4 and IPv6, TCP and UDP,
a fork-based, a select-based model, and yet another based on
libev for larger installations.
Install
Section titled “Install”Please refer to the [install guide]!(doc/INSTALL.md).
Security
Section titled “Security”Matthias Gerstner from OpenSUSE has performed a code review
of sslh from a security point of view, which revealed a
number of problems, including two CVE. His findings have
already been taken partly into account for the more critical
ones. The full
review
is well worth reading if you are using sslh in production.
Part of the securing your installation involves configuring connection limits. This is described in [this guide]!(doc/max_connections.md).
Configuration
Section titled “Configuration”Please refer to the [configuration guide]!(doc/config.md).
Transparent proxying
Section titled “Transparent proxying”Transparent proxying allows the target server to see the
original client IP address, i.e. sslh becomes invisible.
The same result can be achieved more easily by using
proxyprotocol if the backend server supports it. This is a
simple setting to add to the sslh protocol configuration,
usually with an equivalently simple setting to add in
the backend server configuration, so try that first. This is
explained in a [separate document]!(doc/proxyprotocol.md).
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), and makes it possible
to use IP-based banning tools such as fail2ban.
There are two methods. One uses additional virtual network interfaces. The principle and basic setup is described [here]!(doc/simple_transparent_proxy.md), with further scenarios described [there]!(doc/scenarios-for-simple-transparent-proxy.md).
There is also a guide to use [podman]!(doc/podman.md).
Another method uses iptable packet marking features, and is highly dependent on your network environment and infrastructure setup. There is no known generic approach, and if you do not find directions for your exact setup, you will probably need an extensive knowledge of network management and iptables setup”.
It is described in its own [document]!(doc/tproxy.md). In most cases, you will be better off following the first method.
Docker image
Section titled “Docker image”How to use
docker run \ --cap-add CAP_NET_RAW \ --cap-add CAP_NET_BIND_SERVICE \ --rm \ -it \ ghcr.io/yrutschle/sslh:latest \ --foreground \ --listen=0.0.0.0:443 \ --ssh=hostname:22 \ --tls=hostname:443docker-compose example
version: "3"
services: sslh: image: ghcr.io/yrutschle/sslh:latest hostname: sslh ports: - 443:443 command: --foreground --listen=0.0.0.0:443 --tls=nginx:443 --openvpn=openvpn:1194 depends_on: - nginx - openvpn
nginx: image: nginx
openvpn: image: openvpnTransparent mode 1: using sslh container for networking
Note: For transparent mode to work, the sslh container must be able to reach your services via localhost
version: "3"
services: sslh: build: https://github.com/yrutschle/sslh.git container_name: sslh environment: - TZ=${TZ} cap_add: - NET_ADMIN - NET_RAW - NET_BIND_SERVICE sysctls: - net.ipv4.conf.default.route_localnet=1 - net.ipv4.conf.all.route_localnet=1 command: --transparent --foreground --listen=0.0.0.0:443 --tls=localhost:8443 --openvpn=localhost:1194 ports: - 443:443 #sslh
- 80:80 #nginx - 8443:8443 #nginx
- 1194:1194 #openvpn extra_hosts: - localbox:host-gateway restart: unless-stopped
nginx: image: nginx:latest ..... network_mode: service:sslh #set nginx container to use sslh networking. # ^^^ This is required. This makes nginx reachable by sslh via localhost
openvpn: image: openvpn:latest ..... network_mode: service:sslh #set openvpn container to use sslh networkingTransparent mode 2: using host networking
version: "3"
services: sslh: build: https://github.com/yrutschle/sslh.git container_name: sslh environment: - TZ=${TZ} cap_add: - NET_ADMIN - NET_RAW - NET_BIND_SERVICE # must be set manually #sysctls: # - net.ipv4.conf.default.route_localnet=1 # - net.ipv4.conf.all.route_localnet=1 command: --transparent --foreground --listen=0.0.0.0:443 --tls=localhost:8443 --openvpn=localhost:1194 network_mode: host restart: unless-stopped
nginx: image: nginx:latest ..... ports: - 8443:8443 # bind to docker host on port 8443
openvpn: image: openvpn:latest ..... ports: - 1194:1194 # bind to docker host on port 1194Comments? Questions?
Section titled “Comments? Questions?”You can subscribe to the sslh mailing list here:
https://lists.rutschle.net/mailman/listinfo/sslh
This mailing list should be used for discussion, feature requests, and will be the preferred channel for announcements.
Of course, check the [FAQ]!(doc/FAQ.md) first!