ntpd-rs 1.9.0
Overview
This guide covers launching and verifying ntpd-rs 1.9.0 on Ubuntu 24.04 from SecureAnyCloud on Amazon EC2. It is one version of the SecureAnyCloud Hardened Pi-hole, AdGuard Home & DNS Server AMIs listing on AWS Marketplace. You choose it as the software version when you subscribe and launch. The listing's guide hub describes the other DNS and time servers in the listing.
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 that 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, of crossbeam-epoch and of the hickory-resolver DNS library that have since received security fixes, and no newer ntpd-rs release exists. SecureAnyCloud therefore 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 you get:
- ntpd-rs 1.9.0 (
ntpd-rs.service), running as the unprivilegedntpd-rsuser and 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 that is 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 - The default user is
ubuntu, the architecture is x86_64, and the root volume is 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. It then loads a firewall table built from the list, 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, and 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 this.
- 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 that NTS uses are held in memory only and are 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
Before you launch this image, make sure you have:
- 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
The recommended instance type is m5.large. A time server for a few hundred clients runs comfortably on t3.small or larger.
Connecting to your instance
The operating system for ntpd-rs 1.9.0 on Ubuntu 24.04 is Ubuntu 24.04 LTS, and the SSH user is ubuntu.
Launch the instance
Option A: AWS Marketplace console
- Open the SecureAnyCloud Hardened Pi-hole, AdGuard Home & DNS Server AMIs listing on AWS Marketplace. Choose View purchase options, then Subscribe. One subscription covers every version in the listing.
- When 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.largeis recommended; the software charge is $0.04 per vCPU per hour) and your key pair. - For the security group, restrict the listing's default group to your own IP address. The default group allows SSH (22) only. You 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 the following commands on your own workstation. They create a security group that allows SSH from your management address and NTP plus NTS from your VPC, then launch the instance. Replace the AMI ID with the one shown for your Region on the Marketplace launch page. Replace 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, because the AMI already requires IMDSv2.
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 and adds the NTS key exchange server to the configuration. It also writes /root/ntpd-rs-info.txt, which lists 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. Read the note with this command.
sudo cat /root/ntpd-rs-info.txt# ntpd-rs 1.9.0 on Ubuntu 24.04 LTS (sac 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/sac-nts.crt"
#
# Checks on this server:
sudo ntp-ctl status ; sudo /opt/ntpd-rs/ntpd-rs-selftest.sh
# User guide: https://secureanycloud.com/stacks/ntpd-rs-1-9-0/Confirm the server is running
Confirm that ntpd-rs.service and its first-boot unit are active. Then run the self-test. It checks:
- the ntpd-rs 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
- 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.shactive active ntp-daemon 1.9.0 ntpd-rs 1.9.0-1+sac1 [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_OKThe package version 1.9.0-1+sac1 marks the SecureAnyCloud rebuild of ntpd-rs 1.9.0. Step 10 describes it.
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. Add these two rules:
- UDP 123 from your VPC or VPN CIDRs, for NTP
- TCP 4460 from your VPC or VPN CIDRs, for NTS key exchange (needed only for NTS clients)
In the EC2 console, choose the instance, open Security, choose the security group, then choose 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 -utcp 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:68ntpd-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.
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.000412sRight after boot, the server reports Stratum 16, which means it is 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. That is enough, because 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.
Serve time to your clients
Send a client query from the instance's private address to see what your clients receive. /opt/ntpd-rs/ntp-query.py is a minimal NTP client that ships 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" 123NTP 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. The following snippets are configuration 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 /updateRestart the client's time service after the change. Use sudo systemctl restart chrony or sudo systemctl restart systemd-timesyncd, or w32tm /resync on Windows.
Every EC2 instance can also use the Amazon Time Sync Service directly. This server is therefore 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.
Authenticated time with NTS
With NTS, the client first runs a TLS 1.3 key exchange on TCP 4460 and checks the server's certificate. Every NTP packet is then 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 to each NTS client. The certificate is public, and the private key never leaves the server.
The certificate carries the instance's private IP, so NTS clients connect to the private IP. Inspect the certificate and run the key exchange self-check with these commands.
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.crtsubject=CN = 172.31.84.221, O = sac 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=4cae127e6a02b090dde62d8ee35ca78af67cd3b8e9f60a5000a311bdef59e82fCompare 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 shows it with your IP filled in.
[[source]] mode = "nts" address = "172.31.84.221" certificate-authority = "/etc/ntpd-rs/sac-nts.crt"You can watch a complete NTS session from the server itself. The following commands run 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-testSources: 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: 0NTS needs the client to reach both ports, so open TCP 4460 as well as UDP 123 for NTS clients (Step 4).
The instance's private IP does not change for the life of an instance. If it ever does, regenerate the certificate by removing /var/lib/sac/data/ntpd-rs-firstboot.done and running sudo systemctl restart ntpd-rs-firstboot.service ntpd-rs.service. Then copy the new certificate to your clients.
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. Print both with these commands.
grep -nE '^\[\[|^listen|^allowlist|^address|^mode|^count|key-storage' /etc/ntpd-rs/ntp.toml
sudo nft list table inet sac_ntpd_rs15:[[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 sac_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. The following commands add a temporary public test address (192.0.2.53, from the TEST-NET-1 documentation range) to the loopback interface. They send an NTP request and an NTS key exchange from that address to the private IP, send the same from the private IP, and then remove the test address.
The public source gets no NTP reply and no NTS connection, and the NTS attempt 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 loNO 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=4cae127e6a02b090dde62d8ee35ca78af67cd3b8e9f60a5000a311bdef59e82fThe legacy control and "monlist" queries that turned 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-noneNO REPLY mode=6 from 127.0.0.1:123 (source default) NO REPLY mode=7 from 127.0.0.1:123 (source default)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 sac_ntpd_rs allow4table inet sac_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.
To check a change before you restart, run sudo /usr/local/sbin/ntpd-rs-guard.py --check /etc/ntpd-rs/ntp.toml. Also allow the same range in the security group (Step 4).
The rebuilt binaries and their provenance
The package version 1.9.0-1+sac1 is upstream's own ntpd-rs_1.9.0-1_amd64.deb, with its three binaries replaced by ones rebuilt from the ntpd-rs v1.9.0 source tag. The systemd units, users, manual pages and documentation are unchanged.
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. Print them with these commands.
sed -n '1,5p;/^lock changes/p;/^audit/p' /usr/share/doc/ntpd-rs/BUILD-PROVENANCE.txt grep -A1 -E '^name = "(rustls|crossbeam-epoch|hickory-resolver)"$' /usr/share/doc/ntpd-rs/Cargo.lock
sudo dpkg --verify ntpd-rs && echo "package files intact"ntpd-rs 1.9.0 - sac rebuild (package 1.9.0-1+sac1) Why: the official ntpd-rs 1.9.0 binaries link rustls 0.23.40 (RUSTSEC-2026-0285/-0346/-0348/-0349), crossbeam-epoch 0.9.18 (RUSTSEC-2026-0204) and hickory-resolver 0.26.1 (GHSA-5j98-2g5x-46v6, GHSA-6f2x-v7q7-m7m5, GHSA-6w6g-hm98-mhgm), and no newer ntpd-rs release exists. The ntpd-rs code is unchanged; only dependencies were raised to fixed patch releases. lock changes anyhow:1.0.102>1.0.103 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 hickory-net:0.26.1>0.26.2 hickory-proto:0.26.1>0.26.2 hickory-resolver:0.26.1>0.26.2 rustls:0.23.40>0.23.46 rustls-webpki:0.103.13>0.103.16 webpki-root-certs:1.0.1>1.0.10 audit cargo audit (cargo-audit 0.22.2, RustSec advisory-db 4b587f0, 2026-10-21) on this Cargo.lock: 0 vulnerabilities (1 warning: chacha20 0.10.0 yanked); OSV (incl. GHSA) 2026-10-21: 0 advisories name = "crossbeam-epoch" version = "0.9.21" -- name = "hickory-resolver" version = "0.26.2" -- name = "rustls" version = "0.23.46" ??5?????? c /etc/ntpd-rs/ntp.toml package files intactdpkg --verify lists only /etc/ntpd-rs/ntp.toml, the configuration file (c) that SecureAnyCloud replaced with the server configuration. Every installed file matches the package.
When the Pendulum project publishes a newer ntpd-rs release, SecureAnyCloud 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.
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.000295First boot needs no metadata. It reads the private IP from the instance's own routing table.
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. Add only networks you trust (Step 9), and 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 that ntpd-rs is serving. - The server shows Stratum 16. This 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 cause is either the NTS key gate, which means 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/. SecureAnyCloud 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.
SecureAnyCloud 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 sales@secureanycloud.com or visit SecureAnyCloud support.
Deploy on AWS
Launch ntpd-rs 1.9.0 on Ubuntu 24.04 on AWS, with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com