Uhren-Desynchronisation und Proxy-Ausfälle: TLS, JWT, Request-Signaturen und Diagnose in einer Minute
Inhalt des Artikels
- Einleitung: „gestern lief es, heute ist das zertifikat ungültig“
- Grundlagen: wo zeit kritisch ist und warum protokolle überhaupt auf die uhr schauen
- Deep dive: wessen uhr zählt in der kette client – proxy – server
- Wie der fehler in verschiedenen clients aussieht: texte, die du ab zeile eins erkennen musst
- Warum uhren weglaufen: virtualisierung, container, fehlender daemon, manueller eingriff
- Diagnose in einer minute: vergleich mit externer quelle und akzeptable abweichung
- Synchronisation einrichten: systemd-timesyncd und chrony, und wie du sicherstellst, dass es wirklich läuft
- Container und uhren: was vom host geerbt wird und was nicht
- Typische fehler und fälle aus der praxis
- Tools und checkliste „bevor du den proxy beschuldigst“
- Faq: häufige fragen zu zeit, proxy und ausfällen
- Fazit: zeit als erster diagnosepunkt
Einleitung: „Gestern lief es, heute ist das Zertifikat ungültig“
Es gibt eine Klasse von Vorfällen, die sich besser tarnt als jede andere. Ein Skript, das monatelang über einen Proxy Dutzende APIs angefragt hat, fällt plötzlich bei jeder Anfrage um. Im Log steht certificate has expired oder certificate is not yet valid. Der Engineer öffnet das Zertifikat der Zielseite im Browser auf seinem Laptop: Gültigkeitsdauer in Ordnung, Vertrauenskette grün. Er startet das Skript ohne Proxy von seinem Laptop neu – läuft. Proxy wieder rein – wieder Fehler. Die Schlussfolgerung drängt sich auf: Der Proxy ist schuld.
Und diese Schlussfolgerung ist fast immer falsch. Es liegt nicht daran, dass der Proxy „das Zertifikat austauscht“ oder „TLS kaputt macht“. Es liegt daran, dass das Skript auf einer anderen Maschine läuft – auf einem Server, in einer VM, in einem Container – und auf dieser Maschine die Systemuhr verrutscht ist. Um ein paar Minuten, Stunden oder Tage. Der Proxy war einfach die auffälligste Variable in der Gleichung und hat den Treffer eingesteckt.
Wir nehmen dieses Thema komplett auseinander. Du erfährst, an welchen Stellen moderne Protokolle auf die aktuelle Zeit angewiesen sind und warum ein Uhrversatz in unterschiedliche Richtungen unterschiedliche Symptome erzeugt. Du siehst konkrete Fehlertexte in curl, Python und Node.js, damit du das Problem schon an der ersten Zeile des Traces erkennst. Du verstehst, warum Uhren gerade auf virtuellen Maschinen und in Containern weglaufen und warum ein installierter Synchronisationsdienst nicht garantiert, dass er auch arbeitet. Du bekommst eine Ein-Minuten-Diagnose, funktionierende Konfigurationen für chrony und systemd-timesyncd und eine Checkliste, die du abarbeitest, bevor du den Proxy-Anbieter anschreibst.
Grenzen gleich vorweg: Diese Artikel handelt nicht von Zertifikatsausstellung, nicht von Vertrauensketten und nicht von Root-Stores. Wir betrachten genau eine Ursache für Ausfälle – falsche Systemzeit – und alles, was daraus im Betrieb über einen Proxy folgt. Das ist ein schmales Thema, aber genau das verbrennt Stunden Debugging bei Teams, die Integrationen, Monitoring, Datenerhebung und Automatisierung über Proxy-Infrastruktur wie Proxeon betreiben.
Grundlagen: Wo Zeit kritisch ist und warum Protokolle überhaupt auf die Uhr schauen
Fangen wir mit einer einfachen Frage an. Warum müssen Netzwerkprotokolle wissen, wie spät es gerade ist? Die Antwort ist immer dieselbe: um die Lebensdauer von Vertrauen zu begrenzen. Jedes Geheimnis – Zertifikat, Token, Signatur, Einmalcode – kann irgendwann leaken. Wenn es eine Ablaufzeit hat, ist der Schaden eines Leaks begrenzt. Aber eine Ablaufbegrenzung funktioniert nur, wenn die prüfende Seite die aktuelle Zeit kennt. Und sie nimmt sie von genau einer Stelle – von der Systemuhr der Maschine, auf der die Prüfung läuft.
Daraus folgt etwas Grundsätzliches: Jede Zeitprüfung ist lokal. Der Server fragt den Client nicht, wie spät es ist. Der Client fragt den Server nicht. Jeder schaut auf seine eigene Uhr und zieht seine Schlüsse. Wenn die Uhren zweier Seiten auseinanderlaufen, sehen sie unterschiedliche Realitäten, und eine hält die andere für einen Regelbrecher.
Prüfung der Gültigkeitsdauer von Zertifikaten in TLS
Jedes X.509-Zertifikat enthält zwei Felder: notBefore (gültig ab) und notAfter (gültig bis). Beim TLS-Handshake vergleicht der Client die aktuelle Zeit seiner Uhr mit diesem Intervall. Der Ausstellungszeitpunkt des Zertifikats auf dem Server spielt dabei keine Rolle – wichtig ist nur, was der Client für die aktuelle Zeit hält.
Die Richtung des Versatzes bestimmt das Symptom. Wenn die Uhr des Clients nachgeht, wirken frisch ausgestellte Zertifikate, als wären sie noch nicht ausgestellt: Fehler „certificate is not yet valid“. Wenn die Uhr des Clients vorgeht, wirken Zertifikate abgelaufen: „certificate has expired“. Das ist das erste diagnostische Merkmal, das du dir merken solltest.
Warum spitzt sich das Thema gerade jetzt zu? Weil Zertifikate immer kurzlebiger werden. Viele öffentliche Zertifizierungsstellen stellen Zertifikate mit 90 Tagen Laufzeit aus, es gibt Varianten mit 6 Tagen, und Branchenanforderungen verkürzen die Höchstlaufzeit systematisch: ab März 2026 auf 200 Tage, danach 100 Tage, bis 2029 dann 47 Tage. Früher trafen um zwei Wochen nachgehende Uhren fast nie auf ein frisches Zertifikat. Heute kann eine Zielseite alle paar Tage ein neues Zertifikat ausstellen, und eine Maschine mit drei Tagen Rückstand weist es als „noch nicht gültig“ zurück.
Zur selben Gruppe gehören verwandte Mechanismen: OCSP-Antworten und Sperrlisten haben eigene Gültigkeitsfenster, Certificate-Transparency-Einträge enthalten Zeitstempel, und TLS-1.3-Sessiontickets leben nur begrenzt. All das wird anhand der lokalen Uhr geprüft.
JWT: Felder exp, nbf und iat
JSON Web Token ist das verbreitetste Format für Access-Token in APIs. Im Body stecken drei Zeitfelder im Unix-Epoch-Format: iat (Ausstellung), nbf (not before – ab wann gültig) und exp (Ablauf). Die empfangende Seite vergleicht sie mit ihrer Uhr, meist mit kleiner Toleranz (leeway) von 30–60 Sekunden.
Hier entsteht eine interessante Asymmetrie. Normalerweise stellt der Server das Token nach seiner Uhr aus, und der Client cacht es bis zum Erreichen von exp – aber nach seiner Uhr. Geht der Client fünf Minuten nach, hält er das Token noch fünf Minuten für lebendig, nachdem der Server es bereits ablehnt. Du bekommst unerklärliche 401er am Ende der Lebensdauer jedes Tokens. Wenn der Client selbst JWTs signiert – etwa zur Authentifizierung eines Service-Accounts – und seine Uhr vorgeht, sieht der Server iat in der Zukunft und weist das Token als noch nicht gültig zurück.
Request-Signaturen mit Zeitstempel
Viele APIs verlangen HMAC-signierte Requests, wobei die aktuelle Zeit in den signierten String einfließt. Das bekannteste Beispiel ist das Schema Signature Version 4, das S3-kompatible Objektspeicher verwenden. Der Server akzeptiert den Request, wenn sein Zeitstempel nicht mehr als 15 Minuten von der Serveruhr abweicht; sonst gibt es RequestTimeTooSkewed. Payment-Gateways, Börsen- und Broker-APIs, unternehmensinterne Databuses nutzen ähnliche Schemata mit Fenstern von 30 Sekunden bis zu einigen Minuten. Kerberos-Authentifizierung in Unternehmensnetzen toleriert standardmäßig 5 Minuten Abweichung.
Beachte: Die Signatur bildet dein Client nach seiner Uhr. Der Proxy ist daran nicht beteiligt – physisch nicht möglich, er reicht nur Bytes weiter. Ein Fehler „ungültiger Zeitstempel in der Signatur“ über einen Proxy ist also immer eine Frage der Uhr deiner Maschine.
TOTP-Einmalcodes
Zwei-Faktor-Codes nach dem TOTP-Standard werden aus einem gemeinsamen Secret und der Nummer des 30-Sekunden-Intervalls berechnet. Die prüfende Seite erlaubt üblicherweise plus/minus ein Intervall. Wenn deine Uhr um eine Minute wegläuft, werden die Codes gar nicht mehr akzeptiert. Das betrifft auch eigene Integrationen, bei denen ein Service-Account TOTP programmatisch zum Login in ein Firmenportal erzeugt.
Weniger offensichtliche Zeitkonsumenten
- HTTP-Caching. Header Date, Expires und max-age werden mit der lokalen Uhr verglichen; bei Versatz funktioniert der Cache entweder nicht oder liefert Veraltetes.
- Cookies mit Expires. Ein Client mit nachgehender Uhr hält sie länger, mit vorgehender wirft er sie sofort weg.
- HSTS und Pinning. Auch max-age-Direktiven werden lokal gerechnet.
- Zertifikatsrotation innerhalb von Clustern. Komponenten von Orchestratoren stellen Zertifikate nach Zeitplan neu aus und vertrauen sich bei Uhrversatz gegenseitig nicht mehr.
Deep Dive: Wessen Uhr zählt in der Kette Client – Proxy – Server
Jetzt legen wir dieses Bild auf die reale Topologie. Beim Arbeiten über einen Proxy gibt es in der Kette mindestens drei Maschinen mit drei Uhrsätzen: deinen Client, den Proxy-Server und den Zielserver. Die entscheidende Diagnosefrage lautet: Auf welcher davon passiert die Prüfung, die fehlschlägt?
HTTP CONNECT und SOCKS5: Der Proxy sieht kein TLS
Bei einer Anfrage an eine HTTPS-Ressource über einen HTTP-Proxy schickt der Client eine CONNECT-Anweisung mit Hostname und Port. Der Proxy baut eine TCP-Verbindung zum Ziel auf und reicht danach blind Bytes in beide Richtungen durch. Der TLS-Handshake passiert durch den Proxy hindurch, zwischen deinem Client und dem Zielserver. Der Proxy sieht das Zertifikat nicht, kann es nicht verändern und ist an der Prüfung nicht beteiligt. Dasselbe gilt für SOCKS5.
Praktische Schlussfolgerung: Der Fehler „certificate is not yet valid“ oder „certificate has expired“ kann bei einem transparenten Tunnel physisch nicht durch den Proxy verursacht werden. Quelle ist entweder die Uhr deiner Maschine, der Store vertrauenswürdiger Roots auf ihr oder tatsächlich ein abgelaufenes Zertifikat am Ziel. Die ersten beiden Varianten sind lokal. Und die erste prüft man in zehn Sekunden.
Wenn mehr Uhren geprüft werden: TLS bis zum Proxy selbst
Wenn du dich mit dem Proxy über einen geschützten Kanal verbindest (Schema https:// für den Proxy selbst), prüft der Client zwei Zertifikate: das Zertifikat des Proxy-Servers und das der Zielseite. Beide Prüfungen laufen nach der Uhr des Clients. Die Symptomatik ist dieselbe, aber in einer ausführlichen curl-Ausgabe siehst du, an welcher Stelle – „Proxy certificate“ oder „Server certificate“ – der Fehler auftrat.
Was von der Uhr des Proxys selbst abhängt
Die Uhr des Proxy-Servers beeinflusst nur ein begrenztes Set: die Korrektheit seiner eigenen Logs, den Betrieb seines TLS-Listeners (etwa das Einholen von OCSP-Antworten) sowie die Prüfung von Autorisierungsdaten, falls der Anbieter Tokens mit begrenzter Laufzeit verwendet. Die Infrastruktur von Proxeon synchronisiert die Zeit auf ihren Knoten regulär, und in der überwiegenden Mehrheit der Vorfälle ist die Proxy-Uhr unbeteiligt: Sie steckt weder in der Prüfung des Zielzertifikats noch in der Bildung deiner Signaturen noch in der Validierung deiner JWTs. All das machen Client und Zielserver.
Die Uhr des Zielservers
Gelegentlich läuft die Uhr auf der anderen Seite weg. Erkennungszeichen: Fehler treten nur mit einem Host auf, während andere normal erreichbar sind; der Date-Header in der Antwort dieses Hosts weicht merklich von dem anderer Server ab. Dagegen kannst du nichts tun, außer den Betreiber zu informieren und, falls die API es erlaubt, die Abweichung in deinen Requests zu kompensieren – manche Bibliotheken können aus dem Date-Header einer Fehlerantwort einen Korrekturwert ableiten und den nächsten Request mit Versatz signieren.
Latenz und „Verfall in der Warteschlange“
Der Proxy fügt Verzögerung hinzu – von Dutzenden Millisekunden bis Sekunden bei Reconnects mobiler Kanäle und IP-Rotation. Für ein 15-Minuten-Fenster ist das vernachlässigbar. Aber es gibt eine feine Falle in der Wiederholungslogik. Wenn du einen Request signierst, ihn in eine Queue legst und erst nach einigen Minuten mit Retry-Wartezeiten absendest, veraltet der Zeitstempel in der Signatur. Über einen langsamen oder überlasteten Kanal passiert das häufiger. Regel: Signiere erst unmittelbar vor dem Absenden und signiere bei jedem Retry neu, statt ein fertig signiertes Objekt wiederzuverwenden.
Versatz: Größe und Richtung
Lass uns die Beziehung Symptom zu Versatz in einer listenartigen Tabelle sammeln – die nützt beim Lesen von Logs:
- Client-Uhr geht nach: „certificate is not yet valid“, JWT mit Fehler nbf oder iat beim Selbstsignieren, eigene gecachte Token wirken lebendig, aber der Server antwortet 401 „expired“, Signaturen werden als zu alt abgelehnt, TOTP wird nicht akzeptiert.
- Client-Uhr geht vor: „certificate has expired“, eigene JWTs werden als iat aus der Zukunft abgelehnt, Signaturen als „aus der Zukunft“ abgelehnt, Token fliegen früher aus dem Cache, TOTP wird nicht akzeptiert.
- Versatz unter einer Sekunde: Keiner der genannten Mechanismen merkt es.
- Versatz 30 Sekunden – 5 Minuten: TOTP, JWT mit kleinem leeway, strenge HMAC-Schemata brechen.
- Versatz über 15 Minuten: Praktisch alle Request-Signaturen und Kerberos brechen.
- Versatz in Tagen: TLS-Verbindungen zu Seiten mit kurzlebigen Zertifikaten beginnen zu scheitern.
Wie der Fehler in verschiedenen Clients aussieht: Texte, die du ab Zeile eins erkennen musst
Einen Fehler am Text zu erkennen spart mehr Zeit als jede Methodik. Unten stehen typische Formulierungen für verbreitete Clients. Sie sind so wiedergegeben, wie sie Bibliotheken mit OpenSSL unter der Haube ausgeben; Zeilennummern in Klammern variieren von Version zu Version.
curl
Basis-Meldungen bei falscher Zeit:
curl: (60) SSL certificate problem: certificate is not yet validcurl: (60) SSL certificate problem: certificate has expiredCode 60 ist CURLE_PEER_FAILED_VERIFICATION, gemeinsam für alle Probleme der Zertifikatsprüfung. Eine zeitliche Ursache von einem Root-Problem unterscheidet man nur am Text nach dem Doppelpunkt. Mit Flag -v erscheint eine nützliche Zeile mit dem numerischen OpenSSL-Prüfcode: 9 steht für „not yet valid“, 10 für „has expired“:
* SSL certificate verify result: certificate is not yet valid (9), continuing anyway.Die Zeile „continuing anyway“ erscheint nur mit Flag -k; ohne wird die Verbindung abgebrochen. Auf Windows mit Schannel-Backend lautet der Text anders:
curl: (60) schannel: SEC_E_CERT_EXPIRED (0x80090328) - The received certificate has expired.Beispiel für eine vollständige Prüfung über den Proxy mit ausführlicher Ausgabe, um sowohl die Stelle (Proxy oder Ziel) als auch das Verifikationsergebnis zu sehen:
curl -v --proxy http://LOGIN:PASSWORD@PROXY_HOST:PORT https://example.com/ 2>&1 | grep -iE 'verify|expire|valid|date'Python: ssl, requests, httpx, aiohttp
Das Standardmodul ssl wirft eine sprechende Exception mit Suffix:
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: certificate is not yet valid (_ssl.c:1010)Die requests-Bibliothek verpackt sie in SSLError und ergänzt einen Connection-Pool-Wrapper, weshalb die Schlüsselwörter ganz am Ende einer langen Zeile stehen:
requests.exceptions.SSLError: HTTPSConnectionPool(host='example.com', port=443): Max retries exceeded with url: / (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: certificate has expired (_ssl.c:1010)')))Wichtiges Detail beim Arbeiten über einen Proxy: Bei einem CONNECT-Tunnel kommt der Zertifikatsfehler des Ziels als SSLError, nicht als ProxyError. ProxyError erscheint, wenn bereits die Verbindung zum Proxy selbst nicht klappt. Der Exception-Typ verrät also schon, wo du suchen musst.
httpx formuliert es kürzer:
httpx.ConnectError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: certificate has expired (_ssl.c:1010)aiohttp nutzt eine eigene Klasse:
aiohttp.client_exceptions.ClientConnectorCertificateError: Cannot connect to host example.com:443 ssl:True [SSLCertVerificationError: (1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: certificate is not yet valid (_ssl.c:1010)')]JWT-Fehler in PyJWT bei der Prüfung des Tokens auf deiner Seite:
jwt.exceptions.ExpiredSignatureError: Signature has expiredjwt.exceptions.ImmatureSignatureError: The token is not yet valid (nbf)Und hier ein minimal reproduzierbares Beispiel über einen Proxy, das du als Testfall parat haben solltest:
import requests, ssl, sys
PROXY = "http://LOGIN:PASSWORD@PROXY_HOST:PORT"
try:
r = requests.get("https://example.com/", proxies={"https": PROXY}, timeout=15)
print(r.status_code, r.headers.get("Date"))
except requests.exceptions.SSLError as e:
msg = str(e)
if "not yet valid" in msg or "has expired" in msg:
print("Sieht nach einem Systemuhr-Versatz aus, prüfe date -u")
print(msg)Kurze Anmerkung: Das Zeilenumbruch-Zeichen ist in diesem und den folgenden Beispielen als HTML-Entity notiert, damit das Markup einzeilig bleibt; beim Kopieren in den Editor wird daraus ein normaler Zeilenumbruch.
Node.js: https, fetch, undici
Node druckt kurze Meldungen und versieht den Fehler wertvollerweise mit einem code-Feld:
Error: certificate is not yet valid
code: 'CERT_NOT_YET_VALID'Error: certificate has expired
code: 'CERT_HAS_EXPIRED'Falle beim eingebauten fetch: Außen siehst du nur TypeError: fetch failed, und die eigentliche Ursache steckt in der Eigenschaft cause. Logge sie explizit:
try {
const res = await fetch("https://example.com/");
console.log(res.status, res.headers.get("date"));
} catch (e) {
console.error(e.message, e.cause && e.cause.code, e.cause && e.cause.message);
}Für Routing von fetch durch einen Proxy in Node nutzt man den ProxyAgent aus dem undici-Paket. Der Zertifikatsfehler des Ziels kommt dabei mit demselben Code CERT_HAS_EXPIRED oder CERT_NOT_YET_VALID in cause. Für das klassische https-Modul und Bibliotheken wie axios verwendet man einen Agent aus dem https-proxy-agent-Paket – die Fehlercodes sind dieselben, weil sie aus demselben OpenSSL in Node stammen.
Die jsonwebtoken-Bibliothek unterscheidet bei der Token-Prüfung zwei zeitliche Fälle:
TokenExpiredError: jwt expired
NotBeforeError: jwt not activeServerseitige Fehler bei Signaturen
Hier gibt der API-Betreiber den Text vor, aber die typischen Formulierungen sind wiedererkennbar. Bei S3-kompatiblen Speichern:
<Code>RequestTimeTooSkewed</Code>
<Message>The difference between the request time and the current time is too large.</Message>Bei OAuth-Servern der Header WWW-Authenticate mit error="invalid_token" und Beschreibung wie „The token expired“ oder „Token used before issued“. Bei Finanz-APIs Meldungen zu timestamp outside of recvWindow oder „Timestamp for this request is outside of the allowed window“. Wenn du irgendeine dieser Phrasen siehst, ist die Uhr das Erste, was du prüfen solltest.
Warum Uhren weglaufen: Virtualisierung, Container, fehlender Daemon, manueller Eingriff
Der Quarzoszillator auf dem Mainboard ist kein Normal. Seine typische Abweichung beträgt 10–100 ppm, also bis zu 1–9 Sekunden Drift pro Tag ohne Korrektur. Über einen Monat ohne Synchronisation sammeln sich Minuten. Das ist der Grundpegel, den jede Maschine hat. Die meisten Vorfälle entstehen aber nicht durch langsamen Drift, sondern durch Sprünge. Schauen wir uns die Quellen an.
Virtuelle Maschinen: Pause, Snapshot, Migration
Eine virtuelle Maschine besitzt keine physische Uhr. Sie bekommt die Zeit über eine paravirtualisierte Quelle (kvm-clock, Hyper-V reference time, Äquivalente bei anderen Hypervisoren). Wenn der Guest pausiert, in einen Snapshot gespeichert oder zwischen Hosts migriert wird, „friert“ seine interne Uhr ein. Nach dem Aufwachen erhält der Kernel entweder eine Korrektur vom Hypervisor oder nicht – abhängig von den Gast-Agent-Einstellungen. Klassisches Szenario: Das Team hat einen Testserver auf einen zwei Wochen alten Snapshot zurückgesetzt, und er wacht mit einer Uhr auf, die 14 Tage zurückliegt. Alle Zertifikate, die in diesen zwei Wochen ausgestellt wurden, sind für ihn „noch nicht gültig“.
Ein eigenes Problem ist der Konflikt zweier Wahrheiten. Der Gast-Agent des Hypervisors zieht die Zeit periodisch auf den Host, während im Gast ein eigener NTP-Daemon läuft. Ist der Host selbst nicht synchronisiert, bekommt der Gast zwei widersprüchliche Signale, und die Uhr kann vor- und zurückspringen. Empfehlung der meisten Virtualisierungs-Guides: entweder Synchronisation über den Hypervisor oder NTP im Gast, aber nicht beides gleichzeitig.
Entwickler-Laptops: Schlaf und Desktop-VMs
Docker Desktop und WSL2 arbeiten innerhalb einer leichten virtuellen Maschine. Nach Schlafmodus oder Hibernation des Laptops geht die Uhr dieser VM nicht selten genau um die Schlafdauer nach – eine Stunde, eine Nacht, ein Wochenende. Das Host-Betriebssystem zieht die Zeit nach, die VM oft nicht. Ergebnis: Am Montagmorgen fallen alle Skripte in Containern bei TLS um, während im Terminal des Hosts alles in Ordnung ist. Genau diese Situation erzeugt meist die Beschwerde „ohne Proxy vom Laptop geht's, über den Proxy im Container nicht“ – obwohl der Proxy in beiden Fällen an der Prüfung gar nicht beteiligt war.
Container
Ein Container hat keine eigene Echtzeituhr – er nutzt die Uhr des Hosts. Das schauen wir uns in einem eigenen Abschnitt an; hier halten wir fest: Ist der Host verrutscht, sind alle Container mit verrutscht, und von innen im Container reparieren lässt sich das nicht.
Fehlender oder nicht funktionsfähiger Synchronisationsdaemon
Minimale Images, „nackte“ Installationen und selbstgebaute Systeme kommen nicht selten ganz ohne NTP-Client. Und wo einer vorhanden ist, kann er aus einem Dutzend Gründen nicht arbeiten: ausgehendes UDP auf Port 123 ist per Firewall blockiert; Pool-Namen lösen nicht auf; der Dienst ist installiert, aber maskiert; chrony und timesyncd sind gleichzeitig installiert und behindern sich; der Versatz beim Start überschreitet die Schwelle, ab der klassisches ntpd die Korrektur verweigert und beendet. Besonders anfällig sind Server, deren einziger Weg ins Internet über einen HTTP-Proxy des Firmennetzes führt: NTP über UDP kommt durch einen solchen Proxy nicht durch, und die Uhr ist sich selbst überlassen.
Fehlende RTC und leere Batterie
Einplatinencomputer wie der Raspberry Pi und viele Mini-PCs haben keine Hardware-Uhr mit Batterie. Beim Boot starten sie entweder mit der Epoche oder mit der zuletzt gespeicherten Zeit und leben damit bis zur ersten erfolgreichen Synchronisation. Bei alten Servern geht die CMOS-Batterie leer, und nach jedem Ausschalten setzt das BIOS das Datum auf den Werkswert zurück. Ist die Synchronisation nicht erreichbar, läuft die Maschine mit einem Datum von vor zehn Jahren, und jede TLS-Verbindung ist zum Scheitern verurteilt.
Manuelle Korrektur und Zeitzonen-Verwirrung
Das Kommando date -s, das man „zum Testen der Hypothese“ ausführt und dann vergisst, ist ein häufiges Motiv. Ein weiteres ist Dual-Boot von Windows und Linux auf einer Maschine: Windows speichert in der Hardware-Uhr Lokalzeit, Linux erwartet standardmäßig UTC, und nach jedem Neustart ins andere System verschiebt sich die Zeit um die Zeitzonendifferenz – für Moskauer Zeit um drei Stunden. Und schließlich die Verwechslung von Zeit und Zeitzone: Das Ändern der Variable TZ oder der Datei /etc/localtime ändert die Anzeige, aber nicht die Systemzeit. Vergleichen musst du immer UTC.
Diagnose in einer Minute: Vergleich mit externer Quelle und akzeptable Abweichung
Ziel dieses Verfahrens ist, in 60 Sekunden eine Frage zu beantworten: Geht die Uhr auf der Maschine, von der die Requests über den Proxy laufen, richtig? Alle Kommandos werden auf genau der Maschine oder im genau dem Container ausgeführt, in dem der Client ausfällt – nicht auf deinem Laptop.
Schritt 1. Schau auf UTC und vergleiche mit einer vertrauenswürdigen Quelle
date -uVergleiche mit der Uhr eines Handys, das mit dem Mobilfunknetz synchronisiert ist. Eine Abweichung in Minuten fällt sofort auf. Für Sekunden brauchst du die folgenden Schritte.
Schritt 2. Hole die Zeit aus dem HTTP-Header Date über denselben Proxy
Jeder korrekte HTTP-Server liefert den Header Date nach seiner (meist synchronisierten) Uhr. Der Trick: Frage per einfachem HTTP, nicht HTTPS, denn bei starkem Versatz kommt TLS gar nicht zustande und du bekommst den Header überhaupt nicht:
date -u; curl -sI --proxy http://LOGIN:PASSWORD@PROXY_HOST:PORT http://example.com/ | grep -i '^date'Die beiden Zeilen sollten auf Sekundenbruchteile genau übereinstimmen – bis auf Sekundengranularität und Kanallatenz. Sieht du eine Minute oder mehr Unterschied, ist die Diagnose fertig. Dieser Kniff ist besonders wertvoll, weil er kein UDP braucht, durch jeden Proxy funktioniert und unabhängig von TLS ist.
Derselbe Trick in Python – praktisch als Healthcheck für einen Service, der über Proxeon oder einen anderen Proxy läuft:
import email.utils, time, requests
PROXY = "http://LOGIN:PASSWORD@PROXY_HOST:PORT"
r = requests.head("http://example.com/", proxies={"http": PROXY}, timeout=10)
server_ts = email.utils.parsedate_to_datetime(r.headers["Date"]).timestamp()
skew = time.time() - server_ts
print(f"Versatz der lokalen Uhr: {skew:+.1f} s")
if abs(skew) > 30:
raise SystemExit("Die Uhr ist verrutscht, Synchronisation erforderlich")Schritt 3. Frag das System, ob es sich für synchronisiert hält
timedatectlAchte auf zwei Zeilen: „System clock synchronized: yes“ und „NTP service: active“. Jedes „no“ oder „inactive“ ist ein Grund, in den Abschnitt zur Konfiguration zu gehen. Wenn chrony läuft:
chronyc trackingSchau auf „System time“ (Größe und Vorzeichen des Versatzes), „Stratum“ (darf nicht 0 sein) und „Leap status“ (sollte Normal sein, nicht „Not synchronised“).
Schritt 4. Einmalige Abfrage beim NTP-Server, ohne die Uhr zu ändern
Ist UDP nach außen offen, kannst du den Versatz exakt messen, ohne etwas zu ändern:
chronyd -Q 'pool pool.ntp.org iburst'Oder, falls ntpdate installiert ist:
ntpdate -q pool.ntp.orgDie Ausgabe zeigt den Offset in Sekunden mit Vorzeichen.
Schritt 5. Bestätige, dass wirklich die Zeit TLS zerstört
Das Tool openssl kann über einen HTTP-Proxy gehen und gibt das Verifikationsergebnis mit einem numerischen Code aus:
openssl s_client -connect example.com:443 -proxy PROXY_HOST:PORT -servername example.com </dev/null 2>/dev/null | grep 'Verify return code'„Verify return code: 9 (certificate is not yet valid)“ oder „10 (certificate has expired)“ bei einem lebenden Zertifikat sind ein erschöpfender Beweis. Beachte, dass s_client in den meisten Builds keine Proxy-Authentifizierung per Login und Passwort unterstützt; für diese Prüfung nutzt man bequem die IP-Autorisierung, die Proxeon unterstützt. Die Daten des Zertifikats selbst holst du so:
openssl s_client -connect example.com:443 -proxy PROXY_HOST:PORT -servername example.com </dev/null 2>/dev/null | openssl x509 -noout -datesWelche Abweichung gilt als normal
- Bis 100 Millisekunden – gesunde Maschine mit funktionierendem NTP. Nichts zu tun.
- 100 ms – 1 Sekunde – akzeptabel für alle hier behandelten Protokolle, deutet aber auf eine schlechte Quelle oder seltenes Polling hin. Konfiguration prüfen.
- 1 – 30 Sekunden – gelbe Zone. TLS und Signaturen mit breitem Fenster arbeiten noch, TOTP und JWT mit kleinem leeway können schon stolpern. Heute beheben.
- Über 30 Sekunden – rote Zone. Betrachte jeden Auth- oder TLS-Fehler als durch die Uhr verursacht, bis das Gegenteil bewiesen ist.
Wie du das Problem absichtlich reproduzierst
Um sicherzugehen, dass deine Fehlerbehandlung einen zeitlichen Ausfall korrekt erkennt, musst du die Systemuhr nicht anfassen. Die Bibliothek libfaketime ersetzt die Zeit für einen einzelnen Prozess:
faketime '2024-01-01 12:00:00' curl -sS https://example.com/ -o /dev/nullDu bekommst „certificate is not yet valid“ für jede Seite mit einem Zertifikat, das nach dem angegebenen Datum ausgestellt wurde. Für die Zertifikatsprüfung ohne Netzwerk unterstützt OpenSSL das Flag -attime mit Unix-Zeit, praktisch für Tests in der CI.
Synchronisation einrichten: systemd-timesyncd und chrony, und wie du sicherstellst, dass es wirklich läuft
Ein Paket installieren ist keine Synchronisation. Unten zwei funktionierende Schemata und, was wichtiger ist, die Methodik zur Verifikation.
Wahl: timesyncd oder chrony
systemd-timesyncd – minimaler SNTP-Client, der zu systemd gehört. Fragt einen Server aus der Liste ab, kann die Uhr per Sprung und sanft korrigieren, arbeitet nicht als Server, unterstützt keine Hardwarequellen. Ausreichend für Workstations und unkritische Server, wo Genauigkeit im Bereich Dutzender Millisekunden reicht.
chrony – vollwertige NTP-Implementierung. Kombiniert mehrere Quellen, konvergiert schnell nach langer Ausfallzeit, verhält sich bei unterbrochener Netzverbindung sauber, unterstützt NTS (NTP mit TLS-Authentifizierung) und kann Zeit im Netz verteilen. Das ist die Standardwahl für Server, virtuelle Maschinen und jeden Knoten, über den signierte Requests und Proxy-Arbeit laufen.
Wichtig: Zwei Daemons gleichzeitig sind eine Chaosquelle. Bei der Installation von chrony deaktivieren Distributionen timesyncd in der Regel, aber prüfe das explizit.
Variante A: systemd-timesyncd
Konfiguration in /etc/systemd/timesyncd.conf:
[Time]
NTP=ntp.your-provider.internal 0.pool.ntp.org 1.pool.ntp.org
FallbackNTP=2.pool.ntp.org 3.pool.ntp.org
PollIntervalMinSec=32
PollIntervalMaxSec=2048Aktivieren und prüfen:
sudo timedatectl set-ntp true
sudo systemctl restart systemd-timesyncd
timedatectl timesync-statusIn der Ausgabe von timesync-status sollte Server gefüllt sein, Stratum nicht 0, und das Feld Offset in Millisekunden stehen. Ist kein Server genannt oder steht „Packet count: 0“, kommen keine NTP-Antworten an – fast sicher ist UDP 123 dicht.
Variante B: chrony
Datei /etc/chrony/chrony.conf (auf manchen Systemen /etc/chrony.conf):
server ntp.your-provider.internal iburst prefer
pool pool.ntp.org iburst maxsources 4
driftfile /var/lib/chrony/drift
makestep 1.0 3
rtcsync
logdir /var/log/chronyLass uns die wichtigen Direktiven auseinandernehmen, denn genau sie bestimmen das Verhalten bei Ausfällen:
- iburst – sendet beim Start eine Serie von Anfragen hintereinander und synchronisiert in Sekunden, nicht in Minuten.
- makestep 1.0 3 – ist der Versatz größer als 1 Sekunde, wird in den ersten drei Updates ein Sprung erlaubt, danach nur sanftes Heranführen. Ohne diese Direktive wird chrony nach einem Snapshot oder Schlafmodus die Uhr wochenlang „nachziehen“. Für Server, die häufig schlafen oder migrieren, setzen manche makestep 1.0 -1, was den Sprung immer erlaubt.
- rtcsync – schreibt die Zeit regelmäßig in die Hardware-Uhr, damit der Start nach einem Reboot nahe am Wert liegt.
- Interner Provider-Server zuerst – Cloud-Plattformen stellen üblicherweise eine NTP-Adresse im internen Netz bereit; sie ist auch bei gesperrtem externen UDP erreichbar und hat minimale Latenz. Nimm die Adresse aus der Doku deines Providers.
Starten:
sudo systemctl enable --now chrony
sudo chronyc makestep
chronyc tracking
chronyc sources -vWie du erkennst, dass die Synchronisation wirklich arbeitet
Das ist der wichtigste Teil dieses Abschnitts. Anzeichen funktionierender Synchronisation in chrony:
- In chronyc tracking ist Reference ID nicht null und Stratum liegt bei einem typischen Server zwischen 2 und 4. Stratum 0 oder 10 mit leerer Reference ID bedeutet „Ich arbeite mit mir selbst“.
- Leap status: Normal. Der Wert „Not synchronised“ heißt: Die Uhr wird von niemandem korrigiert.
- System time zeigt Versatz in Millisekunden oder Mikrosekunden, nicht in Sekunden.
- In chronyc sources ist mindestens eine Quelle mit Sternchen (aktuell gewählt) oder Plus (an der Kombination beteiligt) markiert. Fragezeichen bei allen Quellen – Antworten kommen nicht an, such die Firewall oder DNS.
- Last offset und RMS offset werden mit der Zeit kleiner, nicht größer.
Für timesyncd ist die äquivalente Prüfung: „System clock synchronized: yes“ in timedatectl und ein Packet count ungleich null in timesync-status.
Wenn NTP per Firewall blockiert ist
Wenn auf dem Server nur ausgehendes HTTP(S) über einen Proxy erlaubt ist, hast du drei Wege. Erstens, bitte darum, UDP 123 zumindest zum internen NTP-Server zu öffnen; das ist eine normale und sichere Bitte an die Netzwerkadmins. Zweitens, synchronisiere dich mit dem Hypervisor oder Host, wenn es eine VM oder ein Container ist. Drittens, ein Kompromiss über das Tool htpdate, das die Uhr anhand der Date-Header von HTTP-Antworten stellt; die Genauigkeit liegt in der Größenordnung von Sekunden, für TLS, JWT und Request-Signaturen reicht das, und es kann über einen HTTP-Proxy arbeiten. Nicht die eleganteste, aber eine brauchbare Variante für isolierte Segmente.
Windows
Auf Windows-Hosts prüft man den Zeitdienst so:
w32tm /query /status
w32tm /resyncIn einer Domänenumgebung dient der Domain Controller als Quelle, außerhalb der Domäne ein konfigurierter NTP-Server. Das Feld Last Successful Sync Time muss frisch sein.
Container und Uhren: Was vom Host geerbt wird und was nicht
Ein Container ist eine Reihe isolierter Namespaces über einem gemeinsamen Kernel. Die Isolation betrifft Prozesse, Netzwerk, Dateisystem, Mountpoints. Aber die Echtzeituhr (CLOCK_REALTIME) wird nicht isoliert. Die mit Linux 5.6 eingeführten Zeit-Namespaces erlauben nur das Verschieben monotoner Uhren und der Zeit seit dem Boot – sie sind für die Migration von Containern gedacht und beeinflussen die Wanduhr nicht. Daraus ergeben sich einige harte Konsequenzen.
Was geerbt wird
- Die Kalenderzeit. Alle Container sehen exakt dieselbe Zeit wie der Host. Ist der Host verrutscht, sind alle mit verrutscht.
- Die Synchronisation. Ein auf dem Host laufendes chrony „repariert“ die Zeit automatisch in allen Containern.
- Sprünge. Korrigiert der Host die Uhr per Sprung, sehen Anwendungen in Containern diesen Sprung sofort.
Was nicht geerbt wird
- Die Zeitzone. Die Datei /etc/localtime und die Variable TZ leben im Image. In minimalen Alpine-Images kann das tzdata-Paket fehlen, und der Container zeigt immer UTC. Das ist kein Zeitfehler, sondern ein Anzeigefehler, aber er führt beim Lesen von Logs regelmäßig in die Irre.
- Der Store der Root-Zertifikate. Nicht Thema dieses Artikels, aber zur Erinnerung: Er liegt auch im Image.
- Das Recht, die Zeit zu ändern. Der Systemaufruf zum Setzen der Zeit benötigt CAP_SYS_TIME, über das ein Container standardmäßig nicht verfügt. Das Kommando date -s in einem Container schlägt mit „Operation not permitted“ fehl. Einen NTP-Daemon in einem normalen Container laufen zu lassen ist sinnlos, und ihm CAP_SYS_TIME zu geben ist schlechte Praxis: Er würde anfangen, die Uhr des gesamten Hosts zu steuern.
Praktische Konsequenzen
Die Diagnose aus dem vorigen Abschnitt kannst und solltest du im Container ausführen – date -u und die Abfrage des Date-Headers über den Proxy zeigen das reale Bild. Die Reparatur findet aber immer auf dem Host statt. Läuft der Container in Kubernetes, ist der relevante Host der konkrete Knoten, auf dem der Pod geplant ist; stelle sicher, dass chrony im Basis-Image der Knoten enthalten ist und dass das Monitoring den Versatzwert von jedem Knoten abgreift.
Docker Desktop und WSL2 auf Laptops sind eine eigene Geschichte, weil der „Host“ der Container dort eine verborgene virtuelle Maschine ist, die nach dem Schlafmodus gern nachgeht. Für WSL2 ist der schnelle Fix, die Zeit aus der Hardware-Uhr zu ziehen, die der Hypervisor in Ordnung hält:
sudo hwclock -sFür Docker Desktop ist der radikale, aber zuverlässige Weg ein Neustart der Anwendung; nach dem Start synchronisiert sich die VM mit dem Host. In neueren Versionen tritt das Problem seltener auf, verschwunden ist es nicht, deshalb ist ein Healthcheck auf Zeitversatz in der Dev-Umgebung keine Paranoia, sondern spart die Morgenstunde.
Beispiel: Healthcheck in einer Compose-Datei
Die Idee: Den Arbeitscontainer nicht hochfahren, wenn die Host-Uhr weiter als erlaubt verrutscht ist. Die Prüfung nutzt denselben Trick mit dem Date-Header über einfaches HTTP durch den Proxy, um nicht von TLS abhängig zu sein:
healthcheck:
test: ["CMD", "python", "/app/clock_check.py"]
interval: 5m
timeout: 20s
retries: 1Das Skript clock_check.py ist das aus dem Diagnoseabschnitt, mit Schwelle 30 Sekunden. Der Orchestrator markiert den Container als ungesund, und du siehst die Ursache im Status statt in einem Berg TLS-Fehler in den Anwendungslogs.
Typische Fehler und Fälle aus der Praxis
Der Abschnitt darüber, was man nicht tun sollte, ist nützlicher als der darüber, was zu tun ist. Hier sind Lösungen gesammelt, die im Moment des Vorfalls logisch wirken und später teuer sind, plus ein paar typische Geschichten mit Zahlen.
Fehler 1: Zertifikatsprüfung abschalten
Flag -k, verify=False, NODE_TLS_REJECT_UNAUTHORIZED=0. Das Skript „läuft“ wieder, Ticket zu. Aber Request-Signaturen und JWTs passen weiterhin nicht, TOTP wird nicht akzeptiert, und du hast den einzigen Mechanismus abgeschaltet, der den Kanal schützte. Die Uhr bleibt schief und bricht in einer Woche etwas anderes. Das Abschalten der Prüfung ist genau für ein Kommando im Rahmen der Diagnose akzeptabel – und niemals im Code.
Fehler 2: leeway oder Signaturfenster „mit Puffer“ erhöhen
Die JWT-Toleranz auf 10 Minuten zu heben heißt, das Fenster für die Wiederverwendung eines abgefangenen Tokens um Faktor 20 zu vergrößern. Das eigentliche Problem (die Uhr) bleibt. Korrekte Toleranz für JWT: 30–60 Sekunden, für HMAC-Signaturen: das, was der API-Anbieter vorgibt.
Fehler 3: Zeit per Hand setzen statt synchronisieren
date -s behebt das Symptom für ein paar Stunden. Drift und der nächste Snapshot bringen alles zurück, nur dass sich dann niemand mehr daran erinnert, dass von Hand nachgeholfen wurde.
Fehler 4: zwei Zeitdaemons gleichzeitig
chrony und timesyncd, oder chrony und der Gast-Agent des Hypervisors mit aktivierter Synchronisation, ziehen die Uhr in unterschiedliche Richtungen. In den chrony-Logs sieht man das als ständige große Korrekturen. Lass nur eine Wahrheitsquelle.
Fehler 5: Lokalzeit mit UTC vergleichen
Ein Unterschied von exakt 3, 5 oder 7 Stunden ist fast immer keine Uhrfehlfunktion, sondern eine Zeitzone. Vergleiche immer date -u mit einer UTC-Quelle, und schreibe im Log die Zeit mit explizitem Zonenangaben.
Fehler 6: dem Status „installed“ vertrauen
Die Ansible-Rolle hat chrony installiert, Task grün. Aber UDP 123 ist dicht, und chrony ist „mit sich selbst“ synchronisiert. Die Prüfung in der Infrastrukturkonfiguration muss auf Leap status und Versatz schauen, nicht auf das Vorhandensein des Pakets.
Fehler 7: signierten Request bei Retries wiederverwenden
Besonders über Proxys mit Reconnects. Jeder Versuch muss einen neuen Zeitstempel und eine neue Signatur bekommen.
Fall A: Snapshot des Test-Stands
Ein Team zur Erhebung offener Preisdaten hielt einen Pool von Workern in virtuellen Maschinen und rollte den Stand einmal pro Woche auf einen „sauberen“ Snapshot zurück. Nach einem der Rollbacks fielen alle Worker, die über einen Proxy liefen, mit „certificate is not yet valid“ auf einem Drittel der Zielseiten um – genau denen, die 90-Tage-Zertifikate nutzten und sie in den 12 Tagen Lebensdauer des Snapshots neu ausgestellt hatten. Vom Laptop des Engineers ohne Proxy öffneten sich die Seiten. Anderthalb Stunden gingen für das Umschalten von Pools und Tarifen beim Proxy-Anbieter drauf, bevor jemand date -u ausführte und 12 Tage Rückstand sah. Der Fix: chrony mit makestep im Basis-Image des Stands und eine Versatzprüfung im Post-Restore-Skript. Wiederholungen gab es nicht.
Fall B: isoliertes Segment und Mini-PC
Ein Mini-PC ohne Hardware-Uhr stand in einem Netzsegment, in dem nach außen nur HTTP über einen Firmenproxy erlaubt war. Nach einem geplanten Stromausfall bootete er mit dem Datum des Image-Builds – 14 Monate in der Vergangenheit. Keine TLS-Verbindung kam zustande, was wie ein Totalausfall des Proxys aussah. Die Lösung: UDP 123 zum internen NTP-Server der Firma freigeben, chrony bekam die Direktive makestep 1.0 -1, und für den Fall fehlenden NTP wurde ein htpdate-Start über den Proxy beim Boot ergänzt.
Fall C: Upload in den Objektspeicher aus Docker Desktop
Ein Entwickler debugte den Upload von Dateien in einen S3-kompatiblen Speicher über einen Proxy aus einem Container auf dem Laptop. Nach der Mittagspause gaben alle Requests RequestTimeTooSkewed zurück. Versatz der Uhr der Docker-Desktop-VM: 23 Minuten, knapp über dem 15-Minuten-Fenster. Ein Neustart von Docker Desktop löste das Problem in einer Minute; im Projekt wurde ein Versatz-Healthcheck ergänzt, der die Ursache sofort zeigt.
Fall D: 50 Sekunden und TOTP
Ein Service automatisierte den Login in ein eigenes Firmenportal mit einem Service-Account und Zwei-Faktor-Authentifizierung per TOTP. Die Codes wurden nicht mehr akzeptiert: Der Server meldete, der Code sei falsch. Versatz der Knotenuhr: 50 Sekunden, weil timesyncd installiert, aber beim Umstieg auf chrony maskiert worden war und chrony nie gestartet wurde. Bei einer Toleranz von einem 30-Sekunden-Intervall reichte das. Keine Codeänderung, nur chrony starten.
Tools und Checkliste „bevor du den Proxy beschuldigst“
Lass uns alles in eine Form bringen, die man an ein Ticket hängen oder in ein Runbook gießen kann. Erst die Tools, dann die Reihenfolge.
Tools
- date -u, timedatectl, chronyc, hwclock – Zustand von Uhr und Synchronisation.
- curl -v --proxy, openssl s_client -proxy – TLS-Prüfung über den Proxy mit Verifikationscode.
- chronyd -Q, ntpdate -q – einmalige Versatzmessung, ohne die Uhr zu ändern.
- Date-Header per HTTP – universelle Zeitquelle, die über jeden Proxy funktioniert.
- libfaketime, openssl -attime – Fehler reproduzieren und Fehlerbehandlung testen.
- htpdate – Notfallsynchronisation in Segmenten mit gesperrtem UDP.
- Metriken – Exporter für chrony oder timesyncd liefern den Versatz ins Monitoring; Alarmierungsschwelle 1 Sekunde, kritisch bei 30 Sekunden.
Checkliste
- Wo genau läuft der ausfallende Code? Laptop, Server, VM, Container, Pod. Alle Prüfungen dort machen.
- Stimmt date -u mit dem Handy überein? Abweichung in Minuten – Stopp, Uhr reparieren.
- Stimmt der Date-Header per http:// über den Proxy mit dem lokalen UTC auf ein paar Sekunden genau überein? Wenn nein – die Uhr.
- Sagt timedatectl synchronized: yes, zeigt chronyc tracking Leap status Normal? Wenn nein – die Synchronisation arbeitet nicht, egal was installiert ist.
- Enthält der Fehlertext not yet valid, has expired, RequestTimeTooSkewed, jwt expired, jwt not active, timestamp? Jedes dieser Wörter dreht sich um Zeit.
- Ist der Fehler ohne Proxy von derselben Maschine derselbe? Wenn ja – der Proxy ist raus.
- Gibt es den Fehler über den Proxy von einer anderen Maschine nicht? Wenn ja – das Problem liegt an der ersten Maschine.
- Zeigt openssl s_client über den Proxy Verify return code 9 oder 10 bei lebendigen Zertifikatsdaten? Diagnose bestätigt.
- Treten die Fehler nur mit einem Zielhost auf? Vergleiche dessen Date mit anderen – vielleicht ist die Uhr auf seiner Seite verrutscht.
- Werden Signaturen bei jedem Retry neu gebildet? Wenn nein – die Retry-Logik reparieren.
- Gab es kürzlich einen Snapshot, Schlafmodus, eine Migration, einen Neustart nach langem Aus? Jedes „ja“ ist fast sicher die Ursache.
- Erst nach allen „nein“ – den Proxy-Anbieter kontaktieren mit beigefügten Ausgaben von date -u, chronyc tracking und curl -v. So löst der Support von Proxeon oder jedem anderen Service die Frage in einem Austausch statt in fünf.
FAQ: häufige Fragen zu Zeit, Proxy und Ausfällen
Kann der Proxy die Zeit in meinen Requests oder in TLS selbst verfälschen?
Beim Tunneln über CONNECT oder SOCKS5 – nein. Der TLS-Handshake läuft zwischen deinem Client und dem Zielserver, der Proxy sieht nur verschlüsselte Bytes. Zeitstempel in Signaturen und JWTs bildet dein Code auf deiner Maschine. Das Einzige, was der Proxy hinzufügen kann, ist Verzögerung, und die liegt in Millisekunden, nicht in Minuten.
Warum geht es vom Laptop ohne Proxy, aber vom Server über den Proxy nicht?
Weil das zwei verschiedene Maschinen mit zwei verschiedenen Uhrsätzen sind. Der Vergleich ist konstruktiv falsch: Es haben sich gleich zwei Variablen geändert. Um den Proxy-Einfluss zu isolieren, starte den Request vom Server ohne Proxy. Um den Maschineneinfluss zu isolieren, starte über den Proxy vom Laptop. Fast immer zeigt sich, dass der Fehler an der Maschine hängt.
Wie genau muss die Zeit für die Arbeit über einen Proxy sein?
Für TLS reicht Genauigkeit in Stunden, wenn Zertifikate langlebig sind, und in Tagen bei kurzlebigen. Für Request-Signaturen ist das typische Fenster 5–15 Minuten. Für JWT 30–60 Sekunden Toleranz. Für TOTP plus/minus 30 Sekunden. Ein funktionierendes NTP liefert Millisekunden, deshalb ist der praktische Standard ein Versatz unter 1 Sekunde und ein Alarm ab 30 Sekunden.
Kann ich einen NTP-Client im Container laufen lassen?
Technisch nur mit CAP_SYS_TIME, und dann steuert er die Uhr des gesamten Hosts, was die Isolation verletzt und mit dem Daemon auf dem Host kollidiert. Der richtige Weg ist Synchronisation auf dem Host oder Cluster-Knoten. Im Container lass nur die Versatzprüfung.
Was tun, wenn UDP 123 fest verschlossen ist und der Ausgang nur über einen HTTP-Proxy läuft?
Fordere die Öffnung von UDP zumindest zum internen NTP der Firma oder des Cloud-Providers – das ist ein normaler Infrastrukturbedarf. Solange das läuft, nutze Synchronisation über den Hypervisor oder htpdate über die Date-Header durch den Proxy. Beachte: NTS schützt NTP über TLS auf TCP-Port 4460, aber die Zeitpakete selbst laufen weiterhin über UDP, NTS umgeht also keine UDP-Sperre.
Die Uhr geht ein paar Minuten vor, aber TLS funktioniert. Warum fallen dann Requests an die API um?
Weil das Fenster bei TLS Tage groß ist, bei Signaturen und Tokens aber Sekunden und Minuten. Das ist eine normale Situation: Zertifikate werden erfolgreich geprüft, aber eine HMAC-Signatur mit einem um drei Minuten in die Zukunft verschobenen Zeitstempel wird vom Server mit hartem Fenster abgelehnt. Schau auf den Fehlertext der API, nicht nur auf den Erfolg der Verbindung.
Wie erkenne ich, dass die Uhr des Zielservers verrutscht ist und nicht meine?
Vergleiche den Date-Header aus der Antwort des problematischen Hosts mit dem von zwei, drei anderen Hosts und mit deinem date -u. Stimmt deine Uhr mit der Mehrheit überein und weicht ein Host ab, liegt das Problem auf seiner Seite. Manche Client-SDKs können den Serverversatz automatisch kompensieren, indem sie ihn aus der Fehlerantwort zur Zeit berechnen.
Muss ich den Request bei einem Retry über den Proxy neu signieren?
Ja. Die Signatur enthält den Zeitstempel und gilt innerhalb des Fensters. Wenn zwischen erstem Versuch und Wiederholung Minuten mit Warten und Reconnects vergangen sind, kann die alte Signatur aus dem Fenster gefallen sein. Bilde die Signatur im Moment des Absendens jedes Versuchs.
Wie baue ich die Uhrprüfung ins Monitoring ein?
Nimm den Versatz von chrony oder timesyncd in dein Metriksystem und setze zwei Schwellen: Warnung bei 1 Sekunde, kritisch bei 30 Sekunden. Zusätzlich einen Healthcheck des Services über den Date-Header durch den Arbeitsproxy; er fängt Situationen ab, in denen der Zeitdaemon formal lebt, die Uhr aber trotzdem verrutscht ist, etwa nach einem Snapshot-Restore.
Hat sich 2026 etwas geändert?
Die wichtigste Änderung ist die Verkürzung der Laufzeiten öffentlicher Zertifikate und die massenhafte Verbreitung automatischer Neuausstellung. Das macht selbst kleine Uhrrückstände von ein paar Tagen zu einer realen Ursache für TLS-Ausfälle, während solche Störungen früher selten waren. Gleichzeitig wächst der Anteil kurzer JWTs und strenger Signaturfenster in APIs. Anders gesagt: Die Anforderungen an die Genauigkeit der Uhren auf Client-Maschinen steigen, und Zeitsynchronisation hört auf, eine „Standard-Einstellung zu sein, über die man nicht nachdenkt“.
Fazit: Zeit als erster Diagnosepunkt
Fassen wir zusammen. Die Prüfung der Zertifikatslaufzeit, der Felder exp und nbf in JWT, des Zeitstempels in der Request-Signatur und des Einmalcodes – alles sind lokale Vergleiche mit der Systemuhr. Beim Arbeiten über einen Proxy kommt eine weitere Maschine in die Kette, und die Aufmerksamkeit verschiebt sich natürlicherweise auf sie, obwohl weder TLS-Handshake noch Signatur noch Token-Validierung durch den Proxy-Tunnel gehen. Der Proxy ist in diesen Szenarien nur Transport. Die Uhr aber läuft aus ganz irdischen Gründen weg: Snapshots und Pausen virtueller Maschinen, Schlafmodus von Laptops mit Docker Desktop und WSL2, fehlender oder nicht funktionsfähiger Synchronisationsdaemon, gesperrtes UDP, leere Batterie, manueller Eingriff.
Das praktische Programm ist einfach und passt in drei Schritte. Erstens, sich daran gewöhnen, zeitliche Fehler am Text zu erkennen: not yet valid, has expired, RequestTimeTooSkewed, jwt expired, jwt not active. Zweitens, die Versatzprüfung zum ersten Punkt jeder Proxy-Fehleranalyse zu machen: date -u, Date-Header per HTTP über den Proxy, timedatectl oder chronyc tracking. Das ist eine Minute, die Stunden spart. Drittens, dafür sorgen, dass das Problem nicht wiederkommt: chrony mit iburst und makestep in Basis-Images, eine Zeitquelle pro Maschine, Synchronisation auf dem Host für Container, eine Versatzmetrik im Monitoring und ein Healthcheck in Services, die über einen Proxy laufen.
Wenn all das erledigt ist, wird die Kontaktaufnahme mit dem Proxy-Anbieter sinnvoll: Du kommst mit ausgeschlossener Hypothese und fertigen Kommando-Ausgaben, und die Analyse läuft zur Sache. Und in den meisten Fällen wird die Kontaktaufnahme einfach nicht nötig sein – weil die Uhr das Erste sein wird, was du prüfst, und das Letzte, was dich überrascht.