Open-source project
yrutschle/sslh avatar
yrutschle/sslh

sslh: Route SSH, HTTPS, and OpenVPN Through a Single Port

Applicative Protocol Multiplexer (e.g. share SSH and HTTPS on the same port)

5,117 stars398 forksCGPL-2.0

At a glance

What is it?
sslh is a GPL-licensed protocol multiplexer that listens on a single port and forwards connections to backend services based on the content of the first incoming data packet. It solves the practical problem of sharing port 443 between HTTPS and SSH when a firewall blocks other ports.
Who is it for?
sslh is the right tool when you need to reach SSH or OpenVPN through a firewall that permits only port 443. It is not a load balancer and does not add encryption to protocols that lack it.
Can I use it commercially?
Yes, with conditions. GPL-2.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
Is it still maintained?
Yes. The repository last received commits 11 days ago.
What is it written in?
Mainly C, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 29, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The Problem sslh Solves: One Port, Multiple Protocols

Corporate firewalls and restrictive network policies commonly allow outbound connections only on port 443, the standard HTTPS port. A developer who needs to SSH into a remote server from such a network cannot use port 22, and cannot easily open a new port. sslh addresses this by accepting all connections on port 443 and forwarding them to the correct backend service based on the first bytes of data the client sends.

The README describes this as 'protocol demultiplexing'. The typical use case is sharing port 443 between an HTTPS web server and an SSH daemon. The client does not need any special configuration. An SSH client connecting to port 443 sends the SSH handshake, sslh recognizes it, and forwards the connection to the SSH daemon. A browser connecting to the same port sends a TLS ClientHello, sslh recognizes that, and routes it to the web server.

HAProxy accomplishes something similar for HTTP-based protocols using its own layer-7 routing. The key difference is that HAProxy is a general-purpose load balancer and requires understanding application-layer HTTP headers; sslh is specifically built for this class of port-sharing problem and handles protocols that HAProxy does not inspect at the same level, such as SSH and WireGuard.

Supported Protocols and Detection Methods

sslh probes for: HTTP, TLS/SSL with SNI and ALPN, SSH, WireGuard, OpenVPN, tinc, XMPP, and SOCKS5. The README also states that any protocol testable by a regular expression can be recognized, which means custom protocols are supported through configuration.

The SNI probe enables virtual hosting behind a single IP. When multiple HTTPS services run on different domains behind one IP, sslh can read the SNI field from the TLS ClientHello and route to the correct backend before the TLS handshake completes.

Detection depends on the first data packet from the client. For protocols where the server speaks first (some older protocols do this), detection is not possible without additional configuration. The README does not document this case explicitly.

Installing and Running with Docker

The Docker image at `ghcr.io/yrutschle/sslh:latest` is the most direct path to running sslh. A basic invocation:

bash
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:443

This forwards TLS traffic to port 443 and SSH traffic to port 22 on the named host. The `CAP_NET_RAW` and `CAP_NET_BIND_SERVICE` capabilities are required for binding to a privileged port and for transparent proxying.

A docker-compose example from the README shows how to integrate sslh with nginx and OpenVPN:

yaml
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

This pattern is common: sslh receives everything on 443 and distributes to named compose services.

Transparent Proxying: Making sslh Invisible to Backends

Without transparent proxying, backend servers (your SSH daemon, your web server) see sslh's IP address as the client, not the original client's address. Transparent proxying restores the original client IP, which matters for access control and for IP-based tools like fail2ban.

The README describes two general approaches. The first uses additional virtual network interfaces and is documented in `doc/simple_transparent_proxy.md`. The second uses iptables packet marking and is more dependent on the specific network environment; the README warns that it requires 'extensive knowledge of network management and iptables setup' and recommends the first method in most cases.

A simpler path that achieves the same result is HAProxy's proxyprotocol. When the backend server supports proxyprotocol, you add a single setting to sslh's protocol configuration and a corresponding setting in the backend. The README calls this 'a simple setting to add' and recommends trying it before implementing transparent proxying.

For Docker deployments, transparent proxying takes one of two specific forms. In the first mode, the sslh container handles all networking for the other services. This requires the backend services to be reachable via localhost from the sslh container, and the Compose configuration must set `net.ipv4.conf.default.route_localnet=1` on both the default and all network interfaces as container sysctls. The container also requires NET_ADMIN, NET_RAW, and NET_BIND_SERVICE capabilities. In the second mode, sslh runs with `network_mode: host`, which places the container in the host's network namespace. The sysctl requirement moves from the container definition to the host itself, which the README notes must be set manually in this configuration.

For containerized environments, the README also documents a podman guide at `doc/podman.md`.

Concurrency Models and Security Status

sslh provides three concurrency implementations. The fork-based model (`sslh-fork`) spawns a new process per connection. The select-based model (`sslh-select`) handles multiple connections in a single process using select(). The libev-based model (`sslh-ev`) uses libev for larger installations. The Docker image uses `sslh-select`, which the Dockerfile confirms: it builds only `sslh-select` with `make sslh-select` rather than building all three binaries.

Other production features include privilege and capabilities dropping after startup, inetd support, systemd support, chroot, logging, and full IPv4 and IPv6 support for both TCP and UDP.

The Dockerfile reveals the build dependencies on Alpine Linux: gcc, libconfig-dev, make, musl-dev, pcre2-dev, perl, git, autoconf, automake, and libtool. The multi-stage build compiles `libproxyprotocol` from source before building sslh, then copies only the binary and shared libraries into the runtime image. The final image includes iptables and ip6tables for the transparent proxying use case.

The security posture deserves explicit attention. In June 2025, Matthias Gerstner from OpenSUSE published a code review of sslh that found two CVEs. The README links to the full review at security.opensuse.org and states that 'the more critical ones' have been addressed. The review is described as 'well worth reading if you are using sslh in production'. Configuring connection limits, documented in `doc/max_connections.md`, is a required hardening step for production deployments.

The project is licensed under GPL-2.0, which requires that modifications be released under the same terms if distributed.

Editorial conclusion

sslh is the right tool when you need to reach SSH or OpenVPN through a firewall that permits only port 443. It is not a load balancer and does not add encryption to protocols that lack it. Before deploying, read the OpenSUSE security review published in June 2025, which identified two CVEs, and configure connection limits as described in doc/max_connections.md. The last push to the repository was on 2026-09-19.

Frequently asked questions

How does sslh detect which protocol a connection is using?

sslh reads the first data packet sent by the client and tests it against probes for HTTP, TLS/SSL, SSH, WireGuard, OpenVPN, tinc, XMPP, SOCKS5, and custom regular expressions. The detection happens before any bytes are forwarded to the backend.

Does sslh work on Ubuntu?

The README does not document a dedicated Ubuntu package, but it references Debian packages that are available for Ubuntu as well. Building from source follows the standard configure/make process documented in doc/INSTALL.md. The Docker image at ghcr.io/yrutschle/sslh:latest is an alternative.

Does sslh have known security vulnerabilities?

The README references a 2025 OpenSUSE code review that found two CVEs. The critical issues have been partly addressed. The README recommends reading the full review at security.opensuse.org before running sslh in production, and configuring connection limits as described in doc/max_connections.md.

Official sources

  1. Issues
  2. License: GPL-2.0
  3. Project website
  4. README
  5. yrutschle/sslh on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/yrutschle-sslh.svg)](https://hysenlabs.com/projects/yrutschle-sslh)