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.
Drei Konfigurationsbeispiele aus dem Enterprise-Umfeld finden sie weiter unten in diesem Artikel.
Download: https://codeberg.org/criegerde/HAProxy/src/branch/main/Layer4or6
In dieser Anleitung gehen wir von dieser exemplarischen Umgebung aus:
- 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.6 apt install socat haproxy=2.6.\*
Um sich den Status des Loabalancers „live“ ansehen zu können nutzen wir das Tool HATOP:
wget http://archive.ubuntu.com/ubuntu/pool/universe/h/hatop/hatop_0.8.0-1.1_all.deb
dpkg -i hatop*.deb
Ruft man nach Fertigstellung des HAProxys das HATop-Tool wie folgt auf,
hatop -s /run/haproxy/admin.sock
so erhält man eine „Live“-Sicht, auf den Loadbalancer.
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 -dsaparam -out /etc/haproxy/dhparam.pem 2048
Die Vorbereitungen sind nun abgeschlossen, so dass wir den HAProxy (Layer6 / http-Mode) konfigurieren können. Beginnen wir mit einer Sicherungskopie der Standardkonfiguration.
touch /var/log/haproxy.log chown syslog:adm /var/log/haproxy.log 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 maxconn 5 mode http stats enable stats show-legends stats hide-version stats refresh 30s stats show-node stats uri / frontend NEXTCLOUD mode http bind :80 bind :443 ssl crt /etc/haproxy/server.pem alpn h2,http/1.1 ssl-min-ver TLSv1.2 maxconn 20000 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 NEXTCLOUD backend NEXTCLOUD balance leastconn cookie SERVERID insert indirect nocache option httpchk GET /login http-check expect rstatus [2-3][0-9][0-9] server server1 192.168.2.206:443 check maxconn 10000 ssl verify none ca-file /etc/haproxy/server.pem cookie nc1 server server2 192.168.2.207:443 check maxconn 10000 ssl verify none ca-file /etc/haproxy/server.pem cookie nc2 backend LetsEncrypt mode http server certbot 127.0.0.1:9080
Konfigurationsbeispiele aus dem Enterprise-Umfeld:
- Layer 6 / http-mode mit Healthcheck und SSL-Terminierung am HAProxy
- Layer 4 / tcp-mode mit http-Healthcheck
- Layer 4 / tcp-mode mit SNI (verschiedene Anwendungen, bspw. Nextcloud und BigBlueButton)
Beispiel 1: Layer 6 / http-mode mit http-Healthcheck:
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 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 ssl-min-ver TLSv1.2 no-tls-tickets tune.ssl.cachesize 1000000 ssl-dh-param-file /etc/haproxy/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 frontend Statistiken bind *:8443 ssl crt /etc/haproxy/server.pem alpn h2,http/1.1 ssl-min-ver TLSv1.2 mode http option httplog maxconn 5 stats enable stats show-legends stats hide-version stats refresh 60s stats show-node stats uri / frontend NEXTCLOUD 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 NEXTCLOUD backend NEXTCLOUD mode http fullconn 20000 balance leastconn stick-table type ip size 128m expire 2h stick on src option forwardfor option httpchk GET /login http-check expect rstatus [2-3][0-9][0-9] server NC1 192.168.2.206:443 weight 1 inter 5s downinter 20s rise 4 fall 2 check ssl verify none ca-file /etc/haproxy/server.pem on-marked-down shutdown-sessions maxconn 10000 server NC2 192.168.2.207:443 weight 1 inter 5s downinter 20s rise 4 fall 2 check ssl verify none ca-file /etc/haproxy/server.pem on-marked-down shutdown-sessions maxconn 10000 backend LetsEncrypt mode http server certbot 127.0.0.1:9080 # (c) Carsten Rieger IT-Services, v. 220102
Beispiel 2: Layer 4 / tcp-mode mit http-Healthcheck:
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 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 ssl-min-ver TLSv1.2 no-tls-tickets tune.ssl.cachesize 1000000 ssl-dh-param-file /etc/haproxy/dhparam.pem defaults log global mode tcp option tcplog 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 frontend Statistiken bind *:8443 ssl crt /etc/haproxy/server.pem alpn h2,http/1.1 ssl-min-ver TLSv1.2 mode http option httplog maxconn 5 stats enable stats show-legends stats hide-version stats refresh 60s stats show-node stats uri / frontend NEXTCLOUD bind *:443 maxconn 20000 mode tcp option tcplog tcp-request inspect-delay 5s tcp-request content accept if { req_ssl_hello_type 1 } default_backend NEXTCLOUD backend NEXTCLOUD mode tcp fullconn 20000 balance leastconn stick-table type ip size 100m expire 2h stick on src option httpchk GET /login http-check expect rstatus [2-3][0-9][0-9] server server1 192.168.2.206:443 weight 1 inter 5s downinter 20s rise 4 fall 2 check check-ssl verify none on-marked-down shutdown-sessions maxconn 10000 server server2 192.168.2.207:443 weight 1 inter 5s downinter 20s rise 4 fall 2 check check-ssl verify none on-marked-down shutdown-sessions maxconn 10000 # (c) Carsten Rieger IT-Services, v. 220102
Beispiel 3: Layer 4 / tcp-mode SSL passthrough für Nextcloud und BigBlueButton:
global log /dev/log local0 log /dev/log local1 notice chroot /var/lib/haproxy stats socket /run/haproxy/admin.sock mode 660 level admin stats timeout 30s user haproxy group haproxy daemon # Default SSL material locations ca-base /etc/ssl/certs crt-base /etc/ssl/private # See: https://ssl-config.mozilla.org/#server=haproxy&server-version=2.0.3&config=intermediate 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 ssl-min-ver TLSv1.2 no-tls-tickets defaults log global mode tcp option tcplog 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 frontend proxy bind *:443 mode tcp option tcplog maxconn 10000 tcp-request inspect-delay 5s tcp-request content accept if { req_ssl_hello_type 1 } acl Nextcloud req_ssl_sni -i nextcloud.domain.de acl BigBlueButton req_ssl_sni -i bbb.domain.de.de use_backend Nextcloud if Nextcloud use_backend BigBlueButton if BigBlueButton backend Nextcloud mode tcp fullconn 5000 balance source stick-table type binary len 32 size 1m expire 600m acl clienthello req_ssl_hello_type 1 acl serverhello rep_ssl_hello_type 2 tcp-request inspect-delay 5s tcp-request content accept if clienthello tcp-response content accept if serverhello stick on payload_lv(43,1) if clienthello stick store-response payload_lv(43,1) if serverhello option ssl-hello-chk server Nextcloud 192.168.2.206:443 check maxconn 5000 backend BigBlueButton mode tcp fullconn 5000 balance source stick-table type binary len 32 size 1m expire 600m acl clienthello req_ssl_hello_type 1 acl serverhello rep_ssl_hello_type 2 tcp-request inspect-delay 5s tcp-request content accept if clienthello tcp-response content accept if serverhello stick on payload_lv(43,1) if clienthello stick store-response payload_lv(43,1) if serverhello option ssl-hello-chk server BigBlueButton 192.168.2.234:443 check maxconn 5000
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.
Möchten Sie Let’s Encrypt Zertifikate nutzen, so installieren Sie die Let’s Encrypt Software certbot (URL):
apt install snapd snap install core && snap refresh core apt remove certbot snap install --classic certbot ln -s /snap/bin/certbot /usr/bin/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 Ihre Unterstützung (diese wird ordnungsgemäß versteuert!) würden sich meine Frau, meine Zwillinge und ich sehr freuen. Vielen Dank!