Authenticated NTP time synchronization

Configuring authentication in NTP time synchronization

© Copyright 2014, Christophe Wolfhugel
Last update: December 23 2015 (new GQ key for ntp.sentrion.eu)


End of this experimentation (December 2015)

This NTP autokey experimentation lasted nearly two years and I have decided to put an end to it: actually either the implementation is so complex that I fail to understand its logic, or it is so broken that I do not wish to spend the time fixing it. I would have been fully happy with the Public Certificate scheme, which is easy to setup server side, but for which validation does not seem implemented on the client side.

The documentation and keys provided below are given for education purposes only but can no longer be used. You can still query my NTP servers as they are public. I have switched to using symetric keys for my internal network and clients.


Summary

After having spent several pair of days over the last years by reading, re-reading available documentation, and then exprimenting again and again, I have come to a rather disappointing conclusion regarding the use of the autokey feature in the NTP implementation: the only autokey identity scheme I have been able to configure and use is GQ (Guillou-Quisquater). Note to readers: I did not try to configure MV identity scheme.

This short article will give you some insight on how I did configure my own strtum 2 reference time servers (ntp.sentrion.eu and ntp.wolfhugel.eu), and how I configured my NTP client nodes (with this article showing smile.wolfhugel.eu as an example) to get authenticated timestamps from the reference servers. Feel free to replicate this configuration and use it for yourself to get time from my machines.

Note to software, hardware and boxes vendors: due to the quantities you sell or distribute, I am sorry, but you are not welcome to synchronize time on these servers as they do not have the capacity to handle thousands or millions or client devices: create your own infrastructure for your customers, this documentation might help you doing so.

All my experimentations were made with NTP 4.2.6, all being issued from several quite recent Debian distributions.

If you just want to fetch authenticated timestamps from my servers without knowing the internals, just jump to this section and taylor the instructions to your own NTP server and hopefully it will work!

Comments are more than welcome, please send them to chris at wolfhugel dot eu.

Reference server configuration (ntp.sentrion.eu)

This is one of my reference time servers, the second being ntp.wolfhugel.eu which is configured in a similar way. Both servers take time from stratum 1 and stratum 2 servers on the Internet (and these sessions are unfortunately not authenticated). The servers do peer each other in an authenticated manner and they serve time to my internal network, as well as as to any Internet user wishing to fetch time, be it authenticated or not. The public service would cease should it become abused. I do use "/usr/local/etc/ntp" to store all keys material.

First, I will need to generate a trusted certificate and the GQ key and parameters on this node:

$ cd /usr/local/etc/ntp
$ ntp-keygen -T -G -m 2048
Parameter "-T" is for trusted self signed certificate, "-G" is for the GQ authentication model, and finally "-m 2048" indicates a 2048-bit key size. Once this has been generated the directory looks like this:
-rw-r--r-- 1 root staff  490 Aug  4 14:47 ntpkey_GQkey_ntp.sentrion.eu.3616152474
-rw-r--r-- 1 root staff 1203 Aug  4 14:47 ntpkey_RSA-MD5cert_ntp.sentrion.eu.3616152474
-rw-r--r-- 1 root staff 1901 Aug  4 14:47 ntpkey_RSAhost_ntp.sentrion.eu.3616152474
lrwxrwxrwx 1 root staff   45 Aug  4 14:47 ntpkey_cert_ntp.sentrion.eu -> ntpkey_RSA-MD5cert_ntp.sentrion.eu.3616152474
lrwxrwxrwx 1 root staff   39 Aug  4 14:47 ntpkey_gqkey_ntp.sentrion.eu -> ntpkey_GQkey_ntp.sentrion.eu.3616152474
lrwxrwxrwx 1 root staff   41 Aug  4 14:47 ntpkey_host_ntp.sentrion.eu -> ntpkey_RSAhost_ntp.sentrion.eu.3616152474
The "ntp-keygen" command automatically generates timestamps and symbolic links. Once you have these you need to extract the GQ public parameters to be given via any means (HTTP is OK) to remote parties:
$ ntp-keygen -e > ntpkey_gqpar_ntp.sentrion.eu
$ ls -l ntpkey_gqpar_ntp.sentrion.eu
-rw-r--r-- 1 root staff 279 Aug  4 14:48 ntpkey_gqpar_ntp.sentrion.eu
This file will have to be distributed to all machines who are going to take time from you and who wish to authenticate your timestamps.

Excerpts from the configuration file:

leapfile /usr/local/etc/ntp/leap-seconds.3535228800
statsdir /var/log/ntpstats/
statistics cryptostats
filegen cryptostats file cryptostats type week enable

## Crypto stuff.
keysdir /usr/local/etc/ntp
crypto

## My servers.
peer    ntp.wolfhugel.eu autokey                ## tea (eth1)
server	x.y.z.t					## my non authenticated external source

# By default, exchange time with everybody, but don't allow configuration.
restrict -4 default kod notrap nomodify nopeer noquery
restrict -6 default kod notrap nomodify nopeer noquery

restrict 172.20.19.0 mask 255.255.255.0                 kod notrap nomodify nopeer
restrict 2001:41d0:2:c731:: mask ffff:ffff:ffff:ffff::  kod notrap nomodify nopeer

restrict 172.20.19.5             kod notrap notrust
restrict 2001:41d0:2:c731:1::5   kod notrap notrust

# Local users may interrogate the ntp server more closely.
restrict 127.0.0.1
restrict ::1

Only "cryptostats" configuration is shown above. Feel free to use additional log or statistics files as permitted by NTP. The "cryptostats" file allows you to check that the GQ parameter files are correctly used and read when needed.

For the ease of management, I do use a dedicated keys directory configured in "keysdir", and then with the "crypto" keyword I will enable cryptography, and this assumes the server name is actually the system's real hostname (i.e. "hostname" gives you the long host name).

When defining a "server" or a "peer" the important keyword here is "autokey" which indicates NTP to use the autokey feature, and in this example I will use autokey with my other server "ntp.wolfhugel.eu". Of course both servers need to be configured to use the same mechanism, i.e. GQ in this particular case.

Furthermore, the "restrict" configuration allows to enforce NTP authentication, i.e. non authenticated packets would be rejected. My restrict clauses have several levels:

Leapfile: according to all documentation read, each time autokey was involved authors indicated that a leapfile configuration option must be present. I did find no explanation as to why this is required for autokey and not when using unauthenticated connections. The current leapfile can be downloaded at ftp://time.nist.gov/pub/.

How to query my NTP servers, authenticated

This section suggests how you could configure your own NTP server in order to synchronize to both ntp.sentrion.eu and ntp.wolfhugel.eu and authenticate the timestamps. The client machine used in this example is named smile.wolfhugel.eu and takes its time only from my own servers - I could of course add additional time sources.

We will assume following in this section:

Once your NTP daemon is properly installed and running, you want to ensure you can synchronize un-authenticated with my servers by adding following lines to your configuration file:

server ntp.sentrion.eu
server ntp.wolfhugel.eu
Then restart your ntpd server and ensure it synchronizes properly to the newly added nodes. If this fails, you first need to understand why and eventually fix it. Please note that my NTP servers do not respond to queries made with ntpq.

If not already done, you need to create the directory which will be containing NTP keys: /usr/local/etc/ntp. Then you will need to retrieve my GQ parameter files (using wget or any similar tool):

These parameter files are public and you may wish to verify their PGP signature. NTP does not actually need the PGP signature, this is just for you to ensure you get the right files from me. My PGP public key can be found on quite a few PGP key servers:
pub   4096R/0xF00AF6EAC245D20B 2013-09-24 [expires: 2023-09-22]
uid                 [ultimate] Christophe Wolfhugel 
sub   4096R/0x886383DAB10C092F 2013-09-24 [expires: 2023-09-22]
You may also download it here. To verify the signature, once you have imported my PGP public key, the commands will be similar to these:
$ gpg --verify ntpkey_gqpar_ntp.sentrion.eu.asc ntpkey_gqpar_ntp.sentrion.eu
$ gpg --verify ntpkey_gqpar_ntp.wolfhugel.eu.asc ntpkey_gqpar_ntp.wolfhugel.eu
And it will tell you if the signature is valid or not.

At this stage the content of your /usr/local/etc/ntp/keys directoryshould be similar to this:

-rw-r--r-- 1 root staff  279 Aug  4 15:56 ntpkey_gqpar_ntp.sentrion.eu
-rw-r--r-- 1 root staff  284 Aug  4 15:54 ntpkey_gqpar_ntp.wolfhugel.eu

Next step for you is to generate your own certificate and key files as well as your GQ private key (even if you won't authentify with me). Unlike what I have found in other documentation, I cam to the conlusion that even your client node will need to have its own trusted certificate with the GQ private key:

$ cd /usr/local/etc/ntp
$ ntp-keygen -T -G -m 2048

Parameter "-T" is for trusted self signed certificate, "-G" is for the GQ identiy scheme, and finally "-m 2048" indicates a 2048-bit key size. Once this has been generated the directory will contain these new files and links (in addition to the GQ parameter files previously downloaded):

-rw-r--r-- 1 root staff  493 Aug  4 15:58 ntpkey_GQkey_smile.wolfhugel.eu.3616153122
-rw-r--r-- 1 root staff 1214 Aug  4 15:58 ntpkey_RSA-MD5cert_smile.wolfhugel.eu.3616153122
-rw-r--r-- 1 root staff 1904 Aug  4 15:58 ntpkey_RSAhost_smile.wolfhugel.eu.3616153122
lrwxrwxrwx 1 root staff   48 Aug  4 15:58 ntpkey_cert_smile.wolfhugel.eu -> ntpkey_RSA-MD5cert_smile.wolfhugel.eu.3616153122
lrwxrwxrwx 1 root staff   42 Aug  4 15:58 ntpkey_gqkey_smile.wolfhugel.eu -> ntpkey_GQkey_smile.wolfhugel.eu.3616153122
lrwxrwxrwx 1 root staff   44 Aug  4 15:58 ntpkey_host_smile.wolfhugel.eu -> ntpkey_RSAhost_smile.wolfhugel.eu.3616153122
(You may wish to change permissions on the private key files, but ensure ntpd can still read them).

You do not need to extract your GQ public parameters here, unless you wish to provide authenticated time stamps to your own client machines.

Final steps in your configuration are to modify ntp.conf in order to configure the use of authentication. Add following two lines:

keysdir /usr/local/etc/ntp
crypto
This will tell ntpd to enable cryptography as well as where to search for key files.

If you wish to log the cryptostats data, which I do strongly recommend, also add or ensure your configuration file contains something similar to this:

statsdir /var/log/ntpstats/
statistics cryptostats
filegen cryptostats file cryptostats type day enable

Do change the configuration to reach my servers by adding the autokey keyword:

server  ntp.sentrion.eu   autokey
server  ntp.wolfhugel.eu  autokey

Lastly, ensure you have the right restrict configuration lines in order to enforce NTP authentication when fetching time from my machines (the notrust tells NTP to ignore unauthenticated packets):

restrict ntp.wolfhugel.eu  noquery notrust
restrict ntp.sentrion.eu   noquery notrust

As a complete example you will find here the actual ntp.conf file I use on my client machine:

# /etc/ntp.conf, configuration for ntpd; see ntp.conf(5) for help
driftfile /var/lib/ntp/ntp.drift

keysdir /usr/local/etc/ntp
crypto

# Enable this if you want statistics to be logged.
statsdir /var/log/ntpstats/

statistics loopstats peerstats clockstats cryptostats
filegen loopstats file loopstats type day enable
filegen peerstats file peerstats type day enable
filegen clockstats file clockstats type day enable
filegen cryptostats file cryptostats type day enable

## My servers
server	ntp.wolfhugel.eu iburst maxpoll 7 autokey
server	ntp.sentrion.eu iburst maxpoll 7 autokey

# By default, exchange time with everybody, but don't allow configuration.
restrict -4 default ignore
restrict -6 default ignore

# My restrictions. Enforce authentification.
restrict ntp.wolfhugel.eu	noquery notrust
restrict ntp.sentrion.eu	noquery notrust

# Local users may interrogate the ntp server more closely.
restrict 127.0.0.1
restrict ::1

If everything has been configured correctly, your NTP server should start, synchronize time and validate authentication of the remote parties:

$ ntpq
ntpq> rv
associd=0 status=0615 leap_none, sync_ntp, 1 event, clock_sync,
version="ntpd 4.2.6p5@1.2349-o Wed Jul 16 16:56:23 UTC 2014 (1)",
processor="x86_64", system="Linux/3.14-2-amd64", leap=00, stratum=3,
precision=-23, rootdelay=12.727, rootdisp=29.154, refid=44.105.60.88,
reftime=d78a2e4f.8ba0179c  Mon, Aug  4 2014 17:07:43.545,
clock=d78a302e.b72107e2  Mon, Aug  4 2014 17:15:42.715, peer=35375, tc=7,
mintc=3, offset=0.072, frequency=44.817, sys_jitter=0.332,
clk_jitter=0.231, clk_wander=0.063, tai=35, leapsec=201207010000,
expire=201506280000, host="smile.wolfhugel.eu",
group="smile.wolfhugel.eu", flags=0x80043, digest="md5",
signature="md5WithRSAEncryption", update=201408041545,
cert="smile.wolfhugel.eu ntp.wolfhugel.eu 0x5", until=201508041509,
cert="smile.wolfhugel.eu ntp.sentrion.eu 0x5", until=201508041508,
cert="ntp.sentrion.eu ntp.sentrion.eu 0x5", until=201608031447,
cert="ntp.wolfhugel.eu ntp.wolfhugel.eu 0x5", until=201608031453,
cert="smile.wolfhugel.eu smile.wolfhugel.eu 0x1", until=201508041458
ntpq> pe
     remote           refid      st t when poll reach   delay   offset  jitter
==============================================================================
*tea.wolfhugel.e 213.251.128.249  2 u   35  128  377   11.903    0.092   0.736
+ntp.sentrion.eu 40.179.132.91    2 u   99  128  377   11.927    0.612   7.824
This shows the synchronization as well as information about the certificates but nothing to prove authentication was successful. You would check associations to get the authentication status:
ntpq> as

ind assid status  conf reach auth condition  last_event cnt
===========================================================
  1 35375  f61a   yes   yes   ok   sys.peer    sys_peer  1
  2 35376  f464   yes   yes   ok  candidate   reachable  6

And you can look at the auth column. The most difficult part if to ensure that the actual GQ public parameters are being used to ensure proper authentication (otherwise you would trust any self signed certificate), this can be found in the "cryptostats" log file:
56873 54395.759 0.0.0.0 ntpkey_RSAhost_smile.wolfhugel.eu.3616153122 mod 2048
56873 54395.842 0.0.0.0 ntpkey_RSA-MD5cert_smile.wolfhugel.eu.3616153122 0x1 len 796
56873 54395.856 0.0.0.0 ntpkey_GQkey_smile.wolfhugel.eu.3616153122 mod 256
56873 54395.856 0.0.0.0 setup 0x80041 host smile.wolfhugel.eu md5WithRSAEncryption
56873 54396.546 2001:41d0:2:c731:1::5 assoc 35375 35375 host ntp.wolfhugel.eu md5WithRSAEncryption
56873 54397.546 2001:41d0:2:c731:1::e1 assoc 35376 35376 host ntp.sentrion.eu md5WithRSAEncryption
56873 54398.547 2001:41d0:2:c731:1::5 cert ntp.wolfhugel.eu ntp.wolfhugel.eu 0x5 md5WithRSAEncryption (8) fs 3616152782
56873 54399.547 2001:41d0:2:c731:1::e1 cert ntp.sentrion.eu ntp.sentrion.eu 0x5 md5WithRSAEncryption (8) fs 3616152474
56873 54400.565 2001:41d0:2:c731:1::5 ntpkey_gqpar_ntp.wolfhugel.eu.3616152782 mod 256
56873 54400.581 2001:41d0:2:c731:1::5 gq ntp.wolfhugel.eu fs 3616152782
56873 54401.535 2001:41d0:2:c731:1::e1 ntpkey_gqpar_ntp.sentrion.eu.3616152474 mod 256
56873 54401.551 2001:41d0:2:c731:1::e1 gq ntp.sentrion.eu fs 3616152474
56873 54402.553 2001:41d0:2:c731:1::5 cook 2994d0d2 ts 3616153602 fs 3616153532
56873 54403.549 2001:41d0:2:c731:1::e1 cook 26557ee5 ts 3616153603 fs 3616153559
First lines indicate your own server is properly loading its own key, certificate and GQ private key. Then you see it receiving remote self-signed certificates, and finally it will load the corresponding GQ parameter files for each remote server.

If at this stage the GQ parameter files are not loaded, you may be time synchronized, but it is likely that authentication is not fully done. One way to test failure is to change the GQ parameter file locally and observe the behaviour. Here I did change the ntp.sentrion.eu GQ parameter file, so it does not correspond anymore to the remote private GQ key (watch out not to alter the ASN.1 syntax of the parameter file, so not all changed are equal):

56873 59044.240 0.0.0.0 ntpkey_RSAhost_smile.wolfhugel.eu.3616153122 mod 2048
56873 59044.241 0.0.0.0 ntpkey_RSA-MD5cert_smile.wolfhugel.eu.3616153122 0x1 len 796
56873 59044.242 0.0.0.0 ntpkey_GQkey_smile.wolfhugel.eu.3616153122 mod 256
56873 59044.242 0.0.0.0 setup 0x80041 host smile.wolfhugel.eu md5WithRSAEncryption
56873 59045.249 2001:41d0:2:c731:1::5 assoc 23144 23144 host ntp.wolfhugel.eu md5WithRSAEncryption
56873 59046.250 2001:41d0:2:c731:1::e1 assoc 23145 23145 host ntp.sentrion.eu md5WithRSAEncryption
56873 59047.251 2001:41d0:2:c731:1::5 cert ntp.wolfhugel.eu ntp.wolfhugel.eu 0x5 md5WithRSAEncryption (8) fs 3616152782
56873 59048.251 2001:41d0:2:c731:1::e1 cert ntp.sentrion.eu ntp.sentrion.eu 0x5 md5WithRSAEncryption (8) fs 3616152474
56873 59049.238 2001:41d0:2:c731:1::5 ntpkey_gqpar_ntp.wolfhugel.eu.3616152782 mod 256
56873 59049.265 2001:41d0:2:c731:1::5 gq ntp.wolfhugel.eu fs 3616152782
56873 59050.238 2001:41d0:2:c731:1::e1 ntpkey_gqpar_ntp.sentrion.eu.3616152474 mod 256
56873 59050.253 2001:41d0:2:c731:1::e1 82080150 23145 10e bad_or_missing_group key
56873 59051.258 2001:41d0:2:c731:1::5 cook 2994d0d2 ts 3616158251 fs 3616155829
56873 59052.269 2001:41d0:2:c731:1::e1 82080150 23145 10e bad_or_missing_group key
56873 59054.274 2001:41d0:2:c731:1::e1 82080150 23145 10e bad_or_missing_group key
56873 59056.254 2001:41d0:2:c731:1::e1 82080150 23145 10e bad_or_missing_group key
As you can read from here, the bad_or_missing_group key error message clearly identified an issue, and you could use ntpq to ensure you do not synchronize to ntp.sentrion.eu anymore.

Impact of authentication on precision

Thanks to Bertrand Petit for having asked the right question: what is the impact of authentication in the precision of the time synchronization? Unfortunately I do not have the knowledge nor tools to make any analysis or comment on this issue.


Summary of other experimentations

This section could also be called summary of all my other failures in trying to use NTP authentication. Please note that I did not test the MV scheme.

Private Certificates

I did generate private certificates on both ends, time synchrnized and parties were authenticated. But I did not find a way to indicate each server to verify the actual identify of the remote party. Debug mode does not show the "ntpd" server trying to validate the certificate anywhere.

Public Certificates

Same result as with Private Certificates.

IFF scheme

Whilst I had no issue configuring the IFF scheme, signature verification on the client always failed. This makes me wonder if I failed the configuration or if the implementation of the algorithms is not correct.

External Certificates

Some documentation seems to tell that using certificates from external authorities was an option, but my experiments always ended up in something like "self-signed" certificate not found.


Other readings

Following documentations did help me setting up my NTP authentication configuration:

https://www.hauke-lampe.de/ntp/ Simple step by step guide, unfortunately, I have not been able to authentify with them.
http://support.ntp.org/bin/view/Support/ConfiguringAutokey The reference documentation.
https://www.eecis.udel.edu/~mills/ident.html Autokey Identity Schemes.
http://archi.laurent.perso.neuf.fr/Doc_reseauNTP.html La documentation NTP de Laurent Archambault (avec une section sur autokey).