HAProxy 2.2 mit SSL Terminierung

HAProxy 2.2 (SSL Termination / SSL offloading)

Um die Last auf mehrere Nextcloud Instanzen zu verteilen schalten wir einen Loadbalancer (HAProxy) vor die Nextcloud Webserver. Zudem verschlüsseln wir die Kommunikation mit SSL sowohl nach extern, als auch nach intern.

In diesem Beispiel nutzen wir folgende Ubuntu 20 Server:

  • haproxy – 192.168.2.205
  • nc1 – 192.168.2.206
  • nc2 – 192.168.2.207

Zuerst passen wir die hosts-Datei auf allen Servern an:

sudo -s
nano /etc/hosts
# Server: haproxy
127.0.0.1 localhost
127.0.1.1 haproxy
...
192.168.2.205 haproxy
192.168.2.206 nc1
192.168.2.207 nc2
# Server: nc1
127.0.0.1 localhost
127.0.1.1 nc1
...
192.168.2.205 haproxy
192.168.2.206 nc1
192.168.2.207 nc2
# Server: nc2
127.0.0.1 localhost
127.0.1.1 nc2
...
192.168.2.205 haproxy
192.168.2.206 nc1
192.168.2.207 nc2

Nun aktualisieren wir den haproxy-Server und installieren den HAProxy-Loadbalancer:

apt update && apt upgrade -y
apt-get install --no-install-recommends software-properties-common
add-apt-repository ppa:vbernat/haproxy-2.2
apt install haproxy=2.2.\*

Am Beispiel von self signed SSL-Zertifikaten verschlüsseln wir sowohl die Kommunikation zum Loadbalancer, als auch die Kommunikation zwischen dem Loadbalancer und den Nextcloud-Instanzen. Um das realisieren zu können installieren wir

apt install ssl-cert

und generieren uns neue self-signed SSL_Zertifikate

make-ssl-cert generate-default-snakeoil -y

Um diese Zertifikate im HAProxy nutzen zu können, fügen wir das Zertifikat und den PrivatKey in eine Datei zusammen:

cat /etc/ssl/private/ssl-cert-snakeoil.key /etc/ssl/certs/ssl-cert-snakeoil.pem > /etc/haproxy/server.pem

Zudem generieren wir uns ein dhparam.pem-File, um auch den Schlüsselaustausch selbst abzusichern:

openssl dhparam -out /etc/haproxy/dhparam.pem 4096

Die Vorbereitungen sind nun abgeschlossen, so dass wir den HAProxy konfigurieren können. Beginnen wir mit einer Sicherungskopie der Standardkonfiguration.

cd /etc/haproxy
cp haproxy.cfg haproxy.cfg.bak

Öffnen Sie dann die Konfigurationsdatei und ersetzen den kompletten Inhalt mit dem nachfolgenden Block:

global
	log /dev/log	local0
	log /dev/log	local1 notice
	chroot /var/lib/haproxy
	stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
	stats timeout 30s
	user haproxy
	group haproxy
	daemon

	# Default SSL material locations
	ca-base /etc/ssl/certs
	crt-base /etc/ssl/private

	ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
    	ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
    	ssl-default-bind-options prefer-client-ciphers no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets

    	ssl-default-server-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
    	ssl-default-server-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
    	ssl-default-server-options no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets
	ssl-dh-param-file /etc/ssl/certs/dhparam.pem

defaults
	log	global
	mode	http
	option	httplog
	option	dontlognull
        timeout connect 5000
        timeout client  50000
        timeout server  50000
	errorfile 400 /etc/haproxy/errors/400.http
	errorfile 403 /etc/haproxy/errors/403.http
	errorfile 408 /etc/haproxy/errors/408.http
	errorfile 500 /etc/haproxy/errors/500.http
	errorfile 502 /etc/haproxy/errors/502.http
	errorfile 503 /etc/haproxy/errors/503.http
	errorfile 504 /etc/haproxy/errors/504.http

listen  stats
	bind 	:8443 ssl crt /etc/haproxy/server.pem alpn h2,http/1.1 ssl-min-ver TLSv1.2
        mode  	http
        stats 	enable
	stats show-legends
	stats hide-version
        stats refresh 30s
        stats show-node
        stats uri  /

frontend c-rieger.de
    mode    http
    bind    :80
    bind    :443 ssl crt /etc/haproxy/server.pem alpn h2,http/1.1 ssl-min-ver TLSv1.2
    acl url_discovery path /.well-known/caldav /.well-known/carddav
    http-request redirect location /remote.php/dav/ code 301 if url_discovery
    redirect scheme https code 301 if !{ ssl_fc }
    http-response set-header Strict-Transport-Security max-age=63072000
    acl is_certbot path_beg /.well-known/acme-challenge/
    use_backend LetsEncrypt if is_certbot    
    default_backend NextcloudServer

backend NextcloudServer
    balance leastconn 
    cookie SERVERID insert indirect nocache
    server server1 192.168.2.206:443 check maxconn 20 ssl verify none ca-file /etc/haproxy/server.pem cookie nc1
    server server2 192.168.2.207:443 check maxconn 20 ssl verify none ca-file /etc/haproxy/server.pem cookie nc2

backend LetsEncrypt
    mode http
    server certbot 127.0.0.1:9080

Nach dem Speichern der Konfigurationsdatei, gefolgt von einem Neustart des HAProxys

service haproxy restart

können Sie die Status-Seite des Loadbalancers bereits aufrufen (https://192.168.2.205:8443):

Vorausgesetzt, Sie haben die Nextcloud-Instanzen (Backends) bereits aufgesetzt (Lesen Sie mehr in diesem Artikel), so können Sie diese „lastverteilt“/“gebalanced“ über https://192.168.2.205 erreichen.

Überprüfen Sie sowohl in der Nextcloud, als auch in der Browserkonsole das Balancingverhalten. Dazu sehen Sie sich zum Einen die System-Einstellungen der Nextcloud an und vollziehen dort visuell nach, auf welchem Node (Knoten/Server) Sie sich befinden. Zum Anderen sehen Sie sich das Pendant, also den vom HAProxy gesetzten Cookie, in der Browserkonsole an. Beide spiegeln im Beispiel den Server nc1 wider.

Sie befinden sich auf dem Sever nc1 – Sie finden in der Browserkonsole den gesetzten Cookie nc1 (SERVERID=nc1)

Möchten Sie Let’s Encrypt Zertifikate nutzen, so installieren Sie die Let’s Encrypt Software certbot:

add-apt-repository -y ppa:certbot/certbot
apt update && apt install -y certbot

Mittels des Kommandozeilentools certbot requestieren wir die SSL Zertifikate

certbot certonly --standalone --preferred-challenges http --http-01-address 127.0.0.1 --http-01-port 9080 -d ihre.domain.de --email ihre@domain.de --agree-tos --non-interactive

und nutzen dann ein Skript um alle Zertifikate zu konsolidieren:

nano /etc/haproxy/le-certificates.sh
#!/bin/bash
for CERTIFICATE in `find /etc/letsencrypt/live/* -type d`; do
CERTIFICATE=`basename $CERTIFICATE`
cat /etc/letsencrypt/live/$CERTIFICATE/fullchain.pem /etc/letsencrypt/live/$CERTIFICATE/privkey.pem > /etc/haproxy/server.pem
done
exit 0

Nun machen wir das Skript noch ausführbar

chmod +x /etc/haproxy/le-certificates.sh

und führen es aus.

/etc/haproxy/le-certificates.sh

Nach einem Neustart des HAProxy-Services

services haproxy restart

werden die neuen Let’s Encypt SSL-Zertifikate schon verwendet.

Um die Zertifikate automatisch zu erneuern benötigen wir ein weiteres Skript:

nano /etc/haproxy/le-renewal.sh
#!/bin/bash
certbot renew --standalone --preferred-challenges http --http-01-address 127.0.0.1 --http-01-port 9080 --post-hook "/etc/haproxy/le-certificates.sh && systemctl reload haproxy.service" --quiet
exit 0

Auch dieses Skript muss wieder ausführbar gemacht werden

chmod +x /etc/haproxy/le-renewal.sh

Nun legen wir noch den wöchentlichen Cronjob an

crontab -e
@weekly /etc/haproxy/le-renewal.sh > /dev/null

wodurch die Zertifikate wöchentlich auf Aktualisierungen geprüft werden und bei einer Aktualisierung auch der haproxy-Service neu gestartet wird.

Die Installation und Einrichtung des HAProxy-Loadbalancers wurde somit erfolgreich abgeschlossen. Über eine Spende würden sich meine Frau, meine Zwillinge und ich sehr freuen. Vielen Dank!

© Carsten Rieger IT-Services