Um in einer kleinen, containerisieren Umgebung mehrere Webserver hinter einer IP zu betreiben habe ich mir mal HAProxy als Reverse Proxy angeschaut. Dabei sind mir die mächtigen acls aufgefallen mittels welcher man flexibel unterschiedliche Backends ansprechen kann. Diese acls lassen Konstellationen dieser Art zu:

                                                                             +-----------+
+-----------+                                  +-----------+                 |sshd       |
|Client     |                                  |HAProxy    +---------------> |           |
|           | +------------------------------+ |           |                 +-----------+
|           | |TLS-Tunnel                    | |           |
|           | |                              | |           |                 +-----------+
|           | +------------------------------+ |           +---------------> |httpd      |
|           |                                  |           |                 |           |
+-----------+                                  +-----------+                 |           |
                                                                             +-----------+

HAProxy entscheidet anhand der ersten übertragenen Byte an welchen Deamon die Verbindung weitergeleitet werden soll. SSH beispielsweise definiert folgenden identification string in RFC 4253, den Client und Server nach erfolgreich aufgebauter TCP-Verbindung austauschen müssen:

SSH-protoversion-softwareversion SP comments CR LF

Wobei protoversion "2.0" oder (hoffentlich nicht mehr) "1.0" sein können.

Mit folgender einfachen HAProxy-Konfig lässt sich die oben abgebildete Magie realisieren:

global
        chroot /var/lib/haproxy
        user haproxy
        group haproxy
        daemon

        ca-base /etc/ssl/certs
        crt-base /etc/ssl/private

        ssl-default-bind-ciphers
ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS
        ssl-default-bind-options no-sslv3

backend httpd
    mode http
    rspadd Strict-Transport-Security:\ max-age=31536000
    server local_http_server 127.0.0.1:80

backend sshd
    mode tcp
    server ssh 127.0.0.1:22
    timeout server 2h


frontend ssl
    mode tcp
    bind *:443 ssl crt /etc/haproxy/cert-key.pem no-sslv3
    tcp-request inspect-delay 5s
    tcp-request content accept if HTTP

    acl client_attempts_sshregex req.payload(0,7) -m reg "^SSH-[1|2]{1}\.0.*"

    use_backend sshd if client_attempts_sshregex
    use_backend httpd if HTTP

Im frontend wird eine acl definiert, die per regulärem Ausdruck SSH-1.0* und SSH-2.0* matched und in diesem Fall das backend sshd als Ziel nutzt. Wenn HTTP gesprochen wird läuft die Verbindung auf den httpd.

Mit openssl lässt sich diese Konstruktion testen. SSH in TLS:

Host:dimi@lnx:~# openssl  s_client -connect blog.3or.de:443 -quiet 
SSH-2.0
SSH-2.0-OpenSSH_7.2p2 Ubuntu-4ubuntu2.1
Protocol mismatch.

Und HTTP in TLS:

dimi@lnx:~# openssl  s_client -connect blog.3or.de:443 -quiet 
GET / HTTP/1.0

HTTP/1.1 200 OK
Server: nginx/1.10.0 (Ubuntu)
Date: Sun, 13 Nov 2016 19:23:00 GMT
Content-Type: text/html
Content-Length: 28
Last-Modified: Sun, 23 Oct 2016 15:36:53 GMT
Connection: close
ETag: "580cd915-1c"
Accept-Ranges: bytes
Strict-Transport-Security: max-age=31536000

<marquee> hallo. </marquee>

Und über die Konfigurationsoption "ProxyCommand" verheiratet man dieses Konstrukt mit seinem SSH-Client:

Host blog.3or.de
        HostName blog.3or.de
        User dimi
        LocalForward localhost:8118 localhost:8118
        IdentityFile /home/dslamar/.ssh/outdoor.id
        ProxyCommand proxytunnel --quiet -d blog.3or.de:443 -e

Der OpenSSH-Client ruft zunächst proxytunnel auf, schiebt dann seine SSH-TCP-Verbindung über STDIN/SDTOUT des proxytunnel-Prozess innerhalb des TLS-Tunnel zum HAProxy-Server. Dieser erkennt erkennt den identification string SSH-2.0, leitet an den sshd weiter.

Das tolle an dieser Lösung: Man kommt, im no-interception HTTPS-Proxy-Szenario, an Proxylösungen wie Bluecoat vorbei. Versucht ein Client über einen Bluecoat-Proxy eine TLS-Verbindung zu einer Seite aufzubauen, baut dieser - bevor die TLS-Verbindung zwischen Client und Server erlaubt wird - parallel dazu eine HTTPS-Verbindung zum Server auf. Diese soll dazu dienen zu prüfen ob der Server tatsächlich HTTPS spricht oder ob der Client eine andere TCP-Verbindung durch den Proxy tunneln will. Erst wenn diese Testverbindung erfolgreich war und der Server wirklich HTTPS spricht wird die Verbindung des Clients erlaubt.