By default, FreeBSD uses the standard ntpd(8)
daemon that is built with the FreeBSD world. This daemon only supports symmetric keys for encryption which must be configured per client/server duo, and thus cannot at-scale guarantee authenticity of the data received from the Network Time Protocol (NTP) server. Recent developments like RFC 8915/Network Time Security (NTS) have allowed for the automatic establishment of those keys over TLS. With a focus on both authenticity (so an attacker on-the-wire cannot set your clock forwards/backwards) and privacy (so a passive attacker cannot identify systems when they change networks), NTS seems to be the way forward, so let’s use it on a FreeBSD machine.
There are two main options for utilizing NTS on FreeBSD: ntpsec(8)
and chrony(8)
. Recently, a rust implementation seems to be getting popular (ntpd-rs). There is also chrony-lite(8)
, but this is literally chrony(8)
without NTS support. So we go for chrony(8)
.
Beginning by either building the net/chrony
port or installing the chrony
pkg, the default configuration is found in /usr/local/etc/chrony.conf
.
First we need to disable the plaintext NTP server being used, by commenting out
pool 0.freebsd.pool.ntp.org iburst
by adding a !
at the front:
! pool 0.freebsd.pool.ntp.org iburst
There are not so many public NTS servers available, but they are documented on jauderho/nts-servers on GitHub. I chose one from Netnod, Cloudflare, and an independent in Switzerland. In the configuration file, we simply add:
server ntp.3eck.net iburst nts
server nts.netnod.se iburst nts
server time.cloudflare.com iburst nts
The nts
at the end of each of those lines ensures that the time from each of the servers will only be used if it is properly authenticated.
We restart chrony and confirm the servers are being communicated to:
$ service chronyd onerestart
Stopping chronyd.
Waiting for PIDS: 34158 34395
Starting chronyd.
$ chronyc -Na sources
MS Name/IP address Stratum Poll Reach LastRx Last sample
===============================================================================
^+ ntp.3eck.net 3 6 17 62 -971us[ -971us] +/- 24ms
^+ nts.netnod.se 1 6 17 62 -3319us[-3319us] +/- 37ms
^* time.cloudflare.com 3 6 17 64 -1501us[-1699us] +/- 22ms
Finally, we need to disable the main ntp(8)
daemon from starting on boot, and enable chrony(8)
to start, too:
$ sysrc ntpdate_enable="NO"
ntpdate_enable: YES -> NO
$ sysrc ntpd_enable="NO"
ntpd_enable: YES -> NO
$ sysrc chronyd_enable="YES"
chronyd_enable: NO -> YES
will do that.
Using tcpdump, we can confirm the key exchange is happening on port 4460, too.