https, openvpn and ssh on one port - thanks to haproxy

Wed 10 September 2014

Sometimes you land in places with restricted internet access to few destination ports (like in many hotels). In such places it is handy to have OpenVPN listening on innocent port like 443. But what to do if you don't have spare ip?

Haproxy solution

One of solutions can be sharing one port for many services - and haproxy is allowing us to do so.

Such solution isn't too complicated:

# /etc/haproxy/haproxy.cfg

frontend ssl
    mode tcp
    bind 0.0.0.0:443 name frontend-ssl
    option tcplog
    tcp-request inspect-delay 2s
    tcp-request content accept  if  { req.ssl_hello_type 1 }
    use_backend main-ssl        if  { req.ssl_hello_type 1 }
    use_backend openvpn         if  !{ req.ssl_hello_type 1 } !{ req.len 0 }
    use_backend ssh             if  !{ req.ssl_hello_type 1 } { req.len 0 }


frontend main
    bind 0.0.0.0:80
    bind 127.0.0.1:8443 ssl crt /etc/haproxy/certs/server.pem crt /etc/haproxy/certs/ accept-proxy
    mode http
    option forwardfor
    default_backend webserver


backend main-ssl
    mode tcp
    server main-ssl 127.0.0.1:8443 send-proxy

backend openvpn
    mode tcp
    server openvpn-localhost 127.0.0.1:1194

backend ssh
    mode tcp
    server ssh-localhost 127.0.0.1:22

backend webserver
    server webserver-localhost 127.0.0.1:8080

What we did?

In this solution we created special fronted (ssl) for haproxy. We are giving haproxy 2 seconds for recognizing connection. When haproxy will recognize proper https connection, then we forward connection to backend main-ssl. Of course we could as well send it directly to nginx, apache, lighttpd or other web server to let it handle https, but here we will send it to other haproxy fronted. Thanks to send/accept-proxy we don't lose informations about connection (client ip etc.).

If haproxy will recognize that data it is getting, isn't proper https request, then it will forward connection to openvpn server.

And finally if haproxy during 2 seconds won't get any data from the client, then we assume it is ssh connection (as in ssh protocol server start talking first, instead of how it is in http, https and openvpn)

Instead of openvpn and ssh we could use any other pair of services where in one protocol client start conversation while in second server.

2 seconds are absolutely arbitrary value. Increasing value will give more time for client to establish data exchange, but in same time will increase wait time you will get during ssh connection.

Why not?

Of course this solution isn't perfect:
  • client browsing our site should use modern browser
  • he must have decent connection (in this config he have 2 seconds to initialize encryption handshake after getting tcp connection)
  • you have to check how future https encryptions will be working.

So this solution isn't something what you would want on production. But still it is great option for personal server.