AdGuard hochverfügbar planen und einrichten

Letzte Aktualisierung am 01.01.2023, 14:01:18 Uhr

Immer wieder werde ich gefragt, ob es möglich ist AdGuard redundant aufzubauen, so dass bei Wartungsarbeiten und Ausfällen weiterhin die Funktionsfähigkeit für die Endanwender gegeben ist.

Bei meinen Recherchen bin ich über ein Issue auf GitHub des Projekts gestolpert. Das Thema beschäftigt die Nutzer bereits über drei Jahre. Bisher hat die Entwicklung von AdGuard keine Möglichkeit implementiert, um out of the Box High availability (HA) zu realisieren. Genau an diesem Punkt setzt ein Kommentar in dem Issue an, dessen Idee ich aufgegriffen habe.

Design/Aufbau

Nachstehend ein Schaubild meines Testsaufbau.

Die von mir entworfene Lösung ist skalierbar. Das heißt es spielt keine Rolle, ob der „Cluster“ aus zwei oder vier Servern besteht.

Wichtig ist, dass die Server untereinander über SSH (Port 22/tcp) erreichbar sind. Des weiteren verfügen alle Server über öffentliche IPv4 und IPV6 Adressen. Somit ist gewährleistet, dass für die Bereitstellung von SSL-Zertifikaten Let’s Encrypt (LE) verwendet werden kann. Somit entstehen keine weiteren jährlichen Kosten.

Bei der Namensgebung habe ich mich dafür entschieden, dem Cluster einem eindeutigen Namen (FQDN) zu geben. In diesem Beitrag ist es „adguard04.wydler.eu“. Der FQDN ist relevant, sobald

A) die Endgeräte DoH nutzen sollen. Denn damit ist der Einsatz eines Load Balancers sinnvoll, der über einem aussagekräftige DNS-Namen ansprechbar sein. Der Load Balancer wird auf Layer 4 konfiguriert. Sprich die Anfragen werden an die Backend Server weitergeleitet.

B) Ein Load Balancer eingesetzt wird, der sowohl mit TCP als auch UDP umgehen kann (z.B. Microsoft Azure Traffic Manager.

Die vermeidlichen Mitglieder des Clusters „adguard04.wydler.eu“ habe ich das Namensschema weitergeführt. D.h. deren Servernamen beginnt mit „adguard04“. Am Ende hänge ich einfach fortlaufend einen Buchstaben des Alphabets an (siehe Schaubild). Somit kann ein Cluster aus maximal 26 Servern (A bis Z) bestehen. Das sollte für die meisten Umgebungen ausreichen. 😉

Der Server mit dem Buchstaben „a“ am Ende ist zugleich der Master innerhalb des Clusters. Alle dort getätigten Konfigurationen in der Weboberfläche von AdGuard werden auf allen anderen Server repliziert.

Grundinstallation

Für meinen Testaufbau habe ich zwei Server in der Hetzner Cloud gemietet. Diese heißen adguard04a.izbw.de und adguard04b.izbw.de. Das anschließend die notwendigen DNS-Einträge (A, AAAA sowie PTR) konfiguriert werden müssen, sehe ich als selbstverständlich an.

Nachstehend die verschiedenen Konfiguration für meinen Testaufbau. Dazu nenne ich vorweg immer den Servernamen, auf denen die Befehle ausgeführt werden müssen.

Node adguard04a

Betriebssystem und die installierten Pakete auf aktuellen Stand bringen.

apt update && apt upgrade -y

SSH Schlüssel erzeugen. Öffentlicher Schlüssel auf anderen Node übertragen.

ssh-keygen -t ed25519
ssh-copy-id root@adguard04b.wydler.eu
ssh root@adguard04b.wydler.eu

Abruf eines SSL-Zertifikat von Let’s Encrypt.

apt install certbot -y
certbot certonly --standalone -d adguard04a.wydler.eu -d adguard04.wydler.eu --agree-tos --cert-name adguard04.wydler.eu -m webmaster@wydler.eu --rsa-key-size 4096 --dry-run

Installation von AdGuard.

curl -sSL https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/scripts/install.sh | sh

Danach die Weboberfläche von AdGuard aufrufen und den Einrichtungsassistent ausführen (http://adguard04a.wydler.eu:3000). Dies ist erforderlich, dass die Konfigurationsdatei angelegt wird.

Konfiguration der Firewall.

ufw allow ssh
ufw allow 53/tcp
ufw allow 53/udp
#ufw allow 67/udp
#ufw allow 68/udp
ufw allow http
ufw allow https
ufw allow 853/tcp

echo y | ufw enable
ufw status

Node adguard04b

apt update && apt upgrade -y

Abruf eines SSL-Zertifikat von Let’s Encrypt.

apt install certbot -y
certbot certonly --standalone -d adguard04b.wydler.eu --agree-tos --cert-name adguard04.wydler.eu -m webmaster@wydler.eu --rsa-key-size 4096 --dry-run

Installation von AdGuard.

curl -sSL https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/scripts/install.sh | sh

Konfiguration der Firewall.

ufw allow ssh
ufw allow 53/tcp
ufw allow 53/udp
#ufw allow 67/udp
#ufw allow 68/udp
ufw allow http
ufw allow https
ufw allow 853/tcp

echo y | ufw enable
ufw status

Einrichtung der Replikation

Alle nachstehenden Befehle werden auf dem Node adguard04a.wydler.eu ausgeführt.

Installation von lsyncd.

apt install lsyncd -y
lsyncd --version

Verzeichnisse für Konfiguration und Logfiles anlegen.

mkdir /etc/lsyncd
mkdir -p /var/log/lsyncd/

Konfiguration von logrotate für die Logfiles einrichten.

cat << EOF > /etc/logrotate.d/lsyncd
/var/log/lsyncd/lsyncd.log /var/log/lsyncd/lsyncd.status
{
  rotate 7
  daily
  compress
  missingok
  notifempty
  postrotate
  systemctl restart lsyncd.service > /dev/null
  endscript
}
EOF

Konfiguration für die Replizierung der Konfigurationsdatei von AdGuard sowie Neustart des Dienstes auf allen Servern.

TAB="$(printf '\t')"
cat > /etc/lsyncd/lsyncd.conf.lua << EOF
settings {
${TAB}logfile = "/var/log/lsyncd/lsyncd.log",
${TAB}statusFile = "/var/log/lsyncd/lsyncd.status",
${TAB}statusInterval = 20,
${TAB}maxProcesses = 1,
${TAB}maxDelays = 30,
${TAB}insist = false
}

targetList = {
${TAB}"adguard04b.wydler.eu"
}

for _, servers in ipairs(targetList) do
${TAB}sync {
${TAB}${TAB}default.rsyncssh,
${TAB}${TAB}source="/opt/AdGuardHome/",
${TAB}${TAB}host=servers,
${TAB}${TAB}targetdir="/opt/AdGuardHome",
${TAB}${TAB}delete='startup',
${TAB}${TAB}delay = 5,
${TAB}${TAB}rsync = {
${TAB}${TAB}${TAB}owner = false,
${TAB}${TAB}${TAB}group = false,
${TAB}${TAB}${TAB}perms = false,
${TAB}${TAB}${TAB}rsh = "/usr/bin/ssh -p 22 -o StrictHostKeyChecking=no",
${TAB}${TAB}${TAB}_extra = {
${TAB}${TAB}${TAB}${TAB}"--include=AdGuardHome.yaml",
${TAB}${TAB}${TAB}${TAB}"--exclude=*",
${TAB}${TAB}${TAB}},
${TAB}${TAB}},
${TAB}${TAB}ssh = {
${TAB}${TAB}${TAB}port = 22
${TAB}${TAB}}
${TAB}}

${TAB}sync {
${TAB}${TAB}default.rsyncssh,
${TAB}${TAB}source = "/opt/AdGuardHome/",
${TAB}${TAB}targetdir = "/opt/AdGuardHome",
${TAB}${TAB}host = servers,
${TAB}${TAB}delay = 10,
${TAB}${TAB}rsync = {
${TAB}${TAB}${TAB}binary = "/opt/AdGuardHome/AdGuardRestart.sh",
${TAB}${TAB}${TAB}_extra = {
${TAB}${TAB}${TAB}${TAB}(""..servers),
${TAB}${TAB}${TAB}}
${TAB}${TAB}}
${TAB}}
end
EOF

Bei dem Abschnitt targetlist müssen alle weiteren Server mit Komma (,) hinzugefügt werden. Mit dem delay in jedem Abschnitt wird sichergestellt, dass alle Änderungen in der Weboberfläche nicht sofort repliziert werden. Gerade auf Hinblick, dass einige Änderungen sofort in die Konfigurationsdatei geschrieben werden ohne das diese explizit über eine Schaltfläche „Speichern“ erst geschrieben werden. Somit soll einer fehlerhaften Konfiguration auf allen Nodes vorgebeugt werden.

Erstellen des Bash-Skripts für den Neustart des AdGuard Dienstes auf dem Zielserver.

cat << \EOF > /opt/AdGuardHome/AdGuardRestart.sh
#!/bin/bash

if [[ $2 =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
  ip=$2
else
  ip=$4
fi

ssh root@$ip "/opt/AdGuardHome/AdGuardHome -s restart"
EOF

Das Skript ist unerlässlich. Denn damit die neue Konfiguration auf den anderen Nodes wirksam wird, muss der Dienst von AdGuard neu gestartet werden.

Berechtigungen des Bash-Skripts anpassen.

chmod 755 /opt/AdGuardHome/AdGuardRestart.sh

Dienst aktivieren, starten und Status prüfen.

systemctl enable lsyncd
systemctl restart lsyncd
systemctl status lsyncd

Logfile von lsyncd fortlaufend ausgeben.

tail -f /var/log/lsyncd/lsyncd.log

Das Ganze ist natürlich skalierbar. D.h. ob es nun 2, 5 oder 10 Server sind, spielt keine Rolle. Das Schema bleibt immer das Selbe.

Die Grundkonfiguration von AdGuard habei ich im Artikel AdGuard Home im Netzwerk implementieren für euch dokumentiert.

Viel Spaß beim Ausprobieren.

Abonnieren
Benachrichtige mich bei
2 Comments
neueste
älteste
Inline Feedbacks
View all comments
Jascha
01.07.2023 21:26

Hi,

ich bekommen wenn ich das RestartScript nutze eine Fehlermeldung das der Hostnae nicht erreichbar ist oder nicht gefunden werden kann.
Ich verstehe das Script auch nicht wirklich. Woher zb. bezieht er den die IP Adresse? Von wo werden den die Variablen $2 und $4 gefüllt?

Grüße

Tobi
16.09.2022 23:00

Danke sieht interessant aus.
Wer die Server nicht öffentlich verfügbar machen möchte, kann sich mal Lego anschauen. Läuft bei mir wunderbar in der lokalen Domäne 🙂
https://github.com/go-acme/lego

P.S. Tollen Blog hast du da. Bin letzte Woche drauf gestoßen und habe dann gesehen, dass wir uns ja von früher kennen 😉