HTTPS and SSH on the same port

Using HAProxy to server SSH and SSL available on the same port:

    maxconn 1000000
    spread-checks 3
    log /var/run/log local0 notice
    tune.ssl.default-dh-param 2048

    mode http
    balance roundrobin
    option http-server-close
    option abortonclose
    option dontlognull
    option redispatch

    timeout check           3s
    timeout client          30s  # Client and server timeout must match the longest
    timeout connect         5s
    timeout http-keep-alive 5s
    timeout http-request    10s  # A complete request may never take that long.
    timeout queue           1m   # Don't queue requests too long if saturated.
    timeout server          10s  # Time we may wait for a response from the server.

    retries 3
    log global

# ----------------------------------------------------------------------------
# stats
# ----------------------------------------------------------------------------
listen stats
    bind *:81
    stats enable
    stats uri /
    stats show-node
    stats show-legends
    stats refresh 5s

# ------------------------------------------------------------------------------
#  https + ssh
# ------------------------------------------------------------------------------
frontend ssl
    mode tcp
    bind *:443 ssl crt /usr/local/etc/haproxy/your.cert
    option tcplog
    tcp-request inspect-delay 5s
    tcp-request content accept if HTTP

    # 5353482d322e30 is the binary representation of the string 'SSH-2.0'
    acl client_attempts_ssh payload(0,7) -m bin 5353482d322e30

    use_backend ssh if !HTTP
    use_backend ssh if client_attempts_ssh
    use_backend http if HTTP

backend http
    mode http
    http-request set-header X-Forwarded-Port %[dst_port]
    server www maxconn 50 check

backend ssh
    mode tcp
    option tcplog
    server ssh
    timeout server 2h

Connecting from an SSH client

To connect throught the HAProxy on port 443 edit the ~/.ssh/config:

Host ssh-over-https
    ProxyCommand openssl s_client -connect -quiet

how does this work ?

The RFC 4253, section 4.2 states that clients must send a string that starts with ‘SSH-2.0’:

4.2.  Protocol Version Exchange

   When the connection has been established, both sides MUST send an
   identification string.  This identification string MUST be

      SSH-protoversion-softwareversion SP comments CR LF

   Since the protocol being defined in this set of documents is version
   2.0, the 'protoversion' MUST be "2.0".
>>> "5353482d322e30".decode("hex")

5353482d322e30 is the binary representation of the string ‘SSH-2.0’. So everything boils down to this line:

acl client_attempts_ssh payload(0,7) -m bin 5353482d322e30

When a new connection is made on the port 443, HAproxy decrypts the SSL layer, and checks whether the stream of data sent by the client starts with this string.