Horloge VPS qui dérive : causes et correction
Votre VPS affiche-t-il une mauvaise heure ? Vérifiez les trois horloges, lisez chronyc et timedatectl, puis rétablissez la synchro NTP qui bloque votre 2FA.
Pourquoi l’horloge de votre VPS dérive
L’horloge d’un VPS dérive parce que rien ne la corrige. Le noyau mesure le temps à partir d’un compteur matériel qui avance légèrement trop vite ou trop lentement. Sans client de synchronisation horaire, cette petite erreur augmente à chaque heure. Dans une machine virtuelle, une seconde cause intervient : votre guest partage un processeur physique avec d’autres guests. Pendant les périodes où il n’est pas planifié, il ne peut pas compter le temps.
Sur un guest KVM actuel, le compteur lui-même est rarement la cause réelle. La source paravirtualisée kvm-clock lit une valeur maintenue par l’hôte. Un guest en bon état reste donc étroitement synchronisé avec son hôte. Les horloges manifestement incorrectes le sont généralement pour une raison plus simple. Aucun daemon de synchronisation ne fonctionne, ou deux daemons fonctionnent en même temps et se contredisent, ou les paquets UDP sortants vers le port 123 ne quittent jamais le réseau de votre fournisseur. Un guest règle son horloge à partir de l’hôte ou de NTP (network time protocol), et non à partir de son propre oscillateur.
Ce qu’une horloge incorrecte casse réellement
- Les codes TOTP (mot de passe à usage unique basé sur le temps) ne correspondent plus. Vous pouvez donc être bloqué hors d’un serveur dont le mot de passe et la clé sont pourtant corrects.
- Un certificat émis il y a une minute est rejeté, et
curlafficheSSL certificate problem: certificate is not yet valid. apt updaterefuse un dépôt avecE: Release file for ... is not valid yet (invalid for another 1d 2h 3min 4s).- Les tâches planifiées s’exécutent au mauvais moment. Un saut de l’horloge peut en exécuter une deux fois et en ignorer une autre.
- Les journaux de deux serveurs ne peuvent pas être alignés. La chronologie d’un incident doit alors être reconstituée au jugé.
La tolérance est plus faible que la plupart des utilisateurs ne le pensent. Les valeurs ci-dessous sont des valeurs par défaut documentées, et non des mesures effectuées lors d’un test.
The data behind this chart
[
{
"label": "TLS certificate boundary",
"tolerance_seconds": 0,
"documented_in": "RFC 5280"
},
{
"label": "chrony steps instead of slewing",
"tolerance_seconds": 1,
"documented_in": "Debian and Ubuntu chrony.conf"
},
{
"label": "TOTP code, one time step",
"tolerance_seconds": 30,
"documented_in": "RFC 6238"
},
{
"label": "Kerberos clock skew",
"tolerance_seconds": 300,
"documented_in": "MIT krb5 default"
}
]Un code TOTP est calculé à partir d’un compteur qui avance toutes les 30 secondes. La plupart des vérificateurs acceptent aussi l’étape immédiatement précédente ou suivante. Une erreur d’une demi-minute dans chaque direction représente toute la marge disponible. Kerberos est beaucoup plus tolérant, avec une marge de dérive par défaut de 300 secondes. Un certificat ne tolère aucune dérive : il est vérifié par rapport à des instants fixes, avec une période de grâce de 0 secondes. Une horloge qui avance d’une seconde rejette donc un certificat parfaitement valide.
Les trois horloges, et celle qui compte
L’horloge système est celle qui compte. C’est le CLOCK_REALTIME du noyau : le nombre de secondes écoulées depuis le 1 janvier 1970 UTC, conservé en mémoire et lu par tout ce qui ajoute une date et une heure. Les lignes de journal, la vérification des certificats, les codes TOTP et les dates de modification des fichiers proviennent tous de cette horloge. Quand quelqu’un dit que l’heure du serveur est incorrecte, c’est de cette horloge qu’il parle.
L’horloge matérielle, également appelée RTC (real time clock), est un compteur distinct qui continue de fonctionner lorsque la machine est éteinte. Sur une machine physique, il s’agit d’une puce alimentée par batterie. Dans une machine invitée, elle est émulée par l’hyperviseur et constitue donc surtout un élément fourni par l’hôte. Linux la lit une fois au démarrage pour obtenir une valeur initiale, puis conserve son propre compteur. timedatectl l’affiche sur la ligne RTC time. Ne vous basez pas sur cette ligne pour diagnostiquer un VPS, car elle indique l’heure définie par l’hôte et non l’état de synchronisation de l’horloge système. Dans un conteneur, il n’y a généralement aucun /dev/rtc, et hwclock --show échoue avec hwclock: Cannot access the Hardware Clock via any known method.
La source d’horloge est le mécanisme utilisé par le noyau pour compter entre ces lectures. Demandez au noyau laquelle il a sélectionnée :
cat /sys/devices/system/clocksource/clocksource0/current_clocksource
cat /sys/devices/system/clocksource/clocksource0/available_clocksourceSur KVM, vous verrez généralement kvm-clock. Cette source lit une valeur maintenue par l’hôte. C’est pourquoi une machine invitée KVM sans client NTP reste approximativement à l’heure pendant un certain temps. tsc est le compteur propre au processeur. Les machines invitées Xen indiquent xen, et les machines invitées Hyper-V indiquent une source hyperv. Ne modifiez pas ce paramètre sans raison mesurée, car le noyau sélectionne déjà la meilleure source de confiance pour ce matériel.
Certains hôtes fournissent également un périphérique PTP (precision time protocol) à la machine invitée. chrony peut ainsi lire directement l’horloge de l’hôte au lieu de passer par le réseau. Cela mérite d’être vérifié, mais ce périphérique est souvent indisponible sur un VPS mutualisé :
sudo modprobe ptp_kvm
ls /dev/ptp*
cat /sys/class/ptp/ptp0/clock_nameSi modprobe échoue ou si aucun périphérique n’apparaît, votre hôte ne le fournit pas et NTP sur le réseau est votre seule option. Si clock_name désigne l’horloge virtuelle KVM, chrony peut l’utiliser avec une ligne refclock PHC /dev/ptp0 poll 2 dans sa configuration.
Lire l’état de l’heure sur votre propre machine
Commencez par une commande. Elle répond en une seule vue à la question « quelque chose maintient-il cette horloge à l’heure ? ».
timedatectlLisez ces lignes au lieu de vous fier à une valeur dont vous vous souvenez :
Local timeetUniversal timecorrespondent au même instant, affiché dans votre fuseau horaire et en UTC. S’ils sont identiques, la machine est déjà réglée sur UTC.RTC timeest l’horloge matérielle décrite plus haut. Sur un VPS, ignorez-la.Time zoneindique ce que le système utilise pour formater l’heure locale.System clock synchronizedest le propre indicateur du kernel. Un daemon de temps le définit une fois qu’il fait confiance à ses sources.nosignifie donc qu’aucun processus n’a discipliné cette horloge depuis le boot.NTP serviceconcerne spécifiquement systemd-timesyncd.n/aest normal sur une machine qui exécute chrony, car timesyncd n’y est pas installé.System clock synchronized: yesavecNTP service: n/asignifie que chrony fait le travail et que le kernel est d’accord avec lui.
Demandez ensuite quelle est l’ampleur du décalage. Ne l’évaluez pas à l’œil en la comparant à votre téléphone. Si chrony fonctionne :
chronyc tracking
chronyc sources -vchronyc tracking affiche les valeurs qui répondent à cette question. System time est le décalage actuel par rapport à l’heure NTP, suivi du mot fast ou slow. Last offset est l’amplitude de la correction la plus récente. Frequency est l’erreur de fréquence que chrony a mesurée sur votre horloge et qu’il compense déjà. Leap status devrait afficher Normal. S’il affiche Not synchronised et que Reference ID vaut 00000000 (), chrony n’a pas encore retenu de source.
chronyc sources -v affiche une légende au-dessus de la liste. Vous n’avez donc pas besoin de mémoriser les symboles. Deux colonnes contiennent l’essentiel de l’information. Le caractère d’état au début de chaque ligne indique la manière dont chrony évalue cette source. * désigne la source actuellement utilisée et ? sur chaque ligne signifie que personne ne répond. Reach correspond à l’historique des réponses des huit derniers polls, affiché en octal : 377 signifie que les huit ont reçu une réponse, tandis que 0 signifie qu’aucun n’en a reçu.
Si systemd-timesyncd est responsable à la place :
timedatectl timesync-status
timedatectl show-timesync --alltimesync-status affiche le serveur avec lequel le système communique, l’intervalle entre les polls et une valeur Offset. Si la commande renvoie une erreur concernant le service au lieu d’afficher son état, timesyncd n’est pas le daemon responsable sur cette machine. Cela répond déjà à votre question.
Pour effectuer une vérification approximative avec le monde extérieur sans outil supplémentaire, comparez votre horloge à un en-tête HTTP public Date. Cet en-tête est transmis en GMT avec une précision d’une seconde :
date -u
curl -sI https://www.cloudflare.com | grep -i '^date:'Un écart d’une ou deux secondes est normal et ne signifie rien. Un écart d’une minute vient de votre configuration.
chrony ou systemd-timesyncd sur un VPS
Ubuntu et Debian fournissent systemd-timesyncd par défaut. Il s’agit d’un client SNTP (simple network time protocol) : il interroge un serveur à la fois et ajuste progressivement l’horloge dans sa direction. Sur une machine qui reste en ligne et dont l’horloge est initialement à peu près correcte, cela suffit et son fonctionnement consomme presque aucune ressource.
chrony est une implémentation NTP complète et constitue un meilleur choix par défaut sur une machine virtuelle, pour des raisons visibles dans sa propre sortie. Il interroge plusieurs sources simultanément et ignore celles qui sont incohérentes. Il mesure l’erreur de fréquence de votre horloge et l’enregistre dans un fichier drift, afin de corriger sa dérive au lieu de suivre chaque mesure. Il récupère également rapidement après les deux événements propres aux VM, contrairement aux machines physiques : l’hôte peut les mettre en pause et les déplacer vers un autre hôte pendant leur fonctionnement. Lorsqu’un périphérique PTP de l’hôte est fourni, c’est chrony qui le lit.
sudo apt update && sudo apt install -y chrony
systemctl status chrony --no-pager
chronyc sources -v
chronyc trackingLisez la sortie d’apt pendant l’installation. Sur Debian et Ubuntu, les paquets chrony et systemd-timesyncd fournissent tous deux time-daemon. apt supprime donc timesyncd pendant l’installation de chrony. Ce comportement est normal et attendu. N’exécutez jamais les deux services, car deux daemons qui règlent la même horloge vont entrer en conflit. Tant que cela se produit, vous ne pouvez faire confiance à l’offset signalé par aucun des deux. Sur Rocky et AlmaLinux, installez chrony avec sudo dnf install -y chrony. L’unité s’appelle alors chronyd au lieu de chrony.
Le fichier de configuration est /etc/chrony/chrony.conf sur Debian et Ubuntu, et /etc/chrony.conf sur Rocky et Alma. La configuration par défaut de la distribution convient déjà à un VPS. Ne la modifiez que pour une raison précise. Deux directives méritent d’être comprises :
- Les lignes
pooletserverindiquent les sources de temps. Ajouteriburstdemande à chrony d’envoyer une rafale rapide au démarrage. La première synchronisation s’effectue ainsi en quelques secondes au lieu de plusieurs minutes. makestepdétermine quand chrony fait un saut d’horloge au lieu de l’ajuster progressivement. Vérifiez sa valeur avecgrep -n makestep /etc/chrony/chrony.conf. La valeur par défaut de Debian et Ubuntu,makestep 1 3, signifie ceci : lors des trois premières mises à jour après le démarrage de chronyd, l’horloge est ajustée par saut si son décalage dépasse une seconde. Ensuite, chrony corrige uniquement l’horloge par slewing.
Si vous voulez authentifier le trafic de temps afin de détecter toute altération sur le chemin, chrony 4 et les versions ultérieures prennent en charge NTS (network time security). Vérifiez d’abord votre version avec chronyd -v. Notez que NTS nécessite également l’ouverture du port TCP sortant 4460, en plus du port UDP 123 :
server time.cloudflare.com iburst ntsRedémarrez le service et vérifiez son fonctionnement avant de lui faire confiance. Une configuration impossible à analyser ne laisse aucun daemon de synchronisation de l’heure actif. L’horloge ne vous l’indiquera pas.
sudo systemctl restart chrony
chronyc sources -v
chronyc trackingPourquoi une horloge décalée de plusieurs minutes le reste
Un daemon de temps peut corriger un décalage de deux façons. Le ralentissement ou l’accélération progressive ajuste la vitesse de l’horloge jusqu’à la disparition de l’erreur. Le temps continue alors d’avancer, sans répéter ni sauter de timestamp. La correction brutale passe directement à la valeur correcte. Elle est rapide, mais peut faire reculer l’horloge. Ce recul est dangereux pour tout ce qui mesure une durée à partir de l’heure système. Les deux daemons préfèrent donc l’ajustement progressif.
C’est pourquoi une horloge très décalée peut le rester longtemps. chrony n’effectue une correction brutale que dans la fenêtre autorisée par makestep, qui correspond par défaut aux premières mises à jour après le démarrage du daemon. Si chronyd fonctionne depuis une semaine et découvre ensuite un décalage de quarante secondes, il l’ajuste progressivement. Corriger quarante secondes de cette manière prend beaucoup plus longtemps que vous ne le souhaitez. Forcez la correction une seule fois, à un moment calme :
sudo chronyc makestep
chronyc trackingchronyc tracking doit maintenant signaler un décalage System time proche de zéro, et Last offset doit indiquer l’ampleur de la correction effectuée. Réfléchissez avant d’exécuter cette commande sur un serveur de base de données très sollicité, car un recul de l’horloge peut perturber les logiciels qui supposent que le temps avance toujours. Redémarrer le daemon est une version plus douce de la même correction, car la fenêtre makestep s’ouvre de nouveau au démarrage.
Les conteneurs partagent l’horloge de l’hôte
Un conteneur ne possède pas d’horloge civile propre : il n’y a donc rien à synchroniser à l’intérieur. Les espaces de noms temporels Linux virtualisent uniquement les horloges monotonic et de démarrage. CLOCK_REALTIME n’est pas virtualisée, ce qui signifie qu’un conteneur lit la même horloge système que l’hôte sur lequel il s’exécute. Corrigez l’horloge sur l’hôte, et tous les conteneurs de cet hôte seront corrigés au même moment.
Cela entraîne plusieurs conséquences. N’installez pas chrony ou ntpd dans une image, car, au mieux, cela ne fera rien. La modification de la date dans un conteneur non privilégié échoue avec date: cannot set date: Operation not permitted, car le noyau exige CAP_SYS_TIME pour cet appel. Accorder CAP_SYS_TIME ne fournit pas une horloge privée au conteneur : cela lui permet de modifier l’horloge de l’hôte, et donc celle de tous les autres conteneurs.
Utiliser un autre fuseau horaire dans un conteneur ne constitue pas un problème d’horloge. Une image contenant son propre /etc/localtime affiche le même instant dans un autre fuseau, de sorte que date semble incorrect alors que l’horloge est juste. Définissez TZ=UTC dans l’environnement du conteneur pour éliminer cette confusion. Le runtime choisi ne change rien sur ce point. la comparaison entre Podman et Docker rootless explique ce qu’il change réellement.
Fuseaux horaires : UTC sur le serveur, heure locale pour les utilisateurs
Configurez la machine en UTC et laissez-la ainsi.
timedatectl list-timezones | grep -i utc
sudo timedatectl set-timezone UTC
timedatectlUTC n’applique pas l’heure d’été. Cela suffit à justifier ce choix. Une tâche quotidienne à 02:30 dans un fuseau qui applique l’heure d’été s’exécute deux fois le jour du passage à l’heure d’hiver et ne s’exécute pas du tout le jour du passage à l’heure d’été. man 8 cron décrit le traitement particulier des décalages de moins de trois heures : les tâches ignorées lors d’un saut en avant s’exécutent peu après le changement, et les tâches qui tombent dans une heure répétée lors d’un saut en arrière ne s’exécutent pas une seconde fois. Ce comportement est raisonnable, mais vous ne devriez pas avoir à en tenir compte à 03:00. En UTC, la tâche s’exécute une fois par jour, tous les jours de l’année. Si une de vos tâches est complètement absente au lieu de s’exécuter à une heure inhabituelle, les raisons pour lesquelles une tâche cron ne s’exécute jamais silencieusement sont probablement à rechercher en priorité.
Le même raisonnement s’applique à la lecture des journaux. journalctl formate les horodatages selon le fuseau horaire du système, tandis que journalctl --utc force l’utilisation d’UTC. Deux serveurs configurés dans deux fuseaux différents transforment chaque incident en exercice de conversion. Sous pression, ces conversions entraînent facilement une mauvaise lecture de la chronologie. Laissez les systèmes en UTC, stockez les horodatages en UTC et effectuez la conversion une seule fois, au moment où un utilisateur les consulte. Toute personne qui souhaite une lecture en heure locale pour une seule commande peut l’obtenir sans modifier la machine :
TZ=Europe/Berlin dateUne autre ligne de la sortie de timedatectl relève de cette section. RTC in local TZ doit afficher no. La valeur yes sert de contournement pour un dual-boot Windows sur un portable. Sur un serveur, elle ajoute seulement un décalage qui risque de provoquer des erreurs ultérieurement. Lorsqu’elle est définie, timedatectl affiche un avertissement indiquant que le système est configuré pour lire l’heure du RTC dans le fuseau horaire local.
Dépannage par symptôme
Votre code d’authentification à deux facteurs est refusé sur un serveur. Vérifiez l’horloge avant toute autre chose. Le code provient d’un compteur qui avance toutes les 30 secondes. Un serveur qui a 90 secondes de retard calcule donc un code correspondant à une période déjà dépassée par votre téléphone. timedatectl affiche System clock synchronized: no, ou chronyc tracking signale un décalage System time important. Il s’agit d’un problème différent d’une clé rejetée directement, qui affiche son propre message et qui est traité dans le guide des échecs d’authentification par clé publique.
apt update indique qu’un fichier Release n’est pas encore valide. Le message complet indique le dépôt concerné et la durée pendant laquelle il restera invalide, par exemple is not valid yet (invalid for another 1d 2h 3min 4s). Votre horloge est en retard par rapport à la date indiquée dans le fichier Release du dépôt. Cette durée mesure directement le retard. Corrigez l’horloge. Ne désactivez pas la vérification de date d’apt pour contourner le problème. Cette vérification empêche quelqu’un de vous servir un index de paquets obsolète.
Chaque ligne de source affiche l’état inaccessible et Reach vaut 0. Personne ne répond. Examinez donc le trafic sortant plutôt que votre configuration. NTP utilise le port UDP 123 en sortie, et certains réseaux le filtrent ou le redirigent. sudo chronyc ntpdata affiche les compteurs de chaque source, notamment Total TX et Total RX. Si le compteur TX augmente alors que RX reste à zéro, vos paquets sortent mais aucune réponse ne revient. Cela indique la présence d’un firewall entre vous et la source.
L’horloge était correcte, puis a changé brusquement. Des événements liés à l’hôte peuvent provoquer ce comportement. La restauration d’un snapshot, la mise en pause d’un guest ou une live migration vers un autre hôte peut laisser l’horloge du guest en retard par rapport à l’heure réelle. chrony le détecte lors de la synchronisation suivante et corrige l’écart. systemd-timesyncd peut d’abord attendre la fin d’un long intervalle entre deux synchronisations. Vérifiez que le daemon démarre au boot avec systemctl is-enabled chrony. Un daemon démarré manuellement s’arrête au prochain redémarrage.
Le décalage est faible, mais ne se stabilise jamais. Examinez le CPU steal. Un guest qui n’est pas planifié lorsque son timer interrupt doit se déclencher obtient des mesures retardées. Le décalage varie donc au lieu de converger. top affiche cette valeur dans la ligne du CPU, sous la forme st. Comprendre le CPU steal time sur un hôte partagé explique la signification de cette valeur et les mesures possibles.
Un certificat que vous venez d’émettre est rejeté comme n’étant pas encore valide. curl affiche SSL certificate problem: certificate is not yet valid, et les navigateurs indiquent un message similaire. Le certificat est correct. C’est l’horloge qui le vérifie qui est en retard. Le client comme le serveur peut être en cause. Vérifiez donc les deux. Si le serveur qui a émis le certificat est celui dont l’horloge est incorrecte, le guide des certificats certbot et nginx traite le renouvellement dans cette même configuration.
Ajoutez-le aux contrôles que vous exécutez déjà
La synchronisation de l’heure est un paramètre configuré au démarrage qui peut échouer silencieusement plusieurs mois plus tard. C’est exactement le type de problème qu’une routine détecte, contrairement à la mémoire. timedatectl et chronyc tracking se lisent ensemble en deux secondes. Exécutez-les dans le cadre des dix premières minutes sur un nouveau VPS, puis à nouveau lorsque vous suivez la checklist de maintenance régulière d’un serveur Linux. Si vous préférez automatiser le contrôle et recevoir une alerte lorsque le décalage augmente, écrire un service et un timer systemd présente le modèle à utiliser pour une petite unité qui envoie un rapport selon une fréquence définie.
FAQ
Comment vérifier que l’horloge de mon VPS est synchronisée ?
Exécutez timedatectl et lisez la ligne System clock synchronized. Il s’agit de l’indicateur du kernel, défini par le daemon qui synchronise l’horloge. Ainsi, yes avec NTP service: n/a est normal et indique un état sain sur une machine utilisant chrony. Pour connaître l’importance de l’écart, exécutez chronyc tracking et lisez System time. Vous pouvez aussi exécuter timedatectl timesync-status et lire Offset si systemd-timesyncd est utilisé. Pour comparer l’horloge à une source externe à la machine, comparez date -u avec l’en-tête Date renvoyé par n’importe quel site HTTPS.
Dois-je utiliser chrony ou systemd-timesyncd sur un VPS ?
Utilisez chrony pour tout serveur important. systemd-timesyncd est un client SNTP qui utilise un seul serveur. Il convient à une machine qui reste en ligne et dont l’horloge est déjà relativement correcte. chrony interroge plusieurs sources, écarte celles qui ne concordent pas, apprend la dérive de l’horloge et se resynchronise rapidement après une pause de l’hôte ou une migration à chaud. L’installation de chrony sur Debian ou Ubuntu supprime automatiquement systemd-timesyncd, car les deux paquets fournissent time-daemon. N’exécutez jamais deux daemons de synchronisation de l’heure en même temps.
Pourquoi mes codes TOTP échouent-ils sur un serveur alors qu’ils fonctionnent partout ailleurs ?
Parce qu’un code TOTP dépend de l’heure courante. Le code provient d’un compteur qui avance toutes les 30 secondes. Le serveur et votre téléphone doivent donc utiliser le même intervalle. La plupart des systèmes de vérification acceptent l’intervalle précédent ou suivant, ce qui laisse environ une demi-minute de tolérance dans chaque direction. Vérifiez timedatectl sur ce serveur. Si System clock synchronized indique no, corrigez la synchronisation. Les codes correspondront de nouveau sans modifier le secret partagé.
Puis-je régler l’heure à l’intérieur d’un conteneur Docker ?
Non, et ce n’est pas nécessaire. Un conteneur partage le CLOCK_REALTIME de l’hôte, car les namespaces de temps de Linux virtualisent uniquement les horloges monotonic et boot-time. Un conteneur sans privilèges reçoit date: cannot set date: Operation not permitted. Ajouter CAP_SYS_TIME lui permet de modifier l’horloge de l’hôte, au lieu de lui fournir sa propre horloge. Synchronisez plutôt l’hôte. Une heure locale différente dans un conteneur correspond à un réglage de fuseau horaire. Définissez donc TZ dans l’environnement du conteneur.
Un serveur doit-il utiliser UTC ou l’heure locale ?
UTC, avec l’heure locale appliquée au moment où une personne lit le résultat. UTC ne change jamais lors du passage à l’heure d’été. Une tâche quotidienne s’exécute donc une fois par jour toute l’année, et les horodatages de plusieurs serveurs restent alignés sans conversion. Définissez UTC avec sudo timedatectl set-timezone UTC. Pour afficher l’heure locale, il suffit de préfixer une commande, par exemple TZ=America/New_York date. Cela ne modifie pas l’horloge système.