ntpd-rs 1.9.0 on AWS User Guide
Overview
This guide covers launching and verifying ntpd-rs 1.9.0 on Ubuntu 24.04 from cloudimg on Amazon EC2. It is one version of the cloudimg Hardened Pi-hole, AdGuard Home & DNS Server AMIs listing on AWS Marketplace; you pick it as the software version when you subscribe and launch. The other DNS and time servers in the listing are described on the listing's guide hub.
ntpd-rs is a memory-safe implementation of the Network Time Protocol (NTP), written in Rust by the Pendulum project (Trifecta Tech Foundation). It is a client and a server in one: it keeps the instance's own clock disciplined against the Amazon Time Sync Service, and it serves that time to the other machines in your VPC. It also speaks Network Time Security (NTS, RFC 8915), which authenticates time so a client can detect tampering.
The image ships ntpd-rs 1.9.0, the newest release. The official 1.9.0 binaries link versions of the rustls TLS library and of crossbeam-epoch that have since received security fixes, and no newer ntpd-rs release exists, so cloudimg rebuilds ntpd-rs 1.9.0 from its source tag with only those dependencies raised to their fixed patch releases (Step 10 shows the provenance). The ntpd-rs code itself is unchanged.
What is on the image:
- ntpd-rs 1.9.0 (
ntpd-rs.service), running as the unprivilegedntpd-rsuser, replacing chrony as the time daemon - Time from the Amazon Time Sync Service (169.254.169.123, reachable from every subnet, even without internet access) and the
time.aws.compool - An NTP server on UDP 123 that answers private networks only, enforced twice: by the ntpd-rs allowlist and by a firewall table built from that same list
- An NTS key exchange server on TCP 4460 with a per-instance certificate, generated on first boot
- Management through
ntp-ctlover a local Unix socket; the metrics exporter is disabled, so there is no management port - A self-test,
/opt/ntpd-rs/ntpd-rs-selftest.sh
Default user: ubuntu · Architecture: x86_64 · Root volume: 8 GiB gp3
Security posture
- Not an open NTP server. The
[[server]]allowlist in/etc/ntpd-rs/ntp.tomladmits loopback, the RFC 1918 private ranges and the carrier-grade NAT range 100.64.0.0/10. Requests from any other address are ignored, without a reply. Before every start, the ntpd-rs guard checks that list (it refuses to start ntpd-rs if the list is missing or admits everyone) and loads a firewall table built from it, so UDP 123 and the NTS port TCP 4460 drop every other source before ntpd-rs sees it. Open NTP servers are abused for traffic amplification; this one is not open. - No amplification queries. ntpd-rs does not implement the legacy control (mode 6) or private (mode 7, "monlist") queries, so there is nothing to amplify even from an allowed address (Step 8 checks it).
- The listing security group opens SSH only. NTP (UDP 123) and NTS (TCP 4460) stay closed until you open them to your own VPC or VPN ranges. Clients use the instance's private IP.
- Per-instance NTS certificate. The NTS private key and certificate are generated on each instance's first boot. No key ships in the image, so no two instances share one. The cookie keys NTS uses are held in memory only and never written to disk.
- Fails closed. ntpd-rs refuses to start until first boot has created its NTS key, and the guard refuses to start it with an open allowlist.
- No credential to steal. NTP has no login. Management is
ntp-ctlover a local Unix socket, never a network port. - No metadata access for the time daemon.
ntpd-rs.serviceand its first-boot unit are blocked from the instance metadata service (169.254.169.254 and fd00:ec2::254). The Amazon Time Sync Service at 169.254.169.123 is not blocked. - IMDSv2 required. The AMI is registered with
ImdsSupport v2.0, so instances launched from it refuse token-less (IMDSv1) metadata requests. - Per-instance identity. SSH host keys and the machine ID are generated fresh on every instance.
Prerequisites
- An AWS account and permission to launch EC2 instances
- An EC2 key pair in the Region you launch in
- A VPC subnet for the time server, and the CIDR ranges of the clients that will use it
Recommended instance type: m5.large. A time server for a few hundred clients runs comfortably on t3.small or larger.
Connecting to your instance
| Version | Operating system | SSH user |
|---|---|---|
| ntpd-rs 1.9.0 on Ubuntu 24.04 | Ubuntu 24.04 LTS | ubuntu |
Step 1: Launch the instance
Option A: AWS Marketplace console
- Open the cloudimg Hardened Pi-hole, AdGuard Home & DNS Server AMIs listing on AWS Marketplace and choose View purchase options, then Subscribe. One subscription covers every version in the listing.
- Once the subscription is active, choose Launch your software.
- Choose One-click launch from AWS Marketplace (or Launch from EC2 Console for the full launch wizard). In Version, choose ntpd-rs 1.9.0 on Ubuntu 24.04, then choose your Region.
- Pick an instance type (
m5.largerecommended; the software charge is $0.04 per vCPU per hour) and your key pair. - Security group: the listing's default group allows SSH (22) only. Restrict it to your own IP address. You will open NTP to your clients in Step 4.
- Choose your VPC and subnet, leave the metadata defaults (the image already requires IMDSv2), then choose Launch.
Option B: AWS CLI
Run this on your own workstation. It creates a security group that allows SSH from your management address and NTP plus NTS from your VPC, then launches the instance. Replace the AMI ID with the one shown for your Region on the Marketplace launch page, and the VPC, subnet, key pair and CIDR ranges with your own:
SG=$(aws ec2 create-security-group --group-name ntpd-rs-time \
--description "ntpd-rs NTP and NTS server" --vpc-id vpc-0123456789abcdef0 \
--query GroupId --output text)
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol tcp --port 22 --cidr <your-mgmt-cidr>
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol udp --port 123 --cidr 10.0.0.0/16
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol tcp --port 4460 --cidr 10.0.0.0/16
aws ec2 run-instances \
--image-id ami-0123456789abcdef0 \
--instance-type m5.large \
--key-name my-key-pair \
--security-group-ids "$SG" \
--subnet-id subnet-0123456789abcdef0 \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=ntpd-rs-time}]'
You do not need --metadata-options: the AMI already makes IMDSv2 required.
Step 2: Connect and read the instance details
Connect as ubuntu with the key pair you chose:
ssh -i ~/.ssh/my-key-pair.pem ubuntu@<public-ip>
First boot finishes within a few seconds of launch. It creates the instance's NTS key and certificate, adds the NTS key exchange server to the configuration and writes /root/ntpd-rs-info.txt with the private IP, the certificate fingerprint, the allowed client ranges and a client example. The note holds no secret: the private key stays in /etc/ntpd-rs/nts/server.key, readable only by root and the ntpd-rs group.
sudo cat /root/ntpd-rs-info.txt
# ntpd-rs 1.9.0 on Ubuntu 24.04 LTS (cloudimg AWS Marketplace image) - THIS instance.
# NTP has no login. The NTS private key was generated on this instance's first boot
# and exists nowhere else.
NTP_SERVER_IP=172.31.84.221
NTP_SERVER_PORT=123
NTS_KE_PORT=4460
NTS_CERT=/etc/ntpd-rs/nts/server.crt
NTS_CERT_SHA256=4C:AE:12:7E:6A:02:B0:90:DD:E6:2D:8E:E3:5C:A7:8A:F6:7C:D3:B8:E9:F6:0A:50:00:A3:11:BD:EF:59:E8:2F
NTS_KEY_FILE=/etc/ntpd-rs/nts/server.key
OBSERVE_SOCKET=/var/run/ntpd-rs/observe
PREFERRED_SOURCE=169.254.169.123 (Amazon Time Sync Service)
ALLOWED_CLIENTS=127.0.0.0/8 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 100.64.0.0/10
# NETWORK ACCESS: the listing security group opens SSH only. To serve clients, add inbound rules
# for udp/123 (NTP) and tcp/4460 (NTS key exchange) ONLY from your own VPC / VPN CIDRs - never
# 0.0.0.0/0 - and point clients at the PRIVATE IP above.
# ntpd-rs answers only the ALLOWED_CLIENTS ranges (the [[server]] allowlist in /etc/ntpd-rs/ntp.toml);
# the ntpd-rs guard drops every other source on udp/123 and tcp/4460 before ntpd-rs sees it.
# To serve another private range, add it to that allowlist, then: sudo systemctl restart ntpd-rs
#
# NTS client (ntpd-rs on another machine): copy /etc/ntpd-rs/nts/server.crt to the client, then
# [[source]]
# mode = "nts"
# address = "172.31.84.221"
# certificate-authority = "/etc/ntpd-rs/cloudimg-nts.crt"
#
# Checks on this server: sudo ntp-ctl status ; sudo /opt/ntpd-rs/ntpd-rs-selftest.sh
# User guide: https://www.cloudimg.co.uk/guides/ntpd-rs-1-9-0-on-ubuntu-24-04-aws/
Step 3: Confirm the server is running
ntpd-rs.service and its first-boot unit are active, and the self-test checks the version, synchronisation with the Amazon Time Sync Service, the allowlist and the guard's firewall table, a local NTP query, a real NTS key exchange with this instance's certificate, the not-an-open-server probe from Step 8, and the exact set of listening ports.
systemctl is-active ntpd-rs.service ntpd-rs-firstboot.service
ntp-daemon --version
dpkg-query -W -f='${Package} ${Version}\n' ntpd-rs
sudo /opt/ntpd-rs/ntpd-rs-selftest.sh
active
active
ntp-daemon 1.9.0
ntpd-rs 1.9.0-1+cloudimg1
[ntpd-rs-selftest] synchronised (stratum 5); 169.254.169.123 is a source
[ntpd-rs-selftest] OK NTS-KE 127.0.0.1:4460 TLS1.3 ntske/1 NTPv4 AEAD15 cookies=8 cert-sha256=4cae127e6a02b090dde62d8ee35ca78af67cd3b8e9f60a5000a311bdef59e82f
[ntpd-rs-selftest] not an open server: 192.0.2.123 gets no NTP reply and no NTS-KE connection; 127.0.0.1 and 172.31.84.221 answered; no mode 6/7 replies
[ntpd-rs-selftest] listening set OK (tcp: 22 4460 ; udp: 68 123 ); management is the unix socket /var/run/ntpd-rs/observe
NTPDRS_SELFTEST_OK
The package version 1.9.0-1+cloudimg1 marks the cloudimg rebuild of ntpd-rs 1.9.0 (Step 10).

Step 4: Open NTP and NTS to your clients
The listing's security group allows SSH only. To serve time, add inbound rules to the instance's security group from your own VPC or VPN CIDR ranges only, never from 0.0.0.0/0:
| Protocol | Port | Source | Purpose |
|---|---|---|---|
| UDP | 123 | your VPC / VPN CIDRs | NTP |
| TCP | 4460 | your VPC / VPN CIDRs | NTS key exchange (only for NTS clients) |
In the EC2 console choose the instance, open Security, choose the security group, then Edit inbound rules. Point clients at the instance's private IP (NTP_SERVER_IP in Step 2). These are the only ports the instance listens on:
sudo ss -Hltnu | awk '{print $1, $5}' | sort -u
tcp 0.0.0.0:22
tcp 0.0.0.0:4460
tcp 127.0.0.53%lo:53
tcp 127.0.0.54:53
tcp [::]:22
udp 0.0.0.0:123
udp 127.0.0.53%lo:53
udp 127.0.0.54:53
udp 172.31.84.221%ens5:68
ntpd-rs serves NTP on UDP 123 and NTS key exchange on TCP 4460. Port 68 is the DHCP client, and 127.0.0.53 and 127.0.0.54 belong to the instance's own local resolver (systemd-resolved). There is no management port: ntp-ctl talks to ntpd-rs over the Unix socket /var/run/ntpd-rs/observe.
Step 5: Check synchronisation
ntp-ctl status shows the server's own synchronisation state and its sources. The Amazon Time Sync Service (169.254.169.123) is always a source; the time.aws.com pool adds up to four more when the subnet has internet access. Both serve the same leap-smeared time, so they agree with each other.
sudo ntp-ctl status | sed -n '1,25p'
Synchronization status:
Dispersion: 9.645487s
Delay: 0.000863s
Stratum: 5
Sources:
169.254.169.123:123 169.254.169.123:123 (1)
Offset: +0.011762
Uncertainty: ±0.018654
Delay: ±0.000348
Poll interval: 16s
Missing polls: 0
Root dispersion: 0.000381s
Root delay: 0.000214s
time.aws.com:123 3.94.91.31:123 (7)
Offset: +0.012091
Uncertainty: ±0.044502
Delay: ±0.001980
Poll interval: 16s
Missing polls: 0
Root dispersion: 0.000229s
Root delay: 0.000412s
Right after boot the server reports Stratum 16 (not yet synchronised); within a minute or two it selects its sources and settles. Offsets are in seconds. In a private subnet without internet access only 169.254.169.123 appears, which is enough: the configuration needs one agreeing source.
The Amazon Time Sync Service also has an IPv6 address, fd00:ec2::123, on Nitro instances in IPv6-enabled subnets. It is not configured by default because on an IPv4-only subnet ntpd-rs would log an unreachable source every second. In a dual-stack subnet you can add it as another [[source]] with mode = "server" and address = "fd00:ec2::123", then restart ntpd-rs.
Step 6: Serve time to your clients
A client query from the instance's private address shows what your clients receive. /opt/ntpd-rs/ntp-query.py is a minimal NTP client shipped with the image:
PRIV=$(ip -4 -o route get 1.1.1.1 | sed -nE 's/.* src ([0-9.]+).*/\1/p')
python3 /opt/ntpd-rs/ntp-query.py "$PRIV" 123
NTP OK: mode=4 stratum=5 offset=-0.000s from 172.31.84.221:123 (source default)
On your client machines, point the time service at the server's private IP. These are configuration snippets for the clients, not commands to run on the server:
# chrony (Ubuntu, Amazon Linux, RHEL family): /etc/chrony/chrony.conf or /etc/chrony.conf
server 172.31.84.221 iburst
# systemd-timesyncd: /etc/systemd/timesyncd.conf
[Time]
NTP=172.31.84.221
# Windows (elevated prompt)
w32tm /config /manualpeerlist:172.31.84.221 /syncfromflags:manual /update
Restart the client's time service after the change (sudo systemctl restart chrony or sudo systemctl restart systemd-timesyncd; on Windows w32tm /resync). Every EC2 instance can also use the Amazon Time Sync Service directly, so this server is most useful as the time source for on-premises machines reached over VPN or Direct Connect, for containers and appliances that need one internal NTP address, and for clients that want authenticated time with NTS.
Step 7: Authenticated time with NTS
With NTS, the client first runs a TLS 1.3 key exchange on TCP 4460, checking the server's certificate, and then every NTP packet is authenticated. The certificate is generated on this instance's first boot. Clients trust it by its file, so copy /etc/ntpd-rs/nts/server.crt (it is public; the private key never leaves the server) to each NTS client.
The certificate carries the instance's private IP, so NTS clients connect to the private IP:
sudo openssl x509 -in /etc/ntpd-rs/nts/server.crt -noout -subject -ext subjectAltName -fingerprint -sha256
PRIV=$(ip -4 -o route get 1.1.1.1 | sed -nE 's/.* src ([0-9.]+).*/\1/p')
sudo python3 /opt/ntpd-rs/nts-ke-selfcheck.py "$PRIV" 4460 /etc/ntpd-rs/nts/server.crt
subject=CN = 172.31.84.221, O = cloudimg ntpd-rs NTS server (per-instance)
X509v3 Subject Alternative Name:
IP Address:172.31.84.221, IP Address:127.0.0.1, DNS:localhost, DNS:ip-172-31-84-221, DNS:ip-172-31-84-221.ec2.internal
sha256 Fingerprint=4C:AE:12:7E:6A:02:B0:90:DD:E6:2D:8E:E3:5C:A7:8A:F6:7C:D3:B8:E9:F6:0A:50:00:A3:11:BD:EF:59:E8:2F
OK NTS-KE 172.31.84.221:4460 TLS1.3 ntske/1 NTPv4 AEAD15 cookies=8 cert-sha256=4cae127e6a02b090dde62d8ee35ca78af67cd3b8e9f60a5000a311bdef59e82f
Compare the fingerprint with NTS_CERT_SHA256 in Step 2 before you trust the file you copied. On an ntpd-rs client, the source looks like this (the info note in Step 2 has it with your IP filled in):
[[source]]
mode = "nts"
address = "172.31.84.221"
certificate-authority = "/etc/ntpd-rs/cloudimg-nts.crt"
You can watch a complete NTS session from the server itself. This runs a second, throwaway ntpd-rs as an NTS client for 25 seconds; its minimum-agreeing-sources = 99 setting means it measures time but never changes the clock. Its source line ends in [NTS]:
PRIV=$(ip -4 -o route get 1.1.1.1 | sed -nE 's/.* src ([0-9.]+).*/\1/p')
sudo install -d -m 0700 /run/nts-test
sudo cp /etc/ntpd-rs/nts/server.crt /run/nts-test/server.crt
printf '[observability]\nobservation-path = "/run/nts-test/observe"\n\n[[source]]\nmode = "nts"\naddress = "%s"\ncertificate-authority = "/run/nts-test/server.crt"\n\n[synchronization]\nminimum-agreeing-sources = 99\n' "$PRIV" | sudo tee /run/nts-test/client.toml >/dev/null
sudo timeout 25 ntp-daemon -c /run/nts-test/client.toml >/dev/null 2>&1 &
sleep 20
sudo ntp-ctl -c /run/nts-test/client.toml status | sed -n '/^Sources/,/Missing polls/p'
sleep 6; sudo rm -rf /run/nts-test
Sources:
172.31.84.221:4460 172.31.84.221:123 [NTS] (1)
Offset: -0.000018
Uncertainty: ±0.007579
Delay: ±0.000057
Poll interval: 16s
Missing polls: 0
NTS needs the client to reach both ports, so open TCP 4460 as well as UDP 123 for NTS clients (Step 4). If the instance's private IP ever changes (it does not for the life of an instance), regenerate the certificate by removing /var/lib/cloudimg/ntpd-rs-firstboot.done and running sudo systemctl restart ntpd-rs-firstboot.service ntpd-rs.service, then copy the new certificate to your clients.

Step 8: Confirm it is not an open NTP server
The allowlist is in /etc/ntpd-rs/ntp.toml, and the guard's firewall table is built from it:
grep -nE '^\[\[|^listen|^allowlist|^address|^mode|^count|key-storage' /etc/ntpd-rs/ntp.toml
sudo nft list table inet cloudimg_ntpd_rs
15:[[source]]
16:mode = "server"
17:address = "169.254.169.123"
19:[[source]]
20:mode = "pool"
21:address = "time.aws.com"
22:count = 4
30:[[server]]
31:listen = "0.0.0.0:123"
32:allowlist = { filter = ["127.0.0.0/8", "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "100.64.0.0/10"], action = "ignore" }
41:[[nts-ke-server]]
42:listen = "0.0.0.0:4460"
table inet cloudimg_ntpd_rs {
set allow4 {
type ipv4_addr
flags interval
auto-merge
elements = { 10.0.0.0/8, 100.64.0.0/10,
127.0.0.0/8, 172.16.0.0/12,
192.168.0.0/16 }
}
set allow6 {
type ipv6_addr
flags interval
auto-merge
}
chain input {
type filter hook input priority filter; policy accept;
udp dport 123 ip saddr != @allow4 drop
udp dport 123 ip6 saddr != @allow6 drop
tcp dport 4460 ip saddr != @allow4 drop
tcp dport 4460 ip6 saddr != @allow6 drop
}
}
No IPv6 source is allowed, so the IPv6 set is empty and every IPv6 request to either port is dropped. There is no key-storage-path: NTS cookie keys stay in memory.
You can prove the policy from the instance itself. This adds a temporary public test address (192.0.2.53, from the TEST-NET-1 documentation range) to the loopback interface, sends an NTP request and an NTS key exchange from it to the private IP, sends the same from the private IP, and removes the address again. The public source gets no NTP reply and no NTS connection (it times out after 10 seconds); the private source is answered:
PRIV=$(ip -4 -o route get 1.1.1.1 | sed -nE 's/.* src ([0-9.]+).*/\1/p')
sudo ip addr add 192.0.2.53/32 dev lo
python3 /opt/ntpd-rs/ntp-query.py "$PRIV" 123 --src 192.0.2.53 --timeout 3 --expect-none
sudo python3 /opt/ntpd-rs/nts-ke-selfcheck.py "$PRIV" 4460 /etc/ntpd-rs/nts/server.crt --src 192.0.2.53 || true
python3 /opt/ntpd-rs/ntp-query.py "$PRIV" 123 --src "$PRIV"
sudo python3 /opt/ntpd-rs/nts-ke-selfcheck.py "$PRIV" 4460 /etc/ntpd-rs/nts/server.crt --src "$PRIV"
sudo ip addr del 192.0.2.53/32 dev lo
NO REPLY mode=3 from 172.31.84.221:123 (source 192.0.2.53)
FAIL NTS-KE TLS session with 172.31.84.221:4460 failed: timed out
NTP OK: mode=4 stratum=4 offset=-0.000s from 172.31.84.221:123 (source 172.31.84.221)
OK NTS-KE 172.31.84.221:4460 TLS1.3 ntske/1 NTPv4 AEAD15 cookies=8 cert-sha256=4cae127e6a02b090dde62d8ee35ca78af67cd3b8e9f60a5000a311bdef59e82f
The legacy control and "monlist" queries that made old NTP servers into amplifiers get no reply at all, even from loopback:
python3 /opt/ntpd-rs/ntp-query.py 127.0.0.1 123 --mode 6 --timeout 3 --expect-none
python3 /opt/ntpd-rs/ntp-query.py 127.0.0.1 123 --mode 7 --timeout 3 --expect-none
NO REPLY mode=6 from 127.0.0.1:123 (source default)
NO REPLY mode=7 from 127.0.0.1:123 (source default)

Step 9: Allow another of your own networks (optional)
NTP is answered for the local host and the private ranges above. To serve another network you trust, for example a VPN range that uses public addresses, add it to the allowlist filter in /etc/ntpd-rs/ntp.toml and restart ntpd-rs. The guard rebuilds the firewall table from the new list on every start, so this is the only file to edit:
sudo sed -i 's|"100.64.0.0/10"\]|"100.64.0.0/10", "203.0.113.0/24"]|' /etc/ntpd-rs/ntp.toml
sudo systemctl restart ntpd-rs
sudo nft list set inet cloudimg_ntpd_rs allow4
table inet cloudimg_ntpd_rs {
set allow4 {
type ipv4_addr
flags interval
auto-merge
elements = { 10.0.0.0/8, 100.64.0.0/10,
127.0.0.0/8, 172.16.0.0/12,
192.168.0.0/16, 203.0.113.0/24 }
}
}
Replace 203.0.113.0/24 with your own range. Never add 0.0.0.0/0 or ::/0: the guard refuses to start ntpd-rs with a filter that admits every address, and the self-test fails. Check a change before you restart with sudo /usr/local/sbin/ntpd-rs-guard.py --check /etc/ntpd-rs/ntp.toml. Remember to allow the same range in the security group (Step 4).
Step 10: The rebuilt binaries and their provenance
The package version 1.9.0-1+cloudimg1 is upstream's own ntpd-rs_1.9.0-1_amd64.deb (systemd units, users, manual pages and documentation unchanged) with its three binaries replaced by ones rebuilt from the ntpd-rs v1.9.0 source tag. The provenance note and the exact Cargo.lock used ship with the package, so you can audit the dependency versions yourself (for example with cargo audit):
sed -n '1,5p;/^lock changes/p;/^audit/p' /usr/share/doc/ntpd-rs/BUILD-PROVENANCE.txt
grep -A1 -E '^name = "(rustls|crossbeam-epoch)"$' /usr/share/doc/ntpd-rs/Cargo.lock
sudo dpkg --verify ntpd-rs && echo "package files intact"
ntpd-rs 1.9.0 - cloudimg rebuild (package 1.9.0-1+cloudimg1)
Why: the official ntpd-rs 1.9.0 release binaries link rustls 0.23.40 (RUSTSEC-2026-0285, RUSTSEC-2026-0346,
RUSTSEC-2026-0348, RUSTSEC-2026-0349) and crossbeam-epoch 0.9.18 (RUSTSEC-2026-0204), and no newer ntpd-rs release
exists. The ntpd-rs code is unchanged; only those dependencies were raised to their fixed patch releases.
lock changes aws-lc-rs:1.17.0>1.18.2 aws-lc-sys:0.41.0>0.46.0 crossbeam-epoch:0.9.18>0.9.21 rustls:0.23.40>0.23.46 rustls-webpki:0.103.13>0.103.16
audit cargo audit (cargo-audit 0.22.2, RustSec advisory-db b19b2c83, 2026-10-20) on this Cargo.lock: 0 vulnerabilities
name = "crossbeam-epoch"
version = "0.9.21"
--
name = "rustls"
version = "0.23.46"
??5?????? c /etc/ntpd-rs/ntp.toml
package files intact
dpkg --verify lists only /etc/ntpd-rs/ntp.toml, the configuration file (c) that cloudimg replaced with the server configuration; every installed file matches the package.
When the Pendulum project publishes a newer ntpd-rs release, cloudimg moves this version to it. The binaries are installed from a local package, not an apt repository, so apt does not replace them; Ubuntu's own security updates still arrive through unattended upgrades.
Step 11: Metadata access is blocked for the time daemon
ntpd-rs.service and the first-boot unit carry a systemd IPAddressDeny rule for the instance metadata addresses, so nothing running inside them can reach the metadata service. Each entry is a single address, so the Amazon Time Sync Service at 169.254.169.123 keeps working, as the source list shows:
systemctl show -p IPAddressDeny ntpd-rs.service ntpd-rs-firstboot.service
sudo ntp-ctl status | grep -A1 '^169.254.169.123'
IPAddressDeny=168.63.129.16/32 fd00:ec2::254/128 169.254.169.254/32
IPAddressDeny=168.63.129.16/32 fd00:ec2::254/128 169.254.169.254/32
169.254.169.123:123 169.254.169.123:123 (1)
Offset: +0.000295
First boot needs no metadata: it reads the private IP from the instance's own routing table.
Step 12: Security recommendations
- Keep the security group tight. Open UDP 123 and TCP 4460 only to the networks that need the server, and SSH only to your management address.
- Keep the allowlist scoped. Only add networks you trust (Step 9), never
0.0.0.0/0. The allowlist and the guard's firewall are layers of defence behind the security group. - Prefer NTS for clients that support it. It stops an attacker on the path from shifting your clients' clocks, which can break TLS certificate checks, Kerberos and log correlation.
- Watch the server.
sudo ntp-ctl statusshows the synchronisation state and the counters of requests received, accepted and ignored;sudo journalctl -u ntpd-rsshows the daemon's log. - Keep Ubuntu patched. Unattended upgrades apply Ubuntu's security updates.
Troubleshooting
- A client gets no answer. Check that the security group allows UDP 123 (not TCP) from the client, that the client uses the server's private IP, and that the client's address is inside the allowlist (Step 8). On the server,
python3 /opt/ntpd-rs/ntp-query.py 127.0.0.1 123proves ntpd-rs is serving. - The server shows Stratum 16. That is normal for a minute or two after start. If it stays there, check
sudo ntp-ctl statusfor the 169.254.169.123 source andsudo journalctl -u ntpd-rs -n 50for errors. - ntpd-rs does not start.
systemctl status ntpd-rsshows which check failed: the NTS key gate (first boot has not finished; seejournalctl -u ntpd-rs-firstboot) or the guard (sudo /usr/local/sbin/ntpd-rs-guard.py --check /etc/ntpd-rs/ntp.tomlexplains why it refused the allowlist).
Support and licensing
ntpd-rs is developed by the Pendulum project of the Trifecta Tech Foundation and is distributed under the Apache License 2.0 or the MIT License, at your option. The licence texts are in /usr/share/doc/ntpd-rs/. cloudimg provides the packaging, the rebuild with patched dependencies, the not-an-open-server allowlist and guard, the per-instance NTS certificate, the metadata block, and 24/7 support.
cloudimg is not affiliated with or endorsed by the Trifecta Tech Foundation or the Pendulum project. ntpd-rs is a mark of its owner and is used here only to identify the software.
For help with deployment or configuration, email support@cloudimg.co.uk or visit cloudimg support.