chrony and Secure Time on Linux
Configure chrony with NTS (authenticated NTP over TLS) on Linux to prevent clock-skew attacks and protect Kerberos, TLS, and TOTP authentication.
Before you start
- ▸Root or sudo access on the target system
- ▸Outbound access to UDP 123 and TCP 4460 from the host
- ▸chrony 4.0 or later for NTS support (check with chronyd --version)
- ▸DNS resolution working before chronyd starts (NTS validates server hostnames)
Accurate system time is not a cosmetic concern. Kerberos tickets expire within five minutes of clock skew. TLS certificate validation fails when clocks diverge. Log correlation across hosts becomes useless when timestamps drift. chrony is the NTP implementation recommended by most major distributions today — it converges faster than the classic ntpd, handles intermittent connectivity gracefully, and since version 4.0 supports Network Time Security (NTS), which authenticates time sources over TLS so you cannot be tricked into accepting spoofed time.
Why Time Matters for Security
Three specific failure modes justify treating time synchronization as a security control rather than a housekeeping task:
- Kerberos / GSSAPI authentication — The protocol rejects tickets whose timestamps differ from the server by more than the
clockskewtolerance, typically 300 seconds. A drifting clock locks users out of SSH, NFS, and any Kerberos-backed service. - TLS/X.509 certificate validation — Both
notBeforeandnotAfterfields are checked against local time. A clock that is weeks behind will reject recently-issued certificates; one that runs ahead will accept already-expired ones. - TOTP / OATH one-time passwords — Google Authenticator, FreeOTP, and hardware tokens generate codes against the current epoch. More than 30–90 seconds of drift causes authentication failures.
Plain NTP (UDP/123) has no authentication of the time source itself. An on-path attacker or a compromised public server can shift your clock arbitrarily. NTS wraps NTP inside a TLS 1.3 handshake so the server's identity is verified before any time data is trusted.
Install chrony
Most distributions ship chrony as the default NTP daemon; verify before installing.
Debian / Ubuntu
sudo apt update
sudo apt install -y chrony
Fedora / RHEL 8+ / Rocky / AlmaLinux
sudo dnf install -y chrony
Arch Linux
sudo pacman -S --noconfirm chrony
Check whether systemd-timesyncd is running — it conflicts with chrony:
sudo systemctl disable --now systemd-timesyncd
Configure NTS (Authenticated Time)
The main configuration file is /etc/chrony.conf (or /etc/chrony/chrony.conf on Debian-family systems). Open it and replace or supplement the existing server/pool directives.
Locate the config file
sudo find /etc -name 'chrony.conf' 2>/dev/null
Replace pool/server lines with NTS-enabled sources
The following public servers support NTS. Using three gives chrony enough sources to detect and discard a falseticker.
sudo tee /etc/chrony.conf <<'EOF'
# NTS-enabled sources (TLS-authenticated NTP)
server time.cloudflare.com iburst nts
server nts.netnod.se iburst nts
server ptbtime1.ptb.de iburst nts
# Fall back to unauthenticated if NTS fails (remove if strict auth required)
# pool pool.ntp.org iburst
# Store NTS keys/cookies between restarts
ntsserverkey /etc/chrony/nts.key
ntsservercert /etc/chrony/nts.crt
# Record drift for faster convergence after reboot
driftfile /var/lib/chrony/drift
# Step the clock on the first four updates only;
# slew afterwards to avoid disrupting running services
makestep 1.0 3
# Log tracking, measurements, and statistics
logdir /var/log/chrony
log tracking measurements statistics
# Allow chronyc queries only from localhost
bindcmdaddress 127.0.0.1
bindcmdaddress ::1
cmdallow 127.0.0.1
# Harden: deny all NTP client queries (this host is not a server)
denyntp all
EOF
Note: The ntsserverkey / ntsservercert lines are only needed if this host will serve time to other hosts over NTS. For a pure client, remove those two lines. Also remove denyntp all if you intend to serve time on your LAN.
Enable and Start chrony
sudo systemctl enable --now chronyd
On Debian/Ubuntu the unit is also called chronyd; confirm with systemctl status chronyd. Give it 30–60 seconds to perform the initial NTS handshake and take its first measurements.
Verify NTS Authentication and Sync Status
Check source activity
chronyc sources -v
You will see output similar to (will vary):
# Realistic abbreviated example — your offsets and reach will differ
MS Name/IP address Stratum Poll Reach LastRx Last sample
^* time.cloudflare.com 3 6 377 42 +0.8ms[+1.1ms] +/- 12ms
^+ nts.netnod.se 1 6 377 43 +1.2ms[+1.4ms] +/- 28ms
^+ ptbtime1.ptb.de 1 6 377 44 -0.5ms[-0.3ms] +/- 19ms
The ^* prefix means the source is selected. Reach of 377 (octal) means all eight recent polls succeeded.
Confirm NTS is active
chronyc authdata
Each NTS-authenticated source shows NTS in the Mode column and a non-zero key ID. A blank or - means authentication is not in use for that source — recheck your config or firewall (NTS uses TCP/4460 for the key exchange).
Inspect tracking statistics
chronyc tracking
Key fields to read: System time (current offset from best source), RMS offset (rolling average), and Frequency (clock drift rate in ppm). A healthy desktop or server typically shows system time offset under 10 ms and drift under ±50 ppm.
Monitor Drift Over Time
chrony writes a drift file so it can pre-correct the clock on next boot. You can also read ongoing statistics from the log files you enabled:
sudo tail -f /var/log/chrony/tracking.log
For systemd-integrated alerting, query the offset programmatically:
chronyc tracking | awk '/System time/ {print $4, $5}'
Wrap this in a cron job or a Prometheus textfile collector if you want drift alerts. A threshold of ±500 ms is a reasonable warning; ±1000 ms should page someone.
Firewall — open NTS key-exchange port
NTS performs its TLS handshake on TCP 4460. Regular NTP time exchange still uses UDP 123. Ensure outbound access on both:
# firewalld (Fedora/RHEL)
sudo firewall-cmd --permanent --add-service=ntp
sudo firewall-cmd --permanent --add-port=4460/tcp
sudo firewall-cmd --reload
# ufw (Ubuntu/Debian)
sudo ufw allow out 123/udp
sudo ufw allow out 4460/tcp
Hardening Checklist
- Use at least three NTS sources from different operators so a single compromised server cannot skew your clock.
- Set
makestep 1.0 3(step only on first three adjustments) to prevent large sudden jumps on long-running systems. - Remove or comment out any unauthenticated
poollines if your threat model requires guaranteed authentication. - Restrict
chronycaccess to localhost withbindcmdaddressandcmdallow— the control socket can be used to force a time step. - On servers that act as internal NTP sources, serve time to LAN clients over NTS by generating a TLS certificate (e.g., via Let's Encrypt or an internal CA) and pointing
ntsserverkey/ntsservercertat it.
Troubleshooting
chronyc authdata shows no NTS
TCP 4460 is likely blocked outbound. Verify with sudo ss -tnp | grep chronyd and check your firewall. Also confirm the server hostname resolves — NTS validation depends on matching the TLS certificate to the DNS name.
Sources show low Reach values or question marks
Run journalctl -u chronyd -n 50 to see resolver or TLS errors. Transient DNS failures at startup are common; give chrony two to three minutes after boot before diagnosing further.
Large initial offset causes step rejection
If the clock is more than makestep's threshold off and you have exhausted the initial step count, force a one-time step as root:
sudo chronyc makestep
Conflict with systemd-timesyncd
Both daemons bind UDP 123. If chrony fails to start, confirm timesyncd is fully stopped: systemctl status systemd-timesyncd. On Ubuntu, timedatectl set-ntp false also disables it cleanly.
Frequently asked questions
- What is the difference between NTP and NTS?
- NTP (UDP 123) synchronizes the clock but has no built-in authentication of the time server. NTS wraps NTP inside a TLS 1.3 handshake so the server's identity is cryptographically verified before any time data is accepted.
- Can I keep a fallback unauthenticated pool alongside NTS sources?
- Yes, chrony will prefer authenticated sources. However, if your security policy requires guaranteed NTS authentication, remove unauthenticated pool lines entirely to prevent silent fallback.
- Does chrony replace or conflict with systemd-timesyncd?
- Both bind UDP 123, so they cannot run simultaneously. Disable systemd-timesyncd before starting chrony. On Ubuntu, 'timedatectl set-ntp false' handles this cleanly.
- How much clock skew will break Kerberos authentication?
- The default Kerberos clockskew tolerance is 300 seconds (5 minutes). Exceeding it causes KRB5KRB_AP_ERR_SKEW errors and failed authentication for SSH, NFS, and any GSSAPI service.
- How do I serve authenticated time to other hosts on my LAN?
- Point ntsserverkey and ntsservercert at a valid TLS certificate for your server's hostname, remove 'denyntp all', and allow inbound TCP 4460 and UDP 123. LAN clients then use 'server yourhost iburst nts' in their chrony.conf.
Related guides
Manage Secrets with Ansible Vault
Encrypt Ansible secrets with AES-256 using ansible-vault: encrypt files and inline vars, automate with password files, and isolate group-level secrets with vault IDs.
AppArmor Explained
Learn how AppArmor profiles work, how to switch between enforce and complain mode, create new profiles, and diagnose access denials on Ubuntu, Debian, and Arch.
Apply CIS Benchmarks with OpenSCAP
Use OpenSCAP and scap-security-guide to evaluate, report on, and remediate Linux systems against CIS Benchmarks — covering install, eval, and automation.
How to Audit a Linux System with auditd
Set up auditd on Linux to track file access, syscalls, and privilege use. Covers persistent rules, file watches, ausearch, and aureport across major distros.