Handbook sections

Synchronizing time

Last reviewed

An accurate clock is invisible plumbing for security: logs, certificates and Kerberos all break when it drifts. Keep one trustworthy, authenticated NTP source disciplining the system clock.

The threat: every control silently assumes the clock is correct

Time is not a security control in itself, but almost every security control silently assumes the clock is correct. Left unsynchronized, a system clock drifts: quartz oscillators alone lose or gain seconds per day, and virtual machines drift far worse after migrations or host pauses. The consequences are exactly where an incident hurts most:

  • Forensics become unreliable. When you stitch together logs from a dozen hosts to reconstruct an attack, skewed timestamps scramble the order of events. A few minutes of drift can hide the true entry point or make a response look like it happened before the breach.
  • Authentication breaks. Kerberos rejects tickets when the clock is off by more than its tolerance (typically 5 minutes), and TLS validates certificates against notBefore/notAfter windows: a wrong clock either accepts an expired certificate or rejects a valid one.
  • An attacker can weaponize it. Tampering with the clock can push expired certificates back into validity, replay time-bounded tokens, or smear an intrusion across log timelines to defeat correlation. A rogue or spoofed NTP source is a real attack vector, which is why authenticated time matters.

Why harden it

Correct time is a prerequisite for trusting everything else. Your audit trail, certificate chain, scheduled renewals and authentication all rest on every host agreeing on now. PCI-DSS (requirement 10) and ANSSI BP-028 explicitly require a common, trusted time reference so that audit logs are admissible and correlatable. Hardening time synchronization is cheap insurance that keeps the rest of your hardening honest.

Defense principles applied

  • A single source of truth: exactly one daemon should discipline the clock. chrony, ntp/ntpsec and systemd-timesyncd fight over the system clock if more than one runs; pick one and mask the others.
  • An authenticated source: point at a controlled internal server or a reputable pool, and prefer NTS (Network Time Security, RFC 8915) so a spoofed source can't slew your clock. NTS wraps NTP in TLS-backed authentication.
  • Reject implausible jumps: maxchange rejects a server that suddenly claims a huge offset (an anti-spoofing guard); prefer slewing over stepping on sensitive hosts so a large offset doesn't disrupt databases or TLS, with makestep bounded to early boot.
  • Don't become an NTP server: a host that answers NTP queries can be abused for amplification DDoS. chrony does not serve by default; keep it that way (no allow), and bind the control socket to localhost.
  • Audit the effective state, not the package: a time daemon can be installed and configured yet masked, failed or stopped; only the running unit actually keeps time.

How do you set it up?

# /etc/chrony/chrony.conf  -- authenticated, single source, safe stepping
server time.cloudflare.com iburst nts   # NTS-authenticated upstream
ntsdumpdir /var/lib/chrony
makestep 1.0 3        # step only during the first 3 updates (boot), then slew
maxchange 1000 1 2    # reject a source that jumps the clock implausibly
rtcsync
# no 'allow' line: do not serve time to the network

Then systemctl enable --now chronyd and systemctl mask systemd-timesyncd ntp so only one daemon disciplines the clock.

What Pavois audits: the live unit state

Pavois asks systemd for the live unit state rather than checking a config file or an installed package. The time-sync-present rule runs systemctl is-active across every known time daemon (chrony, chronyd, systemd-timesyncd, ntp, ntpsec) and passes as soon as any one is running. This is the effective-configuration angle: a daemon can be present and fully configured yet masked or failed, in which case the clock is not disciplined and a file-based scan would wrongly report success. To remediate, pavois harden apply uses a choose resource that installs and enables chrony by default (with systemd-timesyncd as the lightweight alternative). The rule maps to ANSSI BP-028 R71, CIS 2.3.x, PCI-DSS 10.6.1, NIST 3.3.7 and the Ubuntu STIG time controls.

Verify

timedatectl                  # 'System clock synchronized: yes', 'NTP service: active'
chronyc tracking             # offset, stratum, leap status, last update
chronyc sources -v           # which servers, reachability, selected source (*)
chronyc authdata             # NTS cookies present => the source is authenticated
chronyc ntpdata              # per-source detail incl. auth state

Pitfalls

  • Two time daemons fight: chrony and systemd-timesyncd both active will slew against each other. Mask all but one.
  • A firewall blocking UDP/123 (or NTS-KE TCP/4460) silently kills sync: the daemon runs but never reaches a source. Check chronyc sources.
  • makestep left permanent re-steps the clock at runtime: bound it to early boot (makestep 1.0 3) so production never jumps mid-session.
  • Containers usually share the host clock: run the time daemon on the host/VM, not inside every container.

FAQ

How do I stop an attacker spoofing my time source? Use NTS (server ... nts) so the upstream is cryptographically authenticated, and set maxchange to reject implausible jumps. Verify with chronyc authdata.

Why does Pavois check is-active instead of the config? Because a configured daemon that is masked/failed keeps no time; only the running unit disciplines the clock, so the live state is the truth.

chrony or systemd-timesyncd? chrony for servers (NTS, better accuracy, handles intermittent networks); systemd-timesyncd is a lightweight SNTP client fine for minimal or container-adjacent hosts. Run exactly one.