Désynchronisation des horloges et échecs via proxy : TLS, JWT, signatures de requêtes et diagnostic en une minute
Sommaire de l'article
- Introduction : « ça marchait hier, aujourd'hui le certificat est invalide »
- Les bases : où l'heure est critique et pourquoi les protocoles regardent l'horloge
- Plongée en profondeur : quelles horloges comptent dans la chaîne client — proxy — serveur
- À quoi ressemble l'erreur selon le client : les messages à reconnaître dès la première ligne
- Pourquoi les horloges dérivent : virtualisation, conteneurs, absence de démon, correction manuelle
- Diagnostic en une minute : comparaison avec une source externe et écart acceptable
- Configuration de la synchronisation : systemd-timesyncd et chrony, et comment s'assurer que ça marche vraiment
- Conteneurs et horloges : ce qui est hérité de l'hôte et ce qui ne l'est pas
- Erreurs courantes et cas pratiques
- Outils et checklist « avant de blâmer le proxy »
- Faq : questions fréquentes sur l'heure, les proxys et les pannes
- Conclusion : l'heure comme premier point du diagnostic
Introduction : « ça marchait hier, aujourd'hui le certificat est invalide »
Il existe une catégorie d'incidents qui sait se déguiser mieux que n'importe quelle autre. Un script qui passait depuis des mois par un proxy pour interroger des dizaines d'API se met soudain à échouer à chaque requête. Dans les logs : certificate has expired ou certificate is not yet valid. L'ingénieur ouvre le certificat du site cible dans son navigateur sur son portable : la date d'expiration est bonne, la chaîne de confiance est verte. Il relance le script sans proxy depuis son portable — ça marche. Il remet le proxy — échec à nouveau. La conclusion s'impose d'elle-même : le proxy est en cause.
Et cette conclusion est presque toujours fausse. Le problème n'est pas que le proxy « remplace le certificat » ou « casse TLS ». Le problème est que le script s'exécute sur une autre machine — un serveur, une VM, un conteneur — et que sur cette machine, l'horloge système a dérivé. De quelques minutes, heures ou jours. Le proxy s'est simplement trouvé être la variable la plus visible de l'équation et a encaissé le coup.
Nous allons traiter ce sujet de fond en comble. Vous découvrirez où exactement les protocoles modernes s'appuient sur l'heure courante et pourquoi un décalage d'horloge dans un sens ou dans l'autre produit des symptômes différents. Vous verrez les messages d'erreur concrets de curl, Python et Node.js, pour reconnaître le problème dès la première ligne de la stack trace. Vous comprendrez pourquoi les horloges dérivent précisément sur les machines virtuelles et dans les conteneurs, et pourquoi un démon de synchronisation installé ne garantit pas qu'il fonctionne. Vous obtiendrez une procédure de diagnostic en une minute, des configurations chrony et systemd-timesyncd qui marchent, et une checklist à parcourir avant d'écrire au support de votre fournisseur de proxy.
Précisons d'emblée le périmètre. Cet article ne traite pas de l'émission de certificats, ni des chaînes de confiance, ni des magasins de racines. Nous examinons une seule cause d'échec — une heure système incorrecte — et tout ce qui en découle lorsqu'on travaille via un proxy. C'est un sujet étroit, mais c'est précisément lui qui brûle des heures de débogage dans les équipes qui font de l'intégration, de la supervision, de la collecte de données ouvertes et de l'automatisation via une infrastructure proxy comme Proxeon.
Les bases : où l'heure est critique et pourquoi les protocoles regardent l'horloge
Commençons par une question simple. Pourquoi les protocoles réseau ont-ils besoin de savoir quelle heure il est ? La réponse est unique : pour limiter la durée de vie de la confiance. Tout secret — certificat, jeton, signature, code à usage unique — peut finir par fuiter. S'il a une durée de vie, les dégâts d'une fuite sont bornés. Mais cette limitation ne fonctionne que si la partie qui vérifie connaît l'heure courante. Et elle la prend à un seul endroit : l'horloge système de la machine où s'exécute la vérification.
D'où une conséquence fondamentale : chaque vérification temporelle est locale. Le serveur ne demande pas l'heure au client. Le client ne demande pas l'heure au serveur. Chacun regarde son horloge et tire sa conclusion. Si les horloges des deux parties divergent, elles voient des réalités différentes, et l'une considère l'autre comme fautive.
Vérification de la validité du certificat en TLS
Chaque certificat X.509 contient deux champs : notBefore (valide à partir de) et notAfter (valide jusqu'à). Lors de la poignée de main TLS, le client compare l'heure courante de son horloge à cet intervalle. La date d'émission du certificat par le serveur n'entre pas en jeu — seul compte ce que le client pense de l'heure courante.
Le sens du décalage détermine le symptôme. Si l'horloge du client retarde, les certificats récemment émis semblent ne pas encore exister : erreur « certificate is not yet valid ». Si l'horloge du client avance, les certificats semblent expirés : « certificate has expired ». C'est le premier indice diagnostique à mémoriser.
Pourquoi le sujet s'est-il aggravé récemment ? Parce que les certificats deviennent de plus en plus courts. De nombreuses autorités de certification publiques émettent des certificats de 90 jours, il en existe à 6 jours, et les exigences sectorielles réduisent méthodiquement la durée maximale : à partir de mars 2026 — 200 jours, ensuite 100 jours, puis 47 jours d'ici 2029. Avant, une horloge en retard de deux semaines tombait rarement sur un certificat fraîchement émis. Aujourd'hui, un site cible peut renouveler son certificat tous les quelques jours, et une machine en retard de trois jours le rejettera comme « pas encore valide ».
Des mécanismes connexes relèvent du même groupe : les réponses OCSP et les listes de révocation ont leurs propres fenêtres de validité, les entrées Certificate Transparency contiennent des horodatages, et les tickets de session TLS 1.3 ont une durée de vie limitée. Tous sont vérifiés selon l'horloge locale.
JWT : les champs exp, nbf et iat
Un JSON Web Token est le format de jeton d'accès le plus répandu dans les API. Son corps contient trois champs temporels au format Unix epoch : iat (date d'émission), nbf (not before — à partir de quand il est valide) et exp (date d'expiration). La partie qui reçoit le jeton les compare à son horloge, généralement avec une petite tolérance (leeway) de 30 à 60 secondes.
Une asymétrie intéressante apparaît ici. En général, le serveur émet le jeton selon son horloge, et le client le met en cache jusqu'à exp — mais selon sa propre horloge. Si le client retarde de cinq minutes, il considérera le jeton vivant cinq minutes après que le serveur a commencé à le rejeter. Vous obtenez des 401 inexplicables en fin de vie de chaque jeton. Et si c'est le client lui-même qui signe le JWT — par exemple pour authentifier un compte de service — et que son horloge avance, le serveur voit un iat dans le futur et rejette le jeton comme pas encore valide.
Signatures de requêtes avec horodatage
De nombreuses API exigent de signer la requête en HMAC, en incluant l'heure courante dans la chaîne signée. L'exemple le plus connu est le schéma Signature Version 4, utilisé par les stockages objet compatibles S3. Le serveur accepte la requête si son horodatage ne diffère pas de plus de 15 minutes de son horloge ; sinon il renvoie l'erreur RequestTimeTooSkewed. Les passerelles de paiement, les API boursières et de courtage, les bus de données d'entreprise utilisent des schémas similaires avec des fenêtres de 30 secondes à quelques minutes. L'authentification Kerberos en réseau d'entreprise tolère par défaut un écart de 5 minutes.
Notez bien : la signature est formée par votre client selon son horloge. Le proxy n'y participe pas physiquement — il ne fait que transmettre des octets. C'est pourquoi une erreur « horodatage invalide dans la signature » via un proxy est toujours une question d'horloge de votre machine.
Codes à usage unique TOTP
Les codes d'authentification à deux facteurs selon le standard TOTP sont calculés à partir d'un secret partagé et du numéro d'intervalle de 30 secondes. La partie qui vérifie tolère en général plus ou moins un intervalle. Si votre horloge dérive d'une minute, les codes cessent totalement d'être acceptés. Cela vaut aussi pour vos propres intégrations, où un compte de service génère des TOTP par programme pour se connecter à un portail d'entreprise.
Consommateurs d'heure moins évidents
- Cache HTTP. Les en-têtes Date, Expires et max-age sont comparés à l'horloge locale ; en cas de décalage, le cache soit ne fonctionne plus, soit sert du contenu périmé.
- Cookies avec Expires. Un client en retard les conserve plus longtemps, un client en avance les jette immédiatement.
- HSTS et pinning. Les directives max-age sont elles aussi calculées localement.
- Rotation des certificats au sein des clusters. Les composants des orchestrateurs renouvellent les certificats selon un calendrier et, en cas de décalage d'horloge sur un nœud, cessent de se faire confiance mutuellement.
Plongée en profondeur : quelles horloges comptent dans la chaîne client — proxy — serveur
Superposons maintenant ce tableau à une topologie réelle. En travaillant via un proxy, la chaîne comporte au minimum trois machines avec trois jeux d'horloges : votre client, le serveur proxy et le serveur cible. La question diagnostique clé : sur laquelle de ces machines la vérification qui échoue a-t-elle lieu ?
HTTP CONNECT et SOCKS5 : le proxy ne voit pas TLS
Pour une requête vers une ressource HTTPS via un proxy HTTP, le client envoie la commande CONNECT avec le nom d'hôte et le port. Le proxy établit une connexion TCP avec la cible puis transmet aveuglément les octets dans les deux sens. La poignée de main TLS se produit à travers le proxy, entre votre client et le serveur cible. Le proxy ne voit pas le certificat, ne peut pas le modifier et ne participe pas à la vérification. Idem pour SOCKS5.
Conclusion pratique : une erreur « certificate is not yet valid » ou « certificate has expired » en tunnel transparent ne peut physiquement pas être causée par le proxy. Sa source est soit l'horloge de votre machine, soit son magasin de racines de confiance, soit un certificat réellement expiré côté cible. Les deux premiers cas sont locaux. Et le premier se vérifie en dix secondes.
Quand plus d'horloges sont vérifiées : TLS jusqu'au proxy lui-même
Si vous vous connectez au proxy par un canal sécurisé (schéma https:// pour le proxy lui-même), le client vérifie deux certificats : celui du serveur proxy et celui du site cible. Les deux vérifications se font selon l'horloge du client. Les symptômes sont les mêmes, mais dans la sortie détaillée de curl on verra à quelle étape — « Proxy certificate » ou « Server certificate » — l'échec s'est produit.
Ce qui dépend de l'horloge du proxy lui-même
L'horloge du serveur proxy influe sur un ensemble limité de choses : la justesse de ses propres logs, le fonctionnement de son écouteur TLS (par exemple la récupération de réponses OCSP), et la vérification des identifiants d'autorisation si le fournisseur utilise des jetons à durée de vie limitée. L'infrastructure Proxeon synchronise normalement l'heure sur ses nœuds, et dans l'immense majorité des incidents l'horloge du proxy n'est pas en cause : elle ne participe ni à la vérification du certificat cible, ni à la formation de vos signatures, ni à la validation de vos JWT. Tout cela est fait par le client et le serveur cible.
L'horloge du serveur cible
Il arrive, rarement, que l'horloge dérive de l'autre côté. Signe distinctif : les erreurs n'apparaissent qu'avec un seul hôte, alors que l'accès aux autres est normal ; l'en-tête Date dans la réponse de cet hôte diffère nettement de celui des autres serveurs. Vous n'y pouvez rien, sinon prévenir le propriétaire et, si l'API le permet, compenser le décalage dans vos requêtes — certaines bibliothèques savent calculer la correction à partir de l'en-tête Date de la réponse d'erreur et signer la requête suivante en tenant compte du décalage.
Latence et « péremption en file d'attente »
Le proxy ajoute de la latence — de quelques dizaines de millisecondes à quelques secondes en cas de reconnexion de canaux mobiles et de rotation d'IP. Pour une fenêtre de 15 minutes, c'est négligeable. Mais il y a un piège subtil dans la logique de retry. Si vous signez une requête, la mettez en file, puis l'envoyez plusieurs minutes plus tard après une série de retries avec attente, l'horodatage de la signature se périme. Via un canal lent ou surchargé, cela arrive plus souvent. Règle : signez juste avant l'envoi et resignez à chaque retry, ne réutilisez pas un objet signé prêt à l'emploi.
Décalage : amplitude et sens
Rassemblons la dépendance entre symptôme et décalage dans une liste qui servira à lire les logs :
- L'horloge du client retarde : « certificate is not yet valid », JWT avec erreur nbf ou iat en auto-signature, vos jetons en cache semblent vivants mais le serveur répond 401 « expired », les signatures sont rejetées comme trop anciennes, TOTP non accepté.
- L'horloge du client avance : « certificate has expired », vos propres JWT rejetés avec un iat dans le futur, signatures rejetées comme « du futur », jetons évincés du cache avant l'heure, TOTP non accepté.
- Décalage inférieur à la seconde : aucun des mécanismes cités ne le remarquera.
- Décalage de 30 secondes à 5 minutes : TOTP, JWT à faible leeway, schémas HMAC stricts cassent.
- Décalage supérieur à 15 minutes : pratiquement toutes les signatures de requêtes et Kerberos cassent.
- Décalage en jours : les connexions TLS vers les sites à certificats courte durée commencent à échouer.
À quoi ressemble l'erreur selon le client : les messages à reconnaître dès la première ligne
Savoir identifier le problème d'après le texte de l'erreur fait gagner plus de temps que n'importe quelle méthodologie. Voici les formulations caractéristiques des clients populaires. Elles sont données telles que les impriment les bibliothèques s'appuyant sur OpenSSL ; les numéros de ligne entre parenthèses varient d'une version à l'autre.
curl
Messages de base en cas d'heure incorrecte :
curl: (60) SSL certificate problem: certificate is not yet validcurl: (60) SSL certificate problem: certificate has expiredLe code 60 est CURLE_PEER_FAILED_VERIFICATION, commun à tous les problèmes de vérification de certificat. On ne peut distinguer une cause temporelle d'un problème de racines qu'au texte après les deux-points. Avec l'option -v, une ligne utile avec le code numérique de vérification OpenSSL apparaît : 9 correspond à « not yet valid », 10 à « has expired » :
* SSL certificate verify result: certificate is not yet valid (9), continuing anyway.La ligne « continuing anyway » n'apparaît qu'avec l'option -k ; sans elle, la connexion est rompue. Sous Windows avec le backend Schannel, le texte diffère :
curl: (60) schannel: SEC_E_CERT_EXPIRED (0x80090328) - The received certificate has expired.Exemple de vérification complète via un proxy avec sortie détaillée, pour voir à la fois l'étape (proxy ou cible) et le résultat de la vérification :
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
Le module standard ssl lève une exception au suffixe parlant :
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: certificate is not yet valid (_ssl.c:1010)La bibliothèque requests l'enveloppe dans SSLError et ajoute un wrapper de pool de connexions, si bien que les mots clés se retrouvent à la toute fin d'une longue chaîne :
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)')))Détail important en travail via proxy : en tunnel CONNECT, l'erreur de vérification du certificat cible arrive bien comme SSLError, et non comme ProxyError. ProxyError apparaît si la connexion au proxy lui-même a échoué. Le type d'exception indique donc déjà où chercher.
httpx formule plus court :
httpx.ConnectError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: certificate has expired (_ssl.c:1010)aiohttp a sa propre classe :
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)')]Erreurs JWT dans PyJWT lors de la vérification d'un jeton de votre côté :
jwt.exceptions.ExpiredSignatureError: Signature has expiredjwt.exceptions.ImmatureSignatureError: The token is not yet valid (nbf)Voici un exemple minimal reproductible via un proxy, à garder sous la main comme cas de test :
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("Похоже на сдвиг системных часов, проверьте date -u")
print(msg)Petite précision : le caractère de saut de ligne dans cet exemple et les suivants est affiché sous forme d'entité HTML pour que le balisage reste sur une seule ligne ; une fois copié dans un éditeur, il redevient un saut de ligne normal.
Node.js : https, fetch, undici
Node imprime des messages courts et, ce qui est précieux, munit l'erreur d'un champ code :
Error: certificate is not yet valid
code: 'CERT_NOT_YET_VALID'Error: certificate has expired
code: 'CERT_HAS_EXPIRED'Piège du fetch intégré : de l'extérieur, on ne voit qu'un TypeError: fetch failed, et la vraie cause est cachée dans la propriété cause. Loggez-la explicitement :
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);
}Pour router fetch via un proxy dans Node, on utilise ProxyAgent du paquet undici. L'erreur de certificat cible arrivera alors avec le même code CERT_HAS_EXPIRED ou CERT_NOT_YET_VALID dans cause. Pour le module https classique et des bibliothèques comme axios, on utilise un agent du paquet https-proxy-agent — les codes d'erreur sont les mêmes, car ils proviennent du même OpenSSL interne à Node.
La bibliothèque jsonwebtoken, lors de la vérification des jetons, distingue deux cas temporels :
TokenExpiredError: jwt expired
NotBeforeError: jwt not activeErreurs côté serveur sur les signatures
Ici, le texte est défini par l'API, mais les formulations typiques sont reconnaissables. Chez les stockages compatibles S3 :
<Code>RequestTimeTooSkewed</Code>
<Message>The difference between the request time and the current time is too large.</Message>Chez les serveurs OAuth — un en-tête WWW-Authenticate avec error="invalid_token" et une description du type « The token expired » ou « Token used before issued ». Chez les API financières — des messages sur un timestamp outside of recvWindow ou « Timestamp for this request is outside of the allowed window ». Si vous voyez l'une de ces phrases, l'horloge est la première chose à vérifier.
Pourquoi les horloges dérivent : virtualisation, conteneurs, absence de démon, correction manuelle
L'oscillateur à quartz de la carte mère n'est pas une référence. Sa dérive typique est de 10 à 100 ppm, soit jusqu'à 1 à 9 secondes par jour sans correction. Sur un mois sans synchronisation, des minutes s'accumulent. C'est le bruit de fond de toute machine. Mais les incidents majeurs sont causés non par une lente dérive, mais par des sauts. Examinons les sources.
Machines virtuelles : pause, snapshot, migration
Une machine virtuelle ne possède pas d'horloge physique. Elle obtient l'heure via une source paravirtualisée (kvm-clock, Hyper-V reference time, équivalents chez d'autres hyperviseurs). Quand l'invité est mis en pause, sauvegardé dans un snapshot ou migré entre hôtes, son horloge interne « se fige ». Au réveil, le noyau reçoit ou non une correction de l'hyperviseur — cela dépend de la configuration des agents invités. Scénario classique : une équipe restaure un serveur de test sur un snapshot vieux de deux semaines, et il se réveille avec une horloge en retard de 14 jours. Tous les certificats émis pendant ces deux semaines sont pour lui « pas encore valides ».
Un problème à part : le conflit entre deux sources de vérité. L'agent invité de l'hyperviseur « recale » périodiquement l'heure sur celle de l'hôte, tandis qu'un démon NTP tourne à l'intérieur de l'invité. Si l'hôte lui-même n'est pas synchronisé, l'invité reçoit deux signaux contradictoires et l'horloge peut sauter d'avant en arrière. La recommandation de la plupart des guides de virtualisation : soit la synchronisation par l'hyperviseur, soit NTP à l'intérieur, mais pas les deux à la fois.
Portables de développeurs : veille et VM de bureau
Docker Desktop et WSL2 fonctionnent dans une machine virtuelle légère. Après une mise en veille ou une hibernation du portable, l'horloge de cette VM est souvent en retard exactement du temps de veille — une heure, une nuit, un week-end. L'OS hôte recale l'heure, mais pas toujours la VM. Résultat : le lundi matin, tous les scripts dans les conteneurs échouent sur TLS, alors que le terminal de l'hôte va bien. C'est précisément cette situation qui produit le plus souvent la plainte « ça marche sans proxy depuis mon portable, ça ne marche pas via le proxy dans le conteneur » — alors que le proxy, dans les deux cas, pouvait ne pas participer du tout à la vérification.
Conteneurs
Un conteneur n'a pas d'horloge temps réel propre — il utilise celle de l'hôte. Nous détaillerons cela dans une section dédiée, mais retenons l'essentiel : si l'hôte dérive, tous les conteneurs dérivent, et on ne peut pas réparer cela depuis l'intérieur du conteneur.
Absence de démon de synchronisation ou démon non fonctionnel
Les images minimales, les installations « nues » et les systèmes montés à la main sont souvent dépourvus de client NTP. Et là où il est présent, il peut ne pas fonctionner pour une dizaine de raisons : UDP sortant sur le port 123 bloqué par un pare-feu ; noms de pools non résolus ; service installé mais masqué ; chrony et timesyncd installés simultanément et se gênant ; décalage au démarrage supérieur au seuil au-delà duquel le ntpd classique refuse de corriger et se termine. Sont particulièrement vulnérables les serveurs dont la seule sortie Internet passe par un proxy HTTP d'entreprise : NTP sur UDP ne passera pas par un tel proxy, et l'horloge est livrée à elle-même.
Absence de RTC et pile déchargée
Les nano-ordinateurs comme le Raspberry Pi et beaucoup de mini-PC n'ont pas d'horloge matérielle à pile. Au démarrage, ils partent soit de l'époque Unix, soit de la dernière date sauvegardée, et vivent avec jusqu'à la première synchronisation réussie. Sur de vieux serveurs, la pile CMOS s'use, et après chaque extinction le BIOS réinitialise la date à la valeur d'usine. Si la synchronisation est inaccessible, la machine fonctionne avec une date vieille de dix ans, et toute connexion TLS est condamnée.
Correction manuelle et confusion de fuseaux horaires
La commande date -s, exécutée « pour vérifier une hypothèse » puis oubliée, est un scénario fréquent. Autre cas — le double démarrage Windows et Linux sur une même machine : Windows stocke l'heure locale dans l'horloge matérielle, Linux attend par défaut de l'UTC, et à chaque redémarrage dans l'autre OS l'heure se décale de la valeur du fuseau horaire, soit trois heures pour l'heure de Moscou. Enfin, la confusion entre heure et fuseau horaire : changer la variable TZ ou le fichier /etc/localtime modifie l'affichage, mais pas l'heure système. Il faut comparer uniquement en UTC.
Diagnostic en une minute : comparaison avec une source externe et écart acceptable
Le but de cette procédure est de répondre en 60 secondes à une seule question : l'horloge de la machine d'où partent les requêtes via le proxy est-elle correcte ? Toutes les commandes s'exécutent sur cette machine ou dans ce conteneur où le client échoue, pas sur votre portable.
Étape 1. Regardez l'UTC et comparez à toute source de confiance
date -uComparez avec l'heure d'un téléphone synchronisé à l'opérateur. Un écart de plusieurs minutes se voit immédiatement. Pour les secondes, il faut les étapes suivantes.
Étape 2. Récupérez l'heure depuis l'en-tête HTTP Date via le même proxy
Tout serveur HTTP correct renvoie l'en-tête Date selon son horloge (généralement synchronisée). Astuce : interrogez en HTTP simple, pas HTTPS, car avec un décalage important TLS ne s'établira pas et vous n'obtiendrez aucun en-tête :
date -u; curl -sI --proxy http://LOGIN:PASSWORD@PROXY_HOST:PORT http://example.com/ | grep -i '^date'Les deux lignes doivent coïncider à une ou deux secondes près (l'en-tête est à la granularité de la seconde, plus la latence du canal). Un écart d'une minute ou plus — diagnostic établi. Cette astuce est particulièrement précieuse car elle ne nécessite pas UDP, fonctionne via n'importe quel proxy et ne dépend pas de TLS.
Même astuce en Python — pratique à intégrer dans le healthcheck d'un service qui passe par Proxeon ou tout autre proxy :
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"Смещение локальных часов: {skew:+.1f} с")
if abs(skew) > 30:
raise SystemExit("Часы уехали, синхронизация обязательна")Étape 3. Demandez au système s'il se considère synchronisé
timedatectlCherchez deux lignes : « System clock synchronized: yes » et « NTP service: active ». Tout « no » ou « inactive » est une raison d'aller à la section configuration. Si c'est chrony :
chronyc trackingRegardez « System time » (amplitude et signe du décalage), « Stratum » (ne doit pas être 0) et « Leap status » (doit être Normal, et non « Not synchronised »).
Étape 4. Requête ponctuelle vers un serveur NTP sans modifier l'horloge
Si UDP sortant est ouvert, on peut mesurer le décalage précisément, sans rien changer :
chronyd -Q 'pool pool.ntp.org iburst'Ou, si ntpdate est installé :
ntpdate -q pool.ntp.orgLa sortie affichera l'offset en secondes avec son signe.
Étape 5. Confirmez que c'est bien l'heure qui casse TLS
L'utilitaire openssl sait passer par un proxy HTTP et imprime le résultat de vérification avec un code numérique :
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) » ou « 10 (certificate has expired) » avec un certificat valide — preuve irréfutable. Notez que s_client, dans la plupart des compilations, ne gère pas l'authentification proxy par login/mot de passe ; pour ce test, il est pratique d'utiliser l'authentification par IP, que Proxeon prend en charge. Récupérez les dates du certificat ainsi :
openssl s_client -connect example.com:443 -proxy PROXY_HOST:PORT -servername example.com </dev/null 2>/dev/null | openssl x509 -noout -datesQuel écart considérer comme normal
- Jusqu'à 100 millisecondes — machine saine avec NTP fonctionnel. Rien à faire.
- 100 ms — 1 seconde — acceptable pour tous les protocoles évoqués, mais signale une mauvaise source ou un sondage rare. Il vaut la peine de vérifier la configuration.
- 1 — 30 secondes — zone jaune. TLS et signatures à large fenêtre fonctionnent encore, TOTP et JWT à faible leeway peuvent déjà trébucher. À corriger aujourd'hui.
- Plus de 30 secondes — zone rouge. Considérez que toute erreur d'authentification ou TLS est due à l'horloge, jusqu'à preuve du contraire.
Comment reproduire le problème volontairement
Pour vérifier que votre gestion d'erreurs reconnaît correctement une panne temporelle, pas besoin de toucher à l'horloge système. La bibliothèque libfaketime substitue l'heure pour un seul processus :
faketime '2024-01-01 12:00:00' curl -sS https://example.com/ -o /dev/nullVous obtiendrez « certificate is not yet valid » pour tout site dont le certificat a été émis après la date indiquée. Pour tester un certificat sans réseau, OpenSSL prend en charge l'option -attime avec un temps Unix, pratique pour les tests en CI.
Configuration de la synchronisation : systemd-timesyncd et chrony, et comment s'assurer que ça marche vraiment
Installer un paquet n'est pas synchroniser. Voici deux schémas qui marchent et, surtout, la méthode de vérification.
Choix : timesyncd ou chrony
systemd-timesyncd — client SNTP minimaliste, intégré à systemd. Il interroge un serveur d'une liste, sait corriger l'horloge par saut ou en douceur, ne fonctionne pas comme serveur, ne prend pas en charge les sources matérielles. Suffisant pour les postes de travail et les serveurs non critiques où une précision de l'ordre de quelques dizaines de millisecondes suffit.
chrony — implémentation NTP complète. Combine plusieurs sources, converge rapidement après une longue interruption, se comporte proprement en réseau intermittent, prend en charge NTS (NTP avec authentification via TLS), peut distribuer l'heure en interne. C'est le choix par défaut pour les serveurs, les machines virtuelles et tout nœud par lequel passent des requêtes signées et du travail via proxy.
Important : deux démons simultanément — source de chaos. À l'installation de chrony, les distributions désactivent en général timesyncd, mais vérifiez-le explicitement.
Variante A : systemd-timesyncd
Configuration dans le fichier /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=2048Activation et vérification :
sudo timedatectl set-ntp true
sudo systemctl restart systemd-timesyncd
timedatectl timesync-statusDans la sortie de timesync-status, Server doit être rempli, Stratum différent de 0, et le champ Offset en millisecondes. Si le serveur n'est pas indiqué ou si « Packet count: 0 », les réponses NTP n'arrivent pas — presque sûrement UDP 123 fermé.
Variante B : chrony
Fichier /etc/chrony/chrony.conf (sur certains systèmes /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/chronyDétaillons les directives clés, car ce sont elles qui déterminent le comportement en cas de panne :
- iburst — envoie une série de requêtes au démarrage et synchronise en quelques secondes plutôt qu'en minutes.
- makestep 1.0 3 — si le décalage dépasse 1 seconde, un saut est autorisé dans les trois premières mises à jour, puis seulement un ajustement en douceur. Sans cette directive, après un snapshot ou une veille, chrony « rattrapera » l'horloge pendant des semaines. Pour les serveurs qui dorment ou migrent souvent, certains mettent makestep 1.0 -1, autorisant le saut toujours.
- rtcsync — écrit périodiquement l'heure dans l'horloge matérielle, pour repartir d'une valeur proche après redémarrage.
- Serveur interne du fournisseur en premier — les plateformes cloud fournissent généralement une adresse NTP interne au réseau ; elle est accessible même avec UDP externe fermé et a une latence minimale. Prenez l'adresse dans la documentation de votre fournisseur.
Lancement :
sudo systemctl enable --now chrony
sudo chronyc makestep
chronyc tracking
chronyc sources -vComment comprendre que la synchronisation fonctionne réellement
C'est la partie la plus importante de la section. Signes d'une synchronisation fonctionnelle sous chrony :
- Dans chronyc tracking, le champ Reference ID n'est pas nul et Stratum se situe entre 2 et 4 pour un serveur typique. Stratum 0 ou 10 avec un Reference ID vide signifie « je tourne tout seul ».
- Leap status : Normal. La valeur « Not synchronised » signifie que l'horloge n'est corrigée par personne.
- System time affiche un décalage en millisecondes ou microsecondes, pas en secondes.
- Dans chronyc sources, au moins une source est marquée d'une étoile (source actuellement choisie) ou d'un plus (participe à la combinaison). Un point d'interrogation sur toutes les sources — les réponses n'arrivent pas, cherchez un pare-feu ou un problème DNS.
- Last offset et RMS offset diminuent avec le temps au lieu d'augmenter.
Pour timesyncd, la vérification équivalente est « System clock synchronized: yes » dans timedatectl et un Packet count non nul dans timesync-status.
Quand NTP est bloqué par le pare-feu
Si seules les sorties HTTP(S) via proxy sont autorisées, vous avez trois voies. La première — demander l'ouverture d'UDP 123 au moins vers un serveur NTP interne ; c'est une demande standard et sûre aux administrateurs réseau. La deuxième — se synchroniser auprès de l'hyperviseur ou de l'hôte, s'il s'agit d'une VM ou d'un conteneur. La troisième — un compromis via l'utilitaire htpdate, qui règle l'horloge d'après l'en-tête Date des réponses HTTP ; la précision est de l'ordre de la seconde, ce qui suffit pour TLS, JWT et signatures de requêtes, et il sait fonctionner via un proxy HTTP. Pas le plus élégant, mais une option viable pour les segments isolés.
Windows
Sur les hôtes Windows, le service de temps se vérifie ainsi :
w32tm /query /status
w32tm /resyncEn environnement de domaine, la source est le contrôleur de domaine ; hors domaine, c'est le serveur NTP configuré. Le champ Last Successful Sync Time doit être récent.
Conteneurs et horloges : ce qui est hérité de l'hôte et ce qui ne l'est pas
Un conteneur est un ensemble d'espaces de noms isolés au-dessus d'un noyau partagé. L'isolation concerne les processus, le réseau, le système de fichiers, les points de montage. Mais l'horloge temps réel (CLOCK_REALTIME) n'est pas isolée. Les espaces de noms temporels apparus dans Linux 5.6 permettent de décaler uniquement les horloges monotones et le temps depuis le démarrage — c'est utile pour la migration de conteneurs et n'affecte pas l'heure calendaire. D'où plusieurs conséquences strictes.
Ce qui est hérité
- L'heure calendaire. Tous les conteneurs voient exactement la même heure que l'hôte. Si l'hôte dérive, tous les conteneurs dérivent.
- La synchronisation. Un chrony fonctionnant sur l'hôte « répare » automatiquement l'heure dans tous les conteneurs.
- Les sauts. Si l'hôte corrige l'horloge par saut, les applications dans les conteneurs voient ce saut instantanément.
Ce qui n'est pas hérité
- Le fuseau horaire. Le fichier /etc/localtime et la variable TZ vivent à l'intérieur de l'image. Dans les images minimales comme Alpine, le paquet tzdata peut être absent, et le conteneur affiche toujours UTC. Ce n'est pas une erreur d'heure mais d'affichage, mais elle induit régulièrement en erreur à la lecture des logs.
- Le magasin de certificats racines. Hors sujet de cet article, mais rappelons-le : il est lui aussi à l'intérieur de l'image.
- Le droit de modifier l'heure. L'appel système de réglage de l'heure exige CAP_SYS_TIME, que le conteneur n'a pas par défaut. La commande date -s à l'intérieur d'un conteneur échouera avec « Operation not permitted ». Lancer un démon NTP dans un conteneur ordinaire n'a pas de sens, et lui donner CAP_SYS_TIME est une mauvaise pratique : il se mettra à piloter l'horloge de tout l'hôte.
Conséquences pratiques
Le diagnostic de la section précédente peut et doit être exécuté à l'intérieur du conteneur — date -u et la récupération de l'en-tête Date via proxy montreront la réalité. Mais la correction se fait toujours sur l'hôte. Si le conteneur tourne dans Kubernetes, l'hôte concerné est le nœud précis où le pod est planifié ; assurez-vous que chrony fait partie de l'image de base des nœuds et que la supervision relève la métrique de décalage sur chaque nœud.
Docker Desktop et WSL2 sur les portables sont une histoire à part, car « l'hôte » des conteneurs y est une machine virtuelle cachée, encline à retarder après une veille. Pour WSL2, correction rapide — recaler l'heure depuis l'horloge matérielle, que l'hyperviseur tient à jour :
sudo hwclock -sPour Docker Desktop, la méthode radicale mais fiable est de redémarrer l'application ; après le démarrage, la VM se synchronise avec l'hôte. Dans les versions récentes, le problème est plus rare, mais n'a pas totalement disparu, c'est pourquoi un healthcheck du décalage d'horloge en environnement de dev n'est pas de la paranoïa, mais une économie d'une heure matinale.
Exemple : healthcheck dans un fichier compose
L'idée — ne pas démarrer le conteneur de travail si l'horloge de l'hôte a dérivé au-delà du seuil. Le test utilise la même astuce d'en-tête Date en HTTP simple via proxy, pour ne pas dépendre de TLS :
healthcheck:
test: ["CMD", "python", "/app/clock_check.py"]
interval: 5m
timeout: 20s
retries: 1Le script clock_check.py est celui écrit dans la section diagnostic, avec un seuil de 30 secondes. L'orchestrateur marquera le conteneur comme unhealthy et vous verrez la cause dans le statut, plutôt qu'une pile d'erreurs TLS dans les logs de l'application.
Erreurs courantes et cas pratiques
La section sur ce qu'il ne faut pas faire est plus utile que celle sur ce qu'il faut faire. On y trouve les solutions qui semblent logiques au moment de l'incident et coûtent cher ensuite, ainsi que quelques histoires caractéristiques avec des chiffres.
Erreur 1 : désactiver la vérification du certificat
Option -k, verify=False, NODE_TLS_REJECT_UNAUTHORIZED=0. Le script « remarche », le ticket est fermé. Mais les signatures de requêtes et les JWT ne concordent toujours pas, TOTP n'est pas accepté, et vous avez désactivé le seul mécanisme qui protégeait le canal. L'horloge reste fausse et cassera autre chose dans une semaine. Désactiver la vérification est acceptable le temps d'une commande en diagnostic, et jamais dans le code.
Erreur 2 : augmenter le leeway ou la fenêtre de signature « avec marge »
Relever la tolérance JWT à 10 minutes revient à multiplier par 20 la fenêtre de réutilisation d'un jeton intercepté. Le vrai problème (l'horloge) ne disparaît pas. La tolérance correcte pour JWT est de 30 à 60 secondes, pour les signatures HMAC — celle définie par le fournisseur d'API.
Erreur 3 : régler l'heure à la main au lieu de synchroniser
date -s corrige le symptôme pour quelques heures. La dérive et le prochain snapshot ramèneront tout en arrière, sauf que personne ne se souviendra que l'heure a été réglée manuellement.
Erreur 4 : deux démons de temps simultanément
chrony et timesyncd, ou chrony et l'agent invité de l'hyperviseur avec synchronisation activée, tirent l'horloge dans des sens opposés. Dans les logs de chrony, cela se voit comme de constantes corrections importantes. Gardez une seule source de vérité.
Erreur 5 : comparer l'heure locale à l'UTC
Un écart de exactement 3, 5 ou 7 heures n'est presque jamais une panne d'horloge, mais un problème de fuseau horaire. Comparez toujours date -u à une source UTC, et dans les logs écrivez l'heure avec le fuseau explicite.
Erreur 6 : faire confiance au statut « installed »
Le rôle Ansible a installé chrony, la tâche est verte. Mais UDP 123 est fermé, et chrony est synchronisé « tout seul ». La vérification dans la configuration d'infrastructure doit viser Leap status et le décalage, pas le fait que le paquet soit présent.
Erreur 7 : réutiliser une requête signée lors des retries
Surtout via un proxy avec reconnexions. Chaque tentative doit recevoir un nouvel horodatage et une nouvelle signature.
Cas A : snapshot d'un environnement de test
Une équipe de collecte de données ouvertes de prix maintenait un pool de workers dans des machines virtuelles et restaurait chaque semaine l'environnement sur un snapshot « propre ». Après l'une de ces restaurations, tous les workers passant par proxy se mirent à échouer avec « certificate is not yet valid » sur un tiers des sites cibles — précisément ceux qui utilisaient des certificats de 90 jours et les avaient renouvelés pendant les 12 jours de vie du snapshot. Depuis le portable de l'ingénieur sans proxy, les sites s'ouvraient. Une heure et demie fut perdue à changer de pools et de forfaits chez le proxy, avant que quelqu'un exécute date -u et constate un retard de 12 jours. Correction : chrony avec makestep dans l'image de base de l'environnement et vérification du décalage dans le script de post-restauration. Aucune récidive.
Cas B : segment isolé et mini-PC
Un mini-PC sans horloge matérielle se trouvait dans un segment réseau où seul le HTTP via un proxy d'entreprise était autorisé vers l'extérieur. Après une coupure de courant planifiée, il démarra avec la date de construction de l'image — 14 mois dans le passé. Aucune connexion TLS ne s'établissait, ce qui ressemblait à une panne totale du proxy. Solution : ouverture d'UDP 123 vers le serveur NTP interne de l'entreprise, directive makestep 1.0 -1 pour chrony, et, en l'absence de NTP, lancement de htpdate via le proxy au démarrage.
Cas C : exportation vers un stockage objet depuis Docker Desktop
Un développeur déboguait l'exportation de fichiers vers un stockage compatible S3 via proxy depuis un conteneur sur son portable. Après la pause déjeuner, toutes les requêtes se mirent à renvoyer RequestTimeTooSkewed. Décalage de l'horloge de la VM Docker Desktop — 23 minutes, un peu plus que la fenêtre de 15 minutes. Redémarrer Docker Desktop résolut le problème en une minute ; un healthcheck de décalage, qui montre directement la cause, a été ajouté au projet.
Cas D : 50 secondes et TOTP
Un service automatisait la connexion à son propre portail d'entreprise via un compte de service avec authentification à deux facteurs par TOTP. Les codes cessèrent d'être acceptés : le serveur répondait que le code était incorrect. Décalage de l'horloge du nœud — 50 secondes, parce que timesyncd était installé mais masqué lors du passage à chrony, qui ne fut jamais activé. Avec une tolérance d'un intervalle de 30 secondes, cela suffisait. Aucune modification de code, juste le démarrage de chrony.
Outils et checklist « avant de blâmer le proxy »
Rassemblons le tout sous une forme à joindre à un ticket ou à intégrer dans un runbook. D'abord les outils, puis la séquence.
Outils
- date -u, timedatectl, chronyc, hwclock — état de l'horloge et de la synchronisation.
- curl -v --proxy, openssl s_client -proxy — vérification TLS via proxy avec code de vérification.
- chronyd -Q, ntpdate -q — mesure ponctuelle du décalage sans modifier l'horloge.
- En-tête Date en HTTP — source de temps universelle, fonctionnant via n'importe quel proxy.
- libfaketime, openssl -attime — reproduction de la panne et test de la gestion d'erreurs.
- htpdate — synchronisation d'urgence dans les segments où UDP est fermé.
- Métriques — les exporters pour chrony ou timesyncd fournissent le décalage au système de supervision ; seuil d'alerte 1 seconde, critique à 30 secondes.
Checklist
- Où s'exécute exactement le code qui échoue ? Portable, serveur, VM, conteneur, pod. Toutes les vérifications se font là.
- date -u coïncide-t-il avec le téléphone ? Un écart de plusieurs minutes — stop, corrigez l'horloge.
- L'en-tête Date en http:// via proxy coïncide-t-il avec l'UTC local à quelques secondes près ? Sinon — horloge.
- timedatectl affiche-t-il synchronized: yes, chronyc tracking montre-t-il Leap status Normal ? Sinon — la synchronisation ne fonctionne pas, indépendamment de ce qui est installé.
- Le texte de l'erreur contient-il not yet valid, has expired, RequestTimeTooSkewed, jwt expired, jwt not active, timestamp ? Chacun de ces mots parle d'heure.
- Sans proxy depuis la même machine, l'erreur est-elle identique ? Si oui — le proxy n'est pas en cause.
- Via proxy depuis une autre machine, pas d'erreur ? Si oui — le problème est sur la première machine.
- openssl s_client via proxy affiche-t-il Verify return code 9 ou 10 alors que les dates du certificat sont bonnes ? Diagnostic confirmé.
- Erreurs avec un seul hôte cible ? Comparez son Date avec les autres — il se peut que l'horloge ait dérivé de son côté.
- Les signatures sont-elles reformées à chaque retry ? Sinon — corrigez la logique de retry.
- Y a-t-il eu récemment un snapshot, une veille, une migration, un redémarrage après une longue extinction ? Tout « oui » est presque certainement la cause.
- Seulement après tous les « non » — contactez le fournisseur de proxy avec les sorties de date -u, chronyc tracking et curl -v jointes. Ainsi le support de Proxeon ou de tout autre service résoudra la question en un seul échange, et non en cinq.
FAQ : questions fréquentes sur l'heure, les proxys et les pannes
Le proxy peut-il fausser lui-même l'heure dans mes requêtes ou dans TLS ?
En tunnelisation via CONNECT ou SOCKS5 — non. La poignée de main TLS se passe entre votre client et le serveur cible, le proxy ne voit que des octets chiffrés. Les horodatages dans les signatures et les JWT sont formés par votre code sur votre machine. La seule chose que le proxy peut ajouter, c'est de la latence, et elle se mesure en millisecondes, pas en minutes.
Pourquoi ça marche depuis mon portable sans proxy, mais pas depuis le serveur via proxy ?
Parce que ce sont deux machines différentes avec deux jeux d'horloges différents. La comparaison est incorrecte par construction : deux variables ont changé d'un coup. Pour isoler l'effet du proxy, lancez la requête depuis le serveur sans proxy. Pour isoler l'effet de la machine — lancez via proxy depuis le portable. Presque toujours, l'erreur sera liée à la machine.
Quelle précision d'heure faut-il pour travailler via proxy ?
Pour TLS, une précision de quelques heures suffit si les certificats sont de longue durée, et de quelques jours pour les courte durée. Pour les signatures de requêtes, la fenêtre typique est de 5 à 15 minutes. Pour JWT — une tolérance de 30 à 60 secondes. Pour TOTP — plus ou moins 30 secondes. Un NTP fonctionnel donne des millisecondes, donc le standard pratique est un décalage inférieur à 1 seconde, avec alerte au-delà de 30 secondes.
Puis-je lancer un client NTP dans un conteneur ?
Techniquement — seulement avec CAP_SYS_TIME, et alors il pilotera l'horloge de tout l'hôte, ce qui viole l'isolation et entre en conflit avec le démon de l'hôte. La bonne approche est la synchronisation sur l'hôte ou le nœud du cluster. Dans le conteneur, ne gardez que la vérification du décalage.
Que faire si UDP 123 est totalement bloqué et que la sortie passe uniquement par un proxy HTTP ?
Demandez l'ouverture d'UDP au moins vers le NTP interne de l'entreprise ou du fournisseur cloud — c'est un besoin d'infrastructure standard. En attendant, utilisez la synchronisation depuis l'hyperviseur ou htpdate sur les en-têtes Date via proxy. Notez que NTS protège NTP via TLS sur le port TCP 4460, mais les paquets temporels eux-mêmes passent toujours par UDP, donc NTS ne contourne pas un blocage UDP.
L'horloge avance de plusieurs minutes, mais TLS fonctionne. Pourquoi les requêtes API échouent-elles alors ?
Parce que la fenêtre de TLS est en jours, tandis que celle des signatures et jetons est en secondes et minutes. C'est une situation normale : les certificats se vérifient, mais une signature HMAC avec un horodatage de trois minutes dans le futur est rejetée par un serveur à fenêtre stricte. Regardez le texte d'erreur de l'API, pas seulement le succès de la connexion.
Comment savoir si c'est l'horloge du serveur cible qui a dérivé, et non la mienne ?
Comparez l'en-tête Date de la réponse de l'hôte problématique avec le Date de deux ou trois autres hôtes et avec votre date -u. Si votre horloge correspond à la majorité et qu'un seul hôte s'écarte — le problème est de son côté. Certains SDK clients savent compenser automatiquement le décalage serveur, en le calculant à partir de la réponse d'erreur temporelle.
Faut-il resigner la requête lors d'un retry via proxy ?
Oui. La signature inclut un horodatage et n'est valide que dans la fenêtre. Si, entre la première tentative et le retry, des minutes d'attente et des reconnexions se sont écoulées, l'ancienne signature peut sortir de la fenêtre. Formez la signature au moment de l'envoi de chaque tentative.
Comment intégrer la vérification de l'horloge à la supervision ?
Relevez le décalage de chrony ou timesyncd dans le système de métriques et définissez deux seuils : avertissement à 1 seconde, critique à 30 secondes. En complément, un healthcheck du service sur l'en-tête Date via un proxy fonctionnel ; il attrape les situations où le démon de temps est formellement vivant mais l'horloge a quand même dérivé, par exemple après une restauration de snapshot.
Quelque chose a-t-il changé en 2026 ?
Le principal changement est la réduction des durées de vie des certificats publics et la généralisation du renouvellement automatique. Cela fait de même de petits retards d'horloge, de quelques jours, une cause réelle d'échec TLS, alors qu'autrefois de telles pannes étaient rares. En parallèle, la part des JWT courts et des fenêtres de signature strictes augmente dans les API. Autrement dit, les exigences de précision d'horloge des machines clientes augmentent, et la synchronisation de l'heure cesse d'être « un réglage par défaut auquel on ne pense pas ».
Conclusion : l'heure comme premier point du diagnostic
Résumons. La vérification de la validité d'un certificat, des champs exp et nbf d'un JWT, de l'horodatage d'une signature de requête et d'un code à usage unique — tout cela est une comparaison locale à l'horloge système. En travaillant via un proxy, une machine supplémentaire apparaît dans la chaîne, et l'attention se déplace naturellement vers elle, alors que ni la poignée de main TLS, ni la signature, ni la validation du jeton ne passent par le tunnel du proxy. Le proxy, dans ces scénarios, n'est qu'un transport. Les horloges, elles, dérivent pour des raisons bien terre à terre : snapshots et pause des machines virtuelles, veille des portables avec Docker Desktop et WSL2, absence ou dysfonctionnement d'un démon de synchronisation, UDP fermé, pile déchargée, correction manuelle.
Le programme pratique est simple et tient en trois étapes. Première — prendre l'habitude de reconnaître les erreurs temporelles au texte : not yet valid, has expired, RequestTimeTooSkewed, jwt expired, jwt not active. Deuxième — faire de la vérification du décalage d'horloge le premier point de toute analyse d'incident proxy : date -u, en-tête Date en HTTP via proxy, timedatectl ou chronyc tracking. C'est une minute qui en économise des heures. Troisième — faire en sorte que le problème ne revienne pas : chrony avec iburst et makestep dans les images de base, une seule source de temps par machine, synchronisation sur l'hôte pour les conteneurs, métrique de décalage dans la supervision et healthcheck dans les services qui passent par proxy.
Quand tout cela est accompli, contacter le fournisseur de proxy devient pertinent : vous arrivez avec une hypothèse écartée et les sorties de commandes en main, et l'analyse va au fond des choses. Et dans la plupart des cas, le contact ne sera même pas nécessaire — parce que l'horloge sera la première chose que vous aurez vérifiée, et la dernière à vous surprendre.