NTP Setup

Introduction

For people who have a single computer at home, making sure it keeps the correct time is not a great concern. If you’re like me, however, and have a number of machines on your home network, many working closely together, timekeeping becomes more of an issue.

On my home network, I have a DHCP dynamically updating my DNS server and Samba and NFS mounts sharing files that may be accessed from multiple machines. These are just two examples of situations where it is desirable to have all machines involved synchronized to a consistant time. To achieve a consistant time across my network, I run NTP on all my machines.

Preliminaries

You will find an overview, including a very nice diagram, of the Arda Network here.

I have NTP running on all my machines. All are using version 4.1.1.

There are lots of different ways to set up NTP on a network. Anyone interested in all the possiblities should read the documentation that comes with the NTP package, it’s very detailed. I’ll try to explain the reasons for the particular choices I’ve made in my setup. Hopefully, that will help you decide if the same choice is right for you.

I’ve set up my network with a single time server (Metis) that talks to three stratum 2 public time servers on the internet. All other machines on my network synchronize their clocks to Metis. Why do I have all my machines rely on this server for their time, you ask? People with multiple time servers often set them up using different routes to higher stratum time servers. This adds redundancy if one route goes down. I have only one connection to the internet. If this connection goes down, no time servers on my network will be talking to the public time servers I use to sync time. So, I’m really not loosing anything by having only one machine acting as my local time server.

Another way of approaching the same question is to ask why I don’t have all of my machines talk to public time servers on the internet. After all, that would make my setup simpler. I could use the same configuration files on all machines.

I use a single machine to talk to public time servers primarily to keep my footprint on these servers to a minimum. Public time servers have better things to do than serve time to every one of my home machines individually. Besides, this way, I can try out some of the more interesting aspects of the protocol like authentication and access control.

I’m sure that some of you are interested to know why I’ve chosen to use three public time servers to synchronize time to. The simple answer is that the decision was completely arbitrary. That isn’t completely true. General opinion seems to be that you should use more than two time servers if possible, although there seems to be a lot of variation in what the upper limit should be. I’ve also read that more time servers aren’t necessarily better if those time servers are far from your machines (either topologically or geographically) as this will impact timekeeping due to network conditions. So, the answer to the original question is that there are four public time servers in close geographic proximity but that one of them was giving me much more variation in its timekeeping than the other three. That’s why I talk to three public time servers.

Authentication, Access Control and File Permissions

Because I’m really the only person using my home network, authentication and access control between my machines might seem a bit paranoid. My response is that a little paranoia never hurt anyone when is comes to computer security. Besides, this document wouldn’t be as useful if I didn’t have something to say about it.

I looked at using the public key authentication mechanism that is part of NTP but decided not to use it. It just seemed more complicated to understand and implement than I was prepared to deal with. I got the impression that I’d be better served learning ipsec and implementing that instead. I could better justify the headaches of key management if all protocols, not just NTP, were being encrypted as well as authenticated.

Having said that, I did implement the private key authentication mechanism on my network. The big reason I went this way is because NTP’s private key mechanism is much easier to set up than the public key system, especially on a small network like mine. The degree of protection afforded by the private key mechanism is also sufficient for my needs. On a large network, however, key management would become oppressive and a switch to a public key mechanism would become necessary.

On my network, all clients are configured to use an MD5 key when requesting time from Metis. This protects the clients from a bogus time server masquerading as Metis. Unfortunately, I haven’t found any public time servers near me that use authentication so I just have to trust that I’m connecting to the machines I think I am. I use access controls to reduce my exposure in this arena.

Authenication on my network works hand-in-hand with access controls. Access controls probably aren’t enough to provide a reasonable measure of protection from deliberate attacks by themselves but they can prevent accidental disruption of time services. On all machines, I’ve implemented a ‘deny everything not explicitly allowed’ access control policy. I’ve done this by using the ignore flag as the default policy in my ntp.conf files. Succeeding lines then list permissions for specific subnets.

Because I’m using authentication between Metis and my client machines, it’s important to get file permissions and ownership right, especially for the key file. All files are owned by root:root on my Linux machines and by root:wheel on my FreeBSD boxes. File permissions are the same on all machines. Here they are.

File Name Permissions
/etc/ntp.conf 644
/etc/ntp/drift 644
/etc/ntp/keys 600

Time Server Setup

Here is Metis’ NTP configuration file.

/etc/ntp.conf

#
# Undisciplined Local Clock. This is a fake driver intended for backup
# and when no outside source of synchronized time is available. The
# default stratum is usually 3, but in this case we elect to use stratum
# 0. Since the server line does not have the prefer keyword, this driver
# is never used for synchronization, unless no other other
# synchronization source is available. In case the local host is
# controlled by some external source, such as an external oscillator or
# another protocol, the prefer keyword would cause the local host to
# disregard all other synchronization sources, unless the kernel
# modifications are in use and declare an unsynchronized condition.
#
server 127.127.1.0     # local clock
fudge  127.127.1.0 stratum 10

server <1st stratum 2 server fqdn>
server <2nd stratum 2 server fqdn>
server <3rd stratum 2 server fqdn>

#
# Drift file.  Put this in a directory which the daemon can write to.
# No symbolic links allowed, either, since the daemon updates the file
# by creating a temporary in the same directory and then rename()'ing
# it to the file.
#
driftfile /etc/ntp/drift
#multicastclient                        # listen on default 224.0.1.1
#broadcastdelay 0.008

#
# Authentication delay.  If you use, or plan to use someday, the
# authentication facility you should make the programs in the auth_stuff
# directory and figure out what this number should be on your machine.
#
#authenticate no

#
# Keys file.  If you want to diddle your server at run time, make a
# keys file (mode 600 for sure) and define the key number to be
# used for making requests.
# PLEASE DO NOT USE THE DEFAULT VALUES HERE. Pick your own, or remote
# systems might be able to reset your clock at will.
#
keys            /etc/ntp/keys
trustedkey      1 100 101
requestkey      100
controlkey      101

restrict default ignore
restrict 172.16.20.0 mask 255.255.255.0 noquery      # 1st time server subnet
restrict 172.16.21.0 mask 255.255.255.0 noquery      # 2nd time server subnet
restrict 172.16.22.0 mask 255.255.255.0 noquery      # 3rd time server subnet
restrict 192.168.10.0 mask 255.255.255.224 noquery nopeer notrust
restrict 216.58.109.11 noquery nopeer notrust
restrict 127.0.0.1

Some things to note concerning my ntp.conf file include the following points.

  • As the comments in the ntp.conf file explain, I’ve defined an undisciplined local clock using the ‘server 127.127.1.0′ and ‘fudge’ options. I’ve done this so that Metis can continue to serve time to my network when it looses sync with all the public time servers I’ve configured. This happens whenever the IP address of Metis’ internet connection changes. See the ntpd and Dynamic IP Addresses section for more details.
  • The trustedkey, requestkey, and controlkey options are part of NTP’s private key authentication system (as opposed to the public key system using the autokey option). I’ve defined a request key (used by ntpdc) and a control key (used by ntpq). These are very useful for restricting who can send commands to and query ntpd. All defined keys must also appear in the trustedkey option. The first key defined by the trustedkey option is used to authenticate clients on my network looking to synchronize their clocks to Metis. The ntp.conf file in the Time Client Setup section shows how this key is used.
  • I use access control options to restrict how other machines can interact with ntpd on Metis. The first restrict option tells ntpd to ignore all NTP packets from everywhere. The next three lines correspond to the subnets of the configured public time servers. These options tell ntpd to ignore all packets generated by ntpq and ntpdc from these subnets but time sync packets are allowed through. Since Metis is the only machine serving time on my home network, the next restrict option uses the nopeer flag to tell ntpd to never establish a peer association with any other machine on my network. This line also prevents other machines on my network from using ntpq and ntpdc to query ntpd or to send it commands. The notrust flag tells ntpd to serve time only to machines that have a trusted key configured that ntpd on Metis knows about. The key I’m using for this purpose appears in the trustedkey line of ntp.conf with a key id of 1. The next line refers to Metis, my internet gateway. It needs it’s own line because it’s sitting at the far end of an ipsec tunnel meaning the previous line won’t include it. Metis’ restrictions are the same as any other time client on my network. The last line tells ntpd to give unrestricted access to packets coming from the local machine.

And here is what the key file looks like.

/etc/ntp/keys

1       M       <key1>
100     M       <key100>
101     M       <key101>

I use the ntpq program to check up on ntpd from time to time. This is what I see when everything is running as it should. Given the access controls in the ntp.conf file, I have to be logged into Metis to do this.

#ntpq -p
     remote           refid      st t when poll reach   delay   offset  jitter
==============================================================================
 LOCAL(0)        LOCAL(0)        10 l    1   64  377    0.000    0.000   0.004
+<st 2 server> <st 1 server>      2 u 1018 1024  377  148.819    4.583   2.105
+<st 2 server> <st 1 server>      2 u 1022 1024  377   97.823  -10.487   5.031
*<st 2 server> <st 1 server>      2 u  961 1024  377   70.286   -4.628   0.788

Time Client Setup

All my machines that act as time clients are set up identically. All of them pole Metis to set their local time. The client configuration files shown here are from Io but they will be the same on all my machines except Metis.

/etc/ntp.conf

#
# Undisciplined Local Clock. This is a fake driver intended for backup
# and when no outside source of synchronized time is available. The
# default stratum is usually 3, but in this case we elect to use stratum
# 0. Since the server line does not have the prefer keyword, this driver
# is never used for synchronization, unless no other other
# synchronization source is available. In case the local host is
# controlled by some external source, such as an external oscillator or
# another protocol, the prefer keyword would cause the local host to
# disregard all other synchronization sources, unless the kernel
# modifications are in use and declare an unsynchronized condition.
#
#server 127.127.1.0     # local clock
#fudge  127.127.1.0 stratum 10

server metis.arda.homeunix.net key 1 iburst

#
# Drift file.  Put this in a directory which the daemon can write to.
# No symbolic links allowed, either, since the daemon updates the file
# by creating a temporary in the same directory and then rename()'ing
# it to the file.
#
driftfile /etc/ntp/drift
#multicastclient                        # listen on default 224.0.1.1
#broadcastdelay 0.008

#
# Keys file.  If you want to diddle your server at run time, make a
# keys file (mode 600 for sure) and define the key number to be
# used for making requests.
# PLEASE DO NOT USE THE DEFAULT VALUES HERE. Pick your own, or remote
# systems might be able to reset your clock at will.
#
keys            /etc/ntp/keys
trustedkey      1

restrict default ignore
restrict 192.168.10.1 notrust
restrict 127.0.0.1

The ntp.conf file on my time clients is in many ways simpler than the one appearing on Metis.

  • I don’t have a local clock defined because the clients don’t serve time to any other machines and because they all have static IPs. Client machines don’t have the problem Metis has with changing dynamic IPs.
  • Only one server is defined, Metis. The server option includes the key flag to tell the clients to use the defined key for authentication when sending time packets to Metis. The key id (1) corresponds to one of the keys defined in the trustedkey option on Metis.
  • I also use the iburst flag on my clients. Using this flag reduces the time it takes for a client to synchronize to a time server. At least, that’s the idea. I don’t use this flag on Metis because it involves sending a burst of time packets to the server and I didn’t want to do that to the public time servers I have configured. I’m sure these servers deal with enough traffic as it is.
  • Only one key is defined in the trustedkey option. This key is used to authenticate time packets sent to Metis.
  • The access control options are fewer and simpler than on Metis. All NTP packets from everywhere are ignored except time sync packets from Metis. Even in this case, time sync packets must use authentication to be accepted. NTP packets from localhost are accepted unconditionally, the same as on Metis.

And here is what the key file looks like. The key is the same one that appears in the key file on Metis.

/etc/ntp/keys

1       M       <key1>

When I run ntpq on Io, this is what I see.

%ntpq -p
     remote           refid      st t when poll reach   delay   offset  jitter
==============================================================================
*metis.arda.ho   <st 2 server>    3 u  111 1024  377    0.454    0.353   0.386

The stratum 2 server listed in the refid column here is the server that appears in the remote column preceded by the ‘*’ symbol on Metis.

ntpd and Dynamic IP Addresses

When running on a computer with a dynamically assigned IP address, ntpd displays a particular behaviour which must be taken into account. Whenever the IP of the host machine changes, ntpd must somehow be told that the change has taken place otherwise it will continue to try to communicate with any servers or peers using the old IP address. This will cause ntpd to loose sync with these servers and peers in short order.

There are different ways to update ntpd. The simplest is to stop and start ntpd. This is probably the most expedient solution if the computer in question is not serving time to any other machines. If the computer is acting as a time server itself, it will not be able to serve time until it has resynchronized with its own servers.

In my case, Metis has a dynamically assigned IP address and it is also serving time to the other computers on my network. Because of this, I wanted a less heavy-handed solution than restarting ntpd whenever the IP on Metis changed. I discovered that someone had already written a script that would do exactly what I wanted. Here it is.

/usr/local/sbin/reconfigNtpd

#!/bin/sh
#
# reconfigNtpd, written by Jan Ceuleers, March 2003, revised October 2003
#
# Purpose: ntpd running on a machine that has intermittent connectivity to
#          the Internet, and that does not have a static public IP address,
#          loses its associations with public NTP servers, even after the
#          connection to the Internet has been restored.
#          Restarting the ntpd daemon is a possibility, but the downside
#          is that this causes time not to be served to local clients until
#          ntpd has regained synchronisation.
#          A better way is to configure at least one local clock (the local
#          clock 127.127.1.0 at high stratum and/or a refclock), to cover
#          for the periods when there is no Internet connectivity, and to
#          use this script to unconfig/add{server,peer} all public time
#          servers at runtime (i.e. without restarting the daemon)
#
# Usage: reconfigNtpd | ntpdc
#
# Bugs: * This script only generates unconfig/addpeer and unconfig/addserver
#         pairs. Any relevant fudge statements or iburst options etc. are
#         ignored.
#
#       revised by Andew St. Jean, Dec. 2003
#       * script now finds location of keys from ntp.conf file
#       * script identifies the requestkey from the ntp.conf file
#

###
#
# Get keyid and password from /etc/ntp/keys
#
###
#set -- $(grep "^[[:digit:]]" < /etc/ntp/keys)
#echo keyid $1
#echo passwd $3

keyfile=`awk '/^keys/ { print $2}' /etc/ntp.conf`
keyid=`awk '/^requestkey[[:blank:]]/ { print $2 }' /etc/ntp.conf`
passwd=`awk '$0 ~ key { print $3 }' key="^$keyid[[:blank:]]" $keyfile`

echo keyid $keyid
echo passwd $passwd

###
#
# Derive unconfig commands from ntpdc output
#
###
ntpdc -n -p | awk '!/==/ && !/^ / { if (substr($1,2) !~ /^127.127./)
print "unconfig " substr($1,2) }'

###
#
# Derive addserver commands from /etc/ntp.conf
#
###
awk '/^server[[:blank:]]/ { if ($2 !~ /^127.127./) print "addserver " $2 }' /etc/ntp.conf

###
#
# Derive addpeer commands from /etc/ntp.conf
#
###
awk '/^peer[[:blank:]]/ { print "addpeer " $2 }' /etc/ntp.conf

echo quit

The reconfigNtpd script, written by Jan Ceuleers, removes any server and peer associations except those to local time sources from ntpd and then adds them back by looking for servers and peers configured in the ntp.conf file. You need to have ntpdc installed and a request key configured for this script to work. I modified this script from the original so that it would look in ntp.conf for the location of the keys file and it uses the requestkey option in ntp.conf to tell it which key to use rather than taking the first key it finds in the keys file.

Because the reconfigNtpd script can reset all the peer and server associations of ntpd, I don’t want just anyone running it. Here is a listing of the script.

-rwxr-x---    1 root     root         2097 Dec 11 06:54 /usr/local/sbin/reconfigNtpd

To run the reconfigNtpd script whenever Metis’ dynamic IP address changes, I created a /etc/ppp/ip-up.local script. Here is what it looks like.

/etc/ppp/ip-up.local

#!/bin/sh

# Reset all of ntpd's associations whenever the ppp link comes up.
# This deals with the habit of ntpd loosing sync whenever the
# IP address changes.
/usr/local/sbin/reconfigNtpd | /usr/sbin/ntpdc

I am able to use the ip-up.local script because Metis is using a PPPoE link to connect to the internet. Whenever my dynamically assigned IP changes, the ppp link between Metis and my ISP must be restarted.

Startup Scripts

Starting ntpd is done with the standard procedures on my Linux and FreeBSD machines. On Linux, I use a typical init script. The only change I made to the script that comes with the rpm is to remove the ‘-A’ option which disables authentication. Here is a copy of the script that I use.

/etc/rc.d/init.d/ntpd

#!/bin/sh
#
# ntpd         This shell script takes care of starting and stopping
#              ntpd (NTPv4 daemon).
#
# chkconfig: 2345 55 10
# description: ntpd is the NTPv4 daemon.

# Source function library.
. /etc/rc.d/init.d/functions

# Source networking configuration.
. /etc/sysconfig/network

# Check that networking is up.
[ ${NETWORKING} = "no" ] && exit 0

[ -x /usr/sbin/ntpd -a -f /etc/ntp.conf ] || exit 0

# See how we were called.
case "$1" in
  start)
        # Adjust time to make life easy for ntpd
        if [ -f /etc/ntp/step-tickers ]; then
                gprintf "Syncing time for ntpd. "
                /usr/sbin/ntpdate -s -b -p 8 -u `cat /etc/ntp/step-tickers`
        fi
        # Start daemons.
        gprintf "Starting ntpd: "
        #daemon ntpd -A
        daemon ntpd
        echo
        touch /var/lock/subsys/ntpd
        ;;
  stop)
        # Stop daemons.
        gprintf "Shutting down ntpd: "
        killproc ntpd
        echo
        rm -f /var/lock/subsys/ntpd
        ;;
  status)
        status ntpd
        ;;
  restart)
        $0 stop
        $0 start
        ;;
  reload)
        $0 stop
        $0 start
        ;;
  condrestart)
    if [ -f /var/lock/subsys/ntpd ]; then
        $0 stop
        $0 start
    fi
    ;;
  *)
        gprintf "Usage: ntpd {start|stop|restart|status}\n"
        exit 1
esac

exit 0

On my FreeBSD boxes, all I needed to add was one line to my /etc/rc.conf file. Here it is.

xntpd_enable="YES"

The /etc/default/rc.conf file already contained additional lines listing parameters to pass to ntpd and indicating the location of the program. I didn’t need to change either of these lines.

xntpd_program="/usr/sbin/ntpd"  # path to ntpd, if you want a different one.
xntpd_flags="-p /var/run/ntpd.pid"      # Flags to ntpd (if enabled).

Software Home Sites

NTP http://www.ntp.org/index.html

Further Reading

An inventive way to choose which public time servers to talk to http://www.pool.ntp.org/