Project Rebel Homebase: Teil 4 - Der Takt der Rebellion

Ein Netzwerk ohne die korrekte Zeit ist wie ein Orchester ohne Dirigent. In Teil 4 bauen wir ein redundantes NTP-Cluster, lösen Port-Konflikte mit Pi-hole und spiegeln unsere Konfiguration per Gravity-Sync.

Project Rebel Homebase: Teil 4 - Der Takt der Rebellion

Es geht Schritt für Schritt voran in der Rebel Homebase. Im letzten Teil haben wir das Telefonbuch des Internets (DNS) erobert. Wir haben zwei Raspberry Pi Blades, die Werbung blocken, Tracker ins Leere laufen lassen und dank einer virtuellen IP (VIP) unzerstörbar scheinen. Das Netzwerk leuchtet grün. Alles gut?

Nein.

Denn ein Netzwerk ohne die korrekte Zeit ist wie ein Orchester ohne Dirigent. Wenn Server A denkt, es ist 12:00 Uhr, und Server B glaubt, es ist 12:05 Uhr, brechen verschlüsselte Verbindungen zusammen, Logs werden unlesbar und 2FA-Codes funktionieren nicht mehr.

Und da wir hier keine halben Sachen machen, reicht es uns nicht, einfach „irgendeinen Zeit-Server“ im Internet zu fragen. Wir bauen uns unsere eigene, hochverfügbare Atomuhr.
Der erste Plan war tatsächlich, ein DCF77-Modul zu nutzen, aber das wäre dann doch das sprichwörtliche AKW zum Betrieb von 2 LED's.

Was euch heute erwartet

Das Ziel: Ein NTP-Cluster, der auch dann die korrekte Zeit liefert, wenn ein Server brennt.
Der Endgegner: Windows, das keine Antworten von Fremden annimmt, und ein Pi-hole, das gierig Ports besetzt.
Zielgruppe: Leute, die sich gerne selbst Schmerzen zufügen, um am Ende stolz auf grüne Zahlen in der Konsole zu starren.

Dieser Guide baut direkt auf Teil 3 (DNS & Keepalived) auf. Wir nutzen dieselbe Hardware (Blade 01 & 02) und dieselbe virtuelle IP (192.168.1.5).


Warum wir das tun (Die Philosophie)

Jeder Computer hat eine Uhr. Aber Raspberry Pis haben keine Batterie (RTC) auf dem Mainboard. Wenn man den Stecker zieht, vergessen sie die Zeit und wachen im Jahr 1970 auf.
Standardmäßig nutzen viele Systeme time.windows.com oder pool.ntp.org, um das zu korrigieren.

Das Problem: Wir machen uns abhängig.
Die Lösung: Wir holen uns die Zeit von der PTB (Physikalisch-Technische Bundesanstalt) in Braunschweig. Das ist unsere offizielle Zeit für Deutschland. Man glaubt es kaum, gesetzlich festgelegt. Präziser wird es nicht. Und wir verteilen diese „Wahrheit“ von unserer eigenen VIP aus.


Schritt 1: Chrony – Der Zeitwächter

Warum installieren wir eigentlich etwas Neues? Ubuntu hat doch systemd-timesyncd an Bord?

Richtig, aber das ist nur ein Client („Wie spät ist es?“). Er kann die Zeit nicht an andere Geräte in deinem Netzwerk verteilen („Es ist genau 12 Uhr!“). Dafür brauchen wir einen Server.
Der klassische ntpd ist mittlerweile ein ziemlicher (ausentwickelter) Dinosaurier. Wir entscheiden uns für Chrony, und zwar aus zwei Gründen:

  1. Server-Fähigkeit: Chrony kann Zeit empfangen und senden.
  2. Stabilität: Chrony kommt extrem gut damit klar, wenn ein Rechner (wie unser Pi ohne Batterie) beim Booten eine völlig falsche Zeit hat, und korrigiert diese Abweichung schneller und intelligenter als der alte Standard.

Auf beiden Blades (dns1 und dns2) installieren wir ihn:

sudo apt update && sudo apt install -y chrony

Jetzt konfigurieren wir Chrony so, dass es nicht „irgendwen“ fragt, sondern die PTB. Wir nutzen dafür die moderne, modulare Config von Ubuntu 24.04.

Erstellt eine neue Datei für die Quellen:

sudo nano /etc/chrony/sources.d/ptb-braunschweig.sources

Inhalt (einfach Copy & Paste):

# Die Hüter der Zeit aus Braunschweig/Niedersachsen!
server ptbtime1.ptb.de iburst
server ptbtime2.ptb.de iburst
server ptbtime3.ptb.de iburst

Jetzt müssen wir Chrony noch sagen, dass es nicht nur ein Client ist, sondern auch den Server für unser Heimnetz spielen darf.

Ab in die Haupt-Config:

sudo nano /etc/chrony/chrony.conf

Sucht diese Zeilen und passt sie an (oder fügt sie hinzu):

# Erlaube dem gesamten Heimnetz, uns nach der Zeit zu fragen
allow 192.168.1.0/24

# WICHTIG: Lausche auf allen IPs (auch auf der VIP, wenn sie da ist)
bindaddress 0.0.0.0

Neustart:

sudo systemctl restart chrony

Ein kurzer Check mit chronyc sources -v sollte jetzt zeigen, dass wir mit Braunschweig verbunden sind (* vor dem Servernamen).


Schritt 2: Der verdeckte Saboteur (Pi-hole vs. Port 123)

Jetzt kommt der Teil, der mich Stunden gekostet hat. Eigentlich sollte alles laufen. Aber Chrony wollte einfach nicht starten oder nicht antworten.

Der Grund? Pi-hole. Pi-hole ist gierig. Obwohl es eigentlich ein DNS-Blocker ist, bringt seine Engine (FTL) auch Funktionen mit, um Zeit per DHCP zu verteilen. Und dafür krallt sich Pi-hole standardmäßig den Port 123.

Das ist wie ein Parkplatzstreit: Chrony will auf Port 123 parken, aber Pi-hole steht schon quer über zwei Plätze.

Die Lösung: Wir verbieten Pi-hole den Mund, was Zeit angeht.

Auf beiden Blades:

sudo nano /etc/pihole/pihole-FTL.conf

Fügt am Ende diese unscheinbare, aber mächtige und all meine Probleme lösende Zeile hinzu:

# Pi-hole, Finger weg vom NTP-Port!
NTP_SERVER=

(Ja, einfach leer lassen nach dem Ist-Gleich-Zeichen).

Dann Pi-hole neu starten, damit es den Port loslässt:

sudo systemctl restart pihole-FTL

Und jetzt Chrony neu starten:

sudo systemctl restart chrony

Prüft es mit sudo ss -tulpn | grep 123. Dort muss jetzt chronyd stehen. Nicht pihole-FTL.


Schritt 3: Die virtuelle IP als Absender (Der Endgegner)

Wir haben jetzt zwei funktionierende Zeitserver. Aber wir wollen ja einen Cluster. Die Clients sollen nur mit unserer VIP 192.168.1.5 reden.

Das Problem: Wenn ich (Client) die .5 nach der Zeit frage, nimmt der Server das Paket an. Aber Linux ist effizient. Linux antwortet oft über seine „echte“ physikalische IP (z.B. .3).

Das Szenario:

  1. Mein PC fragt .5: „Wie spät ist es?“
  2. Server antwortet als .3: „Es ist 12:00 Uhr.“
  3. Mein PC sagt: „Dich kenne ich nicht. Ich habe .5 gefragt.“ -> Verwirft das Paket.

Das Ergebnis unter Windows ist der Fehler 0x800705B4 (Timeout). Ich habe ihn hassen gelernt.

Die Lösung: Wir fälschen den Absender. Wir nutzen die Linux-Firewall (iptables), um jedes Zeit-Paket, das den Server verlässt, abzufangen und den Stempel „Absender: 192.168.1.5“ draufzukleben.

Auf beiden Blades ausführen:

# Installieren wir erst das Tool, damit die Regel Neustarts überlebt
sudo apt install -y iptables-persistent netfilter-persistent

# Die Magie: SNAT (Source Network Address Translation)
sudo iptables -t nat -A POSTROUTING -p udp --sport 123 -d 192.168.1.0/24 -j SNAT --to-source 192.168.1.5

# Regel speichern
sudo netfilter-persistent save

Jetzt denkt jeder Client, er redet wirklich nur mit der VIP. Windows ist glücklich. Wir sind glücklich.


Schritt 4: Keepalived beibringen, auf die Zeit zu achten

Unsere VIP soll nur auf einem Server liegen, wenn dort DNS UND NTP funktionieren. Wenn die Uhr steht, ist der Server kaputt.

Wir brauchen ein Check-Skript (/usr/local/bin/check_ntp.sh) auf beiden Nodes:

#!/bin/bash
# Prüft, ob Chrony läuft und synchron ist
chronyc tracking > /dev/null 2>&1
exit $?

(Ausführbar machen mit chmod +x nicht vergessen!)

sudo chmod +x /usr/local/bin/check_ntp.sh

Dann erweitern wir die /etc/keepalived/keepalived.conf:

vrrp_script chk_ntp {
    script "/usr/local/bin/check_ntp.sh"
    interval 2
    fall 2
    rise 2
}

vrrp_instance DNS_VIP {
    ...
    track_script {
        chk_dns
        chk_ntp   <-- Neu hinzugefügt!
    }
}

Damit schwenkt die VIP sofort auf den anderen Blade, wenn einer der beiden Dienste (DNS oder Zeit) hustet.


Schritt 5: Der finale Test

Das ist der Moment der Wahrheit. Geht an einen Windows-Rechner, öffnet die PowerShell als Admin und gebt ein:

# Cache leeren, damit Windows nicht alte Wege geht
arp -d 192.168.1.5

# Der Test gegen die VIP
w32tm /stripchart /computer:192.168.1.5 /dataonly /samples:5

Wenn ihr hier grüne Zahlen seht (+00.00xxx), dann habt ihr es geschafft. Eure Homebase tickt jetzt im Takt der Atomuhr, redundant verteilt über zwei Server, maskiert hinter einer virtuellen IP.

Letzter Schritt: UniFi Bescheid sagen

Damit ihr das nicht auf jedem Handy manuell einstellen müsst: Ab in die UniFi Dream Wall -> Networks -> DHCP Service Management. Tragt bei NTP Server die 192.168.1.5 ein.


Schritt 6: Gravity-Sync – Der digitale Spiegel (mit Hindernissen)

Wir haben jetzt zwei DNS-Server, die dank VIP nach außen hin wie eine Einheit wirken. Aber es gibt ein Problem: Wenn du auf dns1 einen Werbe-Eintrag auf die Whitelist setzt, weiß dns2 nichts davon. Ohne Synchronisation müsstest du jede Änderung doppelt pflegen. Das ist kein High-Availability-Setup, das ist Arbeitsbeschaffungsmaßnahme.

Hier kommt Gravity-Sync ins Spiel. Aber Vorsicht: Da das Projekt seit Juli 2024 offiziell archiviert ist, lauern hier ein paar Fallstricke, die mich einige Tassen Kaffee gekostet haben.

6.1 Die Installation (Der "Domain-Bypass")

Der offizielle Installationsbefehl über die Webseite des Entwicklers funktioniert nicht mehr, da die Domain abgeschaltet wurde. Wir müssen den professionelleren direkt über GitHub gehen.

Führt auf beiden Blades diesen Befehl aus:

curl -sSL https://raw.githubusercontent.com/vmstan/gs-install/main/gs-install.sh | bash

Im anschließenden Wizard gebt ihr jeweils die IP des anderen Partners an (auf dns1 die .4, auf dns2 die .3).

6.2 Fallstrick 1: Die "Permission Denied" Falle

Nach der Installation kam der erste Dämpfer: Ein gravity-sync compare quittierte den Dienst mit Permission denied beim Zugriff auf die Pi-hole Datenbank.

Die Lösung: Euer Benutzer (z. B. axel) braucht explizite Leserechte für die Pi-hole-Gruppe und die Datenbank-Datei.

Bash

# Benutzer zur Pi-hole Gruppe hinzufügen
sudo usermod -aG pihole $USER
# Rechte der Datenbank anpassen
sudo chmod 664 /etc/pihole/gravity.db

Wichtig: Einmal aus- und wieder einloggen, damit die Gruppenrechte greifen! Erst dann wird der compare mit grünen Häkchen belohnt.

6.3 Fallstrick 2: Wo ist mein Cronjob? (Systemd-Timer)

Frühere Versionen von Gravity-Sync nutzten den klassischen crontab. In der aktuellen Version 4.0.7 ist das anders. Wer mit crontab -l sucht, findet: nichts.

Gravity-Sync nutzt jetzt moderne systemd-timer. Nach dem Befehl sudo gravity-sync auto wird ein Hintergrunddienst erstellt, den ihr so überwacht:

# Prüfen, wann der nächste Sync geplant ist
systemctl list-timers | grep gravity-sync
# Die Live-Logs des Sync-Dienstes verfolgen
journalctl -u gravity-sync.service -f

6.4 Der erste Sync (The Push)

Wenn alles bereit ist, stoßt ihr den ersten Abgleich von eurem Master (dns1) an:

gravity-sync push

Sobald dort „No replication is required“ steht, sind beide PIs auf dem identischen Stand.


Fazit

War das alles nötig? Objektiv betrachtet: Wahrscheinlich nicht. time.windows.com funktioniert auch. Aber aus Sicht der digitalen Souveränität: Absolut.

Wir haben jetzt ein Fundament, das steht:

  1. Keine Abhängigkeit von Big-Tech-Zeitservern durch eigene NTP-Instanzen.
  2. Einen Port-Konflikt zwischen Chrony und Pi-hole gelöst, den kaum jemand dokumentiert hat.
  3. Den Linux-Kernel mit Firewall-Regeln ausgetrickst, um diverse Clients über eine VIP glücklich zu machen.
  4. Die „Source of Truth“ automatisiert: Dank Gravity-Sync bleiben unsere Blocklisten und DNS-Einträge zwischen den Blades synchron.
  5. Berechtigungshürden genommen: Wir haben gelernt, dass der Sync-User Zugriff auf die gravity.db benötigt, um MD5-Hashes vergleichen zu können.
  6. Den Zeitplan modernisiert: Wir nutzen nun systemd-timer statt alter Cronjobs für die Automatisierung der Replikation.

Das ist Overengineering in Reinkultur. Und es fühlt sich verdammt gut an.

Nächster Halt: (Vielleicht schon) Kubernetes auf ARM. Jetzt, wo das Fundament aus redundanter Namensauflösung, präziser Zeit und synchroner Identität steht, wird es Zeit für die echte Orchestrierung.

Subscribe to pandolin.io

Don’t miss out on the latest issues. Sign up now to get access to the library of members-only issues.
jamie@example.com
Subscribe