Secure Tunnelled NTP Proof of Concept

Greg McLearnCommon Criteria, Tools

Update 2018-Oct-03: This post has been updated within new information from NDcPP v2.1.

Recently, NIAP issued Technical Decision TD0321: Protection of NTP communications.  It states that network time sources are critical pieces of information that must be protected.  However, having no other agreed-upon mechanism to authenticate the source of, or ensure the integrity of NTP packets, NIAP requires vendors to use NTP over one of only a handful of acceptable trusted communications channels: TLS, DTLS, HTTPS*, SSH or IPSec.

This leaves many vendors in a bind since there are (a) no public-facing NTP servers that operate over any of these permissible channels; and, more importantly, (b) there are no widely available NTP server/client implementations that can be used to build such a solution.


2018-Oct-03: With the new release of NDcPP v2.1, vendors now have the option of using HMAC integrity protections built into NTPv3 or NTPv4 or using a trusted channel as described below. The easier solution is to use HMAC integrity.

Generally, when one is faced with the prospect of securing plaintext protocols, the easiest solution is one of tunnelling using a known secure channel between two endpoints.  Each of the permissible tunnelling protocols indicated above has advantages and disadvantages. At Lightship, we developed a quick and dirty proof of concept that uses a TLS process as the headend and chronyd as the NTP server (primarily because it makes it easy to treat the host clock as an authoritative time source which is handy for testing).  The point of this proof of concept is both to educate ourselves on the challenges in securing NTP in this fashion, as well as to enable us to build smarter tools as part of Greenlight – our test automation platform.

We are offering up this information to the community in the hopes that it will help facilitate usable test servers for vendors to develop their secured NTP clients and to help generate discussion as to the general need to secure NTP and the current state of secured NTP solutions within the context of Common Criteria.

Design

Conceptually, the problem is one of packet transport and translation.  We use a combination of the well-known tools stunnel and socat to repackage and translate TCP TLS to UDP necessary for an unmodified chronyd process to accept and respond.  (We recognize this as a suboptimal solution, but this was built up piecemeal as a proof-of-concept.)  Other mechanisms might involve socat in TLS mode, or any other interesting ways to optimize the pipeline.  Other trusted channel solutions might include a similar chain involving SSH tunneling, or using IPSec.  One might even choose to use something like scraping the time from HTTP response objects from highly available web services (like Google) over HTTPS (though the accuracy of such a solution is only in the seconds and you can hardly call it “NTP” at this point).

Presented below is a flowchart of the data through the pipeline between a well-known unmodified NTP server and a well-known unmodified NTP client.

Server

Server-side stunnel

On the server side, let’s work from the TLS headend back to the cronyd process.  We’ve started an stunnel process listening on port 1123. This process will handle the TLS connection proxying and transmit the plaintext TCP packets onward to the socat process listening on port 1122 on only interface 127.0.0.1.  Our /etc/stunnel/secure-ntp.conf configuration looks like this:

pid = /run/stunnel-secure-ntp.pid
[secure-ntp]
sslVersion = all
options = NO_SSLv2
options = NO_SSLv3
options = NO_TLSv1
cert = /etc/stunnel/secure-ntp-server.cert.pem
key = /etc/stunnel/secure-ntp-server.key.pem
accept = 0.0.0.0:1123
connect = 127.0.0.1:1122

Our proof-of-concept doesn’t attempt to do any ciphersuite restrictions (which a production model would need to do), but at least we restrict to TLS 1.1 and up.  The two lines which perform the proxying are shown in red.  Note that the stunnel will forward the plaintext TCP packets to a process listening on localhost port 1122 (described next).

Server-side socat

Next in the process chain is the Swiss-army knife of networking tools: socat.  In our case, we are starting a simple one-liner to translate incoming TCP to UDP between the two ports of interest: our TLS terminator on TCP 1123 and the NTP server on UDP 123:

/bin/socat tcp4-listen:1122,bind=127.0.0.1,reuseaddr,fork udp4:localhost:123

The socat process needs a port to listen on (1122).  Note that socat is actually capable of handling TLS connections natively, though the number of command-line arguments needed to supply to socat might be overwhelming.  A simple TCP TLS to plaintext UDP proxy that uses the same cert/key pair from the stunnel variant above looks like this:

/bin/socat openssl-listen:1123,bind=0.0.0.0,reuseaddr,fork,cert=/etc/stunnel/secure-ntp-server.cert.pem,key=/etc/stunnel/secure-ntp-server.key.pem,verify=0 udp4:localhost:123

(Yet more evidence that there is definitely more than one way to optimize and perform the task of tunneling NTP over TLS.)

NTP server

Our chronyd process is unmodified from the original. We modify the /etc/chrony.conf configuration to treat the local clock as an accurate time source while ensuring that the process itself is only accessible from localhost and not open to external usage:

rtcsync
allow 127.0.0.1
bindaddress 127.0.0.1
local stratum 2
cmdport 0
logdir /var/log/chrony

For the option local stratum 2 permit the local system clock to be used as a stratum 2 time signal (carefully consider the implications of this outside of a test environment!).

From this point on, the chronyd process could reach out and synchronize with any number of legitimate NTP servers in the international pool or within the local network if desired.  Consider however, that while a Common Criteria evaluation doesn’t evaluate beyond the TOE boundary, there might be evaluator or certifier concerns if a secure NTP server was receiving time information from an unsecured third-party source.

Client

Client-side stunnel

Similar to the server-side, our stunnel instance on the client side links a TLS tunnel entry in the network to a socat TCP/UDP translater.  The /etc/stunnel/secure-ntp.conf file on the client looks like this:

pid = /run/stunnel-secure-ntp.pid
[secure-ntp]
client = yes
accept = 127.0.0.1:1123
# This is a good time-server
connect = secure-ntp.example.org:1123
# This is a bad time-server
#connect = bad-secure-ntp.example.org:1123

As for options denoted in red, we define our stunnel as a client process and proxy plaintext connections from port 1123 on the local host to port 1123 on a remote server (eg. described above) via TLS.  In our example configuration, we are not performing any validation of the certificate, or configuring available protocol versions or ciphersuites, which would be necessary.

As a side note, it is possible to set up several tunnels to test how time changes can affect the device under test.  We have another definition for a time-server commented out in the above example configuration file.

Client-side socat

Similar to the server socat process, our one-liner proxy solution looks like this:

socat udp4-recvfrom:123,bind=127.0.0.1,reuseaddr,fork tcp:localhost:1123

There is one subtle difference which is that socat is listening on UDP port 123 masquerading as a local NTP server on our client-side.  It translates the UDP packets to TCP and hands them to the stunnel process on port 1123.  For security purposes, we only bind our UDP receiver on interface 127.0.0.1.

Client-side NTP Client

In our proof-of-concept, consider the ntpdate binary which is used to contact an NTP server (or pool of servers) to set the time on the local machine:

ntpdate -dqu 127.0.0.1

The -dq parameters won’t actually set the date/time, but it will permit debugging information to be displayed on the screen for us to see what is happening.   The -u parameter is necessary only when trying to actually set the date to ensure that the ntpdate process sends from a random source port rather than port 123 (since we have socat listening on UDP port 123 on interface 127.0.0.1).  Without -u in our setup, any attempt to finally synchronize with an NTP server would yield an error message “no server suitable for synchronization found“.

With the entire chain set up, here’s a call to ntpdate with full debugging enabled:

3 Jun 03:08:44 ntpdate[29039]: ntpdate 4.2.8p10@1.3728-o Sun May 7 22:26:53 UTC 2017 (1)
transmit(127.0.0.1)
receive(127.0.0.1)
transmit(127.0.0.1)
receive(127.0.0.1)
transmit(127.0.0.1)
receive(127.0.0.1)
transmit(127.0.0.1)
receive(127.0.0.1)
server 127.0.0.1, port 123
stratum 2, precision -21, leap 00, trust 000
refid [127.0.0.1], delay 0.18126, dispersion 0.00366
transmitted 4, in filter 4
reference time: debe1223.c32e6746 Sun, Jun 3 2018 3:07:15.762
originate timestamp: debe1283.11afd720 Sun, Jun 3 2018 3:08:51.069
transmit timestamp: debe1282.f19a511c Sun, Jun 3 2018 3:08:50.943
filter delay: 0.18126 0.18889 0.18909 0.20862
0.00000 0.00000 0.00000 0.00000
filter offset: 0.024637 0.028065 0.027959 0.033783
0.000000 0.000000 0.000000 0.000000
delay 0.18126, dispersion 0.00366
offset 0.024637 

3 Jun 03:08:51 ntpdate[29039]: adjust time server 127.0.0.1 offset 0.024637 sec

Additional Information

If you run test NTP servers in virtualized environments, be sure to prevent the guest from synchronizing with the host hypervisor for NTP servers you need to have inaccurate time for.  This is especially true to check on reboot.

If daemonizing socat, here’s an example of a systemd service file that works on the server side:

[Unit]
Description=TCP to UDP transformer for secure NTP solution
After=network.target
After=syslog.target
Before=chronyd.service
RequiredBy=chronyd.service
After=stunnel.service
Requires=stunnel.service

[Install]
WantedBy=multi-user.target
Alias=socat-secure-ntp.target

[Service]
Type=simple
ExecStart=/bin/socat tcp4-listen:1122,bind=127.0.0.1,reuseaddr,fork udp4:localhost:123
ExecStop=/bin/killall -15 socat

# Give up if ping don't get an answer
TimeoutSec=600
Restart=always

And here’s a similar service definition for stunnel on the server-side:

[Unit]
Description=SSL tunnel for network daemons
After=network.target
After=syslog.target
Before=chronyd.service
RequiredBy=chronyd.service

[Install]
WantedBy=multi-user.target
Alias=stunnel.target

[Service]
Type=forking
ExecStart=/bin/stunnel /etc/stunnel/secure-ntp.conf
ExecStop=/bin/killall -15 stunnel
PIDFile=/var/run/stunnel-secure-ntp.pid
StartLimitBurst=0

# Give up if ping don't get an answer
TimeoutSec=600
Restart=always

Suitability

Our choice to use TLS has nothing to do with it being any sort of best-practice and only one of being a proof of concept.  Vendors will need to find the best solution to fit their system.  However, whatever solution is used, consider the following:

  1. Familiarity: Use a protocol that your developers already know how to use, debug and test;
  2. Succinctness: Reduce the need for additional SFRs required in the Security Target;
  3. Availability: Use an existing and supported cryptographic library, possibly one that is already validated under CAVP or FIPS 140-2;
  4. Evaluation ease: Labs may have tooling and expertise in some protocols but not others.

Obviously, an ideal solution would be to have NTP servers in the wild that naturally support some form of protection.  However, time isn’t private so confidentiality is not important.  It may be important, however, to ensure its authenticity and to protect it from being modified.  Since NTP v3, the protocol has had the concept of authenticated integrity protection.  However, as of NTPv4 in RFC 5905, it is implemented using weak algorithms occupying only 128-bits which is not necessarily sufficient for Common Criteria purposes.  Much additional work will be required to properly fix how network time is securely delivered but ultimately we don’t expect the NIAP technical decision to be a permanent requirement.

Vendors may choose to implement their own variant of an NTP server to support their TOE in their operational environment. Such an option gives tremendous flexibility to solving the NTP communications problem. For example, a customized version of ntpd and ntpdate that has OpenSSL support baked right into it would be one obvious modification.

Lightship is committed to making certifications faster and easier for vendors.  Talk to us about how we can help you achieve your certifications at the speed of development.

Greg McLearn is Lightship’s Technical Director. He has been doing Common Criteria and security certifications for 10+ years and enjoys getting his hands on some of the latest technology. He has authored several tools to help facilitate security testing.