I reviewed rust-ntpd 1.9.0-0ubuntu2 as checked into stonking. This shouldn't be
considered a full audit but rather a quick gauge of maintainability. The review
focused on the first-party Rust code (src/, ~56 findings); the 260 vendored
crates in rust-vendor/ were only assessed via scanner results and the
static-Built-Using mechanism.
rust-ntpd packages ntpd-rs, a memory-safe Rust implementation of the NTP (v4
and draft v5) and NTS (Network Time Security) protocols from the Pendulum
project. It runs as a systemd daemon under the dedicated ntpd-rs user,
defaulting to client-only synchronization against the Ubuntu NTP pool; server
and NTS-KE modes are opt-in via /etc/ntpd-rs/ntp.toml. Its attack surface is
untrusted network packet parsing (UDP 123, TLS-based NTS-KE), the ntp-ctl
control socket, and the optional Prometheus metrics HTTP endpoint. All Rust
dependencies are vendored; an Ubuntu patch switches NTS cryptography to the
system OpenSSL library.
- CVE History
- Upstream has published 7 security advisories; all are
denial-of-service class, none allow remote code execution or privilege
escalation, and all are fixed well before the packaged 1.9.0
- CVE-2023-33192 (High, CVSS 7.5, unvalidated NTS cookie length
crashed the server, fixed in 0.3.3)
- GHSA-37xq-q42p-rv3p (webpki startup DoS, fixed in 0.3.7)
- CVE-2024-38528 (High, unlimited NTS-KE connections crash, fixed in
1.1.3)
- GHSA-v3mx-w872-mw68 (rustls crash as NTS-KE server, fixed in
1.3.1)
- GHSA-v83q-83hj-rw38 (client DoS via zero-sized/oversized NTS
cookies, fixed in 1.5.0)
- CVE-2025-58066 (Moderate, message storm between two servers, fixed
in 1.6.2)
- CVE-2026-26076 (Moderate, NTS cookie request CPU amplification,
fixed in 1.7.1).
- Upstream handling is good: coordinated GHSA advisories with same-day
fixes for every issue, OpenSSF Best Practices and Scorecard badges, an
external security audit report in docs/audits/, and production
deployment by Let's Encrypt.
- Build-Depends
- All build-deps are in main: debhelper, dh-apparmor, dh-cargo,
dh-sequence-installsysusers, pkgconf, cargo/rustc (>= 1.88), and
libssl-dev (OpenSSL 4.0.1).
- The only external C library linked into the binaries is system OpenSSL
(runtime NEEDED: libssl.so.4, libcrypto.so.4, plus libc/libm/libgcc_s);
the vendored crypto stacks (ring, aws-lc-rs, openssl-src) are not
compiled in thanks to debian/patches/openssl-crypto.patch. OpenSSL
security fixes therefore flow through normal library updates without
rebuilding this package.
- All Rust dependencies are vendored and statically linked (260 crates,
tracked via the Vendored-Sources-Rust field and debian/README.source);
each is a security maintenance burden that the owning team has committed
to handle via vendor refreshes and no-change rebuilds.
- pre/post inst/rm scripts
- None
- init scripts
- None
- systemd units
- Both services run as dedicated unprivileged users (ntpd-rs and
ntpd-rs-observe). The daemon only gets
AmbientCapabilities=CAP_SYS_TIME (needed for clock steering) and
CAP_NET_BIND_SERVICE (only used in opt-in server mode); the metrics
exporter binds loopback only (127.0.0.1:9975).
- Neither unit has systemd sandboxing directives (no ProtectSystem,
PrivateTmp, NoNewPrivileges, CapabilityBoundingSet, ...); upstream is
working on this (issue #2414). A daemon compromise is therefore bounded
only by the unprivileged user, which is acceptable but should be
hardened once upstream provides it.
- The AppArmor profile for ntp-daemon ships in complain mode
(flags=(complain, attach_disconnected)), so it logs violations but
does not actually confine the daemon out of the box. Until systemd
sandboxing lands (upstream issue #2414), the unprivileged service user
is the only effective isolation.
- dbus services
- None
- setuid binaries
- None
- binaries in PATH
- Installs ntp-daemon, ntp-ctl and ntp-metrics-exporter in /usr/bin.
ntp-ctl only reads the daemon's observe socket and prints status;
none perform dangerous operations.
- sudo fragments
- None
- polkit files
- None
- udev rules
- None
- unit tests / autopkgtests
- Tests run at build time and pass: 63 tests (56 library + 7 ntp-ctl
integration), 0 failures; a failure fails the build.
- Six non-trivial autopkgtests are declared, including an end-to-end NTS
server/client exchange on loopback and a metrics-exporter HTTP check;
the MIR team confirmed they pass on all supported architectures.
- cron jobs
- None
- Build logs
- Build succeeds with zero compiler errors; the 129 unique rustc warnings
are all in vendored crates (105 of them test-only `unexpected_cfgs` in
sharded-slab) while the first-party ntpd/ntp-proto crates compile
warning-free.
- Binaries are built with PIE, full RELRO, NX and LTO. Stack canaries and
FORTIFY are absent, which is expected for Rust: the compiler does not
emit the libc string functions those mechanisms instrument, and memory
safety is enforced by the language.
- No network access during the build beyond Ubuntu archive mirrors; all
dependencies are vendored.
- Processes spawned
- Only one runtime process spawn exists: force-sync runs `date -d
@<timestamp>` for display (src/force_sync/mod.rs:51). The argument is
internally computed so no injection is possible, but `date` is
resolved via PATH; since force-sync is invoked by admins through
sudo, a hostile PATH could fake the output. Trivial hardening
candidate (absolute path), no current impact beyond printed output.
- Memory management
- N/A
- File IO
- None
- All file access uses admin-configured paths with error mapping (NTS
certificates/keys, keyset storage, log files).
- NTS keysets are written with mode 0600 and the loader warns if
existing key file permissions are too broad.
- Logging
- None
- Environment variable usage
- None
- Use of privileged functions
- No setuid/setgid/chown/sudo/pkexec anywhere.
- Privileged ioctls (PTP_SYS_OFFSET, PPS, hardware timestamping) in
vendored NTP-functional crates are expected for an NTP daemon and are
bounded by the CAP_SYS_TIME capability granted in the systemd unit.
- Use of cryptography / random number sources etc
- No first-party crypto code
- Use of temp files
- None
- Use of networking
- The untrusted-input surface (NTP v4/v5 and NTS-KE packet parsing) is
implemented in memory-safe Rust with defensive size/magic validation
before parsing, and socket error paths are handled without panics.
- The observation socket uses an unlink-then-bind sequence with a small
TOCTOU window (src/daemon/sockets.rs:60-96); the path is
admin-configured inside a runtime directory owned by the
unprivileged daemon user, so exploitation requires local control of
that directory. Low severity.
- The metrics exporter binds loopback (127.0.0.1:9975) by default and
no cleartext HTTP endpoints exist.
- Use of WebKit
- None
- Use of PolicyKit
- None
- Utility Functions
- None
- Any significant cppcheck results
- None
- Any significant Coverity results
- N/A
- Any significant shellcheck results
- None. The 121 shellcheck notes are all in autopkgtest scripts
(debian/tests/) and vendored upstream CI scripts that never run on
user systems
- Any significant bandit results
- None
- Any significant govulncheck results
- N/A
- Any significant Semgrep results
- None
- Any significant Rust code scan results
- 24,754 hits; 3,110 unique findings after excluding test/doc paths and
deduplication, of which 56 are first-party
- clean first-party code: `#![forbid(unsafe_code)]` in the library and
all three binaries
- every production unwrap/expect call is a startup fail-fast
invariant, none reachable from network input.
- The 9,535 `unsafe` usages all sit in vendored crates (openssl, tokio,
ring, zerocopy, ...), which is expected for crypto/FFI crates.
- 1,242 TODO/FIXME markers across first-party and vendored code, these are
maintainability signals only
The overall security posture is solid: first-party code is memory-safe Rust
with no unsafe blocks, TLS rides on the distro OpenSSL, services run
unprivileged with a clean CVE record of timely coordinated fixes, and all
scanners are quiet. rust-ntpd is a leaf package with no reverse dependencies,
so the static-vendoring blast radius is contained to this single source
package, which the owning team has explicitly committed to maintain through
vendor refreshes, no-change rebuilds and ESM. Since there's talk to make
ntpd-rs the default time synchronization solution in the long run, the
missing systemd sandboxing and, more importantly, the complain-mode
AppArmor profile need to be addressed before ntpd-rs takes on the default
time daemon role.
Security team ACK for promoting rust-ntpd to main. Recommended follow-ups
(not blocking):
- switch the AppArmor profile to enforce mode before ntpd-rs becomes the
default time synchronization solution
- add systemd sandboxing once upstream issue #2414 lands
** CVE added: https://cve.org/CVERecord?id=CVE-2023-33192
** CVE added: https://cve.org/CVERecord?id=CVE-2024-38528
** CVE added: https://cve.org/CVERecord?id=CVE-2025-58066
** CVE added: https://cve.org/CVERecord?id=CVE-2026-26076
** CVE added: https://github.com/advisories/GHSA-37xq-q42p-rv3p
** CVE added: https://github.com/advisories/GHSA-v3mx-w872-mw68
** CVE added: https://github.com/advisories/GHSA-v83q-83hj-rw38
** Changed in: rust-ntpd (Ubuntu)
Assignee: Ubuntu Security Team (ubuntu-security) => (unassigned)
--
You received this bug notification because you are a member of Ubuntu
Bugs, which is subscribed to Ubuntu.
https://bugs.launchpad.net/bugs/2166406
Title:
[MIR] rust-ntpd
To manage notifications about this bug go to:
https://bugs.launchpad.net/ubuntu/+source/rust-ntpd/+bug/2166406/+subscriptions
--
ubuntu-bugs mailing list
[email protected]
https://lists.ubuntu.com/mailman/listinfo/ubuntu-bugs