Wer bei verschiedenen Reverse Proxy Aktivitäten auf seinem Uberspace mit Apache auf Grenzen stößt, kann HAproxy installieren.
Ähnlich wie Pound ist es DASein Swiss Army Knife für alle HTTP, HTTPS und TCP Aktivitäten. Man kann sehr granular auf Layer 3-7 beeinflussen, wie Daten an andere Server und Dienste weitergeschleust werden sollen.
Ich benötigte HAproxy, um eine Website von einem externen Server via TLS gesichert per Uberspace verfügbar zu machen, also ein recht simles Szenario.
Wahrscheinlich möchte man gerne, dass HAproxy über Port 80 und 443 erreichbar ist. Natürlich sind diese Ports bereits von Uberspace's Pound belegt. Pound leitet aber seine Anfragen an Apache weiter und den können wir wie hier beschrieben über eine .htaccess Datei konfigurieren, um Anfragen an unseren eigenen HAproxy weiterzuleiten:
[mareike@neon html]$ cat .htaccess
RewriteEngine On
RewriteRule (.*) http://localhost:64223/$1 [P]
Der Port ist dabei derjenige, auf den wir unseren HAproxy später lauschen lassen. Bitte unbedingt die Hinweise für die Wahl eines Ports beachten! Durch RewriteCond kann noch weiter eingeschränkt werden, wann die Anfragen an HAproxy weitergereicht werden.
Wer zum Beispiel einfach alle Domains, die nicht auf dem Uberspace über ein Verzeichnis angelegt sind, an HAproxy weiterreichen möchte, kann dies hiermit tun:
RewriteCond /var/www/virtual/hund/%{HTTP_HOST} !-d
RewriteCond %{HTTP:X-Forwarded-Server} ^$
RewriteCond %{HTTP_HOST} !^[A-Za-z0-9]+\.[A-Za-z]+\.uberspace\.de$
RewriteRule (.*) http://127.0.0.1:61578/$1 [P]
Das User Verzeichnis hund
muss dabei natürlich entsprechend angepasst werden.
Wer die Uberspace-Subdomains auch ausschließen möchte ohne dafür jeweils ein Verzeichns oder einen Symlink anzulegen, ändert das RegEx auf !^.*\.[A-Za-z0-9]+\.[A-Za-z]+\.uberspace\.de$
ab.
Um einen Loop zu vermeiden, prüfen wir außerdem auf das Vorhandensein des Headers X-Forwarded-Server, welchen HAproxy automatisch hinzufügt.
Jetzt geht es ans Werk. Zunächst laden wir uns die aktuellen Sourcen:
[hund@menkar ~]$ wget -P ~/src http://www.haproxy.org/download/1.6/src/haproxy-1.6.4.tar.gz
Anschließend entpacken wir diese und wechseln in das entsprechende Verzeichnis:
[hund@menkar ~]$ tar xfz ~/src/haproxy-1.6.4.tar.gz -C ~/src; cd ~/src/haproxy-*
Anschließend wird HAproxy mit dem folgenden Befehl übersetzt:
[hund@menkar haproxy-1.6.4]$ make TARGET=linux26 USE_OPENSSL=1 USE_ZLIB=1 ADDLIB=-lz
Nun können wir HAproxy in unserem User Verzeichnis installieren:
[hund@menkar haproxy-1.6.4]$ make PREFIX=$HOME/bin/haproxy install
HAproxy wurde damit unter ~/bin/haproxy/sbin/haproxy installiert. Abschließend räumen wir noch etwas auf, um Speicherplatz zu sparen:
[hund@menkar haproxy-1.6.4]$ cd ~; rm -rf ~/src/haproxy-*
Jetzt legen wir eine kleine Beispielkonfiguration für HAproxy an. Hier wird ein Backend angelegt, welches nur beim Zugriff auf den Hostnamen example.hund.menkar.uberspace.de reagiert:
[hund@menkar ~]$ mkdir ~/etc/haproxy
[hund@menkar ~]$ cat >> ~/etc/haproxy/haproxy.cfg <<__EOF__
global
frontend HTTP
bind 127.0.0.1:64223
mode http
timeout client 5000
acl a_example hdr(X-Forwarded-Host) -i example.hund.menkar.uberspace.de
use_backend b_example if a_example
default_backend uberspace_81
backend uberspace_81
mode http
timeout connect 3000
timeout server 3000
server uberspace 95.143.172.241:81
backend b_example
mode http
timeout connect 5000
timeout server 7200000
server server1-a www.example.com:443 ssl ca-file /etc/ssl/certs/ca-bundle.trust.crt
__EOF__
Alle Anfragen, die HAproxy auf Port 64223 für den Hostnamen example.hund.menkar.uberspace.de beantwortet, werden somit TLS verschlüsselt an den Webserver weitergereicht, der unter www.example.com auf Port 443 lauscht. Wenn auf der Gegenseite kein valides Zertifikat vorliegt, gibt man hinter ssl einfach verify none
an statt ca-file.
Für die Verschlüsselung in Richtung des Browsers sollte für den Uberspace unbedingt ein Zertifikat eingespielt werden (z.B. von LetsEncrypt).
Wenn HAproxy für einen nicht konfigurierten Hostnamen angesprochen wird, dann zeigt dieser normalerweise eine interne 503er Fehlerseite an. Um stattdessen die Fehlerseiten zu verwenden, die der Apache des Uberspace ausgibt, werden alle unbekannten Anfragen zurück an den lokalen Apache von Uberspace zurück gegeben. Um den Loop hier zu vermeiden ist es deshalb sehr wichtig in der .htaccess wie oben genannt auf das Vorhandensein der X-Forwarded Header zu prüfen. Die IP-Adresse des uberspace Servers beim Backend uberspace_81 muss natürlich je nach Host entsprechend angepasst werden, sonst landen die Anfragen auf dem falschen Uberspace Server.
Es ist wohl häufig so, dass der Backend Webserver seine Seite nur dann richtig ausliefert, wenn man den richtigen Hostnamen mitschickt. Der HAproxy übernimmt zunächst den Hostnamen, welche der Browser angesprochen hat. Um dies für die Weiterleitung an den Backend Server zu ändern, kann man dem Backend noch diese Zeilen hinzufügen:
reqirep ^Host: Host:\ www.example.com
rspirep ^Location:\ https://www.example.com(.*) Location:\ https://example.hund.menkar.uberspace.de\1
rspirep ^Location:\ http://www.example.com(.*) Location:\ https://example.hund.menkar.uberspace.de\1
Wir können HAproxy nun bereits manuell starten:
[hund@menkar ~]$ ~/bin/haproxy/sbin/haproxy -D -f ~/etc/haproxy/haproxy.cfg
Komfortabler zum staren und stoppen ist es allerdings einen Dienst über die daemontools einzurichten. Wir beenden also HAproxy erstmal wieder:
[hund@menkar ~]$ killall haproxy
Anschließend richten wir den Dienst ähnlich wie in der verlinkten Anleitung beschrieben ein. Als Service-Verzeichnis bietet sich ~/etc/haproxy an; dort wo wir bereits unsere haproxy.cfg liegen haben.
Die Datei run
für den Dienst kann so aussehen:
[hund@menkar ~]$ cat ~/etc/haproxy/run
#!/bin/sh
exec ~/bin/haproxy/sbin/haproxy -f ~/etc/haproxy/haproxy.cfg 2>&1
Nachdem der Dienst aktiviert wurde, läuft HAproxy und nimmt die Anfragen, die der Apache weiterleitet, für uns an:
[hund@menkar ~]$ ps aux | grep haproxy
hund 9775 0.0 0.0 44660 3124 ? S 22:07 0:00 /home/hund/bin/haproxy/sbin/haproxy -f /home/hund/etc/haproxy/haproxy.cfg
hund 23556 0.0 0.0 103304 876 pts/9 S+ 22:09 0:00 grep haproxy
hund 29021 0.0 0.0 3932 392 ? S 20:35 0:00 supervise haproxy
[hund@menkar ~]$
Ab sofort schränken uns nur noch die Grenzen von HAproxy ein (und das sind nicht sonderlich viele 😉).