Running HTTPS, SSH and VPN on port 443

A port can only be bound to one service at a time, which makes perfect sense since the OS cannot possibly know which application to route the packet to. However, protocols often have distinct signatures, for example the first few bytes of SSH is always SSH-2.0 while HTTP packets always start with {HEAD|GET|PUT|POST|DELETE|OPTIONS}. So technically, it should be possible for a proxy application to perform Deep Packet Inspection (DPI) and route it to the correct service if the signatures are provided.

HAProxy is one such application, with the capability to redirect packets at both TCP as well as HTTP (application) layer. A word of warning though, the documentation is a tad lengthy, 110,000 words over 15,000 lines. After a few weeks of messing around with the settings, I finally got it to work and now have HTTPS, SSH and VPN listening on port 443.

This is not purely an academic exercise, there are some real world benefits. Firstly, if you are connecting from behind a restrictive firewall, say one that only allows port 80 and 443, you will now be able to SSH/VPN out of that network, unless of course that restrictive firewall itself performs DPI, in which case, you might have to consider tunnelling SSH through SSL, which incidentally is also supported by HAProxy. Secondly, you can "cloak" your services. Someone performing a port scan would only see the default service, thus adding another layer of security to your server.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
$ cat /etc/haproxy.cfg 

global
  tune.ssl.default-dh-param 2048

defaults
  timeout connect 5000
  timeout client  50000
  timeout server  50000

frontend ssl
    mode tcp
    bind 0.0.0.0:443
    tcp-request inspect-delay 5s
    tcp-request content accept  if  HTTP
    use_backend ssh             if  { payload(0,7) -m bin 5353482d322e30 }
    use_backend main-ssl        if  { req.ssl_hello_type 1 }
    default_backend openvpn

frontend main
    bind 127.0.0.1:443 ssl crt /some/folder/cert.pem accept-proxy
    mode http
    option forwardfor
    default_backend webserver

frontend http
    bind 0.0.0.0:80
    reqadd X-Forwarded-Proto:\ http
    default_backend webserver

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

backend openvpn
    mode tcp
    timeout server 2h
    server openvpn-localhost 127.0.0.1:1193

backend ssh
    mode tcp
    timeout server 2h
    server ssh-localhost 127.0.0.1:22

backend webserver
    mode http
    option forwardfor
    redirect scheme https code 301 if !{ ssl_fc }
    server webserver-localhost 127.0.0.1:81