guest@6admin.io:~$

SSLH: ssh and https on the same port

The 2007 problem, in companies with nervous firewalls: one outgoing port only, 443, everything else cut. You want your ssh shell and your https and the firewall does not negotiate. The answer is sslh (v1.0, by Yves Rutschle): a small daemon that listens on 443 and delegates based on the first packet: to sshd if the client speaks the SSH protocol, to httpsd otherwise.

The mechanism

client --443--> sslh --detection--> sshd:22
                              \--> https:443

The SSH protocol announces itself politely: the client sends a SSH-2.0-... banner in clear text upon opening. SSL/TLS starts with a ClientHello whose first byte is not an ‘S’. Two bells, two destinations:

# sslh.cfg, the era's spirit
listen: { host: "0.0.0.0", port: "443" }
protocols:
  - { name: ssh,   service: "localhost:22" }
  - { name: ssl,   service: "localhost:444" }

It starts as root to bind 443, then setuid nobody (as good daemons do). The ssh ping goes through, https goes through, the firewall sees one well-behaved 443 flow.

What it allowed

In practice: ssh from cafés and office buildings cut off from the world, admin https on the same port and one portknocking less (see the November article coming in the series). Corner café, throttled network, one port open and yet my shell went through: port 443 turned chatty and I went smiling. For the roaming sysadmin of 2007, it was freedom and it fit in a few hundred lines of C.

[edit: 2026] Why nobody would put it in production today

Retrospective honesty, reasons in order of gravity:

  • Fingerprinting kills obscurity. The “hidden port” is discoverable by behavior: a scanner speaking SSH first gets the SSH-2.0 banner, a following ClientHello gets TLS. sslh does not hide the service, it delays its detection by one packet and there are nmap scripts that do exactly that.
  • The modern era’s philosophy is the opposite. Since WireGuard , the winning model is “silent, crypto-first, standard ports”: a non-peer gets absolute silence, not a multiplexer that talks to it. Hiding ssh behind a shared 443 rebuilds the magic tower WireGuard demolished.
  • The admin surface. One more C daemon between the world and sshd, forking connections and parsing banners: a SPOF and one more CVE, on the path to the machine’s most sensitive data.
  • Modern TLS does not multiplex like that. ALPN, SNI, HTTP/2 and TLS termination in LB/CDN assume a 443 that speaks clean TLS; grafting ssh into the flow confuses probes, logs and mirroring.
  • The port is no longer scarce. IPv6, per-service IPs, SRV records: the “one outgoing port” constraint that justified sslh died with the firewalls that imposed it.

And the death of the magic trick deserves its own story: DPI

Because one last illusion remains to dismantle, the one from modern escape artists who say “the DPI won’t dare”: running ssh, https and an SSL-VPN through the same 443 and hoping the firewall catches nothing. Firewalls stopped looking at ports a long time ago. A modern DPI (the Palo Alto, Fortinet, Cisco boxes and friends) classifies traffic by grammar, not by port number and port 443 teaches it nothing it does not already know.

Pushing cleartext ssh through 443? The client’s first packet starts with SSH-2.0-, the classification lands before you have typed your first command and the block lands two signature lines later. Getting cleverer by wrapping ssh in TLS? The ClientHello has a fingerprint (the JA3/JA4 families made detection teams’ fortunes), an ssh-wrapped-in-TLS has no credible SNI, its certificate is self-signed or exotic and above all the traffic shape betrays it: the ssh binary protocol has characteristic record sizes and padding and an interactive session sends packets the size of a keystroke, at the cadence of a human, which is the most recognizable signal in the world for a machine hunting precisely that. OpenVPN’s SSL-VPN over TCP/443 has its P_CONTROL handshake, SSTP plays at TLS disguise but its SNI and HTTP choreography betray it and commercial VPNs each have their signature in the vendor’s databases, kindly kept up to date for you.

In other words: hoping to push ssh, https and an SSL-VPN through one 443 under a DPI firewall’s nose is hoping the doorman does not know the three languages he hears. He knows them. He pays a subscription for it. Port 443 is only a discreet corridor for well-formed genuine TLS and even then: the traffic shape remains yours to own. What this multiplexing has ever hidden is the port. Never the language.

The chapter is not closed, by the way and it would be dishonest to pretend: disguise has since changed trades. AmneziaWG adds to the original WireGuard the junk packets, custom message headers and handshake padding that erase its shape signature; udp2raw disguises UDP as forged TCP, wstunnel wraps the whole thing in a WebSocket under genuine TLS with a real SNI and Xray REALITY outright borrows the TLS handshake of a real site to carry arbitrary traffic. The race continues, the DPI answers with active probing and statistical detection, the disguise answers by copying the real world. Those tools have a legitimate job (hostile networks, censorship), but note the delicious reversal: to survive the DPI, one must now look like the HTTPS that sslh pretended to hide. The magic tower changed owners, not principles.

sslh still exists (v2.x and its use cases remain real: edge boxes trapped behind a single 443, demo servers, tarpits). What died is the idea of putting it between the internet and a production fleet’s sshd: cryptographic silence won the first debate and DPI won the second.