Chrony: GPS Stratum-1 Time Server

From MattWiki


When it comes to keeping track of time, a substantial number of people rely on the internet or the internal clocks that exist in the devices that they use each day. This is alright for a casual setting like your home, but in a professional or public building, it is better to have the exact, accurate time displayed without inconsistencies on all devices.[1]

This document will walk you thru how to build a Stratum-1 Time Server on a Raspberry Pi using the Raspberry Pi OS (previously called Raspbian).

What we are tying to accomplish

When operation on a medium to large network (over 100 users) or when

In a nutshell, we will be receiving time data from the Global Positioning System (GPS) and distributing it via the Network Time Protocol to other computers on the network.

Note: In this how-to, we will be building this on a 10.0.0.0/24 network with the gateway at 10.0.0.1. The system will have the IP address of 10.0.0.3 and the host name of time01.local.

Software Used

This How-To will being using the following software/version

Hardware

This document is designed to be used on a Raspberry Pi 4b with the Adafruit Ultimate GPS Hat, but that is not your only option. I have build this same system on a Raspberry Pi 3b and on a Raspberry Pi Zero 2 with the Waveshare L76X Multi-GNSS and the Waveshare Ethernet/USB Hat.

Prepare the System

Image the SD Card

We will be using Raspberry Pi OS Lite (64-bit) distribution for this project. The quickest and easiest way of installing it is via the Raspberry Pi Imager found on Raspberry Pi's website.

Download and install Raspberry Pi Imager to a computer with an SD card reader. Put the SD card you'll use with your Raspberry Pi into the reader and run Raspberry Pi Imager.

Configuring the Image

Using the gear in the lower right hand side of the imager, set the following parameters before writing the image.

  • Set hostname
  • Enable SSH
    • You may choose to use a password or you may add a public-key to increase security
  • Set your Time zone
  • Set Keyboard layout

Once everything is set, you may write the image by clicking write.

Update the system

Start by bring the system up-to-date and install needed software

sudo apt update
sudo apt -y dist-upgrade
sudo rpi-update
sudo apt -y install gpsd gpsd-tools chrony

Set a Static IP address

It is important that the system as a IP address that does not change, or known as a Static IP Address.

First confirm that the networking service is disabled and the dhcpcd service is enabled.

sudo systemctl disable networking.service
sudo systemctl enable dhcpcd.service

Once you know that you are using dhcpcd, then configure it for static use.

/etc/dhcpcd.conf

interface eth0
static ip_address=10.0.0.3/24
static routers=10.0.0.1
static domain_name_servers=127.0.0.1
static domain_search=local

Enable Serial Port Interface

The GPS module sends serial data via the onboard uart port. We must enable this port before we may read any data from the GPS module.

echo "enable_uart=1" |sudo tee -a /boot/config.txt

Enable PPS Support

The Adafruit Ultimate GPS HAT is set for GPIO pin 4, so that is what we will use.

echo "dtoverlay=pps-gpio,gpiopin=4" |sudo tee -a /boot/config.txt

If you are instead using the Waveshare L76X Multi-GNSS Hat instead, set the GPIO pin to 18.

echo "dtoverlay=pps-gpio,gpiopin=18" |sudo tee -a /boot/config.txt

Disable Wi-Fi and Bluetooth Support (Optional)

Since we are using an ethernet cable and not a Wi-Fi connection, it is best practice to disable Wi-Fi on the board, by running the following command.

echo "dtoverlay=disable-wifi" |sudo tee -a /boot/config.txt

Likewise, to disable Bluetooth, run the following command.

echo "dtoverlay=disable-bt" |sudo tee -a /boot/config.txt

Reboot

Reboot the system to allow access to the newly configured serial interface.

sudo reboot

Configure GPSd

/etc/default/gpsd

# Default settings for the gpsd init script and the hotplug wrapper.
# Start the gpsd daemon automatically at boot time
START_DAEMON="true"

# Use USB hotplugging to add new USB devices automatically to the daemon
USBAUTO="false"

# Devices gpsd should collect to at boot time.
# They need to be read/writeable, either by user gpsd or the group dialout.
DEVICES="/dev/ttyS0 /dev/pps0"

# Other options you want to pass to gpsd
GPSD_OPTIONS="-n"

Enable GPSd to start at boot time, then start the service.

sudo systemctl enable gpsd.service
sudo systemctl start gpsd.service

Configure Chrony

/etc/chrony/chrony.conf

# Welcome to the chrony configuration file. See chrony.conf(5) for more
# information about usable directives.

# Add GPS Tracking
refclock SHM 0 refid GPS offset 0.000 precision 1e-3 poll 3 noselect
refclock PPS /dev/pps0 refid PPS precision 1e-7

# Use other local NTP Servers
server 10.0.0.2 iburst
server 10.0.0.3 iburst

# Use Debian vendor zone.
pool 0.debian.pool.ntp.org iburst maxsources 2
pool 1.debian.pool.ntp.org iburst maxsources 2
pool 2.debian.pool.ntp.org iburst maxsources 2

# Serve time even if not synchronized to a time source.
local stratum 10

# Use NTP sources found in /etc/chrony/sources.d.
sourcedir /etc/chrony/sources.d

# This directive specify the location of the file containing ID/key pairs for
# NTP authentication.
keyfile /etc/chrony/chrony.keys

# This directive specify the file into which chronyd will store the rate
# information.
driftfile /var/lib/chrony/chrony.drift

# Save NTS keys and cookies.
ntsdumpdir /var/lib/chrony

# Uncomment the following line to turn logging on.
log tracking measurements statistics

# Log files location.
logdir /var/log/chrony

# Allow access from local network
allow 10.0.0.0/24

# Stop bad estimates upsetting machine clock.
maxupdateskew 100.0

# This directive enables kernel synchronization (every 11 minutes) of the
# real-time clock. Note that it can't be used along with the 'rtcfile' directive.
rtcsync
rtcautotrim 30

# Step the system clock instead of slewing it if the adjustment is larger than
# one second, but only in the first three clock updates.
makestep 1.0 3

# Get TAI-UTC offset and leap seconds from the system tz database.
# This directive must be commented out when using time sources serving
# leap-smeared time.
leapsectz right/UTC

Enable Chrony to start at boot time, then start the service.

sudo systemctl enable chronyd.service
sudo systemctl start chronyd.service

Testing and Monitoring

To check if chrony is synchronized, make use of the tracking, sources, and sourcestats commands.

Tracking

To check chrony tracking, issue the following command:

$ chronyc tracking
Reference ID    : 50505300 (PPS)
Stratum         : 1
Ref time (UTC)  : Tue May 09 19:41:36 2023
System time     : 0.000000074 seconds fast of NTP time
Last offset     : +0.000000024 seconds
RMS offset      : 0.000000330 seconds
Frequency       : 9.217 ppm fast
Residual freq   : +0.000 ppm
Skew            : 0.004 ppm
Root delay      : 0.000000001 seconds
Root dispersion : 0.000026438 seconds
Update interval : 16.0 seconds
Leap status     : Normal

The fields are as follows:

  • Reference ID - This is the reference ID and name (or IP address) if available, of the server to which the computer is currently synchronized. Reference ID is a hexadecimal number to avoid confusion with IPv4 addresses.
  • Stratum - The stratum indicates how many hops away from a computer with an attached reference clock we are. Such a computer is a stratum-1 computer, so the computer in the example is two hops away (that is to say, a.b.c is a stratum-2 and is synchronized from a stratum-1).
  • Ref time - This is the time (UTC) at which the last measurement from the reference source was processed.
  • System time - In normal operation, chronyd never steps the system clock, because any jump in the timescale can have adverse consequences for certain application programs. Instead, any error in the system clock is corrected by slightly speeding up or slowing down the system clock until the error has been removed, and then returning to the system clock’s normal speed. A consequence of this is that there will be a period when the system clock (as read by other programs using the gettimeofday() system call, or by the date command in the shell) will be different from chronyd's estimate of the current true time (which it reports to NTP clients when it is operating in server mode). The value reported on this line is the difference due to this effect.
  • Last offset - This is the estimated local offset on the last clock update.
  • RMS offset - This is a long-term average of the offset value.
  • Frequency - The "frequency" is the rate by which the system’s clock would be wrong if chronyd was not correcting it. It is expressed in ppm (parts per million). For example, a value of 1 ppm would mean that when the system’s clock thinks it has advanced 1 second, it has actually advanced by 1.000001 seconds relative to true time.
  • Residual freq - This shows the "residual frequency" for the currently selected reference source. This reflects any difference between what the measurements from the reference source indicate the frequency should be and the frequency currently being used. The reason this is not always zero is that a smoothing procedure is applied to the frequency. Each time a measurement from the reference source is obtained and a new residual frequency computed, the estimated accuracy of this residual is compared with the estimated accuracy (see skew next) of the existing frequency value. A weighted average is computed for the new frequency, with weights depending on these accuracies. If the measurements from the reference source follow a consistent trend, the residual will be driven to zero over time.
  • Skew - This is the estimated error bound on the frequency.
  • Root delay - This is the total of the network path delays to the stratum-1 computer from which the computer is ultimately synchronized. Root delay values are printed in nanosecond resolution. In certain extreme situations, this value can be negative. (This can arise in a symmetric peer arrangement where the computers’ frequencies are not tracking each other and the network delay is very short relative to the turn-around time at each computer.)
  • Root dispersion - This is the total dispersion accumulated through all the computers back to the stratum-1 computer from which the computer is ultimately synchronized. Dispersion is due to system clock resolution, statistical measurement variations etc. Root dispersion values are printed in nanosecond resolution.
  • Leap status - This is the leap status, which can be Normal, Insert second, Delete second or Not synchronized.

Sources

The sources command displays information about the current time sources that chronyd is accessing.

The optional argument -v can be specified, meaning verbose. In this case, extra caption lines are shown as a reminder of the meanings of the columns.

   .-- Source mode  '^' = server, '=' = peer, '#' = local clock.
 / .- Source state '*' = current best, '+' = combined, '-' = not combined,
| /             'x' = may be in error, '~' = too variable, '?' = unusable.
||                                                 .- xxxx [ yyyy ] +/- zzzz
||      Reachability register (octal) -.           |  xxxx = adjusted offset,
||      Log2(Polling interval) --.      |          |  yyyy = measured offset,
||                                \     |          |  zzzz = estimated error.
||                                 |    |           \
MS Name/IP address         Stratum Poll Reach LastRx Last sample
===============================================================================
#? GPS                           0   3     0     -     +0ns[   +0ns] +/-    0ns
#* PPS                           0   4   377    21    +14ns[  +18ns] +/- 1000ns
^- time01.local                  1  10   377   205   +287us[ +288us] +/-  482us
^- time02.local                  1  10   377   329   -110us[ -109us] +/-  578us
^- ns.nts.umn.edu                2  10   377   202  -4882us[-4881us] +/-   19ms
^- nss.nts.umn.edu               2  10   377   203  -5424us[-5424us] +/-   19ms
^- ntp1.state.mn.us              2  10   377   190  -6836us[-6836us] +/-   19ms
^- ntp2.state.mn.us              2  10   377   200    -13ms[  -13ms] +/-   28ms
^- ntp3.state.mn.us              2  10   377   203  -3171us[-3170us] +/-   24ms
^- ns4.state.mn.us               2  10   377   394  -5350us[-5348us] +/-   24ms

The columns are as follows:

  • M - This indicates the mode of the source. ^ means a server, = means a peer and # indicates a locally connected reference clock.
  • S - This column indicates the state of the sources. "*" indicates the source to which chronyd is currently synchronized. "+" indicates acceptable sources which are combined with the selected source. "-" indicates acceptable sources which are excluded by the combining algorithm. "?" indicates sources to which connectivity has been lost or whose packets do not pass all tests. "x" indicates a clock which chronyd thinks is a falseticker (its time is inconsistent with a majority of other sources). "~" indicates a source whose time appears to have too much variability. The "?" condition is also shown at start-up, until at least 3 samples have been gathered from it.
  • Name/IP address - This shows the name or the IP address of the source, or reference ID for reference clock.
  • Stratum - This shows the stratum of the source, as reported in its most recently received sample. Stratum 1 indicates a computer with a locally attached reference clock. A computer that is synchronized to a stratum 1 computer is at stratum 2. A computer that is synchronized to a stratum 2 computer is at stratum 3, and so on.
  • Poll - This shows the rate at which the source is being polled, as a base-2 logarithm of the interval in seconds. Thus, a value of 6 would indicate that a measurement is being made every 64 seconds. chronyd automatically varies the polling rate in response to prevailing conditions.
  • Reach - This shows the source’s reach register printed as an octal number. The register has 8 bits and is updated on every received or missed packet from the source. A value of 377 indicates that a valid reply was received for all of the last eight transmissions.
  • LastRx - This column shows how long ago the last sample was received from the source. This is normally in seconds. The letters m, h, d or y indicate minutes, hours, days or years. A value of 10 years indicates there were no samples received from this source yet.
  • Last sample - This column shows the offset between the local clock and the source at the last measurement. The number in the square brackets shows the actual measured offset. This may be suffixed by ns (indicating nanoseconds), us (indicating microseconds), ms (indicating milliseconds), or s (indicating seconds). The number to the left of the square brackets shows the original measurement, adjusted to allow for any slews applied to the local clock since. The number following the +/- indicator shows the margin of error in the measurement. Positive offsets indicate that the local clock is ahead of the source.

Sources Stats

The sourcestats command displays information about the drift rate and offset estimation process for each of the sources currently being examined by chronyd.

The optional argument -v can be specified, meaning verbose. In this case, extra caption lines are shown as a reminder of the meanings of the columns.

                             .- Number of sample points in measurement set.
                            /    .- Number of residual runs with same sign.
                           |    /    .- Length of measurement set (time).
                           |   |    /      .- Est. clock freq error (ppm).
                           |   |   |      /           .- Est. error in freq.
                           |   |   |     |           /         .- Est. offset.
                           |   |   |     |          |          |   On the -.
                           |   |   |     |          |          |   samples. \
                           |   |   |     |          |          |             |
Name/IP Address            NP  NR  Span  Frequency  Freq Skew  Offset  Std Dev
==============================================================================
GPS                         0   0     0     +0.000   2000.000     +0ns  4000ms
PPS                        47  20   736     -0.000      0.002     -0ns   888ns
time01.local               34  18  122m     +0.010      0.003   +316us  8985ns
time02.local               25  11  116m     +0.009      0.010   -130us    24us
ns.nts.umn.edu             34  25  121m     -0.173      0.540  -2370us  1503us
nss.nts.umn.edu            34  18  121m     -0.379      0.496  -5236us  1603us
ntp1.state.mn.us           34  16  121m     -0.020      0.559  -2302us  1631us
ntp2.state.mn.us           34  18  121m     +0.274      0.750  -1820us  2108us
ntp3.state.mn.us           34  19  121m     +0.268      0.476  -2224us  1496us
ns4.state.mn.us            33  20  118m     +0.212      0.638  -1939us  2060us

The columns are as follows:

  • Name/IP address - This is the name or IP address of the NTP server (or peer) or reference ID of the reference clock to which the rest of the line relates.
  • NP - This is the number of sample points currently being retained for the server. The drift rate and current offset are estimated by performing a linear regression through these points.
  • NR - This is the number of runs of residuals having the same sign following the last regression. If this number starts to become too small relative to the number of samples, it indicates that a straight line is no longer a good fit to the data. If the number of runs is too low, chronyd discards older samples and re-runs the regression until the number of runs becomes acceptable.
  • Span - This is the interval between the oldest and newest samples. If no unit is shown the value is in seconds. In the example, the interval is 46 minutes.
  • Frequency - This is the estimated residual frequency for the server, in parts per million. In this case, the computer’s clock is estimated to be running 1 part in 109 slow relative to the server.
  • Freq Skew - This is the estimated error bounds on Freq (again in parts per million).
  • Offset - This is the estimated offset of the source.
  • Std Dev - This is the estimated sample standard deviation.

Chrony Site Report

The Chrony Site Report allows you to monitor the current status of the process via a web browser.

curl -Ls -o /usr/local/bin/chrony-status.sh
    https://gist.github.com/mattrude/a5b91fd88ec35969af121bd9b3b58a91/raw/chrony-status.sh
sudo chmod +x /usr/local/bin/chrony-status.sh

Once installed, modify your crontab by adding the following.

# m h  dom mon dow   command
*/5 *  *   *   *     /usr/local/bin/chrony-status.sh -w

Now, once every 5 minutes, a new report will be generated at /var/www/html/time.txt

Optional Configurations

Symmetric Key Authentication

Symmetric key authentication provides authentication for server-to-server connections. Apposed to NTS (described below), Symmetric Key Authentication requires both servers to be configured with the same key (symmetrically) before they may communicate with each other. Symmetric key authentication dose NOT require an TLS Certificate.

Creating the needed keys
chronyc keygen 1 AES256 128 |sudo tee -a /etc/chrony/chrony.keys

Network Time Security (NTS)

Network Time Security (NTS) provides cryptographic security for the client-server mode of the Network Time Protocol (NTP). This allows users to obtain time in an authenticated manner.

The NTS protocol is divided into two phases:

  1. NTS key exchange: Establishes the necessary key material between the NTP client and the server, using a Transport Layer Security (TLS) handshake (the same public key infrastructure as the web). Once the keys are exchanged, the TLS channel is closed and the protocol enters the second phase.
  2. NTP authentication: Authenticates NTP time synchronization packets using the results of the TLS handshake. For more information, refer to RFC 8915.
sudo apt install certbot

chrony-nts-deploy-hook.sh

#!/bin/bash
mkdir -p /etc/chrony/nts-cert
if [ -d /etc/letsencrypt/live/time.theodin.network/ ]; then
    cp -RL /etc/letsencrypt/live/time.theodin.network/privkey.pem /etc/chrony/nts-cert/
    cp -RL /etc/letsencrypt/live/time.theodin.network/fullchain.pem /etc/chrony/nts-cert/
    chown root:_chrony /etc/chrony/nts-cert
    chown root:_chrony /etc/chrony/nts-cert/privkey.pem
    chown root:_chrony /etc/chrony/nts-cert/fullchain.pem
    chmod 640 /etc/chrony/nts-cert/privkey.pem
    chmod 640 /etc/chrony/nts-cert/fullchain.pem
    systemctl restart chronyd.service
fi
sudo certbot certonly --dns-cloudflare --dns-cloudflare-credentials /etc/letsencrypt/cloudflaire-theodin.ini -d time.theodin.network --renew-hook /etc/letsencrypt/renewal-hooks/deploy/chrony-nts-deploy-hook.sh

/etc/chrony/chrony.conf

ntsntpserver time.theodin.network
ntsserverkey /etc/chrony/nts-cert/privkey.pem
ntsservercert /etc/chrony/nts-keys/fullchain.pem
ntsdumpdir /var/lib/chrony
ntsprocesses 3
maxntsconnections 512

SNMP Configuration (Optional)

sudo apt install snmpd
curl -Ls https://raw.githubusercontent.com/librenms/librenms-agent/master/snmp/chrony -O /etc/snmp/chrony
sudo chmod +x /etc/snmp/chrony
echo "extend chronyd /etc/snmp/chrony" |sudo tee -a /etc/snmp/snmpd.conf

DNSMasq / PiHole DHCP Configuration (Optional)

If you are using DNSMasq or PiHole for dhcp provisioning you may added the dhcp option 42 to your /etc/dnsmasq.conf or if dnsmasq.conf is being managed by an outside source (say PiHole) you may put it in it's own file (ie. /etc/dnsmasq.d/42-pihole-dhcp-ntp.conf).

/etc/dnsmasq.d/42-pihole-dhcp-ntp.conf

dhcp-option=42,10.0.0.2

Or if you have more then one NTP server, you may add them as following.

dhcp-option=42,10.0.0.2,10.0.0.3,10.0.0.4

Other Resources

Considerations

Other Configuration Examples

Stories

  • Tardis and Trinity College, Dublin - In October 2002, the public web server at Trinity College in Dublin started experiencing an unusual large number of requests, the traffic was ultimately traced to misbehaving copies of a program called Tardis. Ultimately, the solution was to modify the web server configuration so as to deliver a customized version of the home page (greatly reduced in size) and to return a bogus time value, which caused most of the clients to choose a different time server.
  • Flawed Routers Flood University of Wisconsin Internet Time Server - May 2003, the University of Wisconsin - Madison found that it was the recipient of a continuous large scale flood of inbound Internet traffic destined for one of the campus' public Network Time Protocol (NTP) servers. The flood traffic rate was hundreds-of-thousands of packets-per-second, and hundreds of megabits-per-second.
  • D-Link settles dispute with 'time geek' - April 2006, A dispute between D-Link and self-confessed "time geek" Poul-Henning Kamp over the issue went public in April after Kamp wrote an open letter to D-Link prompted by his frustrations in attempting to get the firm to acknowledge that a misconfigured implementation of NTP on its kit left him staring at a bill of around $8,800 a year.
  • NTP server misuse and abuse - NTP server misuse and abuse covers a number of practices which cause damage or degradation to a Network Time Protocol (NTP) server, ranging from flooding it with traffic (effectively a DDoS attack) or violating the server's access policy or the NTP rules of engagement.

References

  1. TimeMachines (2020-09-03). "Reasons Why You Need an NTP Server". Time Machines. Retrieved 2023-05-09.