6 releases (1 stable)
1.0.0 | Apr 9, 2024 |
---|---|
0.1.4 | Nov 16, 2023 |
0.1.3 | Jan 11, 2023 |
0.1.2 | Mar 11, 2022 |
0.1.0 | Nov 1, 2021 |
#43 in Date and time
50 downloads per month
165KB
2.5K
SLoC
ClockBound daemon
Overview
The clockbound
daemon interfaces with chronyd
and the Operating System clock to provide clients with a bound on the
error of the system clock. The clockbound
daemon periodically updates a shared memory segment that stores parameters
to calculate the bound on clock error at any time. Clients leverage the C library (in clock-bound-ffi/
)
or the Rust library (in clock-bound-client/
) to open the shared memory segment and read a timestamp interval
within which true time exists.
Prerequisites
The synchronization daemon - chronyd
The clockbound
daemon continuously communicates with chronyd to compose the clock
error bound parameters. The chronyd
daemon must be running to synchronize the system clock and provide clock
correction parameters.
Chrony installation
- If running on Amazon Linux 2,
chronyd
is already set as the default NTP daemon for you. - If running on Amazon EC2, see the EC2 User Guide
for more information on installing
chronyd
and syncing to the Amazon Time Sync Service.
Chrony permissions
The chronyd
daemon has the ability to drop privileges once initialized. The rest of this guide assumes that chronyd
runs under the chrony
system user, which is the default for most distributions.
Note that this impacts which process can communicate with chronyd
. The clockbound
daemon communicates with chronyd
over chronyd
Unix Datagram Socket (usually at /var/run/chrony/chronyd.sock
). The chronyd
daemon sets permissions
such that only processes running under root
or the chrony
user can write to it.
Chrony configuration
IMPORTANT: configuring the maxclockerror directive
Several sources of synchronization errors are taken into account by clockbound
to provide the guarantee that true time
is within a clock error bound interval. One of these components captures the stability of the local oscillator the
system clock is built upon. By default, chronyd
uses a very optimistic value of 1 PPM, which is appropriate for a
clock error estimate but not for a bound. The exact value to use depends on your hardware (you should check),
otherwise, a value of 50 PPM should be appropriate for most configuration to capture the maximum drift in between clock
updates.
Update the /etc/chrony.conf
configuration file and add the following directive to configure a 50 PPM max drift rate:
# Ensures chronyd grows local dispersion at a rate that is realistic and aligned with clockbound.
maxclockerror 50
Installation
Cargo
ClockBound daemon can be installed using Cargo. Instructions on how to install Cargo can be found at doc.rust-lang.org.
If it's your first time installing Cargo on an AL2 EC2 instance you may need to also install gcc:
sudo yum install gcc
Run cargo build with the release flag:
cargo build --release
Cargo install will place the ClockBound daemon binary in this relative path:
target/release/clockbound
Configuration
Systemd configuration
If built from source using cargo, it is recommended to set up systemd to manage the ClockBound daemon.
In the below systemd configuration, please note:
- The
clockbound
daemon runs as thechrony
user so that it can access the chronyd UDS socket at/var/run/chrony/chronyd.sock
. - The
RuntimeDirectory
that contains the file backing the shared memory segment needs to be preserved over clockbound restart events. This lets client code run without interruption when the clockbound daemon is restarted. - Depending on the version of systemd used (>=235), the
RuntimeDirectory
can be used in combination withRuntimeDirectoryPreserve
.
Configuration steps:
Move binary to the location you want to run it from:
sudo mv target/release/clockbound /usr/local/bin/clockbound
sudo chown chrony:chrony /usr/local/bin/clockbound
Create unit file /usr/lib/systemd/system/clockbound.service
.
The contents of this file will vary depending on what version of systemd that you are running.
To determine the version of systemd that you are running, run systemctl --version
.
In the example below, the systemd version is 219.
$ systemctl --version
systemd 219
+PAM +AUDIT +SELINUX +IMA -APPARMOR +SMACK +SYSVINIT +UTMP +LIBCRYPTSETUP +GCRYPT +GNUTLS +ACL +XZ +LZ4 -SECCOMP +BLKID +ELFUTILS +KMOD +IDN
For systemd version >= 235 create file /usr/lib/systemd/system/clockbound.service
with the following contents:
[Unit]
Description=ClockBound
[Service]
Type=simple
Restart=always
RestartSec=10
ExecStart=/usr/local/bin/clockbound --max-drift-rate 50
RuntimeDirectory=clockbound
RuntimeDirectoryPreserve=yes
WorkingDirectory=/run/clockbound
User=chrony
Group=chrony
[Install]
WantedBy=multi-user.target
For systemd version < 235 create file /usr/lib/systemd/system/clockbound.service
with the following contents:
[Unit]
Description=ClockBound
[Service]
Type=simple
Restart=always
RestartSec=10
PermissionsStartOnly=true
ExecStartPre=/bin/mkdir -p /run/clockbound
ExecStartPre=/bin/chmod 775 /run/clockbound
ExecStartPre=/bin/chown chrony:chrony /run/clockbound
ExecStartPre=/bin/cd /run/clockbound
ExecStart=/usr/local/bin/clockbound --max-drift-rate 50
User=chrony
Group=chrony
[Install]
WantedBy=multi-user.target
Reload systemd:
sudo systemctl daemon-reload
Enable ClockBound daemon to start at boot:
sudo systemctl enable clockbound
Start ClockBound daemon:
sudo systemctl start clockbound
You can then check the status of the service with:
systemctl status clockbound
Logs are accessible at /var/log/daemon.log
or by running the following command:
sudo journalctl -u clockbound
One-off Manual Configuration
The following steps are primarily here for developer or testing purposes.
The ClockBound daemon needs to:
- Write to a shared memory segment that is backed by file
/var/run/clockbound/shm
. - Read from and write to chrony UDS socket at
/var/run/chrony/chronyd.sock
. This is permitted if theclockbound
daemon runs as the userchrony
. - Have a
--max-drift-rate
parameter that matcheschronyd
configuration.
Commands to support that ClockBound daemon setup:
sudo mkdir /var/run/clockbound
sudo chown root:chrony /var/run/clockbound
sudo chmod g+rwx /var/run/clockbound
sudo -u chrony /usr/local/bin/clockbound --max-drift-rate 50
Usage
To communicate with the ClockBound daemon, a client is required.
- See clock-bound-ffi for a C library that an application can use to communicate with the ClockBound daemon.
- See clock-bound-client for a Rust client library.
Clock status
The value of the clock status written to the shared memory segment is driven by the Finite State Machine described below.
Each transition in the FSM is triggered by an update retrieved from chrony with the clock status which can be one of Unknown
, Synchronized
, or FreeRunning
.
PTP Hardware Clock (PHC) Support on EC2
To get accurate clock error bound values when chronyd
is synchronizing to the PHC (since chronyd
assumes the PHC itself has 0 error bound which is not necesarily true), a PHC reference ID and PHC network interface (i.e. ENA interface like eth0) need to be supplied for ClockBound to read the clock error bound of the PHC and add it to chronyd
's clock error bound. This can be done via CLI args -r
(ref ID) and -i
(interface). Ref ID is seen in chronyc tracking
, i.e.:
$ chronyc tracking
Reference ID : 50484330 (PHC0) <-- This 4 character ASCII code
Stratum : 1
Ref time (UTC) : Wed Nov 15 18:24:30 2023
System time : 0.000000014 seconds fast of NTP time
Last offset : +0.000000000 seconds
RMS offset : 0.000000060 seconds
Frequency : 6.614 ppm fast
Residual freq : +0.000 ppm
Skew : 0.019 ppm
Root delay : 0.000010000 seconds
Root dispersion : 0.000001311 seconds
Update interval : 1.0 seconds
Leap status : Normal
and network interface should be the primary network interface (from ifconfig
, the interface with index 0) - on Amazon Linux 2 this will generally be eth0
, and on Amazon Linux 2023 this will generally be ens5
.
For example:
/usr/local/bin/clockbound -r PHC0 -i eth0
To have your systemd unit do this, you'll need to edit the above line to supply the right arguments.
For example:
[Unit]
Description=ClockBound
[Service]
Type=simple
Restart=always
RestartSec=10
ExecStart=/usr/local/bin/clockbound -r PHC0 -i eth0
RuntimeDirectory=clockbound
RuntimeDirectoryPreserve=yes
WorkingDirectory=/run/clockbound
User=chrony
Group=chrony
[Install]
WantedBy=multi-user.target
Security
See CONTRIBUTING for more information.
License
Licensed under the GPL v2 license.
Dependencies
~6–18MB
~192K SLoC