header image

For a few weeks, I've been working on implementing OpenSSL support and vastly improved configurability to OpenBSD-based NTP daemon OpenNTPD. Both of which I have done now. OpenNTPD is written in C. See the implementation on GitHub - openntpd-openssl.

I run a Linux server infrastructure with time-critical daemons such as Kerberos and Bind9 DNS server. Therefore, I see it's essential to have a local, secure NTP server software.

Table of Contents


Motivation and goals

I prefer OpenNTPD over NTP due to its support for HTTPS constraint time validation feature. For those, who are unfamiliar with this security concept, see this link.

However, there were three things which I saw a bit troublesome in the official OpenNTPD:

  • configurability: considerable lack of any advanced configuration options

  • TLS libraries: lack of support for OpenSSL

  • log messages and formatting

Configurability

Simplicity is one of OpenNTPD key principles. For basic use, narrow-wide options might be sufficient. However, I don't like limiting software configurability when it could easily be supported. Software can be both simple but offer advanced features for those that need them. Forced values in configuration may lead to stupid administration situations, since software can technically support flexible configuration - it just lacks implementation for that.

For some, default hard-coded values are okay. However, in a dynamic network (and corporate) environment, you want flexibility since software must adapt to agreed policy. Additionally, you want to avoid bad solutions and tricks such as forced network routing policy for the sake of a single program: tricks which tend to break, are not dynamic and do not scale enough.

Hard-coded values do not scale

Software configuration which is dynamically adjustable gives you more than you can do with just a layer 3/2 network stack. For instance, do you want to use HTTPS servers with custom port numbers in OpenNTPD? It's not possible, so you need to add proper PAT network policy.

By default, a PAT-based solution is bad and does not scale, since you need to know IP addresses of remote HTTPS servers to connect to in your layer 3/2 network configuration. In most cases, OpenNTPD dynamically resolves domain names to IP addresses with DNS. It's quite troublesome to follow dynamically resolved IP addresses in a static-like PAT solution.

My point is: network administration overhead may be overwhelming. Obviously, instead of domain names, you can use static IP addresses in OpenNTPD configuration. It still does not scale since IP addresses can change. Let DNS resolve domain names to IP addresses, and avoid involving layer 3/2 stack for achieving advanced configuration goals. Sounds great? I agree.

I could bring other examples as well but you get my point. Offering a simple OpenNTPD configuration by default but allowing customization for advanced users is beneficial for any software today.

Highlights: UDP & TCP port selection, user agent customization

One added feature I want to highlight is possibility to configure UDP and TCP ports for HTTPS constraints, for your own NTP server and for your NTP client. This flexibility removes need for network administrators to adjust firewall settings specifically for OpenNTPD if custom ports are used. Instead, OpenNTPD configuration can be adapted to an existing network & security policy.

Another implemention I'd like to mention are custom user agent strings for HTTPS constraints. Some HTTPS servers may dislike HTTP requests without an user agent string presented, and even block such requests. To circumvent blockages and to avoid any disrupts caused by them, I have added support for customizable user agent strings to OpenNTPD.

From this day on, you can add whichever agent string you want to your OpenNTPD HTTPS constraints. Although I've described default string OpenNTPD time query on ntpd.conf manual (man 5 ntpd.conf), I'd personally prefer authentic strings like the one I picked from my web server today: Mozilla/5.0 (Linux; Android 10; SAMSUNG SM-G973F) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/12.0 Chrome/79.0.3945.136 Mobile Safari/537.36

In OpenNTPD configuration, this would be interpreted, for instance, as:

constraint from "https://fjordtek.com" port 443 useragent "Mozilla/5.0 (Linux; Android 10; SAMSUNG SM-G973F) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/12.0 Chrome/79.0.3945.136 Mobile Safari/537.36"

TLS libraries

OpenNTPD supports only LibreSSL for constraints. How about OpenSSL? Nope, no support. I understand OpenBSD developers want primarily support their own SSL/TLS application library. I also understand some security concerns and possibly motivating people to switch from OpenSSL to LibreSSL in general.

It's understandable for an OpenBSD project to prefer LibreSSL and not to have support for OpenSSL. However, since I have little requirement for LibreSSL TLS library and see it more like an overhead in my Linux software stack, and I have other software having OpenSSL as dependency, I thought it would also be great to have proper support for it in OpenNTPD. Therefore, I implemented OpenSSL support to OpenNTPD.

In my patched OpenNTPD version, you can switch between the two TLS libraries using OpenNTPD configuration entry constraint_engine with two alternative values: either libressl or openssl. For BSD users, LibreSSL might be the primary choice. For Linux guys, OpenSSL might be preferred. Obviously, you need proper TLS libraries on your system during OpenNTPD compilation time. I also recommend explicitly using compile time configuration parameters --with-openssl[=no] and --with-libressl[=no].

Certificate permission issues

The OpenSSL implementation works but due to lack of OpenBSD native system calls like unveil or pledge on Linux, both LibreSSL and OpenSSL have a certificate file read permission issue during certificate validation phase. I have added a configuration option constraint_ca_validation with alternative values true/false to counter that issue, although it's still a major security concern.

LibreSSL linking issues on Linux

Since Arch Linux OpenNTPD PKGBUILD does not really work regarding to LibreSSL (not talking about openssl), I have written my own implementation which correctly finds and links LibreSSL libraries on a Linux system. For reference, my PKGBUILD.

NOTE: The linked Arch Linux PKGBUILD misleadingly has openssl dependency although the official OpenNTPD does not support OpenSSL at all. depends field should have libressl with proper CFLAGS section (CFLAGS+=' -fcommon -L/usr/lib/libressl/ -Wl,-rpath,/usr/lib/libressl/') for LibreSSL header files on Linux.

Log messages and formatting

Simple technical terms are not enough

Default OpenNTPD log messages are mostly okay. However, I felt better formatting would be a good thing along with major improvements for error messages. Most of the error messages were simply strings like calloc (memory allocation issue), and uhm…, nothing else. I admit that some error situations, such as not finding proper network address family (AF_INET/AF_INET6) are very improbable but still having good messages is important in general, at least for code auditing purposes and getting new developers quicker to understand the code structure.

Offer better understanding

For administrators, proper log messages may actually tell more about nature of the situation, thus giving better understanding in wider perspective (e.g. “Do I have memory issues on the system?” etc.). For bug tickets and cross-referencial purposes, human-readable log messages are a good basis, too.

Reformatting log messages do not have to take away the technical value of the messages themselves but give better understanding about the situation and earlier events.

Therefore, I updated many log strings. The updated strings may be incorrect in some parts but it's a good start, at least. We can update and correct them, but I just needed something to begin with.

Additionally, improving log entry padding improves readability for human eye. Thus, I added some padding for NTP peer query messages.

Code implementation

Code implementation is available at my openntpd-openssl git repository. It stacks directly on openntpd-portable git repository after update.sh run. For instructions, read openntpd-openssl README section. To put in words: pull openNTPD-portable git repository, run included update.sh shell script, pull openNTPD-openssl git repository and apply my additional patches to the codebase. Compile and run.

Conclusions

Offering flexibility in software configuration, and improved software adaption to existing environments are important principles. OpenNTPD is a great piece of software, and implementing a bit more to it supports these principles better. That's what we call development.

Last words: comparing OpenNTPD to other NTP software

Chrony, another NTP project, has done a great OpenNTPD, NTP and chrony comparison analysis which you might want to check out before integrating your own NTP server/client solution. The comparison is available here:

chrony.tuxfamily.org

Please keep in mind that my OpenNTPD patchset updates many hardcoded settings into configurable form. The chrony comparison does not take note of this fact although it is good otherwise.