Der NTP Pool lebt von Freiwilligen, die ihre Server als öffentliche Zeitquelle zur Verfügung stellen. Da mein Netcup-VPS ohnehin schon per Chrony mit NTS gegen mehrere Stratum-1-Quellen synchronisiert, lag es nahe, ihn auch aktiv am Pool teilnehmen zu lassen. In diesem Artikel dokumentiere ich die Konfiguration und wie ich sie vor der Anmeldung getestet habe.
Ausgangslage
Mein VPS lief bereits mit folgender Chrony-Basiskonfiguration:
server time.cloudflare.com iburst nts
server ptbtime1.ptb.de iburst nts
server ptbtime2.ptb.de iburst nts
makestep 1.0 3
rtcsync
Damit synct der Server bereits stabil als Stratum-2-Client gegen Cloudflare und die PTB (Physikalisch-Technische Bundesanstalt). Für die Pool-Teilnahme fehlten aber zwei Dinge: eine allow-Freigabe für eingehende Client-Anfragen und ein Rate-Limiting gegen Missbrauch.
Chrony für Client-Anfragen öffnen
Ohne explizite allow-Direktive beantwortet Chrony ausschließlich die eigenen Upstream-Anfragen – eingehende Zeitanfragen von außen werden sonst stillschweigend verworfen. Ergänzt habe ich:
allow 0.0.0.0/0
allow ::/0
ratelimit interval 1 burst 8 leak 2
ratelimit begrenzt, wie oft eine einzelne IP innerhalb eines Zeitfensters beantwortet wird. Das ist vor allem wichtig, weil NTP historisch für Amplification-Angriffe missbraucht wurde (v. a. über das inzwischen aus modernem NTP entfernte monlist-Kommando). Mit Rate-Limiting bleibt der Server auch bei ungewöhnlichen Anfragemustern kontrollierbar.
Redundanz der Zeitquellen erhöhen
Mit nur drei Upstream-Quellen – davon zwei von derselben Institution – hat Chronys Selektionsalgorithmus wenig Spielraum, einen Ausreißer zuverlässig zu erkennen. Ergänzt habe ich einen echten Pool-Eintrag:
pool 2.de.pool.ntp.org iburst
minsources 2
Der Unterschied zwischen server und pool ist wichtig: server ist für einzelne, feste Hosts gedacht (wie die PTB-Server), pool für DNS-Namen, die zu mehreren wechselnden IPs auflösen – Chrony wählt sich daraus automatisch mehrere Quellen. minsources 2 verhindert außerdem, dass der Server synct, sobald nur noch eine einzige Quelle übrig ist – gerade als Pool-Teilnehmer sollte man selbst keine fehlerhafte Zeit weitergeben.
Die finale Konfiguration:
server time.cloudflare.com iburst nts
server ptbtime1.ptb.de iburst nts
server ptbtime2.ptb.de iburst nts
pool 2.de.pool.ntp.org iburst
server time1.uni-stuttgart.de iburst
server ntp1.rrze.uni-erlangen.de iburst
minsources 2
makestep 1.0 3
rtcsync
driftfile /var/lib/chrony/drift
bindcmdaddress ::1
allow 0.0.0.0/0
allow ::/0
ratelimit interval 1 burst 8 leak 2
Ein Hinweis zum Kommandoport (323): bindcmdaddress ::1 sorgt dafür, dass chronyc-Befehle nur lokal ausgeführt werden können. Eine zusätzliche cmdallow-Direktive, die den Kommandoport global öffnet, braucht es für die Pool-Teilnahme nicht – im Gegenteil, das wäre ein unnötiges Sicherheitsrisiko. Dazu findet man auch offizielle Konfigurationsempfehlungen.
Firewall-Freigaben
Auf Serverebene reicht eine einfache UFW-Regel:
sudo ufw allow 123/udp comment 'NTP Pool'
Bei einer vorgelagerten, zustandslosen (stateless) Firewall lohnt sich ein zweiter Blick: Hier ist es entscheidend, Port 123 als Destination Port für eingehenden Traffic freizugeben – nicht als Source Port. Eine Regel, die nur Pakete von Port 123 erlaubt, deckt lediglich die ausgehenden Antworten des eigenen Chrony-Clients an die Upstream-Server ab, nicht aber eingehende Anfragen von Pool-Clients. Das war bei mir tatsächlich die Ursache, warum externe Testanfragen zunächst ins Leere liefen, obwohl Chrony und UFW schon korrekt konfiguriert waren.
Testen vor der Anmeldung
Bevor man sich im Pool registriert, sollte der Server einige Tage im „Echtbetrieb“ laufen, um Stabilität zu verifizieren – der eigene Score im Pool baut sich ohnehin erst über mehrere Tage auf, ein Vortest kostet also praktisch keine Zeit.
Lokale Diagnose auf dem VPS:
sudo ss -ulnp | grep :123 # Lauscht Chrony auf allen Interfaces?
sudo chronyc tracking # Aktueller Stratum, Offset, Skew
sudo chronyc sourcestats # Stabilität der einzelnen Quellen über Zeit
sudo chronyc clients # Eingehende Client-Anfragen, inkl. Drop-Zähler
Ein Mitschnitt mit tcpdump hilft, um zu sehen, ob externe Testpakete überhaupt am Server ankommen – nützlich, um zu unterscheiden, ob ein Problem an einer vorgelagerten Firewall liegt oder an der Chrony-Konfiguration selbst:
sudo tcpdump -ni any udp port 123 -v
Verbindungstest von einem externen Windows-Rechner:
Da ntpdate unter Windows nicht verfügbar ist, eignet sich das Bordmittel w32tm:
w32tm /stripchart /computer:<vps-ip> /samples:3 /dataonly /packetinfo
Ein Timeout-Fehler (0x800705B4) bedeutet, dass zwar ein Anfrage-Paket verschickt wurde, aber keine Antwort zurückkam – typisches Symptom, wenn entweder allow in Chrony fehlt oder eine Firewall (lokal oder vorgelagert) die Antwort blockiert. Sobald alles korrekt konfiguriert ist, liefert der Befehl statt eines Timeouts einen echten Zeit-Offset-Wert zurück.
Auf Last vorbereitet sein:
Ressourcentechnisch ist NTP-Traffic auch bei mehreren hundert gleichzeitigen Clients auf einem kleinen VPS kein Thema – ein einzelnes UDP-Paket wird in Mikrosekunden verarbeitet, CPU- und RAM-Last bleiben verschwindend gering. Relevanter ist das monatliche Traffic-Volumen, das im NTP-Pool-Panel selbst über ein einstellbares Limit gesteuert wird.
Ergebnis
Nach ein paar Tagen Testbetrieb zeigte chronyc tracking einen System-Offset im niedrigen Mikrosekundenbereich und einen stabilen Skew unter 0,25 ppm – solide Werte für die Anmeldung im Pool. Der eigentliche Fehler auf dem Weg dorthin war unspektakulär, aber lehrreich: eine vertauschte Source-/Destination-Port-Regel in der vorgelagerten Firewall, die sich erst durch den kombinierten Einsatz von tcpdump auf dem Server und w32tm von außen sauber eingrenzen ließ.
Anmeldung am NTP Pool
Über den Link findet man weitere Informationen und kann dort einen Account anlegen seine Server eintragen. Anschließend werden die Server gemonitored und sobald sie Score >10 erreichen, werden sie in das Cluster mit aufgenommen. Bei mir hat das ca. 6-7h gedauert. Dabei wird IPv4 und IPv6 unterschiedlich bewertet.
Meine öffentliche Profilseite.
Ein Einblick in die Anzahl der Requests findet man im Statistikdashboard


