diff --git a/ntpd/src/daemon/mod.rs b/ntpd/src/daemon/mod.rs index b47d066ae..8b78d75d9 100644 --- a/ntpd/src/daemon/mod.rs +++ b/ntpd/src/daemon/mod.rs @@ -91,7 +91,9 @@ async fn run(options: NtpDaemonOptions) -> Result<(), Box> { config.check(); // we always generate the keyset (even if NTS is not used) - let keyset = nts_key_provider::spawn(config.keyset).await; + let keyset = tokio::task::spawn_blocking(move || nts_key_provider::spawn(config.keyset)) + .await + .expect("nts_key_provider::spawn should not panic"); #[cfg(feature = "hardware-timestamping")] let clock_config = config.clock; diff --git a/ntpd/src/daemon/nts_key_provider.rs b/ntpd/src/daemon/nts_key_provider.rs index cd9ecf394..fcc2317fa 100644 --- a/ntpd/src/daemon/nts_key_provider.rs +++ b/ntpd/src/daemon/nts_key_provider.rs @@ -1,7 +1,9 @@ use std::{ fs::{File, OpenOptions}, os::unix::prelude::{OpenOptionsExt, PermissionsExt}, + path::Path, sync::Arc, + time::Duration, }; use ntp_proto::{KeySet, KeySetProvider}; @@ -10,71 +12,96 @@ use tracing::warn; use super::config::KeysetConfig; -pub async fn spawn(config: KeysetConfig) -> watch::Receiver> { - let (mut provider, mut next_interval) = match &config.key_storage_path { - Some(path) => { - let path = path.to_owned(); - - if let Ok(meta) = std::fs::metadata(&path) { - let perm = meta.permissions(); +/// Reads the file metadata and checks if the permissions are as expected. Logs a warning if they are not. +fn permission_check(path: &Path) { + let Ok(meta) = std::fs::metadata(path) else { + return; + }; - if perm.mode() as libc::mode_t & (libc::S_IWOTH | libc::S_IROTH | libc::S_IXOTH) - != 0 - { - warn!("Keyset file permissions: Others can interact with it. This is a potential security issue."); - } - } + if meta.permissions().mode() as libc::mode_t & (libc::S_IWOTH | libc::S_IROTH | libc::S_IXOTH) + != 0 + { + warn!("Keyset file permissions: Others can interact with it. This is a potential security issue."); + } +} - let (provider, time) = tokio::task::spawn_blocking( - move || -> std::io::Result<(KeySetProvider, std::time::SystemTime)> { - let mut input = File::open(path)?; - KeySetProvider::load(&mut input, config.stale_key_count) - }, - ) - .await - .unwrap_or_else(|e| Err(std::io::Error::new(std::io::ErrorKind::Other, e))) - .unwrap_or_else(|e| { - warn!(error = ?e, "Could not load nts server keys, starting with new set"); - ( - KeySetProvider::new(config.stale_key_count), - std::time::SystemTime::now(), - ) - }); - ( - provider, - std::time::Duration::from_secs(config.key_rotation_interval as _).saturating_sub( - std::time::SystemTime::now() - .duration_since(time) - .unwrap_or(std::time::Duration::from_secs(0)), - ), - ) - } - None => ( - KeySetProvider::new(config.stale_key_count), - std::time::Duration::from_secs(config.key_rotation_interval as _), - ), - }; - let (tx, rx) = watch::channel(provider.get()); - tokio::task::spawn_blocking(move || loop { +fn run( + mut provider: KeySetProvider, + config: KeysetConfig, + mut next_interval: Duration, + tx: watch::Sender>, +) { + loop { std::thread::sleep(next_interval); next_interval = std::time::Duration::from_secs(config.key_rotation_interval as _); provider.rotate(); if let Some(path) = &config.key_storage_path { - if let Err(e) = (|| -> std::io::Result<()> { - let mut output = OpenOptions::new() - .create(true) - .truncate(true) - .write(true) - .mode(0o600) - .open(path)?; - provider.store(&mut output) - })() { - warn!(error = ?e, "Could not store nts server keys"); - } + OpenOptions::new() + .create(true) + .truncate(true) + .write(true) + .mode(0o600) + .open(path) + .and_then(|mut output| provider.store(&mut output)) + .unwrap_or_else(|error| { + warn!(?error, "Could not store nts server keys"); + }); } if tx.send(provider.get()).is_err() { break; } - }); + } +} + +pub fn spawn(config: KeysetConfig) -> watch::Receiver> { + let (provider, next_interval) = config + .key_storage_path + .as_ref() + .and_then(|path| { + let path: &Path = path.as_ref(); + + permission_check(path); + + File::open(path).map_or_else( + |error| { + warn!( + ?error, + "Could not read nts server keys file, starting with new set" + ); + None + }, + Some, + ) + }) + .and_then(|mut input| { + KeySetProvider::load(&mut input, config.stale_key_count).map_or_else( + |error| { + warn!( + ?error, + "Could not load nts server keys, starting with new set" + ); + None + }, + |(provider, time)| { + let next_interval = + std::time::Duration::from_secs(config.key_rotation_interval as _) + .saturating_sub( + std::time::SystemTime::now() + .duration_since(time) + .unwrap_or_default(), + ); + Some((provider, next_interval)) + }, + ) + }) + .unwrap_or_else(|| { + ( + KeySetProvider::new(config.stale_key_count), + std::time::Duration::from_secs(config.key_rotation_interval as _), + ) + }); + + let (tx, rx) = watch::channel(provider.get()); + std::thread::spawn(move || run(provider, config, next_interval, tx)); rx }