haproxy ssh
May 30, 2017
🔗HTTPS and SSH on the same port
Using HAProxy to server SSH and SSL available on the same port:
global
maxconn 1000000
spread-checks 3
log /var/run/log local0 notice
daemon
tune.ssl.default-dh-param 2048
defaults
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 10.10.8.3:8000 maxconn 50 check
backend ssh
mode tcp
option tcplog
server ssh 10.10.8.4:22
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 your.haproxy.com:443 -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")
'SSH-2.0'
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.
src: http://blog.chmd.fr/ssh-over-ssl-episode-4-a-haproxy-based-configuration.html