Download FreeRTOS

Quality RTOS & Embedded Software

FreeRTOS website now available in Simplified Chinese
New FreeRTOS Long Term Support version now available.
FreeRTOS Extended Maintenance Program (EMP) registration now open.
FreeRTOS-Plus-TCP v3.0.0 released:
Featured FreeRTOS IoT Integrations:

Instructions for setting up an NTP server
(with AES-128-CMAC authentication support)

Follow these instructions to set up an NTP server in a Linux environment using the chrony package and using AES-CMAC 128 bits as the hash algorithm for the authentication mechanism in client-server communication.

Chrony must be built from Source for AES CMAC support

To use chrony with support for AES-CMAC 128, version 4.0 or above has to be installed in your system. (As mentioned in the release notes, chrony 4.0 provided support for AES CMAC functions with Nettle dependency. Neither Ubuntu 18.04 nor Ubuntu 20.04 provide a chrony package version with AES-CMAC support in their LTS releases.


1. Install dependencies of chrony before building it from source

The list of dependencies can be viewed here: Download all the dependencies.

sudo apt-get install libcap libseccomp pkg-config nettle-dev

nettle is the required package for enabling CMAC support in chrony. If nettle-dev does not install the package headers files and its shared library, *.so files, then nettle will need to be installed from source. Verify the installation of Nettle by looking for the presence of the nettle/cmac.h file, and the and library files, that are usually placed under the /usr/ folder. If you cannot locate these files, install Nettle from source as explained in the next step. Otherwise, skip to step 3.

For AL2 EC2 instances,

sudo yum install libcap libseccomp pkg-config nettle-dev

pkg-config and nettle-dev might not be found. Go to step 2.

2. (Optional) Install Nettle from Source

The build and install instructions for Nettle are here: Follow these instructions to install the package.

2.1 Download source and unzip

tar -xvf tar -xvf nettle-3.7.2.tar.gz

2.2 Build and install from source

./configure --prefix=/usr --disable-static && make -j8
sudo make install
sudo chmod -v 755 /usr/lib/lib{hogweed,nettle}.so

Verify the installation of Nettle by looking for the cmac.h file in the /usr/include/nettle directory and the and libraries in the /usr/lib directory. It is okay if cannot be found.

3. Build chrony from source

As mentioned earlier, chrony >= 4.0 is required for AES-CMAC authentication support (with Nettle). Thus, here are the instructions for building and installing it from source.

3.1 Clone the respository and checkout the latest release tag

Replace 4.1 with the latest release tag in the example here:

git clone
git checkout tags/4.1

3.2 Configure the build with system information of Nettle installation (and desired chrony.conf location)

The ./configure --help command can be used to view the usage documentation of the configure script.

For my environment, I had to specify the folder path for the Nettle package's include and shared library files by setting the CPPFLAGS and LDFLAGS environment variables. Also, I specified the location of the chrony.conf and chrony.keys files by passing the ---sysconfdir=<DIR> parameter.

In addition, I configured the installation to be in the /usr/sbin/ and usr/bin directories, with the --prefix=/usr parameter, so that the built chrony binaries would be automatically picked up by my environment PATH.

export CPPFlAGS='-I/usr/include'
export LDFLAGS='-L/usr/lib'
./configure --prefix=/usr --sysconfdir=/etc/chrony

Ensure that the build configuration logs show that nettle has been detected along with CMAC support in your system and the SECHASH feature has been enabled for the chrony build. (The SECHASH feature represents the support for the various hash/cipher algorithms used for the authentication mechanism.)

This is how the tail end of the logs should look:

Checking for nettle : Yes
Checking for CMAC in nettle : Yes
Checking for gnutls : No
Creating Makefile
Creating doc/Makefile
Creating test/unit/Makefile

3.3 Build and Install chrony!

Build and install the chrony package in your system.

make -j 8
sudo make install

The above instructions will install chronyc and chronyd binaries in your system. (chronyd is the actual daemon that performs NTP client/server operations whereas chronyc is the user-facing client utility to query the status of the daemon.)

You will see something like the following installation log. Note: Errors about documentation output installation can be ignored as the required dependencies of chrony for documentation were not installed.

╰─> sudo make install
[ -d /etc/chrony ] || mkdir -p /etc/chrony
[ -d /usr/sbin ] || mkdir -p /usr/sbin
[ -d /usr/bin ] || mkdir -p /usr/bin
[ -d /var/lib/chrony ] || mkdir -p /var/lib/chrony
if [ -f /usr/sbin/chronyd ]; then rm -f /usr/sbin/chronyd ; fi
if [ -f /usr/bin/chronyc ]; then rm -f /usr/bin/chronyc ; fi
cp chronyd /usr/sbin/chronyd
chmod 755 /usr/sbin/chronyd
cp chronyc /usr/bin/chronyc
chmod 755 /usr/bin/chronyc
make -C doc install
make[1]: Entering directory '/home/ubuntu/Experiments/chrony/doc'
asciidoctor -b manpage -o chrony.conf.adoc
make[1]: asciidoctor: Command not found
Makefile:44: recipe for target '' failed
make[1]: *** [] Error 127
make[1]: Leaving directory '/home/ubuntu/Experiments/chrony/doc'
Makefile:88: recipe for target 'install' failed
make: *** [install] Error 2

3.4 Verify the installation

Verify that the chronyc and chronyd files have been installed in the system.

╰─> chronyc --version 130 ↵
chronyc (chrony) version DEVELOPMENT (-READLINE +SECHASH +IPV6 -DEBUG)

ls -la /usr/sbin/chronyd

Also, verify with the keygen command of chronyc that the built chrony package supports AES-CMAC. Here's an example run of the command showing support for AES-CMAC.

╰─> chronyc keygen 1 AES128 128
1 AES128 HEX:6CD5CA79D68B815093BA1DEC9DBD691C

4. Configure reference NTP servers for chrony (before configuring it as a server)

Before you run chrony as a server, it must be configured as a client to source time from different reference servers.

4.1 Setup the chrony.conf file

The chrony.conf file is used to configure the chrony daemon. The following configuration can be placed in the /etc/chrony/chrony.conf file where both NTP Pool servers and the Amazon Time Sync service (with the address have been configured to source time from for the chrony client.

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

pool iburst maxsources 4
pool iburst maxsources 1
pool iburst maxsources 1
pool iburst maxsources 2
server prefer iburst minpoll 4 maxpoll 4

# 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

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

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

# Stop bad estimates upsetting machine clock.
maxupdateskew 100.0

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

# 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 3

4.2 Create chronyd.service file

The chronyd.service file specifies that chronyd is run as a daemon process. Place this file with the following contents at /etc/systemd/system/:

╰─> sudo cat /etc/systemd/system/chronyd.service
Description=chrony, an NTP client/server
Documentation=man:chronyd(8) man:chronyc(1) man:chrony.conf(5)
Conflicts=systemd-timesyncd.service openntpd.service

ExecStart=/usr/sbin/chronyd $OPTIONS


4.3 Run chronyd as an NTP client and verify

Start the chronyd service and verify the functioning of the NTP client with the chronyc utility using its sources and tracking subcommands.

sudo service chronyd start

╰─> chronyc sources
MS Name/IP address Stratum Poll Reach LastRx Last sample
^- 2 10 377 29m -2864us[-2829us] +/- 105ms
^- 2 10 377 22m +2987us[+2988us] +/- 102ms
^- 2 10 377 92 -1572us[-1571us] +/- 100ms
^- 2 10 377 479 -505us[ -509us] +/- 109ms
^- 3 10 377 25m +9676us[+9687us] +/- 22ms
^- 2 10 377 486 +1995us[+1991us] +/- 87ms
^- 2 10 377 469 +7679us[+7674us] +/- 160ms
^- 3 10 377 990 -5310us[-5326us] +/- 228ms
^* 3 4 377 2 +157ns[ +552ns] +/- 535us

╰─> chronyc tracking
Reference ID : A9FEA97B (
Stratum : 4
Ref time (UTC) : Wed Jun 09 23:41:53 2021
System time : 0.000000990 seconds fast of NTP time
Last offset : +0.000000155 seconds
RMS offset : 0.000001250 seconds
Frequency : 2.439 ppm slow
Residual freq : +0.000 ppm
Skew : 0.023 ppm
Root delay : 0.000512199 seconds
Root dispersion : 0.000275599 seconds
Update interval : 16.2 seconds
Leap status : Normal

5. Configure chrony as an NTP server

First, make sure that chronyd is running on your system as an NTP client by looking at the instructions in Step 4.2.

5.1 Update chrony.conf with allow directive

Once chronyd is working as an NTP client, it can be upgraded to an NTP server by adding the allow directive to the chrony.conf file. The allow directive allows specifying the network and subnet to accept NTP requests from. Refer to for more information.

NTP server allow directive

Click to enlarge

echo 'allow 0/0' >> /etc/chrony/chrony.conf

5.2 Rerun chronyd in NTP server configuration

Re-run chronyd with the updated configuration to enable the NTP server feature.

sudo service chronyd restart

Again, verify that chronyd is running with the chronyc tracking or chronyc sources command.

5.3 Sanity check server mode of chronyd with a Python script

Use the following python script to verify the NTP server mode of chronyd is running locally.

#!/usr/bin/env python
# Requirements: ntplib (easy_install ntplib)
import ntplib
from time import ctime
import argparse

parser = argparse.ArgumentParser( description=" - Test a set of IP addresses for NTP " )
parser.add_argument("--ip", action="append", help="IP address to check for NTP")
a = parser.parse_args()
if a.ip is not None:
c = ntplib.NTPClient()
for ip in a.ip:
response = c.request(ip)
if response:
print (ip+" NTP Active "+ ctime(response.tx_time))
print (ip+" NTP Deactivated")

Invoke the script as python3 <script-name>.py --ip localhost. Here is an example output showing the successful configuration of chronyd as an NTP server.

╰─> python3 ~/Experiments/ --ip localhost
localhost NTP Active Wed Jun 9 23:59:41 2021

6. Enable symmetric key based authentication in the NTP server

You can enable symmetric key based authentication for communication with the NTP server by adding keys to the /etc/chrony/chrony.keys file. Keys can be generated by using the keygen subcommand of the chronyc utility.

6.1 Generate AES-128-CMAC key for authentication and configure it in NTP server

The key generated by the chronyc keygen command can be added to the chrony.keys file.

chronyc keygen 1 AES128 128 > /etc/chrony/chrony.keys

Multiple keys can be added to the chrony.keys file where each key is referenced by the Key ID. In the above command, 1 is the Key ID for the AES-128-CMACM key.

Verify that the key has been added to the chrony.keys file with sudo cat /etc/chrony/chrony.keys.

6.2 Rerun NTP server to use the authentication keys

Once the NTP server is re-run, it will service both:

  • Clients that have authentication code (MAC), generated with the key known to the server, in their request payload, AND
  • Clients that do not communicate in authentication mode, i.e. they do not have MAC in their request payload.

sudo service chronyd restart
Copyright (C) Amazon Web Services, Inc. or its affiliates. All rights reserved.