Network Time Security with Chrony on Linux

Network Time Security (NTS) provides cryptographic authentication and integrity verification for the Network Time Protocol (NTP), protecting Linux systems against time spoofing and man-in-the-middle attacks. This article explains how the NTS protocol functions within Chrony—the default NTP client and server on modern Linux distributions—detailing its two-phase architecture, client and server configuration, and the commands required to verify secure time synchronization.

Understanding NTS Architecture in Chrony

Traditional NTP relies on unencrypted UDP packets sent over port 123, leaving network systems vulnerable to packet alteration and replay attacks. NTS resolves this by combining public key infrastructure (PKI) with symmetric cryptography. Chrony implements NTS through two distinct phases:

  1. NTS Key Establishment (NTS-KE): The Chrony client establishes a standard TLS connection to an NTS-KE server over TCP port 4460. During this handshake, the client validates the server's identity using standard TLS certificates. Once verified, the server generates symmetric encryption keys and encapsulates them inside cryptographic cookies, which are then passed back to the client over TLS. The TLS session is closed once this exchange completes.
  2. NTS-Secured NTP: The Chrony client sends standard NTP packets over UDP port 123, supplemented with NTS extension fields. These extensions include one of the received cookies and a cryptographic authentication code (AEAD). The server uses its master key to decrypt the cookie, retrieve the symmetric key, verify packet integrity, and attach a fresh cookie to the response.

This dual-stage mechanism enables stateless operation on the server side, ensuring high scalability while guaranteeing that the Linux host only synchronizes with authenticated time sources.

Configuring Chrony as an NTS Client

Most modern Linux distributions, such as Debian, Ubuntu, Fedora, and RHEL, ship with Chrony versions supporting NTS (version 4.0 and newer). To enable NTS, adjust Chrony’s primary configuration file, typically located at /etc/chrony.conf or /etc/chrony/chrony.conf.

1. Define NTS Time Sources

Add NTS-capable time servers by appending the nts directive to the server declaration:

server time.cloudflare.com iburst nts
server ptbtime1.ptb.de iburst nts
server nts.netnod.se iburst nts

2. Specify Trusted Certificates

Chrony must validate the TLS certificates presented during the NTS-KE handshake. You can declare the path to the system CA bundle using the ntstrustedcerts directive:

If no certificate path is explicitly defined, Chrony attempts to use the default system trust store automatically.

3. Apply Configuration

Restart the Chrony service via systemctl to apply changes:

sudo systemctl restart chronyd

(On Debian/Ubuntu systems, the service name may be chrony instead of chronyd.)

Configuring Chrony as an NTS Server

To configure a Linux machine as an NTS server for local or network clients, Chrony requires valid TLS certificates (such as those obtained via Let's Encrypt):

# NTP server settings
local stratum 2
allow 192.168.1.0/24

# NTS server settings
ntsservercert /etc/ssl/certs/ntp-server.crt
ntsserverkey /etc/ssl/private/ntp-server.key

Chrony will automatically bind to TCP port 4460 to serve NTS-KE requests and UDP port 123 for secure NTP transactions.

Verifying NTS Operation

To confirm that Chrony is actively communicating via NTS, execute the chronyc command-line utility.

Run the authdata command:

chronyc authdata

In the output table, observe the KeyID and Type columns:

Run the sources command:

chronyc sources -v

Look for the state flag next to the configured server. A * indicates that the clock is actively synchronizing with that authenticated source. If authentication fails, Chrony rejects the response and refuses to adjust the system clock, preventing unauthorized network manipulations from compromising system logs, cryptographic tokens, or scheduled tasks.