From ab1e691ecc48306893c95abec7e3bc499ec45b5f Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Mon, 12 Jan 2026 12:28:40 +0100 Subject: [PATCH 01/51] Implement the basic runtime structure. --- src/commons/error.rs | 14 +- src/server/mod.rs | 1 + src/server/runtime.rs | 353 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 367 insertions(+), 1 deletion(-) create mode 100644 src/server/runtime.rs diff --git a/src/commons/error.rs b/src/commons/error.rs index ac5fc2c82..505c0daeb 100644 --- a/src/commons/error.rs +++ b/src/commons/error.rs @@ -223,6 +223,7 @@ pub enum Error { //----------------------------------------------------------------- // System Issues //----------------------------------------------------------------- + InternalError(String), IoError(KrillIoError), KeyValueError(KeyValueError), QueueError(queue::Error), @@ -406,6 +407,7 @@ impl fmt::Display for Error { //----------------------------------------------------------------- // System Issues //----------------------------------------------------------------- + Error::InternalError(e) => write!(f, "Internal error: {e}"), Error::IoError(e) => write!(f, "I/O error: {e}"), Error::KeyValueError(e) => write!(f, "Key/Value error: {e}"), Error::QueueError(e) => write!(f, "Queue error: {e}"), @@ -750,6 +752,10 @@ impl Error { pub fn io_error_with_context(context: String, cause: io::Error) -> Self { Error::IoError(KrillIoError::new(context, cause)) } + + pub fn internal(msg: impl fmt::Display) -> Self { + Error::InternalError(msg.to_string()) + } } impl std::error::Error for Error {} @@ -760,7 +766,8 @@ impl Error { match self { // Most is bad requests by users, so just mapping the things that // are not - Error::IoError(_) + Error::InternalError(_) + | Error::IoError(_) | Error::SignerError(_) | Error::AggregateStoreError(_) | Error::WalStoreError(_) @@ -792,6 +799,11 @@ impl Error { // System Issues (label: sys-*) //----------------------------------------------------------------- + // internal server error + Error::InternalError(e) => { + ErrorResponse::new("sys-internal", self).with_cause(e) + } + // internal server error Error::IoError(e) => { ErrorResponse::new("sys-io", self).with_cause(e) diff --git a/src/server/mod.rs b/src/server/mod.rs index fb9d9dbcc..6cc26567a 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -4,5 +4,6 @@ pub mod manager; pub mod mq; pub mod properties; pub mod pubd; +pub mod runtime; pub mod scheduler; pub mod taproxy; diff --git a/src/server/runtime.rs b/src/server/runtime.rs new file mode 100644 index 000000000..912e8977e --- /dev/null +++ b/src/server/runtime.rs @@ -0,0 +1,353 @@ +//! The server’s runtime. +//! +//! The Krill server contains both sync and async code. This module provides +//! the means to manage control flow through all these parts. +//! +//! Most processing in Krill happens in sync code because that is easier to +//! reason about. However, certain things – most prominently the HTTP +//! requests made to talk to remote repositories and parent CAs – have the +//! potential to block threads for an unduly long time. So these are best +//! performed as tasks on an async runtime. +//! +//! A consequence of this is that processing needs to be able to go from +//! sync to async and then back to sync. This module provides a mechanism to +//! do this in a safe and ergonomic way. +//! +//! > Side note: The terminology we are using is a bit creative. All the +//! > obvious terms are already used elsewhere and we don’t want ambiguity, +//! > so we had to resort to scroll quite a bit down in a thesaurus. +//! + + +use std::{error, fmt}; +use std::sync::Arc; +use hyper::StatusCode; +use rpki::ca::publication; +use tokio::runtime; +use tokio::sync::oneshot; +use crate::commons::error::KrillError; +use crate::api::status::ErrorResponse; + + +//------------ KrillRuntime -------------------------------------------------- + +pub struct KrillRuntime(Arc); + +impl KrillRuntime { + pub async fn run( + &self, op: F + ) -> Result + where + F: FnOnce() -> Result + Send + 'static, + T: Send + 'static, + E: Into + Send + 'static + { + let (tx, rx) = oneshot::channel(); + self.0.tokio.spawn_blocking(|| { + let _ = tx.send(op()); + }); + rx.await?.map_err(Into::into) + } + + pub async fn run_errand( + &self, op: F + ) -> Result + where + F: FnOnce() -> P + Send + 'static, + P: Phase>, + T: Send + 'static, + E: Into + Send + 'static + { + let (tx, rx) = oneshot::channel(); + self.0.tokio.spawn_blocking(|| { + op().finish(tx); + }); + rx.await?.map_err(Into::into) + } +} + + +//------------ ErrandRuntime ------------------------------------------------- + +#[derive(Clone)] +pub struct ErrandRuntime(Arc); + +impl ErrandRuntime { + fn spawn_async(&self, future: impl Future + Send + 'static) { + let _ = self.0.tokio.spawn(future); + } + + fn spawn_blocking(&self, op: impl FnOnce() + Send + 'static) { + let _ = self.0.tokio.spawn_blocking(op); + } +} + + +//------------ Components ---------------------------------------------------- + +struct Components { + /* + /// The server configuration. + /// + /// This has to be an arc for now since some components keep a copy. + config: Config, + + /// The base URI for communicating with this server. + /// + /// We keep it separately because the config only keeps the configured + /// value which may be missing. + service_uri: uri::Https, + + /// Publication server, with configured publishers + repo_manager: RepositoryManager, + + /// The manager for all our CAs. + ca_manager: CaManager, + + /// The task queue. + /// + /// This needs to remanin an arc for now since it needs to be given to + /// aggregate listeners. + tasks: TaskQueue, + + /// The signer. + /// + /// This needs to remain an arc for now because it is kept with some + /// commands. + signer: KrillSigner, + + /// The actor used for actions initiated by the server itself. + system_actor: Actor, + */ + + /// The Tokio runtime to spawn tasks onto. + /// + /// We currently use it for both async and sync tasks (via + /// `spawn_blocking`). + tokio: runtime::Handle, +} + + +//------------ Errand -------------------------------------------------------- + +pub struct Errand { + /// The capture value passed along during execution. + capture: Cap, + + /// The initial calculation of the errand. + value: MaybeFuture, + + /// The Krill runtime to use and pass along. + krill: ErrandRuntime, +} + +impl Errand { + pub fn then(self, op: Op) -> Then { + Then { + before: self, + op: op + } + } +} + +impl Phase for Errand +where + Cap: Send + 'static, + Fut: Future + Send + 'static, + Fut::Output: Send + 'static, +{ + type Capture = Cap; + type Output = Fut::Output; + + fn run(self, then: Then) + where + Then: + FnOnce(Cap, Self::Output, ErrandRuntime) + + Send + 'static + { + match self.value { + MaybeFuture::Ready(res) => (then)(self.capture, res, self.krill), + MaybeFuture::Future(fut) => { + self.krill.clone().spawn_async(async move { + let res = fut.await; + self.krill.clone().spawn_blocking(move || { + (then)(self.capture, res, self.krill); + }) + }) + } + } + } + + fn finish(self, tx: oneshot::Sender) { + match self.value { + MaybeFuture::Ready(res) => { + let _ = tx.send(res); + } + MaybeFuture::Future(fut) => { + self.krill.spawn_async(async { + let _ = tx.send(fut.await); + }) + } + } + } +} + + +//------------ Then ---------------------------------------------------------- + +/// An errand with an additional stage chained to it. +pub struct Then { + // the errand that produces the output we are processing + before: Before, + + // a function that is run sync and returns a future. + // + // this needs to be spawned blocking when outer resolves. + op: Op, +} + +impl Then { + pub fn then(self, op: OOp) -> Then{ + Then { + before: self, + op, + } + } +} + +impl Phase for Then +where + Before: Phase, + Op: IntoMaybeFuture, +{ + type Capture = Op::Capture; + type Output = Op::Output; + + fn run(self, then: Then) + where + Then: + FnOnce(Op::Capture, Self::Output, ErrandRuntime) + + Send + 'static + { + self.before.run(|mut capture, input, krill| { + match self.op.eval(&mut capture, input, &krill) { + MaybeFuture::Ready(res) => (then)(capture, res, krill), + MaybeFuture::Future(fut) => { + krill.clone().spawn_async(async { + let res = fut.await; + krill.clone().spawn_blocking(|| { + (then)(capture, res, krill); + }) + }) + } + } + }) + } + + fn finish(self, tx: oneshot::Sender) { + self.before.run(|mut capture, input, krill| { + match self.op.eval(&mut capture, input, &krill) { + MaybeFuture::Ready(res) => { + let _ = tx.send(res); + } + MaybeFuture::Future(fut) => { + krill.spawn_async(async { + let _ = tx.send(fut.await); + }) + } + } + }); + } +} + + +//------------ MaybeFuture --------------------------------------------------- + +/// A value that is either already present or the result of a future. +pub enum MaybeFuture { + /// The value is already present. + Ready(Fut::Output), + + /// The value needs to be calculated by resolving the future. + Future(Fut), +} + + +//------------ IntoMaybeFuture ----------------------------------------------- + +/// An operation that will result in a `MaybeFuture`. +pub trait IntoMaybeFuture: Send + 'static { + type Capture: Send + 'static; + type Input: Send + 'static; + type Output: Send + 'static; + type Future: Future + Send + 'static; + + fn eval( + self, + capture: &mut Self::Capture, + input: Self::Input, + krill: &ErrandRuntime, + ) -> MaybeFuture; +} + + +//------------ Phase --------------------------------------------------------- + +/// A single step in running an errand. +pub trait Phase: Sized { + type Capture: Send + 'static; + type Output: Send + 'static; + + fn run(self, then: Then) + where + Then: + FnOnce(Self::Capture, Self::Output, ErrandRuntime) + + Send + 'static + ; + + fn finish(self, tx: oneshot::Sender); +} + + +//------------ RunError ------------------------------------------------------ + +/// An error happened when running an operation. +// +// This is a separate type in preparation for refactoring error handling. For +// now, it just wraps a `KrillError`. +#[derive(Debug)] +pub struct RunError(KrillError); + +impl RunError { + pub fn status(&self) -> StatusCode { + self.0.status() + } + + pub fn to_error_response(&self) -> ErrorResponse { + self.0.to_error_response() + } + + pub fn to_rfc8181_error_code(&self) -> publication::ReportErrorCode { + self.0.to_rfc8181_error_code() + } +} + +impl From for RunError { + fn from(src: KrillError) -> Self { + Self(src) + } +} + +impl From for RunError { + fn from(_: oneshot::error::RecvError) -> Self { + Self(KrillError::internal("operation dropped")) + } +} + +impl fmt::Display for RunError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.0.fmt(f) + } +} + +impl error::Error for RunError { } From e67dc212d0508487a7043b64653491ed38cb3e5d Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Mon, 12 Jan 2026 12:33:07 +0100 Subject: [PATCH 02/51] Rename KrillManager to OldManager. --- src/daemon/http/server.rs | 8 +++--- src/daemon/start.rs | 4 +-- src/server/mod.rs | 4 ++- src/server/{manager.rs => oldmanager.rs} | 36 ++++++++++++------------ src/upgrades/mod.rs | 4 +-- 5 files changed, 29 insertions(+), 27 deletions(-) rename src/server/{manager.rs => oldmanager.rs} (98%) diff --git a/src/daemon/http/server.rs b/src/daemon/http/server.rs index 478eacc0f..aa009a8e5 100644 --- a/src/daemon/http/server.rs +++ b/src/daemon/http/server.rs @@ -10,7 +10,7 @@ use crate::commons::KrillResult; use crate::commons::error::FatalError; use crate::config::Config; use crate::constants::KRILL_ENV_HTTP_LOG_INFO; -use crate::server::manager::KrillManager; +use crate::server::oldmanager::OldManager; use super::auth::Authorizer; use super::dispatch::{DispatchError, dispatch_request}; use super::request::{BodyLimits, HyperRequest, Request}; @@ -23,7 +23,7 @@ use super::response::{HyperResponse, HttpResponse}; /// The Krill HTTP server. pub struct HttpServer { /// The Krill “business logic.” - krill: KrillManager, + krill: OldManager, /// The component responsible for API authorization checks authorizer: Authorizer, @@ -38,7 +38,7 @@ pub struct HttpServer { impl HttpServer { /// Creates a new server from a Krill manager and the configuration. pub fn new( - krill: KrillManager, + krill: OldManager, config: Arc, runtime: &runtime::Handle, ) -> KrillResult> { @@ -95,7 +95,7 @@ impl HttpServer { impl HttpServer { /// Returns a reference to the Krill manager. - pub(super) fn krill(&self) -> &KrillManager { + pub(super) fn krill(&self) -> &OldManager { &self.krill } diff --git a/src/daemon/start.rs b/src/daemon/start.rs index eabd98cf1..33cb56c83 100644 --- a/src/daemon/start.rs +++ b/src/daemon/start.rs @@ -15,7 +15,7 @@ use crate::commons::version::KrillVersion; use crate::config::Config; use crate::constants::KRILL_ENV_UPGRADE_ONLY; use crate::server::properties::PropertiesManager; -use crate::server::manager::KrillManager; +use crate::server::oldmanager::OldManager; use crate::upgrades::{ finalise_data_migration, post_start_upgrade, prepare_upgrade_data_migrations, UpgradeError, UpgradeMode, @@ -82,7 +82,7 @@ pub async fn start_krill_daemon( // Create the Krill manager, this will create the necessary data // sub-directories if needed - let krill = KrillManager::build(config.clone()).await?; + let krill = OldManager::build(config.clone()).await?; // Call post-start upgrades to trigger any upgrade related runtime // actions, such as re-issuing ROAs because subject name strategy has diff --git a/src/server/mod.rs b/src/server/mod.rs index 6cc26567a..88e9dcac4 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -1,9 +1,11 @@ pub mod bgp; pub mod ca; -pub mod manager; pub mod mq; pub mod properties; pub mod pubd; pub mod runtime; pub mod scheduler; pub mod taproxy; + +pub mod oldmanager; + diff --git a/src/server/manager.rs b/src/server/oldmanager.rs similarity index 98% rename from src/server/manager.rs rename to src/server/oldmanager.rs index 24b6d5e85..f9b5061ee 100644 --- a/src/server/manager.rs +++ b/src/server/oldmanager.rs @@ -74,11 +74,11 @@ use crate::constants::{TA_NAME, ta_handle}; use crate::server::bgp::BgpAnalyser; -//------------ KrillManager --------------------------------------------------- +//------------ OldManager --------------------------------------------------- /// This is the Krill server that is doing all the orchestration for all /// components. -pub struct KrillManager { +pub struct OldManager { // The base URI for this service service_uri: uri::Https, @@ -101,7 +101,7 @@ pub struct KrillManager { } /// # Set up and initialization -impl KrillManager { +impl OldManager { /// Creates a new publication server. Note that state is preserved /// in the data storage. pub async fn build(config: Arc) -> KrillResult { @@ -160,7 +160,7 @@ impl KrillManager { mq.schedule(Task::QueueStartTasks, now())?; - let server = KrillManager { + let server = OldManager { service_uri, repo_manager, ca_manager, @@ -283,7 +283,7 @@ impl KrillManager { } /// # Access to components -impl KrillManager { +impl OldManager { pub fn system_actor(&self) -> &Actor { &self.system_actor } @@ -313,7 +313,7 @@ impl KrillManager { } /// # Configure publishers -impl KrillManager { +impl OldManager { /// Returns the repository server stats pub fn repo_stats(&self) -> KrillResult { self.repo_manager.repo_stats() @@ -368,7 +368,7 @@ impl KrillManager { } /// # Manage RFC8181 clients -impl KrillManager { +impl OldManager { pub fn repository_response( &self, publisher: &PublisherHandle, @@ -386,7 +386,7 @@ impl KrillManager { } /// # TA Support -impl KrillManager { +impl OldManager { pub fn ta_proxy_enabled(&self) -> bool { self.config.ta_proxy_enabled() } @@ -479,7 +479,7 @@ impl KrillManager { } /// # Being a parent -impl KrillManager { +impl OldManager { /// Adds a child to a CA and returns the ParentCaInfo that the child /// will need to contact this CA for resource requests. pub fn ca_add_child( @@ -570,7 +570,7 @@ impl KrillManager { } /// # Being a child -impl KrillManager { +impl OldManager { /// Returns the child request for a CA, or NONE if the CA cannot be found. pub fn ca_child_req( &self, @@ -617,7 +617,7 @@ impl KrillManager { } /// # Stats and status of CAS -impl KrillManager { +impl OldManager { pub fn cas_stats( &self, ) -> KrillResult> { @@ -896,7 +896,7 @@ impl KrillManager { } /// # Synchronization operations for CAS -impl KrillManager { +impl OldManager { /// Republish all CAs that need it. pub fn republish_all(&self, force: bool) -> KrillEmptyResult { let cas = self.ca_manager.republish_all(force)?; @@ -937,7 +937,7 @@ impl KrillManager { } /// # Admin CAS -impl KrillManager { +impl OldManager { pub fn ca_handles(&self) -> KrillResult> { self.ca_manager.ca_handles().map(Vec::into_iter) } @@ -1073,7 +1073,7 @@ impl KrillManager { } /// # Handle ASPA requests -impl KrillManager { +impl OldManager { pub fn ca_aspas_definitions_show( &self, ca: &CaHandle, @@ -1104,7 +1104,7 @@ impl KrillManager { } /// # Handle BGPSec requests -impl KrillManager { +impl OldManager { pub fn ca_bgpsec_definitions_show( &self, ca: &CaHandle, @@ -1123,7 +1123,7 @@ impl KrillManager { } /// # Handle route authorization requests -impl KrillManager { +impl OldManager { pub fn ca_routes_update( &self, ca: CaHandle, @@ -1197,7 +1197,7 @@ impl KrillManager { } /// # Handle Repository Server requests -impl KrillManager { +impl OldManager { /// Create the publication server, will fail if it was already created. pub fn repository_init( &self, @@ -1222,7 +1222,7 @@ impl KrillManager { } /// # Handle Resource Tagged Attestation requests -impl KrillManager { +impl OldManager { /// List all known RTAs pub fn rta_list(&self, ca: CaHandle) -> KrillResult { let ca = self.ca_manager.get_ca(&ca)?; diff --git a/src/upgrades/mod.rs b/src/upgrades/mod.rs index ec198b813..08c0d59f2 100644 --- a/src/upgrades/mod.rs +++ b/src/upgrades/mod.rs @@ -34,7 +34,7 @@ use crate::{ }, config::Config, server::{ - manager::KrillManager, + oldmanager::OldManager, properties::PropertiesManager, }, upgrades::pre_0_14_0::{ @@ -1152,7 +1152,7 @@ fn record_preexisting_openssl_keys_in_signer_mapper( /// server is started and operators can make changes. pub async fn post_start_upgrade( report: UpgradeReport, - server: &KrillManager, + server: &OldManager, ) -> KrillResult<()> { if report.versions().from() < &KrillVersion::candidate(0, 9, 3, 2) { info!("Reissue ROAs on upgrade to force short EE certificate subjects in the objects"); From 61c59ded534011c9657ce9a1b63f0b129858390c Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Mon, 12 Jan 2026 18:43:31 +0100 Subject: [PATCH 03/51] =?UTF-8?q?Rename=20HTTP=20server=E2=80=99s=20krill?= =?UTF-8?q?=20member=20to=20old=5Fkrill.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/daemon/http/dispatch/bulk.rs | 16 ++--- src/daemon/http/dispatch/cas.rs | 96 ++++++++++++++--------------- src/daemon/http/dispatch/metrics.rs | 6 +- src/daemon/http/dispatch/pubd.rs | 22 +++---- src/daemon/http/dispatch/root.rs | 10 +-- src/daemon/http/dispatch/stats.rs | 4 +- src/daemon/http/dispatch/ta.rs | 28 ++++----- src/daemon/http/dispatch/testbed.rs | 12 ++-- src/daemon/http/request.rs | 2 +- src/daemon/http/server.rs | 10 +-- 10 files changed, 103 insertions(+), 103 deletions(-) diff --git a/src/daemon/http/dispatch/bulk.rs b/src/daemon/http/dispatch/bulk.rs index 539ae62c9..d0d758f62 100644 --- a/src/daemon/http/dispatch/bulk.rs +++ b/src/daemon/http/dispatch/bulk.rs @@ -40,7 +40,7 @@ async fn cas_import( request.check_post()?; let (request, _) = request.proceed_permitted(Permission::CaAdmin, None)?; let (server, structure) = request.read_json().await?; - server.krill().cas_import(structure).await?; + server.old_krill().cas_import(structure).await?; Ok(HttpResponse::ok()) } @@ -54,9 +54,9 @@ fn cas_issues( let server = request.empty()?; let mut all_issues = AllCertAuthIssues::default(); - for ca in server.krill().ca_handles()? { + for ca in server.old_krill().ca_handles()? { if auth.has_permission(Permission::CaRead, Some(&ca)) { - let issues = server.krill().ca_issues(&ca)?; + let issues = server.old_krill().ca_issues(&ca)?; if !issues.is_empty() { all_issues.cas.insert(ca, issues); } @@ -85,7 +85,7 @@ fn cas_sync_parent( request.check_post()?; let (request, _) = request.proceed_permitted(Permission::CaAdmin, None)?; let server = request.empty()?; - server.krill().cas_refresh_all()?; + server.old_krill().cas_refresh_all()?; Ok(HttpResponse::ok()) } @@ -97,7 +97,7 @@ fn cas_sync_repo( request.check_post()?; let (request, _) = request.proceed_permitted(Permission::CaAdmin, None)?; let server = request.empty()?; - server.krill().cas_repo_sync_all()?; + server.old_krill().cas_repo_sync_all()?; Ok(HttpResponse::ok()) } @@ -109,7 +109,7 @@ fn cas_publish( request.check_post()?; let (request, _) = request.proceed_permitted(Permission::CaAdmin, None)?; let server = request.empty()?; - server.krill().republish_all(false)?; + server.old_krill().republish_all(false)?; Ok(HttpResponse::ok()) } @@ -121,7 +121,7 @@ fn cas_force_publish( request.check_post()?; let (request, _) = request.proceed_permitted(Permission::CaAdmin, None)?; let server = request.empty()?; - server.krill().republish_all(true)?; + server.old_krill().republish_all(true)?; Ok(HttpResponse::ok()) } @@ -133,7 +133,7 @@ fn cas_suspend( request.check_post()?; let (request, _) = request.proceed_permitted(Permission::CaAdmin, None)?; let server = request.empty()?; - server.krill().cas_schedule_suspend_all()?; + server.old_krill().cas_schedule_suspend_all()?; Ok(HttpResponse::ok()) } diff --git a/src/daemon/http/dispatch/cas.rs b/src/daemon/http/dispatch/cas.rs index 6ae701e25..36cd8631d 100644 --- a/src/daemon/http/dispatch/cas.rs +++ b/src/daemon/http/dispatch/cas.rs @@ -52,7 +52,7 @@ fn index_get( Ok(HttpResponse::json( &CertAuthList { cas: { - server.krill().ca_handles()?.filter_map(|handle| { + server.old_krill().ca_handles()?.filter_map(|handle| { auth.has_permission( Permission::CaRead, Some(&handle) ).then_some(CertAuthSummary { handle }) @@ -69,7 +69,7 @@ async fn index_post( Permission::CaCreate, None )?; let (server, init) = request.read_json().await?; - server.krill().ca_init(init)?; + server.old_krill().ca_init(init)?; Ok(HttpResponse::ok()) } @@ -112,7 +112,7 @@ async fn ca_index( )?; let server = request.empty()?; Ok(HttpResponse::json( - &server.krill().ca_info(&ca)? + &server.old_krill().ca_info(&ca)? )) } Method::DELETE => { @@ -120,7 +120,7 @@ async fn ca_index( Permission::CaDelete, Some(&ca) )?; let server = request.empty()?; - server.krill().ca_delete(&ca, auth.actor()).await?; + server.old_krill().ca_delete(&ca, auth.actor()).await?; Ok(HttpResponse::ok()) } _ => Ok(HttpResponse::method_not_allowed()) @@ -153,7 +153,7 @@ async fn aspas_index( )?; let server = request.empty()?; Ok(HttpResponse::json( - &server.krill().ca_aspas_definitions_show(&ca)? + &server.old_krill().ca_aspas_definitions_show(&ca)? )) } Method::POST => { @@ -161,7 +161,7 @@ async fn aspas_index( Permission::AspasUpdate, Some(&ca) )?; let (server, updates) = request.read_json().await?; - server.krill().ca_aspas_definitions_update( + server.old_krill().ca_aspas_definitions_update( ca, updates, auth.actor(), )?; Ok(HttpResponse::ok()) @@ -183,7 +183,7 @@ async fn aspas_as( Permission::AspasUpdate, Some(&ca) )?; let (server, update) = request.read_json().await?; - server.krill().ca_aspas_update_aspa( + server.old_krill().ca_aspas_update_aspa( ca, customer, update, auth.actor() )?; Ok(HttpResponse::ok()) @@ -193,7 +193,7 @@ async fn aspas_as( Permission::AspasUpdate, Some(&ca) )?; let server = request.empty()?; - server.krill().ca_aspas_definitions_update( + server.old_krill().ca_aspas_definitions_update( ca, AspaDefinitionUpdates { add_or_replace: Vec::new(), @@ -223,7 +223,7 @@ async fn bgpsec( )?; let server = request.empty()?; Ok(HttpResponse::json( - &server.krill().ca_bgpsec_definitions_show(&ca)? + &server.old_krill().ca_bgpsec_definitions_show(&ca)? )) } Method::POST => { @@ -231,7 +231,7 @@ async fn bgpsec( Permission::BgpsecUpdate, Some(&ca) )?; let (server, updates) = request.read_json().await?; - server.krill().ca_bgpsec_definitions_update( + server.old_krill().ca_bgpsec_definitions_update( ca, updates, auth.actor() )?; Ok(HttpResponse::ok()) @@ -264,7 +264,7 @@ async fn children_index( )?; let (server, child_req) = request.read_json().await?; Ok(HttpResponse::json( - &server.krill().ca_add_child(&ca, child_req, auth.actor())? + &server.old_krill().ca_add_child(&ca, child_req, auth.actor())? )) } @@ -302,7 +302,7 @@ async fn children_child_index( )?; let server = request.empty()?; Ok(HttpResponse::json( - &server.krill().ca_child_show(&ca, &child)? + &server.old_krill().ca_child_show(&ca, &child)? )) } Method::POST => { @@ -310,7 +310,7 @@ async fn children_child_index( Permission::CaUpdate, Some(&ca) )?; let (server, child_req) = request.read_json().await?; - server.krill().ca_child_update( + server.old_krill().ca_child_update( &ca, child, child_req, auth.actor() )?; Ok(HttpResponse::ok()) @@ -320,7 +320,7 @@ async fn children_child_index( Permission::CaUpdate, Some(&ca) )?; let server = request.empty()?; - server.krill().ca_child_remove(&ca, child, auth.actor())?; + server.old_krill().ca_child_remove(&ca, child, auth.actor())?; Ok(HttpResponse::ok()) } _ => Ok(HttpResponse::method_not_allowed()) @@ -340,7 +340,7 @@ fn children_child_contact( )?; let server = request.empty()?; Ok(HttpResponse::json( - &server.krill().ca_parent_response(&ca, child)? + &server.old_krill().ca_parent_response(&ca, child)? )) } @@ -356,7 +356,7 @@ fn children_child_contact_xml( Permission::CaRead, Some(&ca) )?; let server = request.empty()?; - let res = server.krill().ca_parent_response(&ca, child)?; + let res = server.old_krill().ca_parent_response(&ca, child)?; Ok(HttpResponse::xml(res.to_xml_vec())) } @@ -373,7 +373,7 @@ fn children_child_export( )?; let server = request.empty()?; Ok(HttpResponse::json( - &server.krill().ca_child_export(&ca, &child)? + &server.old_krill().ca_child_export(&ca, &child)? )) } @@ -396,7 +396,7 @@ async fn children_child_import( } )) } - server.krill().ca_child_import(&ca, import, auth.actor())?; + server.old_krill().ca_child_import(&ca, import, auth.actor())?; Ok(HttpResponse::ok()) } @@ -433,7 +433,7 @@ fn history_commands( let server = request.empty()?; Ok(HttpResponse::json( - &server.krill().ca_history( + &server.old_krill().ca_history( &ca, CommandHistoryCriteria { before, after, offset, rows_limit, @@ -457,7 +457,7 @@ fn history_details( let server = request.empty()?; Ok(HttpResponse::json( - &server.krill().ca_command_details(&ca, version).map_err(|err| { + &server.old_krill().ca_command_details(&ca, version).map_err(|err| { match err { Error::AggregateStoreError( AggregateStoreError::UnknownCommand(..) @@ -505,7 +505,7 @@ fn id_index( Permission::CaUpdate, Some(&ca) )?; let server = request.empty()?; - server.krill().ca_update_id(ca, auth.actor())?; + server.old_krill().ca_update_id(ca, auth.actor())?; Ok(HttpResponse::ok()) } @@ -521,7 +521,7 @@ fn id_child_request_json( )?; let server = request.empty()?; Ok(HttpResponse::json( - &server.krill().ca_child_req(&ca)? + &server.old_krill().ca_child_req(&ca)? )) } @@ -537,7 +537,7 @@ fn id_child_request_xml( )?; let server = request.empty()?; Ok(HttpResponse::xml( - server.krill().ca_child_req(&ca)?.to_xml_vec() + server.old_krill().ca_child_req(&ca)?.to_xml_vec() )) } @@ -553,7 +553,7 @@ fn id_publisher_request_json( )?; let server = request.empty()?; Ok(HttpResponse::json( - &server.krill().ca_publisher_req(&ca)? + &server.old_krill().ca_publisher_req(&ca)? )) } @@ -569,7 +569,7 @@ fn id_publisher_request_xml( )?; let server = request.empty()?; Ok(HttpResponse::xml( - server.krill().ca_publisher_req(&ca)?.to_xml_vec() + server.old_krill().ca_publisher_req(&ca)?.to_xml_vec() )) } @@ -588,7 +588,7 @@ fn issues( )?; let server = request.empty()?; Ok(HttpResponse::json( - &server.krill().ca_issues(&ca)? + &server.old_krill().ca_issues(&ca)? )) } @@ -618,7 +618,7 @@ fn keys_roll_init( Permission::CaUpdate, Some(&ca) )?; let server = request.empty()?; - server.krill().ca_keyroll_init(ca, auth.actor())?; + server.old_krill().ca_keyroll_init(ca, auth.actor())?; Ok(HttpResponse::ok()) } @@ -633,7 +633,7 @@ fn keys_roll_activate( Permission::CaUpdate, Some(&ca) )?; let server = request.empty()?; - server.krill().ca_keyroll_activate(ca, auth.actor())?; + server.old_krill().ca_keyroll_activate(ca, auth.actor())?; Ok(HttpResponse::ok()) } @@ -662,7 +662,7 @@ async fn parents_index( )?; let server = request.empty()?; Ok(HttpResponse::json( - &server.krill().ca_status(&ca)?.into_parents() + &server.old_krill().ca_status(&ca)?.into_parents() )) } Method::POST => { @@ -671,7 +671,7 @@ async fn parents_index( )?; let (server, bytes) = request.read_bytes().await?; let parent_req = extract_parent_ca_req(&ca, bytes, None)?; - server.krill().ca_parent_add_or_update( + server.old_krill().ca_parent_add_or_update( ca, parent_req, auth.actor() ).await?; Ok(HttpResponse::ok()) @@ -694,7 +694,7 @@ async fn parents_parent( )?; let server = request.empty()?; Ok(HttpResponse::json( - &server.krill().ca_my_parent_contact(&ca, &parent)? + &server.old_krill().ca_my_parent_contact(&ca, &parent)? )) } Method::POST => { @@ -705,7 +705,7 @@ async fn parents_parent( let parent_req = extract_parent_ca_req( &ca, bytes, Some(parent) )?; - server.krill().ca_parent_add_or_update( + server.old_krill().ca_parent_add_or_update( ca, parent_req, auth.actor() ).await?; Ok(HttpResponse::ok()) @@ -715,7 +715,7 @@ async fn parents_parent( Permission::CaUpdate, Some(&ca) )?; let server = request.empty()?; - server.krill().ca_parent_remove(ca, parent, auth.actor()).await?; + server.old_krill().ca_parent_remove(ca, parent, auth.actor()).await?; Ok(HttpResponse::ok()) } _ => Ok(HttpResponse::method_not_allowed()) @@ -788,7 +788,7 @@ async fn repo_index( )?; let server = request.empty()?; Ok(HttpResponse::json( - &server.krill().ca_repo_details(&ca)? + &server.old_krill().ca_repo_details(&ca)? )) } Method::POST => { @@ -797,7 +797,7 @@ async fn repo_index( )?; let (server, update) = request.read_bytes().await?; let update = extract_repository_contact(&ca, update)?; - server.krill().ca_repo_update(ca, update, auth.actor()).await?; + server.old_krill().ca_repo_update(ca, update, auth.actor()).await?; Ok(HttpResponse::ok()) } _ => Ok(HttpResponse::method_not_allowed()) @@ -842,7 +842,7 @@ fn repo_status( )?; let server = request.empty()?; Ok(HttpResponse::json( - &server.krill().ca_status(&ca)?.into_repo() + &server.old_krill().ca_status(&ca)?.into_repo() )) } @@ -873,7 +873,7 @@ async fn routes_index( )?; let server = request.empty()?; Ok(HttpResponse::json( - &server.krill().ca_routes_show(&ca)? + &server.old_krill().ca_routes_show(&ca)? )) } Method::POST => { @@ -881,7 +881,7 @@ async fn routes_index( Permission::RoutesUpdate, Some(&ca) )?; let (server, updates) = request.read_json().await?; - server.krill().ca_routes_update(ca, updates, auth.actor())?; + server.old_krill().ca_routes_update(ca, updates, auth.actor())?; Ok(HttpResponse::ok()) } _ => Ok(HttpResponse::method_not_allowed()) @@ -900,13 +900,13 @@ async fn routes_try( )?; let (server, mut updates) = request.read_json::().await?; - let effect = server.krill().ca_routes_bgp_dry_run( + let effect = server.old_krill().ca_routes_bgp_dry_run( &ca, updates.clone() )?; if effect.contains_invalids() { updates.set_explicit_max_length(); let resources = updates.affected_prefixes(); - let suggestion = server.krill().ca_routes_bgp_suggest( + let suggestion = server.old_krill().ca_routes_bgp_suggest( &ca, Some(resources) )?; Ok(HttpResponse::json( @@ -916,7 +916,7 @@ async fn routes_try( )) } else { - server.krill().ca_routes_update(ca, updates, auth.actor())?; + server.old_krill().ca_routes_update(ca, updates, auth.actor())?; Ok(HttpResponse::ok()) } } @@ -946,7 +946,7 @@ fn routes_analysis_full( )?; let server = request.empty()?; Ok(HttpResponse::json( - &server.krill().ca_routes_bgp_analysis(&ca)? + &server.old_krill().ca_routes_bgp_analysis(&ca)? )) } @@ -962,7 +962,7 @@ async fn routes_analysis_dryrun( )?; let (server, updates) = request.read_json().await?; Ok(HttpResponse::json( - &server.krill().ca_routes_bgp_dry_run(&ca, updates)? + &server.old_krill().ca_routes_bgp_dry_run(&ca, updates)? )) } @@ -979,7 +979,7 @@ async fn routes_analysis_suggest( )?; let server = request.empty()?; Ok(HttpResponse::json( - &server.krill().ca_routes_bgp_suggest(&ca, None)? + &server.old_krill().ca_routes_bgp_suggest(&ca, None)? )) } Method::POST => { @@ -988,7 +988,7 @@ async fn routes_analysis_suggest( )?; let (server, resources) = request.read_json().await?; Ok(HttpResponse::json( - &server.krill().ca_routes_bgp_suggest( + &server.old_krill().ca_routes_bgp_suggest( &ca, Some(resources) )? )) @@ -1034,7 +1034,7 @@ fn stats_children_connections( )?; let server = request.empty()?; Ok(HttpResponse::json( - &server.krill().ca_stats_child_connections(&ca)? + &server.old_krill().ca_stats_child_connections(&ca)? )) } @@ -1064,7 +1064,7 @@ fn sync_parents( Permission::CaUpdate, Some(&ca) )?; let server = request.empty()?; - server.krill().cas_refresh_single(ca)?; + server.old_krill().cas_refresh_single(ca)?; Ok(HttpResponse::ok()) } @@ -1079,7 +1079,7 @@ fn sync_repo( Permission::CaUpdate, Some(&ca) )?; let server = request.empty()?; - server.krill().cas_repo_sync_single(&ca)?; + server.old_krill().cas_repo_sync_single(&ca)?; Ok(HttpResponse::ok()) } diff --git a/src/daemon/http/dispatch/metrics.rs b/src/daemon/http/dispatch/metrics.rs index 5b1dab510..48d1f74e7 100644 --- a/src/daemon/http/dispatch/metrics.rs +++ b/src/daemon/http/dispatch/metrics.rs @@ -59,7 +59,7 @@ pub async fn dispatch( server.authorizer().login_session_cache_size().await, ); - if let Ok(cas_stats) = server.krill().cas_stats() { + if let Ok(cas_stats) = server.old_krill().cas_stats() { target.single( Metric::gauge("cas", "number of CAs in Krill"), cas_stats.len() @@ -70,7 +70,7 @@ pub async fn dispatch( let mut ca_status_map = HashMap::new(); for ca in cas_stats.keys() { - if let Ok(ca_status) = server.krill().ca_status(ca) { + if let Ok(ca_status) = server.old_krill().ca_status(ca) { ca_status_map.insert(ca.clone(), ca_status); } } @@ -386,7 +386,7 @@ pub async fn dispatch( } } - if let Ok(stats) = server.krill().repo_stats() { + if let Ok(stats) = server.old_krill().repo_stats() { target.single( Metric::gauge( "repo_publisher", diff --git a/src/daemon/http/dispatch/pubd.rs b/src/daemon/http/dispatch/pubd.rs index cc58385d4..eb3b54135 100644 --- a/src/daemon/http/dispatch/pubd.rs +++ b/src/daemon/http/dispatch/pubd.rs @@ -39,7 +39,7 @@ async fn delete( Permission::PubAdmin, None )?; let (server, criteria) = request.read_json().await?; - server.krill().delete_matching_files(criteria)?; + server.old_krill().delete_matching_files(criteria)?; Ok(HttpResponse::ok()) } @@ -57,7 +57,7 @@ async fn init( Permission::PubAdmin, None )?; let (server, uris) = request.read_json().await?; - server.krill().repository_init(uris)?; + server.old_krill().repository_init(uris)?; Ok(HttpResponse::ok()) } Method::DELETE => { @@ -65,7 +65,7 @@ async fn init( Permission::PubAdmin, None )?; let server = request.empty()?; - server.krill().repository_clear()?; + server.old_krill().repository_clear()?; Ok(HttpResponse::ok()) } _ => Ok(HttpResponse::method_not_allowed()) @@ -99,7 +99,7 @@ async fn publishers_index( Ok(HttpResponse::json( &PublisherList { publishers: { - server.krill().publishers()?.into_iter().map( + server.old_krill().publishers()?.into_iter().map( PublisherSummary::from_handle ).collect() } @@ -112,7 +112,7 @@ async fn publishers_index( )?; let (server, pbl) = request.read_json().await?; Ok(HttpResponse::json( - &server.krill().add_publisher(pbl, auth.actor())? + &server.old_krill().add_publisher(pbl, auth.actor())? )) } _ => Ok(HttpResponse::method_not_allowed()) @@ -147,7 +147,7 @@ fn publishers_publisher_index( )?; let server = request.empty()?; Ok(HttpResponse::json( - &server.krill().get_publisher(publisher)? + &server.old_krill().get_publisher(publisher)? )) } Method::DELETE => { @@ -155,7 +155,7 @@ fn publishers_publisher_index( Permission::PubDelete, None )?; let server = request.empty()?; - server.krill().remove_publisher(publisher, auth.actor())?; + server.old_krill().remove_publisher(publisher, auth.actor())?; Ok(HttpResponse::ok()) } _ => Ok(HttpResponse::method_not_allowed()) @@ -172,7 +172,7 @@ fn publishers_publisher_response( let (request, _) = request.proceed_permitted(Permission::PubRead, None)?; let server = request.empty()?; Ok(HttpResponse::json( - &server.krill().repository_response(&publisher)? + &server.old_krill().repository_response(&publisher)? )) } @@ -186,7 +186,7 @@ fn publishers_publisher_response_xml( let (request, _) = request.proceed_permitted(Permission::PubRead, None)?; let server = request.empty()?; Ok(HttpResponse::xml( - server.krill().repository_response(&publisher)?.to_xml_vec() + server.old_krill().repository_response(&publisher)?.to_xml_vec() )) } @@ -201,7 +201,7 @@ fn session_reset( request.check_post()?; let (request, _) = request.proceed_permitted(Permission::PubAdmin, None)?; let server = request.empty()?; - server.krill().repository_session_reset()?; + server.old_krill().repository_session_reset()?; Ok(HttpResponse::ok()) } @@ -217,7 +217,7 @@ async fn stale( request.check_get()?; let (request, _) = request.proceed_permitted( Permission::PubList, None)?; let server = request.empty()?; - let stats = server.krill().repo_stats()?; + let stats = server.old_krill().repo_stats()?; Ok(HttpResponse::json( &PublisherList { publishers: { diff --git a/src/daemon/http/dispatch/root.rs b/src/daemon/http/dispatch/root.rs index f9a85003a..ac2281855 100644 --- a/src/daemon/http/dispatch/root.rs +++ b/src/daemon/http/dispatch/root.rs @@ -74,7 +74,7 @@ async fn rfc8181( let (request, _) = request.proceed_unchecked(); let (server, bytes) = request.read_rfc8181_bytes().await?; Ok(HttpResponse::rfc8181( - server.krill().rfc8181(publisher, bytes)? + server.old_krill().rfc8181(publisher, bytes)? )) } @@ -97,7 +97,7 @@ async fn rfc6492( // always be the anonymous actor. Maybe the CA manager should // determine the actor when looking at the ID certificate? Ok(HttpResponse::rfc6492( - server.krill().rfc6492(ca , bytes, user_agent, auth.actor())? + server.old_krill().rfc6492(ca , bytes, user_agent, auth.actor())? )) } @@ -122,7 +122,7 @@ fn tal( let (request, _) = request.proceed_unchecked(); let server = request.empty()?; Ok(HttpResponse::text( - server.krill().ta_cert_details()?.tal.to_string() + server.old_krill().ta_cert_details()?.tal.to_string() )) } @@ -134,7 +134,7 @@ fn ta_cer( let (request, _) = request.proceed_unchecked(); let server = request.empty()?; Ok(HttpResponse::cert( - server.krill().ta_cert_details()?.cert.to_bytes() + server.old_krill().ta_cert_details()?.cert.to_bytes() )) } @@ -150,7 +150,7 @@ fn rrdp( let Some(remaining) = path.remaining() else { return Ok(HttpResponse::not_found()) }; - let path = match server.krill().resolve_rrdp_request_path(remaining)? { + let path = match server.old_krill().resolve_rrdp_request_path(remaining)? { Some(path) => path, None => { return Ok(HttpResponse::not_found()) diff --git a/src/daemon/http/dispatch/stats.rs b/src/daemon/http/dispatch/stats.rs index aa5914d7e..3eadeb55f 100644 --- a/src/daemon/http/dispatch/stats.rs +++ b/src/daemon/http/dispatch/stats.rs @@ -44,7 +44,7 @@ fn repo( request.check_get()?; let (request, _) = request.proceed_unchecked(); let server = request.empty()?; - Ok(HttpResponse::json(&server.krill().repo_stats()?)) + Ok(HttpResponse::json(&server.old_krill().repo_stats()?)) } @@ -58,6 +58,6 @@ async fn cas( request.check_get()?; let (request, _) = request.proceed_unchecked(); let server = request.empty()?; - Ok(HttpResponse::json(&server.krill().cas_stats()?)) + Ok(HttpResponse::json(&server.old_krill().cas_stats()?)) } diff --git a/src/daemon/http/dispatch/ta.rs b/src/daemon/http/dispatch/ta.rs index 7755b4dbc..167c42912 100644 --- a/src/daemon/http/dispatch/ta.rs +++ b/src/daemon/http/dispatch/ta.rs @@ -69,7 +69,7 @@ async fn proxy_children_index( )?; let (server, child) = request.read_json().await?; Ok(HttpResponse::json( - &server.krill().ta_proxy_children_add(child, auth.actor())? + &server.old_krill().ta_proxy_children_add(child, auth.actor())? )) } _ => Ok(HttpResponse::method_not_allowed()) @@ -126,7 +126,7 @@ fn proxy_children_child_response( )?; let server = request.empty()?; Ok(HttpResponse::json( - &server.krill().ca_parent_response(&ta_handle(), child)? + &server.old_krill().ca_parent_response(&ta_handle(), child)? )) } @@ -142,7 +142,7 @@ fn proxy_children_child_response_xml( )?; let server = request.empty()?; Ok(HttpResponse::xml( - server.krill().ca_parent_response(&ta_handle(), child)?.to_xml_vec() + server.old_krill().ca_parent_response(&ta_handle(), child)?.to_xml_vec() )) } @@ -159,7 +159,7 @@ fn proxy_init( Permission::CaAdmin, None )?; let server = request.empty()?; - server.krill().ta_proxy_init()?; + server.old_krill().ta_proxy_init()?; Ok(HttpResponse::ok()) } @@ -177,7 +177,7 @@ fn proxy_id( )?; let server = request.empty()?; Ok(HttpResponse::json( - &server.krill().ta_proxy_id()? + &server.old_krill().ta_proxy_id()? )) } @@ -206,7 +206,7 @@ async fn proxy_repo_index( )?; let server = request.empty()?; Ok(HttpResponse::json( - &server.krill().ta_proxy_repository_contact()? + &server.old_krill().ta_proxy_repository_contact()? )) } Method::POST => { @@ -217,7 +217,7 @@ async fn proxy_repo_index( let update = super::cas::extract_repository_contact( &ta_handle(), update )?; - server.krill().ta_proxy_repository_update(update, auth.actor())?; + server.old_krill().ta_proxy_repository_update(update, auth.actor())?; Ok(HttpResponse::ok()) } _ => Ok(HttpResponse::method_not_allowed()) @@ -233,7 +233,7 @@ fn proxy_repo_request( let (request, _) = request.proceed_permitted(Permission::CaAdmin, None)?; let server = request.empty()?; Ok(HttpResponse::json( - &server.krill().ta_proxy_publisher_request()? + &server.old_krill().ta_proxy_publisher_request()? )) } @@ -246,7 +246,7 @@ fn proxy_repo_request_xml( let (request, _) = request.proceed_permitted(Permission::CaAdmin, None)?; let server = request.empty()?; Ok(HttpResponse::xml( - server.krill().ta_proxy_publisher_request()?.to_xml_vec() + server.old_krill().ta_proxy_publisher_request()?.to_xml_vec() )) } @@ -276,7 +276,7 @@ async fn proxy_signer_add( Permission::CaAdmin, None )?; let (server, info) = request.read_json().await?; - server.krill().ta_proxy_signer_add(info, auth.actor())?; + server.old_krill().ta_proxy_signer_add(info, auth.actor())?; Ok(HttpResponse::ok()) } @@ -292,7 +292,7 @@ fn proxy_signer_request( )?; let server = request.empty()?; Ok(HttpResponse::json( - &server.krill().ta_proxy_signer_get_request()? + &server.old_krill().ta_proxy_signer_get_request()? )) } Method::POST => { @@ -301,7 +301,7 @@ fn proxy_signer_request( )?; let server = request.empty()?; Ok(HttpResponse::json( - &server.krill().ta_proxy_signer_make_request( + &server.old_krill().ta_proxy_signer_make_request( auth.actor() )? )) @@ -320,7 +320,7 @@ async fn proxy_signer_response( Permission::CaAdmin, None )?; let (server, response) = request.read_json().await?; - server.krill().ta_proxy_signer_process_response(response, auth.actor())?; + server.old_krill().ta_proxy_signer_process_response(response, auth.actor())?; Ok(HttpResponse::ok()) } @@ -334,7 +334,7 @@ async fn proxy_signer_update( Permission::CaAdmin, None )?; let (server, info) = request.read_json().await?; - server.krill().ta_proxy_signer_update(info, auth.actor())?; + server.old_krill().ta_proxy_signer_update(info, auth.actor())?; Ok(HttpResponse::ok()) } diff --git a/src/daemon/http/dispatch/testbed.rs b/src/daemon/http/dispatch/testbed.rs index 907d9acc9..d19a19459 100644 --- a/src/daemon/http/dispatch/testbed.rs +++ b/src/daemon/http/dispatch/testbed.rs @@ -83,7 +83,7 @@ async fn children_index( let (request, _) = request.proceed_unchecked(); let (server, child) = request.read_json().await?; Ok(HttpResponse::json( - &server.krill().ca_add_child( + &server.old_krill().ca_add_child( &testbed_ca_handle(), child, &Actor::anonymous() )? )) @@ -110,7 +110,7 @@ fn children_child_index( request.check_delete()?; let (request, _) = request.proceed_unchecked(); let server = request.empty()?; - server.krill().ca_child_remove( + server.old_krill().ca_child_remove( &testbed_ca_handle(), child, &Actor::anonymous() )?; Ok(HttpResponse::ok()) @@ -126,7 +126,7 @@ fn children_child_response( let (request, _) = request.proceed_unchecked(); let server = request.empty()?; Ok(HttpResponse::xml( - server.krill().ca_parent_response( + server.old_krill().ca_parent_response( &testbed_ca_handle(), child )?.to_xml_vec() )) @@ -152,7 +152,7 @@ async fn publishers_index( let (request, _) = request.proceed_unchecked(); let (server, pbl) = request.read_json().await?; Ok(HttpResponse::json( - &server.krill().add_publisher(pbl, &Actor::anonymous())? + &server.old_krill().add_publisher(pbl, &Actor::anonymous())? )) } @@ -177,7 +177,7 @@ fn publishers_publisher_index( request.check_delete()?; let (request, _) = request.proceed_unchecked(); let server = request.empty()?; - server.krill().remove_publisher( + server.old_krill().remove_publisher( publisher, &Actor::anonymous() )?; Ok(HttpResponse::ok()) @@ -193,7 +193,7 @@ fn publishers_publisher_response( let (request, _) = request.proceed_unchecked(); let server = request.empty()?; Ok(HttpResponse::xml( - server.krill().repository_response(&publisher)?.to_xml_vec() + server.old_krill().repository_response(&publisher)?.to_xml_vec() )) } diff --git a/src/daemon/http/request.rs b/src/daemon/http/request.rs index 05ceb364b..b9a47b714 100644 --- a/src/daemon/http/request.rs +++ b/src/daemon/http/request.rs @@ -59,7 +59,7 @@ impl<'a> Request<'a> { /// Returns whether testbed mode is enabled. pub fn testbed_enabled(&self) -> bool { - self.server.krill().testbed_enabled() + self.server.old_krill().testbed_enabled() } /// Returns the method of this request. diff --git a/src/daemon/http/server.rs b/src/daemon/http/server.rs index aa009a8e5..44011ba97 100644 --- a/src/daemon/http/server.rs +++ b/src/daemon/http/server.rs @@ -23,7 +23,7 @@ use super::response::{HyperResponse, HttpResponse}; /// The Krill HTTP server. pub struct HttpServer { /// The Krill “business logic.” - krill: OldManager, + old_krill: OldManager, /// The component responsible for API authorization checks authorizer: Authorizer, @@ -38,14 +38,14 @@ pub struct HttpServer { impl HttpServer { /// Creates a new server from a Krill manager and the configuration. pub fn new( - krill: OldManager, + old_krill: OldManager, config: Arc, runtime: &runtime::Handle, ) -> KrillResult> { let authorizer = Authorizer::new(config.clone())?; authorizer.spawn_sweep(runtime); Ok(Self { - krill, + old_krill, authorizer, config, started: Timestamp::now(), @@ -95,8 +95,8 @@ impl HttpServer { impl HttpServer { /// Returns a reference to the Krill manager. - pub(super) fn krill(&self) -> &OldManager { - &self.krill + pub(super) fn old_krill(&self) -> &OldManager { + &self.old_krill } /// Returns a reference to the authorizer. From c4f7d7f07e28aecb4556e622647dedd9a2cd5ab6 Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Mon, 12 Jan 2026 18:59:32 +0100 Subject: [PATCH 04/51] Arrange Krill server, manager, and runtime. --- src/server/manager.rs | 136 +++++++++++++++++++++++++++ src/server/mod.rs | 1 + src/server/runtime.rs | 210 +++++++++++++++++++++--------------------- 3 files changed, 243 insertions(+), 104 deletions(-) create mode 100644 src/server/manager.rs diff --git a/src/server/manager.rs b/src/server/manager.rs new file mode 100644 index 000000000..e5d9669b5 --- /dev/null +++ b/src/server/manager.rs @@ -0,0 +1,136 @@ +//! The public part of the Krill RPKI server. +//! + +use std::{error, fmt}; +use hyper::StatusCode; +use rpki::ca::publication; +use tokio::sync::oneshot; +use crate::api::status::ErrorResponse; +use crate::commons::error::KrillError; +use super::runtime::{KrillRuntime, Errand}; + + +//------------ KrillServer --------------------------------------------------- + +/// Provides access to a [`KrillManager`] from an async runtime. +/// +/// A value of this type is owned by the HTTP server and allows it to call +/// into Krill for processing requests. This can only be achieved via the +/// two methods [`run`][Self::run] and [`run_errand`][Self::run_errand] +/// which provide access to the [`KrillManager`] via a closure run on the +/// sync runtime. +/// +/// This type is cheaply clonable and does not need to be kept in an arc. +pub struct KrillServer { + manager: KrillManager, +} + +impl KrillServer { + /// Runs a sync closure which provides an immediate result. + /// + /// The closure `op` is run on the sync runtime. It has access to the + /// [`KrillManager`] via its sole argument. Whatever the closure returns + /// is what this async method resolves into. + /// + /// If, for whatever reason, the closure does not run to completion, + /// an error is returned. + pub async fn run( + &self, op: F + ) -> Result + where + F: FnOnce(&KrillManager) -> Result + Send + 'static, + T: Send + 'static, + E: Into + Send + 'static + { + let (tx, rx) = oneshot::channel(); + let manager = self.manager.clone(); + self.manager.runtime.spawn_blocking(move || { + let _ = tx.send(op(&manager)); + }); + rx.await?.map_err(Into::into) + } + + /// Runs an errand using the `KrillManager`. + /// + /// An errand is a multi-phase process involving a sequence of sync and + /// async portions chained together. If a method of the [`KrillManager`] + /// returns such an errand by returning a value that implements the + /// [`Errand`] trait, the `run_errand` method can be used to evaluate + /// the errand and receive its result. + /// + /// The closure `op` is run on the sync runtime. It has access to the + /// [`KrillManager`] via its sole argument. The returned errand is then + /// run on either the sync or async runtimes as needed. + /// + /// If, for whatever reason, the closure or returned errand do not run to + /// completion, an error is returned. + pub async fn run_errand( + &self, op: F + ) -> Result + where + F: FnOnce(&KrillManager) -> P + Send + 'static, + P: Errand>, + T: Send + 'static, + E: Into + Send + 'static + { + let (tx, rx) = oneshot::channel(); + let manager = self.manager.clone(); + self.manager.runtime.spawn_blocking(move || { + op(&manager).finish(tx); + }); + rx.await?.map_err(Into::into) + } +} + + +//------------ KrillManager -------------------------------------------------- + +#[derive(Clone)] +pub struct KrillManager { + runtime: KrillRuntime, +} + + +//------------ RunError ------------------------------------------------------ + +/// An error happened when running an operation. +// +// This is a separate type in preparation for refactoring error handling. For +// now, it just wraps a `KrillError`. +#[derive(Debug)] +pub struct RunError(KrillError); + +impl RunError { + pub fn status(&self) -> StatusCode { + self.0.status() + } + + pub fn to_error_response(&self) -> ErrorResponse { + self.0.to_error_response() + } + + pub fn to_rfc8181_error_code(&self) -> publication::ReportErrorCode { + self.0.to_rfc8181_error_code() + } +} + +impl From for RunError { + fn from(src: KrillError) -> Self { + Self(src) + } +} + +impl From for RunError { + fn from(_: oneshot::error::RecvError) -> Self { + Self(KrillError::internal("operation dropped")) + } +} + +impl fmt::Display for RunError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.0.fmt(f) + } +} + +impl error::Error for RunError { } + diff --git a/src/server/mod.rs b/src/server/mod.rs index 88e9dcac4..221af7ec9 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -1,5 +1,6 @@ pub mod bgp; pub mod ca; +pub mod manager; pub mod mq; pub mod properties; pub mod pubd; diff --git a/src/server/runtime.rs b/src/server/runtime.rs index 912e8977e..be3eb6f4e 100644 --- a/src/server/runtime.rs +++ b/src/server/runtime.rs @@ -19,65 +19,115 @@ //! -use std::{error, fmt}; use std::sync::Arc; -use hyper::StatusCode; -use rpki::ca::publication; +//use std::time::Duration; +//use log::info; +use rpki::uri; use tokio::runtime; use tokio::sync::oneshot; -use crate::commons::error::KrillError; -use crate::api::status::ErrorResponse; +use crate::commons::actor::Actor; +use crate::commons::crypto::{KrillSigner/*, KrillSignerBuilder*/}; +//use crate::commons::error::KrillError; +use crate::config::Config; +//use crate::constants::{ACTOR_DEF_KRILL, KRILL_SERVER_APP}; +use super::bgp::BgpAnalyser; +use super::ca::CaManager; +use super::mq::TaskQueue; +use super::pubd::RepositoryManager; //------------ KrillRuntime -------------------------------------------------- +#[derive(Clone)] pub struct KrillRuntime(Arc); impl KrillRuntime { - pub async fn run( - &self, op: F - ) -> Result - where - F: FnOnce() -> Result + Send + 'static, - T: Send + 'static, - E: Into + Send + 'static - { - let (tx, rx) = oneshot::channel(); - self.0.tokio.spawn_blocking(|| { - let _ = tx.send(op()); - }); - rx.await?.map_err(Into::into) + /* + pub fn new( + config: Config, + tokio: runtime::Handle, + ) -> Result { + let service_uri = config.service_uri(); + + info!("{KRILL_SERVER_APP} uses service uri: {service_uri}"); + + // Assumes that Config::verify() has already ensured that the signer + // configuration is valid and that Config::resolve() has been + // used to update signer name references to resolve to the + // corresponding signer configurations. + let signer = KrillSignerBuilder::new( + &config.storage_uri, + Duration::from_secs(config.signer_probe_retry_seconds), + &config.signers, + ).with_default_signer( + config.default_signer() + ).with_one_off_signer( + config.one_off_signer() + ).build()?; + + let tasks = TaskQueue::new(&config.storage_uri)?; + let repo_manager = RepositoryManager::build(&config)?; + let ca_manager = CaManager::build(&config)?; + let bgp_analyser = BgpAnalyser::new(&config); + + Ok(Self(Arc::new(Components { + config, + service_uri, + repo_manager, + ca_manager, + tasks, + signer, + bgp_analyser, + system_actor: ACTOR_DEF_KRILL, + tokio, + }))) + } + */ + + pub fn config(&self) -> &Config { + &self.0.config } - pub async fn run_errand( - &self, op: F - ) -> Result - where - F: FnOnce() -> P + Send + 'static, - P: Phase>, - T: Send + 'static, - E: Into + Send + 'static - { - let (tx, rx) = oneshot::channel(); - self.0.tokio.spawn_blocking(|| { - op().finish(tx); - }); - rx.await?.map_err(Into::into) + pub fn service_uri(&self) -> &uri::Https { + &self.0.service_uri } -} + pub fn repo_manager(&self) -> &RepositoryManager { + &self.0.repo_manager + } -//------------ ErrandRuntime ------------------------------------------------- + pub fn ca_manager(&self) -> &CaManager { + &self.0.ca_manager + } -#[derive(Clone)] -pub struct ErrandRuntime(Arc); + pub fn tasks(&self) -> &TaskQueue { + &self.0.tasks + } + + pub fn signer(&self) -> &KrillSigner { + &self.0.signer + } -impl ErrandRuntime { - fn spawn_async(&self, future: impl Future + Send + 'static) { + pub fn system_actor(&self) -> &Actor { + &self.0.system_actor + } + + /// Returns whether testbed mode is enabled. + pub fn is_testbed_enabled(&self) -> bool { + self.config().testbed().is_some() + } + + /// Spawns a future onto the async runtime. + pub fn spawn_async( + &self, future: impl Future + Send + 'static + ) { let _ = self.0.tokio.spawn(future); } - fn spawn_blocking(&self, op: impl FnOnce() + Send + 'static) { + /// Spawns a closure onto the sync runtime. + pub fn spawn_blocking( + &self, op: impl FnOnce() + Send + 'static + ) { let _ = self.0.tokio.spawn_blocking(op); } } @@ -86,7 +136,6 @@ impl ErrandRuntime { //------------ Components ---------------------------------------------------- struct Components { - /* /// The server configuration. /// /// This has to be an arc for now since some components keep a copy. @@ -98,27 +147,23 @@ struct Components { /// value which may be missing. service_uri: uri::Https, - /// Publication server, with configured publishers + /// Publication server with configured publishers repo_manager: RepositoryManager, /// The manager for all our CAs. ca_manager: CaManager, /// The task queue. - /// - /// This needs to remanin an arc for now since it needs to be given to - /// aggregate listeners. tasks: TaskQueue, /// The signer. - /// - /// This needs to remain an arc for now because it is kept with some - /// commands. signer: KrillSigner, + /// The BGP analyser. + bgp_analyser: Arc, + /// The actor used for actions initiated by the server itself. system_actor: Actor, - */ /// The Tokio runtime to spawn tasks onto. /// @@ -128,9 +173,9 @@ struct Components { } -//------------ Errand -------------------------------------------------------- +//------------ Init ---------------------------------------------------------- -pub struct Errand { +pub struct Init { /// The capture value passed along during execution. capture: Cap, @@ -138,10 +183,10 @@ pub struct Errand { value: MaybeFuture, /// The Krill runtime to use and pass along. - krill: ErrandRuntime, + krill: KrillRuntime, } -impl Errand { +impl Init { pub fn then(self, op: Op) -> Then { Then { before: self, @@ -150,7 +195,7 @@ impl Errand { } } -impl Phase for Errand +impl Errand for Init where Cap: Send + 'static, Fut: Future + Send + 'static, @@ -162,7 +207,7 @@ where fn run(self, then: Then) where Then: - FnOnce(Cap, Self::Output, ErrandRuntime) + FnOnce(Cap, Self::Output, KrillRuntime) + Send + 'static { match self.value { @@ -215,9 +260,9 @@ impl Then { } } -impl Phase for Then +impl Errand for Then where - Before: Phase, + Before: Errand, Op: IntoMaybeFuture, { type Capture = Op::Capture; @@ -226,7 +271,7 @@ where fn run(self, then: Then) where Then: - FnOnce(Op::Capture, Self::Output, ErrandRuntime) + FnOnce(Op::Capture, Self::Output, KrillRuntime) + Send + 'static { self.before.run(|mut capture, input, krill| { @@ -286,68 +331,25 @@ pub trait IntoMaybeFuture: Send + 'static { self, capture: &mut Self::Capture, input: Self::Input, - krill: &ErrandRuntime, + krill: &KrillRuntime, ) -> MaybeFuture; } -//------------ Phase --------------------------------------------------------- +//------------ Errand -------------------------------------------------------- /// A single step in running an errand. -pub trait Phase: Sized { +pub trait Errand: Sized { type Capture: Send + 'static; type Output: Send + 'static; fn run(self, then: Then) where Then: - FnOnce(Self::Capture, Self::Output, ErrandRuntime) + FnOnce(Self::Capture, Self::Output, KrillRuntime) + Send + 'static ; fn finish(self, tx: oneshot::Sender); } - -//------------ RunError ------------------------------------------------------ - -/// An error happened when running an operation. -// -// This is a separate type in preparation for refactoring error handling. For -// now, it just wraps a `KrillError`. -#[derive(Debug)] -pub struct RunError(KrillError); - -impl RunError { - pub fn status(&self) -> StatusCode { - self.0.status() - } - - pub fn to_error_response(&self) -> ErrorResponse { - self.0.to_error_response() - } - - pub fn to_rfc8181_error_code(&self) -> publication::ReportErrorCode { - self.0.to_rfc8181_error_code() - } -} - -impl From for RunError { - fn from(src: KrillError) -> Self { - Self(src) - } -} - -impl From for RunError { - fn from(_: oneshot::error::RecvError) -> Self { - Self(KrillError::internal("operation dropped")) - } -} - -impl fmt::Display for RunError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.0.fmt(f) - } -} - -impl error::Error for RunError { } From c2885df1b60f79e789cde90f25130844034cf0e9 Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Tue, 13 Jan 2026 10:24:55 +0100 Subject: [PATCH 05/51] Make BGP Analyser in Krill Runtime available. --- src/server/runtime.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/server/runtime.rs b/src/server/runtime.rs index be3eb6f4e..e22be2c87 100644 --- a/src/server/runtime.rs +++ b/src/server/runtime.rs @@ -108,6 +108,10 @@ impl KrillRuntime { &self.0.signer } + pub fn bpg_analyseer(&self) -> &BgpAnalyser { + &self.0.bgp_analyser + } + pub fn system_actor(&self) -> &Actor { &self.0.system_actor } From f416c7ec74706589a144cdac99880ca14a6262aa Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Tue, 13 Jan 2026 18:23:50 +0100 Subject: [PATCH 06/51] Refactor Aggregate to use trait methods instead of event listeners. --- src/cli/ta/signer.rs | 22 +- .../crypto/signing/dispatch/signerinfo.rs | 4 + src/commons/eventsourcing/agg.rs | 62 ++- src/commons/eventsourcing/mod.rs | 5 +- src/commons/eventsourcing/store.rs | 81 ++-- src/commons/eventsourcing/test.rs | 29 +- src/server/ca/certauth.rs | 184 ++++---- src/server/ca/commands.rs | 125 ++---- src/server/ca/manager.rs | 412 +++++++++--------- src/server/ca/publishing.rs | 7 +- src/server/mq.rs | 58 ++- src/server/oldmanager.rs | 105 +++-- src/server/properties/mod.rs | 4 + src/server/pubd/access.rs | 4 + src/server/runtime.rs | 4 +- src/server/scheduler.rs | 26 +- src/server/taproxy.rs | 139 ++++-- src/tasigner/config.rs | 5 +- src/tasigner/signer.rs | 74 ++-- 19 files changed, 743 insertions(+), 607 deletions(-) diff --git a/src/cli/ta/signer.rs b/src/cli/ta/signer.rs index bd05d76d1..725f22994 100644 --- a/src/cli/ta/signer.rs +++ b/src/cli/ta/signer.rs @@ -19,8 +19,8 @@ use crate::commons::storage::Ident; use crate::commons::httpclient; use crate::tasigner::{ Config, TrustAnchorProxySignerExchanges, - TrustAnchorSigner, TrustAnchorSignerCommand, TrustAnchorSignerInitCommand, - TrustAnchorSignerInitCommandDetails, + TrustAnchorSigner, TrustAnchorSignerCommand, TrustAnchorSignerContext, + TrustAnchorSignerInitCommand, TrustAnchorSignerInitCommandDetails, }; @@ -116,7 +116,7 @@ pub struct TrustAnchorSignerManager { store: AggregateStore, ta_handle: CaHandle, config: Config, - signer: Arc, + signer: KrillSigner, actor: Actor, } @@ -140,6 +140,10 @@ impl TrustAnchorSignerManager { }) } + fn context(&self) -> TrustAnchorSignerContext<'_> { + TrustAnchorSignerContext::new(&self.signer, self.config.ta_timing) + } + pub fn init( &self, info: SignerInitInfo, @@ -158,13 +162,11 @@ impl TrustAnchorSignerManager { tal_rsync: info.tal_rsync, private_key_pem: info.private_key_pem, ta_mft_nr_override: info.ta_mft_nr_override, - timing: self.config.ta_timing, - signer: self.signer.clone(), }, &self.actor, ); - self.store.add(cmd)?; + self.store.add_with_context(cmd, self.context())?; Ok(Success) } @@ -181,12 +183,10 @@ impl TrustAnchorSignerManager { info.repo_info, info.tal_https, info.tal_rsync, - self.config.ta_timing, - self.signer.clone(), &self.actor, ); - self.store.command(cmd)?; + self.store.command_with_context(cmd, self.context())?; Ok(Success) } @@ -204,12 +204,10 @@ impl TrustAnchorSignerManager { let cmd = TrustAnchorSignerCommand::make_process_request_command( &self.ta_handle, signed_request, - self.config.ta_timing, ta_mft_number_override, - self.signer.clone(), &self.actor, ); - self.store.command(cmd)?; + self.store.command_with_context(cmd, self.context())?; self.show_last_response() } diff --git a/src/commons/crypto/signing/dispatch/signerinfo.rs b/src/commons/crypto/signing/dispatch/signerinfo.rs index fe109b3a5..89b508b58 100644 --- a/src/commons/crypto/signing/dispatch/signerinfo.rs +++ b/src/commons/crypto/signing/dispatch/signerinfo.rs @@ -334,6 +334,8 @@ impl Aggregate for SignerInfo { type Error = Error; + type Context<'a> = (); + fn init(handle: &MyHandle, init: SignerInfoInitEvent) -> Self { SignerInfo { version: 0, @@ -373,6 +375,7 @@ impl Aggregate for SignerInfo { fn process_command( &self, command: Self::Command, + _context: Self::Context<'_>, ) -> Result, Self::Error> { Ok(match command.into_details() { SignerInfoCommandDetails::Init => { @@ -413,6 +416,7 @@ impl Aggregate for SignerInfo { fn process_init_command( command: SignerInfoInitCommand, + _context: Self::Context<'_>, ) -> Result { let details = command.into_details(); Ok(SignerInfoInitEvent { diff --git a/src/commons/eventsourcing/agg.rs b/src/commons/eventsourcing/agg.rs index f29dfc810..4f979a7aa 100644 --- a/src/commons/eventsourcing/agg.rs +++ b/src/commons/eventsourcing/agg.rs @@ -40,7 +40,9 @@ pub trait Aggregate: Storable + 'static { >; /// The type representing consecutive commands. - type Command: Command; + type Command: Command< + StorableDetails = Self::StorableCommandDetails + >; /// The type representing the details of a command to be stored. type StorableCommandDetails: WithStorableDetails; @@ -54,6 +56,12 @@ pub trait Aggregate: Storable + 'static { /// The type returned when processing a command fails. type Error: std::error::Error + Send + Sync + From; + /// The type for passing context into processing. + /// + /// This should be a shared reference or a collection of + /// shared references, hence the requirement to be `Copy`. + type Context<'a>: Copy where Self: 'a; + /// Creates a new instance. /// /// Expects an [`InitEvent`][Self::InitEvent] with data needed to @@ -77,6 +85,7 @@ pub trait Aggregate: Storable + 'static { /// history. fn process_init_command( command: Self::InitCommand, + context: Self::Context<'_>, ) -> Result; /// Processes a command. @@ -91,6 +100,7 @@ pub trait Aggregate: Storable + 'static { fn process_command( &self, command: Self::Command, + context: Self::Context<'_>, ) -> Result, Self::Error>; /// Returns the current version of the aggregate. @@ -124,6 +134,36 @@ pub trait Aggregate: Storable + 'static { } } } + + /// Process events before they are saved. + /// + /// This method is called on the updated aggregate, i.e., the events + /// given by `events` have already been applied to it. + /// + /// The method is allowed to return an error, in which case all the + /// changes made by `events` are rolled back to the previous version of + /// the aggregate. + /// + /// The default implementation of this method does nothing and returns + /// `Ok(())`. + fn pre_save_events( + &self, events: &[Self::Event], context: Self::Context<'_> + ) -> Result<(), Self::Error> { + let _ = (events, context); + Ok(()) + } + + /// Process events after they have been saved. + /// + /// This method is called on the updated aggregate, i.e., the events + /// given by `events` have already been applied to it. + /// + /// The default implementation does nothing. + fn post_save_events( + &self, events: &[Self::Event], context: Self::Context<'_> + ) { + let _ = (events, context); + } } @@ -679,26 +719,6 @@ impl StoredEffect { } -//------------ PreSaveEventListener ------------------------------------------ - -/// A listener that receives events before the aggregate is saved. -/// -/// The listener is allowed to return an error in case of issues, which will -/// will result in rolling back the intended change to an aggregate. -pub trait PreSaveEventListener: Send + Sync + 'static { - fn listen(&self, agg: &A, events: &[A::Event]) -> Result<(), A::Error>; -} - -//------------ PostSaveEventListener ----------------------------------------- - -/// A listener that receives events after the aggregate is saved. -/// -/// The listener is not allowed to fail. -pub trait PostSaveEventListener: Send + Sync + 'static { - fn listen(&self, agg: &A, events: &[A::Event]); -} - - //------------ Helper Functions ---------------------------------------------- /// Unfailably creates a JSON value from a serializable object. diff --git a/src/commons/eventsourcing/mod.rs b/src/commons/eventsourcing/mod.rs index 05c6eb5fa..b865c14e3 100644 --- a/src/commons/eventsourcing/mod.rs +++ b/src/commons/eventsourcing/mod.rs @@ -301,9 +301,8 @@ mod wal; pub use self::agg::{ Aggregate, Command, CommandDetails, Event, InitCommand, - InitCommandDetails, InitEvent, PostSaveEventListener, - PreSaveEventListener, SentCommand, SentInitCommand, StoredCommand, - StoredCommandBuilder, StoredEffect, WithStorableDetails + InitCommandDetails, InitEvent, SentCommand, SentInitCommand, + StoredCommand, StoredCommandBuilder, StoredEffect, WithStorableDetails }; pub use self::store::{AggregateStore, AggregateStoreError, Storable}; pub use self::wal::{ diff --git a/src/commons/eventsourcing/store.rs b/src/commons/eventsourcing/store.rs index 0caf9317a..4e1619bf5 100644 --- a/src/commons/eventsourcing/store.rs +++ b/src/commons/eventsourcing/store.rs @@ -18,10 +18,7 @@ use crate::api::history::{ }; use crate::commons::error::KrillIoError; use crate::commons::storage::{Ident, KeyValueError, KeyValueStore}; -use super::agg::{ - Aggregate, Command, InitCommand, PostSaveEventListener, - PreSaveEventListener, StoredCommand -}; +use super::agg::{Aggregate, Command, InitCommand, StoredCommand}; //------------ Storable ------------------------------------------------------ @@ -61,12 +58,6 @@ pub struct AggregateStore { /// A cache for the command history of an instance. history_cache: Option>>>, - - /// The pre-save listeners. - pre_save_listeners: Vec>>, - - /// The post-save listeners. - post_save_listeners: Vec>>, } /// # Starting up @@ -114,8 +105,6 @@ impl AggregateStore { else { None }, - pre_save_listeners: Vec::new(), - post_save_listeners: Vec::new(), } } @@ -135,23 +124,6 @@ impl AggregateStore { } Ok(()) } - - /// Adds a listener that will receive all events before they are stored. - pub fn add_pre_save_listener>( - &mut self, - sync_listener: Arc, - ) { - self.pre_save_listeners.push(sync_listener); - } - - /// Adds a listener that will receive a reference to all events after they - /// are stored. - pub fn add_post_save_listener>( - &mut self, - listener: Arc, - ) { - self.post_save_listeners.push(listener); - } } /// # Manage Aggregates @@ -217,7 +189,9 @@ impl AggregateStore { } /// Adds a new aggregate instance based on the init command. - pub fn add(&self, cmd: A::InitCommand) -> Result, A::Error> { + pub fn add_with_context( + &self, cmd: A::InitCommand, context: A::Context<'_>, + ) -> Result, A::Error> { let scope = Self::scope_for_agg(cmd.handle()); self.kv.execute(Some(&scope), |kv| { @@ -242,7 +216,7 @@ impl AggregateStore { // XXX cmd needs to be cloned here because of the Fn // closure of execute. - match A::process_init_command(cmd.clone()) { + match A::process_init_command(cmd.clone(), context) { Ok(init_event) => { let aggregate = A::init( cmd.handle(), init_event.clone(), @@ -285,8 +259,10 @@ impl AggregateStore { /// /// On error, it will save the command and the error, then return the /// error. - pub fn command(&self, cmd: A::Command) -> Result, A::Error> { - self.execute_opt_command(cmd.handle(), Some(&cmd), false) + pub fn command_with_context( + &self, cmd: A::Command, context: A::Context<'_> + ) -> Result, A::Error> { + self.execute_opt_command(cmd.handle(), Some((&cmd, context)), false) } /// Get the latest aggregate and optionally apply a command to it. @@ -295,7 +271,7 @@ impl AggregateStore { fn execute_opt_command( &self, handle: &MyHandle, - cmd_opt: Option<&A::Command>, + cmd_opt: Option<(&A::Command, A::Context<'_>)>, save_snapshot: bool, ) -> Result, A::Error> { let scope = Self::scope_for_agg(handle); @@ -401,7 +377,7 @@ impl AggregateStore { // If a command was passed in, try to apply it, and make sure that // it is preserved. - let res = if let Some(cmd) = cmd_opt { + let res = if let Some((cmd, context)) = cmd_opt { let aggregate = Arc::make_mut(&mut agg); let version = aggregate.version(); @@ -438,7 +414,7 @@ impl AggregateStore { std::process::exit(1); } - match aggregate.process_command(cmd.clone()) { + match aggregate.process_command(cmd.clone(), context) { Err(e) => { // Store the processed command with the error. let processed = processed.finish_with_error(&e); @@ -471,17 +447,12 @@ impl AggregateStore { // should inform the pre-save listeners. They may // still generate errors, and if they do, then we // return with an error, without saving. - let mut opt_err: Option = None; + let mut opt_err = None; if let Some(events) = processed.events() { - for pre_save_listener - in &self.pre_save_listeners { - if let Err(e) - = pre_save_listener.as_ref() - .listen(aggregate, events) - { - opt_err = Some(e); - break; - } + if let Err(err) = aggregate.pre_save_events( + events, context + ) { + opt_err = Some(err); } } @@ -500,11 +471,9 @@ impl AggregateStore { // Now send the events to the 'post-save' // listeners. if let Some(events) = processed.events() { - for listener in &self.post_save_listeners { - listener.as_ref().listen( - aggregate, events - ); - } + aggregate.post_save_events( + events, context + ); } Ok(()) @@ -555,6 +524,16 @@ impl AggregateStore { } } +impl<'a, A: Aggregate = ()>> AggregateStore { + pub fn add(&self, cmd: A::InitCommand) -> Result, A::Error> { + self.add_with_context(cmd, ()) + } + + pub fn command(&self, cmd: A::Command) -> Result, A::Error> { + self.command_with_context(cmd, ()) + } +} + //--- Command History diff --git a/src/commons/eventsourcing/test.rs b/src/commons/eventsourcing/test.rs index 4ae69874e..afc196df3 100644 --- a/src/commons/eventsourcing/test.rs +++ b/src/commons/eventsourcing/test.rs @@ -5,7 +5,7 @@ use std::fmt; use std::str::FromStr; -use std::sync::{Arc, RwLock}; +use std::sync::{RwLock}; use serde::{Deserialize, Serialize}; use rpki::ca::idexchange::MyHandle; use crate::api::history::{CommandHistoryCriteria, CommandSummary}; @@ -302,6 +302,8 @@ impl Aggregate for Person { type Error = PersonError; + type Context<'a> = &'a EventCounter; + fn init(id: &MyHandle, event: PersonInitEvent) -> Self { Person { id: id.clone(), @@ -313,6 +315,7 @@ impl Aggregate for Person { fn process_init_command( command: Self::InitCommand, + _context: Self::Context<'_>, ) -> Result { Ok(PersonInitEvent { name: command.into_details().name, @@ -337,6 +340,7 @@ impl Aggregate for Person { fn process_command( &self, command: Self::Command, + _context: Self::Context<'_>, ) -> Result, Self::Error> { match command.into_details() { PersonCommandDetails::ChangeName(name) => { @@ -353,6 +357,12 @@ impl Aggregate for Person { } } } + + fn post_save_events( + &self, events: &[Self::Event], context: Self::Context<'_> + ) { + context.counter.write().unwrap().total += events.len(); + } } @@ -381,12 +391,6 @@ impl EventCounter { } } -impl PostSaveEventListener for EventCounter { - fn listen(&self, _agg: &A, events: &[A::Event]) { - self.counter.write().unwrap().total += events.len(); - } -} - //------------ Test Function ------------------------------------------------- @@ -394,22 +398,21 @@ impl PostSaveEventListener for EventCounter { fn event_sourcing_framework() { let storage_uri = mem_storage(); - let counter = Arc::new(EventCounter::default()); + let counter = EventCounter::default(); - let mut manager = AggregateStore::::create( + let manager = AggregateStore::::create( &storage_uri, const { Ident::make("person") }, false, ) .unwrap(); - manager.add_post_save_listener(counter.clone()); let alice_name = "alice smith".to_string(); let alice_handle = MyHandle::from_str("alice").unwrap(); let alice_init_cmd = PersonInitCommand::make(alice_handle.clone(), alice_name); - manager.add(alice_init_cmd).unwrap(); + manager.add_with_context(alice_init_cmd, &counter).unwrap(); let mut alice = manager.get_latest(&alice_handle).unwrap(); assert_eq!("alice smith", alice.name()); @@ -420,7 +423,7 @@ fn event_sourcing_framework() { let get_older = PersonCommand::go_around_sun( alice_handle.clone(), None ); - alice = manager.command(get_older).unwrap(); + alice = manager.command_with_context(get_older, &counter).unwrap(); age += 1; if age == 21 { @@ -436,7 +439,7 @@ fn event_sourcing_framework() { Some(22), "alice smith-doe", ); - let alice = manager.command(change_name).unwrap(); + let alice = manager.command_with_context(change_name, &counter).unwrap(); assert_eq!("alice smith-doe", alice.name()); assert_eq!(21, alice.age()); diff --git a/src/server/ca/certauth.rs b/src/server/ca/certauth.rs index b2305c1b1..e1198d1f3 100644 --- a/src/server/ca/certauth.rs +++ b/src/server/ca/certauth.rs @@ -2,8 +2,6 @@ use std::vec; use std::collections::HashMap; -use std::ops::Deref; -use std::sync::Arc; use bytes::Bytes; use chrono::Duration; use log::{debug, info, trace, warn}; @@ -48,6 +46,7 @@ use crate::commons::error::Error; use crate::commons::eventsourcing::Aggregate; use crate::constants::test_mode_enabled; use crate::config::{Config, IssuanceTimingConfig}; +use crate::server::runtime::KrillRuntime; use super::aspa::AspaDefinitions; use super::bgpsec::BgpSecDefinitions; use super::child::{ChildDetails, ChildCertificateUpdates, UsedKeyState}; @@ -132,6 +131,8 @@ impl Aggregate for CertAuth { type Error = Error; + type Context<'a> = &'a KrillRuntime; + fn init(handle: &MyHandle, event: CertAuthInitEvent) -> Self { CertAuth { handle: handle.clone(), @@ -155,11 +156,10 @@ impl Aggregate for CertAuth { } fn process_init_command( - command: CertAuthInitCommand, + _command: CertAuthInitCommand, + krill: &KrillRuntime, ) -> Result { - Rfc8183Id::generate( - &command.details().signer - ).map(|id| CertAuthInitEvent { id }) + Rfc8183Id::generate(krill.signer()).map(|id| CertAuthInitEvent { id }) } fn version(&self) -> u64 { @@ -173,6 +173,7 @@ impl Aggregate for CertAuth { fn process_command( &self, command: CertAuthCommand, + krill: &KrillRuntime, ) -> Result, Error> { trace!( "Sending command to CA '{}', version: {}: {}", @@ -187,9 +188,11 @@ impl Aggregate for CertAuth { } CertAuthCommandDetails::ChildImport( - import_child, config, signer, + import_child ) => { - self.process_child_import(import_child, &config, signer) + self.process_child_import( + import_child, krill.config(), krill.signer(), + ) } CertAuthCommandDetails::ChildUpdateResources(child, res) => { @@ -207,9 +210,11 @@ impl Aggregate for CertAuth { } CertAuthCommandDetails::ChildCertify( - child, request, config, signer, + child, request, ) => { - self.process_child_certify( child, request, &config, signer) + self.process_child_certify( + child, request, krill.config(), krill.signer() + ) } CertAuthCommandDetails::ChildRevokeKey(child, request) => { @@ -231,8 +236,8 @@ impl Aggregate for CertAuth { // Parent commands - CertAuthCommandDetails::GenerateNewIdKey(signer) => { - self.process_generate_new_id_key(signer) + CertAuthCommandDetails::GenerateNewIdKey => { + self.process_generate_new_id_key(krill.signer()) } CertAuthCommandDetails::AddParent(parent, info) => { @@ -248,35 +253,33 @@ impl Aggregate for CertAuth { } CertAuthCommandDetails::UpdateEntitlements( - parent, entitlements, signer, + parent, entitlements ) => { - self.process_update_entitlements(parent, entitlements, signer) + self.process_update_entitlements( + parent, entitlements, krill.signer(), + ) } - CertAuthCommandDetails::UpdateRcvdCert( - class_name, rcvd_cert, config, signer, - ) => { + CertAuthCommandDetails::UpdateRcvdCert(class_name, rcvd_cert) => { self.process_update_received_cert( - class_name, rcvd_cert, &config, &signer + class_name, rcvd_cert, krill.config(), krill.signer() ) } - CertAuthCommandDetails::DropResourceClass( - rcn, reason, signer, - ) => { - self.process_drop_resource_class(rcn, reason, signer) + CertAuthCommandDetails::DropResourceClass(rcn, reason) => { + self.process_drop_resource_class(rcn, reason, krill.signer()) } // Key rolls - CertAuthCommandDetails::KeyRollInitiate(duration, signer) => { - self.process_keyroll_initiate(duration, signer) + CertAuthCommandDetails::KeyRollInitiate(duration) => { + self.process_keyroll_initiate(duration, krill.signer()) } - CertAuthCommandDetails::KeyRollActivate( - duration, config, signer, - ) => { - self.process_keyroll_activate(duration, config, signer) + CertAuthCommandDetails::KeyRollActivate(duration) => { + self.process_keyroll_activate( + duration, krill.config(), krill.signer() + ) } CertAuthCommandDetails::KeyRollFinish(rcn, response) => { @@ -285,83 +288,73 @@ impl Aggregate for CertAuth { // Publishing - CertAuthCommandDetails::RepoUpdate(contact, signer) => { - self.process_update_repo(contact, &signer) + CertAuthCommandDetails::RepoUpdate(contact) => { + self.process_update_repo(contact, krill.signer()) } // ROAs - CertAuthCommandDetails::RouteAuthorizationsUpdate( - updates, config, signer, - ) => { + CertAuthCommandDetails::RouteAuthorizationsUpdate(updates) => { self.process_route_authorizations_update( - updates, &config, &signer + updates, krill.config(), krill.signer() ) } - CertAuthCommandDetails::RouteAuthorizationsRenew( - config, signer, - ) => { + CertAuthCommandDetails::RouteAuthorizationsRenew => { self.process_route_authorizations_renew( - false, &config, &signer + false, krill.config(), krill.signer() ) } - CertAuthCommandDetails::RouteAuthorizationsForceRenew( - config, signer, - ) => { + CertAuthCommandDetails::RouteAuthorizationsForceRenew=> { self.process_route_authorizations_renew( - true, &config, &signer + true, krill.config(), krill.signer() ) } // ASPA - CertAuthCommandDetails::AspasUpdate(updates, config, signer) => { + CertAuthCommandDetails::AspasUpdate(updates) => { self.process_aspas_update( - updates, &config, &signer + updates, krill.config(), krill.signer() ) } CertAuthCommandDetails::AspasUpdateExisting( - customer, update, config, signer, + customer, update, ) => { self.process_aspas_update_existing( - customer, update, &config, &signer + customer, update, krill.config(), krill.signer(), ) } - CertAuthCommandDetails::AspasRenew(config, signer) => { - self.process_aspas_renew(&config, &signer) + CertAuthCommandDetails::AspasRenew => { + self.process_aspas_renew(krill.config(), krill.signer()) } // BGPsec router keys - CertAuthCommandDetails::BgpSecUpdateDefinitions( - updates, config, signer, - ) => { + CertAuthCommandDetails::BgpSecUpdateDefinitions(updates) => { self.process_bgpsec_definitions_update( - updates, &config, &signer + updates, krill.config(), krill.signer(), ) } - CertAuthCommandDetails::BgpSecRenew(config, signer) => { - self.process_bgpsec_renew(&config, &signer) + CertAuthCommandDetails::BgpSecRenew => { + self.process_bgpsec_renew(krill.config(), krill.signer()) } // RTA - CertAuthCommandDetails::RtaMultiPrepare( - name, request, signer, - ) => { - self.process_rta_multi_prep(name, request, &signer) + CertAuthCommandDetails::RtaMultiPrepare(name, request) => { + self.process_rta_multi_prep(name, request, krill.signer()) } - CertAuthCommandDetails::RtaCoSign(name, rta, signer) => { - self.process_rta_cosign(name, rta, signer.deref()) + CertAuthCommandDetails::RtaCoSign(name, rta) => { + self.process_rta_cosign(name, rta, krill.signer()) } - CertAuthCommandDetails::RtaSign(name, request, signer) => { - self.process_rta_sign(name, request, signer.deref()) + CertAuthCommandDetails::RtaSign(name, request) => { + self.process_rta_sign(name, request, krill.signer()) } } } @@ -676,6 +669,47 @@ impl Aggregate for CertAuth { } } } + + fn pre_save_events( + &self, events: &[Self::Event], krill: &KrillRuntime, + ) -> Result<(), Self::Error> { + // Let the object store update its ROAs and issued + // certificates and/or generate manifests and CRLs when relevant + // changes occur in a `CertAuth`. + krill.ca_manager().ca_objects_store().cert_auth_pre_save_events( + self, events + )?; + + // Let the [`TaskQueue`] handle events pre-save so + // that relevant changes in a `CertAuth` can trigger follow-up + // actions. This is done as pre-save listener, because commands + // that would result in a follow-up should fail, if the task cannot be + // planned. + // + // Tasks will typically be picked up after the CA changes are + // committed, but they may also be picked up sooner by another + // thread. Because of that the tasks will remember which minimal + // version of the CA they are intended for, so that they can + // be rescheduled should they have been picked up too soon. + // + // An example of a triggered task: schedule a synchronisation with the + // repository (publication server) in case ROAs have been + // updated. + krill.tasks().cert_auth_pre_save_events(self, events)?; + + Ok(()) + } + + fn post_save_events( + &self, events: &[Self::Event], krill: &KrillRuntime, + ) { + // Also let the [`TaskQueue`] handle events post-save. We + // use this to send best-effort post-save signals to children + // in case a certificate was updated or a child key was revoked. + // This is a no-op for remote children (we cannot send a signal over + // RFC 6492). + krill.tasks().cert_auth_post_save_events(self, events); + } } /// # Data presentation @@ -1101,7 +1135,7 @@ impl CertAuth { &self, import_child: ImportChild, config: &Config, - signer: Arc, + signer: &KrillSigner, ) -> KrillResult> { // overview: // - perform checks (e.g. not supported in case we have multiple RCs) @@ -1303,7 +1337,7 @@ impl CertAuth { child_handle: ChildHandle, request: IssuanceRequest, config: &Config, - signer: Arc, + signer: &KrillSigner, ) -> KrillResult> { let (child_rcn, limit, csr) = request.unpack(); @@ -1335,7 +1369,7 @@ impl CertAuth { csr_info: CsrInfo, limit: RequestResourceLimit, config: &Config, - signer: Arc, + signer: &KrillSigner, events: &mut Vec, ) -> KrillResult<()> { if !csr_info.global_uris() && !test_mode_enabled() { @@ -1716,7 +1750,7 @@ impl CertAuth { /// Processes the “generate new ID key” command. fn process_generate_new_id_key( &self, - signer: Arc, + signer: &KrillSigner, ) -> KrillResult> { let id = Rfc8183Id::generate(&signer)?; @@ -1831,7 +1865,7 @@ impl CertAuth { &self, parent_handle: ParentHandle, entitlements: ResourceClassListResponse, - signer: Arc, + signer: &KrillSigner, ) -> KrillResult> { let mut res = Vec::new(); @@ -1856,7 +1890,7 @@ impl CertAuth { && !entitled_classes.contains(&class.parent_rc_name()) }) { - let revoke_requests = rc.revoke(signer.deref())?; + let revoke_requests = rc.revoke(signer)?; info!( "Updating Entitlements for CA: {}, Removing RC: {}", @@ -1996,7 +2030,7 @@ impl CertAuth { &self, rcn: ResourceClassName, reason: DropReason, - signer: Arc, + signer: &KrillSigner, ) -> KrillResult> { warn!( "Dropping resource class '{rcn}' because of reason: {reason}" @@ -2005,7 +2039,7 @@ impl CertAuth { Error::ResourceClassUnknown(rcn.clone()) })?; - rc.revoke(signer.deref()).map(|revoke_requests| { + rc.revoke(signer).map(|revoke_requests| { vec![CertAuthEvent::ResourceClassRemoved { resource_class_name: rcn, parent: rc.parent_handle().clone(), @@ -2021,14 +2055,14 @@ impl CertAuth { fn process_keyroll_initiate( &self, duration: Duration, - signer: Arc, + signer: &KrillSigner, ) -> KrillResult> { let mut res = Vec::new(); for (rcn, rc) in self.resources.iter() { let repo = self.repository_contact()?; if rc.append_keyroll_initiate( - &repo.repo_info, duration, &signer, &mut res + &repo.repo_info, duration, signer, &mut res )? { info!( "Started key roll for ca: {}, rc: {}, under parent: {}", @@ -2046,14 +2080,14 @@ impl CertAuth { fn process_keyroll_activate( &self, staging_time: Duration, - config: Arc, - signer: Arc, + config: &Config, + signer: &KrillSigner, ) -> KrillResult> { let mut res = vec![]; for (rcn, rc) in self.resources.iter() { if rc.append_keyroll_activate( - staging_time, &config.issuance_timing, &signer, &mut res + staging_time, &config.issuance_timing, signer, &mut res )? { info!( "Activated key for ca: {}, rc: {}, under parent: {}", diff --git a/src/server/ca/commands.rs b/src/server/ca/commands.rs index f635cfc7a..702c14aaf 100644 --- a/src/server/ca/commands.rs +++ b/src/server/ca/commands.rs @@ -1,7 +1,6 @@ //! The commands issued to an RPKI CA. use std::fmt; -use std::sync::Arc; use chrono::Duration; use rpki::ca::idexchange::{ChildHandle, ParentHandle, ServiceUri}; use rpki::ca::provisioning::{ @@ -29,12 +28,10 @@ use crate::api::roa::RoaConfigurationUpdates; use crate::api::rta::{ ResourceTaggedAttestation, RtaContentRequest, RtaPrepareRequest, }; -use crate::commons::crypto::KrillSigner; use crate::commons::eventsourcing::{ self, InitCommandDetails, SentCommand, SentInitCommand, WithStorableDetails, }; -use crate::config::Config; use super::events::CertAuthEvent; use super::rc::DropReason; @@ -48,10 +45,7 @@ pub type CertAuthInitCommand = SentInitCommand; /// The details for the init command for a `CertAuth` instance. #[derive(Clone, Debug)] -pub struct CertAuthInitCommandDetails { - /// The signer to use for initializing the CA. - pub signer: Arc, -} +pub struct CertAuthInitCommandDetails; impl InitCommandDetails for CertAuthInitCommandDetails { type StorableDetails = CertAuthStorableCommand; @@ -85,7 +79,7 @@ pub enum CertAuthCommandDetails { ChildAdd(ChildHandle, IdCertInfo, ResourceSet), /// Import a child under this parent CA - ChildImport(ImportChild, Arc, Arc), + ChildImport(ImportChild), /// Update the resource entitlements for an existing child. ChildUpdateResources(ChildHandle, ResourceSet), @@ -100,7 +94,7 @@ pub enum CertAuthCommandDetails { ), /// Process an issuance request sent by an existing child. - ChildCertify(ChildHandle, IssuanceRequest, Arc, Arc), + ChildCertify(ChildHandle, IssuanceRequest), /// Process a revoke request by an existing child. ChildRevokeKey(ChildHandle, RevocationRequest), @@ -139,7 +133,7 @@ pub enum CertAuthCommandDetails { /// this ID for parents, and children. In practice however, one may not /// want to use this until RFC8183 is extended with some words/ on how /// to re-do the ID exchange. - GenerateNewIdKey(Arc), + GenerateNewIdKey, /// Add a parent to this CA. /// @@ -158,20 +152,18 @@ pub enum CertAuthCommandDetails { /// /// Remove/create/update resource classes and certificate requests or key /// revocation requests as needed. - UpdateEntitlements(ParentHandle, Entitlements, Arc), + UpdateEntitlements(ParentHandle, Entitlements), /// Process a new certificate received from a parent. UpdateRcvdCert( ResourceClassName, ReceivedCert, - Arc, - Arc, ), /// Drop a resource class under a parent. /// /// This is usually done because of issues obtaining a certificate for it. - DropResourceClass(ResourceClassName, DropReason, Arc), + DropResourceClass(ResourceClassName, DropReason), //--- Key rolls @@ -180,7 +172,7 @@ pub enum CertAuthCommandDetails { /// A key roll is only initiated for resource classes where there is a /// current active key only, i.e. there is no roll in progress, and this /// key's age exceeds the given duration. - KeyRollInitiate(Duration, Arc), + KeyRollInitiate(Duration), /// Activate a rolled key. /// @@ -196,7 +188,7 @@ pub enum CertAuthCommandDetails { /// RFC6489 dictates that 24 hours must be observed. However, shorter /// time frames can be used for testing, and in case of emergency /// rolls. - KeyRollActivate(Duration, Arc, Arc), + KeyRollActivate(Duration), /// Finish the keyroll. /// @@ -212,11 +204,7 @@ pub enum CertAuthCommandDetails { /// Note: ROA *objects* will be created by the CA itself. The command /// just contains the intent for which announcements should be /// authorized. - RouteAuthorizationsUpdate( - RoaConfigurationUpdates, - Arc, - Arc, - ), + RouteAuthorizationsUpdate(RoaConfigurationUpdates), /// Re-issue all ROA objects which would otherwise expire soon. /// @@ -224,23 +212,18 @@ pub enum CertAuthCommandDetails { /// Note that this command is intended to be sent by the scheduler - /// once a day is fine - and will only be stored if there are any /// updates to be done. - RouteAuthorizationsRenew(Arc, Arc), + RouteAuthorizationsRenew, /// Re-issue all ROA objects regardless of their expiration time. - RouteAuthorizationsForceRenew(Arc, Arc), + RouteAuthorizationsForceRenew, //--- ASPA /// Update ASPA definitions - AspasUpdate(AspaDefinitionUpdates, Arc, Arc), + AspasUpdate(AspaDefinitionUpdates), /// Update an existing AspaProviders for the given AspaCustomer - AspasUpdateExisting( - CustomerAsn, - AspaProvidersUpdate, - Arc, - Arc, - ), + AspasUpdateExisting(CustomerAsn, AspaProvidersUpdate), /// Re-issue any and all ASPA objects which would otherwise expire soon. /// @@ -248,38 +231,34 @@ pub enum CertAuthCommandDetails { /// /// This command is intended to be sent by the scheduler – once a day is /// fine – and will only be stored if there are any updates to be done. - AspasRenew(Arc, Arc), + AspasRenew, //--- BGPsec router keys /// Update BgpSecDefinitions - BgpSecUpdateDefinitions( - BgpSecDefinitionUpdates, - Arc, - Arc, - ), + BgpSecUpdateDefinitions(BgpSecDefinitionUpdates), /// Re-issue any and all BGPsec certificates which are soon to expire. - BgpSecRenew(Arc, Arc), + BgpSecRenew, //--- Publishing // Update the repository where this CA publishes. - RepoUpdate(RepositoryContact, Arc), + RepoUpdate(RepositoryContact), //--- RTA /// Sign a new RTA - RtaSign(RtaName, RtaContentRequest, Arc), + RtaSign(RtaName, RtaContentRequest), /// Prepare a multi-signed RTA - RtaMultiPrepare(RtaName, RtaPrepareRequest, Arc), + RtaMultiPrepare(RtaName, RtaPrepareRequest), /// Co-sign an existing multi-signed RTA - RtaCoSign(RtaName, ResourceTaggedAttestation, Arc), + RtaCoSign(RtaName, ResourceTaggedAttestation), } impl eventsourcing::CommandDetails for CertAuthCommandDetails { @@ -422,7 +401,7 @@ impl From for CertAuthStorableCommand { resources, } } - CertAuthCommandDetails::ChildImport(import_child, _, _) => { + CertAuthCommandDetails::ChildImport(import_child) => { CertAuthStorableCommand::ChildImport { child: import_child.name, ski: import_child @@ -455,7 +434,7 @@ impl From for CertAuthStorableCommand { mapping, } } - CertAuthCommandDetails::ChildCertify(child, req, _, _) => { + CertAuthCommandDetails::ChildCertify(child, req) => { let (resource_class_name, limit, csr) = req.unpack(); let ki = csr.public_key().key_identifier(); CertAuthStorableCommand::ChildCertify { @@ -477,7 +456,7 @@ impl From for CertAuthStorableCommand { CertAuthCommandDetails::ChildUnsuspend(child) => { CertAuthStorableCommand::ChildUnsuspend { child } } - CertAuthCommandDetails::GenerateNewIdKey(_) => { + CertAuthCommandDetails::GenerateNewIdKey => { CertAuthStorableCommand::GenerateNewIdKey } CertAuthCommandDetails::AddParent(parent, contact) => { @@ -496,9 +475,7 @@ impl From for CertAuthStorableCommand { CertAuthStorableCommand::RemoveParent { parent } } CertAuthCommandDetails::UpdateEntitlements( - parent, - cmd_entitlements, - _, + parent, cmd_entitlements, ) => { let mut entitlements = vec![]; for entitlement in cmd_entitlements.classes() { @@ -514,28 +491,23 @@ impl From for CertAuthStorableCommand { } } CertAuthCommandDetails::UpdateRcvdCert( - resource_class_name, - rcvd_cert, - _, - _, + resource_class_name, rcvd_cert, ) => CertAuthStorableCommand::UpdateRcvdCert { resource_class_name, resources: rcvd_cert.resources.clone(), }, CertAuthCommandDetails::DropResourceClass( - resource_class_name, - reason, - _, + resource_class_name, reason, ) => CertAuthStorableCommand::DropResourceClass { resource_class_name, reason, }, - CertAuthCommandDetails::KeyRollInitiate(older_than, _) => { + CertAuthCommandDetails::KeyRollInitiate(older_than) => { CertAuthStorableCommand::KeyRollInitiate { older_than_seconds: older_than.num_seconds(), } } - CertAuthCommandDetails::KeyRollActivate(staged_for, _, _) => { + CertAuthCommandDetails::KeyRollActivate(staged_for) => { CertAuthStorableCommand::KeyRollActivate { staged_for_seconds: staged_for.num_seconds(), } @@ -544,51 +516,46 @@ impl From for CertAuthStorableCommand { CertAuthStorableCommand::KeyRollFinish { resource_class_name, } + } CertAuthCommandDetails::RouteAuthorizationsUpdate(updates) => { + CertAuthStorableCommand::RoaDefinitionUpdates { updates } } - CertAuthCommandDetails::RouteAuthorizationsUpdate( - updates, - _, - _, - ) => CertAuthStorableCommand::RoaDefinitionUpdates { updates }, - CertAuthCommandDetails::RouteAuthorizationsRenew(_, _) => { + CertAuthCommandDetails::RouteAuthorizationsRenew => { CertAuthStorableCommand::ReissueBeforeExpiring } - CertAuthCommandDetails::RouteAuthorizationsForceRenew(_, _) => { + CertAuthCommandDetails::RouteAuthorizationsForceRenew => { CertAuthStorableCommand::ForceReissue } - CertAuthCommandDetails::AspasUpdate(updates, _, _) => { + CertAuthCommandDetails::AspasUpdate(updates) => { CertAuthStorableCommand::AspasUpdate { updates } } CertAuthCommandDetails::AspasUpdateExisting( - customer, - update, - _, - _, - ) => CertAuthStorableCommand::AspasUpdateExisting { - customer, - update, - }, - CertAuthCommandDetails::AspasRenew(_, _) => { + customer, update, + ) => { + CertAuthStorableCommand::AspasUpdateExisting { + customer, update + } + } + CertAuthCommandDetails::AspasRenew => { CertAuthStorableCommand::ReissueBeforeExpiring } - CertAuthCommandDetails::BgpSecUpdateDefinitions(_, _, _) => { + CertAuthCommandDetails::BgpSecUpdateDefinitions(_) => { CertAuthStorableCommand::BgpSecDefinitionUpdates } - CertAuthCommandDetails::BgpSecRenew(_, _) => { + CertAuthCommandDetails::BgpSecRenew => { CertAuthStorableCommand::ReissueBeforeExpiring } - CertAuthCommandDetails::RepoUpdate(contact, _) => { + CertAuthCommandDetails::RepoUpdate(contact) => { CertAuthStorableCommand::RepoUpdate { service_uri: contact.server_info.service_uri.clone(), } } - CertAuthCommandDetails::RtaMultiPrepare(name, _, _) => { + CertAuthCommandDetails::RtaMultiPrepare(name, _) => { CertAuthStorableCommand::RtaPrepare { name } } - CertAuthCommandDetails::RtaSign(name, _, _) => { + CertAuthCommandDetails::RtaSign(name, _) => { CertAuthStorableCommand::RtaSign { name } } - CertAuthCommandDetails::RtaCoSign(name, _, _) => { + CertAuthCommandDetails::RtaCoSign(name, _) => { CertAuthStorableCommand::RtaCoSign { name } } } diff --git a/src/server/ca/manager.rs b/src/server/ca/manager.rs index 61e6243dc..4d8f5242d 100644 --- a/src/server/ca/manager.rs +++ b/src/server/ca/manager.rs @@ -62,6 +62,7 @@ use crate::daemon::http::auth::{AuthInfo, Permission}; // XXX remove use crate::config::Config; use crate::server::mq::{now, Task, TaskQueue}; use crate::server::pubd::RepositoryManager; +use crate::server::runtime::KrillRuntime; use crate::server::taproxy::{ TrustAnchorProxy, TrustAnchorProxyCommand, TrustAnchorProxyInitCommand, }; @@ -144,7 +145,7 @@ impl CaManager { ) -> KrillResult { // Create the AggregateStore for the event-sourced `CertAuth` // structures that handle most CA functions. - let mut ca_store = AggregateStore::::create( + let ca_store = AggregateStore::::create( &config.storage_uri, CASERVER_NS, config.use_history_cache, @@ -181,56 +182,13 @@ impl CaManager { signer.clone(), )?); - // Register the `CaObjectsStore` as a pre-save listener to the - // 'ca_store' so that it can update its ROAs and issued - // certificates and/or generate manifests and CRLs when relevant - // changes occur in a `CertAuth`. - ca_store.add_pre_save_listener(ca_objects_store.clone()); - - // Register the `MessageQueue` as a pre-save listener to 'ca_store' so - // that relevant changes in a `CertAuth` can trigger follow-up - // actions. This is done as pre-save listener, because commands - // that would result in a follow-up should fail, if the task cannot be - // planned. - // - // Tasks will typically be picked up after the CA changes are - // committed, but they may also be picked up sooner by another - // thread. Because of that the tasks will remember which minimal - // version of the CA they are intended for, so that they can - // be rescheduled should they have been picked up too soon. - // - // An example of a triggered task: schedule a synchronisation with the - // repository (publication server) in case ROAs have been - // updated. - ca_store.add_pre_save_listener(tasks.clone()); - - // Now also register the `MessageQueue` as a post-save listener. We - // use this to send best-effort post-save signals to children - // in case a certificate was updated or a child key was revoked. - // This is a no-op for remote children (we cannot send a signal over - // RFC 6492). - ca_store.add_post_save_listener(tasks.clone()); - // Create TA proxy store if we need it. let ta_proxy_store = if config.ta_proxy_enabled() { - let mut store = AggregateStore::::create( + Some(AggregateStore::::create( &config.storage_uri, TA_PROXY_SERVER_NS, config.use_history_cache, - )?; - - // We need a pre-save listener so that we can schedule: - // - publication on updates - // - signing by the Trust Anchor Signer when there are requests - // [in testbed mode] - store.add_pre_save_listener(tasks.clone()); - - // We need a post-save listener so that we can schedule: - // - re-sync for local children when the proxy has new responses - // AND is saved - store.add_post_save_listener(tasks.clone()); - - Some(store) + )?) } else { None @@ -282,8 +240,12 @@ impl CaManager { handle: CaHandle, actor: &Actor, command: CertAuthCommandDetails, + krill: &KrillRuntime, ) -> Result, KrillError> { - self.ca_store.command(SentCommand::new(handle, None, command, actor)) + self.ca_store.command_with_context( + SentCommand::new(handle, None, command, actor), + krill + ) } /// Republish the embedded TA and CAs if needed. @@ -320,6 +282,15 @@ impl CaManager { } } +/// # Private access to components +/// +impl CaManager { + pub(super) fn ca_objects_store(&self) -> &CaObjectsStore { + &self.ca_objects_store + } +} + + /// # Trust Anchor Support /// impl CaManager { @@ -329,10 +300,11 @@ impl CaManager { fn send_ta_proxy_command( &self, cmd: TrustAnchorProxyCommand, + krill: &KrillRuntime, ) -> KrillResult> { self.ta_proxy_store.as_ref().ok_or_else(|| { Error::custom("ta_support_enabled is false") - })?.command(cmd) + })?.command_with_context(cmd, krill.into()) } /// Sends a command to the TA signer. @@ -341,10 +313,11 @@ impl CaManager { fn send_ta_signer_command( &self, cmd: TrustAnchorSignerCommand, + krill: &KrillRuntime, ) -> KrillResult> { self.ta_signer_store.as_ref().ok_or_else(|| { Error::custom("ta_signer_enabled is false") - })?.command(cmd) + })?.command_with_context(cmd, krill.into()) } /// Returns the TA proxy. @@ -375,7 +348,9 @@ impl CaManager { /// /// Returns an error if TA proxy support is not enabled or the proxy is /// alreay initialized. - pub fn ta_proxy_init(&self) -> KrillResult<()> { + pub fn ta_proxy_init( + &self, krill: &KrillRuntime + ) -> KrillResult<()> { let ta_handle = ta_handle(); let ta_proxy_store = self.ta_proxy_store.as_ref().ok_or_else(|| { @@ -386,12 +361,12 @@ impl CaManager { return Err(Error::TaAlreadyInitialized) } - ta_proxy_store.add( + ta_proxy_store.add_with_context( TrustAnchorProxyInitCommand::make( ta_handle, - self.signer.clone(), &self.system_actor, - ) + ), + krill.into(), )?; Ok(()) } @@ -405,6 +380,7 @@ impl CaManager { tal_https: Vec, tal_rsync: uri::Rsync, private_key_pem: Option, + krill: &KrillRuntime, ) -> KrillResult<()> { let handle = ta_handle(); @@ -427,8 +403,6 @@ impl CaManager { tal_rsync, private_key_pem, ta_mft_nr_override: None, - timing: self.config.ta_timing, - signer: self.signer.clone(), }; let cmd = TrustAnchorSignerInitCommand::new( handle, @@ -436,7 +410,7 @@ impl CaManager { &self.system_actor, ); - ta_signer_store.add(cmd)?; + ta_signer_store.add_with_context(cmd, krill.into())?; Ok(()) } @@ -463,13 +437,15 @@ impl CaManager { &self, contact: RepositoryContact, actor: &Actor, + krill: &KrillRuntime, ) -> KrillResult<()> { self.send_ta_proxy_command( TrustAnchorProxyCommand::add_repo( &ta_handle(), contact, actor, - ) + ), + krill, )?; Ok(()) } @@ -493,9 +469,11 @@ impl CaManager { &self, info: TrustAnchorSignerInfo, actor: &Actor, + krill: &KrillRuntime, ) -> KrillResult<()> { self.send_ta_proxy_command( - TrustAnchorProxyCommand::add_signer(&ta_handle(), info, actor) + TrustAnchorProxyCommand::add_signer(&ta_handle(), info, actor), + krill )?; Ok(()) } @@ -507,9 +485,11 @@ impl CaManager { &self, info: TrustAnchorSignerInfo, actor: &Actor, + krill: &KrillRuntime, ) -> KrillResult<()> { self.send_ta_proxy_command( - TrustAnchorProxyCommand::update_signer(&ta_handle(), info, actor) + TrustAnchorProxyCommand::update_signer(&ta_handle(), info, actor), + krill )?; Ok(()) } @@ -520,9 +500,11 @@ impl CaManager { pub fn ta_proxy_signer_make_request( &self, actor: &Actor, + krill: &KrillRuntime, ) -> KrillResult { self.send_ta_proxy_command( - TrustAnchorProxyCommand::make_signer_request(&ta_handle(), actor) + TrustAnchorProxyCommand::make_signer_request(&ta_handle(), actor), + krill )?.get_signer_request(self.config.ta_timing, &self.signer) } @@ -540,13 +522,15 @@ impl CaManager { &self, response: TrustAnchorSignedResponse, actor: &Actor, + krill: &KrillRuntime, ) -> KrillResult<()> { self.send_ta_proxy_command( TrustAnchorProxyCommand::process_signer_response( &ta_handle(), response, actor, - ) + ), + krill )?; Ok(()) } @@ -559,11 +543,12 @@ impl CaManager { ta_key_pem: Option, repo_manager: &Arc, actor: &Actor, + krill: &KrillRuntime, ) -> KrillResult<()> { let ta_handle = ta_handle(); // Initialise proxy - self.ta_proxy_init()?; + self.ta_proxy_init(krill)?; // Add repository let pub_req = self.ta_proxy_publisher_request()?; @@ -577,28 +562,30 @@ impl CaManager { let contact = RepositoryContact::try_from_response( repository_response ).map_err(Error::rfc8183)?; - self.ta_proxy_repository_update(contact, &self.system_actor)?; + self.ta_proxy_repository_update(contact, &self.system_actor, krill)?; // Initialise signer - self.ta_signer_init(ta_uris, ta_aia, ta_key_pem)?; + self.ta_signer_init(ta_uris, ta_aia, ta_key_pem, krill)?; // Add signer to proxy let signer_info = self.get_trust_anchor_signer()?.get_signer_info(); - self.ta_proxy_signer_add(signer_info, &self.system_actor)?; + self.ta_proxy_signer_add(signer_info, &self.system_actor, krill)?; - self.sync_ta_proxy_signer_if_possible()?; + self.sync_ta_proxy_signer_if_possible(krill)?; self.cas_repo_sync_single(repo_manager, &ta_handle, 0).await?; Ok(()) } /// Renews the embedded testbed TA; - pub fn ta_renew_testbed_ta(&self) -> KrillResult<()> { + pub fn ta_renew_testbed_ta( + &self, krill: &KrillRuntime, + ) -> KrillResult<()> { if self.testbed_enabled() { let proxy = self.get_trust_anchor_proxy()?; if !proxy.has_open_request() { info!("Renew the testbed TA"); - self.sync_ta_proxy_signer_if_possible()?; + self.sync_ta_proxy_signer_if_possible(krill)?; } } Ok(()) @@ -609,7 +596,9 @@ impl CaManager { /// impl CaManager { /// Initializes a CA without a repo, no parents, no children, no nothing - pub fn init_ca(&self, handle: CaHandle) -> KrillResult<()> { + pub fn init_ca( + &self, handle: CaHandle, krill: &KrillRuntime, + ) -> KrillResult<()> { if handle == ta_handle() || handle.as_str() == "version" { return Err(Error::TaNameReserved) } @@ -621,12 +610,12 @@ impl CaManager { // need to create a new CA entry in // self.ca_objects_store or self.status_store, because they will // generate empty default entries if needed. - self.ca_store.add( + self.ca_store.add_with_context( CertAuthInitCommand::new( handle, - CertAuthInitCommandDetails { signer: self.signer.clone() }, + CertAuthInitCommandDetails, &self.system_actor, - ) + ), krill )?; Ok(()) } @@ -643,12 +632,12 @@ impl CaManager { &self, handle: CaHandle, actor: &Actor, + krill: &KrillRuntime, ) -> KrillResult<()> { self.process_ca_command( handle, actor, - CertAuthCommandDetails::GenerateNewIdKey( - self.signer.clone(), - ) + CertAuthCommandDetails::GenerateNewIdKey, + krill )?; Ok(()) } @@ -750,6 +739,7 @@ impl CaManager { repo_manager: &RepositoryManager, ca_handle: &CaHandle, actor: &Actor, + krill: &KrillRuntime, ) -> KrillResult<()> { warn!("Deleting CA '{ca_handle}' as requested by: {actor}"); @@ -761,7 +751,7 @@ impl CaManager { before removing it." ); for parent in ca.parents() { - if let Err(e) = self.ca_parent_revoke(ca_handle, parent).await { + if let Err(e) = self.ca_parent_revoke(ca_handle, parent, krill).await { warn!( "Removing CA '{ca_handle}', but could not send revoke request \ to parent '{parent}': {e}" @@ -845,6 +835,7 @@ impl CaManager { req: AddChildRequest, service_uri: &uri::Https, actor: &Actor, + krill: &KrillRuntime, ) -> KrillResult { info!("CA '{}' process add child request: {}", &ca, &req); if ca.as_str() != TA_NAME { @@ -853,7 +844,8 @@ impl CaManager { req.handle.clone(), req.id_cert.into(), req.resources - ) + ), + krill )?; self.ca_parent_response(ca, req.handle, service_uri) } @@ -861,7 +853,7 @@ impl CaManager { let child_handle = req.handle.clone(); let add_child_cmd = TrustAnchorProxyCommand::add_child(ca, req, actor); - self.send_ta_proxy_command(add_child_cmd)?; + self.send_ta_proxy_command(add_child_cmd, krill)?; self.ca_parent_response(ca, child_handle, service_uri) } } @@ -908,14 +900,14 @@ impl CaManager { ca: &CaHandle, import_child: ImportChild, actor: &Actor, + krill: &KrillRuntime, ) -> KrillResult<()> { trace!("Importing CA: {} under parent: {}", import_child.name, ca); self.process_ca_command(ca.clone(), actor, CertAuthCommandDetails::ChildImport( - import_child, - self.config.clone(), - self.signer.clone(), - ) + import_child + ), + krill, )?; Ok(()) } @@ -990,13 +982,15 @@ impl CaManager { child: ChildHandle, req: UpdateChildRequest, actor: &Actor, + krill: &KrillRuntime, ) -> KrillResult<()> { if let Some(id) = req.id_cert { self.process_ca_command(ca.clone(), actor, CertAuthCommandDetails::ChildUpdateId( child.clone(), id.into(), - ) + ), + krill, )?; } if let Some(resources) = req.resources { @@ -1005,6 +999,7 @@ impl CaManager { child.clone(), resources, ), + krill, )?; } if let Some(suspend) = req.suspend { @@ -1013,14 +1008,16 @@ impl CaManager { ca.clone(), actor, CertAuthCommandDetails::ChildSuspendInactive( child.clone() - ) + ), + krill, )?; } else { self.process_ca_command( ca.clone(), actor, CertAuthCommandDetails::ChildUnsuspend( child.clone(), - ) + ), + krill, )?; } } @@ -1028,7 +1025,8 @@ impl CaManager { self.process_ca_command(ca.clone(), actor, CertAuthCommandDetails::ChildUpdateResourceClassNameMapping( child, mapping, - ) + ), + krill, )?; } Ok(()) @@ -1043,10 +1041,13 @@ impl CaManager { ca: &CaHandle, child: ChildHandle, actor: &Actor, + krill: &KrillRuntime, ) -> KrillResult<()> { self.status_store.remove_child(ca, &child)?; - self.process_ca_command(ca.clone(), actor, - CertAuthCommandDetails::ChildRemove(child) + self.process_ca_command( + ca.clone(), actor, + CertAuthCommandDetails::ChildRemove(child), + krill, )?; Ok(()) } @@ -1061,6 +1062,7 @@ impl CaManager { msg_bytes: Bytes, user_agent: Option, actor: &Actor, + krill: &KrillRuntime, ) -> KrillResult { if ca_handle.as_str() == TA_NAME { return Err(Error::custom( @@ -1080,7 +1082,7 @@ impl CaManager { ); match self.rfc6492_process_request( - ca_handle, req_msg, user_agent, actor + ca_handle, req_msg, user_agent, actor, krill ) { Ok(msg) => { let should_log_cms = !msg.is_list_response(); @@ -1111,6 +1113,7 @@ impl CaManager { req_msg: provisioning::Message, user_agent: Option, actor: &Actor, + krill: &KrillRuntime, ) -> KrillResult { let (sender, _recipient, payload) = req_msg.unpack(); @@ -1137,6 +1140,7 @@ impl CaManager { child_handle.clone(), UpdateChildRequest::unsuspend(), actor, + krill, )?; } } @@ -1144,7 +1148,7 @@ impl CaManager { let res_msg = match payload { provisioning::Payload::Revoke(req) => { self.rfc6492_revoke( - ca_handle, child_handle.clone(), req, actor + ca_handle, child_handle.clone(), req, actor, krill, ) } provisioning::Payload::List => { @@ -1152,7 +1156,7 @@ impl CaManager { } provisioning::Payload::Issue(req) => { self.rfc6492_issue( - ca_handle, child_handle.clone(), req, actor + ca_handle, child_handle.clone(), req, actor, krill, ) } _ => Err(Error::custom("Unsupported RFC6492 message")), @@ -1231,6 +1235,7 @@ impl CaManager { child_handle: ChildHandle, issue_req: IssuanceRequest, actor: &Actor, + krill: &KrillRuntime, ) -> KrillResult { if ca_handle.as_str() == TA_NAME { let request = ProvisioningRequest::Issuance(issue_req); @@ -1239,6 +1244,7 @@ impl CaManager { child_handle, request, actor, + krill, ) } else { @@ -1250,9 +1256,8 @@ impl CaManager { CertAuthCommandDetails::ChildCertify( child_handle.clone(), issue_req.clone(), - self.config.clone(), - self.signer.clone(), - ) + ), + krill )?; // The updated CA will now include the newly issued certificate. @@ -1281,10 +1286,13 @@ impl CaManager { child: ChildHandle, revoke_request: RevocationRequest, actor: &Actor, + krill: &KrillRuntime, ) -> KrillResult { if ca_handle.as_str() == TA_NAME { let request = ProvisioningRequest::Revocation(revoke_request); - self.ta_slow_rfc6492_request(ca_handle, child, request, actor) + self.ta_slow_rfc6492_request( + ca_handle, child, request, actor, krill + ) } else { let res = RevocationResponse::from(&revoke_request); @@ -1294,7 +1302,8 @@ impl CaManager { res, ); self.process_ca_command(ca_handle.clone(), actor, - CertAuthCommandDetails::ChildRevokeKey(child, revoke_request) + CertAuthCommandDetails::ChildRevokeKey(child, revoke_request), + krill, )?; Ok(msg) } @@ -1310,6 +1319,7 @@ impl CaManager { child: ChildHandle, request: ProvisioningRequest, actor: &Actor, + krill: &KrillRuntime, ) -> KrillResult { let proxy = self.get_trust_anchor_proxy()?; if let Some(response) = proxy.response_for_child(&child, &request)? { @@ -1326,7 +1336,8 @@ impl CaManager { child, request.key_identifier(), actor, - ) + ), + krill, )?; Ok(response) @@ -1354,7 +1365,8 @@ impl CaManager { child.clone(), request, actor, - ) + ), + krill, )?; provisioning::Message::not_performed_response( @@ -1384,6 +1396,7 @@ impl CaManager { handle: CaHandle, parent_req: ParentCaReq, actor: &Actor, + krill: &KrillRuntime, ) -> KrillResult<()> { let ca = self.get_ca(&handle)?; @@ -1402,7 +1415,7 @@ impl CaManager { parent_req.handle, contact, ) }; - self.process_ca_command(handle.clone(), actor, cmd)?; + self.process_ca_command(handle.clone(), actor, cmd, krill)?; Ok(()) } @@ -1417,10 +1430,11 @@ impl CaManager { handle: CaHandle, parent: ParentHandle, actor: &Actor, + krill: &KrillRuntime, ) -> KrillResult<()> { // Best effort, request revocations for any remaining keys under this // parent. - if let Err(e) = self.ca_parent_revoke(&handle, &parent).await { + if let Err(e) = self.ca_parent_revoke(&handle, &parent, krill).await { warn!( "Removing parent '{parent}' from CA '{handle}', but could not send \ revoke requests: {e}" @@ -1431,6 +1445,7 @@ impl CaManager { self.process_ca_command( handle.clone(), actor, CertAuthCommandDetails::RemoveParent(parent), + krill )?; Ok(()) } @@ -1440,10 +1455,11 @@ impl CaManager { &self, handle: &CaHandle, parent: &ParentHandle, + krill: &KrillRuntime, ) -> KrillResult<()> { let ca = self.get_ca(handle)?; let revoke_requests = ca.revoke_under_parent(parent, &self.signer)?; - self.send_revoke_requests(handle, parent, revoke_requests) + self.send_revoke_requests(handle, parent, revoke_requests, krill) .await?; Ok(()) } @@ -1501,7 +1517,8 @@ impl CaManager { // // XXX PANICS pub fn ca_suspend_inactive_children( - &self, ca_handle: &CaHandle, started: Timestamp, actor: &Actor + &self, ca_handle: &CaHandle, started: Timestamp, actor: &Actor, + krill: &KrillRuntime, ) { // Set threshold hours if it was configured AND this server has been // started longer ago than the hours specified. Otherwise we @@ -1545,7 +1562,7 @@ impl CaManager { let req = UpdateChildRequest::suspend(); if let Err(e) = self.ca_child_update( - ca_handle, child, req, actor + ca_handle, child, req, actor, krill, ) { error!( "Could not suspend inactive child, error: {e}" @@ -1628,6 +1645,7 @@ impl CaManager { min_ca_version: u64, // set this 0 if it does not matter parent: &ParentHandle, actor: &Actor, + krill: &KrillRuntime, ) -> KrillResult { let ca = self.get_ca(handle)?; @@ -1641,10 +1659,12 @@ impl CaManager { } else { if ca.has_pending_requests(parent) { - self.send_requests(handle, parent, actor).await?; + self.send_requests(handle, parent, actor, krill).await?; } else { - self.get_updates_from_parent(handle, parent, actor).await?; + self.get_updates_from_parent( + handle, parent, actor, krill, + ).await?; } Ok(true) } @@ -1655,7 +1675,9 @@ impl CaManager { /// If the TA signer is remote, logs a warning suggesting doing a /// manual synchronization, assuming that this method is only ever called /// if the TA proxy requires synchronization. - pub fn sync_ta_proxy_signer_if_possible(&self) -> KrillResult<()> { + pub fn sync_ta_proxy_signer_if_possible( + &self, krill: &KrillRuntime, + ) -> KrillResult<()> { let ta_handle = ta_handle(); if self.get_trust_anchor_proxy().is_err() { @@ -1679,7 +1701,8 @@ impl CaManager { TrustAnchorProxyCommand::make_signer_request( &ta_handle, &self.system_actor, - ) + ), + krill, )?; // Get sign request for signer. @@ -1696,11 +1719,10 @@ impl CaManager { TrustAnchorSignerCommand::make_process_request_command( &ta_handle, signed_request.into(), - self.config.ta_timing, None, // do not override next manifest number - self.signer.clone(), &self.system_actor, - ) + ), + krill, )?; // Get the response from the signer and give it to the proxy. @@ -1710,7 +1732,8 @@ impl CaManager { &ta_handle, exchange.clone().response, &self.system_actor, - ) + ), + krill, )?; Ok(()) } @@ -1723,6 +1746,7 @@ impl CaManager { handle: &CaHandle, parent: &ParentHandle, actor: &Actor, + krill: &KrillRuntime, ) -> KrillResult<()> { if handle == &ta_handle() { return Ok(()) @@ -1737,11 +1761,11 @@ impl CaManager { let ca = self.get_ca(handle)?; let parent_contact = ca.parent(parent)?; let entitlements = self.get_entitlements_from_contact( - handle, parent, parent_contact, true, + handle, parent, parent_contact, true, krill, ).await?; self.update_entitlements( - handle, parent.clone(), entitlements, actor, + handle, parent.clone(), entitlements, actor, krill, )?; Ok(()) @@ -1753,24 +1777,26 @@ impl CaManager { /// certificate requests. async fn send_requests( &self, handle: &CaHandle, parent: &ParentHandle, actor: &Actor, + krill: &KrillRuntime, ) -> KrillResult<()> { self.send_revoke_requests_handle_responses( - handle, parent, actor + handle, parent, actor, krill, ).await?; self.send_cert_requests_handle_responses( - handle, parent, actor + handle, parent, actor, krill, ).await } /// Sends all open revocation requests and handles the responses. async fn send_revoke_requests_handle_responses( &self, handle: &CaHandle, parent: &ParentHandle, actor: &Actor, + krill: &KrillRuntime, ) -> KrillResult<()> { let child = self.get_ca(handle)?; let requests = child.revoke_requests(parent); let revoke_responses = self.send_revoke_requests( - handle, parent, requests + handle, parent, requests, krill, ).await?; for (rcn, revoke_responses) in revoke_responses { @@ -1780,7 +1806,8 @@ impl CaManager { CertAuthCommandDetails::KeyRollFinish( rcn.clone(), response, - ) + ), + krill, )?; } } @@ -1796,6 +1823,7 @@ impl CaManager { handle: &CaHandle, parent: &ParentHandle, revoke_requests: HashMap>, + krill: &KrillRuntime, ) -> KrillResult>> { let child = self.get_ca(handle)?; let server_info = child.parent(parent)?.parent_server_info(); @@ -1804,6 +1832,7 @@ impl CaManager { revoke_requests, &child.id_cert().public_key.key_identifier(), server_info, + krill, ) .await { Err(e) => { self.status_store.set_parent_failure( @@ -1826,6 +1855,7 @@ impl CaManager { handle: &CaHandle, rcn: ResourceClassName, revocation: RevocationRequest, + krill: &KrillRuntime, ) -> KrillResult>> { let child = self.ca_store.get_latest(handle)?; @@ -1833,7 +1863,7 @@ impl CaManager { let mut requests = HashMap::new(); requests.insert(rcn, vec![revocation]); - self.send_revoke_requests(handle, parent, requests).await + self.send_revoke_requests(handle, parent, requests, krill).await } /// Sends revoke requests using the provisioning protocol. @@ -1842,6 +1872,7 @@ impl CaManager { revoke_requests: HashMap>, signing_key: &KeyIdentifier, server_info: &ParentServerInfo, + krill: &KrillRuntime, ) -> KrillResult>> { let mut revoke_map = HashMap::new(); @@ -1856,7 +1887,7 @@ impl CaManager { ); let response = self.send_rfc6492_and_validate_response( - revoke, server_info, signing_key + revoke, server_info, signing_key, krill ) .await?; let payload = response.into_payload(); @@ -1937,6 +1968,7 @@ impl CaManager { /// Sends certification requests to a parent CA and proceses the response. async fn send_cert_requests_handle_responses( &self, ca_handle: &CaHandle, parent: &ParentHandle, actor: &Actor, + krill: &KrillRuntime, ) -> KrillResult<()> { let ca = self.get_ca(ca_handle)?; let requests = ca.cert_requests(parent); @@ -1967,6 +1999,7 @@ impl CaManager { ), server_info, &signing_key, + krill ).await { Err(e) => { // If any of the requests for an RC results in an @@ -1982,7 +2015,7 @@ impl CaManager { } Ok(response) => { if let Err(err) = self.handle_cert_response( - ca_handle, parent, &rcn, actor, response + ca_handle, parent, &rcn, actor, response, krill, ) { errors.push(err); break; @@ -2020,6 +2053,7 @@ impl CaManager { rcn: &ResourceClassName, actor: &Actor, response: provisioning::Message, + krill: &KrillRuntime, ) -> KrillResult<()> { let payload = response.into_payload(); let payload_type = payload.payload_type(); @@ -2090,11 +2124,9 @@ impl CaManager { if let Err(e) = self.process_ca_command( ca_handle.clone(), actor, CertAuthCommandDetails::UpdateRcvdCert( - rcn.clone(), - rcvd_cert, - self.config.clone(), - self.signer.clone(), - ) + rcn.clone(), rcvd_cert, + ), + krill, ) { // Note that sending the command to update a received // certificate cannot fail unless there are bigger issues @@ -2117,8 +2149,8 @@ impl CaManager { CertAuthCommandDetails::DropResourceClass( rcn.clone(), reason.clone(), - self.signer.clone(), - ) + ), + krill, )?; return Err(Error::CaParentSyncError( @@ -2173,8 +2205,8 @@ impl CaManager { CertAuthCommandDetails::DropResourceClass( rcn.clone(), reason.to_string(), - self.signer.clone(), - ) + ), + krill, )?; // Push the error for reporting, this will also @@ -2212,8 +2244,8 @@ impl CaManager { CertAuthCommandDetails::DropResourceClass( rcn.clone(), reason.to_string(), - self.signer.clone(), - ) + ), + krill, )?; // Push the error for reporting, this will also @@ -2277,6 +2309,7 @@ impl CaManager { parent: ParentHandle, entitlements: ResourceClassListResponse, actor: &Actor, + krill: &KrillRuntime, ) -> KrillResult { let current_version = self.get_ca(ca)?.version(); let new_version = self.process_ca_command( @@ -2284,8 +2317,8 @@ impl CaManager { CertAuthCommandDetails::UpdateEntitlements( parent, entitlements, - self.signer.clone(), ), + krill, )?.version(); Ok(new_version > current_version) } @@ -2297,11 +2330,14 @@ impl CaManager { parent: &ParentHandle, contact: &ParentCaContact, existing_parent: bool, + krill: &KrillRuntime, ) -> KrillResult { let server_info = contact.parent_server_info(); let uri = &server_info.service_uri; - let result = self.get_entitlements_rfc6492(ca, server_info).await; + let result = self.get_entitlements_rfc6492( + ca, server_info, krill + ).await; match &result { Err(error) => { @@ -2332,6 +2368,7 @@ impl CaManager { &self, handle: &CaHandle, server_info: &ParentServerInfo, + krill: &KrillRuntime, ) -> KrillResult { debug!( "Getting entitlements for CA '{}' from parent '{}'", @@ -2351,6 +2388,7 @@ impl CaManager { list, server_info, &child.id_cert().public_key.key_identifier(), + krill, ).await?; let payload = response.into_payload(); @@ -2375,6 +2413,7 @@ impl CaManager { message: provisioning::Message, server_info: &ParentServerInfo, signing_key: &KeyIdentifier, + krill: &KrillRuntime, ) -> KrillResult { let service_uri = &server_info.service_uri; if let Some(parent) = Self::local_parent( @@ -2388,6 +2427,7 @@ impl CaManager { message, user_agent, &self.system_actor, + krill, ) } else { @@ -2768,6 +2808,7 @@ impl CaManager { new_contact: RepositoryContact, check_repo: bool, actor: &Actor, + krill: &KrillRuntime, ) -> KrillResult<()> { let ca = self.get_ca(&ca_handle)?; if check_repo { @@ -2784,10 +2825,8 @@ impl CaManager { } self.process_ca_command( ca_handle, actor, - CertAuthCommandDetails::RepoUpdate( - new_contact, - self.signer.clone(), - ) + CertAuthCommandDetails::RepoUpdate(new_contact), + krill )?; Ok(()) } @@ -2995,14 +3034,12 @@ impl CaManager { ca: CaHandle, updates: AspaDefinitionUpdates, actor: &Actor, + krill: &KrillRuntime, ) -> KrillResult<()> { self.process_ca_command( ca, actor, - CertAuthCommandDetails::AspasUpdate( - updates, - self.config.clone(), - self.signer.clone(), - ), + CertAuthCommandDetails::AspasUpdate(updates), + krill, )?; Ok(()) } @@ -3014,15 +3051,14 @@ impl CaManager { customer: CustomerAsn, update: AspaProvidersUpdate, actor: &Actor, + krill: &KrillRuntime, ) -> KrillResult<()> { self.process_ca_command( ca.clone(), actor, CertAuthCommandDetails::AspasUpdateExisting( - customer, - update, - self.config.clone(), - self.signer.clone(), - ) + customer, update, + ), + krill, )?; Ok(()) } @@ -3044,14 +3080,12 @@ impl CaManager { ca: CaHandle, updates: BgpSecDefinitionUpdates, actor: &Actor, + krill: &KrillRuntime, ) -> KrillResult<()> { self.process_ca_command( ca.clone(), actor, - CertAuthCommandDetails::BgpSecUpdateDefinitions( - updates, - self.config.clone(), - self.signer.clone(), - ), + CertAuthCommandDetails::BgpSecUpdateDefinitions(updates), + krill, )?; Ok(()) } @@ -3075,14 +3109,12 @@ impl CaManager { ca: CaHandle, updates: RoaConfigurationUpdates, actor: &Actor, + krill: &KrillRuntime, ) -> KrillResult<()> { self.process_ca_command( ca.clone(), actor, - CertAuthCommandDetails::RouteAuthorizationsUpdate( - updates, - self.config.clone(), - self.signer.clone(), - ), + CertAuthCommandDetails::RouteAuthorizationsUpdate(updates), + krill, )?; Ok(()) } @@ -3098,15 +3130,13 @@ impl CaManager { /// CAs are expected to note extended validity eligibility and request /// updated certificates themselves. pub fn renew_objects_all( - &self, actor: &Actor + &self, actor: &Actor, krill: &KrillRuntime, ) -> KrillResult<()> { for ca in self.ca_store.list()? { if let Err(e) = self.process_ca_command( ca.clone(), actor, - CertAuthCommandDetails::RouteAuthorizationsRenew( - self.config.clone(), - self.signer.clone(), - ) + CertAuthCommandDetails::RouteAuthorizationsRenew, + krill, ) { error!( "Renewing ROAs for CA '{ca}' failed with error: {e}" @@ -3115,10 +3145,8 @@ impl CaManager { if let Err(e) = self.process_ca_command( ca.clone(), actor, - CertAuthCommandDetails::AspasRenew( - self.config.clone(), - self.signer.clone(), - ), + CertAuthCommandDetails::AspasRenew, + krill, ) { error!( "Renewing ASPAs for CA '{ca}' failed with error: {e}" @@ -3127,10 +3155,8 @@ impl CaManager { if let Err(e) = self.process_ca_command( ca.clone(), actor, - CertAuthCommandDetails::BgpSecRenew( - self.config.clone(), - self.signer.clone(), - ), + CertAuthCommandDetails::BgpSecRenew, + krill, ) { error!( "Renewing BGPsec certificates for CA '{ca}' \ @@ -3151,14 +3177,13 @@ impl CaManager { pub fn force_renew_roas_all( &self, actor: &Actor, + krill: &KrillRuntime, ) -> KrillResult<()> { for ca in self.ca_store.list()? { if let Err(e) = self.process_ca_command( ca.clone(), actor, - CertAuthCommandDetails::RouteAuthorizationsForceRenew( - self.config.clone(), - self.signer.clone(), - ), + CertAuthCommandDetails::RouteAuthorizationsForceRenew, + krill, ) { error!( "Renewing ROAs for CA '{ca}' failed with error: {e}" @@ -3179,14 +3204,12 @@ impl CaManager { name: RtaName, request: RtaContentRequest, actor: &Actor, + krill: &KrillRuntime, ) -> KrillResult<()> { self.process_ca_command( ca.clone(), actor, - CertAuthCommandDetails::RtaSign( - name, - request, - self.signer.clone(), - ) + CertAuthCommandDetails::RtaSign(name, request), + krill, )?; Ok(()) } @@ -3198,14 +3221,12 @@ impl CaManager { name: RtaName, request: RtaPrepareRequest, actor: &Actor, + krill: &KrillRuntime, ) -> KrillResult<()> { self.process_ca_command( ca.clone(), actor, - CertAuthCommandDetails::RtaMultiPrepare( - name, - request, - self.signer.clone(), - ) + CertAuthCommandDetails::RtaMultiPrepare(name, request), + krill, )?; Ok(()) } @@ -3217,14 +3238,12 @@ impl CaManager { name: RtaName, rta: ResourceTaggedAttestation, actor: &Actor, + krill: &KrillRuntime, ) -> KrillResult<()> { self.process_ca_command( ca.clone(), actor, - CertAuthCommandDetails::RtaCoSign( - name, - rta, - self.signer.clone(), - ) + CertAuthCommandDetails::RtaCoSign(name, rta), + krill, )?; Ok(()) } @@ -3241,13 +3260,12 @@ impl CaManager { handle: CaHandle, max_age: Duration, actor: &Actor, + krill: &KrillRuntime, ) -> KrillResult<()> { self.process_ca_command( handle.clone(), actor, - CertAuthCommandDetails::KeyRollInitiate( - max_age, - self.signer.clone(), - ) + CertAuthCommandDetails::KeyRollInitiate(max_age), + krill, )?; Ok(()) } @@ -3263,14 +3281,12 @@ impl CaManager { handle: CaHandle, staging: Duration, actor: &Actor, + krill: &KrillRuntime, ) -> KrillResult<()> { self.process_ca_command( handle.clone(), actor, - CertAuthCommandDetails::KeyRollActivate( - staging, - self.config.clone(), - self.signer.clone(), - ) + CertAuthCommandDetails::KeyRollActivate(staging), + krill, )?; Ok(()) } diff --git a/src/server/ca/publishing.rs b/src/server/ca/publishing.rs index 603565e47..9db5f00ff 100644 --- a/src/server/ca/publishing.rs +++ b/src/server/ca/publishing.rs @@ -25,7 +25,6 @@ use crate::api::roa::RoaInfo; use crate::commons::KrillResult; use crate::commons::crypto::KrillSigner; use crate::commons::error::Error; -use crate::commons::eventsourcing::PreSaveEventListener; use crate::commons::storage::{Ident, KeyValueStore}; use crate::constants::CA_OBJECTS_NS; use crate::config::IssuanceTimingConfig; @@ -85,11 +84,9 @@ impl CaObjectsStore { issuance_timing, }) } -} -/// React to any events on a CA that cause the set of object to change. -impl PreSaveEventListener for CaObjectsStore { - fn listen( + /// React to any events on a CA that cause the set of object to change. + pub(super) fn cert_auth_pre_save_events( &self, ca: &CertAuth, events: &[CertAuthEvent], diff --git a/src/server/mq.rs b/src/server/mq.rs index e73937b75..0351262e0 100644 --- a/src/server/mq.rs +++ b/src/server/mq.rs @@ -12,7 +12,6 @@ use rpki::repository::x509::Time; use serde::{Deserialize, Serialize}; use url::Url; use crate::api::ca::Timestamp; -use crate::commons::eventsourcing; use crate::commons::{Error, KrillResult}; use crate::commons::eventsourcing::Aggregate; use crate::commons::queue::{Queue, ScheduleMode}; @@ -417,8 +416,20 @@ impl TaskQueue { } } -/// Implement listening for CertAuth events. -impl TaskQueue { +/// # Handling of [`CertAuth`] events. +/// +impl TaskQueue { + pub(super) fn cert_auth_pre_save_events( + &self, + ca: &CertAuth, + events: &[CertAuthEvent], + ) -> KrillResult<()> { + for event in events { + self.schedule_for_ca_event(ca, ca.version(), event)?; + } + Ok(()) + } + fn schedule_for_ca_event( &self, ca: &CertAuth, @@ -582,28 +593,14 @@ impl TaskQueue { _ => Ok(()), } } -} - -/// Implement pre-save listening for CertAuth events. -impl eventsourcing::PreSaveEventListener for TaskQueue { - fn listen( - &self, - ca: &CertAuth, - events: &[CertAuthEvent], - ) -> KrillResult<()> { - for event in events { - self.schedule_for_ca_event(ca, ca.version(), event)?; - } - Ok(()) - } -} -/// Implement post-save listening for CertAuth events. -/// -/// Used for best effort signaling to local child CAs that a sync with -/// their parent is needed. -impl eventsourcing::PostSaveEventListener for TaskQueue { - fn listen(&self, ca: &CertAuth, events: &[CertAuthEvent]) { + /// Implement post-save listening for CertAuth events. + /// + /// Used for best effort signaling to local child CAs that a sync with + /// their parent is needed. + pub(super) fn cert_auth_post_save_events( + &self, ca: &CertAuth, events: &[CertAuthEvent] + ) { for event in events { match event { CertAuthEvent::ChildUpdatedResources { child, .. } @@ -635,9 +632,10 @@ impl eventsourcing::PostSaveEventListener for TaskQueue { } } -/// Implement pre-save listening for TrustAnchorProxy events. -impl eventsourcing::PreSaveEventListener for TaskQueue { - fn listen( +/// # Handling of [`TrustAnchorProxy`] events. +/// +impl TaskQueue { + pub(super) fn ta_proxy_pre_save_events( &self, proxy: &TrustAnchorProxy, events: &[TrustAnchorProxyEvent], @@ -677,11 +675,9 @@ impl eventsourcing::PreSaveEventListener for TaskQueue { } Ok(()) } -} -/// Implement post-save listening for TrustAnchorProxy events. -impl eventsourcing::PostSaveEventListener for TaskQueue { - fn listen( + /// Implement post-save listening for TrustAnchorProxy events. + pub(super) fn ta_proxy_post_save_events( &self, _proxy: &TrustAnchorProxy, events: &[TrustAnchorProxyEvent], diff --git a/src/server/oldmanager.rs b/src/server/oldmanager.rs index f9b5061ee..66f73a54f 100644 --- a/src/server/oldmanager.rs +++ b/src/server/oldmanager.rs @@ -1,4 +1,5 @@ //! An RPKI publication protocol server. + use std::collections::HashMap; use std::path::PathBuf; use std::str::FromStr; @@ -72,6 +73,7 @@ use crate::api::ta::{ }; use crate::constants::{TA_NAME, ta_handle}; use crate::server::bgp::BgpAnalyser; +use crate::server::runtime::KrillRuntime; //------------ OldManager --------------------------------------------------- @@ -79,6 +81,9 @@ use crate::server::bgp::BgpAnalyser; /// This is the Krill server that is doing all the orchestration for all /// components. pub struct OldManager { + krill: KrillRuntime, + + // The base URI for this service service_uri: uri::Https, @@ -104,6 +109,7 @@ pub struct OldManager { impl OldManager { /// Creates a new publication server. Note that state is preserved /// in the data storage. + #[allow(unreachable_code, unused_variables)] pub async fn build(config: Arc) -> KrillResult { let service_uri = config.service_uri(); @@ -161,6 +167,7 @@ impl OldManager { mq.schedule(Task::QueueStartTasks, now())?; let server = OldManager { + krill: todo!(), service_uri, repo_manager, ca_manager, @@ -392,7 +399,7 @@ impl OldManager { } pub fn ta_proxy_init(&self) -> KrillResult<()> { - self.ca_manager.ta_proxy_init() + self.ca_manager.ta_proxy_init(&self.krill) } pub fn ta_proxy_id(&self) -> KrillResult { @@ -411,7 +418,7 @@ impl OldManager { actor: &Actor, ) -> KrillResult<()> { self.ca_manager - .ta_proxy_repository_update(contact, actor) + .ta_proxy_repository_update(contact, actor, &self.krill) } pub fn ta_proxy_repository_contact( @@ -425,7 +432,7 @@ impl OldManager { info: TrustAnchorSignerInfo, actor: &Actor, ) -> KrillResult<()> { - self.ca_manager.ta_proxy_signer_add(info, actor) + self.ca_manager.ta_proxy_signer_add(info, actor, &self.krill) } pub fn ta_proxy_signer_update( @@ -433,14 +440,14 @@ impl OldManager { info: TrustAnchorSignerInfo, actor: &Actor, ) -> KrillResult<()> { - self.ca_manager.ta_proxy_signer_update(info, actor) + self.ca_manager.ta_proxy_signer_update(info, actor, &self.krill) } pub fn ta_proxy_signer_make_request( &self, actor: &Actor, ) -> KrillResult { - self.ca_manager.ta_proxy_signer_make_request(actor) + self.ca_manager.ta_proxy_signer_make_request(actor, &self.krill) } pub fn ta_proxy_signer_get_request( @@ -455,7 +462,7 @@ impl OldManager { actor: &Actor, ) -> KrillResult<()> { self.ca_manager - .ta_proxy_signer_process_response(response, actor) + .ta_proxy_signer_process_response(response, actor, &self.krill) } pub fn ta_proxy_children_add( @@ -469,6 +476,7 @@ impl OldManager { child_request, &self.config.service_uri(), actor, + &self.krill, ) } @@ -488,7 +496,9 @@ impl OldManager { req: AddChildRequest, actor: &Actor, ) -> KrillResult { - self.ca_manager.ca_add_child(ca, req, &self.service_uri, actor) + self.ca_manager.ca_add_child( + ca, req, &self.service_uri, actor, &self.krill, + ) } /// Shows the parent contact for a child. @@ -517,7 +527,7 @@ impl OldManager { req: UpdateChildRequest, actor: &Actor, ) -> KrillEmptyResult { - self.ca_manager.ca_child_update(ca, child, req, actor) + self.ca_manager.ca_child_update(ca, child, req, actor, &self.krill) } /// Update IdCert or resources of a child. @@ -527,7 +537,7 @@ impl OldManager { child: ChildHandle, actor: &Actor, ) -> KrillEmptyResult { - self.ca_manager.ca_child_remove(ca, child, actor) + self.ca_manager.ca_child_remove(ca, child, actor, &self.krill) } /// Show details for a child under the CA. @@ -555,7 +565,7 @@ impl OldManager { child: ImportChild, actor: &Actor, ) -> KrillResult<()> { - self.ca_manager.ca_child_import(ca, child, actor) + self.ca_manager.ca_child_import(ca, child, actor, &self.krill) } /// Show children stats under the CA. @@ -597,11 +607,13 @@ impl OldManager { Error::CaParentResponseInvalid(ca.clone(), e.to_string()) })?; self.ca_manager.get_entitlements_from_contact( - &ca, &parent_req.handle, &contact, false + &ca, &parent_req.handle, &contact, false, &self.krill, ).await?; // Seems good. Add/update the parent. - self.ca_manager.ca_parent_add_or_update(ca, parent_req, actor) + self.ca_manager.ca_parent_add_or_update( + ca, parent_req, actor, &self.krill, + ) } pub async fn ca_parent_remove( @@ -611,7 +623,7 @@ impl OldManager { actor: &Actor, ) -> KrillEmptyResult { self.ca_manager - .ca_parent_remove(handle, parent, actor) + .ca_parent_remove(handle, parent, actor, &self.krill) .await } } @@ -691,6 +703,7 @@ impl OldManager { import_ta.ta_key_pem, &self.repo_manager, &actor, + &self.krill, ) .await?; } else { @@ -711,6 +724,7 @@ impl OldManager { self.repo_manager.clone(), service_uri.clone(), actor.clone(), + self.krill.clone(), ))); } try_join_all(import_fns).await.map_err(|e| { @@ -726,6 +740,7 @@ impl OldManager { repo_manager: Arc, service_uri: Arc, actor: Arc, + krill: KrillRuntime, ) -> KrillEmptyResult { // outline: // - init ca @@ -736,7 +751,7 @@ impl OldManager { info!("Importing CA: '{}'", import.handle); // init CA - ca_manager.init_ca(import.handle.clone())?; + ca_manager.init_ca(import.handle.clone(), &krill)?; // Get Publisher Request let pub_req = { @@ -767,6 +782,7 @@ impl OldManager { repo_contact, false, &actor, + &krill, ) .await?; @@ -838,6 +854,7 @@ impl OldManager { child_req, &service_uri, &actor, + &krill, )? }; @@ -851,17 +868,18 @@ impl OldManager { import.handle.clone(), parent_req, &actor, + &krill, )?; // First sync will inform child of its entitlements and // trigger that CSR is created. ca_manager.ca_sync_parent( - &import.handle, 0, &import_parent.handle, &actor + &import.handle, 0, &import_parent.handle, &actor, &krill, ).await?; // Second sync will send that CSR to the parent ca_manager.ca_sync_parent( - &import.handle, 0, &import_parent.handle, &actor + &import.handle, 0, &import_parent.handle, &actor, &krill, ).await?; // If the parent is a TA, then we will need to push a bit @@ -869,9 +887,10 @@ impl OldManager { // triggered tasks, but the task scheduler is // not running when we do this at startup. if import_parent.handle.as_str() == TA_NAME { - ca_manager.sync_ta_proxy_signer_if_possible()?; + ca_manager.sync_ta_proxy_signer_if_possible(&krill)?; ca_manager.ca_sync_parent( - &import.handle, 0, &import_parent.handle, &actor + &import.handle, 0, &import_parent.handle, &actor, + &krill, ).await?; } } @@ -882,7 +901,9 @@ impl OldManager { added: import.roas, removed: vec![] }; - ca_manager.ca_routes_update(import.handle, roa_updates, &actor)?; + ca_manager.ca_routes_update( + import.handle, roa_updates, &actor, &krill + )?; Ok(()) } @@ -966,7 +987,7 @@ impl OldManager { actor: &Actor, ) -> KrillResult<()> { self.ca_manager - .delete_ca(self.repo_manager.as_ref(), ca, actor) + .delete_ca(self.repo_manager.as_ref(), ca, actor, &self.krill) .await } @@ -1010,7 +1031,7 @@ impl OldManager { } pub fn ca_init(&self, init: CertAuthInit) -> KrillEmptyResult { - self.ca_manager.init_ca(init.handle) + self.ca_manager.init_ca(init.handle, &self.krill) } /// Return the info about the CONFIGured repository server for a given Ca. @@ -1032,9 +1053,9 @@ impl OldManager { contact: RepositoryContact, actor: &Actor, ) -> KrillEmptyResult { - self.ca_manager - .update_repo(self.repo_manager.as_ref(), ca, contact, true, actor) - .await + self.ca_manager.update_repo( + self.repo_manager.as_ref(), ca, contact, true, actor, &self.krill + ).await } pub fn ca_update_id( @@ -1042,7 +1063,7 @@ impl OldManager { ca: CaHandle, actor: &Actor, ) -> KrillEmptyResult { - self.ca_manager.ca_update_id(ca, actor) + self.ca_manager.ca_update_id(ca, actor, &self.krill) } pub fn ca_keyroll_init( @@ -1050,7 +1071,9 @@ impl OldManager { ca: CaHandle, actor: &Actor, ) -> KrillEmptyResult { - self.ca_manager.ca_keyroll_init(ca, Duration::seconds(0), actor) + self.ca_manager.ca_keyroll_init( + ca, Duration::seconds(0), actor, &self.krill, + ) } pub fn ca_keyroll_activate( @@ -1058,7 +1081,9 @@ impl OldManager { ca: CaHandle, actor: &Actor, ) -> KrillEmptyResult { - self.ca_manager.ca_keyroll_activate(ca, Duration::seconds(0), actor) + self.ca_manager.ca_keyroll_activate( + ca, Duration::seconds(0), actor, &self.krill, + ) } pub fn rfc6492( @@ -1068,7 +1093,9 @@ impl OldManager { user_agent: Option, actor: &Actor, ) -> KrillResult { - self.ca_manager.rfc6492(&ca, msg_bytes, user_agent, actor) + self.ca_manager.rfc6492( + &ca, msg_bytes, user_agent, actor, &self.krill + ) } } @@ -1087,7 +1114,9 @@ impl OldManager { updates: AspaDefinitionUpdates, actor: &Actor, ) -> KrillEmptyResult { - self.ca_manager.ca_aspas_definitions_update(ca, updates, actor) + self.ca_manager.ca_aspas_definitions_update( + ca, updates, actor, &self.krill + ) } pub fn ca_aspas_update_aspa( @@ -1098,7 +1127,7 @@ impl OldManager { actor: &Actor, ) -> KrillEmptyResult { self.ca_manager.ca_aspas_update_aspa_providers( - ca, customer, update, actor + ca, customer, update, actor, &self.krill, ) } } @@ -1118,7 +1147,9 @@ impl OldManager { updates: BgpSecDefinitionUpdates, actor: &Actor, ) -> KrillResult<()> { - self.ca_manager.ca_bgpsec_definitions_update(ca, updates, actor) + self.ca_manager.ca_bgpsec_definitions_update( + ca, updates, actor, &self.krill, + ) } } @@ -1130,7 +1161,7 @@ impl OldManager { updates: RoaConfigurationUpdates, actor: &Actor, ) -> KrillEmptyResult { - self.ca_manager.ca_routes_update(ca, updates, actor) + self.ca_manager.ca_routes_update(ca, updates, actor, &self.krill) } pub fn ca_routes_show( @@ -1192,7 +1223,7 @@ impl OldManager { /// Re-issue ROA objects so that they will use short subjects (see issue /// #700) pub async fn force_renew_roas(&self) -> KrillResult<()> { - self.ca_manager.force_renew_roas_all(self.system_actor()) + self.ca_manager.force_renew_roas_all(self.system_actor(), &self.krill) } } @@ -1247,7 +1278,7 @@ impl OldManager { request: RtaContentRequest, actor: &Actor, ) -> KrillResult<()> { - self.ca_manager.rta_sign(ca, name, request, actor) + self.ca_manager.rta_sign(ca, name, request, actor, &self.krill) } /// Prepare a multi @@ -1258,7 +1289,9 @@ impl OldManager { request: RtaPrepareRequest, actor: &Actor, ) -> KrillResult { - self.ca_manager.rta_multi_prep(&ca, name.clone(), request, actor)?; + self.ca_manager.rta_multi_prep( + &ca, name.clone(), request, actor, &self.krill, + )?; let ca = self.ca_manager.get_ca(&ca)?; ca.rta_prep_response(&name) } @@ -1271,7 +1304,7 @@ impl OldManager { rta: ResourceTaggedAttestation, actor: &Actor, ) -> KrillResult<()> { - self.ca_manager.rta_multi_cosign(ca, name, rta, actor) + self.ca_manager.rta_multi_cosign(ca, name, rta, actor, &self.krill) } } diff --git a/src/server/properties/mod.rs b/src/server/properties/mod.rs index dd1787d1b..27d4c3b00 100644 --- a/src/server/properties/mod.rs +++ b/src/server/properties/mod.rs @@ -218,6 +218,8 @@ impl Aggregate for Properties { type Error = Error; + type Context<'a> = (); + fn init(handle: &MyHandle, event: PropertiesInitEvent) -> Self { Properties { handle: handle.clone(), @@ -228,6 +230,7 @@ impl Aggregate for Properties { fn process_init_command( command: PropertiesInitCommand, + _context: Self::Context<'_>, ) -> Result { Ok(PropertiesInitEvent { krill_version: command.into_details().krill_version, @@ -253,6 +256,7 @@ impl Aggregate for Properties { fn process_command( &self, command: Self::Command, + _context: Self::Context<'_>, ) -> Result, Self::Error> { if log_enabled!(log::Level::Trace) { trace!( diff --git a/src/server/pubd/access.rs b/src/server/pubd/access.rs index 7921801e6..249d79dce 100644 --- a/src/server/pubd/access.rs +++ b/src/server/pubd/access.rs @@ -296,6 +296,8 @@ impl Aggregate for RepositoryAccess { type InitEvent = RepositoryAccessInitEvent; type Error = Error; + type Context<'a> = (); + fn init(handle: &MyHandle, event: Self::InitEvent) -> Self { RepositoryAccess { handle: handle.clone(), @@ -309,6 +311,7 @@ impl Aggregate for RepositoryAccess { fn process_init_command( command: Self::InitCommand, + _context: Self::Context<'_>, ) -> Result { let details = command.into_details(); @@ -343,6 +346,7 @@ impl Aggregate for RepositoryAccess { fn process_command( &self, command: Self::Command, + _context: Self::Context<'_>, ) -> Result, Self::Error> { info!( "Processing command for publisher '{}', version: {}: {}", diff --git a/src/server/runtime.rs b/src/server/runtime.rs index e22be2c87..73f2ab173 100644 --- a/src/server/runtime.rs +++ b/src/server/runtime.rs @@ -108,7 +108,7 @@ impl KrillRuntime { &self.0.signer } - pub fn bpg_analyseer(&self) -> &BgpAnalyser { + pub fn bpg_analyser(&self) -> &BgpAnalyser { &self.0.bgp_analyser } @@ -164,7 +164,7 @@ struct Components { signer: KrillSigner, /// The BGP analyser. - bgp_analyser: Arc, + bgp_analyser: BgpAnalyser, /// The actor used for actions initiated by the server itself. system_actor: Actor, diff --git a/src/server/scheduler.rs b/src/server/scheduler.rs index 23341cea1..39a915332 100644 --- a/src/server/scheduler.rs +++ b/src/server/scheduler.rs @@ -37,12 +37,14 @@ use crate::{ }, properties::Properties, pubd::{RepositoryAccess, RepositoryContent, RepositoryManager}, + runtime::KrillRuntime, }, }; use super::mq::TaskResult; pub struct Scheduler { + krill: KrillRuntime, tasks: Arc, ca_manager: Arc, repo_manager: Arc, @@ -53,6 +55,7 @@ pub struct Scheduler { } impl Scheduler { + #[allow(unreachable_code, unused_variables)] pub fn build( tasks: Arc, ca_manager: Arc, @@ -62,6 +65,7 @@ impl Scheduler { system_actor: Actor, ) -> Self { Scheduler { + krill: todo!(), tasks, ca_manager, repo_manager, @@ -371,10 +375,9 @@ impl Scheduler { ) -> Result { if self.ca_manager.has_ca(&ca).map_err(FatalError)? { info!("Synchronize CA '{ca}' with its parent '{parent}'"); - match self - .ca_manager - .ca_sync_parent(&ca, ca_version, &parent, &self.system_actor) - .await + match self.ca_manager.ca_sync_parent( + &ca, ca_version, &parent, &self.system_actor, &self.krill, + ).await { Err(e) => { let next = self.config.requeue_remote_failed(); @@ -414,7 +417,7 @@ impl Scheduler { /// Resync the testbed TA signer and proxy async fn renew_testbed_ta(&self) -> Result { - if let Err(e) = self.ca_manager.ta_renew_testbed_ta() { + if let Err(e) = self.ca_manager.ta_renew_testbed_ta(&self.krill) { error!("There was an issue renewing the testbed TA: {e}"); } let weeks_to_resync = self.config.ta_timing.mft_next_update_weeks / 2; @@ -431,7 +434,7 @@ impl Scheduler { ) -> Result { debug!("Synchronise Trust Anchor Proxy with Signer - if Signer is local."); if let Err(e) = - self.ca_manager.sync_ta_proxy_signer_if_possible() + self.ca_manager.sync_ta_proxy_signer_if_possible(&self.krill) { error!("There was an issue synchronising the TA Proxy and Signer: {e}"); } @@ -448,7 +451,7 @@ impl Scheduler { "Verify if CA '{ca_handle}' has children that need to be suspended" ); self.ca_manager.ca_suspend_inactive_children( - &ca_handle, self.started, &self.system_actor, + &ca_handle, self.started, &self.system_actor, &self.krill, ); Ok(TaskResult::FollowUp( @@ -520,7 +523,9 @@ impl Scheduler { async fn renew_objects_if_needed( &self, ) -> Result { - self.ca_manager.renew_objects_all(&self.system_actor).map_err( + self.ca_manager.renew_objects_all( + &self.system_actor, &self.krill + ).map_err( FatalError )?; @@ -659,7 +664,9 @@ impl Scheduler { Ok(TaskResult::Reschedule(in_seconds(1))) } else if self .ca_manager - .send_revoke_requests(&ca_handle, &parent, requests) + .send_revoke_requests( + &ca_handle, &parent, requests, &self.krill, + ) .await .is_err() { @@ -706,6 +713,7 @@ impl Scheduler { &ca_handle, rcn, revocation_request, + &self.krill, ) .await { diff --git a/src/server/taproxy.rs b/src/server/taproxy.rs index 836e44c22..e80595879 100644 --- a/src/server/taproxy.rs +++ b/src/server/taproxy.rs @@ -4,7 +4,8 @@ //! *except* for signing using the Trust Anchor private key. That //! function is handled by the Trust Anchor Signer instead. -use std::{collections::HashMap, fmt, sync::Arc}; +use std::fmt; +use std::collections::HashMap; use chrono::Duration; use log::{log_enabled, trace}; @@ -40,6 +41,8 @@ use crate::api::ta::{ }; use crate::constants::ta_resource_class_name; use crate::server::ca::UsedKeyState; +use crate::server::mq::TaskQueue; +use crate::server::runtime::KrillRuntime; use crate::tasigner::TaTimingConfig; @@ -114,6 +117,8 @@ impl eventsourcing::Aggregate for TrustAnchorProxy { type InitEvent = TrustAnchorProxyInitEvent; type Error = Error; + type Context<'a> = TrustAnchorProxyContext<'a>; + fn init( handle: &CaHandle, event: TrustAnchorProxyInitEvent, ) -> Self { @@ -129,12 +134,12 @@ impl eventsourcing::Aggregate for TrustAnchorProxy { } fn process_init_command( - command: TrustAnchorProxyInitCommand, + _command: TrustAnchorProxyInitCommand, + context: Self::Context<'_>, ) -> Result { Ok(TrustAnchorProxyInitEvent { id: { - command.into_details().signer.create_self_signed_id_cert()? - .into() + context.signer.create_self_signed_id_cert()?.into() } }) } @@ -238,6 +243,7 @@ impl eventsourcing::Aggregate for TrustAnchorProxy { fn process_command( &self, command: Self::Command, + _context: Self::Context<'_>, ) -> Result, Self::Error> { if log_enabled!(log::Level::Trace) { trace!( @@ -296,6 +302,29 @@ impl eventsourcing::Aggregate for TrustAnchorProxy { ) => self.process_give_child_response(child_handle, key), } } + + fn pre_save_events( + &self, events: &[Self::Event], context: TrustAnchorProxyContext, + ) -> Result<(), Self::Error> { + // We need to let the task queue handle events pre-save so that we + // can schedule: + // - publication on updates + // - signing by the Trust Anchor Signer when there are requests + // in testbed mode + context.tasks.ta_proxy_pre_save_events(self, events)?; + + Ok(()) + } + + fn post_save_events( + &self, events: &[Self::Event], context: TrustAnchorProxyContext, + ) { + // We need to let the task queue handle events post-save so that we + // can schedule: + // - re-sync for local children when the proxy has new responses + // AND is saved + context.tasks.ta_proxy_post_save_events(self, events); + } } // # Process command details @@ -746,6 +775,30 @@ impl TrustAnchorProxy { } +//------------ TrustAnchorProxyContext --------------------------------------- + +/// The context for processing of trust anchor proxy commands. +/// +/// This is a separate type from [`KrillRuntime`] to simplify testing. This +/// shouldn’t be too bad, since it can be created from a `&KrillRuntime` via +/// a simple call to `into`. +#[derive(Clone, Copy)] +pub struct TrustAnchorProxyContext<'a> { + #[allow(dead_code)] // XXX remove!! + tasks: &'a TaskQueue, + signer: &'a KrillSigner, +} + +impl<'a> From<&'a KrillRuntime> for TrustAnchorProxyContext<'a> { + fn from(src: &'a KrillRuntime) -> Self { + Self { + tasks: src.tasks(), + signer: src.signer(), + } + } +} + + //------------ TrustAnchorProxyInitCommand ----------------------------------- pub type TrustAnchorProxyInitCommand = @@ -754,12 +807,11 @@ pub type TrustAnchorProxyInitCommand = impl TrustAnchorProxyInitCommand { pub fn make( id: MyHandle, - signer: Arc, actor: &Actor, ) -> Self { TrustAnchorProxyInitCommand::new( id, - TrustAnchorProxyInitCommandDetails { signer }, + TrustAnchorProxyInitCommandDetails, actor, ) } @@ -769,9 +821,7 @@ impl TrustAnchorProxyInitCommand { //------------ TrustAnchorProxyInitCommandDetails ---------------------------- #[derive(Clone, Debug)] -pub struct TrustAnchorProxyInitCommandDetails { - signer: Arc, -} +pub struct TrustAnchorProxyInitCommandDetails; impl fmt::Display for TrustAnchorProxyInitCommandDetails { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -1216,14 +1266,15 @@ impl eventsourcing::CommandDetails for TrustAnchorProxyCommandDetails { } -//----------------- TESTS ---------------------------------------------------- +//============ Tests ========================================================= + #[cfg(test)] mod tests { use rpki::ca::idexchange::{RepoInfo, ServiceUri}; use super::*; - use std::{sync::Arc, time::Duration}; + use std::time::Duration; use crate::{ api::admin::{PublicationServerInfo, RepositoryContact}, @@ -1236,8 +1287,9 @@ mod tests { config::ConfigDefaults, }; use crate::tasigner::{ - TrustAnchorSigner, TrustAnchorSignerInitCommand, - TrustAnchorSignerInitCommandDetails, TrustAnchorSignerCommand, + TrustAnchorSigner, TrustAnchorSignerContext, + TrustAnchorSignerInitCommand, TrustAnchorSignerInitCommandDetails, + TrustAnchorSignerCommand, }; #[test] @@ -1265,15 +1317,13 @@ mod tests { // We will import a TA key - this is only (supposed to be) // supported for the openssl signer let signers = ConfigDefaults::openssl_signer_only(); - let signer = Arc::new( - KrillSignerBuilder::new( - storage_uri, - Duration::from_secs(1), - &signers, - ) - .build() - .unwrap(), - ); + let signer = KrillSignerBuilder::new( + storage_uri, + Duration::from_secs(1), + &signers, + ).build().unwrap(); + + let tasks = TaskQueue::new(storage_uri).unwrap(); let timing = TaTimingConfig::default(); @@ -1282,11 +1332,20 @@ mod tests { let proxy_handle = CaHandle::new("proxy".into()); let proxy_init = TrustAnchorProxyInitCommand::make( proxy_handle.clone(), - signer.clone(), &actor, ); - ta_proxy_store.add(proxy_init).unwrap(); + let proxy_context = TrustAnchorProxyContext { + tasks: &tasks, + signer: &signer, + }; + let signer_context = TrustAnchorSignerContext::new( + &signer, timing + ); + + ta_proxy_store.add_with_context( + proxy_init, proxy_context + ).unwrap(); let repository = { let repo_info = RepoInfo::new( @@ -1313,7 +1372,9 @@ mod tests { repository, &actor, ); - let mut proxy = ta_proxy_store.command(add_repo_cmd).unwrap(); + let mut proxy = ta_proxy_store.command_with_context( + add_repo_cmd, proxy_context, + ).unwrap(); let signer_handle = CaHandle::new("signer".into()); let tal_https = @@ -1337,13 +1398,13 @@ mod tests { tal_rsync: tal_rsync.clone(), private_key_pem: Some(import_key_pem.to_string()), ta_mft_nr_override: Some(42), - timing, - signer: signer.clone(), }, &actor, ); - let mut ta_signer = ta_signer_store.add(signer_init_cmd).unwrap(); + let mut ta_signer = ta_signer_store.add_with_context( + signer_init_cmd, signer_context + ).unwrap(); let signer_info = ta_signer.get_signer_info(); let add_signer_cmd = TrustAnchorProxyCommand::add_signer( &proxy_handle, @@ -1351,7 +1412,9 @@ mod tests { &actor, ); - proxy = ta_proxy_store.command(add_signer_cmd).unwrap(); + proxy = ta_proxy_store.command_with_context( + add_signer_cmd, proxy_context, + ).unwrap(); // The initial signer starts off with a TA certificate // and a CRL and manifest with revision number 42, as specified in @@ -1370,7 +1433,9 @@ mod tests { &proxy_handle, &actor, ); - proxy = ta_proxy_store.command(make_publish_request_cmd).unwrap(); + proxy = ta_proxy_store.command_with_context( + make_publish_request_cmd, proxy_context, + ).unwrap(); let signed_request = proxy.get_signer_request(timing, &signer).unwrap(); @@ -1380,14 +1445,12 @@ mod tests { TrustAnchorSignerCommand::make_process_request_command( &signer_handle, signed_request.into(), - timing, Some(55), // override the next manifest number again - signer, &actor, ); - ta_signer = ta_signer_store - .command(ta_signer_process_request_command) - .unwrap(); + ta_signer = ta_signer_store.command_with_context( + ta_signer_process_request_command, signer_context, + ).unwrap(); let exchange = ta_signer.get_exchange(&request_nonce).unwrap(); let ta_proxy_process_signer_response_command = @@ -1397,9 +1460,9 @@ mod tests { &actor, ); - proxy = ta_proxy_store - .command(ta_proxy_process_signer_response_command) - .unwrap(); + proxy = ta_proxy_store.command_with_context( + ta_proxy_process_signer_response_command, proxy_context, + ).unwrap(); // The TA should have published again, the revision used for // manifest and crl will have been updated to the diff --git a/src/tasigner/config.rs b/src/tasigner/config.rs index e2ad1f962..44089c8d5 100644 --- a/src/tasigner/config.rs +++ b/src/tasigner/config.rs @@ -2,7 +2,6 @@ use std::{ fs::File, io::{self, Read}, path::PathBuf, - sync::Arc, }; use log::LevelFilter; @@ -229,7 +228,7 @@ impl Config { } // Signer support - pub fn signer(&self) -> Result, ConfigError> { + pub fn signer(&self) -> Result { // Assumes that Config::verify() has already ensured that the signer // configuration is valid and that Config::resolve() has been // used to update signer name references to resolve to the @@ -248,7 +247,7 @@ impl Config { ConfigError::Other(format!("Could not create KrillSigner: {e}")) })?; - Ok(Arc::new(signer)) + Ok(signer) } /// Returns a reference to the default signer configuration. diff --git a/src/tasigner/signer.rs b/src/tasigner/signer.rs index 418dfb6a3..97496b790 100644 --- a/src/tasigner/signer.rs +++ b/src/tasigner/signer.rs @@ -6,7 +6,7 @@ //! The proxy makes sign requests for the signer to sign. use super::*; -use std::{collections::HashMap, fmt, sync::Arc}; +use std::{collections::HashMap, fmt}; use chrono::SecondsFormat; use log::{log_enabled, trace}; @@ -46,6 +46,7 @@ use crate::api::ta::{ TrustAnchorSignerResponse, }; use crate::constants::ta_resource_class_name; +use crate::server::runtime::KrillRuntime; //------------ TrustAnchorSigner --------------------------------------------- @@ -85,6 +86,8 @@ impl eventsourcing::Aggregate for TrustAnchorSigner { type InitEvent = TrustAnchorSignerInitEvent; type Error = Error; + type Context<'a> = TrustAnchorSignerContext<'a>; + fn init(handle: &CaHandle, event: Self::InitEvent) -> Self { TrustAnchorSigner { handle: handle.clone(), @@ -99,27 +102,25 @@ impl eventsourcing::Aggregate for TrustAnchorSigner { fn process_init_command( command: TrustAnchorSignerInitCommand, + context: Self::Context<'_>, ) -> Result { let cmd = command.into_details(); - let timing = cmd.timing; - - let signer = cmd.signer; - let id = signer.create_self_signed_id_cert()?.into(); + let id = context.signer.create_self_signed_id_cert()?.into(); let proxy_id = cmd.proxy_id; let ta_cert_details = Self::create_ta_cert_details( cmd.repo_info, cmd.tal_https, cmd.tal_rsync, cmd.private_key_pem, - timing.certificate_validity_years, - &signer, + context.ta_timing_config.certificate_validity_years, + context.signer, )?; let objects = TrustAnchorObjects::create( &ta_cert_details.cert, cmd.ta_mft_nr_override.unwrap_or(1), - timing.mft_next_update_weeks, - &signer, + context.ta_timing_config.mft_next_update_weeks, + context.signer, )?; Ok(TrustAnchorSignerInitEvent { @@ -162,6 +163,7 @@ impl eventsourcing::Aggregate for TrustAnchorSigner { fn process_command( &self, command: Self::Command, + context: Self::Context<'_>, ) -> Result, Self::Error> { if log_enabled!(log::Level::Trace) { trace!( @@ -175,29 +177,26 @@ impl eventsourcing::Aggregate for TrustAnchorSigner { match command.into_details() { TrustAnchorSignerCommandDetails::TrustAnchorSignerRequest { signed_request, - ta_timing_config, ta_mft_number_override, - signer, } => self.process_signer_request( signed_request, - ta_timing_config, + context.ta_timing_config, ta_mft_number_override, - &signer, + context.signer, ), TrustAnchorSignerCommandDetails::TrustAnchorSignerReissueRequest { repo_info, tal_https, tal_rsync, - timing, - signer } => { - let years = timing.certificate_validity_years; + let years = + context.ta_timing_config.certificate_validity_years; let res = self.update_ta_cert_details( repo_info, tal_https, tal_rsync, years, - &signer + context.signer ); match res { Err(r) => Err(r), @@ -538,6 +537,33 @@ impl TrustAnchorSigner { } +//------------ TrustAnchorSignerContext -------------------------------------- + +#[derive(Clone, Copy)] +pub struct TrustAnchorSignerContext<'a> { + signer: &'a KrillSigner, + ta_timing_config: TaTimingConfig, +} + +impl<'a> TrustAnchorSignerContext<'a> { + pub fn new( + signer: &'a KrillSigner, + ta_timing_config: TaTimingConfig, + ) -> Self { + Self { signer, ta_timing_config } + } +} + +impl<'a> From<&'a KrillRuntime> for TrustAnchorSignerContext<'a> { + fn from(src: &'a KrillRuntime) -> Self { + Self { + signer: src.signer(), + ta_timing_config: src.config().ta_timing, + } + } +} + + //------------ TrustAnchorSignerInitCommand ---------------------------------- pub type TrustAnchorSignerInitCommand = @@ -554,8 +580,6 @@ pub struct TrustAnchorSignerInitCommandDetails { pub tal_rsync: uri::Rsync, pub private_key_pem: Option, pub ta_mft_nr_override: Option, - pub timing: TaTimingConfig, - pub signer: Arc, } impl fmt::Display for TrustAnchorSignerInitCommandDetails { @@ -582,9 +606,7 @@ impl TrustAnchorSignerCommand { pub fn make_process_request_command( id: &CaHandle, signed_request: TrustAnchorSignedRequest, - ta_timing_config: TaTimingConfig, ta_mft_number_override: Option, - signer: Arc, actor: &Actor, ) -> TrustAnchorSignerCommand { TrustAnchorSignerCommand::new( @@ -592,9 +614,7 @@ impl TrustAnchorSignerCommand { None, TrustAnchorSignerCommandDetails::TrustAnchorSignerRequest { signed_request, - ta_timing_config, ta_mft_number_override, - signer, }, actor, ) @@ -605,8 +625,6 @@ impl TrustAnchorSignerCommand { repo_info: RepoInfo, tal_https: Vec, tal_rsync: uri::Rsync, - ta_timing_config: TaTimingConfig, - signer: Arc, actor: &Actor, ) -> TrustAnchorSignerCommand { TrustAnchorSignerCommand::new( @@ -616,8 +634,6 @@ impl TrustAnchorSignerCommand { repo_info, tal_https, tal_rsync, - timing: ta_timing_config, - signer, }, actor ) @@ -631,16 +647,12 @@ impl TrustAnchorSignerCommand { pub enum TrustAnchorSignerCommandDetails { TrustAnchorSignerRequest { signed_request: TrustAnchorSignedRequest, - ta_timing_config: TaTimingConfig, ta_mft_number_override: Option, - signer: Arc, }, TrustAnchorSignerReissueRequest { repo_info: RepoInfo, tal_https: Vec, tal_rsync: uri::Rsync, - timing: TaTimingConfig, - signer: Arc, }, } From 624e3c2b8637de63ada70e4147ec8a5745c0aa61 Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Fri, 16 Jan 2026 13:53:16 +0100 Subject: [PATCH 07/51] Make auth providers not keep the full config. --- src/daemon/http/auth/authorizer.rs | 14 ++-- src/daemon/http/auth/providers/admin_token.rs | 2 +- .../auth/providers/openid_connect/provider.rs | 73 +++++++++---------- src/daemon/http/auth/providers/unix_user.rs | 2 +- src/daemon/http/server.rs | 2 +- 5 files changed, 45 insertions(+), 48 deletions(-) diff --git a/src/daemon/http/auth/authorizer.rs b/src/daemon/http/auth/authorizer.rs index fe967b4d5..da482c5a6 100644 --- a/src/daemon/http/auth/authorizer.rs +++ b/src/daemon/http/auth/authorizer.rs @@ -202,25 +202,23 @@ impl Authorizer { /// /// The authorizer will be created according to information provided via /// `config`. - pub fn new( - config: Arc, - ) -> KrillResult { + pub fn new(config: &Config) -> KrillResult { let (primary_provider, legacy_provider) = match config.auth_type { AuthType::AdminToken => { - (admin_token::AuthProvider::new(config.clone()).into(), None) + (admin_token::AuthProvider::new(&config).into(), None) } #[cfg(feature = "multi-user")] AuthType::ConfigFile => { ( config_file::AuthProvider::new(&config)?.into(), - Some(admin_token::AuthProvider::new(config.clone())) + Some(admin_token::AuthProvider::new(&config)) ) } #[cfg(feature = "multi-user")] AuthType::OpenIDConnect => { ( - openid_connect::AuthProvider::new(config.clone())?.into(), - Some(admin_token::AuthProvider::new(config.clone())) + openid_connect::AuthProvider::new(&config)?.into(), + Some(admin_token::AuthProvider::new(&config)) ) } }; @@ -229,7 +227,7 @@ impl Authorizer { primary_provider, legacy_provider, #[cfg(unix)] - unix_socket_provider: unix_user::AuthProvider::new(config.clone())? + unix_socket_provider: unix_user::AuthProvider::new(&config)? }) } diff --git a/src/daemon/http/auth/providers/admin_token.rs b/src/daemon/http/auth/providers/admin_token.rs index 546323558..c0e12571e 100644 --- a/src/daemon/http/auth/providers/admin_token.rs +++ b/src/daemon/http/auth/providers/admin_token.rs @@ -41,7 +41,7 @@ pub struct AuthProvider { impl AuthProvider { /// Creates a new admin token auth provider from the given config. - pub fn new(config: Arc) -> Self { + pub fn new(config: &Config) -> Self { AuthProvider { required_token: config.admin_token.clone(), user_id: "admin-token".into(), diff --git a/src/daemon/http/auth/providers/openid_connect/provider.rs b/src/daemon/http/auth/providers/openid_connect/provider.rs index e4d84df99..3e1599082 100644 --- a/src/daemon/http/auth/providers/openid_connect/provider.rs +++ b/src/daemon/http/auth/providers/openid_connect/provider.rs @@ -49,6 +49,7 @@ use openidconnect::{ CoreRevocableToken, } }; +use rpki::uri; use serde::{Deserialize, Serialize}; use tokio::runtime; use urlparse::{urlparse, GetQuery}; @@ -77,7 +78,7 @@ use crate::{ }, }, session::*, - AuthInfo, LoggedInUser, Permission, + AuthInfo, LoggedInUser, Permission, RoleMap, }, http::util::url_encode, }, @@ -168,20 +169,33 @@ type Session = ClientSession; //------------ AuthProvider -------------------------------------------------- pub struct AuthProvider { - config: Arc, + oidc_conf: ConfigAuthOpenIDConnect, + + /// The role directory. + roles: Arc, + + /// The URI we provide our service under. + service_uri: uri::Https, + session_cache: SessionCache, session_key: CryptState, conn: Arc>>, } impl AuthProvider { - pub fn new( - config: Arc, - ) -> KrillResult { + pub fn new(config: &Config) -> KrillResult { let session_key = Self::init_session_key(&config)?; + let Some(oidc_conf) = config.auth_openidconnect.as_ref() else { + return Err(Error::ConfigError( + "Missing [auth_openidconnect] config section!".into(), + )); + }; + Ok(Self { - config, + oidc_conf: oidc_conf.clone(), + roles: config.auth_roles.clone(), + service_uri: config.service_uri(), session_cache: SessionCache::new(), session_key, conn: Arc::new(RwLock::new(None)), @@ -238,9 +252,9 @@ impl AuthProvider { // URL. Strip off /.well-known/openid-configuration because // the openid-connect crate wants to add this itself and will // fail if it is already present in the URL. - let issuer = self.oidc_conf()?.issuer_url.clone(); - let issuer = - issuer.trim_end_matches("/.well-known/openid-configuration"); + let issuer = self.oidc_conf.issuer_url.trim_end_matches( + "/.well-known/openid-configuration" + ); let issuer = IssuerUrl::new(issuer.to_string())?; info!( @@ -387,12 +401,12 @@ impl AuthProvider { // | | | supported // -------------------|-------------------|------------------|--------------------------------------------- - let config_file_url = self.oidc_conf()?.logout_url.as_ref(); + let config_file_url = self.oidc_conf.logout_url.as_ref(); let mut rp_initiated_logout_url = meta.additional_metadata().end_session_endpoint.as_ref(); let mut revocation_url = meta.additional_metadata().revocation_endpoint.as_ref(); - let service_uri = self.config.service_uri().as_str().to_string(); + let service_uri = self.service_uri.as_str().to_string(); if let Some(rev_url) = revocation_url { // From: https://tools.ietf.org/html/rfc7009#section-2 @@ -472,10 +486,9 @@ impl AuthProvider { // been obtained by the Krill operator when they created a // registration for their Krill instance with their identity // provider. - let oidc_conf = self.oidc_conf()?; - let client_id = ClientId::new(oidc_conf.client_id.clone()); + let client_id = ClientId::new(self.oidc_conf.client_id.clone()); let client_secret = - ClientSecret::new(oidc_conf.client_secret.clone()); + ClientSecret::new(self.oidc_conf.client_secret.clone()); // Create a client we can use to communicate with the provider based // on what we just learned and using the credentials we read @@ -497,8 +510,7 @@ impl AuthProvider { // that we can exchange the temporary code for access and id // tokens. let redirect_uri = RedirectUrl::new( - self.config - .service_uri() + self.service_uri .join(AUTH_CALLBACK_ENDPOINT.as_bytes()) .unwrap() .to_string(), @@ -747,15 +759,6 @@ impl AuthProvider { crypt::crypt_init(config) } - fn oidc_conf(&self) -> KrillResult<&ConfigAuthOpenIDConnect> { - match &self.config.auth_openidconnect { - Some(oidc_conf) => Ok(oidc_conf), - None => Err(Error::ConfigError( - "Missing [auth_openidconnect] config section!".into(), - )), - } - } - fn extract_cookie( &self, request: &HyperRequest, @@ -1008,7 +1011,7 @@ impl AuthProvider { let mut id_token_verifier: CoreIdTokenVerifier = conn.client.id_token_verifier(); - if self.oidc_conf()?.insecure { + if self.oidc_conf.insecure { // This is NOT a good idea. It was needed when testing with // one provider and so may be of use to others in future // too. @@ -1112,7 +1115,7 @@ impl AuthProvider { ) -> KrillResult { Ok(AuthInfo::user( session.user_id.clone(), - self.config.auth_roles.get(&session.secrets.role).ok_or_else(|| { + self.roles.get(&session.secrets.role).ok_or_else(|| { ApiAuthError::ApiAuthPermanentError( format!( "user '{}' with undefined role '{}' \ @@ -1414,10 +1417,6 @@ impl AuthProvider { || Nonce::new(URL_BASE64_ENGINE.encode(nonce_hash)), ); - // This unwrap is safe as we check in new() that the OpenID Connect - // config exists. - let oidc_conf = self.oidc_conf()?; - // From https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest: // "prompt: login - The Authorization Server SHOULD prompt the // End-User for re-authentication. If it cannot re-authenticate the @@ -1429,7 +1428,7 @@ impl AuthProvider { // it has some notion of an existing login session. // https://github.com/NLnetLabs/krill/issues/614 - if oidc_conf.prompt_for_login { + if self.oidc_conf.prompt_for_login { request = request.add_prompt(CoreAuthPrompt::Login); } @@ -1451,11 +1450,11 @@ impl AuthProvider { // TODO: use request.set_pkce_challenge() ? - for scope in &oidc_conf.extra_login_scopes { + for scope in &self.oidc_conf.extra_login_scopes { request = request.add_scope(Scope::new(scope.clone())); } - for (k, v) in oidc_conf.extra_login_params.iter() { + for (k, v) in self.oidc_conf.extra_login_params.iter() { request = request.add_extra_param(k, v); } @@ -1668,7 +1667,7 @@ impl AuthProvider { id_token_claims, user_info_claims ); let id = claims.extract_claims( - &self.oidc_conf()?.id_claims + &self.oidc_conf.id_claims )?.ok_or_else(|| { Self::internal_error( "OpenID Connect: cannot determine user ID.", @@ -1676,14 +1675,14 @@ impl AuthProvider { ) })?; let role_name = claims.extract_claims( - &self.oidc_conf()?.role_claims + &self.oidc_conf.role_claims )?.ok_or_else(|| { Self::internal_error( "OpenID Connect: cannot determine user's role.", None ) })?; - let role = self.config.auth_roles.get( + let role = self.roles.get( &role_name ).ok_or_else(|| { let reason = format!( diff --git a/src/daemon/http/auth/providers/unix_user.rs b/src/daemon/http/auth/providers/unix_user.rs index 58a606dde..8ad34b8e1 100644 --- a/src/daemon/http/auth/providers/unix_user.rs +++ b/src/daemon/http/auth/providers/unix_user.rs @@ -32,7 +32,7 @@ pub struct AuthProvider { impl AuthProvider { /// Creates a new unix user auth provider from the given config. - pub fn new(config: Arc) -> KrillResult { + pub fn new(config: &Config) -> KrillResult { let mut unix_users = HashMap::new(); for (k, v) in config.unix_users().iter() { if let Some(role) = config.auth_roles.get(v) { diff --git a/src/daemon/http/server.rs b/src/daemon/http/server.rs index 44011ba97..87ec1f70e 100644 --- a/src/daemon/http/server.rs +++ b/src/daemon/http/server.rs @@ -42,7 +42,7 @@ impl HttpServer { config: Arc, runtime: &runtime::Handle, ) -> KrillResult> { - let authorizer = Authorizer::new(config.clone())?; + let authorizer = Authorizer::new(&config)?; authorizer.spawn_sweep(runtime); Ok(Self { old_krill, From ff4ed6b4a08eb5d79356b37358d7628b9a69a845 Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Thu, 22 Jan 2026 11:19:17 +0100 Subject: [PATCH 08/51] Move all sync methods to the new manager. --- src/bin/krill.rs | 5 +- src/config.rs | 4 + src/daemon/http/dispatch/bulk.rs | 42 +- src/daemon/http/dispatch/cas.rs | 268 ++++---- src/daemon/http/dispatch/error.rs | 7 + src/daemon/http/dispatch/metrics.rs | 15 +- src/daemon/http/dispatch/pubd.rs | 46 +- src/daemon/http/dispatch/root.rs | 34 +- src/daemon/http/dispatch/stats.rs | 8 +- src/daemon/http/dispatch/ta.rs | 72 ++- src/daemon/http/dispatch/testbed.rs | 52 +- src/daemon/http/request.rs | 2 +- src/daemon/http/response.rs | 16 + src/daemon/http/server.rs | 13 +- src/daemon/start.rs | 29 +- src/server/ca/manager.rs | 26 +- src/server/manager.rs | 942 +++++++++++++++++++++++++++- src/server/oldmanager.rs | 684 +------------------- src/server/runtime.rs | 2 +- 19 files changed, 1254 insertions(+), 1013 deletions(-) diff --git a/src/bin/krill.rs b/src/bin/krill.rs index 7d067afda..647352283 100644 --- a/src/bin/krill.rs +++ b/src/bin/krill.rs @@ -1,7 +1,6 @@ //! The Krill daemon binary. use std::path::PathBuf; -use std::sync::Arc; use clap::Parser; use clap::crate_version; use log::error; @@ -18,9 +17,7 @@ async fn main() { match Config::create(&args.config, false) { Ok(config) => { - if let Err(e) = start_krill_daemon( - Arc::new(config), None - ).await { + if let Err(e) = start_krill_daemon( config, None).await { error!("Krill failed to start: {e}"); ::std::process::exit(1); } diff --git a/src/config.rs b/src/config.rs index a6c599963..3bbfcb574 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1130,6 +1130,10 @@ impl Config { self.testbed.as_ref() } + pub fn testbed_enabled(&self) -> bool { + self.testbed.is_some() + } + /// Returns a reference to the default signer configuration. /// /// Assumes that the configuration is valid. Will panic otherwise. diff --git a/src/daemon/http/dispatch/bulk.rs b/src/daemon/http/dispatch/bulk.rs index d0d758f62..4851a4e9f 100644 --- a/src/daemon/http/dispatch/bulk.rs +++ b/src/daemon/http/dispatch/bulk.rs @@ -23,11 +23,11 @@ async fn cas( ) -> Result { match path.next() { Some("import") => cas_import(request, path).await, - Some("issues") => cas_issues(request, path), - Some("sync") => cas_sync(request, path), - Some("publish") => cas_publish(request, path), - Some("force_publish") => cas_force_publish(request, path), - Some("suspend") => cas_suspend(request, path), + Some("issues") => cas_issues(request, path).await, + Some("sync") => cas_sync(request, path).await, + Some("publish") => cas_publish(request, path).await, + Some("force_publish") => cas_force_publish(request, path).await, + Some("suspend") => cas_suspend(request, path).await, _ => Ok(HttpResponse::not_found()) } } @@ -44,7 +44,7 @@ async fn cas_import( Ok(HttpResponse::ok()) } -fn cas_issues( +async fn cas_issues( request: Request<'_>, path: PathIter<'_>, ) -> Result { @@ -54,9 +54,9 @@ fn cas_issues( let server = request.empty()?; let mut all_issues = AllCertAuthIssues::default(); - for ca in server.old_krill().ca_handles()? { + for ca in server.krill().ca_handles().await? { if auth.has_permission(Permission::CaRead, Some(&ca)) { - let issues = server.old_krill().ca_issues(&ca)?; + let issues = server.krill().ca_issues(ca.clone()).await?; if !issues.is_empty() { all_issues.cas.insert(ca, issues); } @@ -66,18 +66,18 @@ fn cas_issues( Ok(HttpResponse::json(&all_issues)) } -fn cas_sync( +async fn cas_sync( request: Request<'_>, mut path: PathIter<'_>, ) -> Result { match path.next() { - Some("parent") => cas_sync_parent(request, path), - Some("repo") => cas_sync_repo(request, path), + Some("parent") => cas_sync_parent(request, path).await, + Some("repo") => cas_sync_repo(request, path).await, _ => Ok(HttpResponse::not_found()) } } -fn cas_sync_parent( +async fn cas_sync_parent( request: Request<'_>, path: PathIter<'_>, ) -> Result { @@ -85,11 +85,11 @@ fn cas_sync_parent( request.check_post()?; let (request, _) = request.proceed_permitted(Permission::CaAdmin, None)?; let server = request.empty()?; - server.old_krill().cas_refresh_all()?; + server.krill().cas_refresh_all().await?; Ok(HttpResponse::ok()) } -fn cas_sync_repo( +async fn cas_sync_repo( request: Request<'_>, path: PathIter<'_>, ) -> Result { @@ -97,11 +97,11 @@ fn cas_sync_repo( request.check_post()?; let (request, _) = request.proceed_permitted(Permission::CaAdmin, None)?; let server = request.empty()?; - server.old_krill().cas_repo_sync_all()?; + server.krill().cas_repo_sync_all().await?; Ok(HttpResponse::ok()) } -fn cas_publish( +async fn cas_publish( request: Request<'_>, path: PathIter<'_>, ) -> Result { @@ -109,11 +109,11 @@ fn cas_publish( request.check_post()?; let (request, _) = request.proceed_permitted(Permission::CaAdmin, None)?; let server = request.empty()?; - server.old_krill().republish_all(false)?; + server.krill().republish_all(false).await?; Ok(HttpResponse::ok()) } -fn cas_force_publish( +async fn cas_force_publish( request: Request<'_>, path: PathIter<'_>, ) -> Result { @@ -121,11 +121,11 @@ fn cas_force_publish( request.check_post()?; let (request, _) = request.proceed_permitted(Permission::CaAdmin, None)?; let server = request.empty()?; - server.old_krill().republish_all(true)?; + server.krill().republish_all(true).await?; Ok(HttpResponse::ok()) } -fn cas_suspend( +async fn cas_suspend( request: Request<'_>, path: PathIter<'_>, ) -> Result { @@ -133,7 +133,7 @@ fn cas_suspend( request.check_post()?; let (request, _) = request.proceed_permitted(Permission::CaAdmin, None)?; let server = request.empty()?; - server.old_krill().cas_schedule_suspend_all()?; + server.krill().cas_schedule_suspend_all().await?; Ok(HttpResponse::ok()) } diff --git a/src/daemon/http/dispatch/cas.rs b/src/daemon/http/dispatch/cas.rs index 36cd8631d..0fd4f21ae 100644 --- a/src/daemon/http/dispatch/cas.rs +++ b/src/daemon/http/dispatch/cas.rs @@ -14,7 +14,6 @@ use crate::api::import::ImportChild; use crate::api::history::CommandHistoryCriteria; use crate::api::roa::RoaConfigurationUpdates; use crate::commons::error::Error; -use crate::commons::eventsourcing::AggregateStoreError; use super::super::auth::Permission; use super::super::request::{PathIter, Request}; use super::super::response::HttpResponse; @@ -37,13 +36,13 @@ async fn index( request: Request<'_>, ) -> Result { match *request.method() { - Method::GET => index_get(request), + Method::GET => index_get(request).await, Method::POST => index_post(request).await, _ => Ok(HttpResponse::method_not_allowed()) } } -fn index_get( +async fn index_get( request: Request<'_>, ) -> Result { let (request, auth) = request.proceed_unchecked(); @@ -52,11 +51,13 @@ fn index_get( Ok(HttpResponse::json( &CertAuthList { cas: { - server.old_krill().ca_handles()?.filter_map(|handle| { - auth.has_permission( - Permission::CaRead, Some(&handle) - ).then_some(CertAuthSummary { handle }) - }).collect() + server.krill().ca_handles().await?.into_iter().filter_map( + |handle| { + auth.has_permission( + Permission::CaRead, Some(&handle) + ).then_some(CertAuthSummary { handle }) + } + ).collect() } } )) @@ -69,7 +70,7 @@ async fn index_post( Permission::CaCreate, None )?; let (server, init) = request.read_json().await?; - server.old_krill().ca_init(init)?; + server.krill().ca_init(init).await?; Ok(HttpResponse::ok()) } @@ -88,15 +89,15 @@ pub async fn ca( Some("aspas") => aspas(request, path, ca).await, Some("bgpsec") => bgpsec(request, path, ca).await, Some("children") => children(request, path, ca).await, - Some("history") => history(request, path, ca), - Some("id") => id(request, path, ca), - Some("issues") => issues(request, path, ca), - Some("keys") => keys(request, path, ca), + Some("history") => history(request, path, ca).await, + Some("id") => id(request, path, ca).await, + Some("issues") => issues(request, path, ca).await, + Some("keys") => keys(request, path, ca).await, Some("parents") => parents(request, path, ca).await, Some("repo") => repo(request, path, ca).await, Some("routes") => routes(request, path, ca).await, - Some("stats") => stats(request, path, ca), - Some("sync") => sync(request, path, ca), + Some("stats") => stats(request, path, ca).await, + Some("sync") => sync(request, path, ca).await, _ => Ok(HttpResponse::not_found()) } } @@ -112,7 +113,7 @@ async fn ca_index( )?; let server = request.empty()?; Ok(HttpResponse::json( - &server.old_krill().ca_info(&ca)? + &server.krill().ca_info(ca).await? )) } Method::DELETE => { @@ -153,7 +154,7 @@ async fn aspas_index( )?; let server = request.empty()?; Ok(HttpResponse::json( - &server.old_krill().ca_aspas_definitions_show(&ca)? + &server.krill().ca_aspas_definitions_show(ca).await? )) } Method::POST => { @@ -161,9 +162,9 @@ async fn aspas_index( Permission::AspasUpdate, Some(&ca) )?; let (server, updates) = request.read_json().await?; - server.old_krill().ca_aspas_definitions_update( - ca, updates, auth.actor(), - )?; + server.krill().ca_aspas_definitions_update( + ca, updates, auth.actor().clone(), + ).await?; Ok(HttpResponse::ok()) } _ => Ok(HttpResponse::method_not_allowed()) @@ -183,9 +184,9 @@ async fn aspas_as( Permission::AspasUpdate, Some(&ca) )?; let (server, update) = request.read_json().await?; - server.old_krill().ca_aspas_update_aspa( - ca, customer, update, auth.actor() - )?; + server.krill().ca_aspas_update_aspa( + ca, customer, update, auth.actor().clone() + ).await?; Ok(HttpResponse::ok()) } Method::DELETE => { @@ -193,14 +194,14 @@ async fn aspas_as( Permission::AspasUpdate, Some(&ca) )?; let server = request.empty()?; - server.old_krill().ca_aspas_definitions_update( + server.krill().ca_aspas_definitions_update( ca, AspaDefinitionUpdates { add_or_replace: Vec::new(), remove: vec![customer] }, - auth.actor(), - )?; + auth.actor().clone(), + ).await?; Ok(HttpResponse::ok()) } _ => Ok(HttpResponse::method_not_allowed()) @@ -223,7 +224,7 @@ async fn bgpsec( )?; let server = request.empty()?; Ok(HttpResponse::json( - &server.old_krill().ca_bgpsec_definitions_show(&ca)? + &server.krill().ca_bgpsec_definitions_show(ca).await? )) } Method::POST => { @@ -231,9 +232,9 @@ async fn bgpsec( Permission::BgpsecUpdate, Some(&ca) )?; let (server, updates) = request.read_json().await?; - server.old_krill().ca_bgpsec_definitions_update( - ca, updates, auth.actor() - )?; + server.krill().ca_bgpsec_definitions_update( + ca, updates, auth.actor().clone() + ).await?; Ok(HttpResponse::ok()) } _ => Ok(HttpResponse::method_not_allowed()) @@ -264,7 +265,9 @@ async fn children_index( )?; let (server, child_req) = request.read_json().await?; Ok(HttpResponse::json( - &server.old_krill().ca_add_child(&ca, child_req, auth.actor())? + &server.krill().ca_add_child( + ca, child_req, auth.actor().clone() + ).await? )) } @@ -277,12 +280,14 @@ async fn children_child( match path.next() { None => children_child_index(request, ca, child).await, Some("contact") | Some("parent_response.json") => { - children_child_contact(request, path, ca, child) + children_child_contact(request, path, ca, child).await } Some("parent_response.xml") => { - children_child_contact_xml(request, path, ca, child) + children_child_contact_xml(request, path, ca, child).await + } + Some("export") => { + children_child_export(request, path, ca, child).await } - Some("export") => children_child_export(request, path, ca, child), Some("import") => { children_child_import(request, path, ca, child).await } @@ -302,7 +307,7 @@ async fn children_child_index( )?; let server = request.empty()?; Ok(HttpResponse::json( - &server.old_krill().ca_child_show(&ca, &child)? + &server.krill().ca_child_show(ca, child).await? )) } Method::POST => { @@ -310,9 +315,9 @@ async fn children_child_index( Permission::CaUpdate, Some(&ca) )?; let (server, child_req) = request.read_json().await?; - server.old_krill().ca_child_update( - &ca, child, child_req, auth.actor() - )?; + server.krill().ca_child_update( + ca, child, child_req, auth.actor().clone() + ).await?; Ok(HttpResponse::ok()) } Method::DELETE => { @@ -320,14 +325,16 @@ async fn children_child_index( Permission::CaUpdate, Some(&ca) )?; let server = request.empty()?; - server.old_krill().ca_child_remove(&ca, child, auth.actor())?; + server.krill().ca_child_remove( + ca, child, auth.actor().clone() + ).await?; Ok(HttpResponse::ok()) } _ => Ok(HttpResponse::method_not_allowed()) } } -fn children_child_contact( +async fn children_child_contact( request: Request<'_>, path: PathIter<'_>, ca: CaHandle, @@ -340,11 +347,11 @@ fn children_child_contact( )?; let server = request.empty()?; Ok(HttpResponse::json( - &server.old_krill().ca_parent_response(&ca, child)? + &server.krill().ca_parent_response(ca, child).await? )) } -fn children_child_contact_xml( +async fn children_child_contact_xml( request: Request<'_>, path: PathIter<'_>, ca: CaHandle, @@ -356,11 +363,11 @@ fn children_child_contact_xml( Permission::CaRead, Some(&ca) )?; let server = request.empty()?; - let res = server.old_krill().ca_parent_response(&ca, child)?; + let res = server.krill().ca_parent_response(ca, child).await?; Ok(HttpResponse::xml(res.to_xml_vec())) } -fn children_child_export( +async fn children_child_export( request: Request<'_>, path: PathIter<'_>, ca: CaHandle, @@ -373,7 +380,7 @@ fn children_child_export( )?; let server = request.empty()?; Ok(HttpResponse::json( - &server.old_krill().ca_child_export(&ca, &child)? + &server.krill().ca_child_export(ca, child).await? )) } @@ -396,26 +403,26 @@ async fn children_child_import( } )) } - server.old_krill().ca_child_import(&ca, import, auth.actor())?; + server.krill().ca_child_import(ca, import, auth.actor().clone()).await?; Ok(HttpResponse::ok()) } //------------ /api/v1/cas/{ca}/history -------------------------------------- -fn history( +async fn history( request: Request<'_>, mut path: PathIter<'_>, ca: CaHandle, ) -> Result { match path.next() { - Some("commands") => history_commands(request, path, ca), - Some("details") => history_details(request, path, ca), + Some("commands") => history_commands(request, path, ca).await, + Some("details") => history_details(request, path, ca).await, _ => Ok(HttpResponse::not_found()) } } -fn history_commands( +async fn history_commands( request: Request<'_>, path: PathIter<'_>, ca: CaHandle, @@ -433,17 +440,17 @@ fn history_commands( let server = request.empty()?; Ok(HttpResponse::json( - &server.old_krill().ca_history( - &ca, + &server.krill().ca_history( + ca, CommandHistoryCriteria { before, after, offset, rows_limit, .. Default::default() } - )? + ).await? )) } -fn history_details( +async fn history_details( request: Request<'_>, mut path: PathIter<'_>, ca: CaHandle, @@ -456,47 +463,42 @@ fn history_details( )?; let server = request.empty()?; - Ok(HttpResponse::json( - &server.old_krill().ca_command_details(&ca, version).map_err(|err| { - match err { - Error::AggregateStoreError( - AggregateStoreError::UnknownCommand(..) - ) => { - HttpResponse::not_found() - }, - err => { - HttpResponse::response_from_error(err) - } - } - })? - )) + let res = match server.krill().ca_command_details(ca, version).await { + Ok(Some(res)) => res, + Ok(None) => return Err(HttpResponse::not_found().into()), + Err(err) => return Err(err.into()) + }; + + Ok(HttpResponse::json(&res)) } //------------ /api/v1/cas/{ca}/id ------------------------------------------- -fn id( +async fn id( request: Request<'_>, mut path: PathIter<'_>, ca: CaHandle, ) -> Result { match path.next() { - None => id_index(request, ca), + None => id_index(request, ca).await, Some("child_request.json") => { - id_child_request_json(request, path, ca) + id_child_request_json(request, path, ca).await + } + Some("child_request.xml") => { + id_child_request_xml(request, path, ca).await } - Some("child_request.xml") => id_child_request_xml(request, path, ca), Some("publisher_request.json") => { - id_publisher_request_json(request, path, ca) + id_publisher_request_json(request, path, ca).await } Some("publisher_request.xml") => { - id_publisher_request_xml(request, path, ca) + id_publisher_request_xml(request, path, ca).await } _ => Ok(HttpResponse::not_found()) } } -fn id_index( +async fn id_index( request: Request<'_>, ca: CaHandle, ) -> Result { @@ -505,11 +507,11 @@ fn id_index( Permission::CaUpdate, Some(&ca) )?; let server = request.empty()?; - server.old_krill().ca_update_id(ca, auth.actor())?; + server.krill().ca_update_id(ca, auth.actor().clone()).await?; Ok(HttpResponse::ok()) } -fn id_child_request_json( +async fn id_child_request_json( request: Request<'_>, path: PathIter<'_>, ca: CaHandle, @@ -521,11 +523,11 @@ fn id_child_request_json( )?; let server = request.empty()?; Ok(HttpResponse::json( - &server.old_krill().ca_child_req(&ca)? + &server.krill().ca_child_req(ca).await? )) } -fn id_child_request_xml( +async fn id_child_request_xml( request: Request<'_>, path: PathIter<'_>, ca: CaHandle, @@ -537,12 +539,12 @@ fn id_child_request_xml( )?; let server = request.empty()?; Ok(HttpResponse::xml( - server.old_krill().ca_child_req(&ca)?.to_xml_vec() + server.krill().ca_child_req(ca).await?.to_xml_vec() )) } -fn id_publisher_request_json( - request: Request, +async fn id_publisher_request_json( + request: Request<'_>, path: PathIter<'_>, ca: CaHandle, ) -> Result { @@ -553,11 +555,11 @@ fn id_publisher_request_json( )?; let server = request.empty()?; Ok(HttpResponse::json( - &server.old_krill().ca_publisher_req(&ca)? + &server.krill().ca_publisher_req(ca).await? )) } -fn id_publisher_request_xml( +async fn id_publisher_request_xml( request: Request<'_>, path: PathIter<'_>, ca: CaHandle, @@ -569,14 +571,14 @@ fn id_publisher_request_xml( )?; let server = request.empty()?; Ok(HttpResponse::xml( - server.old_krill().ca_publisher_req(&ca)?.to_xml_vec() + server.krill().ca_publisher_req(ca).await?.to_xml_vec() )) } //------------ /api/v1/cas/{ca}/issues --------------------------------------- -fn issues( +async fn issues( request: Request<'_>, path: PathIter<'_>, ca: CaHandle, @@ -588,26 +590,26 @@ fn issues( )?; let server = request.empty()?; Ok(HttpResponse::json( - &server.old_krill().ca_issues(&ca)? + &server.krill().ca_issues(ca).await? )) } //------------ /api/v1/cas/{ca}/keys ----------------------------------------- -fn keys( +async fn keys( request: Request<'_>, mut path: PathIter<'_>, ca: CaHandle, ) -> Result { match path.next() { - Some("roll_init") => keys_roll_init(request, path, ca), - Some("roll_activate") => keys_roll_activate(request, path, ca), + Some("roll_init") => keys_roll_init(request, path, ca).await, + Some("roll_activate") => keys_roll_activate(request, path, ca).await, _ => Ok(HttpResponse::not_found()) } } -fn keys_roll_init( +async fn keys_roll_init( request: Request<'_>, path: PathIter<'_>, ca: CaHandle, @@ -618,11 +620,11 @@ fn keys_roll_init( Permission::CaUpdate, Some(&ca) )?; let server = request.empty()?; - server.old_krill().ca_keyroll_init(ca, auth.actor())?; + server.krill().ca_keyroll_init(ca, auth.actor().clone()).await?; Ok(HttpResponse::ok()) } -fn keys_roll_activate( +async fn keys_roll_activate( request: Request<'_>, path: PathIter<'_>, ca: CaHandle, @@ -633,7 +635,7 @@ fn keys_roll_activate( Permission::CaUpdate, Some(&ca) )?; let server = request.empty()?; - server.old_krill().ca_keyroll_activate(ca, auth.actor())?; + server.krill().ca_keyroll_activate(ca, auth.actor().clone()).await?; Ok(HttpResponse::ok()) } @@ -662,7 +664,7 @@ async fn parents_index( )?; let server = request.empty()?; Ok(HttpResponse::json( - &server.old_krill().ca_status(&ca)?.into_parents() + &server.krill().ca_parent_status(ca).await? )) } Method::POST => { @@ -694,7 +696,7 @@ async fn parents_parent( )?; let server = request.empty()?; Ok(HttpResponse::json( - &server.old_krill().ca_my_parent_contact(&ca, &parent)? + &server.krill().ca_parent_contact(ca, parent).await? )) } Method::POST => { @@ -772,7 +774,7 @@ async fn repo( ) -> Result { match path.next() { None => repo_index(request, ca).await, - Some("status") => repo_status(request, path, ca), + Some("status") => repo_status(request, path, ca).await, _ => Ok(HttpResponse::not_found()) } } @@ -788,7 +790,7 @@ async fn repo_index( )?; let server = request.empty()?; Ok(HttpResponse::json( - &server.old_krill().ca_repo_details(&ca)? + &server.krill().ca_repo_details(ca).await? )) } Method::POST => { @@ -830,7 +832,7 @@ pub fn extract_repository_contact( } } -fn repo_status( +async fn repo_status( request: Request<'_>, path: PathIter<'_>, ca: CaHandle, @@ -842,7 +844,7 @@ fn repo_status( )?; let server = request.empty()?; Ok(HttpResponse::json( - &server.old_krill().ca_status(&ca)?.into_repo() + &server.krill().ca_repo_status(ca).await? )) } @@ -873,7 +875,7 @@ async fn routes_index( )?; let server = request.empty()?; Ok(HttpResponse::json( - &server.old_krill().ca_routes_show(&ca)? + &server.krill().ca_routes_show(ca).await? )) } Method::POST => { @@ -881,7 +883,9 @@ async fn routes_index( Permission::RoutesUpdate, Some(&ca) )?; let (server, updates) = request.read_json().await?; - server.old_krill().ca_routes_update(ca, updates, auth.actor())?; + server.krill().ca_routes_update( + ca, updates, auth.actor().clone() + ).await?; Ok(HttpResponse::ok()) } _ => Ok(HttpResponse::method_not_allowed()) @@ -900,15 +904,15 @@ async fn routes_try( )?; let (server, mut updates) = request.read_json::().await?; - let effect = server.old_krill().ca_routes_bgp_dry_run( - &ca, updates.clone() - )?; + let effect = server.krill().ca_routes_bgp_dry_run( + ca.clone(), updates.clone() + ).await?; if effect.contains_invalids() { updates.set_explicit_max_length(); let resources = updates.affected_prefixes(); - let suggestion = server.old_krill().ca_routes_bgp_suggest( - &ca, Some(resources) - )?; + let suggestion = server.krill().ca_routes_bgp_suggest( + ca, Some(resources) + ).await?; Ok(HttpResponse::json( &BgpAnalysisAdvice { effect, suggestion, @@ -916,7 +920,9 @@ async fn routes_try( )) } else { - server.old_krill().ca_routes_update(ca, updates, auth.actor())?; + server.krill().ca_routes_update( + ca, updates, auth.actor().clone() + ).await?; Ok(HttpResponse::ok()) } } @@ -927,14 +933,14 @@ async fn routes_analysis( ca: CaHandle, ) -> Result { match path.next() { - Some("full") => routes_analysis_full(request, path, ca), + Some("full") => routes_analysis_full(request, path, ca).await, Some("dryrun") => routes_analysis_dryrun(request, path, ca).await, Some("suggest") => routes_analysis_suggest(request, path, ca).await, _ => Ok(HttpResponse::not_found()) } } -fn routes_analysis_full( +async fn routes_analysis_full( request: Request<'_>, path: PathIter<'_>, ca: CaHandle, @@ -946,7 +952,7 @@ fn routes_analysis_full( )?; let server = request.empty()?; Ok(HttpResponse::json( - &server.old_krill().ca_routes_bgp_analysis(&ca)? + &server.krill().ca_routes_bgp_analysis(ca).await? )) } @@ -962,7 +968,7 @@ async fn routes_analysis_dryrun( )?; let (server, updates) = request.read_json().await?; Ok(HttpResponse::json( - &server.old_krill().ca_routes_bgp_dry_run(&ca, updates)? + &server.krill().ca_routes_bgp_dry_run(ca, updates).await? )) } @@ -979,7 +985,7 @@ async fn routes_analysis_suggest( )?; let server = request.empty()?; Ok(HttpResponse::json( - &server.old_krill().ca_routes_bgp_suggest(&ca, None)? + &server.krill().ca_routes_bgp_suggest(ca, None).await? )) } Method::POST => { @@ -988,9 +994,9 @@ async fn routes_analysis_suggest( )?; let (server, resources) = request.read_json().await?; Ok(HttpResponse::json( - &server.old_krill().ca_routes_bgp_suggest( - &ca, Some(resources) - )? + &server.krill().ca_routes_bgp_suggest( + ca, Some(resources) + ).await? )) } _ => Ok(HttpResponse::method_not_allowed()) @@ -1000,29 +1006,31 @@ async fn routes_analysis_suggest( //------------ /api/v1/cas/{ca}/stats ---------------------------------------- -fn stats( +async fn stats( request: Request<'_>, mut path: PathIter<'_>, ca: CaHandle, ) -> Result { match path.next() { - Some("children") => stats_children(request, path, ca), + Some("children") => stats_children(request, path, ca).await, _ => Ok(HttpResponse::not_found()) } } -fn stats_children( +async fn stats_children( request: Request<'_>, mut path: PathIter<'_>, ca: CaHandle, ) -> Result { match path.next() { - Some("connections") => stats_children_connections(request, path, ca), + Some("connections") => { + stats_children_connections(request, path, ca).await + }, _ => Ok(HttpResponse::not_found()) } } -fn stats_children_connections( +async fn stats_children_connections( request: Request<'_>, path: PathIter<'_>, ca: CaHandle, @@ -1034,26 +1042,26 @@ fn stats_children_connections( )?; let server = request.empty()?; Ok(HttpResponse::json( - &server.old_krill().ca_stats_child_connections(&ca)? + &server.krill().ca_stats_child_connections(ca).await? )) } //------------ /api/v1/cas/{ca}/sync ----------------------------------------- -fn sync( +async fn sync( request: Request<'_>, mut path: PathIter<'_>, ca: CaHandle, ) -> Result { match path.next() { - Some("parents") => sync_parents(request, path, ca), - Some("repo") => sync_repo(request, path, ca), + Some("parents") => sync_parents(request, path, ca).await, + Some("repo") => sync_repo(request, path, ca).await, _ => Ok(HttpResponse::not_found()) } } -fn sync_parents( +async fn sync_parents( request: Request<'_>, path: PathIter<'_>, ca: CaHandle, @@ -1064,11 +1072,11 @@ fn sync_parents( Permission::CaUpdate, Some(&ca) )?; let server = request.empty()?; - server.old_krill().cas_refresh_single(ca)?; + server.krill().cas_refresh_single(ca).await?; Ok(HttpResponse::ok()) } -fn sync_repo( +async fn sync_repo( request: Request<'_>, path: PathIter<'_>, ca: CaHandle, @@ -1079,7 +1087,7 @@ fn sync_repo( Permission::CaUpdate, Some(&ca) )?; let server = request.empty()?; - server.old_krill().cas_repo_sync_single(&ca)?; + server.krill().ca_sync_repo(ca).await?; Ok(HttpResponse::ok()) } diff --git a/src/daemon/http/dispatch/error.rs b/src/daemon/http/dispatch/error.rs index 47f5e028c..78cc6002f 100644 --- a/src/daemon/http/dispatch/error.rs +++ b/src/daemon/http/dispatch/error.rs @@ -1,6 +1,7 @@ //! Dispatch error handling. use crate::commons::error::{Error, FatalError}; +use crate::server::manager::RunError; use super::super::response::HttpResponse; @@ -30,6 +31,12 @@ impl From for DispatchError { } } +impl From for DispatchError { + fn from(src: RunError) -> Self { + Self::Response(src.into()) + } +} + impl From for DispatchError { fn from(src: Error) -> Self { Self::Response(HttpResponse::response_from_error(src)) diff --git a/src/daemon/http/dispatch/metrics.rs b/src/daemon/http/dispatch/metrics.rs index 48d1f74e7..6c91f6298 100644 --- a/src/daemon/http/dispatch/metrics.rs +++ b/src/daemon/http/dispatch/metrics.rs @@ -59,7 +59,7 @@ pub async fn dispatch( server.authorizer().login_session_cache_size().await, ); - if let Ok(cas_stats) = server.old_krill().cas_stats() { + if let Ok(cas_stats) = server.krill().cas_stats().await { target.single( Metric::gauge("cas", "number of CAs in Krill"), cas_stats.len() @@ -67,13 +67,10 @@ pub async fn dispatch( if !server.config().metrics.metrics_hide_ca_details { - let mut ca_status_map = HashMap::new(); - - for ca in cas_stats.keys() { - if let Ok(ca_status) = server.old_krill().ca_status(ca) { - ca_status_map.insert(ca.clone(), ca_status); - } - } + let ca_status_map = match server.krill().cas_status_map().await { + Ok(map) => map, + Err(_) => HashMap::new(), + }; let metric = Metric::gauge( "ca_parent_success", @@ -386,7 +383,7 @@ pub async fn dispatch( } } - if let Ok(stats) = server.old_krill().repo_stats() { + if let Ok(stats) = server.krill().repo_stats().await { target.single( Metric::gauge( "repo_publisher", diff --git a/src/daemon/http/dispatch/pubd.rs b/src/daemon/http/dispatch/pubd.rs index eb3b54135..36c5a69e1 100644 --- a/src/daemon/http/dispatch/pubd.rs +++ b/src/daemon/http/dispatch/pubd.rs @@ -20,7 +20,7 @@ pub async fn dispatch( Some("delete") => delete(request, path).await, Some("init") => init(request, path).await, Some("publishers") => publishers(request, path).await, - Some("session_reset") => session_reset(request, path), + Some("session_reset") => session_reset(request, path).await, Some("stale") => stale(request, path).await, _ => Ok(HttpResponse::not_found()) } @@ -39,7 +39,7 @@ async fn delete( Permission::PubAdmin, None )?; let (server, criteria) = request.read_json().await?; - server.old_krill().delete_matching_files(criteria)?; + server.krill().delete_matching_files(criteria).await?; Ok(HttpResponse::ok()) } @@ -57,7 +57,7 @@ async fn init( Permission::PubAdmin, None )?; let (server, uris) = request.read_json().await?; - server.old_krill().repository_init(uris)?; + server.krill().repository_init(uris).await?; Ok(HttpResponse::ok()) } Method::DELETE => { @@ -65,7 +65,7 @@ async fn init( Permission::PubAdmin, None )?; let server = request.empty()?; - server.old_krill().repository_clear()?; + server.krill().repository_clear().await?; Ok(HttpResponse::ok()) } _ => Ok(HttpResponse::method_not_allowed()) @@ -82,7 +82,7 @@ async fn publishers( match path.parse_opt_next()? { None => publishers_index(request).await, Some(publisher) => { - publishers_publisher(request, path, publisher) + publishers_publisher(request, path, publisher).await } } } @@ -99,7 +99,7 @@ async fn publishers_index( Ok(HttpResponse::json( &PublisherList { publishers: { - server.old_krill().publishers()?.into_iter().map( + server.krill().publishers().await?.into_iter().map( PublisherSummary::from_handle ).collect() } @@ -112,31 +112,33 @@ async fn publishers_index( )?; let (server, pbl) = request.read_json().await?; Ok(HttpResponse::json( - &server.old_krill().add_publisher(pbl, auth.actor())? + &server.krill().add_publisher( + pbl, auth.actor().clone() + ).await? )) } _ => Ok(HttpResponse::method_not_allowed()) } } -fn publishers_publisher( +async fn publishers_publisher( request: Request<'_>, mut path: PathIter<'_>, publisher: PublisherHandle, ) -> Result { match path.next() { - None => publishers_publisher_index(request, publisher), + None => publishers_publisher_index(request, publisher).await, Some("response.json") => { - publishers_publisher_response(request, path, publisher) + publishers_publisher_response(request, path, publisher).await } Some("response.xml") => { - publishers_publisher_response_xml(request, path, publisher) + publishers_publisher_response_xml(request, path, publisher).await } _ => Ok(HttpResponse::not_found()) } } -fn publishers_publisher_index( +async fn publishers_publisher_index( request: Request<'_>, publisher: PublisherHandle, ) -> Result { @@ -147,7 +149,7 @@ fn publishers_publisher_index( )?; let server = request.empty()?; Ok(HttpResponse::json( - &server.old_krill().get_publisher(publisher)? + &server.krill().get_publisher(publisher).await? )) } Method::DELETE => { @@ -155,14 +157,16 @@ fn publishers_publisher_index( Permission::PubDelete, None )?; let server = request.empty()?; - server.old_krill().remove_publisher(publisher, auth.actor())?; + server.krill().remove_publisher( + publisher, auth.actor().clone() + ).await?; Ok(HttpResponse::ok()) } _ => Ok(HttpResponse::method_not_allowed()) } } -fn publishers_publisher_response( +async fn publishers_publisher_response( request: Request<'_>, path: PathIter<'_>, publisher: PublisherHandle, @@ -172,11 +176,11 @@ fn publishers_publisher_response( let (request, _) = request.proceed_permitted(Permission::PubRead, None)?; let server = request.empty()?; Ok(HttpResponse::json( - &server.old_krill().repository_response(&publisher)? + &server.krill().repository_response(publisher).await? )) } -fn publishers_publisher_response_xml( +async fn publishers_publisher_response_xml( request: Request<'_>, path: PathIter<'_>, publisher: PublisherHandle, @@ -186,14 +190,14 @@ fn publishers_publisher_response_xml( let (request, _) = request.proceed_permitted(Permission::PubRead, None)?; let server = request.empty()?; Ok(HttpResponse::xml( - server.old_krill().repository_response(&publisher)?.to_xml_vec() + server.krill().repository_response(publisher).await?.to_xml_vec() )) } //------------ /api/v1/pubd/session_reset ------------------------------------ -fn session_reset( +async fn session_reset( request: Request<'_>, path: PathIter<'_>, ) -> Result { @@ -201,7 +205,7 @@ fn session_reset( request.check_post()?; let (request, _) = request.proceed_permitted(Permission::PubAdmin, None)?; let server = request.empty()?; - server.old_krill().repository_session_reset()?; + server.krill().repository_session_reset().await?; Ok(HttpResponse::ok()) } @@ -217,7 +221,7 @@ async fn stale( request.check_get()?; let (request, _) = request.proceed_permitted( Permission::PubList, None)?; let server = request.empty()?; - let stats = server.old_krill().repo_stats()?; + let stats = server.krill().repo_stats().await?; Ok(HttpResponse::json( &PublisherList { publishers: { diff --git a/src/daemon/http/dispatch/root.rs b/src/daemon/http/dispatch/root.rs index ac2281855..37ea2b5e4 100644 --- a/src/daemon/http/dispatch/root.rs +++ b/src/daemon/http/dispatch/root.rs @@ -21,10 +21,10 @@ pub async fn dispatch_request( Some("metrics") => super::metrics::dispatch(request, path).await, Some("rfc8181") => rfc8181(request, path).await, Some("rfc6492") => rfc6492(request, path).await, - Some("rrdp") => rrdp(request, path), + Some("rrdp") => rrdp(request, path).await, Some("stats") => super::stats::dispatch(request, path).await, - Some("ta") => ta(request, path), - Some("testbed.tal") => tal(request, path), + Some("ta") => ta(request, path).await, + Some("testbed.tal") => tal(request, path).await, Some("testbed") => super::testbed::dispatch(request, path).await, Some("ui") => ui(request, path), @@ -74,7 +74,7 @@ async fn rfc8181( let (request, _) = request.proceed_unchecked(); let (server, bytes) = request.read_rfc8181_bytes().await?; Ok(HttpResponse::rfc8181( - server.old_krill().rfc8181(publisher, bytes)? + server.krill().rfc8181(publisher, bytes).await? )) } @@ -97,24 +97,26 @@ async fn rfc6492( // always be the anonymous actor. Maybe the CA manager should // determine the actor when looking at the ID certificate? Ok(HttpResponse::rfc6492( - server.old_krill().rfc6492(ca , bytes, user_agent, auth.actor())? + server.krill().rfc6492( + ca , bytes, user_agent, auth.actor().clone() + ).await? )) } //------------ /ta ----------------------------------------------------------- -fn ta( +async fn ta( request: Request<'_>, mut path: PathIter<'_> ) -> Result { match path.next() { - Some("ta.tal") => tal(request, path), - Some("ta.cer") => ta_cer(request, path), + Some("ta.tal") => tal(request, path).await, + Some("ta.cer") => ta_cer(request, path).await, _ => Ok(HttpResponse::not_found()) } } -fn tal( +async fn tal( request: Request<'_>, path: PathIter<'_> ) -> Result { path.check_exhausted()?; @@ -122,26 +124,24 @@ fn tal( let (request, _) = request.proceed_unchecked(); let server = request.empty()?; Ok(HttpResponse::text( - server.old_krill().ta_cert_details()?.tal.to_string() + server.krill().ta_tal().await? )) } -fn ta_cer( +async fn ta_cer( request: Request<'_>, path: PathIter<'_> ) -> Result { path.check_exhausted()?; request.check_get()?; let (request, _) = request.proceed_unchecked(); let server = request.empty()?; - Ok(HttpResponse::cert( - server.old_krill().ta_cert_details()?.cert.to_bytes() - )) + Ok(HttpResponse::cert(server.krill().ta_cer().await?)) } //------------ /rrdp --------------------------------------------------------- -fn rrdp( +async fn rrdp( request: Request<'_>, path: PathIter<'_> ) -> Result { request.check_get()?; @@ -150,7 +150,9 @@ fn rrdp( let Some(remaining) = path.remaining() else { return Ok(HttpResponse::not_found()) }; - let path = match server.old_krill().resolve_rrdp_request_path(remaining)? { + let path = match server.krill().resolve_rrdp_request_path( + remaining.into() + ).await? { Some(path) => path, None => { return Ok(HttpResponse::not_found()) diff --git a/src/daemon/http/dispatch/stats.rs b/src/daemon/http/dispatch/stats.rs index 3eadeb55f..dad54e96f 100644 --- a/src/daemon/http/dispatch/stats.rs +++ b/src/daemon/http/dispatch/stats.rs @@ -13,7 +13,7 @@ pub async fn dispatch( ) -> Result { match path.next() { Some("info") => info(request, path), - Some("repo") => repo(request, path), + Some("repo") => repo(request, path).await, Some("cas") => cas(request, path).await, _ => Ok(HttpResponse::not_found()) } @@ -36,7 +36,7 @@ fn info( //------------ /stats/repo --------------------------------------------------- -fn repo( +async fn repo( request: Request<'_>, path: PathIter<'_>, ) -> Result { @@ -44,7 +44,7 @@ fn repo( request.check_get()?; let (request, _) = request.proceed_unchecked(); let server = request.empty()?; - Ok(HttpResponse::json(&server.old_krill().repo_stats()?)) + Ok(HttpResponse::json(&server.krill().repo_stats().await?)) } @@ -58,6 +58,6 @@ async fn cas( request.check_get()?; let (request, _) = request.proceed_unchecked(); let server = request.empty()?; - Ok(HttpResponse::json(&server.old_krill().cas_stats()?)) + Ok(HttpResponse::json(&server.krill().cas_stats().await?)) } diff --git a/src/daemon/http/dispatch/ta.rs b/src/daemon/http/dispatch/ta.rs index 167c42912..2d12c0098 100644 --- a/src/daemon/http/dispatch/ta.rs +++ b/src/daemon/http/dispatch/ta.rs @@ -31,8 +31,8 @@ async fn proxy( ) -> Result { match path.next() { Some("children") => proxy_children(request, path).await, - Some("id") => proxy_id(request, path), - Some("init") => proxy_init(request, path), + Some("id") => proxy_id(request, path).await, + Some("init") => proxy_init(request, path).await, Some("repo") => proxy_repo(request, path).await, Some("signer") => proxy_signer(request, path).await, _ => Ok(HttpResponse::not_found()) @@ -48,7 +48,7 @@ async fn proxy_children( ) -> Result { match path.parse_opt_next()? { None => proxy_children_index(request).await, - Some(child) => proxy_children_child(request, path, child), + Some(child) => proxy_children_child(request, path, child).await, } } @@ -69,14 +69,16 @@ async fn proxy_children_index( )?; let (server, child) = request.read_json().await?; Ok(HttpResponse::json( - &server.old_krill().ta_proxy_children_add(child, auth.actor())? + &server.krill().ta_proxy_children_add( + child, auth.actor().clone() + ).await? )) } _ => Ok(HttpResponse::method_not_allowed()) } } -fn proxy_children_child( +async fn proxy_children_child( request: Request<'_>, mut path: PathIter<'_>, child: ChildHandle, @@ -84,10 +86,10 @@ fn proxy_children_child( match path.next() { None => proxy_children_child_index(request, child), Some("parent_response.json") => { - proxy_children_child_response(request, path, child) + proxy_children_child_response(request, path, child).await } Some("parent_response.xml") => { - proxy_children_child_response_xml(request, path, child) + proxy_children_child_response_xml(request, path, child).await } _ => Ok(HttpResponse::not_found()) } @@ -114,7 +116,7 @@ fn proxy_children_child_index( } } -fn proxy_children_child_response( +async fn proxy_children_child_response( request: Request<'_>, path: PathIter<'_>, child: ChildHandle, @@ -126,11 +128,11 @@ fn proxy_children_child_response( )?; let server = request.empty()?; Ok(HttpResponse::json( - &server.old_krill().ca_parent_response(&ta_handle(), child)? + &server.krill().ca_parent_response(ta_handle(), child).await? )) } -fn proxy_children_child_response_xml( +async fn proxy_children_child_response_xml( request: Request<'_>, path: PathIter<'_>, child: ChildHandle, @@ -142,14 +144,16 @@ fn proxy_children_child_response_xml( )?; let server = request.empty()?; Ok(HttpResponse::xml( - server.old_krill().ca_parent_response(&ta_handle(), child)?.to_xml_vec() + server.krill().ca_parent_response( + ta_handle(), child + ).await?.to_xml_vec() )) } //------------ /api/v1/proxy/init -------------------------------------------- -fn proxy_init( +async fn proxy_init( request: Request<'_>, path: PathIter<'_>, ) -> Result { @@ -159,14 +163,14 @@ fn proxy_init( Permission::CaAdmin, None )?; let server = request.empty()?; - server.old_krill().ta_proxy_init()?; + server.krill().ta_proxy_init().await?; Ok(HttpResponse::ok()) } //------------ /api/v1/proxy/id ---------------------------------------------- -fn proxy_id( +async fn proxy_id( request: Request<'_>, path: PathIter<'_>, ) -> Result { @@ -177,7 +181,7 @@ fn proxy_id( )?; let server = request.empty()?; Ok(HttpResponse::json( - &server.old_krill().ta_proxy_id()? + &server.krill().ta_proxy_id().await? )) } @@ -190,8 +194,8 @@ async fn proxy_repo( ) -> Result { match path.next() { None => proxy_repo_index(request).await, - Some("request.json") => proxy_repo_request(request, path), - Some("request.xml") => proxy_repo_request_xml(request, path), + Some("request.json") => proxy_repo_request(request, path).await, + Some("request.xml") => proxy_repo_request_xml(request, path).await, _ => Ok(HttpResponse::not_found()) } } @@ -206,7 +210,7 @@ async fn proxy_repo_index( )?; let server = request.empty()?; Ok(HttpResponse::json( - &server.old_krill().ta_proxy_repository_contact()? + &server.krill().ta_proxy_repository_contact().await? )) } Method::POST => { @@ -217,14 +221,16 @@ async fn proxy_repo_index( let update = super::cas::extract_repository_contact( &ta_handle(), update )?; - server.old_krill().ta_proxy_repository_update(update, auth.actor())?; + server.krill().ta_proxy_repository_update( + update, auth.actor().clone() + ).await?; Ok(HttpResponse::ok()) } _ => Ok(HttpResponse::method_not_allowed()) } } -fn proxy_repo_request( +async fn proxy_repo_request( request: Request<'_>, path: PathIter<'_>, ) -> Result { @@ -233,11 +239,11 @@ fn proxy_repo_request( let (request, _) = request.proceed_permitted(Permission::CaAdmin, None)?; let server = request.empty()?; Ok(HttpResponse::json( - &server.old_krill().ta_proxy_publisher_request()? + &server.krill().ta_proxy_publisher_request().await? )) } -fn proxy_repo_request_xml( +async fn proxy_repo_request_xml( request: Request<'_>, path: PathIter<'_>, ) -> Result { @@ -246,7 +252,7 @@ fn proxy_repo_request_xml( let (request, _) = request.proceed_permitted(Permission::CaAdmin, None)?; let server = request.empty()?; Ok(HttpResponse::xml( - server.old_krill().ta_proxy_publisher_request()?.to_xml_vec() + server.krill().ta_proxy_publisher_request().await?.to_xml_vec() )) } @@ -259,7 +265,7 @@ async fn proxy_signer( ) -> Result { match path.next() { Some("add") => proxy_signer_add(request, path).await, - Some("request") => proxy_signer_request(request, path), + Some("request") => proxy_signer_request(request, path).await, Some("response") => proxy_signer_response(request, path).await, Some("update") => proxy_signer_update(request, path).await, _ => Ok(HttpResponse::not_found()) @@ -276,11 +282,11 @@ async fn proxy_signer_add( Permission::CaAdmin, None )?; let (server, info) = request.read_json().await?; - server.old_krill().ta_proxy_signer_add(info, auth.actor())?; + server.krill().ta_proxy_signer_add(info, auth.actor().clone()).await?; Ok(HttpResponse::ok()) } -fn proxy_signer_request( +async fn proxy_signer_request( request: Request<'_>, path: PathIter<'_>, ) -> Result { @@ -292,7 +298,7 @@ fn proxy_signer_request( )?; let server = request.empty()?; Ok(HttpResponse::json( - &server.old_krill().ta_proxy_signer_get_request()? + &server.krill().ta_proxy_signer_get_request().await? )) } Method::POST => { @@ -301,9 +307,9 @@ fn proxy_signer_request( )?; let server = request.empty()?; Ok(HttpResponse::json( - &server.old_krill().ta_proxy_signer_make_request( - auth.actor() - )? + &server.krill().ta_proxy_signer_make_request( + auth.actor().clone() + ).await? )) } _ => Ok(HttpResponse::method_not_allowed()) @@ -320,7 +326,9 @@ async fn proxy_signer_response( Permission::CaAdmin, None )?; let (server, response) = request.read_json().await?; - server.old_krill().ta_proxy_signer_process_response(response, auth.actor())?; + server.krill().ta_proxy_signer_process_response( + response, auth.actor().clone() + ).await?; Ok(HttpResponse::ok()) } @@ -334,7 +342,7 @@ async fn proxy_signer_update( Permission::CaAdmin, None )?; let (server, info) = request.read_json().await?; - server.old_krill().ta_proxy_signer_update(info, auth.actor())?; + server.krill().ta_proxy_signer_update(info, auth.actor().clone()).await?; Ok(HttpResponse::ok()) } diff --git a/src/daemon/http/dispatch/testbed.rs b/src/daemon/http/dispatch/testbed.rs index d19a19459..6b3ad12fd 100644 --- a/src/daemon/http/dispatch/testbed.rs +++ b/src/daemon/http/dispatch/testbed.rs @@ -72,7 +72,7 @@ async fn children( ) -> Result { match path.parse_opt_next()? { None => children_index(request).await, - Some(child) => children_child(request, path, child), + Some(child) => children_child(request, path, child).await, } } @@ -83,40 +83,40 @@ async fn children_index( let (request, _) = request.proceed_unchecked(); let (server, child) = request.read_json().await?; Ok(HttpResponse::json( - &server.old_krill().ca_add_child( - &testbed_ca_handle(), child, &Actor::anonymous() - )? + &server.krill().ca_add_child( + testbed_ca_handle(), child, Actor::anonymous() + ).await? )) } -fn children_child( +async fn children_child( request: Request<'_>, mut path: PathIter<'_>, child: ChildHandle, ) -> Result { match path.next() { - None => children_child_index(request, child), + None => children_child_index(request, child).await, Some("parent_response.xml") => { - children_child_response(request, path, child) + children_child_response(request, path, child).await } _ => Ok(HttpResponse::not_found()) } } -fn children_child_index( +async fn children_child_index( request: Request<'_>, child: ChildHandle, ) -> Result { request.check_delete()?; let (request, _) = request.proceed_unchecked(); let server = request.empty()?; - server.old_krill().ca_child_remove( - &testbed_ca_handle(), child, &Actor::anonymous() - )?; + server.krill().ca_child_remove( + testbed_ca_handle(), child, Actor::anonymous() + ).await?; Ok(HttpResponse::ok()) } -fn children_child_response( +async fn children_child_response( request: Request<'_>, path: PathIter<'_>, child: ChildHandle, @@ -126,9 +126,9 @@ fn children_child_response( let (request, _) = request.proceed_unchecked(); let server = request.empty()?; Ok(HttpResponse::xml( - server.old_krill().ca_parent_response( - &testbed_ca_handle(), child - )?.to_xml_vec() + server.krill().ca_parent_response( + testbed_ca_handle(), child + ).await?.to_xml_vec() )) } @@ -141,7 +141,9 @@ async fn publishers( ) -> Result { match path.parse_opt_next()? { None => publishers_index(request).await, - Some(publisher) => publishers_publisher(request, path, publisher), + Some(publisher) => { + publishers_publisher(request, path, publisher).await + } } } @@ -152,38 +154,36 @@ async fn publishers_index( let (request, _) = request.proceed_unchecked(); let (server, pbl) = request.read_json().await?; Ok(HttpResponse::json( - &server.old_krill().add_publisher(pbl, &Actor::anonymous())? + &server.krill().add_publisher(pbl, Actor::anonymous()).await? )) } -fn publishers_publisher( +async fn publishers_publisher( request: Request<'_>, mut path: PathIter<'_>, publisher: PublisherHandle, ) -> Result { match path.next() { - None => publishers_publisher_index(request, publisher), + None => publishers_publisher_index(request, publisher).await, Some("response.xml") => { - publishers_publisher_response(request, path, publisher) + publishers_publisher_response(request, path, publisher).await } _ => Ok(HttpResponse::not_found()) } } -fn publishers_publisher_index( +async fn publishers_publisher_index( request: Request<'_>, publisher: PublisherHandle, ) -> Result { request.check_delete()?; let (request, _) = request.proceed_unchecked(); let server = request.empty()?; - server.old_krill().remove_publisher( - publisher, &Actor::anonymous() - )?; + server.krill().remove_publisher(publisher, Actor::anonymous()).await?; Ok(HttpResponse::ok()) } -fn publishers_publisher_response( +async fn publishers_publisher_response( request: Request<'_>, path: PathIter<'_>, publisher: PublisherHandle, @@ -193,7 +193,7 @@ fn publishers_publisher_response( let (request, _) = request.proceed_unchecked(); let server = request.empty()?; Ok(HttpResponse::xml( - server.old_krill().repository_response(&publisher)?.to_xml_vec() + server.krill().repository_response(publisher).await?.to_xml_vec() )) } diff --git a/src/daemon/http/request.rs b/src/daemon/http/request.rs index b9a47b714..136480431 100644 --- a/src/daemon/http/request.rs +++ b/src/daemon/http/request.rs @@ -59,7 +59,7 @@ impl<'a> Request<'a> { /// Returns whether testbed mode is enabled. pub fn testbed_enabled(&self) -> bool { - self.server.old_krill().testbed_enabled() + self.server.config().testbed_enabled() } /// Returns the method of this request. diff --git a/src/daemon/http/response.rs b/src/daemon/http/response.rs index 5e6535e9a..9db768b8a 100644 --- a/src/daemon/http/response.rs +++ b/src/daemon/http/response.rs @@ -8,6 +8,7 @@ use serde::Serialize; use crate::api::admin::Token; use crate::api::status::ErrorResponse; use crate::commons::error::Error; +use crate::server::manager::RunError; //----------- ContentType ---------------------------------------------------- @@ -375,3 +376,18 @@ impl HttpResponse { } } +impl From for HttpResponse { + fn from(src: RunError) -> Self { + let body = serde_json::to_string( + &src.to_error_response() + ).unwrap().into(); + Response { + status: src.status(), + content_type: ContentType::Json.as_str(), + max_age: None, + body, + cause: Some(src.into()), + }.finalize() + } +} + diff --git a/src/daemon/http/server.rs b/src/daemon/http/server.rs index 87ec1f70e..f7bd1e53f 100644 --- a/src/daemon/http/server.rs +++ b/src/daemon/http/server.rs @@ -10,6 +10,7 @@ use crate::commons::KrillResult; use crate::commons::error::FatalError; use crate::config::Config; use crate::constants::KRILL_ENV_HTTP_LOG_INFO; +use crate::server::manager::KrillManager; use crate::server::oldmanager::OldManager; use super::auth::Authorizer; use super::dispatch::{DispatchError, dispatch_request}; @@ -22,6 +23,9 @@ use super::response::{HyperResponse, HttpResponse}; /// The Krill HTTP server. pub struct HttpServer { + /// The Krill server. + krill: KrillManager, + /// The Krill “business logic.” old_krill: OldManager, @@ -38,6 +42,7 @@ pub struct HttpServer { impl HttpServer { /// Creates a new server from a Krill manager and the configuration. pub fn new( + krill: KrillManager, old_krill: OldManager, config: Arc, runtime: &runtime::Handle, @@ -45,6 +50,7 @@ impl HttpServer { let authorizer = Authorizer::new(&config)?; authorizer.spawn_sweep(runtime); Ok(Self { + krill, old_krill, authorizer, config, @@ -94,6 +100,11 @@ impl HttpServer { } impl HttpServer { + /// Returns a reference to the Krill server. + pub(super) fn krill(&self) -> &KrillManager { + &self.krill + } + /// Returns a reference to the Krill manager. pub(super) fn old_krill(&self) -> &OldManager { &self.old_krill @@ -105,7 +116,7 @@ impl HttpServer { } /// Returns a reference to the configuration. - pub(super) fn config(&self) -> &Config { + pub fn config(&self) -> &Config { &self.config } diff --git a/src/daemon/start.rs b/src/daemon/start.rs index 33cb56c83..117db6e06 100644 --- a/src/daemon/start.rs +++ b/src/daemon/start.rs @@ -15,6 +15,7 @@ use crate::commons::version::KrillVersion; use crate::config::Config; use crate::constants::KRILL_ENV_UPGRADE_ONLY; use crate::server::properties::PropertiesManager; +use crate::server::manager::KrillManager; use crate::server::oldmanager::OldManager; use crate::upgrades::{ finalise_data_migration, post_start_upgrade, @@ -25,9 +26,11 @@ use super::http::server::HttpServer; pub async fn start_krill_daemon( - config: Arc, + config: Config, mut signal_running: Option>, ) -> Result<(), Error> { + let arc_config = Arc::new(config.clone()); + write_pid_file_or_die(&config); test_data_dirs_or_die(&config); @@ -82,13 +85,15 @@ pub async fn start_krill_daemon( // Create the Krill manager, this will create the necessary data // sub-directories if needed - let krill = OldManager::build(config.clone()).await?; + let old_krill = OldManager::build(arc_config.clone()).await?; + + let krill = KrillManager::new(config)?; // Call post-start upgrades to trigger any upgrade related runtime // actions, such as re-issuing ROAs because subject name strategy has // changed. if let Some(report) = upgrade_report { - post_start_upgrade(report, &krill).await?; + post_start_upgrade(report, &old_krill).await?; } // If the operator wanted to do the upgrade only, now is a good time to @@ -100,27 +105,27 @@ pub async fn start_krill_daemon( // Build the scheduler which will be responsible for executing // planned/triggered tasks - let scheduler = krill.build_scheduler(); + let scheduler = old_krill.build_scheduler(); let scheduler_future = scheduler.run(); // Create the HTTP server. let server = HttpServer::new( - krill, config.clone(), &runtime::Handle::current() + krill, old_krill, arc_config.clone(), &runtime::Handle::current() )?; // Create self-signed HTTPS cert if configured and not generated earlier. - if config.https_mode().is_generate_https_cert() { - tls_keys::create_key_cert_if_needed(config.tls_keys_dir()) + if server.config().https_mode().is_generate_https_cert() { + tls_keys::create_key_cert_if_needed(server.config().tls_keys_dir()) .map_err(|e| Error::HttpsSetup(format!("{e}")))?; } // Start a hyper server for the configured http sockets. let http_server_futures = futures_util::future::select_all( - config.socket_addresses().into_iter().map(|socket_addr| { + server.config().socket_addresses().into_iter().map(|socket_addr| { tokio::spawn(single_http_listener( server.clone(), socket_addr, - config.clone(), + arc_config.clone(), signal_running.take(), )) }), @@ -129,12 +134,12 @@ pub async fn start_krill_daemon( // Start a hyper server for the configured unix sockets. // We do not await these, as they are not required #[cfg(unix)] - if config.unix_socket_enabled() { - config.unix_socket().map(|path| { + if server.config().unix_socket_enabled() { + server.config().unix_socket().map(|path| { tokio::spawn(single_unix_listener( server.clone(), path.clone(), - config.clone(), + arc_config.clone(), signal_running.take(), )) }); diff --git a/src/server/ca/manager.rs b/src/server/ca/manager.rs index 4d8f5242d..3f974cad3 100644 --- a/src/server/ca/manager.rs +++ b/src/server/ca/manager.rs @@ -32,7 +32,7 @@ use crate::api::aspa::{ }; use crate::api::bgpsec::{BgpSecCsrInfoList, BgpSecDefinitionUpdates}; use crate::api::ca::{ - CertAuthIssues, CertAuthList, CertAuthSummary, ChildCaInfo, IdCertInfo, + CertAuthIssues, ChildCaInfo, IdCertInfo, ParentStatuses, ReceivedCert, RepoStatus, RtaName, Timestamp, }; use crate::api::history::{ @@ -58,7 +58,6 @@ use crate::constants::{ CASERVER_NS, STATUS_NS, TA_PROXY_SERVER_NS, TA_SIGNER_SERVER_NS, TA_NAME, ta_handle, }; -use crate::daemon::http::auth::{AuthInfo, Permission}; // XXX remove use crate::config::Config; use crate::server::mq::{now, Task, TaskQueue}; use crate::server::pubd::RepositoryManager; @@ -647,24 +646,6 @@ impl CaManager { Ok(self.ca_store.list()?) } - /// Returns the CAs that the given policy allows read access to. - pub fn ca_list( - &self, auth: &AuthInfo, - ) -> KrillResult { - Ok(CertAuthList { - cas: self.ca_store - .list()? - .into_iter() - .filter(|handle| { - auth.check_permission( - Permission::CaRead, Some(handle) - ).is_ok() - }) - .map(|handle| CertAuthSummary { handle }) - .collect(), - }) - } - /// Returns the CA by the given handle. /// /// Returns an error if the CA does not exist. @@ -833,7 +814,6 @@ impl CaManager { &self, ca: &CaHandle, req: AddChildRequest, - service_uri: &uri::Https, actor: &Actor, krill: &KrillRuntime, ) -> KrillResult { @@ -847,14 +827,14 @@ impl CaManager { ), krill )?; - self.ca_parent_response(ca, req.handle, service_uri) + self.ca_parent_response(ca, req.handle, krill.service_uri()) } else { let child_handle = req.handle.clone(); let add_child_cmd = TrustAnchorProxyCommand::add_child(ca, req, actor); self.send_ta_proxy_command(add_child_cmd, krill)?; - self.ca_parent_response(ca, child_handle, service_uri) + self.ca_parent_response(ca, child_handle, krill.service_uri()) } } diff --git a/src/server/manager.rs b/src/server/manager.rs index e5d9669b5..50d3b8e79 100644 --- a/src/server/manager.rs +++ b/src/server/manager.rs @@ -2,30 +2,55 @@ //! use std::{error, fmt}; +use std::collections::HashMap; +use std::path::PathBuf; +use bytes::Bytes; +use chrono::Duration; use hyper::StatusCode; -use rpki::ca::publication; +use rpki::ca::{idexchange, publication}; +use rpki::repository::resources::ResourceSet; use tokio::sync::oneshot; +use crate::api; use crate::api::status::ErrorResponse; +use crate::commons::actor::Actor; use crate::commons::error::KrillError; +use crate::commons::eventsourcing::AggregateStoreError; +use crate::config::Config; +use crate::constants::ta_handle; +use crate::server::ca::CaStatus; use super::runtime::{KrillRuntime, Errand}; -//------------ KrillServer --------------------------------------------------- +//------------ KrillManager -------------------------------------------------- -/// Provides access to a [`KrillManager`] from an async runtime. -/// -/// A value of this type is owned by the HTTP server and allows it to call -/// into Krill for processing requests. This can only be achieved via the -/// two methods [`run`][Self::run] and [`run_errand`][Self::run_errand] -/// which provide access to the [`KrillManager`] via a closure run on the -/// sync runtime. -/// -/// This type is cheaply clonable and does not need to be kept in an arc. -pub struct KrillServer { - manager: KrillManager, +#[derive(Clone)] +pub struct KrillManager { + krill_runtime: KrillRuntime, } -impl KrillServer { +impl KrillManager { + /// Create a new Krill server from the provided config. + pub fn new(_config: Config) -> Result { + todo!() + } + + /// Returns a reference to the config. + pub fn config(&self) -> &Config { + self.krill_runtime.config() + } + + /// Returns the system actor. + pub fn system_actor(&self) -> &Actor { + self.krill_runtime.system_actor() + } +} + + +/// # Low-level flow control +/// +/// The two methods in this section are hidden from users by the public +/// methods. +impl KrillManager { /// Runs a sync closure which provides an immediate result. /// /// The closure `op` is run on the sync runtime. It has access to the @@ -34,20 +59,19 @@ impl KrillServer { /// /// If, for whatever reason, the closure does not run to completion, /// an error is returned. - pub async fn run( + async fn run( &self, op: F ) -> Result where - F: FnOnce(&KrillManager) -> Result + Send + 'static, + F: FnOnce(&KrillRuntime) -> Result + Send + 'static, T: Send + 'static, - E: Into + Send + 'static { let (tx, rx) = oneshot::channel(); - let manager = self.manager.clone(); - self.manager.runtime.spawn_blocking(move || { - let _ = tx.send(op(&manager)); + let runtime = self.krill_runtime.clone(); + self.krill_runtime.spawn_blocking(move || { + let _ = tx.send(op(&runtime)); }); - rx.await?.map_err(Into::into) + rx.await? } /// Runs an errand using the `KrillManager`. @@ -64,30 +88,876 @@ impl KrillServer { /// /// If, for whatever reason, the closure or returned errand do not run to /// completion, an error is returned. - pub async fn run_errand( + async fn _run_errand( &self, op: F ) -> Result where - F: FnOnce(&KrillManager) -> P + Send + 'static, - P: Errand>, + F: FnOnce(&KrillRuntime) -> P + Send + 'static, + P: Errand>, T: Send + 'static, - E: Into + Send + 'static { let (tx, rx) = oneshot::channel(); - let manager = self.manager.clone(); - self.manager.runtime.spawn_blocking(move || { - op(&manager).finish(tx); + let runtime = self.krill_runtime.clone(); + self.krill_runtime.spawn_blocking(move || { + op(&runtime).finish(tx); }); - rx.await?.map_err(Into::into) + rx.await? } } -//------------ KrillManager -------------------------------------------------- +/// # Managing all CAs +/// +impl KrillManager { + /// Returns the handles of all CAs. + pub async fn ca_handles( + &self + ) -> Result, RunError> { + self.run(|runtime| Ok(runtime.ca_manager().ca_handles()?)).await + } -#[derive(Clone)] -pub struct KrillManager { - runtime: KrillRuntime, + /// Triggers republising of all CAs that need it. + pub async fn republish_all(&self, force: bool) -> Result<(), RunError> { + self.run(move |runtime| -> Result<_, RunError> { + let cas = runtime.ca_manager().republish_all(force)?; + for ca in cas { + runtime.ca_manager().cas_schedule_repo_sync(ca)?; + } + Ok(()) + }).await + } + + /// Triggers all CAs to re-sync with their repositories + pub async fn cas_repo_sync_all(&self) -> Result<(), RunError> { + self.run(|runtime| { + Ok(runtime.ca_manager().cas_schedule_repo_sync_all()?) + }).await + } + + /// Triggers all CAs to re-sync with their parent CAs. + pub async fn cas_refresh_all(&self) -> Result<(), RunError> { + self.run(|runtime| { + Ok(runtime.ca_manager().cas_schedule_refresh_all()?) + }).await + } + + /// Schedules a check to suspend children for all CAs + pub async fn cas_schedule_suspend_all(&self) -> Result<(), RunError> { + self.run(|runtime| { + Ok(runtime.ca_manager().cas_schedule_suspend_all()?) + }).await + } + + /// Returns statistics for all CAs. + pub async fn cas_stats( + &self, + ) -> Result< + HashMap, + RunError + > { + self.run(|runtime| { + let mut res = HashMap::new(); + + for handle in runtime.ca_manager().ca_handles()? { + // can't fail really, but to be sure + if let Ok(ca) = runtime.ca_manager().get_ca(&handle) { + let roas = ca.configured_roas(); + let roa_count = roas.len(); + let child_count = ca.children().count(); + + let bgp_report = if ca.handle().as_str() == "ta" + || ca.handle().as_str() == "testbed" + { + api::bgp::BgpAnalysisReport::new(vec![]) + } + else { + runtime.bgp_analyser().analyse( + roas.as_slice(), &ca.all_resources(), None + ) + }; + + res.insert( + ca.handle().clone(), + api::ca::CertAuthStats { + roa_count, + child_count, + bgp_stats: bgp_report.into(), + }, + ); + } + } + + Ok(res) + }).await + } + + /// Returns the parent status for the given CA. + pub async fn cas_status_map( + &self, + ) -> Result, RunError> { + self.run(|runtime| { + let mut res = HashMap::new(); + + for handle in runtime.ca_manager().ca_handles()? { + if let Ok(ca_status) = runtime.ca_manager().get_ca_status( + &handle + ) { + res.insert(handle, ca_status); + } + } + + Ok(res) + }).await + } +} + + +/// # Managing a single CA +/// +impl KrillManager { + /// Initialises a new CA. + pub async fn ca_init( + &self, init: api::admin::CertAuthInit + ) -> Result<(), RunError> { + self.run(move |runtime| { + Ok(runtime.ca_manager().init_ca(init.handle, runtime)?) + }).await + } + + /// Returns the public information for a CA. + pub async fn ca_info( + &self, ca: idexchange::CaHandle + ) -> Result { + self.run(move |runtime| { + Ok(runtime.ca_manager().get_ca(&ca).map(|ca| ca.as_ca_info())?) + }).await + } + + /// Creates a new identity certificate for the CA. + pub async fn ca_update_id( + &self, ca: idexchange::CaHandle, actor: Actor, + ) -> Result<(), RunError> { + self.run(move |runtime| { + Ok(runtime.ca_manager().ca_update_id(ca, &actor, runtime)?) + }).await + } + + /// Initiates a key roll for the given CA. + pub async fn ca_keyroll_init( + &self, ca: idexchange::CaHandle, actor: Actor + ) -> Result<(), RunError> { + self.run(move |runtime| { + Ok(runtime.ca_manager().ca_keyroll_init( + ca, Duration::seconds(0), &actor, runtime + )?) + }).await + } + + /// Activates an initiated key roll. + pub async fn ca_keyroll_activate( + &self, ca: idexchange::CaHandle, actor: Actor + ) -> Result<(), RunError> { + self.run(move |runtime| { + Ok(runtime.ca_manager().ca_keyroll_activate( + ca, Duration::seconds(0), &actor, runtime + )?) + }).await + } + + /// Returns the publisher request for a CA. + pub async fn ca_publisher_req( + &self, + ca: idexchange::CaHandle, + ) -> Result { + self.run(move |runtime| { + Ok(runtime.ca_manager().get_ca(&ca)?.publisher_request()) + }).await + } + + /// Return informatiuon about the configured repository for a given CA. + pub async fn ca_repo_details( + &self, ca_handle: idexchange::CaHandle + ) -> Result { + self.run(move |runtime| { + let ca = runtime.ca_manager().get_ca(&ca_handle)?; + let contact = ca.repository_contact()?; + Ok(api::ca::CaRepoDetails { contact: contact.clone() }) + }).await + } + + // ca_repo_update + + /// Trigger re-syncing with the repository. + pub async fn ca_sync_repo( + &self, ca: idexchange::CaHandle + ) -> Result<(), RunError> { + self.run(move |runtime| { + Ok(runtime.ca_manager().cas_schedule_repo_sync(ca)?) + }).await + } + + /// Returns the repository status for the given CA. + pub async fn ca_repo_status( + &self, ca: idexchange::CaHandle + ) -> Result { + self.run(move |runtime| -> Result<_, RunError> { + Ok(runtime.ca_manager().get_ca_status(&ca)?.into_repo()) + }).await + } + + /// Returns the parent status for the given CA. + pub async fn ca_parent_status( + &self, ca: idexchange::CaHandle + ) -> Result { + self.run(move |runtime| -> Result<_, RunError> { + Ok(runtime.ca_manager().get_ca_status(&ca)?.into_parents()) + }).await + } + + /// Triggers re-syncing with the parent CAs. + pub async fn cas_refresh_single( + &self, ca_handle: idexchange::CaHandle + ) -> Result<(), RunError> { + self.run(move |runtime| { + Ok(runtime.ca_manager().cas_schedule_refresh_single(ca_handle)?) + }).await + } + + pub async fn ca_issues( + &self, + ca: idexchange::CaHandle, + ) -> Result { + self.run(move |runtime| { + Ok(runtime.ca_manager().get_ca_issues(&ca)?) + }).await + } + + /// Returns the history of a CA. + pub async fn ca_history( + &self, + ca: idexchange::CaHandle, + crit: api::history::CommandHistoryCriteria, + ) -> Result { + self.run(move |runtime| { + Ok(runtime.ca_manager().ca_history(&ca, crit)?) + }).await + } + + /// Returns the details for the given CA command. + pub async fn ca_command_details( + &self, + ca: idexchange::CaHandle, + version: u64, + ) -> Result, RunError> { + self.run(move |runtime| { + match runtime.ca_manager().ca_command_details(&ca, version) { + Ok(res) => Ok(Some(res)), + Err(err) if matches!( + err, + KrillError::AggregateStoreError( + AggregateStoreError::UnknownCommand(..) + ) + ) => Ok(None), + Err(err) => Err(err.into()), + } + }).await + } + + // ca_delete +} + + +/// # Managing parent CAs +/// +impl KrillManager { + /// Returns the child request. + /// + /// This request is passed to a potential parent CA to register this CA. + pub async fn ca_child_req( + &self, ca: idexchange::CaHandle + ) -> Result { + self.run(move |runtime| { + Ok(runtime.ca_manager().get_ca(&ca)?.child_request()) + }).await + } + + // TODO: ca_parent_add_or_update + + // TODO: ca_parent_remove + + /// Returns the parent contact for a CA’s parent. + pub async fn ca_parent_contact( + &self, + ca: idexchange::CaHandle, + parent: idexchange::ParentHandle, + ) -> Result { + self.run(move |runtime| { + Ok(runtime.ca_manager().get_ca(&ca)?.parent(&parent)?.clone()) + }).await + } +} + + +/// # Managing child CAs +/// +impl KrillManager { + /// Adds a child to a CA. + /// + /// Returns the parent response that the child will need to contact this + /// CA for resource requests. + pub async fn ca_add_child( + &self, + ca: idexchange::CaHandle, + req: api::admin::AddChildRequest, + actor: Actor + ) -> Result { + self.run(move |runtime| { + Ok(runtime.ca_manager().ca_add_child(&ca, req, &actor, runtime)?) + }).await + } + + /// Return the parent response for a child CA. + pub async fn ca_parent_response( + &self, + ca: idexchange::CaHandle, + child: idexchange::ChildHandle, + ) -> Result { + self.run(move |runtime| { + Ok(runtime.ca_manager().ca_parent_response( + &ca, child, runtime.service_uri() + )?) + }).await + } + + /// Updates the identity certificate or resources of a child CA. + pub async fn ca_child_update( + &self, + ca: idexchange::CaHandle, + child: idexchange::ChildHandle, + req: api::admin::UpdateChildRequest, + actor: Actor, + ) -> Result<(), RunError> { + self.run(move |runtime| { + Ok(runtime.ca_manager().ca_child_update( + &ca, child, req, &actor, runtime + )?) + }).await + } + + /// Removes a child CA. + pub async fn ca_child_remove( + &self, + ca: idexchange::CaHandle, + child: idexchange::ChildHandle, + actor: Actor, + ) -> Result<(), RunError> { + self.run(move |runtime| { + Ok(runtime.ca_manager().ca_child_remove( + &ca, child, &actor, runtime + )?) + }).await + } + + /// Returns details for a child CA. + pub async fn ca_child_show( + &self, + ca: idexchange::CaHandle, + child: idexchange::ChildHandle, + ) -> Result { + self.run(move |runtime| { + Ok(runtime.ca_manager().ca_show_child(&ca, &child)?) + }).await + } + + /// Exports a child CA. + pub async fn ca_child_export( + &self, + ca: idexchange::CaHandle, + child: idexchange::ChildHandle, + ) -> Result { + self.run(move |runtime| { + Ok(runtime.ca_manager().ca_child_export(&ca, &child)?) + }).await + } + + /// Imports a child CA. + pub async fn ca_child_import( + &self, + ca: idexchange::CaHandle, + child: api::import::ImportChild, + actor: Actor, + ) -> Result<(), RunError> { + self.run(move |runtime| { + Ok(runtime.ca_manager().ca_child_import( + &ca, child, &actor, runtime + )?) + }).await + } + + /// Returns child CA statistics. + pub async fn ca_stats_child_connections( + &self, + ca: idexchange::CaHandle, + ) -> Result { + self.run(move |runtime| { + Ok( + runtime.ca_manager().get_ca_status( + &ca + )?.get_children_connection_stats() + ) + }).await + } + + /// Handles a synchronization request by a child CA. + pub async fn rfc6492( + &self, + ca: idexchange::CaHandle, + msg_bytes: Bytes, + user_agent: Option, + actor: Actor, + ) -> Result { + self.run(move |runtime| { + Ok(runtime.ca_manager().rfc6492( + &ca, msg_bytes, user_agent, &actor, runtime + )?) + }).await + } +} + +/// # Managing ASPAs +/// +impl KrillManager { + /// Returns the current ASPA definitions for a CA. + pub async fn ca_aspas_definitions_show( + &self, + ca: idexchange::CaHandle, + ) -> Result { + self.run(move |runtime| { + Ok(runtime.ca_manager().ca_aspas_definitions_show(&ca)?) + }).await + } + + /// Updates the APSA definitions of a CA. + pub async fn ca_aspas_definitions_update( + &self, + ca: idexchange::CaHandle, + updates: api::aspa::AspaDefinitionUpdates, + actor: Actor, + ) -> Result<(), RunError> { + self.run(move |runtime| { + Ok(runtime.ca_manager().ca_aspas_definitions_update( + ca, updates, &actor, runtime + )?) + }).await + } + + /// Updates the ASPA provider set for a single customer ASN. + pub async fn ca_aspas_update_aspa( + &self, + ca: idexchange::CaHandle, + customer: api::aspa::CustomerAsn, + update: api::aspa::AspaProvidersUpdate, + actor: Actor, + ) -> Result<(), RunError> { + self.run(move |runtime| { + Ok(runtime.ca_manager().ca_aspas_update_aspa_providers( + ca, customer, update, &actor, runtime + )?) + }).await + } +} + + +/// # Managing BGPsec router keys +/// +impl KrillManager { + /// Lists the currently configured BGPsec router keys for a CA. + pub async fn ca_bgpsec_definitions_show( + &self, ca: idexchange::CaHandle + ) -> Result { + self.run(move |runtime| { + Ok(runtime.ca_manager().ca_bgpsec_definitions_show(&ca)?) + }).await + } + + /// Updates the BGPsec router key definitions for a CA. + pub async fn ca_bgpsec_definitions_update( + &self, + ca: idexchange::CaHandle, + updates: api::bgpsec::BgpSecDefinitionUpdates, + actor: Actor, + ) -> Result<(), RunError> { + self.run(move |runtime| { + Ok(runtime.ca_manager().ca_bgpsec_definitions_update( + ca, updates, &actor, runtime + )?) + }).await + } +} + + +/// # Managing ROAs +/// +impl KrillManager { + /// Returns the list of current ROA definitions for a CA. + pub async fn ca_routes_show( + &self, handle: idexchange::CaHandle + ) -> Result, RunError> { + self.run(move |runtime| { + Ok(runtime.ca_manager().get_ca(&handle)?.configured_roas()) + }).await + } + + /// Updates the ROA definitions of a CA. + pub async fn ca_routes_update( + &self, + ca: idexchange::CaHandle, + updates: api::roa::RoaConfigurationUpdates, + actor: Actor, + ) -> Result<(), RunError> { + self.run(move |runtime| { + Ok(runtime.ca_manager().ca_routes_update( + ca, updates, &actor, runtime + )?) + }).await + } + + /// Produces the BGP analysis for a CA. + pub async fn ca_routes_bgp_analysis( + &self, + handle: idexchange::CaHandle, + ) -> Result { + self.run(move |runtime| { + let ca = runtime.ca_manager().get_ca(&handle)?; + let definitions = ca.configured_roas(); + let resources_held = ca.all_resources(); + Ok(runtime.bgp_analyser().analyse( + definitions.as_slice(), &resources_held, None + )) + }).await + } + + /// Performs a BGP analysis for the given changes to a CA. + pub async fn ca_routes_bgp_dry_run( + &self, + handle: idexchange::CaHandle, + mut updates: api::roa::RoaConfigurationUpdates, + ) -> Result { + self.run(move |runtime| { + let ca = runtime.ca_manager().get_ca(&handle)?; + + updates.set_explicit_max_length(); + let resources_held = ca.all_resources(); + let limit = Some(updates.affected_prefixes()); + + let would_be_routes = ca.get_updated_authorizations(&updates)?; + let would_be_configurations = would_be_routes.roa_configurations(); + let configured_roas = + ca.configured_roas_for_configs(would_be_configurations); + + Ok(runtime.bgp_analyser().analyse( + &configured_roas, &resources_held, limit + )) + }).await + } + + /// Produces suggestions for updates based on a BGP analysis. + pub async fn ca_routes_bgp_suggest( + &self, + handle: idexchange::CaHandle, + limit: Option, + ) -> Result { + self.run(move |runtime| { + let ca = runtime.ca_manager().get_ca(&handle)?; + let configured_roas = ca.configured_roas(); + let resources_held = ca.all_resources(); + + Ok(runtime.bgp_analyser().suggest( + configured_roas.as_slice(), &resources_held, limit + )) + }).await + } +} + + +/// # Publication server +/// +impl KrillManager { + /// Creates the publication server. + /// + /// Fails if there is an initialized publication server already. + pub async fn repository_init( + &self, + uris: api::admin::PublicationServerUris, + ) -> Result<(), RunError> { + self.run(move |runtime| { + Ok(runtime.repo_manager().init(uris)?) + }).await + } + + /// Clears the publication server. + /// + /// This will fail if the server still has publishers or if it hasn’t + /// been intialized yet. + pub async fn repository_clear(&self) -> Result<(), RunError> { + self.run(|runtime| { + Ok(runtime.repo_manager().repository_clear()?) + }).await + } + + /// Performs an RRDP session reset. + /// + /// This is useful after a restart of the server as we can never be + /// certain whether the previous state was the last public state seen + /// by validators, or when the server was started using a back up. + pub async fn repository_session_reset(&self) -> Result<(), RunError> { + self.run(|runtime| { + Ok(runtime.repo_manager().rrdp_session_reset()?) + }).await + } + + /// Converts the RRDP path portion of a HTTP request URI to a path. + /// + /// The `path` should contain everything after the `/rrdp/` portion of + /// the URI’s path. If the path is in principle valid, i.e., could + /// represent an RRDP resource generated by this RRDP sever, the method + /// will return a file system path representing this path. This does not + /// mean there will actually be a file there. The file may have been + /// deleted or may have never existed at all. This is necessary since + /// the RRDP server doesn’t track past files, only the currently valid + /// set of resources. + /// + /// If the path is definitely not valid, returns `Ok(None)`. This should + /// probably be translated into a 404 Not Found response. + pub async fn resolve_rrdp_request_path( + &self, path: String + ) -> Result, RunError> { + self.run(move |runtime| { + Ok(runtime.repo_manager().resolve_rrdp_request_path(&path)?) + }).await + } + + /// Processes an RFC 8181 publisher request. + pub async fn rfc8181( + &self, + publisher: idexchange::PublisherHandle, + msg_bytes: Bytes, + ) -> Result { + self.run(move |runtime| { + Ok(runtime.repo_manager().rfc8181(publisher, msg_bytes)?) + }).await + } +} + +/// # Managing the publication server +/// +impl KrillManager { + /// Returns the repository server stats + pub async fn repo_stats( + &self + ) -> Result { + self.run(|runtime| { + Ok(runtime.repo_manager().repo_stats()?) + }).await + } + + /// Returns all list of the handles of all current publishers. + pub async fn publishers( + &self + ) -> Result, RunError> { + self.run(|runtime| { + Ok(runtime.repo_manager().publishers()?) + }).await + } + + /// Returns details for the publisher with the given handle. + pub async fn get_publisher( + &self, publisher: idexchange::PublisherHandle, + ) -> Result { + self.run(|runtime| { + Ok(runtime.repo_manager().get_publisher_details(publisher)?) + }).await + } + + pub async fn repository_response( + &self, publisher: idexchange::PublisherHandle, + ) -> Result { + self.run(move |runtime| { + Ok(runtime.repo_manager().repository_response(&publisher)?) + }).await + } + + /// Adds a new publishers: + /// + /// This errors out if the publisher already exists. + pub async fn add_publisher( + &self, req: idexchange::PublisherRequest, actor: Actor, + ) -> Result { + self.run(move |runtime| { + let publisher_handle = req.publisher_handle().clone(); + runtime.repo_manager().create_publisher(req, &actor)?; + Ok(runtime.repo_manager().repository_response(&publisher_handle)?) + }).await + } + + /// Removes the publisher with the given handle. + /// + /// Returns an error if no publisher with such a handle exists. + pub async fn remove_publisher( + &self, publisher: idexchange::PublisherHandle, actor: Actor, + ) -> Result<(), RunError> { + self.run(move |runtime| { + Ok(runtime.repo_manager().remove_publisher(publisher, &actor)?) + }).await + } + + /// Deletes files matching the given criteria. + pub async fn delete_matching_files( + &self, criteria: api::admin::RepoFileDeleteCriteria, + ) -> Result<(), RunError> { + self.run(move |runtime| { + Ok(runtime.repo_manager().delete_matching_files(criteria)?) + }).await + } +} + +/// # Managing the trust anchor +/// +impl KrillManager { + /// Initialises the trust anchor proxy. + pub async fn ta_proxy_init(&self) -> Result<(), RunError> { + self.run(|runtime| { + Ok(runtime.ca_manager().ta_proxy_init(runtime)?) + }).await + } + + /// Returns the TAL for the trust anchor. + pub async fn ta_tal( + &self + ) -> Result { + self.run(|runtime| { + let proxy = runtime.ca_manager().get_trust_anchor_proxy()?; + Ok(proxy.get_ta_details()?.tal.to_string()) + }).await + } + + /// Returns the certificate of the trust anchor. + pub async fn ta_cer( + &self + ) -> Result { + self.run(|runtime| { + let proxy = runtime.ca_manager().get_trust_anchor_proxy()?; + Ok(proxy.get_ta_details()?.cert.to_bytes()) + }).await + } + + /// Returns the trust anchor proxy ID certificate. + pub async fn ta_proxy_id(&self) -> Result { + self.run(|runtime| { + Ok(runtime.ca_manager().ta_proxy_id()?) + }).await + } + + /// Returns the trust anchor proxy publisher request. + pub async fn ta_proxy_publisher_request( + &self, + ) -> Result { + self.run(|runtime| { + Ok(runtime.ca_manager().ta_proxy_publisher_request()?) + }).await + } + + /// Updates the trust anchor repository contact. + pub async fn ta_proxy_repository_update( + &self, contact: api::admin::RepositoryContact, actor: Actor + ) -> Result<(), RunError> { + self.run(move |runtime| { + Ok(runtime.ca_manager().ta_proxy_repository_update( + contact, &actor, runtime + )?) + }).await + } + + /// Returns the current trust anchor repository contact. + pub async fn ta_proxy_repository_contact( + &self, + ) -> Result { + self.run(|runtime| { + Ok(runtime.ca_manager().ta_proxy_repository_contact()?) + }).await + } + + /// Adds a trust anchor signer to the trust anchor proxy. + pub async fn ta_proxy_signer_add( + &self, info: api::ta::TrustAnchorSignerInfo, actor: Actor, + ) -> Result<(), RunError> { + self.run(move |runtime| { + Ok(runtime.ca_manager().ta_proxy_signer_add( + info, &actor, runtime + )?) + }).await + } + + /// Updates the trust anchor signer connected to a trust anchor proxy. + pub async fn ta_proxy_signer_update( + &self, info: api::ta::TrustAnchorSignerInfo, actor: Actor, + ) -> Result<(), RunError> { + self.run(move |runtime| { + Ok(runtime.ca_manager().ta_proxy_signer_update( + info, &actor, runtime + )?) + }).await + } + + /// Creates a new trust anchor signer request. + /// + /// Returns an error if there is a pending request. + pub async fn ta_proxy_signer_make_request( + &self, actor: Actor, + ) -> Result { + self.run(move |runtime| { + Ok(runtime.ca_manager().ta_proxy_signer_make_request( + &actor, runtime + )?) + }).await + } + + /// Returns a currently pending trust anchor signer request. + pub async fn ta_proxy_signer_get_request( + &self, + ) -> Result { + self.run(|runtime| { + Ok(runtime.ca_manager().ta_proxy_signer_get_request()?) + }).await + } + + /// Processes a trust anchor signer response. + pub async fn ta_proxy_signer_process_response( + &self, response: api::ta::TrustAnchorSignedResponse, actor: Actor + ) -> Result<(), RunError> { + self.run(move |runtime| { + Ok(runtime.ca_manager().ta_proxy_signer_process_response( + response, &actor, runtime + )?) + }).await + } + + /// Adds a child CA to the trust anchor proxy. + pub async fn ta_proxy_children_add( + &self, + child_request: api::admin::AddChildRequest, + actor: Actor, + ) -> Result { + self.run(move |runtime| { + // TA as parent is handled a special case in the following + Ok(runtime.ca_manager().ca_add_child( + &ta_handle().convert(), + child_request, + &actor, + runtime + )?) + }).await + } } @@ -120,6 +990,12 @@ impl From for RunError { } } +impl From for KrillError { + fn from(src: RunError) -> Self { + src.0 + } +} + impl From for RunError { fn from(_: oneshot::error::RecvError) -> Self { Self(KrillError::internal("operation dropped")) diff --git a/src/server/oldmanager.rs b/src/server/oldmanager.rs index 66f73a54f..9857a1aa4 100644 --- a/src/server/oldmanager.rs +++ b/src/server/oldmanager.rs @@ -1,5 +1,7 @@ //! An RPKI publication protocol server. +#![allow(dead_code, unused_imports)] + use std::collections::HashMap; use std::path::PathBuf; use std::str::FromStr; @@ -60,7 +62,6 @@ use crate::api::history::{ CommandDetails, CommandHistory, CommandHistoryCriteria }; use crate::api::import::ImportChild; -use crate::api::pubd::RepoStats; use crate::api::roa::{ ConfiguredRoa, RoaConfiguration, RoaConfigurationUpdates, RoaPayload, }; @@ -83,7 +84,6 @@ use crate::server::runtime::KrillRuntime; pub struct OldManager { krill: KrillRuntime, - // The base URI for this service service_uri: uri::Https, @@ -283,10 +283,6 @@ impl OldManager { self.system_actor.clone(), ) } - - pub fn service_base_uri(&self) -> &uri::Https { - &self.service_uri - } } /// # Access to components @@ -295,302 +291,10 @@ impl OldManager { &self.system_actor } - pub fn testbed_enabled(&self) -> bool { - self.ca_manager.testbed_enabled() - } - - /// Converts the RRDP path portion of a HTTP request URI to a path. - /// - /// The `path` should contain everything after the `/rrdp/` portion of - /// the URI’s path. If the path is in principle valid, i.e., could - /// represent an RRDP resource generated by this RRDP sever, the method - /// will return a file system path representing this path. This does not - /// mean there will actually be a file there. The file may have been - /// deleted or may have never existed at all. This is necessary since - /// the RRDP server doesn’t track past files, only the currently valid - /// set of resources. - /// - /// If the path is definitely not valid, returns `Ok(None)`. This should - /// probably be translated into a 404 Not Found response. - pub fn resolve_rrdp_request_path( - &self, path: &str - ) -> KrillResult> { - self.repo_manager.resolve_rrdp_request_path(path) - } -} - -/// # Configure publishers -impl OldManager { - /// Returns the repository server stats - pub fn repo_stats(&self) -> KrillResult { - self.repo_manager.repo_stats() - } - - /// Returns all current publishers. - pub fn publishers(&self) -> KrillResult> { - self.repo_manager.publishers() - } - - /// Adds the publishers, blows up if it already existed. - pub fn add_publisher( - &self, - req: idexchange::PublisherRequest, - actor: &Actor, - ) -> KrillResult { - let publisher_handle = req.publisher_handle().clone(); - self.repo_manager.create_publisher(req, actor)?; - self.repository_response(&publisher_handle) - } - - /// Removes a publisher, blows up if it didn't exist. - pub fn remove_publisher( - &self, - publisher: PublisherHandle, - actor: &Actor, - ) -> KrillEmptyResult { - self.repo_manager.remove_publisher(publisher, actor) - } - - /// Removes a publisher, blows up if it didn't exist. - pub fn delete_matching_files( - &self, - criteria: RepoFileDeleteCriteria, - ) -> KrillEmptyResult { - self.repo_manager.delete_matching_files(criteria) - } - - /// Returns a publisher. - pub fn get_publisher( - &self, - publisher: PublisherHandle, - ) -> KrillResult { - self.repo_manager.get_publisher_details(publisher) - } - - pub fn rrdp_base_path(&self) -> PathBuf { - let mut path = self.config.repo_dir().to_path_buf(); - path.push("rrdp"); - path.to_path_buf() - } -} - -/// # Manage RFC8181 clients -impl OldManager { - pub fn repository_response( - &self, - publisher: &PublisherHandle, - ) -> KrillResult { - self.repo_manager.repository_response(publisher) - } - - pub fn rfc8181( - &self, - publisher: PublisherHandle, - msg_bytes: Bytes, - ) -> KrillResult { - self.repo_manager.rfc8181(publisher, msg_bytes) - } -} - -/// # TA Support -impl OldManager { - pub fn ta_proxy_enabled(&self) -> bool { - self.config.ta_proxy_enabled() - } - - pub fn ta_proxy_init(&self) -> KrillResult<()> { - self.ca_manager.ta_proxy_init(&self.krill) - } - - pub fn ta_proxy_id(&self) -> KrillResult { - self.ca_manager.ta_proxy_id() - } - - pub fn ta_proxy_publisher_request( - &self, - ) -> KrillResult { - self.ca_manager.ta_proxy_publisher_request() - } - - pub fn ta_proxy_repository_update( - &self, - contact: RepositoryContact, - actor: &Actor, - ) -> KrillResult<()> { - self.ca_manager - .ta_proxy_repository_update(contact, actor, &self.krill) - } - - pub fn ta_proxy_repository_contact( - &self, - ) -> KrillResult { - self.ca_manager.ta_proxy_repository_contact() - } - - pub fn ta_proxy_signer_add( - &self, - info: TrustAnchorSignerInfo, - actor: &Actor, - ) -> KrillResult<()> { - self.ca_manager.ta_proxy_signer_add(info, actor, &self.krill) - } - - pub fn ta_proxy_signer_update( - &self, - info: TrustAnchorSignerInfo, - actor: &Actor, - ) -> KrillResult<()> { - self.ca_manager.ta_proxy_signer_update(info, actor, &self.krill) - } - - pub fn ta_proxy_signer_make_request( - &self, - actor: &Actor, - ) -> KrillResult { - self.ca_manager.ta_proxy_signer_make_request(actor, &self.krill) - } - - pub fn ta_proxy_signer_get_request( - &self, - ) -> KrillResult { - self.ca_manager.ta_proxy_signer_get_request() - } - - pub fn ta_proxy_signer_process_response( - &self, - response: TrustAnchorSignedResponse, - actor: &Actor, - ) -> KrillResult<()> { - self.ca_manager - .ta_proxy_signer_process_response(response, actor, &self.krill) - } - - pub fn ta_proxy_children_add( - &self, - child_request: AddChildRequest, - actor: &Actor, - ) -> KrillResult { - // TA as parent is handled a special case in the following - self.ca_manager.ca_add_child( - &ta_handle().convert(), - child_request, - &self.config.service_uri(), - actor, - &self.krill, - ) - } - - pub fn ta_cert_details(&self) -> KrillResult { - let proxy = self.ca_manager.get_trust_anchor_proxy()?; - Ok(proxy.get_ta_details()?.clone()) - } -} - -/// # Being a parent -impl OldManager { - /// Adds a child to a CA and returns the ParentCaInfo that the child - /// will need to contact this CA for resource requests. - pub fn ca_add_child( - &self, - ca: &CaHandle, - req: AddChildRequest, - actor: &Actor, - ) -> KrillResult { - self.ca_manager.ca_add_child( - ca, req, &self.service_uri, actor, &self.krill, - ) - } - - /// Shows the parent contact for a child. - pub async fn ca_parent_contact( - &self, - ca: &CaHandle, - child: ChildHandle, - ) -> KrillResult { - self.ca_manager.ca_parent_contact(ca, child, &self.service_uri) - } - - /// Shows the parent contact for a child. - pub fn ca_parent_response( - &self, - ca: &CaHandle, - child: ChildHandle, - ) -> KrillResult { - self.ca_manager.ca_parent_response(ca, child, &self.service_uri) - } - - /// Update IdCert or resources of a child. - pub fn ca_child_update( - &self, - ca: &CaHandle, - child: ChildHandle, - req: UpdateChildRequest, - actor: &Actor, - ) -> KrillEmptyResult { - self.ca_manager.ca_child_update(ca, child, req, actor, &self.krill) - } - - /// Update IdCert or resources of a child. - pub fn ca_child_remove( - &self, - ca: &CaHandle, - child: ChildHandle, - actor: &Actor, - ) -> KrillEmptyResult { - self.ca_manager.ca_child_remove(ca, child, actor, &self.krill) - } - - /// Show details for a child under the CA. - pub fn ca_child_show( - &self, - ca: &CaHandle, - child: &ChildHandle, - ) -> KrillResult { - self.ca_manager.ca_show_child(ca, child) - } - - /// Export a child under the CA. - pub fn ca_child_export( - &self, - ca: &CaHandle, - child: &ChildHandle, - ) -> KrillResult { - self.ca_manager.ca_child_export(ca, child) - } - - /// Import a child under the CA. - pub fn ca_child_import( - &self, - ca: &CaHandle, - child: ImportChild, - actor: &Actor, - ) -> KrillResult<()> { - self.ca_manager.ca_child_import(ca, child, actor, &self.krill) - } - - /// Show children stats under the CA. - pub fn ca_stats_child_connections( - &self, - ca: &CaHandle, - ) -> KrillResult { - self.ca_manager - .get_ca_status(ca) - .map(|status| status.get_children_connection_stats()) - } } /// # Being a child impl OldManager { - /// Returns the child request for a CA, or NONE if the CA cannot be found. - pub fn ca_child_req( - &self, - ca: &CaHandle, - ) -> KrillResult { - self.ca_manager - .get_ca(ca) - .map(|ca| ca.child_request()) - } - /// Updates a parent contact for a CA pub async fn ca_parent_add_or_update( &self, @@ -630,42 +334,6 @@ impl OldManager { /// # Stats and status of CAS impl OldManager { - pub fn cas_stats( - &self, - ) -> KrillResult> { - let mut res = HashMap::new(); - - for handle in self.ca_manager.ca_handles()? { - // can't fail really, but to be sure - if let Ok(ca) = self.ca_manager.get_ca(&handle) { - let roas = ca.configured_roas(); - let roa_count = roas.len(); - let child_count = ca.children().count(); - - let bgp_report = if ca.handle().as_str() == "ta" - || ca.handle().as_str() == "testbed" - { - BgpAnalysisReport::new(vec![]) - } - else { - self.bgp_analyser.analyse( - roas.as_slice(), &ca.all_resources(), None - ) - }; - - res.insert( - ca.handle().clone(), - CertAuthStats { - roa_count, - child_count, - bgp_stats: bgp_report.into(), - }, - ); - } - } - - Ok(res) - } pub async fn cas_import( &self, @@ -738,7 +406,7 @@ impl OldManager { import: api::import::ImportCa, ca_manager: Arc, repo_manager: Arc, - service_uri: Arc, + _service_uri: Arc, actor: Arc, krill: KrillRuntime, ) -> KrillEmptyResult { @@ -852,7 +520,6 @@ impl OldManager { .ca_add_child( &import_parent.handle.convert(), child_req, - &service_uri, &actor, &krill, )? @@ -907,75 +574,10 @@ impl OldManager { Ok(()) } - - pub fn ca_issues( - &self, - ca: &CaHandle, - ) -> KrillResult { - self.ca_manager.get_ca_issues(ca) - } -} - -/// # Synchronization operations for CAS -impl OldManager { - /// Republish all CAs that need it. - pub fn republish_all(&self, force: bool) -> KrillEmptyResult { - let cas = self.ca_manager.republish_all(force)?; - for ca in cas { - self.cas_repo_sync_single(&ca)?; - } - - Ok(()) - } - - /// Re-sync all CAs with their repositories - pub fn cas_repo_sync_all(&self) -> KrillEmptyResult { - self.ca_manager.cas_schedule_repo_sync_all() - } - - /// Re-sync a specific CA with its repository - pub fn cas_repo_sync_single(&self, ca: &CaHandle) -> KrillEmptyResult { - self.ca_manager.cas_schedule_repo_sync(ca.clone()) - } - - /// Refresh all CAs: ask for updates and shrink as needed. - pub fn cas_refresh_all(&self) -> KrillEmptyResult { - self.ca_manager.cas_schedule_refresh_all() - } - - /// Refresh a specific CA with its parents - pub fn cas_refresh_single( - &self, - ca_handle: CaHandle, - ) -> KrillEmptyResult { - self.ca_manager.cas_schedule_refresh_single(ca_handle) - } - - /// Schedule check suspend children for all CAs - pub fn cas_schedule_suspend_all(&self) -> KrillEmptyResult { - self.ca_manager.cas_schedule_suspend_all() - } } /// # Admin CAS impl OldManager { - pub fn ca_handles(&self) -> KrillResult> { - self.ca_manager.ca_handles().map(Vec::into_iter) - } - - pub fn ca_list(&self, auth: &AuthInfo) -> KrillResult { - self.ca_manager.ca_list(auth) - } - - /// Returns the public CA info for a CA, or NONE if the CA cannot be - /// found. - pub fn ca_info(&self, ca: &CaHandle) -> KrillResult { - self.ca_manager.get_ca(ca).map(|ca| ca.as_ca_info()) - } - - pub fn ca_status(&self, ca: &CaHandle) -> KrillResult { - self.ca_manager.get_ca_status(ca) - } /// Delete a CA. Let it do best effort revocation requests and withdraw /// all its objects first. Note that any children of this CA will be left @@ -991,60 +593,6 @@ impl OldManager { .await } - /// Returns the parent contact for a CA and parent, or NONE if either the - /// CA or the parent cannot be found. - pub fn ca_my_parent_contact( - &self, - ca: &CaHandle, - parent: &ParentHandle, - ) -> KrillResult { - let ca = self.ca_manager.get_ca(ca)?; - ca.parent(parent).cloned() - } - - /// Returns the history for a CA. - pub fn ca_history( - &self, - ca: &CaHandle, - crit: CommandHistoryCriteria, - ) -> KrillResult { - self.ca_manager.ca_history(ca, crit) - } - - pub fn ca_command_details( - &self, - ca: &CaHandle, - version: u64, - ) -> KrillResult { - self.ca_manager.ca_command_details(ca, version) - } - - /// Returns the publisher request for a CA, or NONE of the CA cannot be - /// found. - pub fn ca_publisher_req( - &self, - ca: &CaHandle, - ) -> KrillResult { - self.ca_manager - .get_ca(ca) - .map(|ca| ca.publisher_request()) - } - - pub fn ca_init(&self, init: CertAuthInit) -> KrillEmptyResult { - self.ca_manager.init_ca(init.handle, &self.krill) - } - - /// Return the info about the CONFIGured repository server for a given Ca. - /// and the actual objects published there, as reported by a list reply. - pub fn ca_repo_details( - &self, - ca_handle: &CaHandle, - ) -> KrillResult { - let ca = self.ca_manager.get_ca(ca_handle)?; - let contact = ca.repository_contact()?; - Ok(CaRepoDetails { contact: contact.clone() }) - } - /// Update the repository for a CA, or return an error. (see /// `CertAuth::repo_update`) pub async fn ca_repo_update( @@ -1057,57 +605,12 @@ impl OldManager { self.repo_manager.as_ref(), ca, contact, true, actor, &self.krill ).await } - - pub fn ca_update_id( - &self, - ca: CaHandle, - actor: &Actor, - ) -> KrillEmptyResult { - self.ca_manager.ca_update_id(ca, actor, &self.krill) - } - - pub fn ca_keyroll_init( - &self, - ca: CaHandle, - actor: &Actor, - ) -> KrillEmptyResult { - self.ca_manager.ca_keyroll_init( - ca, Duration::seconds(0), actor, &self.krill, - ) - } - - pub fn ca_keyroll_activate( - &self, - ca: CaHandle, - actor: &Actor, - ) -> KrillEmptyResult { - self.ca_manager.ca_keyroll_activate( - ca, Duration::seconds(0), actor, &self.krill, - ) - } - - pub fn rfc6492( - &self, - ca: CaHandle, - msg_bytes: Bytes, - user_agent: Option, - actor: &Actor, - ) -> KrillResult { - self.ca_manager.rfc6492( - &ca, msg_bytes, user_agent, actor, &self.krill - ) - } } /// # Handle ASPA requests impl OldManager { - pub fn ca_aspas_definitions_show( - &self, - ca: &CaHandle, - ) -> KrillResult { - self.ca_manager.ca_aspas_definitions_show(ca) - } + // Left for upgrade only. pub fn ca_aspas_definitions_update( &self, ca: CaHandle, @@ -1118,108 +621,12 @@ impl OldManager { ca, updates, actor, &self.krill ) } - - pub fn ca_aspas_update_aspa( - &self, - ca: CaHandle, - customer: CustomerAsn, - update: AspaProvidersUpdate, - actor: &Actor, - ) -> KrillEmptyResult { - self.ca_manager.ca_aspas_update_aspa_providers( - ca, customer, update, actor, &self.krill, - ) - } -} - -/// # Handle BGPSec requests -impl OldManager { - pub fn ca_bgpsec_definitions_show( - &self, - ca: &CaHandle, - ) -> KrillResult { - self.ca_manager.ca_bgpsec_definitions_show(ca) - } - - pub fn ca_bgpsec_definitions_update( - &self, - ca: CaHandle, - updates: BgpSecDefinitionUpdates, - actor: &Actor, - ) -> KrillResult<()> { - self.ca_manager.ca_bgpsec_definitions_update( - ca, updates, actor, &self.krill, - ) - } } /// # Handle route authorization requests impl OldManager { - pub fn ca_routes_update( - &self, - ca: CaHandle, - updates: RoaConfigurationUpdates, - actor: &Actor, - ) -> KrillEmptyResult { - self.ca_manager.ca_routes_update(ca, updates, actor, &self.krill) - } - - pub fn ca_routes_show( - &self, - handle: &CaHandle, - ) -> KrillResult> { - let ca = self.ca_manager.get_ca(handle)?; - - Ok(ca.configured_roas()) - } - - pub fn ca_routes_bgp_analysis( - &self, - handle: &CaHandle, - ) -> KrillResult { - let ca = self.ca_manager.get_ca(handle)?; - let definitions = ca.configured_roas(); - let resources_held = ca.all_resources(); - Ok(self.bgp_analyser.analyse( - definitions.as_slice(), &resources_held, None - )) - } - - pub fn ca_routes_bgp_dry_run( - &self, - handle: &CaHandle, - mut updates: RoaConfigurationUpdates, - ) -> KrillResult { - let ca = self.ca_manager.get_ca(handle)?; - - updates.set_explicit_max_length(); - let resources_held = ca.all_resources(); - let limit = Some(updates.affected_prefixes()); - - let would_be_routes = ca.get_updated_authorizations(&updates)?; - let would_be_configurations = would_be_routes.roa_configurations(); - let configured_roas = - ca.configured_roas_for_configs(would_be_configurations); - - Ok(self.bgp_analyser.analyse( - &configured_roas, &resources_held, limit - )) - } - - pub fn ca_routes_bgp_suggest( - &self, - handle: &CaHandle, - limit: Option, - ) -> KrillResult { - let ca = self.ca_manager.get_ca(handle)?; - let configured_roas = ca.configured_roas(); - let resources_held = ca.all_resources(); - - Ok(self.bgp_analyser.suggest( - configured_roas.as_slice(), &resources_held, limit - )) - } + // Only for upgrade. /// Re-issue ROA objects so that they will use short subjects (see issue /// #700) pub async fn force_renew_roas(&self) -> KrillResult<()> { @@ -1227,85 +634,4 @@ impl OldManager { } } -/// # Handle Repository Server requests -impl OldManager { - /// Create the publication server, will fail if it was already created. - pub fn repository_init( - &self, - uris: PublicationServerUris, - ) -> KrillResult<()> { - self.repo_manager.init(uris) - } - - /// Clear the publication server. Will fail if it still has publishers. Or - /// if it does not exist - pub fn repository_clear(&self) -> KrillResult<()> { - self.repo_manager.repository_clear() - } - - /// Perform an RRDP session reset. Useful after a restart of the server as - /// we can never be certain whether the previous state was the last - /// public state seen by validators, or.. the server was started using - /// a back up. - pub fn repository_session_reset(&self) -> KrillResult<()> { - self.repo_manager.rrdp_session_reset() - } -} - -/// # Handle Resource Tagged Attestation requests -impl OldManager { - /// List all known RTAs - pub fn rta_list(&self, ca: CaHandle) -> KrillResult { - let ca = self.ca_manager.get_ca(&ca)?; - Ok(ca.rta_list()) - } - - /// Show RTA - pub fn rta_show( - &self, - ca: CaHandle, - name: RtaName, - ) -> KrillResult { - let ca = self.ca_manager.get_ca(&ca)?; - ca.rta_show(&name) - } - - /// Sign an RTA - either a new, or a prepared RTA - pub async fn rta_sign( - &self, - ca: CaHandle, - name: RtaName, - request: RtaContentRequest, - actor: &Actor, - ) -> KrillResult<()> { - self.ca_manager.rta_sign(ca, name, request, actor, &self.krill) - } - - /// Prepare a multi - pub async fn rta_multi_prep( - &self, - ca: CaHandle, - name: RtaName, - request: RtaPrepareRequest, - actor: &Actor, - ) -> KrillResult { - self.ca_manager.rta_multi_prep( - &ca, name.clone(), request, actor, &self.krill, - )?; - let ca = self.ca_manager.get_ca(&ca)?; - ca.rta_prep_response(&name) - } - - /// Co-sign an existing RTA - pub async fn rta_multi_cosign( - &self, - ca: CaHandle, - name: RtaName, - rta: ResourceTaggedAttestation, - actor: &Actor, - ) -> KrillResult<()> { - self.ca_manager.rta_multi_cosign(ca, name, rta, actor, &self.krill) - } -} - // Tested through integration tests diff --git a/src/server/runtime.rs b/src/server/runtime.rs index 73f2ab173..73eab789c 100644 --- a/src/server/runtime.rs +++ b/src/server/runtime.rs @@ -108,7 +108,7 @@ impl KrillRuntime { &self.0.signer } - pub fn bpg_analyser(&self) -> &BgpAnalyser { + pub fn bgp_analyser(&self) -> &BgpAnalyser { &self.0.bgp_analyser } From dc2c08226848662742c756a2d3c7205aadbd5e6f Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Mon, 9 Feb 2026 12:01:23 +0100 Subject: [PATCH 09/51] Create fully sync Krill core. --- src/bin/krill.rs | 5 +- src/config.rs | 2 +- src/daemon/http/auth/authorizer.rs | 13 +- .../auth/providers/openid_connect/provider.rs | 2 +- src/daemon/http/dispatch/bulk.rs | 2 +- src/daemon/http/dispatch/cas.rs | 44 +- src/daemon/http/dispatch/metrics.rs | 6 +- src/daemon/http/dispatch/pubd.rs | 4 +- src/daemon/http/dispatch/root.rs | 2 +- src/daemon/http/dispatch/ta.rs | 12 +- src/daemon/http/server.rs | 23 +- src/daemon/start.rs | 81 +- src/server/ca/certauth.rs | 10 +- src/server/ca/manager.rs | 351 +++-- src/server/ca/publishing.rs | 17 +- src/server/ca/upgrades/data_migration.rs | 20 +- src/server/manager.rs | 568 +++++++- src/server/mod.rs | 2 - src/server/oldmanager.rs | 637 --------- src/server/pubd/access.rs | 13 +- src/server/pubd/manager.rs | 288 ++-- src/server/runtime.rs | 210 +-- src/server/scheduler.rs | 1169 ++++++++--------- src/upgrades/mod.rs | 9 +- tests/common.rs | 12 +- tests/functional_old_data.rs | 19 +- tests/suspend.rs | 21 +- 27 files changed, 1523 insertions(+), 2019 deletions(-) delete mode 100644 src/server/oldmanager.rs diff --git a/src/bin/krill.rs b/src/bin/krill.rs index 647352283..43302cdbf 100644 --- a/src/bin/krill.rs +++ b/src/bin/krill.rs @@ -11,13 +11,12 @@ use krill::daemon::start::start_krill_daemon; //------------ main ---------------------------------------------------------- -#[tokio::main] -async fn main() { +fn main() { let args = Args::parse(); match Config::create(&args.config, false) { Ok(config) => { - if let Err(e) = start_krill_daemon( config, None).await { + if let Err(e) = start_krill_daemon( config, None) { error!("Krill failed to start: {e}"); ::std::process::exit(1); } diff --git a/src/config.rs b/src/config.rs index 3bbfcb574..e0b638b0e 100644 --- a/src/config.rs +++ b/src/config.rs @@ -679,7 +679,7 @@ pub struct Config { pub ta_timing: TaTimingConfig, } -#[derive(Clone, Debug, Deserialize)] +#[derive(Clone, Copy, Debug, Deserialize)] #[serde(deny_unknown_fields)] pub struct IssuanceTimingConfig { #[serde(default = "ConfigDefaults::timing_publish_next_hours")] diff --git a/src/daemon/http/auth/authorizer.rs b/src/daemon/http/auth/authorizer.rs index da482c5a6..388422962 100644 --- a/src/daemon/http/auth/authorizer.rs +++ b/src/daemon/http/auth/authorizer.rs @@ -39,6 +39,7 @@ use super::providers::{config_file, openid_connect}; /// /// This type is a wrapper around the available backend specific auth /// providers that can be found in the [super::providers] module. +#[allow(clippy::large_enum_variant)] enum AuthProvider { Token(admin_token::AuthProvider), @@ -205,20 +206,20 @@ impl Authorizer { pub fn new(config: &Config) -> KrillResult { let (primary_provider, legacy_provider) = match config.auth_type { AuthType::AdminToken => { - (admin_token::AuthProvider::new(&config).into(), None) + (admin_token::AuthProvider::new(config).into(), None) } #[cfg(feature = "multi-user")] AuthType::ConfigFile => { ( - config_file::AuthProvider::new(&config)?.into(), - Some(admin_token::AuthProvider::new(&config)) + config_file::AuthProvider::new(config)?.into(), + Some(admin_token::AuthProvider::new(config)) ) } #[cfg(feature = "multi-user")] AuthType::OpenIDConnect => { ( - openid_connect::AuthProvider::new(&config)?.into(), - Some(admin_token::AuthProvider::new(&config)) + openid_connect::AuthProvider::new(config)?.into(), + Some(admin_token::AuthProvider::new(config)) ) } }; @@ -227,7 +228,7 @@ impl Authorizer { primary_provider, legacy_provider, #[cfg(unix)] - unix_socket_provider: unix_user::AuthProvider::new(&config)? + unix_socket_provider: unix_user::AuthProvider::new(config)? }) } diff --git a/src/daemon/http/auth/providers/openid_connect/provider.rs b/src/daemon/http/auth/providers/openid_connect/provider.rs index 3e1599082..3bbf5251d 100644 --- a/src/daemon/http/auth/providers/openid_connect/provider.rs +++ b/src/daemon/http/auth/providers/openid_connect/provider.rs @@ -184,7 +184,7 @@ pub struct AuthProvider { impl AuthProvider { pub fn new(config: &Config) -> KrillResult { - let session_key = Self::init_session_key(&config)?; + let session_key = Self::init_session_key(config)?; let Some(oidc_conf) = config.auth_openidconnect.as_ref() else { return Err(Error::ConfigError( diff --git a/src/daemon/http/dispatch/bulk.rs b/src/daemon/http/dispatch/bulk.rs index 4851a4e9f..4983a7ac5 100644 --- a/src/daemon/http/dispatch/bulk.rs +++ b/src/daemon/http/dispatch/bulk.rs @@ -40,7 +40,7 @@ async fn cas_import( request.check_post()?; let (request, _) = request.proceed_permitted(Permission::CaAdmin, None)?; let (server, structure) = request.read_json().await?; - server.old_krill().cas_import(structure).await?; + server.krill().cas_import(structure).await?; Ok(HttpResponse::ok()) } diff --git a/src/daemon/http/dispatch/cas.rs b/src/daemon/http/dispatch/cas.rs index 0fd4f21ae..f4f451653 100644 --- a/src/daemon/http/dispatch/cas.rs +++ b/src/daemon/http/dispatch/cas.rs @@ -121,7 +121,7 @@ async fn ca_index( Permission::CaDelete, Some(&ca) )?; let server = request.empty()?; - server.old_krill().ca_delete(&ca, auth.actor()).await?; + server.krill().ca_delete(ca, auth.into_actor()).await?; Ok(HttpResponse::ok()) } _ => Ok(HttpResponse::method_not_allowed()) @@ -163,7 +163,7 @@ async fn aspas_index( )?; let (server, updates) = request.read_json().await?; server.krill().ca_aspas_definitions_update( - ca, updates, auth.actor().clone(), + ca, updates, auth.into_actor(), ).await?; Ok(HttpResponse::ok()) } @@ -185,7 +185,7 @@ async fn aspas_as( )?; let (server, update) = request.read_json().await?; server.krill().ca_aspas_update_aspa( - ca, customer, update, auth.actor().clone() + ca, customer, update, auth.into_actor() ).await?; Ok(HttpResponse::ok()) } @@ -200,7 +200,7 @@ async fn aspas_as( add_or_replace: Vec::new(), remove: vec![customer] }, - auth.actor().clone(), + auth.into_actor(), ).await?; Ok(HttpResponse::ok()) } @@ -233,7 +233,7 @@ async fn bgpsec( )?; let (server, updates) = request.read_json().await?; server.krill().ca_bgpsec_definitions_update( - ca, updates, auth.actor().clone() + ca, updates, auth.into_actor() ).await?; Ok(HttpResponse::ok()) } @@ -266,7 +266,7 @@ async fn children_index( let (server, child_req) = request.read_json().await?; Ok(HttpResponse::json( &server.krill().ca_add_child( - ca, child_req, auth.actor().clone() + ca, child_req, auth.into_actor() ).await? )) } @@ -316,7 +316,7 @@ async fn children_child_index( )?; let (server, child_req) = request.read_json().await?; server.krill().ca_child_update( - ca, child, child_req, auth.actor().clone() + ca, child, child_req, auth.into_actor() ).await?; Ok(HttpResponse::ok()) } @@ -326,7 +326,7 @@ async fn children_child_index( )?; let server = request.empty()?; server.krill().ca_child_remove( - ca, child, auth.actor().clone() + ca, child, auth.into_actor() ).await?; Ok(HttpResponse::ok()) } @@ -403,7 +403,7 @@ async fn children_child_import( } )) } - server.krill().ca_child_import(ca, import, auth.actor().clone()).await?; + server.krill().ca_child_import(ca, import, auth.into_actor()).await?; Ok(HttpResponse::ok()) } @@ -507,7 +507,7 @@ async fn id_index( Permission::CaUpdate, Some(&ca) )?; let server = request.empty()?; - server.krill().ca_update_id(ca, auth.actor().clone()).await?; + server.krill().ca_update_id(ca, auth.into_actor()).await?; Ok(HttpResponse::ok()) } @@ -620,7 +620,7 @@ async fn keys_roll_init( Permission::CaUpdate, Some(&ca) )?; let server = request.empty()?; - server.krill().ca_keyroll_init(ca, auth.actor().clone()).await?; + server.krill().ca_keyroll_init(ca, auth.into_actor()).await?; Ok(HttpResponse::ok()) } @@ -635,7 +635,7 @@ async fn keys_roll_activate( Permission::CaUpdate, Some(&ca) )?; let server = request.empty()?; - server.krill().ca_keyroll_activate(ca, auth.actor().clone()).await?; + server.krill().ca_keyroll_activate(ca, auth.into_actor()).await?; Ok(HttpResponse::ok()) } @@ -673,8 +673,8 @@ async fn parents_index( )?; let (server, bytes) = request.read_bytes().await?; let parent_req = extract_parent_ca_req(&ca, bytes, None)?; - server.old_krill().ca_parent_add_or_update( - ca, parent_req, auth.actor() + server.krill().ca_parent_add_or_update( + ca, parent_req, auth.into_actor() ).await?; Ok(HttpResponse::ok()) } @@ -707,8 +707,8 @@ async fn parents_parent( let parent_req = extract_parent_ca_req( &ca, bytes, Some(parent) )?; - server.old_krill().ca_parent_add_or_update( - ca, parent_req, auth.actor() + server.krill().ca_parent_add_or_update( + ca, parent_req, auth.into_actor() ).await?; Ok(HttpResponse::ok()) } @@ -717,7 +717,9 @@ async fn parents_parent( Permission::CaUpdate, Some(&ca) )?; let server = request.empty()?; - server.old_krill().ca_parent_remove(ca, parent, auth.actor()).await?; + server.krill().ca_parent_remove( + ca, parent, auth.into_actor() + ).await?; Ok(HttpResponse::ok()) } _ => Ok(HttpResponse::method_not_allowed()) @@ -799,7 +801,9 @@ async fn repo_index( )?; let (server, update) = request.read_bytes().await?; let update = extract_repository_contact(&ca, update)?; - server.old_krill().ca_repo_update(ca, update, auth.actor()).await?; + server.krill().ca_repo_update( + ca, update, auth.into_actor(), + ).await?; Ok(HttpResponse::ok()) } _ => Ok(HttpResponse::method_not_allowed()) @@ -884,7 +888,7 @@ async fn routes_index( )?; let (server, updates) = request.read_json().await?; server.krill().ca_routes_update( - ca, updates, auth.actor().clone() + ca, updates, auth.into_actor() ).await?; Ok(HttpResponse::ok()) } @@ -921,7 +925,7 @@ async fn routes_try( } else { server.krill().ca_routes_update( - ca, updates, auth.actor().clone() + ca, updates, auth.into_actor() ).await?; Ok(HttpResponse::ok()) } diff --git a/src/daemon/http/dispatch/metrics.rs b/src/daemon/http/dispatch/metrics.rs index 6c91f6298..79b9dcb3e 100644 --- a/src/daemon/http/dispatch/metrics.rs +++ b/src/daemon/http/dispatch/metrics.rs @@ -67,10 +67,8 @@ pub async fn dispatch( if !server.config().metrics.metrics_hide_ca_details { - let ca_status_map = match server.krill().cas_status_map().await { - Ok(map) => map, - Err(_) => HashMap::new(), - }; + let ca_status_map = + server.krill().cas_status_map().await.unwrap_or_default(); let metric = Metric::gauge( "ca_parent_success", diff --git a/src/daemon/http/dispatch/pubd.rs b/src/daemon/http/dispatch/pubd.rs index 36c5a69e1..d7c72cb30 100644 --- a/src/daemon/http/dispatch/pubd.rs +++ b/src/daemon/http/dispatch/pubd.rs @@ -113,7 +113,7 @@ async fn publishers_index( let (server, pbl) = request.read_json().await?; Ok(HttpResponse::json( &server.krill().add_publisher( - pbl, auth.actor().clone() + pbl, auth.into_actor() ).await? )) } @@ -158,7 +158,7 @@ async fn publishers_publisher_index( )?; let server = request.empty()?; server.krill().remove_publisher( - publisher, auth.actor().clone() + publisher, auth.into_actor() ).await?; Ok(HttpResponse::ok()) } diff --git a/src/daemon/http/dispatch/root.rs b/src/daemon/http/dispatch/root.rs index 37ea2b5e4..ecaf4fd36 100644 --- a/src/daemon/http/dispatch/root.rs +++ b/src/daemon/http/dispatch/root.rs @@ -98,7 +98,7 @@ async fn rfc6492( // determine the actor when looking at the ID certificate? Ok(HttpResponse::rfc6492( server.krill().rfc6492( - ca , bytes, user_agent, auth.actor().clone() + ca , bytes, user_agent, auth.into_actor() ).await? )) } diff --git a/src/daemon/http/dispatch/ta.rs b/src/daemon/http/dispatch/ta.rs index 2d12c0098..759b672a3 100644 --- a/src/daemon/http/dispatch/ta.rs +++ b/src/daemon/http/dispatch/ta.rs @@ -70,7 +70,7 @@ async fn proxy_children_index( let (server, child) = request.read_json().await?; Ok(HttpResponse::json( &server.krill().ta_proxy_children_add( - child, auth.actor().clone() + child, auth.into_actor() ).await? )) } @@ -222,7 +222,7 @@ async fn proxy_repo_index( &ta_handle(), update )?; server.krill().ta_proxy_repository_update( - update, auth.actor().clone() + update, auth.into_actor() ).await?; Ok(HttpResponse::ok()) } @@ -282,7 +282,7 @@ async fn proxy_signer_add( Permission::CaAdmin, None )?; let (server, info) = request.read_json().await?; - server.krill().ta_proxy_signer_add(info, auth.actor().clone()).await?; + server.krill().ta_proxy_signer_add(info, auth.into_actor()).await?; Ok(HttpResponse::ok()) } @@ -308,7 +308,7 @@ async fn proxy_signer_request( let server = request.empty()?; Ok(HttpResponse::json( &server.krill().ta_proxy_signer_make_request( - auth.actor().clone() + auth.into_actor() ).await? )) } @@ -327,7 +327,7 @@ async fn proxy_signer_response( )?; let (server, response) = request.read_json().await?; server.krill().ta_proxy_signer_process_response( - response, auth.actor().clone() + response, auth.into_actor() ).await?; Ok(HttpResponse::ok()) } @@ -342,7 +342,7 @@ async fn proxy_signer_update( Permission::CaAdmin, None )?; let (server, info) = request.read_json().await?; - server.krill().ta_proxy_signer_update(info, auth.actor().clone()).await?; + server.krill().ta_proxy_signer_update(info, auth.into_actor()).await?; Ok(HttpResponse::ok()) } diff --git a/src/daemon/http/server.rs b/src/daemon/http/server.rs index f7bd1e53f..ca7d4b85f 100644 --- a/src/daemon/http/server.rs +++ b/src/daemon/http/server.rs @@ -11,7 +11,6 @@ use crate::commons::error::FatalError; use crate::config::Config; use crate::constants::KRILL_ENV_HTTP_LOG_INFO; use crate::server::manager::KrillManager; -use crate::server::oldmanager::OldManager; use super::auth::Authorizer; use super::dispatch::{DispatchError, dispatch_request}; use super::request::{BodyLimits, HyperRequest, Request}; @@ -26,15 +25,9 @@ pub struct HttpServer { /// The Krill server. krill: KrillManager, - /// The Krill “business logic.” - old_krill: OldManager, - /// The component responsible for API authorization checks authorizer: Authorizer, - /// A copy of the configuration. - config: Arc, - /// Time this server was started started: Timestamp, } @@ -43,17 +36,13 @@ impl HttpServer { /// Creates a new server from a Krill manager and the configuration. pub fn new( krill: KrillManager, - old_krill: OldManager, - config: Arc, runtime: &runtime::Handle, ) -> KrillResult> { - let authorizer = Authorizer::new(&config)?; + let authorizer = Authorizer::new(krill.config())?; authorizer.spawn_sweep(runtime); Ok(Self { krill, - old_krill, authorizer, - config, started: Timestamp::now(), }.into()) } @@ -67,7 +56,8 @@ impl HttpServer { &request ).await; let request = Request::new( - request, self, auth, BodyLimits::from_config(&self.config) + request, self, auth, + BodyLimits::from_config(self.krill.config()) ); let path = match request.path() { Ok(path) => path, @@ -105,11 +95,6 @@ impl HttpServer { &self.krill } - /// Returns a reference to the Krill manager. - pub(super) fn old_krill(&self) -> &OldManager { - &self.old_krill - } - /// Returns a reference to the authorizer. pub(super) fn authorizer(&self) -> &Authorizer { &self.authorizer @@ -117,7 +102,7 @@ impl HttpServer { /// Returns a reference to the configuration. pub fn config(&self) -> &Config { - &self.config + self.krill.config() } pub(super) fn server_info(&self) -> ServerInfo { diff --git a/src/daemon/start.rs b/src/daemon/start.rs index 117db6e06..948921900 100644 --- a/src/daemon/start.rs +++ b/src/daemon/start.rs @@ -2,21 +2,20 @@ use std::{env, process}; use std::net::SocketAddr; use std::path::Path; use std::sync::Arc; -use log::error; +use clap::crate_version; +use log::{error, info}; use hyper::service::service_fn; use hyper_util::rt::{TokioExecutor, TokioIo}; -use tokio::{runtime, select}; use tokio::net::TcpListener; use tokio::sync::oneshot; use tokio_rustls::TlsAcceptor; use crate::commons::file; -use crate::commons::error::Error; +use crate::commons::error::{Error, Error as KrillError}; use crate::commons::version::KrillVersion; use crate::config::Config; -use crate::constants::KRILL_ENV_UPGRADE_ONLY; +use crate::constants::{KRILL_ENV_UPGRADE_ONLY, KRILL_SERVER_APP}; use crate::server::properties::PropertiesManager; -use crate::server::manager::KrillManager; -use crate::server::oldmanager::OldManager; +use crate::server::manager::StartupManager; use crate::upgrades::{ finalise_data_migration, post_start_upgrade, prepare_upgrade_data_migrations, UpgradeError, UpgradeMode, @@ -25,11 +24,11 @@ use super::http::{tls, tls_keys}; use super::http::server::HttpServer; -pub async fn start_krill_daemon( +pub fn start_krill_daemon( config: Config, mut signal_running: Option>, ) -> Result<(), Error> { - let arc_config = Arc::new(config.clone()); + info!("Starting {} v{}", KRILL_SERVER_APP, crate_version!()); write_pid_file_or_die(&config); test_data_dirs_or_die(&config); @@ -83,17 +82,23 @@ pub async fn start_krill_daemon( properties_manager.init(KrillVersion::code_version())?; } - // Create the Krill manager, this will create the necessary data - // sub-directories if needed - let old_krill = OldManager::build(arc_config.clone()).await?; + // XXX TODO This may need some configuration. + let tokio = tokio::runtime::Runtime::new().map_err(|err| { + KrillError::custom( + format!("Failed to create Tokio runtime: {err}") + ) + })?; + + let krill = StartupManager::new(config, tokio.handle().clone())?; - let krill = KrillManager::new(config)?; + // Setup testbed if necessary. + krill.prepare_testbed()?; // Call post-start upgrades to trigger any upgrade related runtime // actions, such as re-issuing ROAs because subject name strategy has // changed. if let Some(report) = upgrade_report { - post_start_upgrade(report, &old_krill).await?; + post_start_upgrade(report, &krill)?; } // If the operator wanted to do the upgrade only, now is a good time to @@ -103,15 +108,11 @@ pub async fn start_krill_daemon( std::process::exit(0); } - // Build the scheduler which will be responsible for executing - // planned/triggered tasks - let scheduler = old_krill.build_scheduler(); - let scheduler_future = scheduler.run(); + krill.run_scheduler()?; + let krill = krill.promote(); // Create the HTTP server. - let server = HttpServer::new( - krill, old_krill, arc_config.clone(), &runtime::Handle::current() - )?; + let server = HttpServer::new(krill, &tokio.handle())?; // Create self-signed HTTPS cert if configured and not generated earlier. if server.config().https_mode().is_generate_https_cert() { @@ -120,35 +121,29 @@ pub async fn start_krill_daemon( } // Start a hyper server for the configured http sockets. - let http_server_futures = futures_util::future::select_all( - server.config().socket_addresses().into_iter().map(|socket_addr| { - tokio::spawn(single_http_listener( - server.clone(), - socket_addr, - arc_config.clone(), - signal_running.take(), - )) - }), - ); + + for socket_addr in server.config().socket_addresses().into_iter() { + tokio.spawn(single_http_listener( + server.clone(), + socket_addr, + signal_running.take(), + )); + } // Start a hyper server for the configured unix sockets. // We do not await these, as they are not required #[cfg(unix)] if server.config().unix_socket_enabled() { - server.config().unix_socket().map(|path| { - tokio::spawn(single_unix_listener( + if let Some(path) = server.config().unix_socket() { + tokio.spawn(single_unix_listener( server.clone(), path.clone(), - arc_config.clone(), signal_running.take(), - )) - }); + )); + } } - select!( - _ = http_server_futures => error!("http server stopped unexpectedly"), - _ = scheduler_future => error!("scheduler stopped unexpectedly"), - ); + tokio.block_on(futures_util::future::pending::<()>()); Err(Error::custom("stopping krill process")) } @@ -157,7 +152,6 @@ pub async fn start_krill_daemon( async fn single_http_listener( server: Arc, addr: SocketAddr, - config: Arc, signal_running: Option>, ) { let listener = match TcpListener::bind(addr).await { @@ -168,12 +162,12 @@ async fn single_http_listener( } }; - let tls = if config.https_mode().is_disable_https() { + let tls = if server.config().https_mode().is_disable_https() { None } else { match tls::create_server_config( - &tls_keys::key_file_path(config.tls_keys_dir()), - &tls_keys::cert_file_path(config.tls_keys_dir()), + &tls_keys::key_file_path(server.config().tls_keys_dir()), + &tls_keys::cert_file_path(server.config().tls_keys_dir()), ) { Ok(config) => Some(TlsAcceptor::from(Arc::new(config))), Err(err) => { @@ -219,7 +213,6 @@ async fn single_http_listener( async fn single_unix_listener( server: Arc, path: std::path::PathBuf, - _config: Arc, signal_running: Option>, ) { use tokio::net::UnixListener; diff --git a/src/server/ca/certauth.rs b/src/server/ca/certauth.rs index e1198d1f3..b929283c0 100644 --- a/src/server/ca/certauth.rs +++ b/src/server/ca/certauth.rs @@ -677,7 +677,7 @@ impl Aggregate for CertAuth { // certificates and/or generate manifests and CRLs when relevant // changes occur in a `CertAuth`. krill.ca_manager().ca_objects_store().cert_auth_pre_save_events( - self, events + self, events, krill )?; // Let the [`TaskQueue`] handle events pre-save so @@ -1387,7 +1387,7 @@ impl CertAuth { resources, limit, &config.issuance_timing, - &signer, + signer, )?; let cert_name = ObjectName::from_key(&issued.key_identifier(), "cer"); @@ -1752,7 +1752,7 @@ impl CertAuth { &self, signer: &KrillSigner, ) -> KrillResult> { - let id = Rfc8183Id::generate(&signer)?; + let id = Rfc8183Id::generate(signer)?; info!( "CA '{}' generated new ID certificate with key id: {}", @@ -1919,7 +1919,7 @@ impl CertAuth { self.handle(), ent, &self.repository_contact()?.repo_info, - &signer, + signer, &mut res, )?; } @@ -1957,7 +1957,7 @@ impl CertAuth { self.handle(), ent, &self.repository_contact()?.repo_info, - &signer, + signer, &mut res )?; } diff --git a/src/server/ca/manager.rs b/src/server/ca/manager.rs index 3f974cad3..a692e4bed 100644 --- a/src/server/ca/manager.rs +++ b/src/server/ca/manager.rs @@ -21,6 +21,7 @@ use rpki::ca::publication::{ }; use rpki::crypto::KeyIdentifier; use rpki::repository::resources::ResourceSet; +use tokio::sync::oneshot; use crate::api::admin::{ AddChildRequest, ParentCaContact, ParentCaReq, ParentServerInfo, PublicationServerInfo, PublishedFile, RepositoryContact, @@ -51,7 +52,6 @@ use crate::commons::httpclient; use crate::commons::KrillResult; use crate::commons::actor::Actor; use crate::commons::cmslogger::CmsLogger; -use crate::commons::crypto::KrillSigner; use crate::commons::error::{Error, Error as KrillError}; use crate::commons::eventsourcing::{Aggregate, AggregateStore, SentCommand}; use crate::constants::{ @@ -59,8 +59,7 @@ use crate::constants::{ ta_handle, }; use crate::config::Config; -use crate::server::mq::{now, Task, TaskQueue}; -use crate::server::pubd::RepositoryManager; +use crate::server::mq::{now, Task}; use crate::server::runtime::KrillRuntime; use crate::server::taproxy::{ TrustAnchorProxy, TrustAnchorProxyCommand, TrustAnchorProxyInitCommand, @@ -110,37 +109,14 @@ pub struct CaManager { /// without the need for user interactions through the API and /// TA signer CLI. ta_signer_store: Option>, - - /// The task queue. - /// - /// This queue: - /// - listens for events in the ca_store, - /// - is processed by the Scheduler, - /// - can be used here to schedule tasks through the API. - tasks: Arc, - - /// The server configuration. - config: Arc, - - /// The signer. - signer: Arc, - - /// The actor used for all thing Krill does itself. - /// - /// This actor is used for (scheduled or triggered) system actions where - /// we have no operator actor context. - system_actor: Actor, } impl CaManager { /// Builds a new CA manager. /// /// Return an error if any of the various stores cannot be initialized. - pub async fn build( - config: Arc, - tasks: Arc, - signer: Arc, - system_actor: Actor, + pub fn new( + config: &Config, ) -> KrillResult { // Create the AggregateStore for the event-sourced `CertAuth` // structures that handle most CA functions. @@ -177,8 +153,7 @@ impl CaManager { // for manifests and CRL generation. let ca_objects_store = Arc::new(CaObjectsStore::create( &config.storage_uri, - config.issuance_timing.clone(), - signer.clone(), + config.issuance_timing, )?); // Create TA proxy store if we need it. @@ -217,18 +192,9 @@ impl CaManager { status_store, ta_proxy_store, ta_signer_store, - tasks, - config, - signer, - system_actor, }) } - /// Returns whether testbed mode is enabled. - pub fn testbed_enabled(&self) -> bool { - self.config.testbed().is_some() - } - /// Processes a command for a CA. /// /// The command will be processed on the latest version of the CA. @@ -256,10 +222,11 @@ impl CaManager { pub fn republish_all( &self, force: bool, + krill: &KrillRuntime ) -> KrillResult> { let mut res = vec![]; for ca in self.ca_store.list()? { - match self.ca_objects_store.reissue_if_needed(force, &ca) { + match self.ca_objects_store.reissue_if_needed(force, &ca, krill) { Err(e) => { error!( "Could not reissue manifest and crl for {ca}.\ @@ -363,7 +330,7 @@ impl CaManager { ta_proxy_store.add_with_context( TrustAnchorProxyInitCommand::make( ta_handle, - &self.system_actor, + krill.system_actor(), ), krill.into(), )?; @@ -406,7 +373,7 @@ impl CaManager { let cmd = TrustAnchorSignerInitCommand::new( handle, details, - &self.system_actor, + krill.system_actor(), ); ta_signer_store.add_with_context(cmd, krill.into())?; @@ -504,15 +471,15 @@ impl CaManager { self.send_ta_proxy_command( TrustAnchorProxyCommand::make_signer_request(&ta_handle(), actor), krill - )?.get_signer_request(self.config.ta_timing, &self.signer) + )?.get_signer_request(krill.config().ta_timing, krill.signer()) } /// Returns the current request for the signer. pub fn ta_proxy_signer_get_request( - &self, + &self, krill: &KrillRuntime, ) -> KrillResult { self.get_trust_anchor_proxy()?.get_signer_request( - self.config.ta_timing, &self.signer + krill.config().ta_timing, krill.signer() ) } @@ -535,12 +502,11 @@ impl CaManager { } /// Initializes an embedded trust anchor with all resources. - pub async fn ta_init_fully_embedded( + pub fn ta_init_fully_embedded( &self, ta_aia: uri::Rsync, ta_uris: Vec, ta_key_pem: Option, - repo_manager: &Arc, actor: &Actor, krill: &KrillRuntime, ) -> KrillResult<()> { @@ -553,25 +519,30 @@ impl CaManager { let pub_req = self.ta_proxy_publisher_request()?; // Create publisher - repo_manager.create_publisher(pub_req, actor)?; - let repository_response = - repo_manager.repository_response(&ta_handle.convert())?; + krill.repo_manager().create_publisher(pub_req, actor)?; + let repository_response = krill.repo_manager().repository_response( + &ta_handle.convert(), krill + )?; // Add repository to proxy let contact = RepositoryContact::try_from_response( repository_response ).map_err(Error::rfc8183)?; - self.ta_proxy_repository_update(contact, &self.system_actor, krill)?; + self.ta_proxy_repository_update( + contact, krill.system_actor(), krill + )?; // Initialise signer self.ta_signer_init(ta_uris, ta_aia, ta_key_pem, krill)?; // Add signer to proxy let signer_info = self.get_trust_anchor_signer()?.get_signer_info(); - self.ta_proxy_signer_add(signer_info, &self.system_actor, krill)?; + self.ta_proxy_signer_add( + signer_info, krill.system_actor(), krill + )?; self.sync_ta_proxy_signer_if_possible(krill)?; - self.cas_repo_sync_single(repo_manager, &ta_handle, 0).await?; + self.cas_repo_sync_single(&ta_handle, 0, krill)?; Ok(()) } @@ -580,7 +551,7 @@ impl CaManager { pub fn ta_renew_testbed_ta( &self, krill: &KrillRuntime, ) -> KrillResult<()> { - if self.testbed_enabled() { + if krill.is_testbed_enabled() { let proxy = self.get_trust_anchor_proxy()?; if !proxy.has_open_request() { info!("Renew the testbed TA"); @@ -613,7 +584,7 @@ impl CaManager { CertAuthInitCommand::new( handle, CertAuthInitCommandDetails, - &self.system_actor, + krill.system_actor(), ), krill )?; Ok(()) @@ -715,9 +686,8 @@ impl CaManager { /// Does best effort revocation requests and withdraws all its objects /// first. Note that any children of this CA will be left orphaned, and /// they will only learn of this sad fact when they choose to call home. - pub async fn delete_ca( + pub fn delete_ca( &self, - repo_manager: &RepositoryManager, ca_handle: &CaHandle, actor: &Actor, krill: &KrillRuntime, @@ -732,7 +702,7 @@ impl CaManager { before removing it." ); for parent in ca.parents() { - if let Err(e) = self.ca_parent_revoke(ca_handle, parent, krill).await { + if let Err(e) = self.ca_parent_revoke(ca_handle, parent, krill) { warn!( "Removing CA '{ca_handle}', but could not send revoke request \ to parent '{parent}': {e}" @@ -758,12 +728,12 @@ impl CaManager { for repo_contact in repos { if self.ca_repo_sync( - repo_manager, ca_handle, ca.id_cert(), &repo_contact, vec![], - ).await.is_err() { + krill + ).is_err() { info!( "Could not clean up deprecated repository. This is \ fine - objects there are no longer referenced." @@ -1056,7 +1026,7 @@ impl CaManager { // Create a logger for CMS (avoid cloning recipient) let cms_logger = CmsLogger::for_rfc6492_rcvd( - self.config.rfc6492_log_dir.as_ref(), + krill.config().rfc6492_log_dir.as_ref(), req_msg.recipient(), req_msg.sender(), ); @@ -1067,7 +1037,7 @@ impl CaManager { Ok(msg) => { let should_log_cms = !msg.is_list_response(); let reply_bytes = ca.sign_rfc6492_response( - msg, &self.signer + msg, krill.signer() )?; if should_log_cms { @@ -1132,7 +1102,7 @@ impl CaManager { ) } provisioning::Payload::List => { - self.rfc6492_list(ca_handle, &child_handle) + self.rfc6492_list(ca_handle, &child_handle, krill) } provisioning::Payload::Issue(req) => { self.rfc6492_issue( @@ -1187,13 +1157,16 @@ impl CaManager { &self, ca_handle: &CaHandle, child: &ChildHandle, + krill: &KrillRuntime, ) -> KrillResult { let list_response = if ca_handle.as_str() != TA_NAME { - self.get_ca(ca_handle)?.list(child, &self.config.issuance_timing) + self.get_ca(ca_handle)?.list( + child, &krill.config().issuance_timing + ) } else { self.get_trust_anchor_proxy()?.entitlements( - child, &self.config.ta_timing + child, &krill.config().ta_timing ).map(|entitlements| { ResourceClassListResponse::new(vec![entitlements]) }) @@ -1248,7 +1221,7 @@ impl CaManager { &child_handle, &my_rcn, pub_key, - &self.config.issuance_timing, + &krill.config().issuance_timing, )?; Ok(provisioning::Message::issue_response( @@ -1405,7 +1378,7 @@ impl CaManager { /// this parent are requested. Any resource classes under the parent will /// be removed and all relevant content will be withdrawn from the /// repository. - pub async fn ca_parent_remove( + pub fn ca_parent_remove( &self, handle: CaHandle, parent: ParentHandle, @@ -1414,7 +1387,7 @@ impl CaManager { ) -> KrillResult<()> { // Best effort, request revocations for any remaining keys under this // parent. - if let Err(e) = self.ca_parent_revoke(&handle, &parent, krill).await { + if let Err(e) = self.ca_parent_revoke(&handle, &parent, krill) { warn!( "Removing parent '{parent}' from CA '{handle}', but could not send \ revoke requests: {e}" @@ -1431,16 +1404,15 @@ impl CaManager { } /// Sends revocation requests for a parent of a CA. - async fn ca_parent_revoke( + fn ca_parent_revoke( &self, handle: &CaHandle, parent: &ParentHandle, krill: &KrillRuntime, ) -> KrillResult<()> { let ca = self.get_ca(handle)?; - let revoke_requests = ca.revoke_under_parent(parent, &self.signer)?; - self.send_revoke_requests(handle, parent, revoke_requests, krill) - .await?; + let revoke_requests = ca.revoke_under_parent(parent, krill.signer())?; + self.send_revoke_requests(handle, parent, revoke_requests, krill)?; Ok(()) } @@ -1449,10 +1421,12 @@ impl CaManager { /// Note: this function can be called manually through the API, but /// normally the CA refresh process is replanned on the task /// queue automatically. - pub fn cas_schedule_refresh_all(&self) -> KrillResult<()> { + pub fn cas_schedule_refresh_all( + &self, krill: &KrillRuntime + ) -> KrillResult<()> { if let Ok(cas) = self.ca_store.list() { for ca_handle in cas { - self.cas_schedule_refresh_single(ca_handle)?; + self.cas_schedule_refresh_single(ca_handle, krill)?; } } Ok(()) @@ -1462,10 +1436,9 @@ impl CaManager { /// /// This possibly also suspend inactive children. pub fn cas_schedule_refresh_single( - &self, - ca_handle: CaHandle, + &self, ca_handle: CaHandle, krill: &KrillRuntime ) -> KrillResult<()> { - self.ca_schedule_sync_parents(&ca_handle) + self.ca_schedule_sync_parents(&ca_handle, krill) } /// Schedules an immediate check suspending all inactive children. @@ -1476,11 +1449,13 @@ impl CaManager { /// While this function can be called manually through the API, it is /// normally replanned on the task queue automatically if suspension is /// enabled. - pub fn cas_schedule_suspend_all(&self) -> KrillResult<()> { - if self.config.suspend_child_after_inactive_seconds().is_some() { + pub fn cas_schedule_suspend_all( + &self, krill: &KrillRuntime + ) -> KrillResult<()> { + if krill.config().suspend_child_after_inactive_seconds().is_some() { if let Ok(cas) = self.ca_store.list() { for ca in cas { - self.tasks.schedule( + krill.tasks().schedule( Task::SuspendChildrenIfNeeded { ca_handle: ca }, now(), )?; @@ -1506,7 +1481,7 @@ impl CaManager { // suspended on upgrade, or that *all* children are suspended // if the server had been down for more than the threshold hours. let threshold_seconds = - self.config.suspend_child_after_inactive_seconds() + krill.config().suspend_child_after_inactive_seconds() .filter(|secs| started < Timestamp::now_minus_seconds(*secs)); // suspend inactive children, if so configured @@ -1559,16 +1534,17 @@ impl CaManager { fn ca_schedule_sync_parents( &self, ca_handle: &CaHandle, + krill: &KrillRuntime, ) -> KrillResult<()> { let Ok(ca) = self.get_ca(ca_handle) else { return Ok(()) }; - if ca.nr_parents() <= self.config.ca_refresh_parents_batch_size { + if ca.nr_parents() <= krill.config().ca_refresh_parents_batch_size { // Nr of parents is below batch size, so just process all // of them for parent in ca.parents() { - self.tasks.schedule( + krill.tasks().schedule( Task::SyncParent { ca_handle: ca_handle.clone(), ca_version: 0, @@ -1586,9 +1562,9 @@ impl CaManager { for parent in status.parents().sync_candidates( ca.parents().collect(), - self.config.ca_refresh_parents_batch_size, + krill.config().ca_refresh_parents_batch_size, ) { - self.tasks.schedule( + krill.tasks().schedule( Task::SyncParent { ca_handle: ca_handle.clone(), ca_version: 0, @@ -1619,7 +1595,7 @@ impl CaManager { /// /// This method is called by the scheduler in response to the scheduled /// sync as well as `KrillServer` when importing a CA. - pub async fn ca_sync_parent( + pub fn ca_sync_parent( &self, handle: &CaHandle, min_ca_version: u64, // set this 0 if it does not matter @@ -1639,12 +1615,12 @@ impl CaManager { } else { if ca.has_pending_requests(parent) { - self.send_requests(handle, parent, actor, krill).await?; + self.send_requests(handle, parent, actor, krill)?; } else { self.get_updates_from_parent( handle, parent, actor, krill, - ).await?; + )?; } Ok(true) } @@ -1680,15 +1656,15 @@ impl CaManager { let proxy = self.send_ta_proxy_command( TrustAnchorProxyCommand::make_signer_request( &ta_handle, - &self.system_actor, + krill.system_actor(), ), krill, )?; // Get sign request for signer. let signed_request = proxy.get_signer_request( - self.config.ta_timing, - &self.signer, + krill.config().ta_timing, + krill.signer(), )?; // Remember the noce of the request so we can retrieve it. @@ -1700,7 +1676,7 @@ impl CaManager { &ta_handle, signed_request.into(), None, // do not override next manifest number - &self.system_actor, + krill.system_actor(), ), krill, )?; @@ -1711,7 +1687,7 @@ impl CaManager { TrustAnchorProxyCommand::process_signer_response( &ta_handle, exchange.clone().response, - &self.system_actor, + krill.system_actor(), ), krill, )?; @@ -1721,7 +1697,7 @@ impl CaManager { /// Tries to get updates from a specific parent of a CA. /// /// Quietly does nothing for the TA CA. - async fn get_updates_from_parent( + fn get_updates_from_parent( &self, handle: &CaHandle, parent: &ParentHandle, @@ -1742,7 +1718,7 @@ impl CaManager { let parent_contact = ca.parent(parent)?; let entitlements = self.get_entitlements_from_contact( handle, parent, parent_contact, true, krill, - ).await?; + )?; self.update_entitlements( handle, parent.clone(), entitlements, actor, krill, @@ -1755,20 +1731,20 @@ impl CaManager { /// /// First sends all open revoke requests, then sends all open /// certificate requests. - async fn send_requests( + fn send_requests( &self, handle: &CaHandle, parent: &ParentHandle, actor: &Actor, krill: &KrillRuntime, ) -> KrillResult<()> { self.send_revoke_requests_handle_responses( handle, parent, actor, krill, - ).await?; + )?; self.send_cert_requests_handle_responses( handle, parent, actor, krill, - ).await + ) } /// Sends all open revocation requests and handles the responses. - async fn send_revoke_requests_handle_responses( + fn send_revoke_requests_handle_responses( &self, handle: &CaHandle, parent: &ParentHandle, actor: &Actor, krill: &KrillRuntime, ) -> KrillResult<()> { @@ -1777,7 +1753,7 @@ impl CaManager { let revoke_responses = self.send_revoke_requests( handle, parent, requests, krill, - ).await?; + )?; for (rcn, revoke_responses) in revoke_responses { for response in revoke_responses { @@ -1798,7 +1774,7 @@ impl CaManager { /// Sends the given revoke requests to a parent. /// /// Returns the responses for the requests. - pub async fn send_revoke_requests( + pub fn send_revoke_requests( &self, handle: &CaHandle, parent: &ParentHandle, @@ -1813,7 +1789,7 @@ impl CaManager { &child.id_cert().public_key.key_identifier(), server_info, krill, - ) .await { + ) { Err(e) => { self.status_store.set_parent_failure( handle, parent, &server_info.service_uri, &e @@ -1830,7 +1806,7 @@ impl CaManager { } /// Sends a revoke request for an unexpected key. - pub async fn send_revoke_unexpected_key( + pub fn send_revoke_unexpected_key( &self, handle: &CaHandle, rcn: ResourceClassName, @@ -1843,11 +1819,11 @@ impl CaManager { let mut requests = HashMap::new(); requests.insert(rcn, vec![revocation]); - self.send_revoke_requests(handle, parent, requests, krill).await + self.send_revoke_requests(handle, parent, requests, krill) } /// Sends revoke requests using the provisioning protocol. - async fn send_revoke_requests_rfc6492( + fn send_revoke_requests_rfc6492( &self, revoke_requests: HashMap>, signing_key: &KeyIdentifier, @@ -1868,7 +1844,7 @@ impl CaManager { let response = self.send_rfc6492_and_validate_response( revoke, server_info, signing_key, krill - ) .await?; + )?; let payload = response.into_payload(); let payload_type = payload.payload_type(); @@ -1946,7 +1922,7 @@ impl CaManager { } /// Sends certification requests to a parent CA and proceses the response. - async fn send_cert_requests_handle_responses( + fn send_cert_requests_handle_responses( &self, ca_handle: &CaHandle, parent: &ParentHandle, actor: &Actor, krill: &KrillRuntime, ) -> KrillResult<()> { @@ -1980,7 +1956,7 @@ impl CaManager { server_info, &signing_key, krill - ).await { + ) { Err(e) => { // If any of the requests for an RC results in an // error, then record the @@ -2304,7 +2280,7 @@ impl CaManager { } /// Requests the entitlements from the parent. - pub async fn get_entitlements_from_contact( + pub fn get_entitlements_from_contact( &self, ca: &CaHandle, parent: &ParentHandle, @@ -2317,7 +2293,7 @@ impl CaManager { let result = self.get_entitlements_rfc6492( ca, server_info, krill - ).await; + ); match &result { Err(error) => { @@ -2344,7 +2320,7 @@ impl CaManager { } /// Performs the provisioning protocol exchange for entitlements. - async fn get_entitlements_rfc6492( + fn get_entitlements_rfc6492( &self, handle: &CaHandle, server_info: &ParentServerInfo, @@ -2369,7 +2345,7 @@ impl CaManager { server_info, &child.id_cert().public_key.key_identifier(), krill, - ).await?; + )?; let payload = response.into_payload(); let payload_type = payload.payload_type(); @@ -2388,7 +2364,7 @@ impl CaManager { } /// Sends a provisioning message and validates the response. - async fn send_rfc6492_and_validate_response( + fn send_rfc6492_and_validate_response( &self, message: provisioning::Message, server_info: &ParentServerInfo, @@ -2397,7 +2373,7 @@ impl CaManager { ) -> KrillResult { let service_uri = &server_info.service_uri; if let Some(parent) = Self::local_parent( - service_uri, &self.config.service_uri() + service_uri, krill.service_uri() ) { let ca_handle = parent.into_converted(); let user_agent = Some("local-child".to_string()); @@ -2406,7 +2382,7 @@ impl CaManager { &ca_handle, message, user_agent, - &self.system_actor, + krill.system_actor(), krill, ) } @@ -2419,21 +2395,22 @@ impl CaManager { let recipient = message.recipient().clone(); let cms_logger = CmsLogger::for_rfc6492_sent( - self.config.rfc6492_log_dir.as_ref(), + krill.config().rfc6492_log_dir.as_ref(), &sender, &recipient, ); - let cms = self.signer.create_rfc6492_cms( + let cms = krill.signer().create_rfc6492_cms( message, signing_key )?.to_bytes(); let res_bytes = self.post_protocol_cms_binary( - &cms, - service_uri, + cms, + service_uri.clone(), provisioning::CONTENT_TYPE, &cms_logger, - ).await?; + krill, + )?; match ProvisioningCms::decode(&res_bytes) { Err(e) => { @@ -2466,30 +2443,47 @@ impl CaManager { } /// Posts a protocol message via HTTP and receives a response. - async fn post_protocol_cms_binary( + fn post_protocol_cms_binary( &self, - msg: &Bytes, - service_uri: &ServiceUri, - content_type: &str, + msg: Bytes, + service_uri: ServiceUri, + content_type: &'static str, cms_logger: &CmsLogger, + krill: &KrillRuntime, ) -> KrillResult { - cms_logger.sent(msg)?; - - let timeout = self.config.post_protocol_msg_timeout_seconds; - - match httpclient::post_binary_with_full_ua( - service_uri.as_str(), - msg, - content_type, - timeout, - ).await { - Err(e) => { + cms_logger.sent(&msg)?; + + let timeout = krill.config().post_protocol_msg_timeout_seconds; + + let (tx, rx) = oneshot::channel(); + let http_uri = service_uri.clone(); + krill.spawn_async(async move { + let _ = tx.send( + httpclient::post_binary_with_full_ua( + http_uri.as_str(), + &msg, + content_type, + timeout, + ).await + ); + }); + match rx.blocking_recv() { + Err(_) => { cms_logger.err(format!( - "Error posting CMS to {service_uri}: {e}" + "Error posting CMS to {service_uri}: \ + internal error: HTTP task disappeared." ))?; - Err(Error::HttpClientError(e)) + Err(KrillError::InternalError( + "HTPP task disappeared".into() + )) } - Ok(bytes) => { + Ok(Err(err)) => { + cms_logger.err(format!( + "Error posting CMS to {service_uri}: {err}" + ))?; + Err(Error::HttpClientError(err)) + } + Ok(Ok(bytes)) => { cms_logger.reply(&bytes)?; Ok(bytes) } @@ -2527,22 +2521,21 @@ impl CaManager { impl CaManager { /// Schedules synchronizing all CAs with their repositories. pub fn cas_schedule_repo_sync_all( - &self, + &self, krill: &KrillRuntime ) -> KrillResult<()> { for ca in self.ca_handles()? { - self.cas_schedule_repo_sync(ca)?; + self.cas_schedule_repo_sync(ca, krill)?; } Ok(()) } /// Schedules synchronizing a CA with its repositories. pub fn cas_schedule_repo_sync( - &self, - ca_handle: CaHandle, + &self, ca_handle: CaHandle, krill: &KrillRuntime ) -> KrillResult<()> { // no need to wait for an updated CA to be committed. let ca_version = 0; - self.tasks.schedule( + krill.tasks().schedule( Task::SyncRepo { ca_handle, ca_version, @@ -2569,11 +2562,11 @@ impl CaManager { /// attempts, then the old repository is assumed to be unreachable and /// it will be dropped - i.e. the CA will no longer try to clean up /// objects. - pub async fn cas_repo_sync_single( + pub fn cas_repo_sync_single( &self, - repo_manager: &RepositoryManager, ca_handle: &CaHandle, ca_version: u64, + krill: &KrillRuntime, ) -> KrillResult { // Note that this is a no-op for new CAs which do not yet have any // repository configured. @@ -2589,9 +2582,7 @@ impl CaManager { )?; let objects = proxy.get_trust_anchor_objects()? .publish_elements()?; - self.ca_repo_sync( - repo_manager, ca_handle, id, repo, objects - ).await?; + self.ca_repo_sync(ca_handle, id, repo, objects, krill)?; Ok(true) } } @@ -2613,12 +2604,12 @@ impl CaManager { self.ca_repo_elements(ca_handle)? { self.ca_repo_sync( - repo_manager, ca_handle, ca.id_cert(), &repo_contact, objects, - ).await?; + krill, + )?; } // Clean-up of old repos @@ -2631,12 +2622,12 @@ impl CaManager { ); if let Err(e) = self.ca_repo_sync( - repo_manager, ca_handle, ca.id_cert(), deprecated.contact(), vec![], - ).await { + krill, + ) { warn!( "Could not clean up deprecated repository: {e}" ); @@ -2662,21 +2653,21 @@ impl CaManager { } /// Synchronizes with the repository. - async fn ca_repo_sync( + fn ca_repo_sync( &self, - repo_manager: &RepositoryManager, ca_handle: &CaHandle, id_cert: &IdCertInfo, repo_contact: &RepositoryContact, publish_elements: Vec, + krill: &KrillRuntime, ) -> KrillResult<()> { debug!("CA '{ca_handle}' sends list query to repo"); let list_reply = self.send_rfc8181_list( - repo_manager, ca_handle, id_cert, &repo_contact.server_info, - ).await?; + krill, + )?; // XXX Do we really need hash maps here? In particular, this will // quietly overwrite double URLs which we should probably catch? @@ -2710,12 +2701,12 @@ impl CaManager { if !delta.is_empty() { debug!("CA '{ca_handle}' sends delta"); self.send_rfc8181_delta( - repo_manager, ca_handle, id_cert, &repo_contact.server_info, delta, - ).await?; + krill + )?; debug!("CA '{ca_handle}' sent delta"); } else { @@ -2781,9 +2772,8 @@ impl CaManager { /// /// If `check_repo` is `true`, checks that the repository can be reached /// and returns an error if not. - pub async fn update_repo( + pub fn update_repo( &self, - repo_manager: &RepositoryManager, ca_handle: CaHandle, new_contact: RepositoryContact, check_repo: bool, @@ -2795,11 +2785,11 @@ impl CaManager { // First verify that this repository can be reached and responds // to a list request. self.send_rfc8181_list( - repo_manager, &ca_handle, ca.id_cert(), &new_contact.server_info, - ).await.map_err(|e| { + krill + ).map_err(|e| { Error::CaRepoIssue(ca_handle.clone(), e.to_string()) })?; } @@ -2812,24 +2802,24 @@ impl CaManager { } /// Sends a publication protocol list request and returns the reply. - async fn send_rfc8181_list( + fn send_rfc8181_list( &self, - repo_manager: &RepositoryManager, ca_handle: &CaHandle, id_cert: &IdCertInfo, server_info: &PublicationServerInfo, + krill: &KrillRuntime, ) -> KrillResult { let signing_key = id_cert.public_key.key_identifier(); let message = publication::Message::list_query(); let reply = match self.send_rfc8181_and_validate_response( - repo_manager, message, server_info, ca_handle, &signing_key, - ).await { + krill, + ) { Ok(reply) => reply, Err(e) => { self.status_store.set_status_repo_failure( @@ -2870,25 +2860,25 @@ impl CaManager { } /// Sends a publication protocol delta request. - async fn send_rfc8181_delta( + fn send_rfc8181_delta( &self, - repo_manager: &RepositoryManager, ca_handle: &CaHandle, id_cert: &IdCertInfo, server_info: &PublicationServerInfo, delta: PublishDelta, + krill: &KrillRuntime, ) -> KrillResult<()> { let signing_key = id_cert.public_key.key_identifier(); let message = publication::Message::delta(delta.clone()); let reply = match self.send_rfc8181_and_validate_response( - repo_manager, message, server_info, ca_handle, &signing_key, - ).await { + krill, + ) { Ok(reply) => reply, Err(e) => { self.status_store.set_status_repo_failure( @@ -2931,44 +2921,45 @@ impl CaManager { } /// Sends a publication protocol request and validates the response. - async fn send_rfc8181_and_validate_response( + fn send_rfc8181_and_validate_response( &self, - repo_manager: &RepositoryManager, message: publication::Message, server_info: &PublicationServerInfo, ca_handle: &CaHandle, signing_key: &KeyIdentifier, + krill: &KrillRuntime, ) -> KrillResult { let repo_service_uri = &server_info.service_uri; if repo_service_uri.as_str().starts_with( - self.config.service_uri().as_str() + krill.service_uri().as_str() ) { // this maps back to *this* Krill instance let query = message.as_query()?; let publisher_handle = ca_handle.convert(); - let response = repo_manager.rfc8181_message( - &publisher_handle, query + let response = krill.repo_manager().rfc8181_message( + &publisher_handle, query, krill )?; response.as_reply().map_err(Error::Rfc8181) } else { // Set up a logger for CMS exchanges. let cms_logger = CmsLogger::for_rfc8181_sent( - self.config.rfc8181_log_dir.as_ref(), + krill.config().rfc8181_log_dir.as_ref(), ca_handle, ); - let cms = self.signer.create_rfc8181_cms( + let cms = krill.signer().create_rfc8181_cms( message, signing_key )?.to_bytes(); let res_bytes = self.post_protocol_cms_binary( - &cms, - repo_service_uri, + cms, + repo_service_uri.clone(), publication::CONTENT_TYPE, &cms_logger, - ).await?; + krill, + )?; match publication::PublicationCms::decode(&res_bytes) { Err(e) => { diff --git a/src/server/ca/publishing.rs b/src/server/ca/publishing.rs index 9db5f00ff..9098006ae 100644 --- a/src/server/ca/publishing.rs +++ b/src/server/ca/publishing.rs @@ -2,7 +2,6 @@ use std::collections::HashMap; use std::str::FromStr; -use std::sync::Arc; use chrono::Duration; use log::debug; use rpki::{rrdp, uri}; @@ -28,6 +27,7 @@ use crate::commons::error::Error; use crate::commons::storage::{Ident, KeyValueStore}; use crate::constants::CA_OBJECTS_NS; use crate::config::IssuanceTimingConfig; +use crate::server::runtime::KrillRuntime; use super::aspa::{AspaInfo, AspaObjectsUpdates}; use super::bgpsec::{BgpSecCertInfo, BgpSecCertificateUpdates}; use super::certauth::CertAuth; @@ -63,9 +63,6 @@ pub struct CaObjectsStore { /// The key-value store where objects are stored. store: KeyValueStore, - /// The signer used when generate objects. - signer: Arc, - /// Configuration for timing of object creation. issuance_timing: IssuanceTimingConfig, } @@ -75,12 +72,10 @@ impl CaObjectsStore { pub fn create( storage_uri: &Url, issuance_timing: IssuanceTimingConfig, - signer: Arc, ) -> KrillResult { let store = KeyValueStore::create(storage_uri, CA_OBJECTS_NS)?; Ok(CaObjectsStore { store, - signer, issuance_timing, }) } @@ -90,6 +85,7 @@ impl CaObjectsStore { &self, ca: &CertAuth, events: &[CertAuthEvent], + krill: &KrillRuntime, ) -> KrillResult<()> { // Note that the `CertAuth` which is passed in has already been // updated with the state changes contained in the event. @@ -138,7 +134,7 @@ impl CaObjectsStore { resource_class_name, current_key, &self.issuance_timing, - &self.signer, + krill.signer(), )?; } CertAuthEvent::KeyPendingToNew { @@ -149,7 +145,7 @@ impl CaObjectsStore { resource_class_name, new_key, &self.issuance_timing, - &self.signer, + krill.signer(), )?; } CertAuthEvent::KeyRollActivated { @@ -196,7 +192,7 @@ impl CaObjectsStore { } } objects.re_issue( - force_reissue, &self.issuance_timing, &self.signer + force_reissue, &self.issuance_timing, krill.signer() )?; Ok(()) }) @@ -295,13 +291,14 @@ impl CaObjectsStore { &self, force: bool, ca_handle: &CaHandle, + krill: &KrillRuntime, ) -> KrillResult { debug!("Re-issue for CA {ca_handle} using force: {force}"); self.with_ca_objects(ca_handle, |objects| { objects.re_issue( force, &self.issuance_timing, - &self.signer, + krill.signer(), ) }) } diff --git a/src/server/ca/upgrades/data_migration.rs b/src/server/ca/upgrades/data_migration.rs index 6063f2f61..1630ae0f8 100644 --- a/src/server/ca/upgrades/data_migration.rs +++ b/src/server/ca/upgrades/data_migration.rs @@ -1,6 +1,4 @@ -use std::sync::Arc; use log::{debug, warn}; -use crate::commons::crypto::KrillSignerBuilder; use crate::constants::CASERVER_NS; use crate::server::ca::certauth::CertAuth; use crate::server::ca::publishing::CaObjectsStore; @@ -12,25 +10,9 @@ use crate::upgrades::data_migration::check_agg_store; pub fn check_ca_objects(config: &Config) -> UpgradeResult<()> { let ca_store = check_agg_store::(config, CASERVER_NS, "CAs")?; - // make a dummy Signer to use for the CaObjectsStore - it won't be used, - // but it's needed for construction. - let probe_interval = - std::time::Duration::from_secs(config.signer_probe_retry_seconds); - let signer = Arc::new( - KrillSignerBuilder::new( - &config.storage_uri, - probe_interval, - &config.signers, - ) - .with_default_signer(config.default_signer()) - .with_one_off_signer(config.one_off_signer()) - .build()?, - ); - let ca_objects_store = CaObjectsStore::create( &config.storage_uri, - config.issuance_timing.clone(), - signer, + config.issuance_timing, )?; let cas_with_objects = ca_objects_store.cas()?; diff --git a/src/server/manager.rs b/src/server/manager.rs index 50d3b8e79..333bdf97c 100644 --- a/src/server/manager.rs +++ b/src/server/manager.rs @@ -1,39 +1,206 @@ //! The public part of the Krill RPKI server. //! -use std::{error, fmt}; +use std::{error, fmt, thread}; use std::collections::HashMap; use std::path::PathBuf; +use std::str::FromStr; use bytes::Bytes; use chrono::Duration; use hyper::StatusCode; +use log::info; use rpki::ca::{idexchange, publication}; use rpki::repository::resources::ResourceSet; -use tokio::sync::oneshot; +use tokio::runtime::{Handle as TokioHandle}; use crate::api; use crate::api::status::ErrorResponse; use crate::commons::actor::Actor; use crate::commons::error::KrillError; use crate::commons::eventsourcing::AggregateStoreError; use crate::config::Config; -use crate::constants::ta_handle; +use crate::constants::{TA_NAME, ta_handle, testbed_ca_handle}; use crate::server::ca::CaStatus; -use super::runtime::{KrillRuntime, Errand}; +use super::scheduler; +use super::mq::{Task, now}; +use super::runtime::KrillRuntime; + + +//------------ StartupManager ------------------------------------------------ + +/// The Krill manager during the start-up phase. +/// +/// This manager provides functionality that is only available before the +/// HTTP server is started and we are still running sync in a single thread. +pub struct StartupManager { + runtime: KrillRuntime, +} + +impl StartupManager { + /// Creates a new manager from the provided config. + /// + /// While the Tokio runtime provided isn’t used, we need the handle + /// already to pass it to the Krill runtime. + pub fn new( + config: Config, tokio: TokioHandle + ) -> Result { + Ok(Self { runtime: KrillRuntime::new(config, tokio)? }) + } + + /// Promotes the startup manager into a full Krill manager. + pub fn promote(self) -> KrillManager { + KrillManager { krill_runtime: self.runtime } + } + + /// Starts the scheduler in a separate thread. + pub fn run_scheduler(&self) -> Result<(), KrillError> { + // When multi-node set ups with a shared queue are + // supported then we can no longer safely reschedule + // ALL running tests. See issue: #1112 + self.runtime.tasks().reschedule_tasks_at_startup()?; + self.runtime.tasks().schedule(Task::QueueStartTasks, now())?; + + let krill = self.runtime.clone(); + thread::spawn(|| scheduler::run(krill)); + Ok(()) + } + + /// Re-issue ROA objects so that they will use short subjects. + /// + /// See issue #700. + pub fn force_renew_roas(&self) -> Result<(), KrillError> { + self.runtime.ca_manager().force_renew_roas_all( + self.runtime.system_actor(), &self.runtime + ) + } + + /// Updates the APSA definitions of a CA. + pub fn ca_aspas_definitions_update( + &self, + ca: idexchange::CaHandle, + updates: api::aspa::AspaDefinitionUpdates, + ) -> Result<(), KrillError> { + self.runtime.ca_manager().ca_aspas_definitions_update( + ca, updates, self.runtime.system_actor(), &self.runtime + ) + } + + pub fn prepare_testbed(&self) -> Result<(), KrillError> { + let Some(testbed) = self.runtime.config().testbed() else { + return Ok(()); + }; + + let testbed_handle = testbed_ca_handle(); + + if self.runtime.ca_manager().has_ca(&testbed_handle)? { + if self.runtime.config().benchmark.is_some() { + info!( + "Resuming BENCHMARK mode - will NOT recreate CAs. If \ + you want to recreate CAs, please wipe the data dir \ + and restart." + ); + } else { + info!( + "Resuming TESTBED mode - ONLY USE THIS FOR TESTING \n + AND TRAINING!" + ); + } + return Ok(()); + } + + // Will do some set up. Both TESTBED and BENCHMARK (which + // implies TESTBED and adds to it) will need a + // testbed ca to be set up first. We will re-use the import + // functionality to do all this. + let testbed_ca = api::import::ImportCa { + handle: testbed_handle, + parents: vec![api::import::ImportParent { + handle: ta_handle().into_converted(), + resources: ResourceSet::all(), + }], + roas: vec![], + }; + + let mut import_cas = vec![testbed_ca]; + + match self.runtime.config().benchmark.as_ref() { + None => { + info!( + "Enabling TESTBED mode - ONLY USE THIS FOR TESTING \ + AND TRAINING!" + ); + } + Some(benchmark) => { + info!( + "Enabling BENCHMARK mode with {} CAs with {} ROas \ + each - ONLY USE THIS FOR TESTING!", + benchmark.cas, benchmark.ca_roas + ); + + let testbed_parent: idexchange::ParentHandle = + testbed_ca_handle().into_converted(); + for nr in 0..benchmark.cas { + let handle = idexchange::CaHandle::new( + format!("benchmark-{nr}").into(), + ); + + // derive resources for benchmark ca + let byte_2_ipv4 = nr / 256; + let byte_3_ipv4 = nr % 256; + + let prefix_str = format!( + "10.{byte_2_ipv4}.{byte_3_ipv4}.0/24" + ); + let resources = + ResourceSet::from_strs("", &prefix_str, "") + .map_err(|e| { + KrillError::ResourceSetError(format!( + "cannot parse resources: {e}" + )) + })?; + + // Create ROA configs + let mut roas: Vec = vec![]; + let asn_range_start = 64512; + for asn in + asn_range_start..asn_range_start + benchmark.ca_roas + { + let payload = api::roa::RoaPayload::from_str( + &format!("{prefix_str} => {asn}") + ).unwrap(); + roas.push(payload.into()); + } + + import_cas.push(api::import::ImportCa { + handle, + parents: vec![api::import::ImportParent { + handle: testbed_parent.clone(), + resources, + }], + roas, + }) + } + } + } + + let startup_structure = api::import::Structure::for_testbed( + testbed.ta_aia().clone(), + testbed.ta_uri().clone(), + testbed.publication_server_uris(), + import_cas, + ); + + cas_import(startup_structure, &self.runtime) + } +} //------------ KrillManager -------------------------------------------------- -#[derive(Clone)] pub struct KrillManager { krill_runtime: KrillRuntime, } impl KrillManager { - /// Create a new Krill server from the provided config. - pub fn new(_config: Config) -> Result { - todo!() - } - /// Returns a reference to the config. pub fn config(&self) -> &Config { self.krill_runtime.config() @@ -66,42 +233,10 @@ impl KrillManager { F: FnOnce(&KrillRuntime) -> Result + Send + 'static, T: Send + 'static, { - let (tx, rx) = oneshot::channel(); let runtime = self.krill_runtime.clone(); - self.krill_runtime.spawn_blocking(move || { - let _ = tx.send(op(&runtime)); - }); - rx.await? - } - - /// Runs an errand using the `KrillManager`. - /// - /// An errand is a multi-phase process involving a sequence of sync and - /// async portions chained together. If a method of the [`KrillManager`] - /// returns such an errand by returning a value that implements the - /// [`Errand`] trait, the `run_errand` method can be used to evaluate - /// the errand and receive its result. - /// - /// The closure `op` is run on the sync runtime. It has access to the - /// [`KrillManager`] via its sole argument. The returned errand is then - /// run on either the sync or async runtimes as needed. - /// - /// If, for whatever reason, the closure or returned errand do not run to - /// completion, an error is returned. - async fn _run_errand( - &self, op: F - ) -> Result - where - F: FnOnce(&KrillRuntime) -> P + Send + 'static, - P: Errand>, - T: Send + 'static, - { - let (tx, rx) = oneshot::channel(); - let runtime = self.krill_runtime.clone(); - self.krill_runtime.spawn_blocking(move || { - op(&runtime).finish(tx); - }); - rx.await? + tokio::task::spawn_blocking(move || { + op(&runtime) + }).await? } } @@ -119,9 +254,9 @@ impl KrillManager { /// Triggers republising of all CAs that need it. pub async fn republish_all(&self, force: bool) -> Result<(), RunError> { self.run(move |runtime| -> Result<_, RunError> { - let cas = runtime.ca_manager().republish_all(force)?; + let cas = runtime.ca_manager().republish_all(force, runtime)?; for ca in cas { - runtime.ca_manager().cas_schedule_repo_sync(ca)?; + runtime.ca_manager().cas_schedule_repo_sync(ca, runtime)?; } Ok(()) }).await @@ -130,21 +265,21 @@ impl KrillManager { /// Triggers all CAs to re-sync with their repositories pub async fn cas_repo_sync_all(&self) -> Result<(), RunError> { self.run(|runtime| { - Ok(runtime.ca_manager().cas_schedule_repo_sync_all()?) + Ok(runtime.ca_manager().cas_schedule_repo_sync_all(runtime)?) }).await } /// Triggers all CAs to re-sync with their parent CAs. pub async fn cas_refresh_all(&self) -> Result<(), RunError> { self.run(|runtime| { - Ok(runtime.ca_manager().cas_schedule_refresh_all()?) + Ok(runtime.ca_manager().cas_schedule_refresh_all(runtime)?) }).await } /// Schedules a check to suspend children for all CAs pub async fn cas_schedule_suspend_all(&self) -> Result<(), RunError> { self.run(|runtime| { - Ok(runtime.ca_manager().cas_schedule_suspend_all()?) + Ok(runtime.ca_manager().cas_schedule_suspend_all(runtime)?) }).await } @@ -209,6 +344,15 @@ impl KrillManager { Ok(res) }).await } + + pub async fn cas_import( + &self, + structure: api::import::Structure, + ) -> Result<(), RunError> { + self.run(move |runtime| { + Ok(cas_import(structure, runtime)?) + }).await + } } @@ -285,14 +429,26 @@ impl KrillManager { }).await } - // ca_repo_update + /// Updates the repository for a CA. + pub async fn ca_repo_update( + &self, + ca: idexchange::CaHandle, + contact: api::admin::RepositoryContact, + actor: Actor, + ) -> Result<(), RunError> { + self.run(move |runtime| { + Ok(runtime.ca_manager().update_repo( + ca, contact, true, &actor, runtime + )?) + }).await + } /// Trigger re-syncing with the repository. pub async fn ca_sync_repo( &self, ca: idexchange::CaHandle ) -> Result<(), RunError> { self.run(move |runtime| { - Ok(runtime.ca_manager().cas_schedule_repo_sync(ca)?) + Ok(runtime.ca_manager().cas_schedule_repo_sync(ca, runtime)?) }).await } @@ -319,7 +475,9 @@ impl KrillManager { &self, ca_handle: idexchange::CaHandle ) -> Result<(), RunError> { self.run(move |runtime| { - Ok(runtime.ca_manager().cas_schedule_refresh_single(ca_handle)?) + Ok(runtime.ca_manager().cas_schedule_refresh_single( + ca_handle, runtime + )?) }).await } @@ -352,18 +510,29 @@ impl KrillManager { self.run(move |runtime| { match runtime.ca_manager().ca_command_details(&ca, version) { Ok(res) => Ok(Some(res)), - Err(err) if matches!( - err, - KrillError::AggregateStoreError( - AggregateStoreError::UnknownCommand(..) - ) - ) => Ok(None), + Err(KrillError::AggregateStoreError( + AggregateStoreError::UnknownCommand(..) + )) => Ok(None), Err(err) => Err(err.into()), } }).await } - // ca_delete + /// Deletes a CA. + /// + /// A best effort to send revocation requests and withdraw all objects + /// is done first. Note that any children of this CA will be left + /// orphaned, and they will only learn of this sad fact when they choose + /// to call home. + pub async fn ca_delete( + &self, + ca: idexchange::CaHandle, + actor: Actor, + ) -> Result<(), RunError> { + self.run(move |runtime| { + Ok(runtime.ca_manager().delete_ca(&ca, &actor, runtime)?) + }).await + } } @@ -381,9 +550,46 @@ impl KrillManager { }).await } - // TODO: ca_parent_add_or_update + /// Updates a parent contact for a CA + pub async fn ca_parent_add_or_update( + &self, + ca: idexchange::CaHandle, + parent_req: api::admin::ParentCaReq, + actor: Actor, + ) -> Result<(), RunError> { + self.run(move |runtime| { + // Verify that we can get entitlements from the new parent before + // adding/updating it. + let contact = + api::admin::ParentCaContact::try_from_rfc8183_parent_response( + parent_req.response.clone(), + ) + .map_err(|e| { + KrillError::CaParentResponseInvalid(ca.clone(), e.to_string()) + })?; + runtime.ca_manager().get_entitlements_from_contact( + &ca, &parent_req.handle, &contact, false, runtime, + )?; + + // Seems good. Add/update the parent. + Ok(runtime.ca_manager().ca_parent_add_or_update( + ca, parent_req, &actor, runtime + )?) + }).await + } - // TODO: ca_parent_remove + pub async fn ca_parent_remove( + &self, + handle: idexchange::CaHandle, + parent: idexchange::ParentHandle, + actor: Actor, + ) -> Result<(), RunError> { + self.run(move |runtime| { + Ok(runtime.ca_manager().ca_parent_remove( + handle, parent, &actor, runtime + )?) + }).await + } /// Returns the parent contact for a CA’s parent. pub async fn ca_parent_contact( @@ -691,7 +897,7 @@ impl KrillManager { uris: api::admin::PublicationServerUris, ) -> Result<(), RunError> { self.run(move |runtime| { - Ok(runtime.repo_manager().init(uris)?) + Ok(runtime.repo_manager().init(uris, runtime)?) }).await } @@ -744,7 +950,7 @@ impl KrillManager { msg_bytes: Bytes, ) -> Result { self.run(move |runtime| { - Ok(runtime.repo_manager().rfc8181(publisher, msg_bytes)?) + Ok(runtime.repo_manager().rfc8181(publisher, msg_bytes, runtime)?) }).await } } @@ -783,7 +989,9 @@ impl KrillManager { &self, publisher: idexchange::PublisherHandle, ) -> Result { self.run(move |runtime| { - Ok(runtime.repo_manager().repository_response(&publisher)?) + Ok(runtime.repo_manager().repository_response( + &publisher, runtime + )?) }).await } @@ -796,7 +1004,9 @@ impl KrillManager { self.run(move |runtime| { let publisher_handle = req.publisher_handle().clone(); runtime.repo_manager().create_publisher(req, &actor)?; - Ok(runtime.repo_manager().repository_response(&publisher_handle)?) + Ok(runtime.repo_manager().repository_response( + &publisher_handle, runtime + )?) }).await } @@ -807,7 +1017,9 @@ impl KrillManager { &self, publisher: idexchange::PublisherHandle, actor: Actor, ) -> Result<(), RunError> { self.run(move |runtime| { - Ok(runtime.repo_manager().remove_publisher(publisher, &actor)?) + Ok(runtime.repo_manager().remove_publisher( + publisher, &actor, runtime + )?) }).await } @@ -927,7 +1139,7 @@ impl KrillManager { &self, ) -> Result { self.run(|runtime| { - Ok(runtime.ca_manager().ta_proxy_signer_get_request()?) + Ok(runtime.ca_manager().ta_proxy_signer_get_request(runtime)?) }).await } @@ -961,6 +1173,220 @@ impl KrillManager { } +//------------ cas_import ---------------------------------------------------- + +fn cas_import( + structure: api::import::Structure, + krill: &KrillRuntime, +) -> Result<(), KrillError> { + let actor = krill.system_actor().clone(); + + // We need to know which CAs already exist. They should not be + // imported again, but can serve as parents. + let mut existing_cas = HashMap::new(); + for handle in krill.ca_manager().ca_handles()? { + let parent_handle = handle.convert(); + let resources = krill.ca_manager().get_ca( + &handle + )?.all_resources(); + existing_cas.insert(parent_handle, resources); + } + structure.validate_ca_hierarchy(existing_cas)?; + + if let Some(publication_server_uris) = + structure.publication_server.clone() + { + info!("Initialising publication server"); + krill.repo_manager().init(publication_server_uris, krill)?; + } + + if let Some(import_ta) = structure.ta.clone() { + if krill.config().ta_proxy_enabled() + && krill.config().ta_signer_enabled() + { + info!("Creating embedded Trust Anchor"); + krill.ca_manager().ta_init_fully_embedded( + import_ta.ta_aia, + vec![import_ta.ta_uri], + import_ta.ta_key_pem, + &actor, + krill + )?; + } else { + return Err(KrillError::custom( + "Import TA requires ta_support_enabled = true \ + and ta_signer_enabled = true", + )); + } + } + + info!("Bulk import {} CAs", structure.cas.len()); + + // XXX This used to be done in parallel. However, doing it in + // serial means we can be sure that a CA’s parents and the + // necessary resources already exist. We could potentially + // speed this up by creating sets of CAs that can be added + // in parallel, but import is rare enough that it probably + // doesn’t matter. + + for ca in structure.cas { + import_ca(ca, krill)?; + } + Ok(()) +} + +fn import_ca( + import: api::import::ImportCa, + krill: &KrillRuntime, +) -> Result<(), KrillError> { + // outline: + // - init ca + // - set up under repo + // - set up under parent + // - wait for resources + // - recurse for children + info!("Importing CA: '{}'", import.handle); + + let actor = krill.system_actor(); + + // init CA + krill.ca_manager().init_ca(import.handle.clone(), krill)?; + + // Get Publisher Request + let pub_req = { + let ca = krill.ca_manager().get_ca(&import.handle)?; + idexchange::PublisherRequest::new( + ca.id_cert().base64.clone(), + import.handle.convert(), + None, + ) + }; + + // Add Publisher + krill.repo_manager().create_publisher(pub_req, actor)?; + + // Get Repository Contact for CA + let repo_contact = { + let repo_response = krill.repo_manager().repository_response( + &import.handle.convert(), krill + )?; + api::admin::RepositoryContact::try_from_response( + repo_response + ).map_err(KrillError::rfc8183)? + }; + + // Add Repository to CA + krill.ca_manager().update_repo( + import.handle.clone(), + repo_contact, + false, + actor, + krill, + )?; + + for import_parent in import.parents { + // The parent should have been created. We can be sure of that + // because we verified that all parents are either "ta" (which + // is always created) or another CA that appeared on the list + // before this CA. + let parent_as_ca: idexchange::CaHandle = + import_parent.handle.convert(); + + // If the parent is the TA, then there is no need to wait. + if import_parent.handle.as_str() != TA_NAME { + let Ok(parent) = krill.ca_manager().get_ca( + &parent_as_ca + ) else { + return Err(KrillError::Custom(format!( + "Could not import CA {}. Parent: {} is not created", + import.handle, parent_as_ca + ))) + }; + + if !parent.all_resources().contains( + &import_parent.resources + ) { + return Err(KrillError::Custom(format!( + "Could not import CA {}. \ + Parent: {} does not contain all resources.", + import.handle, parent_as_ca + ))) + } + } + + // Add the CA as the child of parent and get the parent response + let response = { + let ca = krill.ca_manager().get_ca(&import.handle)?; + let id_cert = ca.child_request().validate().map_err( + KrillError::rfc8183 + )?; + let child_req = api::admin::AddChildRequest { + handle: import.handle.convert(), + resources: import_parent.resources, + id_cert, + }; + + krill.ca_manager().ca_add_child( + &import_parent.handle.convert(), + child_req, + actor, + krill, + )? + }; + + // Add the parent to the child and force sync + { + let parent_req = api::admin::ParentCaReq { + handle: import_parent.handle.clone(), + response + }; + krill.ca_manager().ca_parent_add_or_update( + import.handle.clone(), + parent_req, + actor, + krill, + )?; + + // First sync will inform child of its entitlements and + // trigger that CSR is created. + krill.ca_manager().ca_sync_parent( + &import.handle, 0, &import_parent.handle, actor, krill, + )?; + + // Second sync will send that CSR to the parent + krill.ca_manager().ca_sync_parent( + &import.handle, 0, &import_parent.handle, actor, krill, + )?; + + // If the parent is a TA, then we will need to push a bit + // more.. Normally this should be handled by + // triggered tasks, but the task scheduler is + // not running when we do this at startup. + if import_parent.handle.as_str() == TA_NAME { + krill.ca_manager().sync_ta_proxy_signer_if_possible( + krill + )?; + krill.ca_manager().ca_sync_parent( + &import.handle, 0, &import_parent.handle, actor, + krill, + )?; + } + } + } + + // Add ROA definitions + let roa_updates = api::roa::RoaConfigurationUpdates { + added: import.roas, + removed: vec![] + }; + krill.ca_manager().ca_routes_update( + import.handle, roa_updates, actor, krill + )?; + + Ok(()) +} + + //------------ RunError ------------------------------------------------------ /// An error happened when running an operation. @@ -996,9 +1422,9 @@ impl From for KrillError { } } -impl From for RunError { - fn from(_: oneshot::error::RecvError) -> Self { - Self(KrillError::internal("operation dropped")) +impl From for RunError { + fn from(_: tokio::task::JoinError) -> Self { + Self(KrillError::internal("task panicked")) } } diff --git a/src/server/mod.rs b/src/server/mod.rs index 221af7ec9..27aa74fc8 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -8,5 +8,3 @@ pub mod runtime; pub mod scheduler; pub mod taproxy; -pub mod oldmanager; - diff --git a/src/server/oldmanager.rs b/src/server/oldmanager.rs deleted file mode 100644 index 9857a1aa4..000000000 --- a/src/server/oldmanager.rs +++ /dev/null @@ -1,637 +0,0 @@ -//! An RPKI publication protocol server. - -#![allow(dead_code, unused_imports)] - -use std::collections::HashMap; -use std::path::PathBuf; -use std::str::FromStr; -use std::sync::Arc; -use bytes::Bytes; -use clap::crate_version; -use chrono::Duration; -use futures_util::future::try_join_all; -use log::info; - -use rpki::{ - ca::{ - idexchange, - idexchange::{CaHandle, ChildHandle, ParentHandle, PublisherHandle}, - }, - repository::resources::ResourceSet, - uri, -}; - -use crate::daemon::http::auth::AuthInfo; -use crate::{ - commons::{ - actor::Actor, - crypto::KrillSignerBuilder, - error::Error, - KrillEmptyResult, KrillResult, - }, - constants::*, - config::Config, - server::{ - ca::{ - self, CaManager, CaStatus, - }, - mq::{now, Task, TaskQueue}, - pubd::RepositoryManager, - scheduler::Scheduler, - }, -}; -use crate::api; -use crate::api::admin::{ - AddChildRequest, CertAuthInit, ParentCaContact, ParentCaReq, - PublicationServerUris, PublisherDetails, RepoFileDeleteCriteria, - RepositoryContact, UpdateChildRequest, -}; -use crate::api::aspa::{ - AspaDefinitionList, AspaDefinitionUpdates, AspaProvidersUpdate, - CustomerAsn, -}; -use crate::api::bgp::{BgpAnalysisReport, BgpAnalysisSuggestion}; -use crate::api::bgpsec::{BgpSecCsrInfoList, BgpSecDefinitionUpdates}; -use crate::api::ca::{ - CaRepoDetails, CertAuthInfo, CertAuthIssues, - CertAuthList, CertAuthStats, ChildCaInfo, ChildrenConnectionStats, - IdCertInfo, RtaList, RtaName, - RtaPrepResponse, -}; -use crate::api::history::{ - CommandDetails, CommandHistory, CommandHistoryCriteria -}; -use crate::api::import::ImportChild; -use crate::api::roa::{ - ConfiguredRoa, RoaConfiguration, RoaConfigurationUpdates, RoaPayload, -}; -use crate::api::rta::{ - ResourceTaggedAttestation, RtaContentRequest, RtaPrepareRequest, -}; -use crate::api::ta::{ - ApiTrustAnchorSignedRequest, TaCertDetails, TrustAnchorSignedResponse, - TrustAnchorSignerInfo, -}; -use crate::constants::{TA_NAME, ta_handle}; -use crate::server::bgp::BgpAnalyser; -use crate::server::runtime::KrillRuntime; - - -//------------ OldManager --------------------------------------------------- - -/// This is the Krill server that is doing all the orchestration for all -/// components. -pub struct OldManager { - krill: KrillRuntime, - - // The base URI for this service - service_uri: uri::Https, - - // Publication server, with configured publishers - repo_manager: Arc, - - // Handles the internal TA and/or CAs - ca_manager: Arc, - - // Handles the internal TA and/or CAs - bgp_analyser: Arc, - - // Shared message queue - mq: Arc, - - // System actor - system_actor: Actor, - - pub config: Arc, -} - -/// # Set up and initialization -impl OldManager { - /// Creates a new publication server. Note that state is preserved - /// in the data storage. - #[allow(unreachable_code, unused_variables)] - pub async fn build(config: Arc) -> KrillResult { - let service_uri = config.service_uri(); - - info!("Starting {} v{}", KRILL_SERVER_APP, crate_version!()); - info!("{KRILL_SERVER_APP} uses service uri: {service_uri}"); - - // Assumes that Config::verify() has already ensured that the signer - // configuration is valid and that Config::resolve() has been - // used to update signer name references to resolve to the - // corresponding signer configurations. - let probe_interval = - std::time::Duration::from_secs(config.signer_probe_retry_seconds); - let signer = KrillSignerBuilder::new( - &config.storage_uri, - probe_interval, - &config.signers, - ) - .with_default_signer(config.default_signer()) - .with_one_off_signer(config.one_off_signer()) - .build()?; - let signer = Arc::new(signer); - - let system_actor = ACTOR_DEF_KRILL; - - // Task queue Arc is shared between ca_manager, repo_manager and the - // scheduler. - let mq = Arc::new(TaskQueue::new(&config.storage_uri)?); - - // for now, support that existing embedded repositories are still - // supported. this should be removed in future after people - // have had a chance to separate. - let repo_manager = Arc::new(RepositoryManager::build( - config.clone(), - mq.clone(), - signer.clone(), - )?); - - let ca_manager = Arc::new( - ca::CaManager::build( - config.clone(), - mq.clone(), - signer, - system_actor.clone(), - ) - .await?, - ); - - let bgp_analyser = Arc::new(BgpAnalyser::new(&config)); - - // When multi-node set ups with a shared queue are - // supported then we can no longer safely reschedule - // ALL running tests. See issue: #1112 - mq.reschedule_tasks_at_startup()?; - - mq.schedule(Task::QueueStartTasks, now())?; - - let server = OldManager { - krill: todo!(), - service_uri, - repo_manager, - ca_manager, - bgp_analyser, - mq, - system_actor, - config: config.clone(), - }; - - // Check if we need to do any testbed or benchmarking set up. - let testbed_handle = testbed_ca_handle(); - - if let Some(testbed) = config.testbed() { - if server.ca_manager.has_ca(&testbed_handle)? { - if config.benchmark.is_some() { - info!("Resuming BENCHMARK mode - will NOT recreate CAs. If you wanted this, then wipe the data dir and restart."); - } else { - info!("Resuming TESTBED mode - ONLY USE THIS FOR TESTING AND TRAINING!"); - } - } else { - // Will do some set up. Both TESTBED and BENCHMARK (which - // implies TESTBED and adds to it) will need a - // testbed ca to be set up first. We will re-use the import - // functionality to do all this. - let testbed_ca = api::import::ImportCa { - handle: testbed_handle, - parents: vec![api::import::ImportParent { - handle: ta_handle().into_converted(), - resources: ResourceSet::all(), - }], - roas: vec![], - }; - - let mut import_cas = vec![testbed_ca]; - - match config.benchmark.as_ref() { - None => { - info!("Enabling TESTBED mode - ONLY USE THIS FOR TESTING AND TRAINING!"); - } - Some(benchmark) => { - info!( - "Enabling BENCHMARK mode with {} CAs with {} ROas each - ONLY USE THIS FOR TESTING!", - benchmark.cas, benchmark.ca_roas - ); - - let testbed_parent: ParentHandle = - testbed_ca_handle().into_converted(); - for nr in 0..benchmark.cas { - let handle = CaHandle::new( - format!("benchmark-{nr}").into(), - ); - - // derive resources for benchmark ca - let byte_2_ipv4 = nr / 256; - let byte_3_ipv4 = nr % 256; - - let prefix_str = format!( - "10.{byte_2_ipv4}.{byte_3_ipv4}.0/24" - ); - let resources = - ResourceSet::from_strs("", &prefix_str, "") - .map_err(|e| { - Error::ResourceSetError(format!( - "cannot parse resources: {e}" - )) - })?; - - // Create ROA configs - let mut roas: Vec = vec![]; - let asn_range_start = 64512; - for asn in asn_range_start - ..asn_range_start + benchmark.ca_roas - { - let payload = RoaPayload::from_str(&format!( - "{prefix_str} => {asn}" - )) - .unwrap(); - roas.push(payload.into()); - } - - import_cas.push(api::import::ImportCa { - handle, - parents: vec![api::import::ImportParent { - handle: testbed_parent.clone(), - resources, - }], - roas, - }) - } - } - } - - let startup_structure = api::import::Structure::for_testbed( - testbed.ta_aia().clone(), - testbed.ta_uri().clone(), - testbed.publication_server_uris(), - import_cas, - ); - server.cas_import(startup_structure).await?; - } - } - - Ok(server) - } - - pub fn build_scheduler(&self) -> Scheduler { - Scheduler::build( - self.mq.clone(), - self.ca_manager.clone(), - self.repo_manager.clone(), - self.bgp_analyser.clone(), - self.config.clone(), - self.system_actor.clone(), - ) - } -} - -/// # Access to components -impl OldManager { - pub fn system_actor(&self) -> &Actor { - &self.system_actor - } - -} - -/// # Being a child -impl OldManager { - /// Updates a parent contact for a CA - pub async fn ca_parent_add_or_update( - &self, - ca: CaHandle, - parent_req: ParentCaReq, - actor: &Actor, - ) -> KrillEmptyResult { - // Verify that we can get entitlements from the new parent before - // adding/updating it. - let contact = ParentCaContact::try_from_rfc8183_parent_response( - parent_req.response.clone(), - ) - .map_err(|e| { - Error::CaParentResponseInvalid(ca.clone(), e.to_string()) - })?; - self.ca_manager.get_entitlements_from_contact( - &ca, &parent_req.handle, &contact, false, &self.krill, - ).await?; - - // Seems good. Add/update the parent. - self.ca_manager.ca_parent_add_or_update( - ca, parent_req, actor, &self.krill, - ) - } - - pub async fn ca_parent_remove( - &self, - handle: CaHandle, - parent: ParentHandle, - actor: &Actor, - ) -> KrillEmptyResult { - self.ca_manager - .ca_parent_remove(handle, parent, actor, &self.krill) - .await - } -} - -/// # Stats and status of CAS -impl OldManager { - - pub async fn cas_import( - &self, - structure: api::import::Structure, - ) -> KrillResult<()> { - let actor = Arc::new(self.system_actor().clone()); - - // We need to know which CAs already exist. They should not be - // imported again, but can serve as parents. - let mut existing_cas = HashMap::new(); - for handle in self.ca_manager.ca_handles()? { - let parent_handle = handle.convert(); - let resources = - self.ca_manager.get_ca(&handle)?.all_resources(); - existing_cas.insert(parent_handle, resources); - } - structure.validate_ca_hierarchy(existing_cas)?; - - if let Some(publication_server_uris) = - structure.publication_server.clone() - { - info!("Initialising publication server"); - self.repo_manager.init(publication_server_uris)?; - } - - if let Some(import_ta) = structure.ta.clone() { - if self.config.ta_proxy_enabled() - && self.config.ta_signer_enabled() - { - info!("Creating embedded Trust Anchor"); - self.ca_manager - .ta_init_fully_embedded( - import_ta.ta_aia, - vec![import_ta.ta_uri], - import_ta.ta_key_pem, - &self.repo_manager, - &actor, - &self.krill, - ) - .await?; - } else { - return Err(Error::custom( - "Import TA requires ta_support_enabled = true and ta_signer_enabled = true", - )); - } - } - - info!("Bulk import {} CAs", structure.cas.len()); - // Set up each online TA child with local repo, do this in parallel. - let mut import_fns = vec![]; - let service_uri = Arc::new(self.config.service_uri()); - for ca in structure.cas { - import_fns.push(tokio::spawn(Self::import_ca( - ca, - self.ca_manager.clone(), - self.repo_manager.clone(), - service_uri.clone(), - actor.clone(), - self.krill.clone(), - ))); - } - try_join_all(import_fns).await.map_err(|e| { - Error::Custom(format!("Could not import CAs: {e}")) - })?; - - Ok(()) - } - - async fn import_ca( - import: api::import::ImportCa, - ca_manager: Arc, - repo_manager: Arc, - _service_uri: Arc, - actor: Arc, - krill: KrillRuntime, - ) -> KrillEmptyResult { - // outline: - // - init ca - // - set up under repo - // - set up under parent - // - wait for resources - // - recurse for children - info!("Importing CA: '{}'", import.handle); - - // init CA - ca_manager.init_ca(import.handle.clone(), &krill)?; - - // Get Publisher Request - let pub_req = { - let ca = ca_manager.get_ca(&import.handle)?; - idexchange::PublisherRequest::new( - ca.id_cert().base64.clone(), - import.handle.convert(), - None, - ) - }; - - // Add Publisher - repo_manager.create_publisher(pub_req, &actor)?; - - // Get Repository Contact for CA - let repo_contact = { - let repo_response = - repo_manager.repository_response(&import.handle.convert())?; - RepositoryContact::try_from_response(repo_response) - .map_err(Error::rfc8183)? - }; - - // Add Repository to CA - ca_manager - .update_repo( - &repo_manager, - import.handle.clone(), - repo_contact, - false, - &actor, - &krill, - ) - .await?; - - for import_parent in import.parents { - // The parent should have been created. If it wasn't created yet, - // then we will need to wait for it. Note that we can - // be sure that it will be created because we verified - // that all parents are either "ta" (which is always created) or - // another CA that appeared on the list before this CA. - // - // But.. you know.. just to be safe, let's not hang in here - // forever.. - let wait_ms = 100; - let max_tries = 3000; // *100ms -> 5 mins, should be enough even on slow systems - let mut tried = 0; - let parent_as_ca: CaHandle = import_parent.handle.convert(); - - // If the parent is the TA, then there is no need to wait. - if import_parent.handle.as_str() != TA_NAME { - loop { - tried += 1; - if let Ok(parent) = ca_manager.get_ca(&parent_as_ca) - { - if parent.all_resources().contains( - &import_parent.resources - ) { - break; - } - else { - info!( - "Parent {} does not (yet) have resources for {}. Will wait a bit and try again", - parent.handle(), - import.handle - ); - } - } else { - info!( - "Parent {} for CA {} is not yet created. Will wait a bit and try again", - parent_as_ca, import.handle - ); - } - tokio::time::sleep(std::time::Duration::from_millis( - wait_ms, - )) - .await; - if tried >= max_tries { - return Err(Error::Custom(format!( - "Could not import CA {}. Parent: {} is not created", - import.handle, parent_as_ca - ))); - } - } - } - - // Add the CA as the child of parent and get the parent response - let response = { - let ca = ca_manager.get_ca(&import.handle)?; - let id_cert = - ca.child_request().validate().map_err(Error::rfc8183)?; - let child_req = AddChildRequest { - handle: import.handle.convert(), - resources: import_parent.resources, - id_cert, - }; - - ca_manager - .ca_add_child( - &import_parent.handle.convert(), - child_req, - &actor, - &krill, - )? - }; - - // Add the parent to the child and force sync - { - let parent_req = ParentCaReq { - handle: import_parent.handle.clone(), - response - }; - ca_manager.ca_parent_add_or_update( - import.handle.clone(), - parent_req, - &actor, - &krill, - )?; - - // First sync will inform child of its entitlements and - // trigger that CSR is created. - ca_manager.ca_sync_parent( - &import.handle, 0, &import_parent.handle, &actor, &krill, - ).await?; - - // Second sync will send that CSR to the parent - ca_manager.ca_sync_parent( - &import.handle, 0, &import_parent.handle, &actor, &krill, - ).await?; - - // If the parent is a TA, then we will need to push a bit - // more.. Normally this should be handled by - // triggered tasks, but the task scheduler is - // not running when we do this at startup. - if import_parent.handle.as_str() == TA_NAME { - ca_manager.sync_ta_proxy_signer_if_possible(&krill)?; - ca_manager.ca_sync_parent( - &import.handle, 0, &import_parent.handle, &actor, - &krill, - ).await?; - } - } - } - - // Add ROA definitions - let roa_updates = RoaConfigurationUpdates { - added: import.roas, - removed: vec![] - }; - ca_manager.ca_routes_update( - import.handle, roa_updates, &actor, &krill - )?; - - Ok(()) - } -} - -/// # Admin CAS -impl OldManager { - - /// Delete a CA. Let it do best effort revocation requests and withdraw - /// all its objects first. Note that any children of this CA will be left - /// orphaned, and they will only learn of this sad fact when they choose - /// to call home. - pub async fn ca_delete( - &self, - ca: &CaHandle, - actor: &Actor, - ) -> KrillResult<()> { - self.ca_manager - .delete_ca(self.repo_manager.as_ref(), ca, actor, &self.krill) - .await - } - - /// Update the repository for a CA, or return an error. (see - /// `CertAuth::repo_update`) - pub async fn ca_repo_update( - &self, - ca: CaHandle, - contact: RepositoryContact, - actor: &Actor, - ) -> KrillEmptyResult { - self.ca_manager.update_repo( - self.repo_manager.as_ref(), ca, contact, true, actor, &self.krill - ).await - } -} - -/// # Handle ASPA requests -impl OldManager { - - // Left for upgrade only. - pub fn ca_aspas_definitions_update( - &self, - ca: CaHandle, - updates: AspaDefinitionUpdates, - actor: &Actor, - ) -> KrillEmptyResult { - self.ca_manager.ca_aspas_definitions_update( - ca, updates, actor, &self.krill - ) - } -} - -/// # Handle route authorization requests -impl OldManager { - - // Only for upgrade. - /// Re-issue ROA objects so that they will use short subjects (see issue - /// #700) - pub async fn force_renew_roas(&self) -> KrillResult<()> { - self.ca_manager.force_renew_roas_all(self.system_actor(), &self.krill) - } -} - -// Tested through integration tests diff --git a/src/server/pubd/access.rs b/src/server/pubd/access.rs index 249d79dce..2b3885be0 100644 --- a/src/server/pubd/access.rs +++ b/src/server/pubd/access.rs @@ -96,20 +96,21 @@ impl RepositoryAccessProxy { pub fn init( &self, uris: PublicationServerUris, - signer: Arc, + signer: &KrillSigner, ) -> KrillResult<()> { if self.is_initialized()? { return Err(Error::RepositoryServerAlreadyInitialized) }; let actor = ACTOR_DEF_KRILL; + let id_cert = signer.create_self_signed_id_cert()?.into(); let cmd = RepositoryAccessInitCommand::new( self.key.clone(), RepositoryAccessInitCommandDetails { rrdp_base_uri: uris.rrdp_base_uri, rsync_jail: uris.rsync_jail, - signer, + id_cert, }, &actor, ); @@ -315,12 +316,10 @@ impl Aggregate for RepositoryAccess { ) -> Result { let details = command.into_details(); - let id_cert_info = details.signer.create_self_signed_id_cert()?.into(); - Ok(RepositoryAccessInitEvent { - id_cert: id_cert_info, rrdp_base_uri: details.rrdp_base_uri, rsync_jail: details.rsync_jail, + id_cert: details.id_cert, }) } @@ -496,8 +495,8 @@ pub struct RepositoryAccessInitCommandDetails { /// The base URI of the rsync server used by the repository. pub rsync_jail: uri::Rsync, - /// A Krill signer to use for signing. - pub signer: Arc, + /// The identity certificate of the repository. + pub id_cert: IdCertInfo, } impl InitCommandDetails for RepositoryAccessInitCommandDetails { diff --git a/src/server/pubd/manager.rs b/src/server/pubd/manager.rs index 1d1d876ff..4e43e89bf 100644 --- a/src/server/pubd/manager.rs +++ b/src/server/pubd/manager.rs @@ -1,7 +1,6 @@ //! The manager for the publication server. use std::path::PathBuf; -use std::sync::Arc; use bytes::Bytes; use log::{debug, info}; use rpki::ca::publication; @@ -17,10 +16,10 @@ use crate::api::pubd::RepoStats; use crate::commons::KrillResult; use crate::commons::actor::Actor; use crate::commons::cmslogger::CmsLogger; -use crate::commons::crypto::KrillSigner; use crate::commons::error::Error; -use crate::config::Config; -use crate::server::mq::{now, Task, TaskQueue}; +use crate::config::{Config, RrdpUpdatesConfig}; +use crate::server::mq::{now, Task}; +use crate::server::runtime::KrillRuntime; use super::access::RepositoryAccessProxy; use super::content::RepositoryContentProxy; use super::rrdp::RrdpUpdateNeeded; @@ -33,41 +32,22 @@ use super::rrdp::RrdpUpdateNeeded; /// * publish content to RRDP and rsync pub struct RepositoryManager { /// The repository access manager portion. - access: Arc, + access: RepositoryAccessProxy, /// The repository content manager portion. - content: Arc, + content: RepositoryContentProxy, - /// Shared task queue. - /// - /// Used to schedule RRDP updates when content is updated. - tasks: Arc, - - /// Shared server config. - config: Arc, - - /// Shared signer. - signer: Arc, + /// The configuration for RRDP update details. + rrdp_updates_config: RrdpUpdatesConfig, } impl RepositoryManager { /// Builds the repository manager. - pub fn build( - config: Arc, - tasks: Arc, - signer: Arc, - ) -> Result { - let access_proxy = Arc::new(RepositoryAccessProxy::create(&config)?); - let content_proxy = Arc::new( - RepositoryContentProxy::create(&config)? - ); - + pub fn new(config: &Config) -> Result { Ok(RepositoryManager { - access: access_proxy, - content: content_proxy, - tasks, - config, - signer, + access: RepositoryAccessProxy::create(config)?, + content: RepositoryContentProxy::create(config)?, + rrdp_updates_config: config.rrdp_updates_config, }) } @@ -77,12 +57,14 @@ impl RepositoryManager { } /// Create the publication server, will fail if it was already created. - pub fn init(&self, uris: PublicationServerUris) -> KrillResult<()> { + pub fn init( + &self, uris: PublicationServerUris, krill: &KrillRuntime, + ) -> KrillResult<()> { info!("Initializing repository"); - self.access.init(uris.clone(), self.signer.clone())?; - self.content.init(self.config.repo_dir(), uris)?; + self.access.init(uris.clone(), krill.signer())?; + self.content.init(krill.config().repo_dir(), uris)?; self.content - .write_repository(self.config.rrdp_updates_config)?; + .write_repository(krill.config().rrdp_updates_config)?; Ok(()) } @@ -127,9 +109,10 @@ impl RepositoryManager { &self, publisher_handle: PublisherHandle, msg_bytes: Bytes, + krill: &KrillRuntime, ) -> KrillResult { let cms_logger = CmsLogger::for_rfc8181_rcvd( - self.config.rfc8181_log_dir.as_ref(), + krill.config().rfc8181_log_dir.as_ref(), &publisher_handle, ); @@ -145,7 +128,9 @@ impl RepositoryManager { let is_list_query = query == publication::Query::List; - let response_result = self.rfc8181_message(&publisher_handle, query); + let response_result = self.rfc8181_message( + &publisher_handle, query, krill + ); let should_log_cms = response_result.is_err() || !is_list_query; @@ -163,7 +148,7 @@ impl RepositoryManager { }; let response_bytes = self.access.create_response( - response, &self.signer + response, krill.signer(), )?.to_bytes(); if should_log_cms { @@ -179,6 +164,7 @@ impl RepositoryManager { &self, publisher_handle: &PublisherHandle, query: publication::Query, + krill: &KrillRuntime, ) -> KrillResult { match query { publication::Query::List => { @@ -192,7 +178,7 @@ impl RepositoryManager { debug!( "Received RFC 8181 delta query for {publisher_handle}" ); - self.publish(publisher_handle, delta)?; + self.publish(publisher_handle, delta, krill)?; Ok(publication::Message::success()) } } @@ -200,7 +186,7 @@ impl RepositoryManager { /// Performs an RRDP session reset. pub fn rrdp_session_reset(&self) -> KrillResult<()> { - self.content.session_reset(self.config.rrdp_updates_config) + self.content.session_reset(self.rrdp_updates_config) } /// Lets a known publisher publish in a repository. @@ -208,6 +194,7 @@ impl RepositoryManager { &self, publisher_handle: &PublisherHandle, delta: PublishDelta, + krill: &KrillRuntime, ) -> KrillResult<()> { let publisher = self.access.get_publisher(publisher_handle)?; @@ -217,7 +204,7 @@ impl RepositoryManager { publisher.base_uri(), )?; - self.tasks.schedule(Task::RrdpUpdateIfNeeded, now()) + krill.tasks().schedule(Task::RrdpUpdateIfNeeded, now()) } /// Updates RRDP and makes new delta if needed. @@ -227,7 +214,7 @@ impl RepositoryManager { /// time for the next update is returned. pub fn update_rrdp_if_needed(&self) -> KrillResult> { match self.content.rrdp_update_needed( - self.config.rrdp_updates_config)? + self.rrdp_updates_config)? { RrdpUpdateNeeded::No => return Ok(None), RrdpUpdateNeeded::Later(time) => return Ok(Some(time)), @@ -235,9 +222,9 @@ impl RepositoryManager { } let content = self.content.update_rrdp( - self.config.rrdp_updates_config + self.rrdp_updates_config )?; - content.write_repository(self.config.rrdp_updates_config)?; + content.write_repository(self.rrdp_updates_config)?; Ok(None) } @@ -248,7 +235,7 @@ impl RepositoryManager { criteria: RepoFileDeleteCriteria, ) -> KrillResult<()> { // update RRDP first so we apply any staged deltas. - self.content.update_rrdp(self.config.rrdp_updates_config)?; + self.content.update_rrdp(self.rrdp_updates_config)?; // delete matching files using the updated snapshot and stage a delta // if needed. @@ -256,10 +243,10 @@ impl RepositoryManager { // update RRDP again to make the delta effective immediately. let content = - self.content.update_rrdp(self.config.rrdp_updates_config)?; + self.content.update_rrdp(self.rrdp_updates_config)?; // Write the updated repository - NOTE: we no longer lock it. - content.write_repository(self.config.rrdp_updates_config)?; + content.write_repository(self.rrdp_updates_config)?; Ok(()) } @@ -301,8 +288,9 @@ impl RepositoryManager { pub fn repository_response( &self, publisher: &PublisherHandle, + krill: &KrillRuntime, ) -> KrillResult { - let rfc8181_uri = self.config.rfc8181_uri(publisher); + let rfc8181_uri = krill.config().rfc8181_uri(publisher); self.access.repository_response(rfc8181_uri, publisher) } @@ -326,11 +314,12 @@ impl RepositoryManager { &self, name: PublisherHandle, actor: &Actor, + krill: &KrillRuntime, ) -> KrillResult<()> { self.content.remove_publisher(name.clone())?; self.access.remove_publisher(name, actor)?; - self.tasks.schedule(Task::RrdpUpdateIfNeeded, now()) + krill.tasks().schedule(Task::RrdpUpdateIfNeeded, now()) } } @@ -339,7 +328,7 @@ impl RepositoryManager { /// Updates the RRDP files and rsync content on disk. pub fn write_repository(&self) -> KrillResult<()> { self.content - .write_repository(self.config.rrdp_updates_config) + .write_repository(self.rrdp_updates_config) } } @@ -347,13 +336,14 @@ impl RepositoryManager { //============ Tests ========================================================= #[cfg(test)] +#[allow(unused)] // XXX TODO mod tests { use std::fs; use std::path::{Path, PathBuf}; use std::str::{from_utf8, FromStr}; use std::time::Duration; + use std::thread::sleep; use bytes::Bytes; - use tokio::time::sleep; use url::Url; use rpki::uri; use rpki::ca::idexchange::Handle; @@ -414,66 +404,65 @@ mod tests { ) } - fn make_server( - storage_uri: &Url - ) -> (RepositoryManager, tempfile::TempDir) { - let data_dir = tempfile::tempdir().unwrap(); - - enable_test_mode(); - let mut config = Config::test( - storage_uri, - Some(data_dir.path()), - true, - false, - false, - false, - ); - let _ = config.init_logging(); - config.process().unwrap(); - - let signer = KrillSignerBuilder::new( - storage_uri, - Duration::from_secs(1), - &config.signers, - ) - .with_default_signer(config.default_signer()) - .with_one_off_signer(config.one_off_signer()) - .build() - .unwrap(); - - let signer = Arc::new(signer); - let config = Arc::new(config); - let mq = Arc::new(TaskQueue::new(&config.storage_uri).unwrap()); - let repository_manager = - RepositoryManager::build(config, mq, signer).unwrap(); - - let uris = PublicationServerUris { - rrdp_base_uri: https("https://localhost/repo/rrdp/"), - rsync_jail: rsync("rsync://localhost/repo/"), - }; - - repository_manager.init(uris).unwrap(); + struct TestServer { + krill: KrillRuntime, + storage_uri: Url, + data_dir: tempfile::TempDir, + tokio: tokio::runtime::Runtime, + } + + impl TestServer { + fn new() -> Self { + let storage_uri = test::mem_storage(); + let data_dir = tempfile::tempdir().unwrap(); + let tokio = tokio::runtime::Runtime::new().unwrap(); + + enable_test_mode(); + let mut config = Config::test( + &storage_uri, + Some(data_dir.path()), + true, + false, + false, + false, + ); + let _ = config.init_logging(); + config.process().unwrap(); + + let krill = KrillRuntime::new( + config, tokio.handle().clone() + ).unwrap(); + let uris = PublicationServerUris { + rrdp_base_uri: https("https://localhost/repo/rrdp/"), + rsync_jail: rsync("rsync://localhost/repo/"), + }; + + krill.repo_manager().init(uris, &krill).unwrap(); + + Self { krill, storage_uri, data_dir, tokio } + } - (repository_manager, data_dir) + fn repo(&self) -> &RepositoryManager { + self.krill.repo_manager() + } } #[test] fn should_add_publisher() { - // we need a disk, as repo_dir, etc. use data_dir by default - let storage_uri = test::mem_storage(); - let (server, _data_dir) = make_server(&storage_uri); + let server = TestServer::new(); - let alice = publisher_alice(&storage_uri); + let alice = publisher_alice(&server.storage_uri); let alice_handle = Handle::from_str("alice").unwrap(); let publisher_req = make_publisher_req(alice_handle.as_str(), alice.id_cert()); let actor = ACTOR_DEF_TEST; - server.create_publisher(publisher_req, &actor).unwrap(); + server.repo().create_publisher(publisher_req, &actor).unwrap(); - let alice_found = - server.get_publisher_details(alice_handle).unwrap(); + let alice_found = server.repo().get_publisher_details( + alice_handle + ).unwrap(); assert_eq!(alice_found.base_uri, alice.base_uri()); assert_eq!(alice_found.id_cert, *alice.id_cert()); @@ -482,21 +471,20 @@ mod tests { #[test] fn should_not_add_publisher_twice() { - let storage_uri = test::mem_storage(); - let (server, _data_dir) = make_server(&storage_uri); + let server = TestServer::new(); - let alice = publisher_alice(&storage_uri); + let alice = publisher_alice(&server.storage_uri); let alice_handle = Handle::from_str("alice").unwrap(); let publisher_req = make_publisher_req(alice_handle.as_str(), alice.id_cert()); let actor = ACTOR_DEF_TEST; - server - .create_publisher(publisher_req.clone(), &actor) - .unwrap(); + server.repo().create_publisher( + publisher_req.clone(), &actor + ).unwrap(); - match server.create_publisher(publisher_req, &actor) { + match server.repo().create_publisher(publisher_req, &actor) { Err(Error::PublisherDuplicate(name)) => { assert_eq!(name, alice_handle) } @@ -506,29 +494,26 @@ mod tests { #[test] fn should_list_files() { - let storage_uri = test::mem_storage(); - let (server, _data_dir) = make_server(&storage_uri); + let server = TestServer::new(); - let alice = publisher_alice(&storage_uri); + let alice = publisher_alice(&server.storage_uri); let alice_handle = Handle::from_str("alice").unwrap(); let publisher_req = make_publisher_req(alice_handle.as_str(), alice.id_cert()); let actor = ACTOR_DEF_TEST; - server.create_publisher(publisher_req, &actor).unwrap(); + server.repo().create_publisher(publisher_req, &actor).unwrap(); - let list_reply = server.list(&alice_handle).unwrap(); + let list_reply = server.repo().list(&alice_handle).unwrap(); assert_eq!(0, list_reply.elements().len()); } - #[tokio::test] - async fn should_publish_files() { - // we need a disk, as repo_dir, etc. use data_dir by default - let storage_uri = test::mem_storage(); - let (server, data_dir) = make_server(&storage_uri); + #[test] + fn should_publish_files() { + let server = TestServer::new(); - let session = session_dir(data_dir.path()); + let session = session_dir(server.data_dir.path()); // Check that the server starts with dir for serial 1 for RRDP // and does not use 0 (RFC 8182) @@ -536,14 +521,14 @@ mod tests { assert!(session_dir_contains_serial(&session, RRDP_FIRST_SERIAL)); // set up server with default repository, and publisher alice - let alice = publisher_alice(&storage_uri); + let alice = publisher_alice(&server.storage_uri); let alice_handle = Handle::from_str("alice").unwrap(); let publisher_req = make_publisher_req(alice_handle.as_str(), alice.id_cert()); let actor = ACTOR_DEF_TEST; - server.create_publisher(publisher_req, &actor).unwrap(); + server.repo().create_publisher(publisher_req, &actor).unwrap(); // get the file out of a list_reply fn find_in_reply<'a>( @@ -568,12 +553,12 @@ mod tests { delta.add_publish(file1.as_publish()); delta.add_publish(file2.as_publish()); - server.publish(&alice_handle, delta).unwrap(); - server.update_rrdp_if_needed().unwrap(); - server.write_repository().unwrap(); + server.repo().publish(&alice_handle, delta, &server.krill).unwrap(); + server.repo().update_rrdp_if_needed().unwrap(); + server.repo().write_repository().unwrap(); // Two files should now appear in the list - let list_reply = server.list(&alice_handle).unwrap(); + let list_reply = server.repo().list(&alice_handle).unwrap(); assert_eq!(2, list_reply.elements().len()); assert!(find_in_reply( &list_reply, @@ -586,7 +571,7 @@ mod tests { ) .is_some()); - sleep(Duration::from_secs(2)).await; + sleep(Duration::from_secs(2)); // Update // - update file @@ -609,12 +594,12 @@ mod tests { delta.add_withdraw(file2.as_withdraw()); delta.add_publish(file3.as_publish()); - server.publish(&alice_handle, delta).unwrap(); - server.update_rrdp_if_needed().unwrap(); - server.write_repository().unwrap(); + server.repo().publish(&alice_handle, delta, &server.krill).unwrap(); + server.repo().update_rrdp_if_needed().unwrap(); + server.repo().write_repository().unwrap(); // Two files should now appear in the list - let list_reply = server.list(&alice_handle).unwrap(); + let list_reply = server.repo().list(&alice_handle).unwrap(); assert_eq!(2, list_reply.elements().len()); assert!(find_in_reply( @@ -645,7 +630,7 @@ mod tests { let mut delta = PublishDelta::empty(); delta.add_publish(file_outside.as_publish()); - match server.publish(&alice_handle, delta) { + match server.repo().publish(&alice_handle, delta, &server.krill) { Err(Error::Rfc8181Delta( PublicationDeltaError::UriOutsideJail(_, _), )) => {} // ok @@ -660,7 +645,7 @@ mod tests { let mut delta = PublishDelta::empty(); delta.add_update(file2_update.as_update(file2.hash())); - match server.publish(&alice_handle, delta) { + match server.repo().publish(&alice_handle, delta, &server.krill) { Err(Error::Rfc8181Delta( PublicationDeltaError::NoObjectForHashAndOrUri(_), )) => {} @@ -671,7 +656,7 @@ mod tests { let mut delta = PublishDelta::empty(); delta.add_withdraw(file2.as_withdraw()); - match server.publish(&alice_handle, delta) { + match server.repo().publish(&alice_handle, delta, &server.krill) { Err(Error::Rfc8181Delta( PublicationDeltaError::NoObjectForHashAndOrUri(_), )) => {} // ok @@ -684,7 +669,7 @@ mod tests { let mut delta = PublishDelta::empty(); delta.add_publish(file3.as_publish()); - match server.publish(&alice_handle, delta) { + match server.repo().publish(&alice_handle, delta, &server.krill) { Err(Error::Rfc8181Delta( PublicationDeltaError::ObjectAlreadyPresent(uri), )) => { @@ -717,9 +702,9 @@ mod tests { let mut delta = PublishDelta::empty(); delta.add_publish(file4.as_publish()); - server.publish(&alice_handle, delta).unwrap(); - server.update_rrdp_if_needed().unwrap(); - server.write_repository().unwrap(); + server.repo().publish(&alice_handle, delta, &server.krill).unwrap(); + server.repo().update_rrdp_if_needed().unwrap(); + server.repo().write_repository().unwrap(); // Should include new snapshot and delta assert!(session_dir_contains_serial(&session, RRDP_FIRST_SERIAL + 3)); @@ -733,9 +718,11 @@ mod tests { assert!(session_dir_contains_delta(&session, RRDP_FIRST_SERIAL + 2)); // Removing the publisher should remove its contents - server.remove_publisher(alice_handle, &actor).unwrap(); - server.update_rrdp_if_needed().unwrap(); - server.write_repository().unwrap(); + server.repo().remove_publisher( + alice_handle, &actor, &server.krill + ).unwrap(); + server.repo().update_rrdp_if_needed().unwrap(); + server.repo().write_repository().unwrap(); // new snapshot should be published, and should be empty now assert!(session_dir_contains_snapshot( @@ -768,18 +755,17 @@ mod tests { #[test] pub fn repository_session_reset() { - let storage_uri = test::mem_storage(); - let (server, data_dir) = make_server(&storage_uri); + let server = TestServer::new(); // set up server with default repository, and publisher alice - let alice = publisher_alice(&storage_uri); + let alice = publisher_alice(&server.storage_uri); let alice_handle = Handle::from_str("alice").unwrap(); let publisher_req = make_publisher_req(alice_handle.as_str(), alice.id_cert()); let actor = ACTOR_DEF_TEST; - server.create_publisher(publisher_req, &actor).unwrap(); + server.repo().create_publisher(publisher_req, &actor).unwrap(); // get the file out of a list_reply fn find_in_reply<'a>( @@ -804,12 +790,12 @@ mod tests { delta.add_publish(file1.as_publish()); delta.add_publish(file2.as_publish()); - server.publish(&alice_handle, delta).unwrap(); - server.update_rrdp_if_needed().unwrap(); - server.write_repository().unwrap(); + server.repo().publish(&alice_handle, delta, &server.krill).unwrap(); + server.repo().update_rrdp_if_needed().unwrap(); + server.repo().write_repository().unwrap(); // Two files should now appear in the list - let list_reply = server.list(&alice_handle).unwrap(); + let list_reply = server.repo().list(&alice_handle).unwrap(); assert_eq!(2, list_reply.elements().len()); assert!(find_in_reply( &list_reply, @@ -823,10 +809,10 @@ mod tests { .is_some()); // Find RRDP files on disk - let stats_before = server.repo_stats().unwrap(); + let stats_before = server.repo().repo_stats().unwrap(); let session_before = stats_before.session; let snapshot_before_session_reset = find_in_session_and_serial_dir( - data_dir.path(), + server.data_dir.path(), session_before, RRDP_FIRST_SERIAL + 1, "snapshot.xml", @@ -835,14 +821,14 @@ mod tests { assert!(snapshot_before_session_reset.is_some()); // Now test that a session reset works... - server.rrdp_session_reset().unwrap(); + server.repo().rrdp_session_reset().unwrap(); // Should write new session and snapshot - let stats_after = server.repo_stats().unwrap(); + let stats_after = server.repo().repo_stats().unwrap(); let session_after = stats_after.session; let snapshot_after_session_reset = find_in_session_and_serial_dir( - data_dir.path(), + server.data_dir.path(), session_after, RRDP_FIRST_SERIAL, "snapshot.xml", @@ -856,7 +842,7 @@ mod tests { // and clean up old dir let snapshot_before_session_reset = find_in_session_and_serial_dir( - data_dir.path(), + server.data_dir.path(), session_before, RRDP_FIRST_SERIAL + 1, "snapshot.xml", diff --git a/src/server/runtime.rs b/src/server/runtime.rs index 73eab789c..e813cece0 100644 --- a/src/server/runtime.rs +++ b/src/server/runtime.rs @@ -19,17 +19,17 @@ //! +use std::mem::drop; use std::sync::Arc; -//use std::time::Duration; -//use log::info; +use std::time::Duration; +use log::info; use rpki::uri; use tokio::runtime; -use tokio::sync::oneshot; use crate::commons::actor::Actor; -use crate::commons::crypto::{KrillSigner/*, KrillSignerBuilder*/}; -//use crate::commons::error::KrillError; +use crate::commons::crypto::{KrillSigner, KrillSignerBuilder}; +use crate::commons::error::KrillError; use crate::config::Config; -//use crate::constants::{ACTOR_DEF_KRILL, KRILL_SERVER_APP}; +use crate::constants::{ACTOR_DEF_KRILL, KRILL_SERVER_APP}; use super::bgp::BgpAnalyser; use super::ca::CaManager; use super::mq::TaskQueue; @@ -42,7 +42,6 @@ use super::pubd::RepositoryManager; pub struct KrillRuntime(Arc); impl KrillRuntime { - /* pub fn new( config: Config, tokio: runtime::Handle, @@ -66,8 +65,8 @@ impl KrillRuntime { ).build()?; let tasks = TaskQueue::new(&config.storage_uri)?; - let repo_manager = RepositoryManager::build(&config)?; - let ca_manager = CaManager::build(&config)?; + let repo_manager = RepositoryManager::new(&config)?; + let ca_manager = CaManager::new(&config)?; let bgp_analyser = BgpAnalyser::new(&config); Ok(Self(Arc::new(Components { @@ -82,7 +81,6 @@ impl KrillRuntime { tokio, }))) } - */ pub fn config(&self) -> &Config { &self.0.config @@ -125,14 +123,9 @@ impl KrillRuntime { pub fn spawn_async( &self, future: impl Future + Send + 'static ) { - let _ = self.0.tokio.spawn(future); - } - - /// Spawns a closure onto the sync runtime. - pub fn spawn_blocking( - &self, op: impl FnOnce() + Send + 'static - ) { - let _ = self.0.tokio.spawn_blocking(op); + // Explicitely drop the join handle so Clippy doesn’t complain. The + // task will continue running. + drop(self.0.tokio.spawn(future)); } } @@ -176,184 +169,3 @@ struct Components { tokio: runtime::Handle, } - -//------------ Init ---------------------------------------------------------- - -pub struct Init { - /// The capture value passed along during execution. - capture: Cap, - - /// The initial calculation of the errand. - value: MaybeFuture, - - /// The Krill runtime to use and pass along. - krill: KrillRuntime, -} - -impl Init { - pub fn then(self, op: Op) -> Then { - Then { - before: self, - op: op - } - } -} - -impl Errand for Init -where - Cap: Send + 'static, - Fut: Future + Send + 'static, - Fut::Output: Send + 'static, -{ - type Capture = Cap; - type Output = Fut::Output; - - fn run(self, then: Then) - where - Then: - FnOnce(Cap, Self::Output, KrillRuntime) - + Send + 'static - { - match self.value { - MaybeFuture::Ready(res) => (then)(self.capture, res, self.krill), - MaybeFuture::Future(fut) => { - self.krill.clone().spawn_async(async move { - let res = fut.await; - self.krill.clone().spawn_blocking(move || { - (then)(self.capture, res, self.krill); - }) - }) - } - } - } - - fn finish(self, tx: oneshot::Sender) { - match self.value { - MaybeFuture::Ready(res) => { - let _ = tx.send(res); - } - MaybeFuture::Future(fut) => { - self.krill.spawn_async(async { - let _ = tx.send(fut.await); - }) - } - } - } -} - - -//------------ Then ---------------------------------------------------------- - -/// An errand with an additional stage chained to it. -pub struct Then { - // the errand that produces the output we are processing - before: Before, - - // a function that is run sync and returns a future. - // - // this needs to be spawned blocking when outer resolves. - op: Op, -} - -impl Then { - pub fn then(self, op: OOp) -> Then{ - Then { - before: self, - op, - } - } -} - -impl Errand for Then -where - Before: Errand, - Op: IntoMaybeFuture, -{ - type Capture = Op::Capture; - type Output = Op::Output; - - fn run(self, then: Then) - where - Then: - FnOnce(Op::Capture, Self::Output, KrillRuntime) - + Send + 'static - { - self.before.run(|mut capture, input, krill| { - match self.op.eval(&mut capture, input, &krill) { - MaybeFuture::Ready(res) => (then)(capture, res, krill), - MaybeFuture::Future(fut) => { - krill.clone().spawn_async(async { - let res = fut.await; - krill.clone().spawn_blocking(|| { - (then)(capture, res, krill); - }) - }) - } - } - }) - } - - fn finish(self, tx: oneshot::Sender) { - self.before.run(|mut capture, input, krill| { - match self.op.eval(&mut capture, input, &krill) { - MaybeFuture::Ready(res) => { - let _ = tx.send(res); - } - MaybeFuture::Future(fut) => { - krill.spawn_async(async { - let _ = tx.send(fut.await); - }) - } - } - }); - } -} - - -//------------ MaybeFuture --------------------------------------------------- - -/// A value that is either already present or the result of a future. -pub enum MaybeFuture { - /// The value is already present. - Ready(Fut::Output), - - /// The value needs to be calculated by resolving the future. - Future(Fut), -} - - -//------------ IntoMaybeFuture ----------------------------------------------- - -/// An operation that will result in a `MaybeFuture`. -pub trait IntoMaybeFuture: Send + 'static { - type Capture: Send + 'static; - type Input: Send + 'static; - type Output: Send + 'static; - type Future: Future + Send + 'static; - - fn eval( - self, - capture: &mut Self::Capture, - input: Self::Input, - krill: &KrillRuntime, - ) -> MaybeFuture; -} - - -//------------ Errand -------------------------------------------------------- - -/// A single step in running an errand. -pub trait Errand: Sized { - type Capture: Send + 'static; - type Output: Send + 'static; - - fn run(self, then: Then) - where - Then: - FnOnce(Self::Capture, Self::Output, KrillRuntime) - + Send + 'static - ; - - fn finish(self, tx: oneshot::Sender); -} - diff --git a/src/server/scheduler.rs b/src/server/scheduler.rs index 39a915332..38c3a4aca 100644 --- a/src/server/scheduler.rs +++ b/src/server/scheduler.rs @@ -1,21 +1,17 @@ //! Deal with asynchronous scheduled processes, either triggered by an //! event that occurred, or planned (e.g. re-publishing). -use std::{collections::HashMap, sync::Arc, time::Duration}; - -use tokio::time::sleep; - +use std::collections::HashMap; +use std::thread::sleep; +use std::time::Duration; use log::{debug, error, info, warn}; -use rpki::ca::{ - idexchange::{CaHandle, ParentHandle}, - provisioning::{ResourceClassName, RevocationRequest}, -}; +use rpki::ca::idexchange::{CaHandle, ParentHandle}; +use rpki::ca::provisioning::{ResourceClassName, RevocationRequest}; use url::Url; use crate::{ api::ca::Timestamp, commons::{ - actor::Actor, crypto::dispatch::signerinfo::SignerInfo, error::FatalError, eventsourcing::{Aggregate, AggregateStore, WalStore, WalSupport}, @@ -28,707 +24,666 @@ use crate::{ SCHEDULER_RESYNC_REPO_CAS_THRESHOLD, SCHEDULER_USE_JITTER_CAS_THRESHOLD, SIGNERS_NS, }, - config::Config, server::{ - ca::{CaManager, CertAuth}, - bgp::BgpAnalyser, + ca::CertAuth, mq::{ - in_hours, in_minutes, in_seconds, in_weeks, now, Task, TaskQueue, + in_hours, in_minutes, in_seconds, in_weeks, now, Task, }, properties::Properties, - pubd::{RepositoryAccess, RepositoryContent, RepositoryManager}, + pubd::{RepositoryAccess, RepositoryContent}, runtime::KrillRuntime, }, }; use super::mq::TaskResult; -pub struct Scheduler { - krill: KrillRuntime, - tasks: Arc, - ca_manager: Arc, - repo_manager: Arc, - bgp_analyser: Arc, - config: Arc, - system_actor: Actor, - started: Timestamp, -} -impl Scheduler { - #[allow(unreachable_code, unused_variables)] - pub fn build( - tasks: Arc, - ca_manager: Arc, - repo_manager: Arc, - bgp_analyser: Arc, - config: Arc, - system_actor: Actor, - ) -> Self { - Scheduler { - krill: todo!(), - tasks, - ca_manager, - repo_manager, - bgp_analyser, - config, - system_actor, - started: Timestamp::now(), - } - } +//------------ run ----------------------------------------------------------- - /// Run the scheduler in the background. It will sweep the message queue - /// for tasks and re-schedule new tasks as needed. - pub async fn run(&self) { - loop { - while let Some((task_key, value)) = self.tasks.pop() { - match serde_json::from_value(value) { - Err(e) => { - // If we cannot parse the value of this task, then we - // have a major - // issue. Essentially, this can only happen if we did - // a Krill upgrade - // to a new version that no longer understands - // existing tasks. - // - // So, if we ever change the content of tasks then we - // should make sure that Krill - // is either backward compatible, or the task queue is - // migrated on upgrade. - error!("Fatal error parsing task: {}. Krill will now stop! This may be because this task is not for this Krill version ({}). If this issue persists, then try deleting this task from storage, it will appear in the 'tasks' dir if you use disk storage. The error was {}", task_key, KrillVersion::code_version(), e); - std::process::exit(1); - } - Ok(task) => match self.process_task(task).await { - Ok(result) => { - if let Err(e) = match result { - TaskResult::Done => { - self.tasks.finish(&task_key) - } - TaskResult::FollowUp(task, priority) => { - self.tasks.schedule_and_finish_existing( - task, priority, - ) - } - TaskResult::Reschedule(priority) => { - self.tasks.reschedule(&task_key, priority) - } - } { - error!("Error finishing / scheduling task {task_key}. Krill will stop as there is no good way to recover from this. When Krill starts it will try to reschedule any missing tasks. Error was: {e}"); - std::process::exit(1); +pub(super) fn run(krill: KrillRuntime) { + let started = Timestamp::now(); + + loop { + while let Some((task_key, value)) = krill.tasks().pop() { + match serde_json::from_value(value) { + Err(e) => { + // If we cannot parse the value of this task, then we + // have a major issue. Essentially, this can only happen + // if we did a Krill upgrade to a new version that no + // longer understands existing tasks. + // + // So, if we ever change the content of tasks then we + // should make sure that Krill is either backward + // compatible, or the task queue ismigrated on upgrade. + error!( + "Fatal error parsing task: {}. Krill will now stop! \ + This may be because this task is not for this \ + Krill version ({}). If this issue persists, then \ + try deleting this task from storage, it will \ + appear in the 'tasks' dir if you use disk storage. \ + The error was {}", + task_key, KrillVersion::code_version(), e + ); + std::process::exit(1); + } + Ok(task) => match process_task(&krill, task, started) { + Ok(result) => { + if let Err(e) = match result { + TaskResult::Done => { + krill.tasks().finish(&task_key) } - } - Err(e) => { - error!("Error processing task: {task_key}. Tasks are only allowed to return fatal errors. Krill will stop as there is no good way to recover from this. When Krill starts it will try to reschedule any missing tasks. Error was: {e}"); + TaskResult::FollowUp(task, priority) => { + krill.tasks().schedule_and_finish_existing( + task, priority, + ) + } + TaskResult::Reschedule(priority) => { + krill.tasks().reschedule(&task_key, priority) + } + } { + error!( + "Error finishing / scheduling task \ + {task_key}. Krill will stop as there is no \ + good way to recover from this. When Krill \ + starts it will try to reschedule any \ + missing tasks. Error was: {e}" + ); std::process::exit(1); } - }, - } + } + Err(e) => { + error!( + "Error processing task: {task_key}. Tasks are \ + only allowed to return fatal errors. Krill will \ + stop as there is no good way to recover from \ + this. When Krill starts it will try to \ + reschedule any missing tasks. Error was: {e}" + ); + std::process::exit(1); + } + }, } - - sleep(Duration::from_millis(500)).await; } + + sleep(Duration::from_millis(500)); } +} - /// Process a single task - /// - /// May only return fatal errors. Temporary, or suspected temporary, - /// issues such as not being able to contact a parent CA should result - /// in an Ok(TaskResult::Reschedule) instead. - async fn process_task( - &self, - task: Task, - ) -> Result { - match task { - Task::QueueStartTasks => self.queue_start_tasks().await, /* return error and stop server on failure */ - Task::SyncRepo { - ca_handle: ca, - ca_version, - } => self.sync_repo(ca, ca_version).await, +/// Process a single task +/// +/// May only return fatal errors. Temporary, or suspected temporary, +/// issues such as not being able to contact a parent CA should result +/// in an Ok(TaskResult::Reschedule) instead. +fn process_task( + krill: &KrillRuntime, task: Task, started: Timestamp, +) -> Result { + match task { + Task::QueueStartTasks => { + queue_start_tasks(krill) + } - Task::SyncParent { - ca_handle: ca, - ca_version, - parent, - } => self.sync_parent(ca, ca_version, parent).await, + Task::SyncRepo { ca_handle, ca_version } => { + sync_repo(krill, ca_handle, ca_version) + } - Task::RenewTestbedTa => self.renew_testbed_ta().await, + Task::SyncParent { ca_handle, ca_version, parent } => { + sync_parent(krill, ca_handle, ca_version, parent) + } - Task::SyncTrustAnchorProxySignerIfPossible => { - self.sync_ta_proxy_signer_if_possible().await - } + Task::RenewTestbedTa => renew_testbed_ta(krill), - Task::SuspendChildrenIfNeeded { ca_handle: ca } => { - self.suspend_children_if_needed(ca).await - } + Task::SyncTrustAnchorProxySignerIfPossible => { + sync_ta_proxy_signer_if_possible(krill) + } - Task::RepublishIfNeeded => self.republish_if_needed().await, + Task::SuspendChildrenIfNeeded { ca_handle: ca } => { + suspend_children_if_needed(krill, ca, started) + } - Task::RenewObjectsIfNeeded => { - self.renew_objects_if_needed().await - } + Task::RepublishIfNeeded => republish_if_needed(krill), - Task::UpdateSnapshots => self.update_snapshots(), + Task::RenewObjectsIfNeeded => { + renew_objects_if_needed(krill) + } - Task::RrdpUpdateIfNeeded => self.update_rrdp_if_needed(), + Task::UpdateSnapshots => update_snapshots(krill), - Task::ResourceClassRemoved { - ca_handle: ca, - ca_version, - parent, - rcn, - revocation_requests, - } => { - self.resource_class_removed( - ca, - ca_version, - parent, - rcn, - revocation_requests, - ) - .await - } + Task::RrdpUpdateIfNeeded => update_rrdp_if_needed(krill), - Task::UnexpectedKey { - ca_handle: ca, - ca_version, - rcn, - revocation_request, - } => { - self.unexpected_key(ca, ca_version, rcn, revocation_request) - .await - } + Task::ResourceClassRemoved { + ca_handle, ca_version, parent, rcn, revocation_requests, + } => { + resource_class_removed( + krill, ca_handle, ca_version, parent, rcn, revocation_requests, + ) + } - Task::RefreshAnnouncementsInfo => { - self.announcements_refresh().await - } + Task::UnexpectedKey { + ca_handle, ca_version, rcn, revocation_request, + } => { + unexpected_key( + krill, ca_handle, ca_version, rcn, revocation_request + ) + } - Task::SweepLoginCache => { - // Don’t do anything. These are deprecated. - Ok(TaskResult::Done) - } + Task::RefreshAnnouncementsInfo => { + announcements_refresh(krill) + } + + Task::SweepLoginCache => { + // Don’t do anything. These are deprecated. + Ok(TaskResult::Done) } } +} - /// Queues missing tasks for background jobs when the server is started - async fn queue_start_tasks(&self) -> Result { - // The task queue is persistent starting with Krill 0.14.0 - // - // Tasks should not disappear. But.. to make sure that: - // a) krill is self-healing - // b) this works on the first upgrade to 0.14.0 - // - // We will add all MISSING tasks that we think will be needed. - // - // This works simplest by adding all task with the Existing::KeepOld - // option of the queue. +/// Queues missing tasks for background jobs when the server is started +fn queue_start_tasks(krill: &KrillRuntime) -> Result { + // The task queue is persistent starting with Krill 0.14.0 + // + // Tasks should not disappear. But.. to make sure that: + // a) krill is self-healing + // b) this works on the first upgrade to 0.14.0 + // + // We will add all MISSING tasks that we think will be needed. + // + // This works simplest by adding all task with the Existing::KeepOld + // option of the queue. + + // If there are only a few CAs in this Krill instance, then we + // will just want to re-sync them with their parents and repository + // on start up. + // + // If there are many, then we apply some random delays (jitter) + // to avoid a thundering herd. Note that the operator can always + // choose to run bulk operations manually if they know that they + // cannot wait. + let cas = krill.ca_manager().ca_handles().map_err(FatalError)?; + debug!("Adding missing tasks at start up"); + + // If we have many CAs then we need to apply some jitter + // in the priority of CA to parent and CA to repository + // syncs to avoid generating a thundering herd. + + let use_jitter = cas.len() >= SCHEDULER_USE_JITTER_CAS_THRESHOLD; + + for handle in &cas { + let ca = krill.ca_manager().get_ca(handle).map_err(FatalError)?; + let ca_handle = ca.handle(); + let ca_version = ca.version(); + + debug!( + "Adding tasks for CA {}, using jitter: {}", + ca.handle(), + use_jitter + ); + + for parent in ca.parents() { + krill.tasks().schedule_missing( + Task::SyncParent { + ca_handle: ca_handle.clone(), + ca_version, + parent: parent.clone(), + }, + krill.config().ca_refresh_start_up(use_jitter), + ) + .map_err(FatalError)?; + } - // If there are only a few CAs in this Krill instance, then we - // will just want to re-sync them with their parents and repository - // on start up. + // Plan a sync with the repo. But only in case we only have a + // handful of CAs. // - // If there are many, then we apply some random delays (jitter) - // to avoid a thundering herd. Note that the operator can always - // choose to run bulk operations manually if they know that they - // cannot wait. - let cas = self.ca_manager.ca_handles().map_err(FatalError)?; - debug!("Adding missing tasks at start up"); - - // If we have many CAs then we need to apply some jitter - // in the priority of CA to parent and CA to repository - // syncs to avoid generating a thundering herd. - - let use_jitter = cas.len() >= SCHEDULER_USE_JITTER_CAS_THRESHOLD; - - for handle in &cas { - let ca = self - .ca_manager - .get_ca(handle) - .map_err(FatalError)?; - let ca_handle = ca.handle(); - let ca_version = ca.version(); + // Note: if circumstances dictate a sync e.g. because ROAs are + // changed, then it will be scheduled accordingly. + // Furthermore, users can use the 'bulk' function to + // explicitly force schedule a sync. + if cas.len() <= SCHEDULER_RESYNC_REPO_CAS_THRESHOLD { + krill.tasks().schedule_missing( + Task::SyncRepo { + ca_handle: ca_handle.clone(), + ca_version, + }, + now(), + ).map_err(FatalError)?; + } - debug!( - "Adding tasks for CA {}, using jitter: {}", - ca.handle(), - use_jitter - ); + // If suspension is enabled then plan a task for it. Since this is + // a cheap no-op in most cases, we do not need jitter. If we do + // not add this task then it will not be executed + // (obviously), but more importantly.. by adding this + // task we ensure that it will keep being re-scheduled + // when it's done. + if krill.config().suspend_child_after_inactive_seconds().is_some() { + krill.tasks().schedule_missing( + Task::SuspendChildrenIfNeeded { + ca_handle: ca_handle.clone(), + }, + now(), + ).map_err(FatalError)?; + } + } - for parent in ca.parents() { - self.tasks - .schedule_missing( - Task::SyncParent { - ca_handle: ca_handle.clone(), - ca_version, - parent: parent.clone(), - }, - self.config.ca_refresh_start_up(use_jitter), - ) - .map_err(FatalError)?; - } + krill.tasks().schedule_missing( + Task::RepublishIfNeeded, now() + ).map_err(FatalError)?; + krill.tasks().schedule_missing( + Task::RenewObjectsIfNeeded, now() + ).map_err(FatalError)?; + + // BGP announcement info is only kept in-memory, so it + // is lost after a restart, so schedule refreshing this + // immediately. + if krill.config().bgp_riswhois_enabled { + krill.tasks().schedule( + Task::RefreshAnnouncementsInfo, now() + ).map_err(FatalError)?; + } - // Plan a sync with the repo. But only in case we only have a - // handful of CAs. - // - // Note: if circumstances dictate a sync e.g. because ROAs are - // changed, then it will be scheduled accordingly. - // Furthermore, users can use the 'bulk' function to - // explicitly force schedule a sync. - if cas.len() <= SCHEDULER_RESYNC_REPO_CAS_THRESHOLD { - self.tasks - .schedule_missing( - Task::SyncRepo { - ca_handle: ca_handle.clone(), - ca_version, - }, - now(), - ) - .map_err(FatalError)?; - } + // Plan updating snapshots soon after a restart. + // This also ensures that this task gets triggered in long + // running tests, such as functional_parent_child.rs. + krill.tasks().schedule_missing( + Task::UpdateSnapshots, now() + ).map_err(FatalError)?; + + if krill.config().testbed().is_some() { + krill.tasks().schedule_missing( + Task::RenewTestbedTa, now() + ).map_err(FatalError)?; + } - // If suspension is enabled then plan a task for it. Since this is - // a cheap no-op in most cases, we do not need jitter. If we do - // not add this task then it will not be executed - // (obviously), but more importantly.. by adding this - // task we ensure that it will keep being re-scheduled - // when it's done. - if self.config.suspend_child_after_inactive_seconds().is_some() { - self.tasks - .schedule_missing( - Task::SuspendChildrenIfNeeded { - ca_handle: ca_handle.clone(), - }, - now(), - ) - .map_err(FatalError)?; - } - } + Ok(TaskResult::Done) +} - self.tasks - .schedule_missing(Task::RepublishIfNeeded, now()) - .map_err(FatalError)?; - self.tasks - .schedule_missing(Task::RenewObjectsIfNeeded, now()) - .map_err(FatalError)?; +fn sync_repo( + krill: &KrillRuntime, + ca: CaHandle, + version: u64, +) -> Result { + info!("Synchronize CA {ca} with repository"); - // BGP announcement info is only kept in-memory, so it - // is lost after a restart, so schedule refreshing this - // immediately. - if self.config.bgp_riswhois_enabled { - self.tasks - .schedule(Task::RefreshAnnouncementsInfo, now()) - .map_err(FatalError)?; - } + match krill.ca_manager().cas_repo_sync_single(&ca, version, krill) { + Err(e) => { + let next = krill.config().requeue_remote_failed(); - // Plan updating snapshots soon after a restart. - // This also ensures that this task gets triggered in long - // running tests, such as functional_parent_child.rs. - self.tasks - .schedule_missing(Task::UpdateSnapshots, now()) - .map_err(FatalError)?; + error!( + "Failed to publish for '{ca}'. \ + Will reschedule to: '{next}'. Error: {e}" + ); - if self.config.testbed().is_some() { - self.tasks - .schedule_missing(Task::RenewTestbedTa, now()) - .map_err(FatalError)?; + Ok(TaskResult::Reschedule(next)) + } + Ok(true) => Ok(TaskResult::Done), + Ok(false) => { + debug!("sync was premature, reschedule"); + let next = in_seconds(1); + Ok(TaskResult::Reschedule(next)) } - - Ok(TaskResult::Done) } +} - async fn sync_repo( - &self, - ca: CaHandle, - version: u64, - ) -> Result { - info!("Synchronize CA {ca} with repository"); - - match self - .ca_manager - .cas_repo_sync_single(self.repo_manager.as_ref(), &ca, version) - .await - { +/// Try to synchronize a CA with a specific parent, reschedule if this +/// fails +fn sync_parent( + krill: &KrillRuntime, + ca: CaHandle, + ca_version: u64, + parent: ParentHandle, +) -> Result { + if krill.ca_manager().has_ca(&ca).map_err(FatalError)? { + info!("Synchronize CA '{ca}' with its parent '{parent}'"); + match krill.ca_manager().ca_sync_parent( + &ca, ca_version, &parent, krill.system_actor(), krill, + ) { Err(e) => { - let next = self.config.requeue_remote_failed(); + let next = krill.config().requeue_remote_failed(); error!( - "Failed to publish for '{ca}'. Will reschedule to: '{next}'. Error: {e}" + "Failed to synchronize CA '{ca}' with its parent \ + '{parent}'. Will reschedule to: '{next}'. Error: {e}" ); - Ok(TaskResult::Reschedule(next)) } - Ok(true) => Ok(TaskResult::Done), + Ok(true) => { + let next = krill.config().ca_refresh_next(); + Ok(TaskResult::FollowUp( + Task::SyncParent { + ca_handle: ca, + ca_version, + parent, + }, + next, + )) + } Ok(false) => { - debug!("sync was premature, reschedule"); + debug!("reschedule premature task"); let next = in_seconds(1); Ok(TaskResult::Reschedule(next)) } } } + else { + // Note: if one day we can have a notification extension to RFC + // 6492 then we will also be able to alert + // remote children. + debug!( + "Skipping parent sync fo CA '{ca}'. It is either a remote \ + child, or a local CA that has been removed" + ); + Ok(TaskResult::Done) + } +} - /// Try to synchronize a CA with a specific parent, reschedule if this - /// fails - async fn sync_parent( - &self, - ca: CaHandle, - ca_version: u64, - parent: ParentHandle, - ) -> Result { - if self.ca_manager.has_ca(&ca).map_err(FatalError)? { - info!("Synchronize CA '{ca}' with its parent '{parent}'"); - match self.ca_manager.ca_sync_parent( - &ca, ca_version, &parent, &self.system_actor, &self.krill, - ).await - { - Err(e) => { - let next = self.config.requeue_remote_failed(); +/// Resync the testbed TA signer and proxy +fn renew_testbed_ta(krill: &KrillRuntime) -> Result { + if let Err(e) = krill.ca_manager().ta_renew_testbed_ta(krill) { + error!("There was an issue renewing the testbed TA: {e}"); + } + let weeks_to_resync = krill.config().ta_timing.mft_next_update_weeks / 2; + Ok(TaskResult::FollowUp( + Task::RenewTestbedTa, + in_weeks(weeks_to_resync), + )) +} - error!( - "Failed to synchronize CA '{ca}' with its parent '{parent}'. Will reschedule to: '{next}'. Error: {e}" - ); - Ok(TaskResult::Reschedule(next)) - } - Ok(true) => { - let next = self.config.ca_refresh_next(); - Ok(TaskResult::FollowUp( - Task::SyncParent { - ca_handle: ca, - ca_version, - parent, - }, - next, - )) - } - Ok(false) => { - debug!("reschedule premature task"); - let next = in_seconds(1); - Ok(TaskResult::Reschedule(next)) - } - } - } else { - // Note: if one day we can have a notification extension to RFC - // 6492 then we will also be able to alert - // remote children. - debug!( - "Skipping parent sync fo CA '{ca}'. It is either a remote child, or a local CA that has been removed" - ); - Ok(TaskResult::Done) - } +/// Try to synchronise the Trust Anchor Proxy with the *local* Signer - if +/// it exists in this server. +fn sync_ta_proxy_signer_if_possible( + krill: &KrillRuntime +) -> Result { + debug!("Synchronise Trust Anchor Proxy with Signer - if Signer is local."); + if let Err(e) = krill.ca_manager().sync_ta_proxy_signer_if_possible(krill) { + error!("There was an issue synchronising the TA Proxy and Signer: {e}"); } + Ok(TaskResult::Done) +} + +/// Try to suspend children for a CA +fn suspend_children_if_needed( + krill: &KrillRuntime, ca_handle: CaHandle, started: Timestamp +) -> Result { + if krill.ca_manager().has_ca(&ca_handle).map_err(FatalError)? { + debug!( + "Verify if CA '{ca_handle}' has children that need to be suspended" + ); + krill.ca_manager().ca_suspend_inactive_children( + &ca_handle, started, krill.system_actor(), krill + ); - /// Resync the testbed TA signer and proxy - async fn renew_testbed_ta(&self) -> Result { - if let Err(e) = self.ca_manager.ta_renew_testbed_ta(&self.krill) { - error!("There was an issue renewing the testbed TA: {e}"); - } - let weeks_to_resync = self.config.ta_timing.mft_next_update_weeks / 2; Ok(TaskResult::FollowUp( - Task::RenewTestbedTa, - in_weeks(weeks_to_resync), + Task::SuspendChildrenIfNeeded { ca_handle }, + in_hours(1), )) - } - - /// Try to synchronise the Trust Anchor Proxy with the *local* Signer - if - /// it exists in this server. - async fn sync_ta_proxy_signer_if_possible( - &self, - ) -> Result { - debug!("Synchronise Trust Anchor Proxy with Signer - if Signer is local."); - if let Err(e) = - self.ca_manager.sync_ta_proxy_signer_if_possible(&self.krill) - { - error!("There was an issue synchronising the TA Proxy and Signer: {e}"); - } + } else { + debug!( + "Drop task to suspend children for removed CA {ca_handle}" + ); Ok(TaskResult::Done) } +} - /// Try to suspend children for a CA - async fn suspend_children_if_needed( - &self, - ca_handle: CaHandle, - ) -> Result { - if self.ca_manager.has_ca(&ca_handle).map_err(FatalError)? { - debug!( - "Verify if CA '{ca_handle}' has children that need to be suspended" - ); - self.ca_manager.ca_suspend_inactive_children( - &ca_handle, self.started, &self.system_actor, &self.krill, - ); - - Ok(TaskResult::FollowUp( - Task::SuspendChildrenIfNeeded { ca_handle }, - in_hours(1), - )) - } else { - debug!( - "Drop task to suspend children for removed CA {ca_handle}" - ); - Ok(TaskResult::Done) - } +/// Let CAs that need it republish their CRL/MFT +fn republish_if_needed( + krill: &KrillRuntime +) -> Result { + // Note that CRL/MFT re-issuance is handled by the `CaObjects` + // companion struct, rather than the event-sourced `CertAuth`. + // Meaning... that we do not get to see an event in case there + // is an actual update and therefore we get no triggered task + // to synchronise with the repository. + // + // Instead we get back a list of CAs that had changes, and we need to + // schedule a synchronisation for each of them here. + let cas = krill.ca_manager().republish_all( + false, krill + ).map_err(FatalError)?; + + for ca_handle in cas { + info!("Re-issued MFT and CRL for CA: {ca_handle}"); + + let ca_version = 0; // we use 0 because we don't need to wait for + // an updated CertAuth + krill.tasks().schedule( + Task::SyncRepo { + ca_handle, + ca_version, + }, + now(), + ).map_err(FatalError)?; } - /// Let CAs that need it republish their CRL/MFT - async fn republish_if_needed(&self) -> Result { - // Note that CRL/MFT re-issuance is handled by the `CaObjects` - // companion struct, rather than the event-sourced `CertAuth`. - // Meaning... that we do not get to see an event in case there - // is an actual update and therefore we get no triggered task - // to synchronise with the repository. - // - // Instead we get back a list of CAs that had changes, and we need to - // schedule a synchronisation for each of them here. - let cas = self - .ca_manager - .republish_all(false) - .map_err(FatalError)?; - - for ca_handle in cas { - info!("Re-issued MFT and CRL for CA: {ca_handle}"); - - let ca_version = 0; // we use 0 because we don't need to wait for an updated CertAuth - self.tasks - .schedule( - Task::SyncRepo { - ca_handle, - ca_version, - }, - now(), - ) - .map_err(FatalError)?; - } - - // check again in a short while.. no jitter needed as this is a cheap - // operation which is often a no-op. + // check again in a short while.. no jitter needed as this is a cheap + // operation which is often a no-op. - Ok(TaskResult::FollowUp( - Task::RepublishIfNeeded, - in_minutes(SCHEDULER_INTERVAL_REPUBLISH_MINS), - )) - } + Ok(TaskResult::FollowUp( + Task::RepublishIfNeeded, + in_minutes(SCHEDULER_INTERVAL_REPUBLISH_MINS), + )) +} - /// Update announcement info - async fn announcements_refresh(&self) -> Result { - if let Err(e) = self.bgp_analyser.update().await { +/// Update announcement info +fn announcements_refresh( + krill: &KrillRuntime +) -> Result { + let runtime = krill.clone(); + krill.spawn_async(async move { + if let Err(e) = runtime.bgp_analyser().update().await { error!("Failed to update BGP announcements: {}", e) } + }); + + // check again in 10 minutes, note.. this is a no-op in case the + // actual update was less then 1 hour ago. + // See BGP_RIS_REFRESH_MINUTES constant. + Ok(TaskResult::FollowUp( + Task::RefreshAnnouncementsInfo, in_minutes(10) + )) +} - // check again in 10 minutes, note.. this is a no-op in case the - // actual update was less then 1 hour ago. - // See BGP_RIS_REFRESH_MINUTES constant. - Ok(TaskResult::FollowUp( - Task::RefreshAnnouncementsInfo, in_minutes(10) - )) - } - - /// Let CAs that need it re-issue signed objects - async fn renew_objects_if_needed( - &self, - ) -> Result { - self.ca_manager.renew_objects_all( - &self.system_actor, &self.krill - ).map_err( - FatalError - )?; - - // check again in a short while.. note that this is usually a cheap - // no-op - Ok(TaskResult::FollowUp( - Task::RenewObjectsIfNeeded, - in_minutes(SCHEDULER_INTERVAL_RENEW_MINS), - )) - } +/// Let CAs that need it re-issue signed objects +fn renew_objects_if_needed( + krill: &KrillRuntime +) -> Result { + krill.ca_manager().renew_objects_all( + krill.system_actor(), krill + ).map_err(FatalError)?; + + // check again in a short while.. note that this is usually a cheap + // no-op + Ok(TaskResult::FollowUp( + Task::RenewObjectsIfNeeded, + in_minutes(SCHEDULER_INTERVAL_RENEW_MINS), + )) +} - // Call update_snapshots on all AggregateStores and WalStores - fn update_snapshots(&self) -> Result { - fn update_aggregate_store_snapshots( - storage_uri: &Url, - namespace: &Ident, - ) { - match AggregateStore::::create(storage_uri, namespace, false) { - Err(e) => { - // Note: this is highly unlikely.. probably something else - // is broken and Krill would - // have panicked as a result already. +// Call update_snapshots on all AggregateStores and WalStores +fn update_snapshots(krill: &KrillRuntime) -> Result { + fn update_aggregate_store_snapshots( + storage_uri: &Url, + namespace: &Ident, + ) { + match AggregateStore::::create(storage_uri, namespace, false) { + Err(e) => { + // Note: this is highly unlikely.. probably something else + // is broken and Krill would + // have panicked as a result already. + error!( + "Could not update snapshots for {namespace} will try \ + again in 24 hours. Error: {e}" + ); + } + Ok(store) => { + if let Err(e) = store.update_snapshots() { + // Note: this is highly unlikely.. probably something + // else is broken and Krill + // would have panicked as a result already. error!( - "Could not update snapshots for {namespace} will try again in 24 hours. Error: {e}" + "Could not update snapshots for {namespace} will \ + try again in 24 hours. Error: {e}" ); } - Ok(store) => { - if let Err(e) = store.update_snapshots() { - // Note: this is highly unlikely.. probably something - // else is broken and Krill - // would have panicked as a result already. - error!( - "Could not update snapshots for {namespace} will try again in 24 hours. Error: {e}" - ); - } else { - info!("Updated snapshots for {namespace}"); - } + else { + info!("Updated snapshots for {namespace}"); } } } + } - fn update_wal_store_snapshots( - storage_uri: &Url, - namespace: &Ident, - ) { - match WalStore::::create(storage_uri, namespace) { - Err(e) => { - // Note: this is highly unlikely.. probably something else - // is broken and Krill would - // have panicked as a result already. + fn update_wal_store_snapshots( + storage_uri: &Url, + namespace: &Ident, + ) { + match WalStore::::create(storage_uri, namespace) { + Err(e) => { + // Note: this is highly unlikely.. probably something else + // is broken and Krill would + // have panicked as a result already. + error!( + "Could not update snapshots for {namespace} will \ + try again in 24 hours. Error: {e}" + ); + } + Ok(store) => { + if let Err(e) = store.update_snapshots() { + // Note: this is highly unlikely.. probably something + // else is broken and Krill + // would have panicked as a result already. error!( - "Could not update snapshots for {namespace} will try again in 24 hours. Error: {e}" + "Could not update snapshots for {namespace} will \ + try again in 24 hours. Error: {e}" ); } - Ok(store) => { - if let Err(e) = store.update_snapshots() { - // Note: this is highly unlikely.. probably something - // else is broken and Krill - // would have panicked as a result already. - error!( - "Could not update snapshots for {namespace} will try again in 24 hours. Error: {e}" - ); - } - } } } + } - update_aggregate_store_snapshots::( - &self.config.storage_uri, - CASERVER_NS, - ); - update_aggregate_store_snapshots::( - &self.config.storage_uri, - SIGNERS_NS, - ); - update_aggregate_store_snapshots::( - &self.config.storage_uri, - PROPERTIES_NS, - ); - update_aggregate_store_snapshots::( - &self.config.storage_uri, - PUBSERVER_NS, - ); - - update_wal_store_snapshots::( - &self.config.storage_uri, - PUBSERVER_CONTENT_NS, - ); + update_aggregate_store_snapshots::( + &krill.config().storage_uri, CASERVER_NS, + ); + update_aggregate_store_snapshots::( + &krill.config().storage_uri, SIGNERS_NS, + ); + update_aggregate_store_snapshots::( + &krill.config().storage_uri, PROPERTIES_NS, + ); + update_aggregate_store_snapshots::( + &krill.config().storage_uri, PUBSERVER_NS, + ); + update_wal_store_snapshots::( + &krill.config().storage_uri, PUBSERVER_CONTENT_NS, + ); + + Ok(TaskResult::FollowUp(Task::UpdateSnapshots, in_hours(24))) +} - Ok(TaskResult::FollowUp(Task::UpdateSnapshots, in_hours(24))) +fn update_rrdp_if_needed( + krill: &KrillRuntime +) -> Result { + match krill.repo_manager().update_rrdp_if_needed() { + Err(e) => { + error!("Could not update RRDP deltas! Error: {e}"); + // Should we panic in this case? For now, just keep trying, + // this may be an issue that gets resolved + // (permission? disk space?) + Ok(TaskResult::Reschedule(in_hours(1))) + } + Ok(None) => { + // update was done, or there were no staged changes + Ok(TaskResult::Done) + } + Ok(Some(later_time)) => { + // Update was NOT done. There are staged changes, but the rrdp + // update interval has not yet passed. It can + // be done at later_time. + Ok(TaskResult::Reschedule(later_time.into())) + } } +} - fn update_rrdp_if_needed(&self) -> Result { - match self.repo_manager.update_rrdp_if_needed() { - Err(e) => { - error!("Could not update RRDP deltas! Error: {e}"); - // Should we panic in this case? For now, just keep trying, - // this may be an issue that gets resolved - // (permission? disk space?) - Ok(TaskResult::Reschedule(in_hours(1))) - } - Ok(None) => { - // update was done, or there were no staged changes - Ok(TaskResult::Done) - } - Ok(Some(later_time)) => { - // Update was NOT done. There are staged changes, but the rrdp - // update interval has not yet passed. It can - // be done at later_time. - Ok(TaskResult::Reschedule(later_time.into())) - } +fn resource_class_removed( + krill: &KrillRuntime, + ca_handle: CaHandle, + ca_version: u64, + parent: ParentHandle, + rcn: ResourceClassName, + revocation_requests: Vec, +) -> Result { + info!( + "Trigger send revoke requests for removed RC for '{ca_handle}' \ + under '{parent}'" + ); + + let requests = HashMap::from([(rcn, revocation_requests)]); + + if krill.ca_manager().has_ca(&ca_handle).map_err(FatalError)? { + let ca = krill.ca_manager().get_ca(&ca_handle).map_err(FatalError)?; + if ca.version() < ca_version { + // premature, we need to wait for the CA to be committed. + Ok(TaskResult::Reschedule(in_seconds(1))) + } + else if krill.ca_manager().send_revoke_requests( + &ca_handle, &parent, requests, krill, + ).is_err() { + debug!( + "Could not revoke key for resource class removed by \ + parent - most likely already revoked." + ); + Ok(TaskResult::Done) + } + else { + debug!( + "Revoked keys for CA '{ca_handle}' under parent '{parent}'" + ); + Ok(TaskResult::Done) } } + else { + debug!( + "Dropping task for removed resource class of removed \ + CA {ca_handle}" + ); + Ok(TaskResult::Done) + } +} - async fn resource_class_removed( - &self, - ca_handle: CaHandle, - ca_version: u64, - parent: ParentHandle, - rcn: ResourceClassName, - revocation_requests: Vec, - ) -> Result { +fn unexpected_key( + krill: &KrillRuntime, + ca_handle: CaHandle, + ca_version: u64, + rcn: ResourceClassName, + revocation_request: RevocationRequest, +) -> Result { + if krill.ca_manager().has_ca(&ca_handle).map_err(FatalError)? { info!( - "Trigger send revoke requests for removed RC for '{ca_handle}' under '{parent}'" + "Trigger sending revocation requests for unexpected key \ + with id '{}' in RC '{}'", + revocation_request.key(), + rcn ); + let ca = krill.ca_manager().get_ca(&ca_handle).map_err(FatalError)?; - let requests = HashMap::from([(rcn, revocation_requests)]); - - if self.ca_manager.has_ca(&ca_handle).map_err(FatalError)? { - let ca = self - .ca_manager - .get_ca(&ca_handle) - .map_err(FatalError)?; - if ca.version() < ca_version { - // premature, we need to wait for the CA to be committed. - Ok(TaskResult::Reschedule(in_seconds(1))) - } else if self - .ca_manager - .send_revoke_requests( - &ca_handle, &parent, requests, &self.krill, - ) - .await - .is_err() - { - debug!("Could not revoke key for resource class removed by parent - most likely already revoked."); - Ok(TaskResult::Done) - } else { - debug!( - "Revoked keys for CA '{ca_handle}' under parent '{parent}'" + if ca.version() < ca_version { + debug!("reschedule premature task"); + let next = in_seconds(100); + Ok(TaskResult::Reschedule(next)) + } + else { + if let Err(e) = krill.ca_manager().send_revoke_unexpected_key( + &ca_handle, rcn, revocation_request, krill + ) { + warn!( + "Could not revoke surplus key, most likely already \ + revoked by parent. Error was: {e}" ); - Ok(TaskResult::Done) } - } else { - debug!("Dropping task for removed resource class of removed CA {ca_handle}"); - Ok(TaskResult::Done) - } - } - async fn unexpected_key( - &self, - ca_handle: CaHandle, - ca_version: u64, - rcn: ResourceClassName, - revocation_request: RevocationRequest, - ) -> Result { - if self.ca_manager.has_ca(&ca_handle).map_err(FatalError)? { - info!( - "Trigger sending revocation requests for unexpected key with id '{}' in RC '{}'", - revocation_request.key(), - rcn - ); - let ca = self - .ca_manager - .get_ca(&ca_handle) - .map_err(FatalError)?; - - if ca.version() < ca_version { - debug!("reschedule premature task"); - let next = in_seconds(100); - Ok(TaskResult::Reschedule(next)) - } else { - if let Err(e) = self - .ca_manager - .send_revoke_unexpected_key( - &ca_handle, - rcn, - revocation_request, - &self.krill, - ) - .await - { - warn!( - "Could not revoke surplus key, most likely already revoked by parent. Error was: {e}" - ); - } - - Ok(TaskResult::Done) - } - } else { - debug!( - "Dropping task for surplus key for removed CA {ca_handle}" - ); Ok(TaskResult::Done) } } + else { + debug!("Dropping task for surplus key for removed CA {ca_handle}"); + Ok(TaskResult::Done) + } } + diff --git a/src/upgrades/mod.rs b/src/upgrades/mod.rs index 08c0d59f2..08c623a45 100644 --- a/src/upgrades/mod.rs +++ b/src/upgrades/mod.rs @@ -34,7 +34,7 @@ use crate::{ }, config::Config, server::{ - oldmanager::OldManager, + manager::StartupManager, properties::PropertiesManager, }, upgrades::pre_0_14_0::{ @@ -1150,13 +1150,13 @@ fn record_preexisting_openssl_keys_in_signer_mapper( /// Should be called after the KrillServer is started, but before the web /// server is started and operators can make changes. -pub async fn post_start_upgrade( +pub fn post_start_upgrade( report: UpgradeReport, - server: &OldManager, + server: &StartupManager, ) -> KrillResult<()> { if report.versions().from() < &KrillVersion::candidate(0, 9, 3, 2) { info!("Reissue ROAs on upgrade to force short EE certificate subjects in the objects"); - server.force_renew_roas().await?; + server.force_renew_roas()?; } for (ca, configs) in report.into_aspa_configs().into_iter() { @@ -1168,7 +1168,6 @@ pub async fn post_start_upgrade( server.ca_aspas_definitions_update( ca, aspa_updates, - server.system_actor(), )?; } diff --git a/tests/common.rs b/tests/common.rs index 9b054b137..b903c63a0 100644 --- a/tests/common.rs +++ b/tests/common.rs @@ -412,10 +412,8 @@ impl KrillServer { ); let (tx, running) = oneshot::channel(); let mut res = Self { - join: tokio::spawn(async { - if let Err(err) = start_krill_daemon( - config.into(), Some(tx) - ).await { + join: tokio::task::spawn_blocking(|| { + if let Err(err) = start_krill_daemon(config, Some(tx)) { error!("Krill failed to start: {err}"); } }), @@ -443,10 +441,8 @@ impl KrillServer { ); let (tx, running) = oneshot::channel(); let mut res = Self { - join: tokio::spawn(async { - if let Err(err) = start_krill_daemon( - config.into(), Some(tx) - ).await { + join: tokio::task::spawn_blocking(|| { + if let Err(err) = start_krill_daemon(config, Some(tx)) { error!("Krill failed to start: {err}"); } }), diff --git a/tests/functional_old_data.rs b/tests/functional_old_data.rs index 018ccef1c..09d705a1a 100644 --- a/tests/functional_old_data.rs +++ b/tests/functional_old_data.rs @@ -36,29 +36,32 @@ async fn functional_old_data() { ); config.ta_support_enabled = true; - eprintln!(">>>> Check whether Krill still starts."); - let server = common::KrillServer::start_with_config(config).await; - let signer_config = include_str!("../test-resources/migrations/v0_14_5_signer/ta.conf"); let signer_config = signer_config.replace("%TEMPDIR%", tempdir.path().join("ta").to_str().unwrap()); + let signer_config = krill::tasigner::Config::parse_str( + &signer_config + ).unwrap(); + + config.ta_timing = signer_config.ta_timing; + + eprintln!(">>>> Check whether Krill still starts."); + let server = common::KrillServer::start_with_config(config).await; eprintln!(">>>> Configure the TA signer."); - let signer = TrustAnchorSignerManager::create( - krill::tasigner::Config::parse_str( - &signer_config - ).unwrap() - ).unwrap(); + let signer = TrustAnchorSignerManager::create(signer_config).unwrap(); eprintln!(">>>> Make TA proxy signer request."); let request = server.client().ta_proxy_signer_make_request().await.unwrap(); assert_eq!(request.ta_renew_time.unwrap().year(), 2026); assert_eq!(request.renew_times[0].1.year(), 2039); + eprintln!("{request}"); eprintln!(">>>> Sign TA proxy signer request."); let response = signer.process(request.into(), None).unwrap(); + eprintln!("{response}"); assert_eq!(response.content().child_responses.len(), 1); eprintln!(">>>> Process TA proxy signer response."); diff --git a/tests/suspend.rs b/tests/suspend.rs index f1692113f..40e323cc7 100644 --- a/tests/suspend.rs +++ b/tests/suspend.rs @@ -42,7 +42,12 @@ async fn test_suspension() { server.expect_not_suspended(&testbed, &ca).await; eprintln!(">>>> Wait a bit."); - common::sleep_seconds(15).await; + common::sleep_seconds(5).await; + + let publisher_details = server.client() + .publisher_details(&testbed.convert()).await.unwrap(); + let old_cert = publisher_details.current_files.iter() + .find(|x| x.uri.ends_with(".cer")).unwrap(); eprintln!(">>>> Refresh testbed only, check that CA is suspended."); // This happens because CA isn’t updating. @@ -52,7 +57,6 @@ async fn test_suspension() { eprintln!(">>>> Let CA refresh with testbed, this should un-suspend it."); server.client().ca_sync_parents(&ca).await.unwrap(); - server.client().bulk_suspend().await.unwrap(); server.expect_not_suspended(&testbed, &ca).await; eprintln!(">>>> Explicitly suspend CA."); @@ -68,6 +72,19 @@ async fn test_suspension() { UpdateChildRequest::unsuspend() ).await.unwrap(); server.expect_not_suspended(&testbed, &ca).await; + + server.client().bulk_sync_parents().await.unwrap(); + + eprintln!(">>>> Wait a bit."); + common::sleep_seconds(5).await; + + let publisher_details = server.client() + .publisher_details(&testbed.convert()).await.unwrap(); + let new_cert = publisher_details.current_files.iter() + .find(|x| x.uri == old_cert.uri).unwrap(); + + eprintln!(">>>> Check that new CA serial differs from old CA serial."); + assert_ne!(old_cert.base64, new_cert.base64); } From d9c48a0f9b57a1c7e495b8db6a15295198f6868a Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Mon, 23 Feb 2026 14:49:26 +0100 Subject: [PATCH 10/51] Properly shut down threads and things. --- Cargo.lock | 736 +++++++++++++++--------- Cargo.toml | 2 +- src/api/status.rs | 6 + src/bin/krill.rs | 2 +- src/commons/httpclient.rs | 6 +- src/config.rs | 3 + src/daemon/http/server.rs | 18 +- src/daemon/start.rs | 293 +++++++--- src/server/bgp/analyser.rs | 7 +- src/server/bgp/riswhois.rs | 43 +- src/server/ca/manager.rs | 23 +- src/server/manager.rs | 45 +- src/server/runtime.rs | 262 +++++++-- src/server/scheduler.rs | 33 +- tests/auth_check.rs | 13 +- tests/benchmark.rs | 2 +- tests/client_coverage.rs | 8 +- tests/common.rs | 106 ++-- tests/functional_aspa.rs | 2 +- tests/functional_bgpsec.rs | 2 +- tests/functional_delegated_ca_import.rs | 5 +- tests/functional_keyroll.rs | 2 +- tests/functional_old_data.rs | 6 +- tests/functional_parent_child.rs | 6 +- tests/functional_roas.rs | 2 +- tests/functional_ta.rs | 6 +- tests/migrate_repository.rs | 4 +- tests/remote_parent_and_repo.rs | 5 +- tests/suspend.rs | 6 +- tests/testbed.rs | 6 +- 30 files changed, 1139 insertions(+), 521 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ad8d5bdda..614cf2049 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -82,11 +82,20 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + [[package]] name = "arc-swap" -version = "1.7.1" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" +checksum = "f9f3647c145568cec02c42054e07bdf9a5a698e15b466fb2341bfc393cd24aa5" +dependencies = [ + "rustversion", +] [[package]] name = "ascii-canvas" @@ -115,7 +124,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b62ddb9cb1ec0a098ad4bbf9344d0713fa193ae1a80af55febcff2627b6a00c1" dependencies = [ - "getrandom 0.2.16", + "getrandom 0.2.17", "instant", "rand 0.8.5", ] @@ -140,9 +149,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" -version = "1.8.0" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "basic-cookies" @@ -188,9 +197,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" [[package]] name = "block-buffer" @@ -203,21 +212,21 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.19.0" +version = "3.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" [[package]] name = "bytes" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" [[package]] name = "cc" -version = "1.2.46" +version = "1.2.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97463e1064cb1b1c1384ad0a0b9c8abd0988e2a91f52606c80ef14aadb63e36" +checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" dependencies = [ "find-msvc-tools", "shlex", @@ -237,16 +246,16 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chrono" -version = "0.4.42" +version = "0.4.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" +checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" dependencies = [ "iana-time-zone", "js-sys", "num-traits", "serde", "wasm-bindgen", - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -261,9 +270,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.52" +version = "4.5.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa8120877db0e5c011242f96806ce3c94e0737ab8108532a76a3300a01db2ab8" +checksum = "2797f34da339ce31042b27d23607e051786132987f595b02ba4f6a6dffb7030a" dependencies = [ "clap_builder", "clap_derive", @@ -271,9 +280,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.52" +version = "4.5.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02576b399397b659c26064fbc92a75fede9d18ffd5f80ca1cd74ddab167016e1" +checksum = "24a241312cea5059b13574bb9b3861cabf758b879c15190b37b6d6fd63ab6876" dependencies = [ "anstream", "anstyle", @@ -284,21 +293,21 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.49" +version = "4.5.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" +checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] name = "clap_lex" -version = "0.7.6" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" +checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831" [[package]] name = "colorchoice" @@ -322,6 +331,16 @@ dependencies = [ "libc", ] +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -430,7 +449,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] @@ -454,7 +473,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] @@ -465,7 +484,7 @@ checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" dependencies = [ "darling_core", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] @@ -487,9 +506,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.5.5" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" dependencies = [ "powerfmt", "serde_core", @@ -536,7 +555,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] @@ -612,9 +631,9 @@ dependencies = [ [[package]] name = "ena" -version = "0.14.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d248bdd43ce613d87415282f69b9bb99d947d290b10962dd6c56233312c2ad5" +checksum = "eabffdaee24bd1bf95c5ef7cec31260444317e72ea56c4c91750e8b7ee58d5f1" dependencies = [ "log", ] @@ -711,21 +730,20 @@ checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" [[package]] name = "filetime" -version = "0.2.26" +version = "0.2.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc0505cd1b6fa6580283f6bdf70a73fcf4aba1184038c90902b92b3dd0df63ed" +checksum = "f98844151eee8917efc50bd9e8318cb963ae8b297431495d3f758616ea5c57db" dependencies = [ "cfg-if", "libc", "libredox", - "windows-sys 0.60.2", ] [[package]] name = "find-msvc-tools" -version = "0.1.5" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" [[package]] name = "fixedbitset" @@ -739,6 +757,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "foldhash" version = "0.2.0" @@ -771,53 +795,52 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" dependencies = [ "futures-core", ] [[package]] name = "futures-core" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" [[package]] name = "futures-macro" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] name = "futures-sink" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" [[package]] name = "futures-task" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" [[package]] name = "futures-util" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ "futures-core", "futures-macro", "futures-task", "pin-project-lite", - "pin-utils", "slab", ] @@ -834,9 +857,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ "cfg-if", "js-sys", @@ -857,6 +880,19 @@ dependencies = [ "wasip2", ] +[[package]] +name = "getrandom" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", + "wasip3", +] + [[package]] name = "group" version = "0.13.0" @@ -870,9 +906,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" +checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" dependencies = [ "atomic-waker", "bytes", @@ -880,7 +916,7 @@ dependencies = [ "futures-core", "futures-sink", "http", - "indexmap 2.12.0", + "indexmap 2.13.0", "slab", "tokio", "tokio-util", @@ -895,13 +931,22 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.16.0" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash 0.1.5", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" dependencies = [ "allocator-api2", "equivalent", - "foldhash", + "foldhash 0.2.0", ] [[package]] @@ -942,23 +987,22 @@ dependencies = [ [[package]] name = "hostname" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56f203cd1c76362b69e3863fd987520ac36cf70a8c92627449b2f64a8cf7d65" +checksum = "617aaa3557aef3810a6369d0a99fac8a080891b68bd9f9812a1eeda0c0730cbd" dependencies = [ "cfg-if", "libc", - "windows-link 0.1.3", + "windows-link", ] [[package]] name = "http" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" dependencies = [ "bytes", - "fnv", "itoa", ] @@ -1054,14 +1098,13 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.18" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52e9a2a24dc5c6821e71a7030e1e14b7b632acac55c40e9d2e082c621261bb56" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" dependencies = [ "base64 0.22.1", "bytes", "futures-channel", - "futures-core", "futures-util", "http", "http-body", @@ -1080,9 +1123,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.64" +version = "0.1.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -1150,9 +1193,9 @@ checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" [[package]] name = "icu_properties" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e93fcd3157766c0c8da2f8cff6ce651a31f0810eaa1c51ec363ef790bbb5fb99" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" dependencies = [ "icu_collections", "icu_locale_core", @@ -1164,9 +1207,9 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02845b3647bb045f1100ecd6480ff52f34c35f82d9880e029d329c21d1054899" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" [[package]] name = "icu_provider" @@ -1183,6 +1226,12 @@ dependencies = [ "zerovec", ] +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + [[package]] name = "ident_case" version = "1.0.1" @@ -1223,12 +1272,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.12.0" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ "equivalent", - "hashbrown 0.16.0", + "hashbrown 0.16.1", "serde", "serde_core", ] @@ -1268,9 +1317,9 @@ checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" [[package]] name = "iri-string" -version = "0.7.9" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f867b9d1d896b67beb18518eda36fdb77a32ea590de864f1325b294a6d14397" +checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" dependencies = [ "memchr", "serde", @@ -1313,15 +1362,15 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.15" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" [[package]] name = "js-sys" -version = "0.3.82" +version = "0.3.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65" +checksum = "c7e709f3e3d22866f9c25b3aff01af289b18422cc8b4262fb19103ee80fe513d" dependencies = [ "once_cell", "wasm-bindgen", @@ -1454,11 +1503,17 @@ dependencies = [ "spin", ] +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + [[package]] name = "libc" -version = "0.2.177" +version = "0.2.182" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" +checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" [[package]] name = "libflate" @@ -1480,7 +1535,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a599cb10a9cd92b1300debcef28da8f70b935ec937f44fcd1b70a7c986a11c5c" dependencies = [ "core2", - "hashbrown 0.16.0", + "hashbrown 0.16.1", "rle-decode-fast", ] @@ -1491,31 +1546,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" dependencies = [ "cfg-if", - "windows-link 0.2.1", + "windows-link", ] [[package]] name = "libm" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libredox" -version = "0.1.10" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" +checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "libc", - "redox_syscall", + "redox_syscall 0.7.1", ] [[package]] name = "linux-raw-sys" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "litemap" @@ -1534,9 +1589,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.28" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "maybe-async" @@ -1546,14 +1601,14 @@ checksum = "5cf92c10c7e361d6b99666ec1c6f9805b0bea2c3bd8c78dc6fe98ac5bd78db11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] name = "memchr" -version = "2.7.6" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "mime" @@ -1563,9 +1618,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "mio" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" dependencies = [ "libc", "wasi", @@ -1574,9 +1629,9 @@ dependencies = [ [[package]] name = "native-tls" -version = "0.2.14" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" +checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2" dependencies = [ "libc", "log", @@ -1601,7 +1656,7 @@ version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "cfg-if", "cfg_aliases", "libc", @@ -1676,7 +1731,7 @@ checksum = "51e219e79014df21a225b1860a479e2dcd7cbd9130f4defd4bd0e191ea31d67d" dependencies = [ "base64 0.22.1", "chrono", - "getrandom 0.2.16", + "getrandom 0.2.17", "http", "rand 0.8.5", "serde", @@ -1736,7 +1791,7 @@ version = "0.10.75" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "cfg-if", "foreign-types", "libc", @@ -1753,14 +1808,14 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] name = "openssl-probe" -version = "0.1.6" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" [[package]] name = "openssl-sys" @@ -1825,9 +1880,9 @@ checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.5.18", "smallvec", - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -1868,7 +1923,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap 2.12.0", + "indexmap 2.13.0", ] [[package]] @@ -1955,6 +2010,16 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.117", +] + [[package]] name = "primeorder" version = "0.13.6" @@ -1966,9 +2031,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.103" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] @@ -1984,9 +2049,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.42" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" dependencies = [ "proc-macro2", ] @@ -2026,7 +2091,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha 0.9.0", - "rand_core 0.9.3", + "rand_core 0.9.5", ] [[package]] @@ -2046,7 +2111,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core 0.9.3", + "rand_core 0.9.5", ] [[package]] @@ -2055,14 +2120,14 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.16", + "getrandom 0.2.17", ] [[package]] name = "rand_core" -version = "0.9.3" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" dependencies = [ "getrandom 0.3.4", ] @@ -2073,7 +2138,16 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", +] + +[[package]] +name = "redox_syscall" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35985aa610addc02e24fc232012c86fd11f14111180f902b67e2d5331f8ebf2b" +dependencies = [ + "bitflags 2.11.0", ] [[package]] @@ -2082,7 +2156,7 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ - "getrandom 0.2.16", + "getrandom 0.2.17", "libredox", "thiserror", ] @@ -2104,14 +2178,14 @@ checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] name = "regex" -version = "1.12.2" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" dependencies = [ "aho-corasick", "memchr", @@ -2121,9 +2195,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" dependencies = [ "aho-corasick", "memchr", @@ -2132,15 +2206,15 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" +checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c" [[package]] name = "reqwest" -version = "0.12.24" +version = "0.12.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" dependencies = [ "base64 0.22.1", "bytes", @@ -2194,7 +2268,7 @@ checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.16", + "getrandom 0.2.17", "libc", "untrusted", "windows-sys 0.52.0", @@ -2237,9 +2311,9 @@ dependencies = [ [[package]] name = "rsa" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40a0376c50d0358279d9d643e4bf7b7be212f1f4ff1da9070a7b54d22ef75c88" +checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d" dependencies = [ "const-oid", "digest", @@ -2276,11 +2350,11 @@ dependencies = [ [[package]] name = "rustix" -version = "1.1.2" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "errno", "libc", "linux-raw-sys", @@ -2289,9 +2363,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.35" +version = "0.23.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" +checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b" dependencies = [ "log", "once_cell", @@ -2313,18 +2387,18 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.13.0" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94182ad936a0c91c324cd46c6511b9510ed16af436d7b5bab34beab0afd55f7a" +checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" dependencies = [ "zeroize", ] [[package]] name = "rustls-webpki" -version = "0.103.8" +version = "0.103.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" +checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" dependencies = [ "ring", "rustls-pki-types", @@ -2339,9 +2413,9 @@ checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "ryu" -version = "1.0.20" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" [[package]] name = "salsa20" @@ -2393,9 +2467,9 @@ dependencies = [ [[package]] name = "schemars" -version = "1.1.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9558e172d4e8533736ba97870c4b2cd63f84b382a3d6eb063da41b91cce17289" +checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" dependencies = [ "dyn-clone", "ref-cast", @@ -2446,12 +2520,12 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.11.1" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ - "bitflags 2.10.0", - "core-foundation", + "bitflags 2.11.0", + "core-foundation 0.10.1", "core-foundation-sys", "libc", "security-framework-sys", @@ -2459,9 +2533,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.15.0" +version = "2.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" dependencies = [ "core-foundation-sys", "libc", @@ -2520,20 +2594,20 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] name = "serde_json" -version = "1.0.145" +version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ "itoa", "memchr", - "ryu", "serde", "serde_core", + "zmij", ] [[package]] @@ -2558,9 +2632,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e24345aa0fe688594e73770a5f6d1b216508b4f93484c0026d521acd30134392" +checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776" dependencies = [ "serde_core", ] @@ -2579,17 +2653,17 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.16.0" +version = "3.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10574371d41b0d9b2cff89418eda27da52bcaff2cc8741db26382a77c29131f1" +checksum = "4fa237f2807440d238e0364a218270b98f767a00d3dada77b1c53ae88940e2e7" dependencies = [ "base64 0.22.1", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.12.0", + "indexmap 2.13.0", "schemars 0.9.0", - "schemars 1.1.0", + "schemars 1.2.1", "serde_core", "serde_json", "serde_with_macros", @@ -2598,14 +2672,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.16.0" +version = "3.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08a72d8216842fdd57820dc78d840bef99248e35fb2554ff923319e60f2d686b" +checksum = "52a8e3ca0ca629121f70ab50f95249e5a6f925cc0f6ffe8256c45b728875706c" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] @@ -2627,10 +2701,11 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.6" +version = "1.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" dependencies = [ + "errno", "libc", ] @@ -2646,15 +2721,15 @@ dependencies = [ [[package]] name = "siphasher" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" +checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" [[package]] name = "slab" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "smallvec" @@ -2664,9 +2739,9 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "socket2" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" +checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0" dependencies = [ "libc", "windows-sys 0.60.2", @@ -2744,9 +2819,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.110" +version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", @@ -2770,7 +2845,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] @@ -2787,12 +2862,12 @@ dependencies = [ [[package]] name = "system-configuration" -version = "0.6.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" dependencies = [ - "bitflags 2.10.0", - "core-foundation", + "bitflags 2.11.0", + "core-foundation 0.9.4", "system-configuration-sys", ] @@ -2819,12 +2894,12 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.23.0" +version = "3.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" +checksum = "0136791f7c95b1f6dd99f9cc786b91bb81c3800b639b3478e561ddb7be95e5f1" dependencies = [ "fastrand", - "getrandom 0.3.4", + "getrandom 0.4.1", "once_cell", "rustix", "windows-sys 0.61.2", @@ -2877,7 +2952,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] @@ -2891,9 +2966,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.44" +version = "0.3.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" +checksum = "f9e442fc33d7fdb45aa9bfeb312c095964abdf596f7567261062b2a7107aaabd" dependencies = [ "deranged", "itoa", @@ -2901,22 +2976,22 @@ dependencies = [ "num-conv", "num_threads", "powerfmt", - "serde", + "serde_core", "time-core", "time-macros", ] [[package]] name = "time-core" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" +checksum = "8b36ee98fd31ec7426d599183e8fe26932a8dc1fb76ddb6214d05493377d34ca" [[package]] name = "time-macros" -version = "0.2.24" +version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" +checksum = "71e552d1249bf61ac2a52db88179fd0673def1e1ad8243a00d9ec9ed71fee3dd" dependencies = [ "num-conv", "time-core", @@ -2958,9 +3033,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.48.0" +version = "1.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" +checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" dependencies = [ "bytes", "libc", @@ -2980,7 +3055,7 @@ checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] @@ -3005,9 +3080,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.17" +version = "0.7.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" dependencies = [ "bytes", "futures-core", @@ -3018,11 +3093,11 @@ dependencies = [ [[package]] name = "toml" -version = "0.9.8" +version = "0.9.12+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0dc8b1fb61449e27716ec0e1bdf0f6b8f3e8f6b05391e8497b8b6d7804ea6d8" +checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" dependencies = [ - "indexmap 2.12.0", + "indexmap 2.13.0", "serde_core", "serde_spanned", "toml_datetime", @@ -3033,33 +3108,33 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.7.3" +version = "0.7.5+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" dependencies = [ "serde_core", ] [[package]] name = "toml_parser" -version = "1.0.4" +version = "1.0.9+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" +checksum = "702d4415e08923e7e1ef96cd5727c0dfed80b4d2fa25db9647fe5eb6f7c5a4c4" dependencies = [ "winnow", ] [[package]] name = "toml_writer" -version = "1.0.4" +version = "1.0.6+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df8b2b54733674ad286d16267dcfc7a71ed5c776e4ac7aa3c3e2561f7c637bf2" +checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607" [[package]] name = "tower" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" dependencies = [ "futures-core", "futures-util", @@ -3072,11 +3147,11 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.6" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "bytes", "futures-util", "http", @@ -3102,9 +3177,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.41" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ "pin-project-lite", "tracing-core", @@ -3112,9 +3187,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.34" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" dependencies = [ "once_cell", ] @@ -3144,9 +3219,9 @@ checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" [[package]] name = "unicode-ident" -version = "1.0.22" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] name = "unicode-normalization" @@ -3171,14 +3246,15 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.7" +version = "2.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" dependencies = [ "form_urlencoded", "idna", "percent-encoding", "serde", + "serde_derive", ] [[package]] @@ -3201,13 +3277,13 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.18.1" +version = "1.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" +checksum = "b672338555252d43fd2240c714dc444b8c6fb0a5c5335e65a07bba7742735ddb" dependencies = [ - "getrandom 0.3.4", + "getrandom 0.4.1", "js-sys", - "serde", + "serde_core", "wasm-bindgen", ] @@ -3254,14 +3330,23 @@ version = "1.0.1+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" dependencies = [ - "wit-bindgen", + "wit-bindgen 0.46.0", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen 0.51.0", ] [[package]] name = "wasm-bindgen" -version = "0.2.105" +version = "0.2.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60" +checksum = "ec1adf1535672f5b7824f817792b1afd731d7e843d2d04ec8f27e8cb51edd8ac" dependencies = [ "cfg-if", "once_cell", @@ -3272,11 +3357,12 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.55" +version = "0.4.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "551f88106c6d5e7ccc7cd9a16f312dd3b5d36ea8b4954304657d5dfba115d4a0" +checksum = "fe88540d1c934c4ec8e6db0afa536876c5441289d7f9f9123d4f065ac1250a6b" dependencies = [ "cfg-if", + "futures-util", "js-sys", "once_cell", "wasm-bindgen", @@ -3285,9 +3371,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.105" +version = "0.2.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2" +checksum = "19e638317c08b21663aed4d2b9a2091450548954695ff4efa75bff5fa546b3b1" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3295,31 +3381,65 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.105" +version = "0.2.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc" +checksum = "2c64760850114d03d5f65457e96fc988f11f01d38fbaa51b254e4ab5809102af" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.105" +version = "0.2.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76" +checksum = "60eecd4fe26177cfa3339eb00b4a36445889ba3ad37080c2429879718e20ca41" dependencies = [ "unicode-ident", ] +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap 2.13.0", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags 2.11.0", + "hashbrown 0.15.5", + "indexmap 2.13.0", + "semver", +] + [[package]] name = "web-sys" -version = "0.3.82" +version = "0.3.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a1f95c0d03a47f4ae1f7a64643a6bb97465d9b740f0fa8f90ea33915c99a9a1" +checksum = "9d6bb20ed2d9572df8584f6dc81d68a41a625cadc6f15999d649a70ce7e3597a" dependencies = [ "js-sys", "wasm-bindgen", @@ -3364,7 +3484,7 @@ checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ "windows-implement", "windows-interface", - "windows-link 0.2.1", + "windows-link", "windows-result", "windows-strings", ] @@ -3377,7 +3497,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] @@ -3388,15 +3508,9 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] -[[package]] -name = "windows-link" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" - [[package]] name = "windows-link" version = "0.2.1" @@ -3409,7 +3523,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" dependencies = [ - "windows-link 0.2.1", + "windows-link", "windows-result", "windows-strings", ] @@ -3420,7 +3534,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" dependencies = [ - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -3429,7 +3543,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" dependencies = [ - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -3465,7 +3579,7 @@ version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -3490,7 +3604,7 @@ version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ - "windows-link 0.2.1", + "windows-link", "windows_aarch64_gnullvm 0.53.1", "windows_aarch64_msvc 0.53.1", "windows_i686_gnu 0.53.1", @@ -3599,9 +3713,9 @@ checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] name = "winnow" -version = "0.7.13" +version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" [[package]] name = "wit-bindgen" @@ -3609,6 +3723,94 @@ version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap 2.13.0", + "prettyplease", + "syn 2.0.117", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn 2.0.117", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags 2.11.0", + "indexmap 2.13.0", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap 2.13.0", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + [[package]] name = "writeable" version = "0.6.2" @@ -3644,28 +3846,28 @@ checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", "synstructure", ] [[package]] name = "zerocopy" -version = "0.8.27" +version = "0.8.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" +checksum = "db6d35d663eadb6c932438e763b262fe1a70987f9ae936e60158176d710cae4a" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.27" +version = "0.8.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" +checksum = "4122cd3169e94605190e77839c9a40d40ed048d305bfdc146e7df40ab0f3e517" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] @@ -3685,7 +3887,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", "synstructure", ] @@ -3725,5 +3927,11 @@ checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/Cargo.toml b/Cargo.toml index 02164a95b..21364495c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,7 @@ futures-util = "0.3" hex = "0.4" http-body-util = "0.1" hyper = { version = "1.6.0", features = ["server"] } -hyper-util = { version = "0.1", features = [ "server" ] } +hyper-util = { version = "0.1", features = [ "server", "server-auto", "server-graceful" ] } intervaltree = "0.2.7" lazy_static = "1.5" libflate = "2.1.0" diff --git a/src/api/status.rs b/src/api/status.rs index 5b660acae..d3a63bf10 100644 --- a/src/api/status.rs +++ b/src/api/status.rs @@ -143,6 +143,12 @@ impl ErrorResponse { } } +impl From<(&'static str, &'static str)> for ErrorResponse { + fn from((label, msg): (&'static str, &'static str)) -> Self { + Self::new(label, msg) + } +} + impl fmt::Display for ErrorResponse { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", &serde_json::to_string(&self).unwrap()) diff --git a/src/bin/krill.rs b/src/bin/krill.rs index 43302cdbf..20acf6b03 100644 --- a/src/bin/krill.rs +++ b/src/bin/krill.rs @@ -16,7 +16,7 @@ fn main() { match Config::create(&args.config, false) { Ok(config) => { - if let Err(e) = start_krill_daemon( config, None) { + if let Err(e) = start_krill_daemon( config, None, None) { error!("Krill failed to start: {e}"); ::std::process::exit(1); } diff --git a/src/commons/httpclient.rs b/src/commons/httpclient.rs index 842a8cb92..5ab2a4f56 100644 --- a/src/commons/httpclient.rs +++ b/src/commons/httpclient.rs @@ -7,6 +7,7 @@ use reqwest::{ header::{HeaderMap, HeaderValue, CONTENT_TYPE, USER_AGENT}, Response, StatusCode, }; +use rpki::ca::idexchange::ServiceUri; use serde::de::DeserializeOwned; use serde::ser::Serialize; @@ -261,11 +262,12 @@ async fn do_empty_post( /// Note: Bytes may be empty if the post was successful, but the response was /// empty. pub async fn post_binary_with_full_ua( - uri: &str, - data: &Bytes, + uri: ServiceUri, + data: Bytes, content_type: &str, timeout: u64, ) -> Result { + let uri = uri.as_str(); let body = data.to_vec(); let mut headers = HeaderMap::new(); diff --git a/src/config.rs b/src/config.rs index e0b638b0e..100637239 100644 --- a/src/config.rs +++ b/src/config.rs @@ -574,6 +574,8 @@ pub struct Config { #[serde(default = "ConfigDefaults::syslog_facility")] pub syslog_facility: String, + pub num_threads: Option, + #[serde(default = "ConfigDefaults::admin_token", alias = "auth_token")] pub admin_token: Token, @@ -1304,6 +1306,7 @@ impl Config { log_type, log_file: None, syslog_facility, + num_threads: None, admin_token, auth_type, #[cfg(feature = "multi-user")] diff --git a/src/daemon/http/server.rs b/src/daemon/http/server.rs index ca7d4b85f..40ca0eb4f 100644 --- a/src/daemon/http/server.rs +++ b/src/daemon/http/server.rs @@ -1,5 +1,5 @@ use std::env; -use std::sync::Arc; +use std::sync::{Arc, Weak}; use clap::crate_version; use hyper::StatusCode; use log::{error, info, warn, trace}; @@ -49,15 +49,23 @@ impl HttpServer { /// Processes an HTTP request. pub async fn process_request( - &self, request: HyperRequest + this: Weak, request: HyperRequest ) -> Result { + // If we can’t upgrade the weak this, return a 503. + let Some(this) = this.upgrade() else { + return Ok(HttpResponse::error( + StatusCode::SERVICE_UNAVAILABLE, + ("sys-unavailable", "Service Unavailable"), + ).into_hyper()) + }; + let logger = RequestLogger::begin(&request); - let (auth, new_token) = self.authorizer.authenticate_request( + let (auth, new_token) = this.authorizer.authenticate_request( &request ).await; let request = Request::new( - request, self, auth, - BodyLimits::from_config(self.krill.config()) + request, &this, auth, + BodyLimits::from_config(this.krill.config()) ); let path = match request.path() { Ok(path) => path, diff --git a/src/daemon/start.rs b/src/daemon/start.rs index 948921900..1463bd974 100644 --- a/src/daemon/start.rs +++ b/src/daemon/start.rs @@ -2,12 +2,15 @@ use std::{env, process}; use std::net::SocketAddr; use std::path::Path; use std::sync::Arc; +use std::time::Duration; use clap::crate_version; -use log::{error, info}; -use hyper::service::service_fn; +use log::{error, info, warn}; use hyper_util::rt::{TokioExecutor, TokioIo}; +use hyper_util::server::conn; +use hyper_util::server::graceful::GracefulShutdown; use tokio::net::TcpListener; -use tokio::sync::oneshot; +use tokio::sync::{oneshot, watch}; +use tokio::task::JoinSet; use tokio_rustls::TlsAcceptor; use crate::commons::file; use crate::commons::error::{Error, Error as KrillError}; @@ -27,6 +30,7 @@ use super::http::server::HttpServer; pub fn start_krill_daemon( config: Config, mut signal_running: Option>, + signal_exit: Option>, ) -> Result<(), Error> { info!("Starting {} v{}", KRILL_SERVER_APP, crate_version!()); @@ -89,7 +93,7 @@ pub fn start_krill_daemon( ) })?; - let krill = StartupManager::new(config, tokio.handle().clone())?; + let mut krill = StartupManager::new(config, tokio.handle().clone())?; // Setup testbed if necessary. krill.prepare_testbed()?; @@ -109,7 +113,7 @@ pub fn start_krill_daemon( } krill.run_scheduler()?; - let krill = krill.promote(); + let (krill, pool) = krill.promote()?; // Create the HTTP server. let server = HttpServer::new(krill, &tokio.handle())?; @@ -120,32 +124,54 @@ pub fn start_krill_daemon( .map_err(|e| Error::HttpsSetup(format!("{e}")))?; } - // Start a hyper server for the configured http sockets. + let (exit_tx, exit_rx) = watch::channel(false); + let mut join = JoinSet::new(); + // Start a hyper server for the configured http sockets. for socket_addr in server.config().socket_addresses().into_iter() { - tokio.spawn(single_http_listener( - server.clone(), - socket_addr, - signal_running.take(), - )); + join.spawn_on( + single_http_listener( + server.clone(), + socket_addr, + signal_running.take(), + exit_rx.clone(), + ), + tokio.handle(), + ); } // Start a hyper server for the configured unix sockets. - // We do not await these, as they are not required #[cfg(unix)] if server.config().unix_socket_enabled() { if let Some(path) = server.config().unix_socket() { - tokio.spawn(single_unix_listener( - server.clone(), - path.clone(), - signal_running.take(), - )); + join.spawn_on( + single_unix_listener( + server.clone(), + path.clone(), + signal_running.take(), + exit_rx.clone(), + ), + tokio.handle(), + ); } } - tokio.block_on(futures_util::future::pending::<()>()); + tokio.block_on(async { + if let Some(exit) = signal_exit { + let _ = exit.await; + } + else { + // TODO also catch SIGTERM here. + let _ = tokio::signal::ctrl_c().await; + } + let _ = exit_tx.send(true); + let _ = join.join_all().await; + }); + + drop(server); + pool.terminate(); - Err(Error::custom("stopping krill process")) + Ok(()) } /// Runs an HTTP listener on a single socket. @@ -153,14 +179,9 @@ async fn single_http_listener( server: Arc, addr: SocketAddr, signal_running: Option>, + mut signal_exit: watch::Receiver, ) { - let listener = match TcpListener::bind(addr).await { - Ok(listener) => listener, - Err(err) => { - error!("Could not bind to {addr}: {err}"); - return; - } - }; + let listener = TcpListener::bind(addr).await.unwrap(); let tls = if server.config().https_mode().is_disable_https() { None @@ -177,34 +198,75 @@ async fn single_http_listener( } }; + let conn_builder = conn::auto::Builder::new(TokioExecutor::new()); + let graceful = GracefulShutdown::new(); + + let weak_server = Arc::downgrade(&server); + drop(server); + if let Some(tx) = signal_running { let _ = tx.send(()); } loop { - let stream = match listener.accept().await { - Ok((stream, _addr)) => { - tls::MaybeTlsTcpStream::new(stream, tls.as_ref()) - } - Err(err) => { - error!("Fatal error in HTTP server {addr}: {err}"); - return; + // Break here already if `signal_exit` is true. + if *signal_exit.borrow_and_update() { + drop(listener); + break; + } + + tokio::select! { + conn = listener.accept() => { + let (stream, _addr) = match conn { + Ok(conn) => conn, + Err(e) => { + warn!("TCP socket accept error: {}", e); + tokio::time::sleep( + Duration::from_millis(100) + ).await; + continue; + } + }; + + let stream = TokioIo::new( + tls::MaybeTlsTcpStream::new( + stream, tls.as_ref() + ) + ); + + let server = weak_server.clone(); + let conn = conn_builder.serve_connection_with_upgrades( + stream, + hyper::service::service_fn(move |req| { + HttpServer::process_request(server.clone(), req) + }) + ); + let conn = graceful.watch(conn.into_owned()); + + tokio::spawn(async move { + if let Err(err) = conn.await { + warn!("TCP connection error: {}", err); + } + }); + }, + + res = signal_exit.changed() => { + // Break if the channel is closed or the new value is `true`. + if res.is_err() || *signal_exit.borrow() { + drop(listener); + break; + } } - }; - let server = server.clone(); - tokio::task::spawn(async move { - let _ = hyper_util::server::conn::auto::Builder::new( - TokioExecutor::new(), - ) - .serve_connection( - TokioIo::new(stream), - service_fn(move |req| { - let server = server.clone(); - async move { server.process_request(req).await } - }), - ) - .await; - }); + } + } + + tokio::select! { + _ = graceful.shutdown() => { }, + _ = tokio::time::sleep(Duration::from_secs(10)) => { + warn!( + "Waited 10 seconds for TCP listener to shutdown, aborting..." + ); + } } } @@ -214,12 +276,14 @@ async fn single_unix_listener( server: Arc, path: std::path::PathBuf, signal_running: Option>, + mut signal_exit: watch::Receiver, ) { + use nix::unistd::{Uid, User}; use tokio::net::UnixListener; if path.exists() { if let Err(err) = std::fs::remove_file(&path) { - error!("Fatal error in UNIX socket: {err}"); + error!("Failed to remove existing Unix socket file: {err}"); return; }; } @@ -227,62 +291,107 @@ async fn single_unix_listener( let listener = match UnixListener::bind(&path) { Ok(listener) => listener, Err(err) => { - error!("Could not bind to {}: {}", &path.to_string_lossy(), err); + error!( + "Could not bind to Unix socket '{}': {}", + &path.to_string_lossy(), err + ); return; } }; + let conn_builder = conn::auto::Builder::new(TokioExecutor::new()); + let graceful = GracefulShutdown::new(); + + let weak_server = Arc::downgrade(&server); + drop(server); + if let Some(tx) = signal_running { let _ = tx.send(()); } loop { - use tokio::net::unix; + // Break here already if `signal_exit` is true. + if *signal_exit.borrow_and_update() { + drop(listener); + break; + } - let (stream, _addr) = match listener.accept().await { - Ok(stream) => stream, - Err(err) => { - error!("Fatal UNIX socket error: {err}"); - continue; - } - }; - let uid: unix::uid_t = match stream.peer_cred() { - Ok(cred) => cred.uid(), - Err(err) => { - error!("Could not obtain peer credentials: {err}"); - continue; - } - }; - let user: nix::unistd::User = match nix::unistd::User::from_uid( - nix::unistd::Uid::from_raw(uid) - ) { - Ok(Some(user)) => user, - Err(err) => { - error!("Could not obtain user details for UNIX socket: {err}"); - continue; + tokio::select! { + conn = listener.accept() => { + let (stream, _addr) = match conn { + Ok(stream) => stream, + Err(err) => { + warn!("Unix socket accept error: {}", err); + tokio::time::sleep( + Duration::from_millis(100) + ).await; + continue; + } + }; + + + let uid = match stream.peer_cred() { + Ok(cred) => Uid::from_raw(cred.uid()), + Err(err) => { + warn!( + "Unix socket could not obtain peer credentials: \ + {err}" + ); + continue; + } + }; + let user = match User::from_uid(uid) { + Ok(Some(user)) => user, + Ok(None) => { + error!( + "Unix socket could not obtain user details: \ + unknown user ID." + ); + continue; + } + Err(err) => { + error!( + "Unix socket could not obtain user details: {err}" + ); + continue; + }, + }; + + let server = weak_server.clone(); + let conn = conn_builder.serve_connection_with_upgrades( + TokioIo::new(stream), + hyper::service::service_fn(move |mut req| { + let extensions = req.extensions_mut(); + extensions.insert(user.clone()); + HttpServer::process_request(server.clone(), req) + }) + ); + let conn = graceful.watch(conn.into_owned()); + + tokio::spawn(async move { + if let Err(err) = conn.await { + warn!("Unix connection error: {}", err); + } + }); }, - _ => { - error!("Could not obtain user details for UNIX socket"); - continue; + + res = signal_exit.changed() => { + // Break if the channel is closed or the new value is `true`. + if res.is_err() || *signal_exit.borrow() { + drop(listener); + break; + } } - }; + } + } - let server = server.clone(); - tokio::task::spawn(async move { - let _ = hyper_util::server::conn::auto::Builder::new( - TokioExecutor::new(), - ) - .serve_connection( - TokioIo::new(stream), - service_fn(move |mut req| { - let extensions = req.extensions_mut(); - extensions.insert(user.clone()); - let server = server.clone(); - async move { server.process_request(req).await } - }), - ) - .await; - }); + tokio::select! { + _ = graceful.shutdown() => { }, + _ = tokio::time::sleep(Duration::from_secs(10)) => { + warn!( + "Waited 10 seconds for TCP listener to shutdown, aborting..." + ); + } } } diff --git a/src/server/bgp/analyser.rs b/src/server/bgp/analyser.rs index ac753333e..cae4f8ddd 100644 --- a/src/server/bgp/analyser.rs +++ b/src/server/bgp/analyser.rs @@ -15,6 +15,7 @@ use crate::api::roa::{ AsNumber, ConfiguredRoa, Ipv4Prefix, Ipv6Prefix, RoaPayload, TypedPrefix, }; use crate::config::Config; +use crate::server::runtime::KrillRuntime; use super::riswhois::{ RisWhois, RisWhoisError, RisWhoisLoader, RouteOrigin, RouteOriginSet, RoutePrefix, @@ -79,7 +80,9 @@ impl BgpAnalyser { /// /// Returns `Ok(true)` if it did do a download, `Ok(false)` if no download /// was necessary, or an error if downloading was attempted but failed. - pub async fn update(&self) -> Result { + pub fn update( + &self, krill: &KrillRuntime + ) -> Result { let Some(loader) = self.loader.as_ref() else { return Ok(false) }; @@ -96,7 +99,7 @@ impl BgpAnalyser { return Ok(false) } - self.riswhois.store(Some(Arc::new(loader.load().await?))); + self.riswhois.store(Some(Arc::new(loader.load(krill)?))); self.last_checked.store(Time::now().timestamp(), Ordering::Relaxed); Ok(true) } diff --git a/src/server/bgp/riswhois.rs b/src/server/bgp/riswhois.rs index 5fb422fb9..72ab4b4fe 100644 --- a/src/server/bgp/riswhois.rs +++ b/src/server/bgp/riswhois.rs @@ -11,9 +11,11 @@ compile_error!("cannot build on 16 bit systems"); use std::{cmp, error, fmt, io}; use std::io::BufReader; use std::str::FromStr; +use std::sync::Arc; use libflate::gzip; use crate::api::roa::{AsNumber, Ipv4Prefix, Ipv6Prefix, TypedPrefix}; use crate::api::bgp::Announcement; +use crate::server::runtime::KrillRuntime; //------------ Configuration ------------------------------------------------- @@ -35,38 +37,49 @@ const MINIMUM_SEEN_BY: u32 = 13; /// A type that knows where RISwhois data lives and download it. pub struct RisWhoisLoader { /// The HTTP(S) URL of the location of IPv4 data set. - v4_url: String, + v4_url: Arc, /// The HTTP(S) URL of the location of IPv6 data set. - v6_url: String, + v6_url: Arc, } impl RisWhoisLoader { /// Creates a new loader from the URLS of the IPv4 and IPv6 data sets. pub fn new(v4_url: String, v6_url: String) -> Self { - Self { v4_url, v6_url } + Self { + v4_url: v4_url.into(), + v6_url: v6_url.into(), + } } /// Downloads and processes a new data set. - pub async fn load(&self) -> Result { + pub fn load( + &self, krill: &KrillRuntime + ) -> Result { Ok(RisWhois::new( - Self::load_tree(&self.v4_url).await?, - Self::load_tree(&self.v6_url).await?, + Self::load_tree(self.v4_url.clone(), krill)?, + Self::load_tree(self.v6_url.clone(), krill)?, )) } /// Downloads and process the tree for one address family. - async fn load_tree( - uri: &str + fn load_tree( + uri: Arc, krill: &KrillRuntime ) -> Result, RisWhoisError> where

::Err: error::Error + Send + Sync + 'static { - Self::parse_gz_data( - &reqwest::get(uri).await.map_err(|err| { - RisWhoisError::new(uri, io::Error::other(err)) - })?.bytes().await.map_err(|err| { - RisWhoisError::new(uri, io::Error::other(err)) - })? - ).map_err(|err| RisWhoisError::new(uri, err)) + let uri_clone = uri.clone(); + let data = krill.exec_async(async move { + Ok( + reqwest::get(uri_clone.as_ref()).await.map_err(|err| { + RisWhoisError::new(&uri_clone, io::Error::other(err)) + })?.bytes().await.map_err(|err| { + RisWhoisError::new(&uri_clone, io::Error::other(err)) + })? + ) + }).map_err(|err| RisWhoisError::new(&uri, io::Error::other(err)))??; + Self::parse_gz_data(&data).map_err(|err| { + RisWhoisError::new(&uri, err) + }) } /// Parses the gzipped data. diff --git a/src/server/ca/manager.rs b/src/server/ca/manager.rs index a692e4bed..cc6558e9d 100644 --- a/src/server/ca/manager.rs +++ b/src/server/ca/manager.rs @@ -21,7 +21,6 @@ use rpki::ca::publication::{ }; use rpki::crypto::KeyIdentifier; use rpki::repository::resources::ResourceSet; -use tokio::sync::oneshot; use crate::api::admin::{ AddChildRequest, ParentCaContact, ParentCaReq, ParentServerInfo, PublicationServerInfo, PublishedFile, RepositoryContact, @@ -2454,20 +2453,16 @@ impl CaManager { cms_logger.sent(&msg)?; let timeout = krill.config().post_protocol_msg_timeout_seconds; - - let (tx, rx) = oneshot::channel(); let http_uri = service_uri.clone(); - krill.spawn_async(async move { - let _ = tx.send( - httpclient::post_binary_with_full_ua( - http_uri.as_str(), - &msg, - content_type, - timeout, - ).await - ); - }); - match rx.blocking_recv() { + let res = krill.exec_async( + httpclient::post_binary_with_full_ua( + http_uri, + msg, + content_type, + timeout, + ) + ); + match res { Err(_) => { cms_logger.err(format!( "Error posting CMS to {service_uri}: \ diff --git a/src/server/manager.rs b/src/server/manager.rs index 333bdf97c..f566925a0 100644 --- a/src/server/manager.rs +++ b/src/server/manager.rs @@ -1,7 +1,7 @@ //! The public part of the Krill RPKI server. //! -use std::{error, fmt, thread}; +use std::{error, fmt}; use std::collections::HashMap; use std::path::PathBuf; use std::str::FromStr; @@ -12,6 +12,7 @@ use log::info; use rpki::ca::{idexchange, publication}; use rpki::repository::resources::ResourceSet; use tokio::runtime::{Handle as TokioHandle}; +use tokio::sync::oneshot; use crate::api; use crate::api::status::ErrorResponse; use crate::commons::actor::Actor; @@ -22,7 +23,7 @@ use crate::constants::{TA_NAME, ta_handle, testbed_ca_handle}; use crate::server::ca::CaStatus; use super::scheduler; use super::mq::{Task, now}; -use super::runtime::KrillRuntime; +use super::runtime::{KrillRuntime, SpawnError, ThreadPool, ThreadPoolHandle}; //------------ StartupManager ------------------------------------------------ @@ -33,6 +34,7 @@ use super::runtime::KrillRuntime; /// HTTP server is started and we are still running sync in a single thread. pub struct StartupManager { runtime: KrillRuntime, + thread_pool: ThreadPool, } impl StartupManager { @@ -43,16 +45,27 @@ impl StartupManager { pub fn new( config: Config, tokio: TokioHandle ) -> Result { - Ok(Self { runtime: KrillRuntime::new(config, tokio)? }) + Ok(Self { + thread_pool: ThreadPool::new(&config)?, + runtime: KrillRuntime::new(config, tokio)?, + }) } /// Promotes the startup manager into a full Krill manager. - pub fn promote(self) -> KrillManager { - KrillManager { krill_runtime: self.runtime } + pub fn promote( + self + ) -> Result<(KrillManager, ThreadPool), KrillError> { + Ok(( + KrillManager { + krill_runtime: self.runtime, + thread_pool: self.thread_pool.handle(), + }, + self.thread_pool, + )) } /// Starts the scheduler in a separate thread. - pub fn run_scheduler(&self) -> Result<(), KrillError> { + pub fn run_scheduler(&mut self) -> Result<(), KrillError> { // When multi-node set ups with a shared queue are // supported then we can no longer safely reschedule // ALL running tests. See issue: #1112 @@ -60,7 +73,7 @@ impl StartupManager { self.runtime.tasks().schedule(Task::QueueStartTasks, now())?; let krill = self.runtime.clone(); - thread::spawn(|| scheduler::run(krill)); + self.thread_pool.spawn(|rx| scheduler::run(krill, rx)); Ok(()) } @@ -198,6 +211,7 @@ impl StartupManager { pub struct KrillManager { krill_runtime: KrillRuntime, + thread_pool: ThreadPoolHandle, } impl KrillManager { @@ -233,10 +247,12 @@ impl KrillManager { F: FnOnce(&KrillRuntime) -> Result + Send + 'static, T: Send + 'static, { + let (tx, rx) = oneshot::channel(); let runtime = self.krill_runtime.clone(); - tokio::task::spawn_blocking(move || { - op(&runtime) - }).await? + self.thread_pool.spawn(move || { + let _ = tx.send(op(&runtime)); + }).await?; + rx.await? } } @@ -1422,8 +1438,13 @@ impl From for KrillError { } } -impl From for RunError { - fn from(_: tokio::task::JoinError) -> Self { +impl From for RunError { + fn from(err: SpawnError) -> Self { + Self(KrillError::internal(err)) + } +} +impl From for RunError { + fn from(_: tokio::sync::oneshot::error::RecvError) -> Self { Self(KrillError::internal("task panicked")) } } diff --git a/src/server/runtime.rs b/src/server/runtime.rs index e813cece0..e2adb1bfd 100644 --- a/src/server/runtime.rs +++ b/src/server/runtime.rs @@ -1,30 +1,18 @@ -//! The server’s runtime. -//! -//! The Krill server contains both sync and async code. This module provides -//! the means to manage control flow through all these parts. -//! -//! Most processing in Krill happens in sync code because that is easier to -//! reason about. However, certain things – most prominently the HTTP -//! requests made to talk to remote repositories and parent CAs – have the -//! potential to block threads for an unduly long time. So these are best -//! performed as tasks on an async runtime. -//! -//! A consequence of this is that processing needs to be able to go from -//! sync to async and then back to sync. This module provides a mechanism to -//! do this in a safe and ergonomic way. -//! -//! > Side note: The terminology we are using is a bit creative. All the -//! > obvious terms are already used elsewhere and we don’t want ambiguity, -//! > so we had to resort to scroll quite a bit down in a thesaurus. +//! The Krill server runtime. //! +//! The runtime contains all the components of a Krill server in one central +//! place and allows access to them. A reference to it is being passed around +//! when performing actions that may require access to other compontents. - +use std::{cmp, error, fmt, thread}; use std::mem::drop; -use std::sync::Arc; +use std::sync::{mpsc as std_mpsc}; +use std::sync::{Arc, Mutex}; use std::time::Duration; -use log::info; +use log::{error, info}; use rpki::uri; use tokio::runtime; +use tokio::sync::{mpsc as tokio_mpsc, oneshot}; use crate::commons::actor::Actor; use crate::commons::crypto::{KrillSigner, KrillSignerBuilder}; use crate::commons::error::KrillError; @@ -38,10 +26,24 @@ use super::pubd::RepositoryManager; //------------ KrillRuntime -------------------------------------------------- +/// The Krill runtime. +/// +/// The runtime contains all the components of the Krill server and provides +/// access to them. It is keeps them behind an arc, so it can be cloned and +/// passed around cheaply. +/// +/// Many methods of the various components expect a refernce to the runtime +/// so they can initiate follow-up operations on other Krill components. #[derive(Clone)] pub struct KrillRuntime(Arc); impl KrillRuntime { + /// Creates a new Krill runtime. + /// + /// The runtime and all the components will be configured using `config`. + /// The `tokio` runtime handle will be used by the + /// [`spawn_async`][Self::spawn_async] method as the runtime to spawn + /// async tasks onto. pub fn new( config: Config, tokio: runtime::Handle, @@ -82,34 +84,42 @@ impl KrillRuntime { }))) } + /// Returns the config used to create the runtime. pub fn config(&self) -> &Config { &self.0.config } + /// Returns the service URI of this Krill server instance. pub fn service_uri(&self) -> &uri::Https { &self.0.service_uri } + /// Returns the repository manager. pub fn repo_manager(&self) -> &RepositoryManager { &self.0.repo_manager } + /// Returns the CA manager. pub fn ca_manager(&self) -> &CaManager { &self.0.ca_manager } + /// Returns the task queue. pub fn tasks(&self) -> &TaskQueue { &self.0.tasks } + /// Returns the signer. pub fn signer(&self) -> &KrillSigner { &self.0.signer } + /// Returns the BGP analyser. pub fn bgp_analyser(&self) -> &BgpAnalyser { &self.0.bgp_analyser } + /// Returns the actor to be used for sytem tasks. pub fn system_actor(&self) -> &Actor { &self.0.system_actor } @@ -119,13 +129,22 @@ impl KrillRuntime { self.config().testbed().is_some() } - /// Spawns a future onto the async runtime. - pub fn spawn_async( - &self, future: impl Future + Send + 'static - ) { - // Explicitely drop the join handle so Clippy doesn’t complain. The - // task will continue running. - drop(self.0.tokio.spawn(future)); + /// Runs a future on a Tokio runtime and blocks until it resolves. + /// + /// This method blocks the current thread. + pub fn exec_async( + &self, future: F + ) -> Result + where + F: Future + Send + 'static, + F::Output: Send + 'static, + { + let (tx, rx) = oneshot::channel(); + let join = self.0.tokio.spawn(async move { + let _ = tx.send(future.await); + }); + drop(join); // explicitly drop to avoid warning + rx.blocking_recv().map_err(|_| ExecAsyncError(())) } } @@ -162,10 +181,189 @@ struct Components { /// The actor used for actions initiated by the server itself. system_actor: Actor, - /// The Tokio runtime to spawn tasks onto. - /// - /// We currently use it for both async and sync tasks (via - /// `spawn_blocking`). + /// The Tokio runtime to spawn async tasks onto. tokio: runtime::Handle, } + +//------------ ThreadPool ---------------------------------------------------- + +pub struct ThreadPool { + /// The sending end of the job queue. + worker_tx: tokio_mpsc::Sender, + + /// The sending ends of all shutdown queues for regular threads. + thread_tx: Vec>, + + /// The join handles of all child threads. + join: Vec>, +} + +impl ThreadPool { + pub fn new( + config: &Config + ) -> Result { + let (worker_tx, rx) = tokio_mpsc::channel(1); + let rx = Arc::new(Mutex::new(rx)); + + let thread_count = match config.num_threads { + Some(num) => num, + None => { + match thread::available_parallelism() { + Ok(num) => num.into(), + Err(err) => { + return Err(KrillError::internal( + format_args!( + "failed to determine thread number. Please \ + specify `num_threads` in config. \ + ({err})" + ) + )); + } + } + } + }; + let thread_count = cmp::min(thread_count, 1); + + let mut join = Vec::new(); + for _ in 0..thread_count { + let rx = rx.clone(); + join.push(thread::spawn(move || { + Self::worker_thread(rx) + })); + } + + Ok(Self { + worker_tx, + thread_tx: Vec::new(), + join + }) + } + + fn worker_thread( + rx: Arc>>, + ) { + loop { + let job = { + let mut queue = match rx.lock() { + Ok(queue) => queue, + Err(err) => { + error!( + "Fatal: worker thread failed to aquire lock: {err}" + ); + return; + } + }; + let Some(job) = queue.blocking_recv() else { + // None is returned when the queue is closed or when + // all the senders are gone. + return; + }; + match job { + ThreadPoolMessage::Job(job) => job, + ThreadPoolMessage::Shutdown => { + // Close the queue. If there are any tasks left, + // we want to still process those, so continue here. + queue.close(); + continue; + } + } + }; + (job)(); + } + } + + pub fn handle(&self) -> ThreadPoolHandle { + ThreadPoolHandle { tx: self.worker_tx.clone() } + } + + pub fn spawn( + &mut self, f: impl FnOnce(std_mpsc::Receiver<()>) + Send + 'static + ) { + let (tx, rx) = std_mpsc::sync_channel(1); + self.thread_tx.push(tx); + self.join.push(thread::spawn(|| f(rx))); + } + + pub fn terminate(self) { + let _ = self.worker_tx.blocking_send(ThreadPoolMessage::Shutdown); + for tx in self.thread_tx { + let _ = tx.send(()); + } + for join in self.join { + // `join` returns an error if the thread panicked. We can + // consider it done in this case. + eprintln!("Joining thread {:?}.", join.thread().id()); + let _ = join.join(); + } + } +} + + +//------------ ThreadPoolHandle ---------------------------------------------- + +#[derive(Clone)] +pub struct ThreadPoolHandle { + /// The sending end of the job queue. + tx: tokio_mpsc::Sender, +} + +impl ThreadPoolHandle { + pub async fn spawn( + &self, job: impl FnOnce() + Send + 'static + ) -> Result<(), SpawnError> { + self.tx.send( + ThreadPoolMessage::Job(Box::new(job)) + ).await.map_err(|_| SpawnError(())) + } +} + + +//------------ ThreadPoolMessage --------------------------------------------- + +/// The message sent to the queue of the thread pool. +enum ThreadPoolMessage { + /// A job to run. + Job(Box), + + /// The thread pool is shutting down. + Shutdown, +} + + +//============ SpawnError ==================================================== + +//------------ SpawnError ---------------------------------------------------- + +/// An error happened while trying to spawn a job. +/// +/// This error means that all worker threads have disappeared. +#[derive(Clone, Debug)] +pub struct SpawnError(()); + +impl fmt::Display for SpawnError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("all worker threads disappeared") + } +} + +impl error::Error for SpawnError { } + + +//------------ ExecAsyncError ------------------------------------------------ + +/// An error happened while waiting for a future to resolve. +/// +/// This error means that the executed future was dropped before being +/// resolved. +#[derive(Clone, Debug)] +pub struct ExecAsyncError(()); + +impl fmt::Display for ExecAsyncError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("the future was dropped before resolving") + } +} + +impl error::Error for ExecAsyncError { } + diff --git a/src/server/scheduler.rs b/src/server/scheduler.rs index 38c3a4aca..7c982390d 100644 --- a/src/server/scheduler.rs +++ b/src/server/scheduler.rs @@ -2,7 +2,7 @@ //! event that occurred, or planned (e.g. re-publishing). use std::collections::HashMap; -use std::thread::sleep; +use std::sync::mpsc; use std::time::Duration; use log::{debug, error, info, warn}; use rpki::ca::idexchange::{CaHandle, ParentHandle}; @@ -40,11 +40,19 @@ use super::mq::TaskResult; //------------ run ----------------------------------------------------------- -pub(super) fn run(krill: KrillRuntime) { +pub(super) fn run( + krill: KrillRuntime, + shutdown: mpsc::Receiver<()>, +) { let started = Timestamp::now(); + // Outer loop: Waits half a second if the task queue is empty. loop { - while let Some((task_key, value)) = krill.tasks().pop() { + // Inner loop: Breaks when the the task queue becomes empty. + while + shutdown.try_recv() == Err(mpsc::TryRecvError::Empty) + && let Some((task_key, value)) = krill.tasks().pop() + { match serde_json::from_value(value) { Err(e) => { // If we cannot parse the value of this task, then we @@ -105,7 +113,15 @@ pub(super) fn run(krill: KrillRuntime) { } } - sleep(Duration::from_millis(500)); + match shutdown.recv_timeout(Duration::from_millis(500)) { + Ok(()) | Err(mpsc::RecvTimeoutError::Disconnected) => { + // Shutdown requested or the sender went away: Quit. + break; + } + Err(mpsc::RecvTimeoutError::Timeout) => { + // Timeout: continue. + } + } } } @@ -466,12 +482,9 @@ fn republish_if_needed( fn announcements_refresh( krill: &KrillRuntime ) -> Result { - let runtime = krill.clone(); - krill.spawn_async(async move { - if let Err(e) = runtime.bgp_analyser().update().await { - error!("Failed to update BGP announcements: {}", e) - } - }); + if let Err(e) = krill.bgp_analyser().update(krill) { + error!("Failed to update BGP announcements: {}", e) + } // check again in 10 minutes, note.. this is a no-op in case the // actual update was less then 1 hour ago. diff --git a/tests/auth_check.rs b/tests/auth_check.rs index 16a1b5a40..24d0f5e79 100644 --- a/tests/auth_check.rs +++ b/tests/auth_check.rs @@ -10,7 +10,9 @@ mod common; #[tokio::test] async fn auth_check() { - let (server, _tempdir) = common::KrillServer::start().await; + let server = common::KrillServer::start().await; + + eprintln!("server is up."); // Get a client with a changed auth token. let client = KrillClient::new( @@ -31,6 +33,7 @@ async fn auth_check() { ) ) ); + eprintln!("back."); } #[tokio::test] @@ -40,7 +43,7 @@ async fn auth_check_unix() { use krill::cli::client::ServerUri; - let (mut config, _tempdir) = common::TestConfig::mem_storage() + let (mut config, tempdir) = common::TestConfig::mem_storage() .enable_testbed().enable_ca_refresh().finalize(); // The user that is executing the test gets read access to everything @@ -51,7 +54,9 @@ async fn auth_check_unix() { config.unix_socket = Some(file_sock.path().into()); config.unix_users = HashMap::from([(user.name, "readonly".to_string())]); - let _server = common::KrillServer::start_with_config_unix(config).await; + let _server = common::KrillServer::start_with_config_unix( + config, Some(tempdir) + ).await; let client = KrillClient::new( ServerUri::try_from( @@ -73,4 +78,4 @@ async fn auth_check_unix() { ) ) ); -} \ No newline at end of file +} diff --git a/tests/benchmark.rs b/tests/benchmark.rs index 6019934d6..293ffb9da 100644 --- a/tests/benchmark.rs +++ b/tests/benchmark.rs @@ -17,7 +17,7 @@ async fn benchmark() { let ca_roas = 10; config.benchmark = Some(Benchmark { cas, ca_roas }); config.log_level = LevelFilter::Info; - let server = common::KrillServer::start_with_config(config).await; + let server = common::KrillServer::start_with_config(config, None).await; wait_for_nr_cas_under_testbed(server.client(), cas).await; // We expect all CAs, plus the testbed and the ta as publishers diff --git a/tests/client_coverage.rs b/tests/client_coverage.rs index 70f0fae81..12a4db61c 100644 --- a/tests/client_coverage.rs +++ b/tests/client_coverage.rs @@ -165,7 +165,7 @@ async fn client_coverage(server: KrillServer) { #[tokio::test] async fn http() { - let (server, _tempdir) = common::KrillServer::start_with_testbed().await; + let server = common::KrillServer::start_with_testbed().await; client_coverage(server).await; } @@ -174,7 +174,7 @@ async fn http() { async fn unix() { use std::collections::HashMap; - let (mut config, _tempdir) = common::TestConfig::mem_storage() + let (mut config, tempdir) = common::TestConfig::mem_storage() .enable_testbed().set_zero_port().enable_ca_refresh().finalize(); // The user that is executing the test gets access to everything @@ -184,6 +184,8 @@ async fn unix() { config.unix_socket_enabled = true; config.unix_socket = Some(file_sock.path().into()); config.unix_users = HashMap::from([(user.name, "admin".to_string())]); - let server = common::KrillServer::start_with_config_unix(config).await; + let server = common::KrillServer::start_with_config_unix( + config, Some(tempdir) + ).await; client_coverage(server).await; } diff --git a/tests/common.rs b/tests/common.rs index b903c63a0..9e8af2909 100644 --- a/tests/common.rs +++ b/tests/common.rs @@ -275,6 +275,7 @@ impl TestConfig { log_type, log_file: None, syslog_facility, + num_threads: None, admin_token, auth_type, #[cfg(feature = "multi-user")] @@ -321,10 +322,12 @@ impl TestConfig { /// A test Krill server. pub struct KrillServer { - join: JoinHandle<()>, + join: Option>, running: Option>, + exit: Option>, server_uri: ServerUri, client: KrillClient, + data_dir: Option, } impl KrillServer { @@ -332,50 +335,50 @@ impl KrillServer { /// /// The server will use memory storage. The function will start the /// server and wait for it to become ready. - pub async fn start() -> (Self, TempDir) { + pub async fn start() -> Self { let (config, data_dir) = TestConfig::mem_storage().finalize(); - (Self::start_with_config(config).await, data_dir) + Self::start_with_config(config, Some(data_dir)).await } /// Starts a test server with testbed enabled. /// /// The server will use memory storage. The function will start the /// server and wait for it to become ready. - pub async fn start_with_testbed() -> (Self, TempDir) { + pub async fn start_with_testbed() -> Self { let (config, data_dir) = TestConfig::mem_storage().enable_testbed().finalize(); - (Self::start_with_config(config).await, data_dir) + Self::start_with_config(config, Some(data_dir)).await } /// Starts a test server with testbed enabled and a modified config. pub async fn start_with_config_testbed( op: impl FnOnce(&mut Config) - ) -> (Self, TempDir) { + ) -> Self { let (mut config, data_dir) = TestConfig::mem_storage().enable_testbed().finalize(); op(&mut config); - (Self::start_with_config(config).await, data_dir) + Self::start_with_config(config, Some(data_dir)).await } /// Starts a test server with file storage with testbed enabled. /// /// The server will use memory storage. The function will start the /// server and wait for it to become ready. - pub async fn start_with_file_storage_and_testbed() -> (Self, TempDir) { + pub async fn start_with_file_storage_and_testbed() -> Self { let (config, data_dir) = TestConfig::file_storage().enable_testbed().finalize(); - (Self::start_with_config(config).await, data_dir) + Self::start_with_config(config, Some(data_dir)).await } /// Starts a second test server with testbed enabled. /// /// The server will use memory storage. The function will start the /// server and wait for it to become ready. - pub async fn start_second_with_testbed() -> (Self, TempDir) { + pub async fn start_second_with_testbed() -> Self { let (config, data_dir) = TestConfig::mem_storage() .alternative_port().enable_testbed().enable_second_signer() .finalize(); - (Self::start_with_config(config).await, data_dir) + Self::start_with_config(config, Some(data_dir)).await } /// Starts a publication daemon. @@ -384,7 +387,7 @@ impl KrillServer { /// udpates. pub async fn start_pubd( rrdp_delta_min_interval_seconds: u32 - ) -> (Self, TempDir) { + ) -> Self { let (mut config, data_dir) = TestConfig::mem_storage() .alternative_port() .enable_second_signer() // XXX Not sure why? @@ -392,63 +395,64 @@ impl KrillServer { config.rrdp_updates_config.rrdp_delta_interval_min_seconds = rrdp_delta_min_interval_seconds; let port = config.port; - let server = Self::start_with_config(config).await; + let server = Self::start_with_config(config, Some(data_dir)).await; server.pubserver_init(port).await; - (server, data_dir) + server } /// Starts a test server with the given config. /// /// This will start the server and wait for it to become ready. - pub async fn start_with_config(config: Config) -> Self { + pub async fn start_with_config( + config: Config, data_dir: Option, + ) -> Self { let server_uri = ServerUri::from_str( &format!( "https://{}:{}/", config.ip.first().unwrap(), config.port ) ).unwrap(); - let client = KrillClient::new( - server_uri.clone(), Some(config.admin_token.clone()) - ); - let (tx, running) = oneshot::channel(); - let mut res = Self { - join: tokio::task::spawn_blocking(|| { - if let Err(err) = start_krill_daemon(config, Some(tx)) { - error!("Krill failed to start: {err}"); - } - }), - running: Some(running), - server_uri, - client: client.unwrap(), - }; - res.ready().await; - res + Self::start_with_server_uri(config, server_uri, data_dir).await } /// Starts a test server with the given config over a UNIX socket /// /// This will start the server and wait for it to become ready. #[cfg(unix)] - pub async fn start_with_config_unix(config: Config) -> Self { + pub async fn start_with_config_unix( + config: Config, data_dir: Option + ) -> Self { let server_uri = ServerUri::from_str( &format!( "unix://{}", config.unix_socket().unwrap().display() ) ).unwrap(); + Self::start_with_server_uri(config, server_uri, data_dir).await + } + + /// Starts a test server with the given server URI. + async fn start_with_server_uri( + config: Config, server_uri: ServerUri, data_dir: Option + ) -> Self { let client = KrillClient::new( - server_uri.clone(), None + server_uri.clone(), Some(config.admin_token.clone()) ); - let (tx, running) = oneshot::channel(); + let (running_tx, running_rx) = oneshot::channel(); + let (exit_tx, exit_rx) = oneshot::channel(); let mut res = Self { - join: tokio::task::spawn_blocking(|| { - if let Err(err) = start_krill_daemon(config, Some(tx)) { + join: Some(tokio::task::spawn_blocking(|| { + if let Err(err) = start_krill_daemon( + config, Some(running_tx), Some(exit_rx), + ) { error!("Krill failed to start: {err}"); } - }), - running: Some(running), + })), + running: Some(running_rx), + exit: Some(exit_tx), server_uri, client: client.unwrap(), + data_dir, }; res.ready().await; res @@ -458,6 +462,10 @@ impl KrillServer { &self.server_uri } + pub fn data_dir(&self) -> Option<&TempDir> { + self.data_dir.as_ref() + } + async fn ready(&mut self) { let running = match self.running.take() { Some(running) => running, @@ -485,9 +493,14 @@ impl KrillServer { } /// Aborts the server and waits for it to conclude cleanup. - pub async fn abort(self) { - self.join.abort(); - let _ = self.join.await; + pub async fn abort(mut self) { + if let Some(exit) = self.exit.take() { + exit.send(()).unwrap(); + } + if let Some(join) = self.join.take() { + join.abort(); + let _ = join.await; + } } /// Returns a Krill client for this server. @@ -503,6 +516,17 @@ impl KrillServer { } } +impl Drop for KrillServer { + fn drop(&mut self) { + if let Some(exit) = self.exit.take() { + exit.send(()).unwrap(); + } + if let Some(join) = self.join.take() { + join.abort(); + } + } +} + impl KrillServer { /// Creates a CA publishing in the built-in publisher. diff --git a/tests/functional_aspa.rs b/tests/functional_aspa.rs index 96d385285..d8a34f9a3 100644 --- a/tests/functional_aspa.rs +++ b/tests/functional_aspa.rs @@ -31,7 +31,7 @@ mod common; /// ``` #[tokio::test] async fn functional_aspa() { - let (server, _tempdir) = common::KrillServer::start_with_testbed().await; + let server = common::KrillServer::start_with_testbed().await; let testbed = common::ca_handle("testbed"); let ca = common::ca_handle("CA"); diff --git a/tests/functional_bgpsec.rs b/tests/functional_bgpsec.rs index 59d18365e..87a87e2cd 100644 --- a/tests/functional_bgpsec.rs +++ b/tests/functional_bgpsec.rs @@ -25,7 +25,7 @@ mod common; /// ``` #[tokio::test] async fn functional_bgpsec() { - let (server, _tempdir) = common::KrillServer::start_with_testbed().await; + let server = common::KrillServer::start_with_testbed().await; let testbed = common::ca_handle("testbed"); let ca = common::ca_handle("CA"); diff --git a/tests/functional_delegated_ca_import.rs b/tests/functional_delegated_ca_import.rs index c2888f30f..1a7ed2b38 100644 --- a/tests/functional_delegated_ca_import.rs +++ b/tests/functional_delegated_ca_import.rs @@ -15,9 +15,8 @@ mod common; #[tokio::test] async fn functional_delegated_ca_import() { // Start two testbeds - let (server1, _tmp1) = common::KrillServer::start_with_testbed().await; - let (server2, _tmp2) - = common::KrillServer::start_second_with_testbed().await; + let server1 = common::KrillServer::start_with_testbed().await; + let server2 = common::KrillServer::start_second_with_testbed().await; let testbed = common::ca_handle("testbed"); let parent_1 = common::ca_handle("parent_1"); diff --git a/tests/functional_keyroll.rs b/tests/functional_keyroll.rs index 53a5a18e5..019b5c362 100644 --- a/tests/functional_keyroll.rs +++ b/tests/functional_keyroll.rs @@ -25,7 +25,7 @@ mod common; /// * revoke and retire old key, mft and crl #[tokio::test] async fn functional_keyroll() { - let (server, _tempdir) = common::KrillServer::start_with_testbed().await; + let server = common::KrillServer::start_with_testbed().await; let testbed = common::ca_handle("testbed"); let ca = common::ca_handle("CA"); diff --git a/tests/functional_old_data.rs b/tests/functional_old_data.rs index 09d705a1a..c7bb85f91 100644 --- a/tests/functional_old_data.rs +++ b/tests/functional_old_data.rs @@ -47,7 +47,9 @@ async fn functional_old_data() { config.ta_timing = signer_config.ta_timing; eprintln!(">>>> Check whether Krill still starts."); - let server = common::KrillServer::start_with_config(config).await; + let server = common::KrillServer::start_with_config( + config, Some(tempdir) + ).await; eprintln!(">>>> Configure the TA signer."); let signer = TrustAnchorSignerManager::create(signer_config).unwrap(); @@ -69,5 +71,7 @@ async fn functional_old_data() { eprintln!(">>>> Fetch TAL and check it isn't empty."); assert!(!server.client().testbed_tal().await.unwrap().is_empty()); + + server.abort().await; } diff --git a/tests/functional_parent_child.rs b/tests/functional_parent_child.rs index 424d0938c..9a3f89e65 100644 --- a/tests/functional_parent_child.rs +++ b/tests/functional_parent_child.rs @@ -33,7 +33,7 @@ mod common; /// gracefully #[tokio::test] async fn functional_parent_child() { - let (server, tmpdir) + let server = common::KrillServer::start_with_file_storage_and_testbed().await; let testbed = common::ca_handle("testbed"); @@ -157,7 +157,7 @@ async fn functional_parent_child() { assert!(server.client().ca_details(&ca3).await.is_ok()); assert!( fs::metadata( - tmpdir.path().join("data/ca_objects/CA3.json") + server.data_dir().unwrap().path().join("data/ca_objects/CA3.json") ).unwrap().is_file() ); @@ -167,7 +167,7 @@ async fn functional_parent_child() { assert!(server.client().ca_details(&ca3).await.is_err()); assert_eq!( fs::metadata( - tmpdir.path().join("data/ca_objects/CA3.json") + server.data_dir().unwrap().path().join("data/ca_objects/CA3.json") ).unwrap_err().kind(), io::ErrorKind::NotFound ); diff --git a/tests/functional_roas.rs b/tests/functional_roas.rs index d474e96d0..6d9fb4f57 100644 --- a/tests/functional_roas.rs +++ b/tests/functional_roas.rs @@ -24,7 +24,7 @@ mod common; /// ``` #[tokio::test] async fn functional_roas() { - let (server, _tmpdir) = common::KrillServer::start_with_testbed().await; + let server = common::KrillServer::start_with_testbed().await; let testbed = common::ca_handle("testbed"); let ca = common::ca_handle("CA"); diff --git a/tests/functional_ta.rs b/tests/functional_ta.rs index 04e22368a..7d82b0b7a 100644 --- a/tests/functional_ta.rs +++ b/tests/functional_ta.rs @@ -23,11 +23,13 @@ mod common; /// [Krill as a Trust Anchor]: https://krill.docs.nlnetlabs.nl/en/stable/trust-anchor.html #[tokio::test] async fn functional_ta() { - let (mut config, _tempdir) = common::TestConfig::mem_storage() + let (mut config, tempdir) = common::TestConfig::mem_storage() .enable_second_signer().finalize(); let port = config.port; config.ta_support_enabled = true; - let server = common::KrillServer::start_with_config(config).await; + let server = common::KrillServer::start_with_config( + config, Some(tempdir) + ).await; eprintln!(">>>> Initialise TA proxy."); server.client().ta_proxy_init().await.unwrap(); diff --git a/tests/migrate_repository.rs b/tests/migrate_repository.rs index 1e7183e9b..9a312e546 100644 --- a/tests/migrate_repository.rs +++ b/tests/migrate_repository.rs @@ -25,14 +25,14 @@ async fn migrate_repository() { // Use a 5 second RRDP update interval for the Krill server, so that we // can also test here that the re-scheduling of delayed RRDP deltas // works. - let (server, _krilltmp) = common::KrillServer::start_with_config_testbed( + let server = common::KrillServer::start_with_config_testbed( |config| { config.rrdp_updates_config.rrdp_delta_interval_min_seconds = 5 } ).await; eprintln!(">>>> Start a secondary publication server."); - let (pubd, _pubtmp) = common::KrillServer::start_pubd(5).await; + let pubd = common::KrillServer::start_pubd(5).await; // Wait for the *testbed* CA to get its certificate, this means // that all CAs which are set up as part of krill_start under the diff --git a/tests/remote_parent_and_repo.rs b/tests/remote_parent_and_repo.rs index 67eea99b5..40218397b 100644 --- a/tests/remote_parent_and_repo.rs +++ b/tests/remote_parent_and_repo.rs @@ -13,9 +13,8 @@ mod common; #[tokio::test] async fn remote_parent_and_repo() { // Start two testbeds - let (server1, _tmp1) = common::KrillServer::start_with_testbed().await; - let (server2, _tmp2) - = common::KrillServer::start_second_with_testbed().await; + let server1 = common::KrillServer::start_with_testbed().await; + let server2 = common::KrillServer::start_second_with_testbed().await; let testbed = common::ca_handle("testbed"); let ca1 = common::ca_handle("CA1"); diff --git a/tests/suspend.rs b/tests/suspend.rs index 40e323cc7..d559f3de3 100644 --- a/tests/suspend.rs +++ b/tests/suspend.rs @@ -19,9 +19,11 @@ mod common; /// ``` #[tokio::test] async fn test_suspension() { - let (config, _tmpdir) = common::TestConfig::mem_storage() + let (config, tmpdir) = common::TestConfig::mem_storage() .enable_testbed().enable_suspend().finalize(); - let server = common::KrillServer::start_with_config(config).await; + let server = common::KrillServer::start_with_config( + config, Some(tmpdir) + ).await; let testbed = common::ca_handle("testbed"); let ca = common::ca_handle("CA"); diff --git a/tests/testbed.rs b/tests/testbed.rs index c92641854..2e80f3a08 100644 --- a/tests/testbed.rs +++ b/tests/testbed.rs @@ -12,9 +12,11 @@ mod common; #[tokio::test] async fn add_and_remove_certificate_authority() { - let (config, _tmpdir) = common::TestConfig::mem_storage() + let (config, tmpdir) = common::TestConfig::mem_storage() .enable_testbed().enable_ca_refresh().finalize(); - let server = common::KrillServer::start_with_config(config).await; + let server = common::KrillServer::start_with_config( + config, Some(tmpdir) + ).await; let ca = common::ca_handle("CA"); let ca_res = common::resources("AS1", "", ""); From 72ac0b984a36e6db5eed002625c4e3a2b5b6e937 Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Mon, 23 Feb 2026 16:43:17 +0100 Subject: [PATCH 11/51] Wait for threads before dropping the test server thing. --- tests/common.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/common.rs b/tests/common.rs index 9e8af2909..2df97ae93 100644 --- a/tests/common.rs +++ b/tests/common.rs @@ -523,6 +523,12 @@ impl Drop for KrillServer { } if let Some(join) = self.join.take() { join.abort(); + + // We can’t await the join handle here, so we will have to + // poll it. + while !join.is_finished() { + std::thread::sleep(std::time::Duration::from_millis(100)); + } } } } From b123528032b1b78473baabcd23c75aede2280b31 Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Mon, 23 Feb 2026 16:43:47 +0100 Subject: [PATCH 12/51] We need to wait a bit in the functional_old_data test. --- src/server/runtime.rs | 1 - tests/functional_old_data.rs | 6 ++++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/server/runtime.rs b/src/server/runtime.rs index e2adb1bfd..43fc95d34 100644 --- a/src/server/runtime.rs +++ b/src/server/runtime.rs @@ -293,7 +293,6 @@ impl ThreadPool { for join in self.join { // `join` returns an error if the thread panicked. We can // consider it done in this case. - eprintln!("Joining thread {:?}.", join.thread().id()); let _ = join.join(); } } diff --git a/tests/functional_old_data.rs b/tests/functional_old_data.rs index c7bb85f91..fa1f9c9aa 100644 --- a/tests/functional_old_data.rs +++ b/tests/functional_old_data.rs @@ -54,16 +54,18 @@ async fn functional_old_data() { eprintln!(">>>> Configure the TA signer."); let signer = TrustAnchorSignerManager::create(signer_config).unwrap(); + // XXX Wait for Krill to process pending tasks. + eprintln!(">>>> Wait a bit for Krill to catch up."); + common::sleep_millis(1000).await; + eprintln!(">>>> Make TA proxy signer request."); let request = server.client().ta_proxy_signer_make_request().await.unwrap(); assert_eq!(request.ta_renew_time.unwrap().year(), 2026); assert_eq!(request.renew_times[0].1.year(), 2039); - eprintln!("{request}"); eprintln!(">>>> Sign TA proxy signer request."); let response = signer.process(request.into(), None).unwrap(); - eprintln!("{response}"); assert_eq!(response.content().child_responses.len(), 1); eprintln!(">>>> Process TA proxy signer response."); From d577c4c2b4c3e97e251dd6ac91411dbc0b371cab Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Tue, 24 Feb 2026 14:16:22 +0100 Subject: [PATCH 13/51] Documentation for the crate::server::runtime module. --- src/commons/actor.rs | 2 +- src/commons/eventsourcing/mod.rs | 32 +++++++---- src/commons/eventsourcing/wal.rs | 4 +- src/constants.rs | 2 +- src/daemon/http/auth/roles.rs | 2 +- src/server/mod.rs | 38 ++++++++++++ src/server/runtime.rs | 99 +++++++++++++++++++++++++++++++- 7 files changed, 159 insertions(+), 20 deletions(-) diff --git a/src/commons/actor.rs b/src/commons/actor.rs index e30829848..172ef6751 100644 --- a/src/commons/actor.rs +++ b/src/commons/actor.rs @@ -3,7 +3,7 @@ //! All actors are represented by the type [`Actor`] defined in this module. //! An actor can be anonymous, a system actor representing Krill’s own //! subsytems, or a user identified by the server’s -//! [`Authorizer`](crate::daemon::auth::Authorizer). +//! [`Authorizer`](crate::daemon::http::auth::Authorizer). use std::fmt; use std::sync::Arc; diff --git a/src/commons/eventsourcing/mod.rs b/src/commons/eventsourcing/mod.rs index b865c14e3..4821c1975 100644 --- a/src/commons/eventsourcing/mod.rs +++ b/src/commons/eventsourcing/mod.rs @@ -110,8 +110,8 @@ //! should be called “aggregate root,” but because this a bit wordy Krill //! just calls it 'Aggregate' instead. The trait [`Aggregate`] is defined for //! this that is implemented by, for instance, -//! [`CertAuth`][crate::ca::CertAuth] and -//! [`RepositoryAccess`][crate::pubd::RepositoryAccess]. +//! [`CertAuth`][crate::server::ca::CertAuth] and +//! [`RepositoryAccess`][crate::server::pubd::RepositoryAccess]. //! //! As we will see further down, when we get to describe the //! [`AggregateStore`] which ties all of this together. This allows us to @@ -164,7 +164,7 @@ //! implemented in aggregates and used event sourcing. However, CAs in the //! RPKI have to re-publish CRLs and Manifests very often. Even when //! there are no changes to ROAs these objects must be refreshed before they -//! go stale. By default Krill re-publishes every! 16 hours. +//! go stale. By default Krill re-publishes every 16 hours. //! //! This resulted in an enormous amount of commands and events to be //! generated for CAs, for a change that is done automatically – and which @@ -174,16 +174,17 @@ //! events from scratch, i.e., without using snapshots. //! //! Therefore we decided to implement a hybrid model in Krill. The Krill -//! [`CertAuth`][crate::ca::CertAuth] is still in charge of *almost* +//! [`CertAuth`][crate::server::ca::CertAuth] is still in charge of *almost* //! all changes, and in particular all *semantic* changes that users made. //! But, the generation of Manifests and CRLs is offloaded to an associated -//! component [`CaObjects`][crate::ca::publishing::CaObjects] that just keeps +//! component +//! [`CaObjects`][crate::server::ca::publishing::CaObjects] that just keeps //! the latest Manifest and CRL. It can re-sign these because it has access //! to a [`KrillSigner`][crate::commons::crypto::KrillSigner] and it can get //! the public key identifier needed for signing from the `CertAuth`. //! //! This is relevant here, because under the hood we use a -//! [`PreSaveEventListener`] to ensure that a new Manifest and CRL are +//! event listeners to ensure that a new Manifest and CRL are //! written when there is a change in ROAs or issued certificates, observed //! in events. //! @@ -191,19 +192,26 @@ //! ## Event Listeners //! //! The Krill event sourcing stack defines two different event listener -//! traits which are called by the [`AggregateStore`] when an aggregate is -//! successfully updated. The first, [`PreSaveEventListener`] is called -//! before updates are saved, and it can fail, the second, -//! [`PostSaveEventListener`] is called after all changes have been applied, -//! and thus cannot fail. +//! which are called by the [`AggregateStore`] when an aggregate is +//! successfully updated. Both of them are methods on the [`Aggregate`] +//! trait. The first, [`pre_save_events`][Aggregate::pre_save_events] is +//! called before updates are saved, and it can fail, the second, +//! [`post_save_events`][Aggregate::post_save_events] is called after all +//! changes have been applied, and thus cannot fail. //! //! In a nutshell, we use the event listeners for two things: //! //! * a pre-save trigger that the -//! [`CaObjects`][crate::ca::publishing::CaObjects] +//! [`CaObjects`][crate::server::ca::publishing::CaObjects] //! for a CA gets an updated Manifest and CRL, and //! * triggers that follow-up tasks are put on the scheduler, based on events. //! +//! In order to allow these listeners to trigger functionality in other Krill +//! components, the methods receive a reference to a +//! [`KrillRuntime`][crate::server::runtime::KrillRuntime]. This means that +//! the event sourcing module isn’t completely generic and tied to the use in +//! Krill specifically. +//! //! As discussed in issue //! [1182](https://github.com/NLnetLabs/krill/issues/1182), it would be best //! to remove the `PreSaveEventListener` trait and do everything through diff --git a/src/commons/eventsourcing/wal.rs b/src/commons/eventsourcing/wal.rs index 2b951da4e..6439194b1 100644 --- a/src/commons/eventsourcing/wal.rs +++ b/src/commons/eventsourcing/wal.rs @@ -43,8 +43,8 @@ use super::store::Storable; /// # Use within Krill /// /// Within Krill, write-ahead logging is currently used by the -/// [`Scheduler`][crate::daemon::scheduler::Scheduler] and -/// [`RepositoryContent`][crate::pubd::RepositoryContent]. +/// [`TaskQueue`][crate::server::mq::TaskQueue] and +/// [`RepositoryContent`][crate::server::pubd::RepositoryContent]. pub trait WalSupport: Storable { /// The type representing a command. type Command: WalCommand; diff --git a/src/constants.rs b/src/constants.rs index 747a7ce08..2ab4629c8 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -56,7 +56,7 @@ pub const KRILL_ENV_LOG_LEVEL: &str = "KRILL_LOG_LEVEL"; /// The environment variable with the log target. /// /// The variable should contain the name of a -/// [`LogType`][crate::daemon::config::LogType]. It will be overwritten by +/// [`LogType`][crate::config::LogType]. It will be overwritten by /// the config file. The default is “file.” pub const KRILL_ENV_LOG_TYPE: &str = "KRILL_LOG_TYPE"; diff --git a/src/daemon/http/auth/roles.rs b/src/daemon/http/auth/roles.rs index 8137ed44d..3973fa439 100644 --- a/src/daemon/http/auth/roles.rs +++ b/src/daemon/http/auth/roles.rs @@ -19,7 +19,7 @@ use super::{Permission, PermissionSet}; /// do not operate on resources. /// /// Currently, roles are given names and are defined in -/// [Config::auth_roles][crate::daemon::config::Config::auth_roles] and +/// [Config::auth_roles][crate::config::Config::auth_roles] and /// referenced by authorization providers through those names. #[derive(Clone, Debug, Deserialize, Eq, PartialEq)] #[serde(from = "RoleConf")] diff --git a/src/server/mod.rs b/src/server/mod.rs index 27aa74fc8..7d9a1a71c 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -1,3 +1,41 @@ +//! The Krill server. +//! +//! This module contains all the components that implement the Krill server +//! itself, its business logic, if you will. The server is controlled via +//! the [`daemon`][super::daemon] which primarily provides the HTTP server. +//! +//! Nearly everything in the Krill server is sync, with the notable exception +//! of the HTTP client user to talk to remote parents and publication servers. +//! However, the server can be run in parallel in multiple threads. The +//! translation between the async HTTP server code and the sync Krill server +//! happens in [`daemon`][super::daemon] as well. +//! +//! Primarily, interaction with the server should happen through the types +//! in the [`manager`] module only. +//! +//! The additional modules contain the individual components of the server. +//! The main components are: +//! +//! * [`ca`]: the RPKI certification authority which collects the +//! configuration for each CA and translates it into objects. +//! * [`pubd`]: the publication server which manages the data that is +//! published by a Krill instance. +//! * [`taproxy`]: the server-side of managing a trust anchor which interacts +//! with the [`tasigner`][crate::tasigner]. +//! +//! In addition, there are a number of helper components: +//! +//! * [`bgp`]: the BGP analyser which uses RISwhois data to check ROA +//! configurations and suggests changes. +//! * [`mq`] and [`scheduler`]: a task queue which is used to schedule and +//! then execute follow-up or recurring tasks. +//! * [`properties`]: a place to store and update certain properties of a +//! Krill instance. +//! +//! All of this is tied together through the [`runtime`] module which makes +//! all components available to all other components, avoiding complicated +//! side-ways relationships. + pub mod bgp; pub mod ca; pub mod manager; diff --git a/src/server/runtime.rs b/src/server/runtime.rs index 43fc95d34..8c1a095b8 100644 --- a/src/server/runtime.rs +++ b/src/server/runtime.rs @@ -3,6 +3,9 @@ //! The runtime contains all the components of a Krill server in one central //! place and allows access to them. A reference to it is being passed around //! when performing actions that may require access to other compontents. +//! +//! In addition, this module also provides the [`ThreadPool`] that is used +//! by the daemon to run its jobs on. use std::{cmp, error, fmt, thread}; use std::mem::drop; @@ -32,7 +35,7 @@ use super::pubd::RepositoryManager; /// access to them. It is keeps them behind an arc, so it can be cloned and /// passed around cheaply. /// -/// Many methods of the various components expect a refernce to the runtime +/// Many methods of the various components expect a reference to the runtime /// so they can initiate follow-up operations on other Krill components. #[derive(Clone)] pub struct KrillRuntime(Arc); @@ -42,7 +45,7 @@ impl KrillRuntime { /// /// The runtime and all the components will be configured using `config`. /// The `tokio` runtime handle will be used by the - /// [`spawn_async`][Self::spawn_async] method as the runtime to spawn + /// [`exec_async`][Self::exec_async] method as the runtime to spawn /// async tasks onto. pub fn new( config: Config, @@ -151,6 +154,9 @@ impl KrillRuntime { //------------ Components ---------------------------------------------------- +/// All the components of a Krill server. +/// +/// A value of this type is kept by [`KrillRuntime`] behind an arc. struct Components { /// The server configuration. /// @@ -188,18 +194,56 @@ struct Components { //------------ ThreadPool ---------------------------------------------------- +/// A thread pool to run jobs on. +/// +/// This type represents the thread pool itself and should be kept around +/// during the entire lifetime of the pool. +/// +/// Jobs are spawned onto the pool through a +/// [`ThreadPoolHandle`] which can be obtained via the +/// [`handle`][Self::handle] method. +/// +/// Additional, non-worker threads can be created using the +/// [`spawn`][Self::spawn] method. This feature is used for the +/// scheduler thread. Spawning a thread via the thread pool differs regular +/// threads in that it provides a means to signal that the thread should +/// exit. +/// +/// This becomes relevant when it is time to shut down the application. In +/// this case, the [`terminate`][Self::terminate] method is called. The +/// imminent shutdown is signalled to all the worker threads and the +/// additional threads and then the method blocks and waits for all threads +/// to exit. +/// +/// During shutdown, the thread pool will not accept new jobs but the +/// worker threads will process all already queued jobs. This is slightly +/// theoretical as the queue capacity is 1, so there should be at most one +/// queued job. pub struct ThreadPool { /// The sending end of the job queue. + /// + /// The receiving end of this queue is shared between all worker threads. worker_tx: tokio_mpsc::Sender, /// The sending ends of all shutdown queues for regular threads. + /// + /// Each thread spawned via the `spawn` method gets the receiving end + /// of one of these. During shutdown, a `()` is sent to each of them. thread_tx: Vec>, /// The join handles of all child threads. + /// + /// We will wait for all of them during shutdown. join: Vec>, } impl ThreadPool { + /// Creates a new thread pool based on the config. + /// + /// Currently, we only use [`config.num_threads`][Config::num_threads] + /// to allow users to configure the number of worker threads. By default, + /// the number is the available parallelism as reported by the standard + /// library. pub fn new( config: &Config ) -> Result { @@ -233,6 +277,8 @@ impl ThreadPool { })); } + info!("Created thread pool with {thread_count} threads"); + Ok(Self { worker_tx, thread_tx: Vec::new(), @@ -240,6 +286,24 @@ impl ThreadPool { }) } + /// The thread function of each worker thread. + /// + /// The function receives a copy of the receiving end of the job queue + /// behind a mutex which allows the thread to acquire new work. + /// + /// The work distribution mechanism is extremely simple: When it is out + /// of work, a thread will try to acquire the lock on the receiver. When + /// it acquires the lock, it will the perform a blocking read on the + /// queue. + /// + /// If the received message is a new job, it will drop the lock and + /// perform the job. If the message signals a shutdown, it will call + /// `close` on the queue – which will switch the queue into shutdown + /// mode, drop the lock and start again at the top. + /// + /// When the queue is in shutdown mode, trying to receive a message will + /// return `None` once the queue has been exhausted. This is the signal + /// for the thread to exit. fn worker_thread( rx: Arc>>, ) { @@ -273,10 +337,20 @@ impl ThreadPool { } } + /// Creates a new handle to the thread pool pub fn handle(&self) -> ThreadPoolHandle { ThreadPoolHandle { tx: self.worker_tx.clone() } } + /// Spawns a new additional thread on the thread pool. + /// + /// The method expects a closure which will receive the receiver for a + /// standard library MPSC queue. When the thread pool is being shut down, + /// a single `()` is sent to this queue. + /// + /// Additional threads are waited upon when the thread pool is terminated + /// and there currently is no timeout for that, so make sure your thread + /// actually listens to the shutdown signal and terminates eventually. pub fn spawn( &mut self, f: impl FnOnce(std_mpsc::Receiver<()>) + Send + 'static ) { @@ -285,6 +359,10 @@ impl ThreadPool { self.join.push(thread::spawn(|| f(rx))); } + /// Terminates the thread pool. + /// + /// The method sends signals to all threads to initiate their own shutdown + /// and then blocks until all threads have terminated. pub fn terminate(self) { let _ = self.worker_tx.blocking_send(ThreadPoolMessage::Shutdown); for tx in self.thread_tx { @@ -301,6 +379,12 @@ impl ThreadPool { //------------ ThreadPoolHandle ---------------------------------------------- +/// A handle to a thread pool, allowing to spawn jobs onto it. +/// +/// The sole purpose of this type is to allow spawning jobs onto the thread +/// pool it is connected to via the [`spawn`][Self::spawn] method. +/// +/// Handles can be cloned relatively cheaply. #[derive(Clone)] pub struct ThreadPoolHandle { /// The sending end of the job queue. @@ -308,6 +392,15 @@ pub struct ThreadPoolHandle { } impl ThreadPoolHandle { + /// Spawns a job onto the thread pool. + /// + /// The job is represented by the closure. As this closure will be run + /// on a different thread, it needs to be `Send + 'static`. + /// + /// Returns an error if the thread pool does not accept new jobs any more. + /// + /// This is an async function that will only return once the job has been + /// dipatched of. pub async fn spawn( &self, job: impl FnOnce() + Send + 'static ) -> Result<(), SpawnError> { @@ -336,7 +429,7 @@ enum ThreadPoolMessage { /// An error happened while trying to spawn a job. /// -/// This error means that all worker threads have disappeared. +/// This error means that the thread pool does not accept new jobs any more. #[derive(Clone, Debug)] pub struct SpawnError(()); From e0c4be435e8b5708b38ce2da3a96bc3d14554c24 Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Thu, 26 Feb 2026 14:10:16 +0100 Subject: [PATCH 14/51] Add some more documentation. --- src/daemon/http/auth/mod.rs | 2 +- src/daemon/http/server.rs | 3 + src/daemon/mod.rs | 18 ++++++ src/daemon/start.rs | 119 ++++++++++++++++++++++++++++++++++-- 4 files changed, 136 insertions(+), 6 deletions(-) diff --git a/src/daemon/http/auth/mod.rs b/src/daemon/http/auth/mod.rs index 40316795c..68adf83ad 100644 --- a/src/daemon/http/auth/mod.rs +++ b/src/daemon/http/auth/mod.rs @@ -1,4 +1,4 @@ - +//! User authentication and authorization for the HTTP server. pub use self::authorizer::{AuthInfo, Authorizer, LoggedInUser}; pub use self::permission::{Permission, PermissionSet}; diff --git a/src/daemon/http/server.rs b/src/daemon/http/server.rs index 40ca0eb4f..c6b89b063 100644 --- a/src/daemon/http/server.rs +++ b/src/daemon/http/server.rs @@ -1,3 +1,5 @@ +//! The core of the HTTP server. + use std::env; use std::sync::{Arc, Weak}; use clap::crate_version; @@ -21,6 +23,7 @@ use super::response::{HyperResponse, HttpResponse}; //------------ HttpServer ---------------------------------------------------- /// The Krill HTTP server. +/// pub struct HttpServer { /// The Krill server. krill: KrillManager, diff --git a/src/daemon/mod.rs b/src/daemon/mod.rs index 04c2f5eb0..3e59077c0 100644 --- a/src/daemon/mod.rs +++ b/src/daemon/mod.rs @@ -2,6 +2,24 @@ //! //! This module contains the code actually driving the daemon including //! processing HTTP requests for the API. +//! +//! Everything related to processing HTTP requests can be found in the +//! [`http`] module. The [`start`] module contains the actual socket +//! listeners and connection handlers as well as the start-up code for +//! the daemon. +//! +//! # _Refactoring to be done_ +//! +//! * Turn the current [`HttpServer`][http::server::HttpServer] into the +//! `KrillDaemon` and attach all the things currently done in [`start`] +//! to it. +//! * Then move everything from [`http`] up here. +//! * Limit what needs to be `pub`. Ideally, only the new `KrillDaemon` +//! needs to be, but there are a few things that are referred to in the +//! config. Consider moving those to the [`config`][crate::config] module +//! and refer to them from here instead. +//! * Improve the error flow within the daemon to allow fatal errors to make +//! Krill exit. pub mod http; pub mod start; diff --git a/src/daemon/start.rs b/src/daemon/start.rs index 1463bd974..13ed9d406 100644 --- a/src/daemon/start.rs +++ b/src/daemon/start.rs @@ -1,3 +1,10 @@ +//! Starts and then runs the Krill daemon. +//! +//! Despite its name, this module contains the core game loop of Krill. It +//! provides both the socket listeners and connection handlers for the HTTP +//! server and sets everything up. All of this is provided via the +//! [`start_krill_daemon`] function. + use std::{env, process}; use std::net::SocketAddr; use std::path::Path; @@ -27,6 +34,22 @@ use super::http::{tls, tls_keys}; use super::http::server::HttpServer; +//------------ start_krill_daemon -------------------------------------------- + +/// Starts the Krill daemon and blocks until it exits. +/// +/// The configuration of the Krill daemon and server is taken from `config`. +/// +/// If `signal_running` is given, a `()` will be sent to it once the first +/// listener socket is ready to process requests. +/// +/// If `signal_exit` is given, the daemon will exit when a `()` is sent to +/// the channel. Otherwise it waits for a SIGINT or SIGTERM or a fatal +/// error happening. +/// +/// The function will return an error if something goes wrong during startup. +/// Once it blocks waiting for an exit, it will return `Ok(())` even if +/// something went wrong. In this case, an error message will be logged, pub fn start_krill_daemon( config: Config, mut signal_running: Option>, @@ -161,8 +184,7 @@ pub fn start_krill_daemon( let _ = exit.await; } else { - // TODO also catch SIGTERM here. - let _ = tokio::signal::ctrl_c().await; + exit_signalled().await; } let _ = exit_tx.send(true); let _ = join.join_all().await; @@ -174,7 +196,24 @@ pub fn start_krill_daemon( Ok(()) } -/// Runs an HTTP listener on a single socket. + +//------------ single_http_listener ------------------------------------------ + +/// Creates and listens on a TCP socket for the Krill API. +/// +/// The socket will listening on the given `addr`. Unless TLS is disabled in +/// the config associated with `server`, then listener will start a TLS +/// handshake on connections. +/// +/// Requests will be dispatched to `server`. +/// +/// If `signal_running` is given, a signal is sent when the listener is ready +/// to receive connections. +/// +/// The listener will shut down after `true` is sent to `signal_exit`. This +/// will also initate closing of all currently open connections. The function +/// will return when both the listener and all connections are closed or after +/// then seconds. async fn single_http_listener( server: Arc, addr: SocketAddr, @@ -270,8 +309,22 @@ async fn single_http_listener( } } + +//------------ single_unix_listener ------------------------------------------ + +/// Creates and listens on a Unix socket for the Krill API. +/// +/// The socket will listening on the given `addr`. It will dispatch requests +/// to `server`. +/// +/// If `signal_running` is given, a signal is sent when the listener is ready +/// to receive connections. +/// +/// The listener will shut down after `true` is sent to `signal_exit`. This +/// will also initate closing of all currently open connections. The function +/// will return when both the listener and all connections are closed or after +/// then seconds. #[cfg(unix)] -/// Runs an UNIX listener on a single socket. async fn single_unix_listener( server: Arc, path: std::path::PathBuf, @@ -395,6 +448,55 @@ async fn single_unix_listener( } } + +//------------ exit_signalled ------------------------------------------------ + +/// Returns when an exit signal was received. +/// +/// This non-Unix implementation returns when the equivalent of a Ctrl+C is +/// received. +#[cfg(not(unix))] +async fn exit_signalled() { + tokio::signal::ctrl_c().await +} + +/// Returns when an exit signal was received. +/// +/// Returns when either a SIGINT or SIGTERM was received. +/// +#[cfg(unix)] +async fn exit_signalled() { + use tokio::signal::unix::{SignalKind, signal}; + + let mut sigterm = match signal(SignalKind::terminate()) { + Ok(sig) => sig, + Err(err) => { + error!("Failed to install SIGTERM handler: {err}."); + return; + } + }; + let mut sigint = match signal(SignalKind::interrupt()) { + Ok(sig) => sig, + Err(err) => { + error!("Failed to install SIGINT handler: {err}."); + return; + } + }; + + tokio::select! { + _ = sigterm.recv() => { + info!("Received SIGTERM. Shutting down."); + } + _ = sigint.recv() => { + info!("Received SIGINT. Shutting down."); + } + } +} + + +//------------ Helper functions ---------------------------------------------- + +/// Writes the process ID to the configured PID file or dies trying. fn write_pid_file_or_die(config: &Config) { if let Err(e) = file::save( process::id().to_string().as_bytes(), config.pid_file() @@ -405,6 +507,7 @@ fn write_pid_file_or_die(config: &Config) { } } +/// Checks that all the configured test directories are present or dies. fn test_data_dirs_or_die(config: &Config) { test_data_dir_or_die("tls_keys_dir", config.tls_keys_dir()); test_data_dir_or_die("repo_dir", config.repo_dir()); @@ -416,6 +519,9 @@ fn test_data_dirs_or_die(config: &Config) { } } +/// Checks that the given directory can be written to. +/// +/// Does so by writing to a file “test” and deleting it thereafter. fn test_data_dir_or_die(config_item: &str, dir: &Path) { let test_file = dir.join("test"); @@ -439,7 +545,10 @@ fn test_data_dir_or_die(config_item: &str, dir: &Path) { } } - +/// Writes an error message and a hint how to proceeed. +// +// XXX Doesn’t actually die? +// fn print_write_error_hint_and_die(error_msg: String) { eprintln!("{error_msg}"); eprintln!(); From 326ddb99e16b61c39dddf5ca371a4773fcf07f85 Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Thu, 26 Feb 2026 15:44:52 +0100 Subject: [PATCH 15/51] Partial manual merge from main. --- .github/workflows/ci.yml | 6 +- CONTRIBUTING.md | 20 +- Cargo.lock | 509 +++++-- Cargo.toml | 17 +- Changelog.md | 49 +- Dockerfile | 2 +- doc/manual/source/man/krillc.rst | 11 + doc/manual/source/trust-anchor.rst | 19 +- src/api/ca.rs | 23 +- src/cli/client.rs | 17 +- src/cli/options/parents.rs | 23 + .../crypto/signing/dispatch/krillsigner.rs | 3 +- .../crypto/signing/signers/pkcs11/context.rs | 22 +- .../crypto/signing/signers/pkcs11/signer.rs | 7 +- src/config.rs | 3 +- src/server/bgp/rotoapi.rs | 1313 ----------------- src/server/ca/aspa.rs | 2 +- src/server/ca/certauth.rs | 25 +- src/server/ca/child.rs | 5 +- src/server/ca/keys.rs | 1 + src/server/ca/publishing.rs | 15 +- src/server/ca/roa.rs | 2 +- src/server/mq.rs | 3 - src/server/scheduler.rs | 23 +- 24 files changed, 606 insertions(+), 1514 deletions(-) delete mode 100644 src/server/bgp/rotoapi.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8d43eaf69..4eedd9985 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,7 +46,7 @@ jobs: # Test against the oldest supported version. # Test against beta Rust to get early warning of any problems that might occur with the upcoming Rust release. # Order: oldest Rust to newest Rust. - rust: [1.85.0, stable, beta] + rust: [1.88.0, stable, beta] # Test with no features and all features. args: ["--no-default-features", "--all-features"] @@ -67,7 +67,7 @@ jobs: runs-on: ubuntu-22.04 strategy: matrix: - rust: [1.85.0, stable, beta] + rust: [1.88.0, stable, beta] features: ["hsm", "hsm,hsm-tests-kmip"] steps: - name: Checkout repository @@ -113,7 +113,7 @@ jobs: runs-on: ubuntu-22.04 strategy: matrix: - rust: [1.85.0, stable, beta] + rust: [1.88.0, stable, beta] features: ["hsm,hsm-tests-pkcs11"] steps: - name: Checkout repository diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fdec0a678..7ea01460d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -36,12 +36,12 @@ the relevant RFCs, and how they are related. ### Join the Community -We invite you to join the [RPKI mailing -list](https://lists.nlnetlabs.nl/mailman/listinfo/rpki) and/or [Discord -server](https://discord.gg/8dvKB5Ykhy). Please don't open a GitHub issue for a -question. Instead, follow the discussion on the mailing list and Discord and ask -questions there before you start sending patches. We prefer public discussions -over private ones, so everyone in the community can participate and learn. +We invite you to join the [RPKI community](https://community.nlnetlabs.nl) +and/or [Discord server](https://discord.gg/8dvKB5Ykhy). Please don't open a +GitHub issue for a question. Instead, follow the discussion on the mailing list +and Discord and ask questions there before you start sending patches. We prefer +public discussions over private ones, so everyone in the community can +participate and learn. ### License and copyright @@ -71,10 +71,7 @@ for Krill. This documentation is edited via text files in the [reStructuredText](http://www.sphinx-doc.org/en/stable/rest.html) markup language and then compiled into a static website/offline document using the open source [Sphinx](http://www.sphinx-doc.org) and -[ReadTheDocs](https://readthedocs.org/) tools. You can contribute to the -Krill user manual by sending patches via pull requests on the -[krill-manual](https://github.com/NLnetLabs/krill-manual) GitHub -source repository. +[ReadTheDocs](https://readthedocs.org/) tools. You can contribute to the [man page](https://github.com/NLnetLabs/krill/blob/main/doc/krill.1) by @@ -82,6 +79,9 @@ sending nroff formatted patches. ## Sharing Your Changes +Please contact us before you start coding. One-line fixes are generally fine, +but for new features and rewrites we want to be in the loop from the start. + We would like you to submit a [pull request on GitHub](https://github.com/NLnetLabs/krill/pulls). Please note that you can create a draft pull request to indicate that you're still working on something diff --git a/Cargo.lock b/Cargo.lock index 614cf2049..f23026f2e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -118,6 +118,28 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "aws-lc-rs" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9a7b350e3bb1767102698302bc37256cbd48422809984b98d292c40e2579aa9" +dependencies = [ + "aws-lc-sys", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.37.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b092fe214090261288111db7a2b2c2118e5a7f30dc2569f1732c4069a6840549" +dependencies = [ + "cc", + "cmake", + "dunce", + "fs_extra", +] + [[package]] name = "backoff" version = "0.4.0" @@ -189,12 +211,6 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - [[package]] name = "bitflags" version = "2.11.0" @@ -229,9 +245,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" dependencies = [ "find-msvc-tools", + "jobserver", + "libc", "shlex", ] +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + [[package]] name = "cfg-if" version = "1.0.4" @@ -244,6 +268,17 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +[[package]] +name = "chacha20" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "rand_core 0.10.0", +] + [[package]] name = "chrono" version = "0.4.44" @@ -309,12 +344,31 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831" +[[package]] +name = "cmake" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75443c44cd6b379beb8c5b45d85d0773baf31cce901fe7bb252f4eff3008ef7d" +dependencies = [ + "cc", +] + [[package]] name = "colorchoice" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + [[package]] name = "const-oid" version = "0.9.6" @@ -365,6 +419,15 @@ dependencies = [ "libc", ] +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + [[package]] name = "crc32fast" version = "1.5.0" @@ -404,23 +467,22 @@ dependencies = [ [[package]] name = "cryptoki" -version = "0.10.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "781357a7779a8e92ea985121bbf379a9adf0777f44ab6392efc6abd5aa9b67db" +checksum = "ff765b99fc49f3116c9a908484486a2b92fd73c48da45c3a69716471c6cc56c6" dependencies = [ - "bitflags 1.3.2", + "bitflags", "cryptoki-sys", "libloading", "log", - "paste", "secrecy", ] [[package]] name = "cryptoki-sys" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "753e27d860277930ae9f394c119c8c70303236aab0ffab1d51f3d207dbb2bc4b" +checksum = "f1fd850498411e4057f1cba79e6e2bc7cbe960544c1046ab46d4685c403a1121" dependencies = [ "libloading", ] @@ -432,7 +494,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "curve25519-dalek-derive", "digest", "fiat-crypto", @@ -558,6 +620,12 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + [[package]] name = "dyn-clone" version = "1.0.20" @@ -793,6 +861,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + [[package]] name = "futures-channel" version = "0.3.32" @@ -875,9 +949,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi", "wasip2", + "wasm-bindgen", ] [[package]] @@ -889,6 +965,7 @@ dependencies = [ "cfg-if", "libc", "r-efi", + "rand_core 0.10.0", "wasip2", "wasip3", ] @@ -1080,22 +1157,6 @@ dependencies = [ "tower-service", ] -[[package]] -name = "hyper-tls" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" -dependencies = [ - "bytes", - "http-body-util", - "hyper", - "hyper-util", - "native-tls", - "tokio", - "tokio-native-tls", - "tower-service", -] - [[package]] name = "hyper-util" version = "0.1.20" @@ -1366,6 +1427,38 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror 1.0.69", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom 0.3.4", + "libc", +] + [[package]] name = "js-sys" version = "0.3.88" @@ -1440,7 +1533,7 @@ dependencies = [ "percent-encoding", "pin-project-lite", "r2d2", - "rand 0.9.2", + "rand 0.10.0", "regex", "reqwest", "rpassword", @@ -1511,9 +1604,9 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" [[package]] name = "libc" -version = "0.2.182" +version = "0.2.180" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" +checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" [[package]] name = "libflate" @@ -1561,16 +1654,16 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" dependencies = [ - "bitflags 2.11.0", + "bitflags", "libc", "redox_syscall 0.7.1", ] [[package]] name = "linux-raw-sys" -version = "0.12.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" [[package]] name = "litemap" @@ -1593,6 +1686,12 @@ version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + [[package]] name = "maybe-async" version = "0.2.10" @@ -1627,23 +1726,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "native-tls" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2" -dependencies = [ - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - [[package]] name = "new_debug_unreachable" version = "1.0.6" @@ -1652,11 +1734,11 @@ checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" [[package]] name = "nix" -version = "0.30.1" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" +checksum = "225e7cfe711e0ba79a68baeddb2982723e4235247aefce1482f2f16c27865b66" dependencies = [ - "bitflags 2.11.0", + "bitflags", "cfg-if", "cfg_aliases", "libc", @@ -1738,7 +1820,7 @@ dependencies = [ "serde_json", "serde_path_to_error", "sha2", - "thiserror", + "thiserror 1.0.69", "url", ] @@ -1781,7 +1863,7 @@ dependencies = [ "serde_with", "sha2", "subtle", - "thiserror", + "thiserror 1.0.69", "url", ] @@ -1791,7 +1873,7 @@ version = "0.10.75" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" dependencies = [ - "bitflags 2.11.0", + "bitflags", "cfg-if", "foreign-types", "libc", @@ -1885,12 +1967,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "paste" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" - [[package]] name = "pbkdf2" version = "0.12.2" @@ -2040,13 +2116,69 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.31.0" +version = "0.39.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33" +checksum = "958f21e8e7ceb5a1aa7fa87fab28e7c75976e0bfe7e23ff069e0a260f894067d" dependencies = [ "memchr", ] +[[package]] +name = "quinn" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror 2.0.18", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" +dependencies = [ + "aws-lc-rs", + "bytes", + "getrandom 0.3.4", + "lru-slab", + "rand 0.9.2", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror 2.0.18", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.60.2", +] + [[package]] name = "quote" version = "1.0.44" @@ -2094,6 +2226,17 @@ dependencies = [ "rand_core 0.9.5", ] +[[package]] +name = "rand" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc266eb313df6c5c09c1c7b1fbe2510961e5bcd3add930c1e31f7ed9da0feff8" +dependencies = [ + "chacha20", + "getrandom 0.4.1", + "rand_core 0.10.0", +] + [[package]] name = "rand_chacha" version = "0.3.1" @@ -2132,13 +2275,19 @@ dependencies = [ "getrandom 0.3.4", ] +[[package]] +name = "rand_core" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba" + [[package]] name = "redox_syscall" version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.11.0", + "bitflags", ] [[package]] @@ -2147,7 +2296,7 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35985aa610addc02e24fc232012c86fd11f14111180f902b67e2d5331f8ebf2b" dependencies = [ - "bitflags 2.11.0", + "bitflags", ] [[package]] @@ -2158,7 +2307,7 @@ checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ "getrandom 0.2.17", "libredox", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -2212,9 +2361,9 @@ checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c" [[package]] name = "reqwest" -version = "0.12.28" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" +checksum = "ab3f43e3283ab1488b624b44b0e988d0acea0b3214e694730a055cb6b2efa801" dependencies = [ "base64 0.22.1", "bytes", @@ -2226,21 +2375,21 @@ dependencies = [ "http-body-util", "hyper", "hyper-rustls", - "hyper-tls", "hyper-util", "js-sys", "log", "mime", - "native-tls", "percent-encoding", "pin-project-lite", + "quinn", + "rustls", "rustls-pki-types", + "rustls-platform-verifier", "serde", "serde_json", - "serde_urlencoded", "sync_wrapper", "tokio", - "tokio-native-tls", + "tokio-rustls", "tower", "tower-http", "tower-service", @@ -2293,9 +2442,9 @@ dependencies = [ [[package]] name = "rpki" -version = "0.18.6" +version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98a043d99463db58c05283f5ae5d9ced858cc3483011747264e21f50b9201cdd" +checksum = "99010980abb0aee7a5c4b1c04d0f42f74f57ac168adb5d38a99b3517a4d72df9" dependencies = [ "base64 0.22.1", "bcder", @@ -2339,6 +2488,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + [[package]] name = "rustc_version" version = "0.4.1" @@ -2350,11 +2505,11 @@ dependencies = [ [[package]] name = "rustix" -version = "1.1.4" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" dependencies = [ - "bitflags 2.11.0", + "bitflags", "errno", "libc", "linux-raw-sys", @@ -2367,15 +2522,27 @@ version = "0.23.36" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b" dependencies = [ + "aws-lc-rs", "log", "once_cell", - "ring", "rustls-pki-types", "rustls-webpki", "subtle", "zeroize", ] +[[package]] +name = "rustls-native-certs" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework", +] + [[package]] name = "rustls-pemfile" version = "2.2.0" @@ -2391,15 +2558,44 @@ version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" dependencies = [ + "web-time", "zeroize", ] +[[package]] +name = "rustls-platform-verifier" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784" +dependencies = [ + "core-foundation 0.10.1", + "core-foundation-sys", + "jni", + "log", + "once_cell", + "rustls", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki", + "security-framework", + "security-framework-sys", + "webpki-root-certs", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" + [[package]] name = "rustls-webpki" version = "0.103.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" dependencies = [ + "aws-lc-rs", "ring", "rustls-pki-types", "untrusted", @@ -2411,12 +2607,6 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" -[[package]] -name = "ryu" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" - [[package]] name = "salsa20" version = "0.10.2" @@ -2510,9 +2700,9 @@ dependencies = [ [[package]] name = "secrecy" -version = "0.8.0" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bd1c54ea06cfd2f6b63219704de0b9b4f72dcc2b8fdef820be6cd799780e91e" +checksum = "e891af845473308773346dc847b2c23ee78fe442e0472ac50e22a18a93d3ae5a" dependencies = [ "serde", "zeroize", @@ -2524,7 +2714,7 @@ version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ - "bitflags 2.11.0", + "bitflags", "core-foundation 0.10.1", "core-foundation-sys", "libc", @@ -2639,18 +2829,6 @@ dependencies = [ "serde_core", ] -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - [[package]] name = "serde_with" version = "3.16.1" @@ -2689,7 +2867,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "digest", ] @@ -2866,7 +3044,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" dependencies = [ - "bitflags 2.11.0", + "bitflags", "core-foundation 0.9.4", "system-configuration-sys", ] @@ -2941,7 +3119,16 @@ version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl 2.0.18", ] [[package]] @@ -2955,6 +3142,17 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "thread_local" version = "1.1.9" @@ -3058,16 +3256,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "tokio-native-tls" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" -dependencies = [ - "native-tls", - "tokio", -] - [[package]] name = "tokio-rustls" version = "0.26.4" @@ -3151,7 +3339,7 @@ version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" dependencies = [ - "bitflags 2.11.0", + "bitflags", "bytes", "futures-util", "http", @@ -3429,7 +3617,7 @@ version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" dependencies = [ - "bitflags 2.11.0", + "bitflags", "hashbrown 0.15.5", "indexmap 2.13.0", "semver", @@ -3445,6 +3633,25 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-root-certs" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "804f18a4ac2676ffb4e8b5b5fa9ae38af06df08162314f96a68d2a363e21a8ca" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "winapi" version = "0.3.9" @@ -3546,6 +3753,15 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + [[package]] name = "windows-sys" version = "0.52.0" @@ -3582,6 +3798,21 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + [[package]] name = "windows-targets" version = "0.52.6" @@ -3615,6 +3846,12 @@ dependencies = [ "windows_x86_64_msvc 0.53.1", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" @@ -3627,6 +3864,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" @@ -3639,6 +3882,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -3663,6 +3912,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + [[package]] name = "windows_i686_msvc" version = "0.52.6" @@ -3675,6 +3930,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" @@ -3687,6 +3948,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" @@ -3699,6 +3966,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -3781,7 +4054,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" dependencies = [ "anyhow", - "bitflags 2.11.0", + "bitflags", "indexmap 2.13.0", "log", "serde", diff --git a/Cargo.toml b/Cargo.toml index 21364495c..9e8761d47 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ name = "krill" version = "0.15.1-dev" edition = "2024" -rust-version = "1.85" +rust-version = "1.88" authors = ["NLnet Labs "] description = "Resource Public Key Infrastructure (RPKI) daemon" homepage = "https://www.nlnetlabs.nl/projects/routing/krill/" @@ -40,26 +40,26 @@ log = "0.4" openssl = { version = "0.10", features = ["v110"] } percent-encoding = "2.3.1" pin-project-lite = "0.2.16" -rand = "0.9" +rand = "0.10" regex = { version = "1.11.1", default-features = false, features = [ "std" ] } -reqwest = { version = "0.12.23", features = ["json"] } -rpki = { version = "0.18.6", features = ["ca", "compat", "rrdp"] } +reqwest = { version = "0.13.2", features = ["json"] } +rpki = { version = "0.19.2", features = ["ca", "compat", "rrdp"] } rustls-pemfile = "2.2.0" serde = { version = "1.0", features = ["derive", "rc"] } serde_json = "1.0" tempfile = "3.19.1" tokio = { version = "1", features = [ "macros", "rt", "rt-multi-thread", "signal", "time" ] } -tokio-rustls = { version = "0.26", default-features = false, features = [ "ring", "logging", "tls12" ] } +tokio-rustls = { version = "0.26" } toml = "0.9.5" url = { version = "2.5.4", features = ["serde"] } uuid = { version = "1.16", features = ["serde", "v4"] } # Dependencies used by the "hsm" feature backoff = { version = "0.4.0", optional = true } -cryptoki = { version = "0.10", optional = true } +cryptoki = { version = "0.12", optional = true, features = [ "serde" ] } kmip = { version = "0.4.3", package = "kmip-protocol", features = [ "tls-with-openssl" ], optional = true } r2d2 = { version = "0.8.10", optional = true } -secrecy = { version = "0.8", features = ["serde"], optional = true } +secrecy = { version = "0.10.3", features = ["serde"], optional = true } # Dependencies used by the "multi-user" feature basic-cookies = { version = "0.1", optional = true } @@ -78,7 +78,7 @@ urlparse = { version = "0.7", optional = true } [target.'cfg(unix)'.dependencies] syslog = "7.0.0" -nix = { version = "0.30.1", features = ["user"] } +nix = { version = "0.31.1", features = ["user"] } [features] default = ["multi-user", "hsm"] @@ -102,7 +102,6 @@ hsm-tests-pkcs11 = ["hsm"] [dev-dependencies] tar = "0.4" stderrlog = "0.6" -tempfile = "3.19.1" urlparse = "0.7" # Make sure that Krill crashes on panics, rather than losing threads and diff --git a/Changelog.md b/Changelog.md index 39947ea6e..9b13beeab 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,15 @@ # Change Log -## Unreleased Next Version +## Unreleased next version + +Bug fixes + +Other changes + + +## 0.16.1-rc1 + +Released 2026-02-19. Breaking changes @@ -12,6 +21,8 @@ Breaking changes fields and added `bgp_riswhois_enabled`, `bgp_riswhois_v4_uri`, `bgp_riswhois_v6_uri`, and `bgp_riswhois_refresh_duration` fields, all of which are optional. ([#1329] +* Krill will now refuse to start if the config file contains unknown + options. ([#1322]) New @@ -20,6 +31,9 @@ New By default, only the `root` user is allowed with the `admin` role, but both allowed users and what role they are mapped to can be configure. ([#1322]) +* Added a `krillc parents refresh` command to allow refreshing the parents + of a single CA rather than having to do a bulk refresh which can take a + very long time if there are many CAs. ([#1353]) Bug fixes @@ -33,18 +47,51 @@ Bug fixes * Start sweeping the authenticator cache upon daemon startup. This merely reduces memory consumption of the cache. Expired authentication tokens were not used either way. ([#1337]) +* Fixed a bug introduced in 0.15.0 where CAs do to not clear fulfilled + certification requests causing them to re-request a certificate every + time they contact their parent. ([#1345]) +* Do not re-try syncing with a parent of a CA when that parent isn’t + known. ([#1349]) +* Fixed un-suspending child CAs: rather then re-publishing the previously + revoked certificate, a new certificate is now issued. ([#1341]) Other changes * The default config files don’t serve as config documentation any more. Rather, there is now a `krill.conf.5` manual page. This manual page is also included in the Krill manual. ([#1322]) +* The cryptography library used by the rustls TLS implementation has been + switched to aws-lc-rs. This has some consequences for packaging: +* Dropped packaging for Ubuntu 20.04 (Focal Fossa). ([#1359]) [#1322]: https://github.com/NLnetLabs/krill/pull/1322 [#1326]: https://github.com/NLnetLabs/krill/pull/1326 [#1329]: https://github.com/NLnetLabs/krill/pull/1329 [#1331]: https://github.com/NLnetLabs/krill/pull/1331 [#1337]: https://github.com/NLnetLabs/krill/pull/1337 +[#1341]: https://github.com/NLnetLabs/krill/pull/1341 +[#1344]: https://github.com/NLnetLabs/krill/pull/1344 +[#1349]: https://github.com/NLnetLabs/krill/pull/1349 +[#1353]: https://github.com/NLnetLabs/krill/pull/1353 +[#1359]: https://github.com/NLnetLabs/krill/pull/1359 + + +## 0.15.1 ‘Contains Adult Language’ + +Released 2026-01-19. + +Bug fixes + +* Fixed a bug introduced in 0.15.0 where CAs do to not clear fulfilled + certification requests causing them to re-request a certificate every + time they contact their parent. ([#1345]) + +Other changes + +* Updated dependencies. + +[#1345]: https://github.com/NLnetLabs/krill/pull/1345 +[#1346]: https://github.com/NLnetLabs/krill/pull/1346 ## 0.15.0 ‘But I Digress’ diff --git a/Dockerfile b/Dockerfile index 2729fc682..f53f91701 100644 --- a/Dockerfile +++ b/Dockerfile @@ -44,7 +44,7 @@ ARG MODE=build # ======== # # Only used when MODE=build. -ARG BASE_IMG=alpine:3.22 +ARG BASE_IMG=alpine:3.23 # CARGO_ARGS diff --git a/doc/manual/source/man/krillc.rst b/doc/manual/source/man/krillc.rst index ac7b200ad..de46050bf 100644 --- a/doc/manual/source/man/krillc.rst +++ b/doc/manual/source/man/krillc.rst @@ -342,6 +342,17 @@ Manage parents for a CA .. option:: -r , --response= Path to the RFC 8183 Child Request XML file + + .. subcmd:: refresh + + Refresh the parents of this CA + + + *OPTIONS* + + .. option:: -c , --ca= [env: KRILL_CLI_MY_CA] + + Name of the CA to control .. subcmd:: contact diff --git a/doc/manual/source/trust-anchor.rst b/doc/manual/source/trust-anchor.rst index 659a25312..865c6c7d7 100644 --- a/doc/manual/source/trust-anchor.rst +++ b/doc/manual/source/trust-anchor.rst @@ -377,6 +377,13 @@ endpoints for the TA certificate. --tal-rsync +.. code-block:: bash + + krillta signer init --proxy-id ./proxy-id.json \ + --proxy-repository-contact ./proxy-repo.json \ + --tal-https https://krillrepo.example.com/ta/ta.cer \ + --tal-rsync rsync://krillrepo.example.com/ta/ta.cer + Associate the TA Signer with the Proxy -------------------------------------- @@ -384,7 +391,7 @@ Get the TA Signer 'info' JSON file and save it: .. code-block:: bash - krillta signer show > ./signer-info.json + krillta signer --format json show > ./signer-info.json Then 'initialise' the signer associated with the TA Proxy. (we should @@ -423,9 +430,15 @@ Step 2: Add "online" as a child of "ta" .. code-block:: bash - krillc show --ca online --format json >./online.json + krillc --format json show --ca online >./online.json krillta proxy children add --info ./online.json >./res.xml +.. Note:: You can specify the address space (ASNs, IPv4, IPv6) covered by + your child CA using the followings parameters: + - ``--asn`` The ASN resources for the child [default: AS0-AS4294967295] + - ``--ipv4`` The IPv4 resources for the child [default: 0.0.0.0/0] + - ``--ipv6`` The IPv6 resources for the child [default: ::/0] + Step 3: Add "ta" as a parent of "online" .. code-block:: bash @@ -512,7 +525,7 @@ Save the TA Signer Response .. code-block:: bash - krillta signer last > ./response.json + krillta signer --format json last > ./response.json Upload the Signer Response diff --git a/src/api/ca.rs b/src/api/ca.rs index b35b8607a..33ac06828 100644 --- a/src/api/ca.rs +++ b/src/api/ca.rs @@ -279,7 +279,7 @@ pub struct CertInfo { /// The serial number of this certificate. /// - /// This is needed for revocatio. + /// This is needed for revocation. pub serial: Serial, /// The certifcate signing request for the certificate. @@ -1532,10 +1532,11 @@ impl ChildExchange { if agent == "local-child" { return true; } - else if let Some(version) = agent.strip_prefix("krill/") { - if let Ok(krill_version) = KrillVersion::from_str(version) { - return krill_version > KrillVersion::release(0, 9, 1); - } + else if + let Some(version) = agent.strip_prefix("krill/") + && let Ok(krill_version) = KrillVersion::from_str(version) + { + return krill_version > KrillVersion::release(0, 9, 1); } } false @@ -1900,22 +1901,22 @@ impl fmt::Display for ResourceClassKeysInfo { write!(f, "State: ")?; match &self { - ResourceClassKeysInfo::Pending(_) => write!(f, "pending")?, - ResourceClassKeysInfo::Active(_) => write!(f, "active")?, + ResourceClassKeysInfo::Pending(_) => writeln!(f, "pending")?, + ResourceClassKeysInfo::Active(_) => writeln!(f, "active")?, ResourceClassKeysInfo::RollPending(_) => { - write!(f, "roll phase 1: pending and active key")? + writeln!(f, "roll phase 1: pending and active key")? } ResourceClassKeysInfo::RollNew(_) => { - write!(f, "roll phase 2: new and active key")? + writeln!(f, "roll phase 2: new and active key")? } ResourceClassKeysInfo::RollOld(_) => { - write!(f, "roll phase 3: active and old key")? + writeln!(f, "roll phase 3: active and old key")? } } if let Some(key) = self.current_key() { let resources = &key.incoming_cert.resources; - writeln!(f, " Resources:")?; + writeln!(f, "Resources:")?; writeln!(f, " ASNs: {}", resources.asn())?; writeln!(f, " IPv4: {}", resources.ipv4())?; writeln!(f, " IPv6: {}", resources.ipv6())?; diff --git a/src/cli/client.rs b/src/cli/client.rs index 703fc2c01..7fbd6e0e2 100644 --- a/src/cli/client.rs +++ b/src/cli/client.rs @@ -538,6 +538,14 @@ impl KrillClient { ).await } + pub async fn parent_refresh( + &self, ca: &CaHandle, + ) -> Result { + self.post_empty( + ca_path(ca).into_iter().chain(once("sync/parents")), + ).await + } + pub async fn parent_details( &self, ca: &CaHandle, parent: &ParentHandle ) -> Result { @@ -1120,10 +1128,11 @@ impl TryFrom for ServerUri { } // Check for a five-character scheme. - if let Some(scheme) = value.as_bytes().get(0..8) { - if scheme.eq_ignore_ascii_case(b"https://") { - return Ok(Self::Http(value)) - } + if + let Some(scheme) = value.as_bytes().get(0..8) + && scheme.eq_ignore_ascii_case(b"https://") + { + return Ok(Self::Http(value)) } Err("unsupported URI scheme") diff --git a/src/cli/options/parents.rs b/src/cli/options/parents.rs index e72593fb3..6c5593069 100644 --- a/src/cli/options/parents.rs +++ b/src/cli/options/parents.rs @@ -23,6 +23,9 @@ pub enum Command { /// Add a parent to, or update a parent of a CA Add(Add), + /// Refresh the parents of this CA + Refresh(Refresh), + /// Show contact information for a parent of a CA Contact(CaContact), @@ -38,6 +41,7 @@ impl Command { match self { Self::Request(cmd) => cmd.run(client).await.into(), Self::Add(cmd) => cmd.run(client).await.into(), + Self::Refresh(cmd) => cmd.run(client).await.into(), Self::Contact(cmd) => cmd.run(client).await.into(), Self::Statuses(cmd) => cmd.run(client).await.into(), Self::Remove(cmd) => cmd.run(client).await.into(), @@ -104,6 +108,25 @@ impl Add { } +//------------ Refresh ------------------------------------------------------- + +#[derive(clap::Parser)] +pub struct Refresh { + #[command(flatten)] + ca: ca::Handle, +} + +impl Refresh { + pub async fn run( + self, client: &KrillClient + ) -> Result { + client.parent_refresh( + &self.ca.ca, + ).await + } +} + + //------------ CaContact ----------------------------------------------------- #[derive(clap::Args)] diff --git a/src/commons/crypto/signing/dispatch/krillsigner.rs b/src/commons/crypto/signing/dispatch/krillsigner.rs index 4d5a161e6..005a10167 100644 --- a/src/commons/crypto/signing/dispatch/krillsigner.rs +++ b/src/commons/crypto/signing/dispatch/krillsigner.rs @@ -368,12 +368,13 @@ impl KrillSigner { pub fn sign_rta( &self, rta_builder: &mut rta::RtaBuilder, + signing_time: Time, ee: Cert, ) -> CryptoResult<()> { let key = ee.subject_key_identifier(); rta_builder.push_cert(ee); rta_builder - .sign(&self.router, &key, None, None) + .sign(&self.router, &key, signing_time) .map_err(crypto::Error::signing) } diff --git a/src/commons/crypto/signing/signers/pkcs11/context.rs b/src/commons/crypto/signing/signers/pkcs11/context.rs index 6ee459d1a..3869dbdfd 100644 --- a/src/commons/crypto/signing/signers/pkcs11/context.rs +++ b/src/commons/crypto/signing/signers/pkcs11/context.rs @@ -28,7 +28,7 @@ use std::sync::OnceLock; use cryptoki::error::Error as Pkcs11Error; use cryptoki::{ - context::{CInitializeArgs, Info, Pkcs11}, + context::{CInitializeArgs, CInitializeFlags, Info, Pkcs11}, mechanism::Mechanism, object::{Attribute, AttributeType, ObjectHandle}, session::{Session, UserType}, @@ -166,7 +166,9 @@ impl Pkcs11Context { // this way of configuring the PKCS#11 token. // TODO: add a timeout around the call to initialize? - if let Err(err) = self.initialize(CInitializeArgs::OsThreads) { + if let Err(err) = self.initialize( + CInitializeArgs::new(CInitializeFlags::OS_LOCKING_OK) + ) { error!( "Failed to initialize PKCS#11 library '{}': {}", self.lib_file_name, err @@ -188,7 +190,12 @@ impl Pkcs11Context { self.initialized = false; // will continue even if the call fails. - self.finalize(); + if let Err(err) = self.finalize() { + error!( + "Error uninitializing PKCS#11 library '{}': {}", + self.lib_file_name, err + ); + } // remove the library we represent from the initialized set (as it // is no longer initialized), subsequent attempts to use the @@ -231,12 +238,13 @@ impl Pkcs11Context { res } - fn logged_cryptoki_call_with_take( + fn logged_cryptoki_call_with_take( &mut self, cryptoki_call_name: &'static str, call: F, - ) where - F: FnOnce(Pkcs11), + ) -> Result + where + F: FnOnce(Pkcs11) -> Result, { trace!("{}::{}()", self.lib_file_name, cryptoki_call_name); let ctx = self.ctx.take().unwrap(); // leave a None in the ctx member field @@ -254,7 +262,7 @@ impl Pkcs11Context { }) } - fn finalize(&mut self) { + fn finalize(&mut self) -> Result<(), Pkcs11Error>{ self.logged_cryptoki_call_with_take("Finalize", |cryptoki| { cryptoki.finalize() }) diff --git a/src/commons/crypto/signing/signers/pkcs11/signer.rs b/src/commons/crypto/signing/signers/pkcs11/signer.rs index 5a128844a..6486838e5 100644 --- a/src/commons/crypto/signing/signers/pkcs11/signer.rs +++ b/src/commons/crypto/signing/signers/pkcs11/signer.rs @@ -1389,9 +1389,9 @@ fn is_transient_error(err: &Pkcs11Error) -> bool { | Pkcs11Error::ParseInt(_) | Pkcs11Error::Utf8(_) | Pkcs11Error::NulError(_) + | Pkcs11Error::MissingSymbol(_) | Pkcs11Error::InvalidValue - | Pkcs11Error::PinNotSet - | Pkcs11Error::AlreadyInitialized => { + | Pkcs11Error::PinNotSet => { // The Rust `pkcs11` crate had a serious problem such as the // loaded library not exporting a required function or // that it was asked to initialize an already initialized library. @@ -1512,6 +1512,7 @@ fn is_transient_error(err: &Pkcs11Error) -> bool { cryptoki::error::RvError::TokenNotPresent => true, /* not present at the time the function was executed but might be later */ cryptoki::error::RvError::TokenNotRecognized => false, cryptoki::error::RvError::TokenWriteProtected => true, /* maybe the write protection is a transient condition? */ + cryptoki::error::RvError::UnknownErrorCode(_) => false, /* we don’t know ... */ cryptoki::error::RvError::UnwrappingKeyHandleInvalid => false, cryptoki::error::RvError::UnwrappingKeySizeRange => false, cryptoki::error::RvError::UnwrappingKeyTypeInconsistent => { @@ -1523,7 +1524,7 @@ fn is_transient_error(err: &Pkcs11Error) -> bool { cryptoki::error::RvError::UserPinNotInitialized => true, /* maybe the operator will initialize the PIN */ cryptoki::error::RvError::UserTooManyTypes => true, /* maybe some sessions are terminated while retrying permitting us to succeed? */ cryptoki::error::RvError::UserTypeInvalid => true, /* maybe the operator will fix the users type */ - cryptoki::error::RvError::VendorDefined => true, /* we have no way of knowing what this kind of failure is, maybe it is transient */ + cryptoki::error::RvError::VendorDefined(_) => true, /* we have no way of knowing what this kind of failure is, maybe it is transient */ cryptoki::error::RvError::WrappedKeyInvalid => false, cryptoki::error::RvError::WrappedKeyLenRange => false, cryptoki::error::RvError::WrappingKeyHandleInvalid => false, diff --git a/src/config.rs b/src/config.rs index 100637239..2d9f284ca 100644 --- a/src/config.rs +++ b/src/config.rs @@ -10,6 +10,7 @@ use std::{ use chrono::Duration; use log::{error, info, warn, LevelFilter}; +use rand::RngExt; use rpki::{ ca::idexchange::PublisherHandle, repository::x509::{Time, Validity}, @@ -725,7 +726,6 @@ impl IssuanceTimingConfig { let random_mins = if self.timing_publish_next_jitter_hours == 0 { 0 } else { - use rand::Rng; let mut rng = rand::rng(); rng.random_range(0..(60 * self.timing_publish_next_jitter_hours)) } as i64; @@ -1120,7 +1120,6 @@ impl Config { let random_seconds = if jitter_seconds == 0 { 0 } else { - use rand::Rng; let mut rng = rand::rng(); rng.random_range(0..jitter_seconds) }; diff --git a/src/server/bgp/rotoapi.rs b/src/server/bgp/rotoapi.rs deleted file mode 100644 index 9b2e05be4..000000000 --- a/src/server/bgp/rotoapi.rs +++ /dev/null @@ -1,1313 +0,0 @@ -//! The BGP analyser. - -use std::time::{Duration, Instant}; -use std::{error, fmt}; -use std::collections::HashMap; -use std::ops::Range; -use std::str::FromStr; -use intervaltree::IntervalTree; -use rpki::repository::resources::{Addr, AddressRange, Prefix, ResourceSet}; -use serde::{Deserialize, Serialize}; -use serde_json::Value; -use tokio::sync::Mutex; -use crate::api::bgp::{ - Announcement, BgpAnalysisEntry, BgpAnalysisReport, BgpAnalysisState, - BgpAnalysisSuggestion, ReplacementRoaSuggestion, -}; -use crate::api::roa::{ - AsNumber, ConfiguredRoa, Ipv4Prefix, Ipv6Prefix, RoaPayload, TypedPrefix, -}; - - -//------------ BgpAnalyser ------------------------------------------------- - -/// The BGP analyser to check the consequence of ROAs in the global BGP. -pub struct BgpAnalyser { - /// Should we actually use the BGP API. - bgp_api_enabled: bool, - - /// The base URI of the BGP API. - bgp_api_uri: String, - - /// The HTTP client to talk to the BGP API with. - client: reqwest::Client, - - /// The cache for the HTTP client responses - cache: Mutex>, - - /// The duration to keep responses cached. - cache_duration: Duration, -} - -impl BgpAnalyser { - /// Creates a new BGP analyser from the BGP API details. - pub fn new( - bgp_api_enabled: bool, - bgp_api_uri: String, - cache_duration: Duration, - ) -> Self { - BgpAnalyser { - bgp_api_enabled, - bgp_api_uri, - client: reqwest::Client::new(), - cache: Mutex::new(HashMap::new()), - cache_duration, - } - } - - /// Creates a BGP analyisis report for the given ROAs and resources. - pub async fn analyse( - &self, - roas: &[ConfiguredRoa], - resources_held: &ResourceSet, - limited_scope: Option, - ) -> BgpAnalysisReport { - let mut entries = Vec::new(); - - // Create a list of the ROAs that are contained in the held - // resources but not in the limited scope. Everything that is in - // neither goes directly into the `entries` as ‘not held.’ - let mut roas_held = Vec::new(); - for roa in roas { - if let Some(limit) = limited_scope.as_ref() { - if !limit.contains_roa_address( - &roa.roa_configuration.payload.as_roa_ip_address() - ) { - continue - } - } - - if resources_held.contains_roa_address( - &roa.roa_configuration.payload.as_roa_ip_address() - ) { - roas_held.push(roa.clone()); - } - else { - entries.push(BgpAnalysisEntry::roa_not_held(roa.clone())); - } - } - - if !self.bgp_api_enabled { - // Nothing to analyse. Push all ROAs as ‘no announcement info.’ - entries.extend( - roas_held.into_iter().map(|roa| { - BgpAnalysisEntry::roa_no_announcement_info(roa) - }) - ); - return BgpAnalysisReport::new(entries); - } - - // Now get all the necessary data from BGP API. - // - // Return early if this failed. - // - // If this succeeds, `scoped_announcements` will contain all - // announcements that overlap any of the IP address resources we - // are considering. - let scope = IpRange::from_resource_set( - match &limited_scope { - Some(limit) => limit, - None => resources_held, - } - ); - - let mut scoped_announcements: Vec = vec![]; - for block in scope.into_iter() { - let announcements = self.retrieve(block).await; - if let Ok(mut announcements) = announcements { - scoped_announcements.append( - announcements.as_mut()); - } - else { - entries.extend( - roas_held.into_iter().map(|roa| { - BgpAnalysisEntry::roa_no_announcement_info(roa) - }) - ); - return BgpAnalysisReport::new(entries); - } - } - - // Now create a prefix tree for all the configured ROAs: `roa_tree`. - let roa_tree = IpRangeStore::create( - roas_held.iter().map(|configured| { - let payload = configured.roa_configuration.payload; - (payload.prefix.into(), payload) - }) - ); - - // Now go over all announcements and determine their ROV status from - // our ROAs. Turn that into a prefix tree: `validated_tree`. - let validated: Vec = scoped_announcements - .into_iter() - .map(|a| roa_tree.validate_announcement(a)) - .collect(); - let validated_tree = IpRangeStore::create( - validated.iter().map(|v| (v.announcement.prefix.into(), v.clone())) - ); - - // Now we go over each individual configured ROA and check how it - // influenced the validated tree. - for roa in roas_held { - // Get all announcements covered by the ROA. - let covered = validated_tree.matching_or_more_specific( - roa.roa_configuration.payload.prefix - ); - - // Get all other ROAs that cover the prefix of this ROA. - let other_roas_covering_this_prefix: Vec<_> = roa_tree - .matching_or_less_specific( - roa.roa_configuration.payload.prefix - ) - .into_iter() - .filter(|other| roa.roa_configuration.payload != **other) - .cloned() - .collect(); - - // Get all ROAs that include this ROA. - let other_roas_including_this_definition: Vec<_> = - other_roas_covering_this_prefix - .iter() - .filter(|other| { - other.asn == roa.roa_configuration.payload.asn - && other.prefix.addr_len() - <= roa.roa_configuration.payload - .prefix.addr_len() - && other.effective_max_length() - >= roa.roa_configuration.payload - .effective_max_length() - }) - .cloned() - .collect(); - - let authorizes: Vec = covered - .iter() - .filter(|va| { - // VALID announcements under THIS ROA - // Already covered so it's under this ROA prefix - // ASN must match - // Prefix length must be allowed under this ROA (it - // could be allowed by another ROA and therefore - // valid) - va.validity == AnnouncementValidity::Valid - && va.announcement.prefix.addr_len() - <= roa.roa_configuration.payload - .effective_max_length() - && va.announcement.asn - == roa.roa_configuration.payload.asn - }) - .map(|va| va.announcement) - .collect(); - - let disallows: Vec = covered - .iter() - .filter(|va| { - let validity = va.validity; - validity == AnnouncementValidity::InvalidLength - || validity == AnnouncementValidity::InvalidAsn - }) - .map(|va| va.announcement) - .collect(); - - let authorizes_excess = { - let max_length = - roa.roa_configuration.payload.effective_max_length(); - let nr_of_specific_ann = authorizes - .iter() - .filter(|ann| ann.prefix.addr_len() == max_length) - .count() - as u128; - - nr_of_specific_ann > 0 - && nr_of_specific_ann - < roa.roa_configuration.payload - .nr_of_specific_prefixes() - }; - - if roa.roa_configuration.payload.asn == AsNumber::AS0 { - // see if this AS0 ROA is redundant, if it is mark it as - // such - if other_roas_covering_this_prefix.is_empty() { - // will disallow all covered announcements by - // definition (because AS0 announcements cannot exist) - let announcements = covered - .iter() - .map(|va| va.announcement) - .collect(); - entries.push(BgpAnalysisEntry::roa_as0( - roa, - announcements, - )); - } else { - entries.push(BgpAnalysisEntry::roa_as0_redundant( - roa, - other_roas_covering_this_prefix, - )); - } - } else if !other_roas_including_this_definition.is_empty() { - entries.push(BgpAnalysisEntry::roa_redundant( - roa, - authorizes, - disallows, - other_roas_including_this_definition, - )) - } else if authorizes.is_empty() && disallows.is_empty() { - entries.push(BgpAnalysisEntry::roa_unseen(roa)) - } else if authorizes_excess { - entries.push(BgpAnalysisEntry::roa_too_permissive( - roa, authorizes, disallows, - )) - } else if authorizes.is_empty() { - entries.push(BgpAnalysisEntry::roa_disallowing( - roa, disallows, - )) - } else { - entries.push(BgpAnalysisEntry::roa_seen( - roa, authorizes, disallows, - )) - } - } - - // Loop over all validated announcements and report - for v in validated.into_iter() { - match v.validity { - AnnouncementValidity::Valid => { - entries.push(BgpAnalysisEntry::announcement_valid( - v.announcement, - v.authorizing.unwrap(), /* always set for valid - * announcements */ - )) - } - AnnouncementValidity::Disallowed => { - entries.push( - BgpAnalysisEntry::announcement_disallowed( - v.announcement, - v.disallowing, - ), - ); - } - AnnouncementValidity::InvalidLength => { - entries.push( - BgpAnalysisEntry::announcement_invalid_length( - v.announcement, - v.disallowing, - ), - ); - } - AnnouncementValidity::InvalidAsn => { - entries.push( - BgpAnalysisEntry::announcement_invalid_asn( - v.announcement, - v.disallowing, - ), - ); - } - AnnouncementValidity::NotFound => { - entries.push( - BgpAnalysisEntry::announcement_not_found( - v.announcement, - ), - ); - } - } - } - - BgpAnalysisReport::new(entries) - } - - /// Returns suggestions for the given ROAs and resources. - pub async fn suggest( - &self, - roas: &[ConfiguredRoa], - resources_held: &ResourceSet, - limited_scope: Option, - ) -> BgpAnalysisSuggestion { - let mut suggestion = BgpAnalysisSuggestion::default(); - - // perform analysis - let entries = self - .analyse(roas, resources_held, limited_scope) - .await - .into_entries(); - for entry in &entries { - match entry.state() { - BgpAnalysisState::RoaUnseen => { - suggestion.stale.push(entry.configured_roa().clone()) - } - BgpAnalysisState::RoaTooPermissive => { - let replace_with = entry - .authorizes() - .iter() - .filter(|ann| { - !entries.iter().any(|other| { - other != entry - && other.authorizes().contains(*ann) - }) - }) - .map(|auth| RoaPayload::from(*auth)) - .collect(); - - suggestion.too_permissive.push( - ReplacementRoaSuggestion { - current: entry.configured_roa().clone(), - new: replace_with, - } - ); - } - BgpAnalysisState::RoaSeen | BgpAnalysisState::RoaAs0 => { - suggestion.keep.push(entry.configured_roa().clone()) - } - BgpAnalysisState::RoaDisallowing => { - suggestion.disallowing.push(entry.configured_roa().clone()) - } - BgpAnalysisState::RoaRedundant => { - suggestion.redundant.push(entry.configured_roa().clone()) - } - BgpAnalysisState::RoaNotHeld => { - suggestion.not_held.push(entry.configured_roa().clone()) - } - BgpAnalysisState::RoaAs0Redundant => { - suggestion.as0_redundant.push( - entry.configured_roa().clone() - ) - } - BgpAnalysisState::AnnouncementValid => {} - BgpAnalysisState::AnnouncementNotFound => { - suggestion.not_found.push(entry.announcement()) - } - BgpAnalysisState::AnnouncementInvalidAsn => { - suggestion.invalid_asn.push(entry.announcement()) - } - BgpAnalysisState::AnnouncementInvalidLength => { - suggestion.invalid_length.push(entry.announcement()) - } - BgpAnalysisState::AnnouncementDisallowed => { - suggestion.keep_disallowing.push(entry.announcement()) - } - BgpAnalysisState::RoaNoAnnouncementInfo => { - suggestion.keep.push(entry.configured_roa().clone()) - } - } - } - - suggestion - } - - /// Retrieves all announcements overlapping an IP range from BGP API. - async fn retrieve( - &self, - block: IpRange, - ) -> Result, BgpApiError> { - let mut announcements: Vec = vec![]; - - for prefix in block.to_prefixes() { - let resp = self.get_url(self.format_url(prefix)).await?; - match self.obtain_announcements(resp) { - Some(mut ann) => announcements.append(&mut ann), - None => return Err(BgpApiError::MalformedData), - } - } - - Ok(announcements) - } - - /// Formats the URL to retrieve announcements for the given prefix. - fn format_url(&self, prefix: TypedPrefix) -> String { - format!("{}/api/v1/prefix/{:?}/{}/search", - self.bgp_api_uri, prefix.ip_addr(), prefix.addr_len() - ) - } - - /// Fetches the URL and parses the returned JSON. - async fn get_url(&self, url: String) -> Result { - #[cfg(test)] - if url.starts_with("test") { - // When testing, the "test" URL is special. Also, unwrapping is - // fine. - let value = serde_json::from_str::(include_str!( - "../../../test-resources/bgp/bgp-api.json") - ).unwrap(); - let Value::Object(mut value) = value else { - panic!("not an object") - }; - return Ok(value.remove(url.as_str()).unwrap()) - } - - { - let mut local_cache = self.cache.lock().await; - - if let Some((time, value)) = &local_cache.get(&url) { - if time.elapsed() > self.cache_duration { - local_cache.remove(&url); - } else { - return Ok(value.clone()); - } - } - } - - let value: Value = self.client.get( - url.as_str() - ).send().await?.json().await?; - - self.cache.lock().await.insert( - url, (Instant::now(), value.clone()) - ); - Ok(value) - } - - /// Obtain the announcements from the JSON tree. - /// - /// Returns `None` if the JSON structure was in any way unexpected. - fn obtain_announcements(&self, json: Value) -> Option> { - let mut anns: Vec = Vec::new(); - if let Some(result_type) = json.get("result")?.get("type") { - if result_type.as_str()? == "empty-match" { - return Some(anns); - } - } - - let prefix_str = json.get("result")?.get("prefix")?.as_str()?; - for meta in json.get("result")?.get("meta")?.as_array()? { - self.parse_meta(meta, prefix_str, &mut anns)?; - } - if let Some(relations) = json.get("result")?.get("relations") { - for relation in relations.as_array()? { - if relation.get("type")?.as_str()? == "more-specific" { - for member in relation.get("members")?.as_array()? { - self.parse_member(member, &mut anns)?; - } - } - } - } - Some(anns) - } - - /// Parses the a single entry in the members array. - /// - /// Returns `None` if the JSON structure was in any way unexpected. - fn parse_member( - &self, member: &Value, anns: &mut Vec - ) -> Option<()> { - let prefix_str = member.get("prefix")?.as_str()?; - for meta in member.get("meta")?.as_array()? { - self.parse_meta(meta, prefix_str, anns)?; - } - Some(()) - } - - /// Parses the meta member of a result. - /// - /// Returns `None` if the JSON structure was in any way unexpected. - fn parse_meta( - &self, - meta: &Value, - prefix_str: &str, - anns: &mut Vec - ) -> Option<()> { - if meta.get("sourceType")?.as_str()? == "bgp" { - for asn in meta.get("originASNs")?.as_array()? { - // Strip off "AS" prefix - let asn = AsNumber::from_str(asn.as_str()?.get(2..)?).ok()?; - let prefix = TypedPrefix::from_str(prefix_str).ok()?; - - anns.push(Announcement { asn, prefix }); - } - } - Some(()) - } -} - - -//------------ ValidatedAnnouncement ----------------------------------------- - -/// A BGP announcement that has been validated agains ROAs. -#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -pub struct ValidatedAnnouncement { - /// The actual announcement. - pub announcement: Announcement, - - /// The RPKI validity status. - pub validity: AnnouncementValidity, - - /// The ROA payload that authorizes the announcement. - pub authorizing: Option, - - /// The ROA payload that disallows the announcement. - pub disallowing: Vec, -} - - -//------------ AnnouncementValidity ------------------------------------------ - -/// The RPKI validity of an announcement. -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -pub enum AnnouncementValidity { - /// The announcement is RPKI valid. - Valid, - - /// The announcement is RPKI valid because of the prefix length. - InvalidLength, - - /// The announcement is RPKI valid because of the originating ASN. - InvalidAsn, - - /// The announcement is not allowed. - Disallowed, - - /// The announcement is RPKI unknown. - NotFound, -} - - -//------------ IpRange ----------------------------------------------------- - -/// A range of IP addresses. -// -// We are using IPv4-mapped IPv6 addresses for IPv4 and can thus store -// everything as the `u128` of an IPv6 address. -#[derive(Clone, Debug)] -pub struct IpRange(Range); - -impl IpRange { - /// Returns the IPv4 (left) and IPv6 (right) ranges as a tuple. - pub fn from_resource_set( - set: &ResourceSet, - ) -> Vec { - let mut res = vec![]; - for block in set.ipv4().iter() { - res.push(IpRange(Range { - start: block.min().to_v4().to_ipv6_mapped().into(), - end: block.max().to_v4().to_ipv6_mapped().into(), - })) - } - for block in set.ipv6().iter() { - res.push(IpRange(Range { - start: block.min().to_v6().into(), - end: block.max().to_v6().into(), - })) - } - res - } - - /// Returns whether this range contains the other range. - fn contains(&self, other: &Range) -> bool { - self.0.start <= other.start && self.0.end >= other.end - } - - /// Returns whether this range is contained by the other range. - fn is_contained_by(&self, other: &Range) -> bool { - other.start <= self.0.start && other.end >= self.0.end - } - - /// Converts the range into a typed prefix. - pub fn to_prefixes(&self) -> Vec { - let is_ipv4 = - (self.0.start & 0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_0000_0000) == - 0x0000_0000_0000_0000_0000_FFFF_0000_0000; - - let mut min = self.0.start; - let mut max = self.0.end; - - if is_ipv4 { - // Krill stores IPv4 internally as an IPv4-mapped IPv6 address, - // rpki-rs stores IPv4 addresses in the top bytes, so that prefix - // handling works regardless of the IP type. - min <<= 96; - max <<= 96; - } - - let range = AddressRange::from(( - Addr::from_bits(min), - Addr::from_bits(max) - )); - - match is_ipv4 { - true => range.to_v4_prefixes() - .map(|x| TypedPrefix::from(Ipv4Prefix::from(x))).collect(), - false => range.to_v6_prefixes() - .map(|x| TypedPrefix::from(Ipv6Prefix::from(x))).collect() - } - } -} - -impl From for IpRange { - fn from(tp: TypedPrefix) -> Self { - match tp { - TypedPrefix::V4(pfx) => { - let (min, max) = Prefix::from(pfx).range(); - let start = min.to_v4().to_ipv6_mapped().into(); - let end = max.to_v4().to_ipv6_mapped().into(); - IpRange(Range { start, end }) - } - TypedPrefix::V6(pfx) => { - let (min, max) = Prefix::from(pfx).range(); - let start = min.to_v6().into(); - let end = max.to_v6().into(); - IpRange(Range { start, end }) - } - } - } -} - - -//------------ IpRangeStore --------------------------------------------- - -pub struct IpRangeStore { - tree: IntervalTree>, -} - -impl IpRangeStore { - pub fn create(items: impl IntoIterator) -> Self { - let mut values: HashMap, Vec> = HashMap::new(); - for (range, value) in items { - values.entry(range.0).or_default().push(value); - } - IpRangeStore { tree: values.into_iter().collect() } - } - - pub fn matching_or_more_specific( - &self, - range: impl Into, - ) -> Vec<&V> { - let range: IpRange = range.into(); - let mut res = vec![]; - for el in self.tree.query(range.0.clone()) { - if range.contains(&el.range) { - for v in &el.value { - res.push(v) - } - } - } - res - } - - pub fn matching_or_less_specific( - &self, - range: impl Into, - ) -> Vec<&V> { - let range: IpRange = range.into(); - let mut res = vec![]; - for el in self.tree.query(range.0.clone()) { - if range.is_contained_by(&el.range) { - for v in &el.value { - res.push(v) - } - } - } - res - } - - pub fn size(&self) -> usize { - self.tree.iter().count() - } - - pub fn all(&self) -> Vec<&V> { - self.tree - .iter() - .flat_map(|el| el.value.as_slice()) - .collect() - } -} - -impl IpRangeStore { - fn validate_announcement( - &self, announcement: Announcement - ) -> ValidatedAnnouncement { - let covering = self.matching_or_less_specific(announcement.prefix); - if covering.is_empty() { - return ValidatedAnnouncement { - announcement, - validity: AnnouncementValidity::NotFound, - authorizing: None, - disallowing: vec![], - } - } - - let mut invalidating = vec![]; - let mut same_asn_found = false; - let mut none_as0_found = false; - for roa in covering { - if roa.asn == announcement.asn { - if roa.prefix.matching_or_less_specific(announcement.prefix) - && roa.effective_max_length() - >= announcement.prefix.addr_len() - { - return ValidatedAnnouncement { - announcement, - validity: AnnouncementValidity::Valid, - authorizing: Some(*roa), - disallowing: vec![], - }; - } - else { - same_asn_found = true; - } - } - if roa.asn != AsNumber::AS0 { - none_as0_found = true; - } - invalidating.push(*roa); - } - - // Valid announcements already returned, we only have invalids left. - let validity = if same_asn_found { - AnnouncementValidity::InvalidLength - } - else if none_as0_found { - AnnouncementValidity::InvalidAsn - } - else { - AnnouncementValidity::Disallowed - }; - - ValidatedAnnouncement { - announcement, - validity, - authorizing: None, - disallowing: invalidating, - } - } -} - - -//============ Error Types =================================================== - -//------------ BgpApiError --------------------------------------------------- - -/// An error happened whil accessing the BGP API. -#[derive(Debug)] -pub enum BgpApiError { - /// The HTTP request failed. - Reqwest(reqwest::Error), - - /// Decoding the content failed. - Serde(serde_json::Error), - - /// The data was malformed. - MalformedData -} - -impl From for BgpApiError { - fn from(e: reqwest::Error) -> BgpApiError { - BgpApiError::Reqwest(e) - } -} - -impl From for BgpApiError { - fn from(e: serde_json::Error) -> BgpApiError { - BgpApiError::Serde(e) - } -} - -impl fmt::Display for BgpApiError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Self::Reqwest(err) => err.fmt(f), - Self::Serde(err) => err.fmt(f), - Self::MalformedData => f.write_str("malformed data") - } - } -} - -impl error::Error for BgpApiError { } - - -//============ Tests ========================================================= - -#[cfg(test)] -mod test { - use rpki::repository::resources::Prefix; - use crate::api::bgp::BgpAnalysisState; - use crate::api::roa::{ - Ipv4Prefix, Ipv6Prefix, RoaConfigurationUpdates - }; - use crate::commons::test::{configured_roa, roa_payload}; - use super::*; - - fn ann(s: &str) -> Announcement { - Announcement::from_str(s).unwrap() - } - - fn pfx(s: &str) -> TypedPrefix { - TypedPrefix::from_str(s).unwrap() - } - - fn range_pfx(s: &str) -> IpRange { - IpRange::from(pfx(s)) - } - - fn make_test_tree() -> IpRangeStore { - IpRangeStore::create( - [ - ann("10.0.0.0/24 => 64496"), - ann("10.0.1.0/24 => 64496"), - ann("10.0.0.0/23 => 64496"), - ann("10.0.0.0/20 => 64496"), - ann("10.0.0.0/16 => 64496"), - ].into_iter().map(|ann| (ann.prefix.into(), ann)) - ) - } - - #[tokio::test] - async fn analyse_bgp() { - let roa_too_permissive = configured_roa("10.0.0.0/22-23 => 64496"); - let roa_as0 = configured_roa("10.0.4.0/24 => 0"); - let roa_unseen_completely = configured_roa("10.0.3.0/24 => 64497"); - - let roa_not_held = configured_roa("10.1.0.0/24 => 64497"); - - let roa_authorizing_single = - configured_roa("192.168.1.0/24 => 64497"); - let roa_unseen_redundant = configured_roa("192.168.1.0/24 => 64498"); - let roa_as0_redundant = configured_roa("192.168.1.0/24 => 0"); - - let resources_held = - ResourceSet::from_strs("", "10.0.0.0/16, 192.168.0.0/16", "") - .unwrap(); - let limit = None; - - let analyser = BgpAnalyser::new( - true, "test".to_string(), Duration::default() - ); - - let report = analyser - .analyse( - &[ - roa_too_permissive, - roa_as0, - roa_unseen_completely, - roa_not_held, - roa_authorizing_single, - roa_unseen_redundant, - roa_as0_redundant, - ], - &resources_held, - limit, - ) - .await; - - let expected: BgpAnalysisReport = serde_json::from_str(include_str!( - "../../../test-resources/bgp/expected_full_report.json" - )) - .unwrap(); - - assert_eq!(report, expected); - } - - #[tokio::test] - async fn analyse_bgp_disallowed_announcements() { - let roa = configured_roa("10.0.0.0/22 => 0"); - - let roas = &[roa]; - let analyser = BgpAnalyser::new( - true, "test".to_string(), Duration::default(), - ); - - let resources_held = - ResourceSet::from_strs("", "10.0.0.0/8, 192.168.0.0/16", "") - .unwrap(); - let report = analyser.analyse(roas, &resources_held, None).await; - - assert!(!report.contains_invalids()); - - let mut disallowed = report - .matching_announcements(BgpAnalysisState::AnnouncementDisallowed); - disallowed.sort(); - - let disallowed_1 = ann("10.0.0.0/22 => 64496"); - let disallowed_2 = ann("10.0.0.0/22 => 64497"); - let disallowed_3 = ann("10.0.0.0/24 => 64496"); - let disallowed_4 = ann("10.0.2.0/23 => 64496"); - let mut expected = - vec![disallowed_1, disallowed_2, disallowed_3, disallowed_4]; - expected.sort(); - - assert_eq!(disallowed, expected); - - // The suggestion should not try to add the disallowed announcements - // because they were disallowed by an AS0 roa. - let suggestion = analyser.suggest(roas, &resources_held, None).await; - let updates = RoaConfigurationUpdates::from(suggestion); - - let added = &updates.added; - for announcement in disallowed { - assert!(!added.iter().any(|added_roa| { - let added_payload = added_roa.payload; - let announcement_payload = RoaPayload::from(announcement); - added_payload.includes(announcement_payload) - })); - } - } - - #[tokio::test] - async fn analyse_bgp_no_announcements() { - let roa1 = configured_roa("10.0.0.0/23-24 => 64496"); - let roa2 = configured_roa("10.0.3.0/24 => 64497"); - let roa3 = configured_roa("10.0.4.0/24 => 0"); - - let roas = vec![roa1, roa2, roa3]; - - let resources_held = - ResourceSet::from_strs("", "10.0.0.0/16", "").unwrap(); - - let analyser = BgpAnalyser::new( - false, "".to_string(), Duration::default() - ); - let table = analyser.analyse(&roas, &resources_held, None).await; - let table_entries = table.entries(); - assert_eq!(3, table_entries.len()); - - let roas_no_info: Vec = table_entries - .iter() - .filter(|e| e.state() == BgpAnalysisState::RoaNoAnnouncementInfo) - .map(|e| e.configured_roa().clone()) - .collect(); - - assert_eq!(roas_no_info, roas); - } - - #[tokio::test] - async fn make_bgp_analysis_suggestion() { - let roa_too_permissive = configured_roa("10.0.0.0/22-23 => 64496"); - let roa_redundant = configured_roa("10.0.0.0/23 => 64496"); - let roa_as0 = configured_roa("10.0.4.0/24 => 0"); - let roa_unseen_completely = configured_roa("10.0.3.0/24 => 64497"); - let roa_authorizing_single = - configured_roa("192.168.1.0/24 => 64497"); - let roa_unseen_redundant = configured_roa("192.168.1.0/24 => 64498"); - let roa_as0_redundant = configured_roa("192.168.1.0/24 => 0"); - - let roas = &[ - roa_too_permissive, - roa_redundant, - roa_as0, - roa_unseen_completely, - roa_authorizing_single, - roa_unseen_redundant, - roa_as0_redundant, - ]; - - let analyser = BgpAnalyser::new( - true, "test".to_string(), Duration::default() - ); - - let resources_held = - ResourceSet::from_strs("", "10.0.0.0/8, 192.168.0.0/16", "") - .unwrap(); - let limit = - Some(ResourceSet::from_strs("", "10.0.0.0/22", "").unwrap()); - let suggestion_resource_subset = - analyser.suggest(roas, &resources_held, limit).await; - - let expected: BgpAnalysisSuggestion = - serde_json::from_str(include_str!( - "../../../test-resources/bgp/expected_suggestion_some_roas.json" - )) - .unwrap(); - assert_eq!(suggestion_resource_subset, expected); - - let suggestion_all_roas_in_scope = - analyser.suggest(roas, &resources_held, None).await; - - let expected: BgpAnalysisSuggestion = - serde_json::from_str(include_str!( - "../../../test-resources/bgp/expected_suggestion_all_roas.json" - )) - .unwrap(); - - assert_eq!(suggestion_all_roas_in_scope, expected); - } - - #[test] - fn format_url() { - let analyser = BgpAnalyser::new( - true, "https://rest.bgp-api.net".to_string(), Duration::default() - ); - assert_eq!( - "https://rest.bgp-api.net/api/v1/prefix/192.168.0.0/16/search", - analyser.format_url(TypedPrefix::from(Ipv4Prefix::from( - Prefix::from_str("192.168.0.0/16").unwrap() - ))) - ); - assert_eq!( - "https://rest.bgp-api.net/api/v1/prefix/2001:db8::/32/search", - analyser.format_url(TypedPrefix::from(Ipv6Prefix::from( - Prefix::from_str("2001:db8::/32").unwrap() - ))) - ); - } - - #[tokio::test] - async fn retrieve_announcements() { - let analyser = BgpAnalyser::new( - true, "test".to_string(), Duration::default() - ); - - let ipv4s = "185.49.140.0/22"; - let ipv6s = "2a04:b900::/29"; - - let ranges = IpRange::from_resource_set( - &ResourceSet::from_strs("", ipv4s, "").unwrap() - ); - for range in ranges { - assert_eq!(3, analyser.retrieve(range).await.unwrap().len()); - } - - let ranges = IpRange::from_resource_set( - &ResourceSet::from_strs("", "", ipv6s).unwrap() - ); - for range in ranges { - assert_eq!(6, analyser.retrieve(range).await.unwrap().len()); - } - } - - #[tokio::test] - async fn retrieve_broken_announcements() { - let analyser = BgpAnalyser::new( - true, "test".to_string(), Duration::default() - ); - - let ipv4s = "1.1.1.1/32, 3.3.3.3/32, 4.4.4.4/32"; - let set = ResourceSet::from_strs("", ipv4s, "").unwrap(); - - let ranges = IpRange::from_resource_set(&set); - - for range in ranges { - assert!(analyser.retrieve(range).await.is_err()); - } - } - - #[tokio::test] - async fn analyse_nlnet_labs_snapshot() { - let analyser = BgpAnalyser::new( - true, "test".to_string(), Duration::default() - ); - - let ipv4s = "185.49.140.0/22"; - let ipv6s = "2a04:b900::/29"; - let set = ResourceSet::from_strs("AS211321", ipv4s, ipv6s).unwrap(); - - let roas = &[ - configured_roa("2a04:b906::/48-48 => 0"), - configured_roa("2a04:b907::/48-48 => 0"), - configured_roa("185.49.142.0/24-24 => 0"), - configured_roa("2a04:b900::/30-32 => 8587"), - configured_roa("185.49.140.0/23-23 => 8587"), - configured_roa("2a04:b900::/30-30 => 8587"), - configured_roa("2a04:b905::/48-48 => 14618"), - configured_roa("2a04:b905::/48-48 => 16509"), - configured_roa("2a04:b902::/32-32 => 16509"), - configured_roa("2a04:b904::/48-48 => 211321"), - configured_roa("2a04:b907::/47-47 => 211321"), - configured_roa("185.49.142.0/23-23 => 211321"), - configured_roa("2a04:b902::/48-48 => 211321"), - configured_roa("185.49.143.0/24-24 => 211321"), - ]; - - let report = analyser.analyse(roas, &set, None).await; - - let entry_expect_roa = |x: &str, y| { - let x = x.to_string(); - assert!(report.entries().iter().any(|s| - s.state() == y && - s.configured_roa().to_string() == x - )); - }; - - let entry_expect_ann = |x: &str, y: u32, z: BgpAnalysisState| { - let x = x.to_string(); - assert!(report.entries().iter().any(|s| - s.state() == z && - s.announcement().asn == AsNumber::from_u32(y) && - s.announcement().prefix.to_string() == x - )); - }; - - entry_expect_roa( - "2a04:b907::/48-48 => 0", BgpAnalysisState::RoaAs0Redundant - ); - entry_expect_roa( - "185.49.142.0/24-24 => 0", BgpAnalysisState::RoaAs0Redundant - ); - entry_expect_roa( - "2a04:b900::/30-30 => 8587", BgpAnalysisState::RoaRedundant - ); - entry_expect_roa( - "2a04:b905::/48-48 => 14618", BgpAnalysisState::RoaUnseen - ); - entry_expect_roa( - "2a04:b902::/32-32 => 16509", BgpAnalysisState::RoaUnseen - ); - entry_expect_ann( - "2a04:b907::/48", 211321, - BgpAnalysisState::AnnouncementInvalidLength - ); - entry_expect_ann( - "185.49.142.0/24", 211321, - BgpAnalysisState::AnnouncementInvalidLength - ); - entry_expect_roa( - "2a04:b902::/48-48 => 211321", BgpAnalysisState::RoaUnseen - ); - entry_expect_roa( - "185.49.143.0/24-24 => 211321", BgpAnalysisState::RoaUnseen - ); - } - - #[test] - fn validate_announcement() { - let roas = [ - roa_payload("10.0.0.0/23-24 => 64496"), // authorizing 1 - roa_payload("10.0.0.0/23 => 64498"), // authorizing 2 - roa_payload("10.1.0.0/23-24 => 64496") // irrelevant, - ]; - - let ann_v1 = ann("10.0.0.0/24 => 64496"); - let ann_v2 = ann("10.0.1.0/24 => 64496"); - let ann_ia = ann("10.0.0.0/24 => 64497"); - let ann_il = ann("10.0.1.0/24 => 64498"); - let ann_nf = ann("10.2.0.0/24 => 64497"); - - let roas = IpRangeStore::create( - roas.into_iter().map(|roa| (roa.prefix.into(), roa)) - ); - - fn assert_state( - ann: &Announcement, - roas: &IpRangeStore, - expected: AnnouncementValidity, - ) { - assert_eq!(roas.validate_announcement(*ann).validity, expected); - } - - assert_state(&ann_v1, &roas, AnnouncementValidity::Valid); - assert_state(&ann_v2, &roas, AnnouncementValidity::Valid); - assert_state(&ann_ia, &roas, AnnouncementValidity::InvalidAsn); - assert_state(&ann_il, &roas, AnnouncementValidity::InvalidLength); - assert_state(&ann_nf, &roas, AnnouncementValidity::NotFound); - } - - #[test] - fn range_contains() { - let more_specific_1 = range_pfx("10.0.0.0/24"); - let more_specific_2 = range_pfx("10.0.1.0/24"); - let test_pfx = range_pfx("10.0.0.0/23"); - - assert!(test_pfx.contains(&more_specific_1.0)); - assert!(test_pfx.contains(&more_specific_2.0)); - } - - #[test] - fn typed_prefix_tree_more_specific() { - let tree = make_test_tree(); - let search = TypedPrefix::from_str("10.0.0.0/23").unwrap(); - assert_eq!(3, tree.matching_or_more_specific(search).len()); - - let search = TypedPrefix::from_str("10.0.2.0/24").unwrap(); - assert_eq!(0, tree.matching_or_more_specific(search).len()); - } - - #[test] - fn typed_prefix_tree_less_specific() { - let tree = make_test_tree(); - let search = TypedPrefix::from_str("10.0.0.0/23").unwrap(); - assert_eq!(3, tree.matching_or_less_specific(search).len()); - - let search = TypedPrefix::from_str("10.0.0.0/24").unwrap(); - assert_eq!(4, tree.matching_or_less_specific(search).len()); - - let search = TypedPrefix::from_str("10.0.0.0/16").unwrap(); - assert_eq!(1, tree.matching_or_less_specific(search).len()); - - let search = TypedPrefix::from_str("10.0.0.0/15").unwrap(); - assert_eq!(0, tree.matching_or_less_specific(search).len()); - } - - #[test] - fn set_to_ranges() { - let asns = "AS65000-AS65003, AS65005"; - let ipv4s = "10.0.0.0/8, 192.168.0.0"; - let ipv6s = "::1, 2001:db8::/32"; - let set = ResourceSet::from_strs(asns, ipv4s, ipv6s).unwrap(); - - let ranges = IpRange::from_resource_set(&set); - assert_eq!(4, ranges.len()); - } - - #[test] - fn to_prefixes() { - let ipv4s = "10.0.0.0/8, 192.168.0.0-192.168.2.255"; - let ipv6s = "::1-::3, 2001:db8::/32"; - let set = ResourceSet::from_strs("", ipv4s, ipv6s).unwrap(); - - let ranges: Vec> = - IpRange::from_resource_set(&set) - .into_iter() - .map(|x| x.to_prefixes()) - .collect(); - - assert_eq!(1, ranges[0].len()); - assert_eq!(2, ranges[1].len()); - assert_eq!(2, ranges[2].len()); - assert_eq!(1, ranges[3].len()); - } - - #[tokio::test] - async fn correct_analysis() { - let analyser = BgpAnalyser::new( - true, "test".to_string(), Duration::default() - ); - - let ipv4s = "103.60.200.0/22, 103.160.116.0/23, 103.184.174.0/23, 103.233.208.0/22, 122.99.120.0/22, 202.14.148.0/24, 203.0.80.0/24, 203.1.68.0/23"; - let ipv6s = ""; - let set = ResourceSet::from_strs("AS1000-1200", ipv4s, ipv6s).unwrap(); - - let roas = &[ - configured_roa("103.60.200.0/22-22 => 211321"), - configured_roa("103.160.116.0/23-23 => 211321"), - configured_roa("103.184.174.0/23-23 => 211321"), - configured_roa("103.233.208.0/22-22 => 211321"), - configured_roa("122.99.120.0/22-22 => 211321"), - configured_roa("202.14.148.0/24-24 => 211321"), - configured_roa("203.0.80.0/24-24 => 211321"), - configured_roa("203.1.68.0/23-23 => 211321"), - ]; - - for block in IpRange::from_resource_set(&set) { - assert!(analyser.retrieve(block).await.is_ok()); - } - - let report = analyser.analyse(roas, &set, None).await; - - let mut expected_results = vec![ - BgpAnalysisState::RoaUnseen, - BgpAnalysisState::RoaDisallowing, - BgpAnalysisState::RoaDisallowing, - BgpAnalysisState::RoaDisallowing, - BgpAnalysisState::RoaDisallowing, - BgpAnalysisState::RoaDisallowing, - BgpAnalysisState::RoaDisallowing, - BgpAnalysisState::RoaDisallowing, - BgpAnalysisState::AnnouncementInvalidAsn, - BgpAnalysisState::AnnouncementInvalidAsn, - BgpAnalysisState::AnnouncementInvalidAsn, - BgpAnalysisState::AnnouncementInvalidAsn, - BgpAnalysisState::AnnouncementInvalidAsn, - BgpAnalysisState::AnnouncementInvalidAsn, - BgpAnalysisState::AnnouncementInvalidAsn, - BgpAnalysisState::AnnouncementInvalidAsn, - BgpAnalysisState::AnnouncementInvalidAsn, - BgpAnalysisState::AnnouncementInvalidAsn, - BgpAnalysisState::AnnouncementInvalidAsn, - BgpAnalysisState::AnnouncementInvalidAsn, - BgpAnalysisState::AnnouncementInvalidAsn, - BgpAnalysisState::AnnouncementInvalidAsn, - BgpAnalysisState::AnnouncementInvalidAsn, - BgpAnalysisState::AnnouncementInvalidAsn, - ]; - expected_results.reverse(); - for entry in report.entries() { - assert_eq!(expected_results.pop().unwrap(), entry.state()); - } - } -} diff --git a/src/server/ca/aspa.rs b/src/server/ca/aspa.rs index b342b3c5c..4cbf55f6f 100644 --- a/src/server/ca/aspa.rs +++ b/src/server/ca/aspa.rs @@ -333,7 +333,7 @@ impl AspaObjects { ); object_builder.set_issuer( Some(certified_key.incoming_cert().subject.clone())); - object_builder.set_signing_time(Some(Time::now())); + object_builder.set_signing_time(Time::now()); object_builder }; diff --git a/src/server/ca/certauth.rs b/src/server/ca/certauth.rs index b929283c0..db30411cb 100644 --- a/src/server/ca/certauth.rs +++ b/src/server/ca/certauth.rs @@ -230,7 +230,9 @@ impl Aggregate for CertAuth { } CertAuthCommandDetails::ChildUnsuspend(child) => { - self.process_child_unsuspend(&child) + self.process_child_unsuspend( + &child, krill.config(), krill.signer() + ) } @@ -1586,6 +1588,8 @@ impl CertAuth { fn process_child_unsuspend( &self, child_handle: &ChildHandle, + config: &Config, + signer: &KrillSigner, ) -> KrillResult> { let child = self.get_child(child_handle)?; @@ -1613,11 +1617,18 @@ impl CertAuth { > Time::now() + Duration::days(1) && child.resources.contains(&suspended.resources) { - // certificate is still fit for publication, so move - // it back to issued - cert_updates.unsuspended.push( - suspended.to_converted() - ); + // reissue a new certificate because the old one is on + // the CRL. + self.append_child_certify( + child_handle.clone(), + &suspended.resources, + rcn.clone(), + suspended.csr_info.clone(), + suspended.limit.clone(), + config, + signer, + &mut res, + )?; } else { // certificate should not be published as is. Remove @@ -2628,7 +2639,7 @@ impl CertAuth { // submitted keys) and add the cert for (_rcn, ee) in rc_ee.into_iter() { let ee_key = ee.subject_key_identifier(); - signer.sign_rta(&mut rta_builder, ee)?; + signer.sign_rta(&mut rta_builder, Time::now(), ee)?; signer.destroy_key(&ee_key)?; } diff --git a/src/server/ca/child.rs b/src/server/ca/child.rs index 03190464e..46f098bf4 100644 --- a/src/server/ca/child.rs +++ b/src/server/ca/child.rs @@ -181,7 +181,7 @@ pub struct ChildCertificates { #[serde(alias = "inner")] issued: HashMap, - /// The certificates for suspeneded child CAs. + /// The certificates for suspended child CAs. #[serde( skip_serializing_if = "HashMap::is_empty", default = "HashMap::new" @@ -396,6 +396,9 @@ pub struct ChildCertificateUpdates { pub suspended: Vec, /// The certificats that have been unsuspended. + /// + /// This is no longer used as of Krill 0.16.0, but kept because it is in + /// stored state. #[serde(skip_serializing_if = "Vec::is_empty", default)] pub unsuspended: Vec, } diff --git a/src/server/ca/keys.rs b/src/server/ca/keys.rs index 317aecca6..a74056219 100644 --- a/src/server/ca/keys.rs +++ b/src/server/ca/keys.rs @@ -78,6 +78,7 @@ impl CertifiedKey { /// Updates the certificate received for the key. pub fn set_incoming_cert(&mut self, cert: ReceivedCert) { + self.request = None; self.incoming_cert = cert } diff --git a/src/server/ca/publishing.rs b/src/server/ca/publishing.rs index 9098006ae..d74255129 100644 --- a/src/server/ca/publishing.rs +++ b/src/server/ca/publishing.rs @@ -1321,16 +1321,13 @@ impl KeyObjectSet { } } + // Since Krill 0.16 suspended certificates will reissued rather than + // unsuspended, so this does nothing anymore except for migrations. for cert in &cert_updates.unsuspended { - self.revocations.remove(&cert.revocation()); let published_object = PublishedObject::for_cert_info(cert); - if let Some(old) = self - .published_objects - .insert(cert.name.clone(), published_object) - { - // this should not happen, but just to be safe. - self.revocations.add(old.revoke()); - } + self.published_objects.insert( + cert.name.clone(), published_object + ); } for suspended in &cert_updates.suspended { @@ -1743,7 +1740,7 @@ impl ManifestBuilder { mft_uri, ); object_builder.set_issuer(Some(signing_cert.subject.clone())); - object_builder.set_signing_time(Some(Time::now())); + object_builder.set_signing_time(Time::now()); signer.sign_manifest(mft_content, object_builder, &aki)? }; diff --git a/src/server/ca/roa.rs b/src/server/ca/roa.rs index 0ebaa8405..95a84489c 100644 --- a/src/server/ca/roa.rs +++ b/src/server/ca/roa.rs @@ -868,7 +868,7 @@ impl Roas { object_builder.set_issuer( Some(certified_key.incoming_cert().subject.clone()) ); - object_builder.set_signing_time(Some(Time::now())); + object_builder.set_signing_time(Time::now()); Ok(signer.sign_roa( roa_builder, object_builder, &certified_key.key_id() diff --git a/src/server/mq.rs b/src/server/mq.rs index 0351262e0..8608bb8b0 100644 --- a/src/server/mq.rs +++ b/src/server/mq.rs @@ -293,7 +293,6 @@ impl TaskQueue { } impl TaskQueue { pub fn pop(&self) -> Option<(Box, serde_json::Value)> { - trace!("Try to get a task off the queue"); match self.q.claim_scheduled_pending_task() { Err(e) => { // Log error and return nothing. @@ -305,11 +304,9 @@ impl TaskQueue { None } Ok(None) => { - trace!("No pending task found."); None } Ok(Some((key, value))) => { - trace!("found task: {key}"); Some((key, value)) } } diff --git a/src/server/scheduler.rs b/src/server/scheduler.rs index 7c982390d..348f25b38 100644 --- a/src/server/scheduler.rs +++ b/src/server/scheduler.rs @@ -13,7 +13,7 @@ use crate::{ api::ca::Timestamp, commons::{ crypto::dispatch::signerinfo::SignerInfo, - error::FatalError, + error::{Error, FatalError}, eventsourcing::{Aggregate, AggregateStore, WalStore, WalSupport}, storage::Ident, version::KrillVersion, @@ -355,11 +355,22 @@ fn sync_parent( Err(e) => { let next = krill.config().requeue_remote_failed(); - error!( - "Failed to synchronize CA '{ca}' with its parent \ - '{parent}'. Will reschedule to: '{next}'. Error: {e}" - ); - Ok(TaskResult::Reschedule(next)) + if let Error::CaParentUnknown(..) = e { + warn!( + "CA '{ca}' tried to sync with parent '{parent}'. + Parent is unknown. Not rescheduling. Error: {e} + " + ); + + Ok(TaskResult::Done) + } + else { + error!( + "Failed to synchronize CA '{ca}' with its parent \ + '{parent}'. Will reschedule to: '{next}'. Error: {e}" + ); + Ok(TaskResult::Reschedule(next)) + } } Ok(true) => { let next = krill.config().ca_refresh_next(); From d4716c06f26315984126ad0e685a351573ac9852 Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Thu, 26 Feb 2026 16:30:36 +0100 Subject: [PATCH 16/51] Second part of manual merge from main. --- Cargo.toml | 2 +- Dockerfile | 2 +- doc/krill.1.gz | Bin 871 -> 875 bytes doc/krill.conf.5.gz | Bin 9582 -> 9613 bytes doc/krillc.1.gz | Bin 3054 -> 3076 bytes doc/krillta.1.gz | Bin 1705 -> 1713 bytes doc/krillup.1.gz | Bin 867 -> 874 bytes doc/manual/source/index.rst | 10 +- pkg/rules/packages-to-build.yml | 4 - src/api/history.rs | 30 +++--- .../crypto/signing/signers/kmip/signer.rs | 9 +- .../crypto/signing/signers/pkcs11/signer.rs | 9 +- src/commons/eventsourcing/store.rs | 11 +- src/commons/file.rs | 27 ++--- src/commons/queue.rs | 6 +- src/commons/storage/backends/disk.rs | 20 ++-- src/commons/storage/backends/memory.rs | 6 +- src/commons/storage/ident.rs | 6 +- src/config.rs | 30 +++--- .../auth/providers/openid_connect/provider.rs | 45 ++++---- src/daemon/http/dispatch/cas.rs | 19 ++-- src/daemon/start.rs | 35 +++---- src/server/bgp/analyser.rs | 11 +- src/server/bgp/riswhois.rs | 12 +-- src/server/ca/certauth.rs | 51 ++++----- src/server/ca/child.rs | 9 +- src/server/ca/manager.rs | 99 +++++++++--------- .../ca/upgrades/pre_0_10_0/migration.rs | 9 +- .../ca/upgrades/pre_0_14_0/migration.rs | 9 +- src/server/pubd/access.rs | 40 ++++--- src/server/pubd/rrdp.rs | 86 +++++++-------- src/server/taproxy.rs | 18 ++-- src/upgrades/mod.rs | 15 +-- 33 files changed, 313 insertions(+), 317 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9e8761d47..706491466 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] # Note: some of these values are also used when building Debian packages below. name = "krill" -version = "0.15.1-dev" +version = "0.17.0-dev" edition = "2024" rust-version = "1.88" authors = ["NLnet Labs "] diff --git a/Dockerfile b/Dockerfile index f53f91701..24de6418e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -135,7 +135,7 @@ RUN chmod a+x /tmp/out/bin/* # # The previous build stage from which binaries are copied is controlled by the # MODE ARG (see above). -FROM alpine:3.18 AS final +FROM ${BASE_IMG} AS final # Copy binaries from the 'source' build stage into the image we are building COPY --from=source /tmp/out/bin/* /usr/local/bin/ diff --git a/doc/krill.1.gz b/doc/krill.1.gz index 6f4cbc7b54858c4b4ebd6fae55b29596e5455236..308c42161a462d80c75ef5886ba3eacd452d2c6e 100644 GIT binary patch literal 875 zcmV-x1C;z9iwFqfW}s;R18Z_=Y-}zu0F_lykK#5Ee)q2!p{m+d!3SLLTCLQFyf;R_nCBnT<8z(CZLp~n!?_n!cfY=-9&~O$$%7C$M zOnU>VGE{C*;SMVp`ppbSemBBe8}}$oAr(uKZ$^+L>I-FTgKN#&6z@ETb=+^GZ_dPB z%v`epd%ZbapK*@PImc_pyE>-x`^kJ2^x-dRg4*B>;}FH*tE1H`RW34oDaD!GPSNj1 z7Y^6f-E=hJm-&$=M3WHTV!S$rc661!QOt|CJ!rWp+`ko(3V2IG6AE2@hTcOooy~gC z8`1Y?+`#x_AI8J+M>2_^0{PuCe{_r*=1BlS-#-s?#W>d)& z9`FEDl^bD?69HGz@?m-nnZT`90V4i=zF5Z7nDJI;nvLPt^?Uw-aa%J!`Hj&J>Hl;c zPb&&Y@=t+Ak}u&48M<~-D@*1&oN_;j@1p5)HC@a@e5e7W1wm7b*auysf{woD2zO|l z%wV&R}dXW^h5W*P++QINg=apvGr8;d}F@D5%2E_4R>c+?Ncx17Zo zHlO*gK3qfY?@UvhiVf)*Sdwc?SFZD5J>$eYZ+_Z{=d|c#D6y)ken}69)msE_(@WCA z`vU(D=hqg;Uzh3c$}Kz_a^6S$qli)UkUQgr{T5~+8LP8?3W^fYzr3CL}mvH8sR z^Gs}P@ZRbAKy6q$HXe?C{P*vF{1eiL({=Bmq?s0z6~S=Wo6l+dS*QlTfe`D})s) zG{Rv4rO|6Jm^#zst}$55@tfl;e3SuW-C!+LPpSfy>(#i$8v1^+z{t-gxY5Qv3A2)l zW!WzgWSRO(5$oVuur|ay&tV=W2;>rDJGH0A5 z;;(Xojlo;SAxXh!N252YTx9rKiZihXMSqJfY_6@lJDlOs<^Mqz!6@n##E(RJ}o zF)voT*K$+1doLm-@ScDsBszb9&SMgf#~tWI^u5O%qNodl{@{kpK96pA)GKgH#-ovS z-C;a^fXVP_#Qyd+WIagG>c->#NzBi$ZcB^=b~EbMi| zOrxN{8du?fhp>i(HlP)PHn47LCpR@A;)>{qnxt`P6uRIci6mNfmBfj?<}j~}ZdMgY zLSHtrc3ICiX3$mmbikt7qUl1*J{N~sTd_np#B*w-@8tvCe?pKY{ij2FO`JSu;ILb1KqD>WZB1K38>V zL}RT=x%$&P*v#myEYQ+!7?yz3z5Z_upWy%D{MF+4viZGRsDpJ8$e*>* z2!`Wy`r??JB;9>hBKMBy#NotiPqS$~0eSd&{xD70WbFHSES5HS@20aaN&M+y&IbMH xkMIBf=dY74oUWof?$h(LxZFdb^Tx^A(%YoMqq(EAhQ_~)KL9`+SUyY#003ibxT^pF diff --git a/doc/krill.conf.5.gz b/doc/krill.conf.5.gz index 917173f0bb9e026f6653c1cee308a5545d8db9ad..d686ee2f173e2b46af19a9ecd237025de82bff11 100644 GIT binary patch literal 9613 zcmV;8C34yyiwFqfW}s;R18Z_=Y-}!LZ*FETH2}0+d2`#ymH!`~qHB`fvMVBVOp>it z#i>gE+eT-8Uwle5bB- zu1fu2)Pu=QrE8O_c~vY`W$x;#N$RFD>AiWbqZlvG)~a3@^|nZw+Gb9j#Aj#m@6}T0 zyv3@rwW;EW|KydbTz!UTBb%otuSc1AGMPG|hp9QGryosORP~jv9&8>>CciyZlS%#= zE0%ogOC(F^=B~ZMb9wuG$9sEMeu+lW_QR7eJ8qutx_R~`iaj3V>$mT&@0mXF22QH1 z%y|^6k9Uf&!>=u>?1gZX6g$>>ivI3&&*6ou&pnvx({+GTeSF97%N|?D;7O6C-$=~8 z+#U&RxmkVdjzHjB7Br0L{SWH9_aDa}KE#(d*MI#^efJ0a^H(!dXMa3WXD4TW#N|nR z`XBMhC^b*GcwgBp`!0&_eo)sJSMQ=nx{i|~pR378@l$n)EBnwuIc$;htK;kYk2h~W zUEYsxuA|tMk-V5dmu5!QE2AHwM~$=jgVHK1YE{ftQPO$`y0a1`ysMhq%*5)xy%Euk zu+zL~@{|@#=Hp{iC&wLlllkrB-(&UG%ypC1c*oT`%d{MU(*z=C>Zo2+#)Q+)X)B#B zZ9c7wN0VclBf914qJj!fo67PnjdO83D;}oxx-|Z@%Z1J#j8k<%C`3IJ)QXM+$hs^u z3*_}2rocmC9n1;ft6*JXbp^2`2#E-_T^hA6nuwR@AXIL!82cxD6yR96XM}pv zmDRJ%I3`~-e8@1(i)yKBI{yCV>O-u?xpE8W$C0u%ZRV`2nOGWx`nfTA0y@p3N3))c zo^;k2SVmj9BV}~5P}tSd>#JO4HV4ySAi-d#bY%!)Adelg!Wm{+S~LJI`nf4;0`UW% z<@x$xpG;2HR2GPsB*k*6CnIM{Ep|AwLMR4>( z{meO7r8eFpaEJn=@;1TVI1S9LiF%&1<2kWG=`>X)eu%N@__J>6#k6sza$jgupomwh zAr~MiU|bOGnaPS35jRMK%^?Laf(bK1Mf;ZMNq0*tN{14e9XW#)p! zfDeHv2uty96!z18$*a}Ef>U_9WFZrw~XbEkAgLq5! zfP7HEjL1pinVQmw-DFUVtyEE^?DSw4Zf?mU+y%DS61efj_?AcuC2+3{;1RVT{+gV$ zDOP`36KJf;n-A6L8SEAQoW{|Q2AV{I0w4rH;@YDzC9a5Irmk6XCa=<;TeRsyZhzjo zf5fkzpYsL0#yj}7&ee6lgdyY{150l4TjkAi)|#n6a+2y<$B*w%PEN?gfV^Vdm?Amqls|DaHAMWpO?-V_agPCti+8uzgt*1xs zTs8w)LS#q3)hmzI>z?0YZ4GZhW?%r>^062tm65y7WOJA&`#}1Wn5wpOHYO3ICod?} zd9p?0y48?^-fN_l?9XELS2E7IUV@SkQdkCvN~&u;O>K4TTy~5t;?gYn{J{g-kDq|( zphyin8l;f0GtXcl;PVr6^Zw#E7&E&mW?e5+o_d> zhLPhm>=?QLI_w?M-uuYbL7iI1mUsv>Glg`x%rsykrppqRL}IbGf}KSmx9hxkKrkI7 zx~@U$0=mC2I%T&W8S*_VTQ9!CQ!*P*7E%PouE~)*lOQyx*HO{5&U;oo$7*1a?hqDT z|Lw!yJJXaF3?kt{1}gGKTnx^Zi2*{vyGRr;vpN44b`0LsCOtzc#CS)@c=JVvY|v8? z+m3>k)c!DW`?(L$gkEq5#Knj#rA;^xf`^APu}^zhyE~Qj=BuxDFX?-e+vlk($XQQK zPQa%@@qb(x_K8^Xlag10x)Ztl+XDx}o&bL%e*^AKju6RlkVSe1v^7|gPe!ui8a8j~ z%^R&*n0B9)UMmhb6OK&)B#DM&*%2XqnJhan1APdxZ=gw_1x3lFhTuy4*{6ww&;fFO z3D_K0O)fFBkDTdc4t6Md@X>`A16F#bMyk9J90@6Tw(f{0hhv0 zcqCIJd$GABU1a3R)ZlTcLBzyoj)Wvnu7Qs3YgPpQ zd3LbFq0Ky;>XmL%xN9V^uhqrvn4=E>B3|Q%Vg)Z)dHp6jaAro?PgAnH$9r*Ko z$P-8}V;=wym@o7C*ewtW@cKhgNBr$0j(x!i3-yK}^yY3sG9CgJU;uYUL8?~w6&cry z9QskISN9jM2MnLXWxziE9bp|p(HTy~Gej*A7z#{s-m5MspQXc*uput>abQ}o*YVbA z`>@Hs{pmN8^R!4t%>7YK4@a6G{`nBEv`r8YmmxlcloV`N-$F?;O*Xf+)tO~qO92Z0 zwM9WCdq-SJ%G03~{RrxjYY{4ha zQ_hc%C+B3NGurB$N)pG!r#20sAdUkPENse<{)Qw=#K0>v=W$dNy}aO{NJPx%eZHC- z@%gkjdu-j+ki4&IBl%#NP59275o?at6v&%ADw>2+#?zY_3%#p|X zaD&d>aQog+P(H+jqKX*_b%{Gr1j3}+Wx2^cM}(8}1>asxY_<+4At0d0!Z2;bjfbi> zAkUa$n_=UMtD2BW&+b>m``64qUrXg+M2eraHwj7Bq=+mQoUWIYBz_!ov6reN2pvPE z9`OeO6?^~6<#^6y(hUM#)KtcCL<8p5)u+3AMl)9|Hy$1?9Snp}(t1QZB#n`;s);m= zL$#KIK`?;nAhk&!b+g0+XVzPsKex{U&J~p+CA=#Fx>z>T$)n4(0~8YT!5GH9c=Xfw zh($;YWvd6StxBh+vQJVhnHL0<+bm1oLak?wI*b0)Q_(-`(jGsZ9;@xYUg6Q}z3ZxZ z^2gu*=h?1xM1Lm`dnN%^nx@WC?YGId(%Uz0y@4M>oA%Jb{5Ix+?*j zsw%4Pl2&wAlt^?}shKqoa>;Ej{HiORaHWO|S~`7#qmA@~CjImVXt0v(!(g4_W^a); z3BmZX!)r<8sU6@wgtWdP7vcs{4 zhf2WAIj@lx<#;}LMoP_XEH-ugh+u0U&#gi9*SD3@^r{QQtbN|f*_15qNPZ6^BoV~Z zu|w<-N`5VJVE5hd~D zG!;9cRZ%_Sps1HNQCG@q3q=Pu*SJeXz-N;*^&$CQKzesUKw_5z=_iz)&UIolTd(_M z^ehdJhE((qx9HfH3Dz|Yv&cH}GFlt3czlZe zkr&eA)JZLlkvIeA(uL%7rChrv;GcYLpIMFE7=&WTRFTBmo`<9WZr?tI*+ zrCsa`-(1x6`*p?dwmcvCGCws?8B#4xlKjb5MZP343|;mPJEYRww%|F7>BZZt@%8ln z=KZ^C&#TrC56f6cN=%3yDqvhJ5xaFglkd-{MnrU^ZUcAL8JdUz3yrR{FcM1`Dh(x4 zcr3MZ(3UhrH3YZg^d%*gviYN`S8#5TZ~Auj{PgVm-?OzkpNw9=C}lk<@e-C46)ERr zggnQY$!MmjS+bstfOwf5RQl(V=+L5*fjqorh z)`c@aab~S<@E9qhe;V3gQnMNILNelTwrm?VJ*>FhvVKt0?udDi=nN|SEQKG&aV(FV&g%Xh^{ea-xWB%A zWi*McKK*BoI7E^*Mfe4Y@@{&EKJAjMjp?kSiZ(Q@^{kvY2lq;9=ilIPkZTKU#X`j@ zD~d+Jf$I`NZ)sH2bi&rMgCZor^ zSmnop;B-KEmWJM>p3DqbX@~mEKe$?cZEk8GZEisOwuRv|J|W*;W|lbe-*-3H9J)w% z6;vAT2eNX|PY>zffMe^}*%Y)Xg^-lN=CCI6roAIUD0NWo;`a+wA#tKDXDog-g%)WT znHwjW1+r6{{!6^t*v*-w;&%1Fz9WJs8o4mNc(e_VeJS_O7rrQQs2U~;&qFE@M&>wt z2Zl)FIB1C&mf+Kqk1j(4YC7FTefZ8gmmO5b={q;S>Uc3tVIfpFD^ ziB6-EMgBk)XJTAK71xdx<{4@96ahS91UxNG>(6e(uAxS>ohC@qezAXg^5z1@OPxVV zW$%bAC!TYBEy$Mi>-+u0Dyg)_!1!89F{sKva_Y; z*+xG#L=lYReZm%Ph8PsQH_jz$z+3R{j6;_?GHg`IYU+X-r3pB+-bq*@%d#cx1qB4Z zP$>U~_3B8$SEye}a+kU!vhi9j7%Ci6QIw4hz|e}!f8J#~9L5xt{a+C`4-@DoNB}rF znlgzsiGOG400S6z3B0?gNCv42u`=R6SYUEuO++Ps0WjDO|CnIx0FuUT~z? zr)3J(k9xinZ~O8@;^)qklCH+vWc@-mxy)@wXj2HyJ28MOIuic^fwi6|k;oYVS_H)RL>9zJn3PZ8L;WOO!P+ z+<)T*2OOi4A{9#Jo!f|LT7moit!tO1ol@SkBYw*;1rlV5HMZN!EkH{efpo3N`^}by z<{qq*NyfZTn`rr9%1@|80Wa6h<@l}$eeCcoAL=mv;y}ezwJrl9nrYkDayEiIb3yD& z>d;AHno( z(E8q?ExZ)kejCJ`EF21R#LE66%(UlML6#u}dhO7xVGv1wiMqJZCwe|EkJ=by_kD>b zHlBsJply(Dd(SW^?U#Iswsm*wl}(e=Zr_RP1|ZSr9mvM541W0%2}R!wJ=mUgJz3bh zQbI$y`#NI?VNA&WpYhuk;m`UMCr5=m(b z(lCnLsY=4*%;7tOb>u4|ds|H5gB%=v?hUh=H&mzzol$C9uBnjq5N|A`q>zG*{#w=` zvN!aw*I)hmF8u9Yf18R2hGqKuNHt~3|K*8n80|slcChn+wo)L_?UxpmFG($hDc7b> zJ)-W4o-f*Hu1|g6R;ZA7`0lE&CrD+X%hP&`A|3#jb1zy1s32|g{gD8x#9jfq7__i^ zQ$4=)x_@81I~g(aj}-qVH2qJZKmQC&;0vMt0l-{dY_&DJlM@{dqhFrax;u(~2cFY! z4D(9(MG_-R&V~Ylnm&9PKY! zX_w1yJec9FD7Zf0_}N1VgC5QgR|3G-V#GvmI!zpfAQSOMnuStYP>9fPNisolST&)^n7eFrC#Py33aT#;(F)PN4@LL4oSe?iR7+bwOm68>F&#AEpK`K zXwjfU#dq3LNa)VA-@xQ%1*4EZOh#n;+YdS@EcA1~Vb+S$Vjd}wB)bb8{NYUFVz`K+ zjCN4E`&f>Wss5^^e4(cEJxl3sh)vCN39DmknTPQ(bh{DGJgmV5{mRUH(WT$G_u&P9 zaQYXyf9Rz4kk;M-U#KY_C*`)Q+wHm zcW9}+7ONjaH?EgZ=3I2VyZf*cz}{rdKS?@C$POoQ9l^rTOnj}bl|P6+7g`x=B-S*mEU9WJ z@p2Q#xVnE5Rf4H{2!A<~-fh3X&`)^3KM98%&=1~A3H|`@FhaIe+xd+hRl{8BU`_PY{W)0bu}0bWU@BTj(B`h%l$E8N>6!0r3XyWgCib_CGM z-%?L!@!1I*F8WLWo-NQrCO&1d;Y`PyJBAdaCh}|CKYE(IRbqjJ@-wJ)bL1**e^M|f3$osC|{urJ|}bo#`X|W z9~c7qD7EaBn5hvEaUVbd956Ib=?ey?J(a3BlWOtpzS`ADgW*xg1k3@S2yzrY{-a_q z3$oW}oMo(3jTVTHBm>7T03e4Qpayf-yb1r)0LB29GRV^X4+5Gzmjq(3B)q>G-?A9c zT%hA`3QV3<>yiMuzr5{tia2)3+mAPTW?N$N$e%}3h}c#=oyKQJ#Y>lrThHZ z+YcnNz3y&MWHRLJiskm5*um3~8~+n)&-gxn;lM*iezvDxmh>U5`|8d?yEb-yPgHIG zue+=5ZQMA*-}x(u0tIZqu_Rj7*@q&4?{aO8q?f~;kuSM`pd`v-^g@B8{L&yr|3m*_ ze@W+=nO!bLN}`0TuV|1DKAYT^nc3ahnP(p69B6ePA$za%vCcCA2r7z*-@?x`GJw@= zl=ph!pwGW}|LzVg^?K*IR2?9;60Q=1rFi21l`ML6nixy0NPcR~0 z3FC&K_!|1>w?0E~*DJcfVq2u$vPeg%BxWST!C-T~buih6!O{AFJ`{6-6)7;8d?;X9 zB}z^s?$O4)&is>$LD#w|r)L)PN>5z)C02Tk502Q^V z>D#3+XYfw_kS5A!!6kJJg=Y0u6l?JLdb34|KG*4)N*|QG8*rH>un@eJ{_&IW%@`lG zYk_wLus(5I%nb?l0wclxMbPb5A5^6}3gMb{qyBXA+Q01nQIRcMjK94ms`4a-@Q4mj zh=?%{hj0Vv+VS@a2sTI}Av%ixvO2gIQbuoZwV&K3FT#1UNQy98 z*?O{%2!K19@gy%77%OYlu88*?HIJG+l)SB|kk# z0y-6Vt{0b=ecF}x78T1INVzptQb(MasMbvz+`JXRNs|mtz4%E>?iF4Fw@Sb+53zyp zIlwG*gq!Ljj&LoStmmHX!8RqpTH6v0>CC*~Jj5%Hc6ri3JO*GZGGuNL|I+759SdQv zkE{}9J97v5{>M8hB;c%4Im6mvy-baqTGU5Zb>Q9%q)b|BBaEH4@NTOn0k`vT>uz>d zCHzdD={$bGEmi9n<(6A1&(+NnH+#OIOANmhli%CDF0cwXy!8+A^=88}tvI>ANBm9# z3wQ)eEC{-@H~Z1^b*W%mqci66)u5F`jS#{`ejj3MMP7RI(2r&N)j#XZ7@mlfrIjok zKWpETZfC5Mr!f&EB@|bffwSuKH>9_fP4S6vscl|+`4ly!WRCOo*uWLoEwxpa$dTs2 zAkec04y%!hy7Bhm`Qy^qPNciUf(^O9_Xt;Y9J>n*uKNM)LWn*gYRC!KFT4%0&i?PW zAy)}Iwjth7T5LmE8impQeHP!--Vh>coln!bWgNIw2w}+`#u76CO3Bf8%mAtR?m@adnF_j>e3XdZ(XY2^VQ_W2t|v+!zxNEvsUAc%_vT9~?TTeU0hFh|n!X|`U@ zn2EuYQk;bM_EbyG`8NAV?WB)dq0c>NC5D;3Ry_&A%%IjJxI!Hy%NZR&4zrxZG;+{} z($J>VG+)W9cHvB2kZ8uxbpKm;9Q;RAnGOxSQ}Ne(-L4j#BVnR-G_cok+-$LJGLT#B zollwld*F`FWj7=h&LfUi>|9$~{IErevZy?jiA@;ht2tT3dL=27e1Tj}rVpbCxgxyl zZaY~tPKTs%y;m3on`@|tTeys$it1GkSrn`pjtuCL%f^Nz0XZW{%1LCJax&oz!wH-U z9LN&8I4v(wF6m3q*F9A3RJvWWJWCck_C5rPWjGwhX{#-E>$LcoJVq!QiK)qkbuCTP zrXx>5Rx8>&mYRG8*_4U&shnyuEK<+%TcPwg8uOXCVq_NsT^;GYyzt!VBMfz*2>HEG zwkK|IiwGUAUMryZX_aKcZ$Z9T{8*KvIWXQ>^qqh8lG3To3{lgT(vdKLbL50P>F_a* zXLTdv-?D3ii0KC1s_g$Xi0bC+Khr7apIip=_UTJVX6LMso7j6--r7(W^UYnFQlW2= zlUh=fxEc+rLf7gpHEHkb2(D?q2dq9E4Q(t6=aaFaN^Iydc!_y$4D%2Z!K=p@1!ml6 z2Al573#xzHTnXJE#x*5KGXvkFkXYG5oI{3p-}n#zeAgyrK)jyoi_%CJ({Su(Y8clix1$ zq8~qg_rd2E1bqa7fcLesKw9Jgw{#2zA2sG0e8A?-TMWJeD=_S85yGon5{FMgJPR?b zP-c|X(A*gE@Ws^RrRdsjD)I0b=i~Ebw4!U5^7A>8(lme2HL<=UC0EAzdM#4G^(LYx zN9%VEOIMTd9HsvVNTK_IDQy=q!rpNyYILI{P`Ab;H^n#(q~+~)bfn2wW0 zlQq=@Vq{J_cMQu`)jveZoC$N-@4lT#t**x)OtBP+TNRsf+iE1{rhJq*N(*^)n1wKz z4DG>9%gh-h*Z!DY?w&t3qaVrPW|Yl9!Xa2nSv+2kVh45MmF7`G9uc1(t{EO_jSK}w zKJy;R!0M!z7LIe6?KI64n66gB5=loH`RNb#fVq)>0(ooq08=_;HX#mYK36C&Kx`N@ z1vNd}kvf~PXX-fORhlOpfj6i|iB2MLX6G4 zgL#aB+aS0BAEDe|HcD&NG9ixYL@wX)642t$liM0GaiBe^>s0!$E4x zuS?YL;@HV=nZj`r;9KN|5ER$W)yrn;>CypTIb`jI=T7H2RW=kbizbuZ0vGCanerD9 zggcX?WMOl5ZREis;4RL+wp|ooWP-Kf9W_Y_0Jl*$N12&3c5XSwB*9DWunz{6^>U<~ zYOX-fGnHRBU8Py&3`E}gX_YcHn&1ee0>EVOyt|ufa_l=pD85$ax9aomAr`Tx zfY0_7LMI|84U$u+MW$aG65yq9IiQXwi65lhYR6LRq3{Pl%+l2}Jgk(zBY?T$m<5zV zV(1JZEy=LI?feO%V^w|IDI)s+oLYC}?-F#Y{Dn*TSowebS@GElmg%O+G4nWkw;%*I z+A~vgv}nH7P-c{;$^De)eNS+>w+N_veY*iu=l@KQFV*A*;wRFtK=4AWs$Ujehg#Ub{0`K zbNt=P@fKAn_U!XeyQ#e_zudOTZJlpcpx;$4`&yPER07W9II6e2!cK^N2kB3CG4|@) z4*VBSO2m5?7q2|+igVrFUW;#XNKkFBKOn2G^5qM8lf8oDzBeKenbIRLi*HL~r8qZJ zX-V-MBC*$yq_3YW+mnOcE~f|Ww`GOZupxDa{%TQW+h4Ua2G(l9=v|p(fMH3*w4R^z zHuICsLpeOmyLWUP@|8n<%nuY^N$<@;;EnI?1p_<6#iWqbxYQBt47*n{*40t$k)EfK zc~@wG8+!(`7Jt(*RDfxzGE^be283TSxT0arRGt^=}v|=e_@7N$2(t z(J<%Vz9la%L)o<#bbD9-{^!5&S@JXAed_Zc=rY0d&t*i6^u+XE=iYw+i-ptBwtoNs DrV*%G literal 9582 zcmV-!C6U@6iwFpV%sOcR18Z_=Y-}!LZ*FETH2|z!d2`!Fmj550qAQ!(vTG#Bl9Sn5 z)p~0+vS-Y-b(AGnX3F%knm|+R5eOh?KomEX@4oN%UN;UN9x~Z@G9iwB{rcS(9FM7OFCLb=4$wQ@;^`+-7FB(vs|TA$WYCO)r(251$`Wnd++}s8$JeRl6x4gG=<=5yq+I;xx z>z13R+isqHjbeX}(e>MR*Y{COB#PJ0q{_;iN3r^Jr*Jy@wLz7g7_O6I%UVy-Kc4Q` zym0loM^k;i4tT0h@A!Rzv9S$3DYEoHV(#SjvEY`Q<)IMi0EaAS_@MXi)ps90jXr*i zFK@2@@}2tbNBZ}zN!01-kve;IhRav+>5uX0acZ9E;zMP#?7Jwwd#|o9uHHqDxQ>$| zpQ-V&;-~78uIxiYB5#YFUmacFf4X`5`SN~ra~;L5jO4|HG-PU2y)^ofw4-r0e^6Rw zMXidNDoU((M0ZusjY=4*n%T_6>b|`Z;YNToE1EpTg7Iv0V(R3iLvK909sg^r-kOgnH7I)zi#4CSNps$T-c4YN2ave}8lJF;=5o zxjE^_k+L;_IqPaBmL@{|+?YHeI?bX-vl<^i>8vqi?`-9cl+npt5mbxUm$}MpPD~@S zM+|mKR|Xi<;R!-kIKwPUi-wSker}2yD1PLRaj0>VYHCeF)af3A2oTPw^ARQ;t zj>uW@i6Db5aKLWz+GfP;f*4&XMht9gW#$56z?+aL2utbRD8SQx$&2OO5~t|tf`v@b zdI_#KSqg+wGb7&NCkcmP8JrP|NlO5LHe!(M*Lb6FGLRGEnVQmw-DFgZtyEE^ri$4v z+}y$<+y%DSlFr5#qg#-cl)$|(gpa5N_-k@#Q>^~H0&1+v>krlG*}uiF=+9{!{bWd! zASi@{APBBK8dK602xjVWK*J8ln#I;rg$m>%T=sh2thf0xR5K zG(@xo*)CzUS6)~aXogM_!ry|R< zhZLQt{rx)rks-zTEQ)8fx{Pe8-l*}5^yl{QxRBdlHtrwstLNu@@s&Xk-Dft6JGa;U z66U~B2KL<2Z0NoGg zd0m%ovM5p$FuyBJVrMHB;v$~oeMA`REXp7TfOi{eA)A7 zti|Cik|i>XY!O+!lFG=0FR##1BfCu+1DVQIGuSWc9LGO z40Y!-&v=(unXB-|+8O(3iraRCUX&Gy_q0p0SB5PfTNt)qOG}yuhiceibm4M<9Rc<( zvUOOe*0F^iBF#*JZkL%RoWOKh!cqwqd+XR)3bu|T0F;UV5#mXT)O`2$G>+bD=ipBoCAg{ z^2S{Z(T3@v0|l=m(ZST_{9m$f`ntED?l-x8p11-IePVJPBn?L3r@3LjNlX5w z4BW=9Q z!4aYjAB}kNU}M0PNtG9lBOxn8TM%I?3Mwtpvnoi`#eE7Dqp+dQT*0=k?_{Qdq`!f5 zUc9y*DEByw;N zFirHh)I`w4XQMbUXK0rf>{%gjx9k>V%^e57!G86C!L4BHzV=1LOV1j1RsiPlR4;Us zlB=hr_@%nI9dYDBh=|8|Uo6RkR^A4H8l3GRqiRZawVdseP*aRt!uS^zlFulxT#P2^ z4C%w=g+rLs)(!*%I#9-jC7BnLW&+i^sdCw|n9U%EPCt>RQhZ*mVs%YXx*S99Nbtb^ zE}9xKJY>MgQBW>Ta!8t&XlDk8_6HU7MP6iIy<6w+LB_n!+=QYfhX8L|s2c*X5OUmq zyi@clMRP#8)qwE}{WFCsjm>I^k$35m48>8;9ok^43iW+Y(h*zkd*D9GK|@fo;kZ?h z*;h9zQx^S`4;*iM%HTF|SCnQ`=$RCFq3j+-M6*w~A4aN}{sZCfATHQN2%xktB?9_^ z3X8lxadQeR7|Q-+p!nWLF}ngD7W_5C>CNLDdJuvjIt@8Q1i@O}S1`sGIq6WPUff^2 z90*LI{V2_2`$@mX~)y9qo z`L0a@5J?%s{RxN=zB+_{Fj_XZwbhwr?~L$<{%?bBN?=D^x60F@9Q_VDMxKu9|CNpd zIvhsE(xwyC6L*D`x1+ahy-}#y)@3&pNy0G#xo?fYZ@w$Aqv!i;R4<9yW>u%K{bDIX zNUJ`78mW2VYNX{X3=qe-r9s6!WkP z@Gn1(qEPI5NqL`Ll-a7SvXNhJ3vA7dGGrf&VBfW%z?%qUS4=9@=pfT2-63VbNvdu3 z8_qkznVc^%_o{HyRX7rb3y3=m(?*M%{&i%+MR4=mbc`-s5rufODaAjX)PQ3PcVxU~XM~ zzPsmR=8EOUoezt8L=XH!+=liEwH=&q1*bXTdFHV<;iO*Z;fS32QJ z5SPt#`b3U5(hnNC>W$H0C2YlDo#Iw=kv0i%+}AC(gz}URSnp3}-x>@tg{Tb(iI&C< z8UZtg>}pZ~rVrCxE`=O!yz z#e;ntD($zrpoq55XW1Jm=9cvN5Tb%Co~GUL_NVH%Vhs)jS|Y=(@Ey8*fS6dRoO`$0 zs}$r>C>Hp7!blRQP>8Aw&iQ1xHQKul`zB4rj%ry{kF-(LOSaG}^IC)$!seRpqU`u= zlBV82V+=>%US5!B=O9`N?I$yx*v!_eK1n^R!=oWZ{lg8K_GN|jQIi>EjoJ5zYZ&>H zc5wDP*RXxhci(d9OmV$ofldV;itqLB#D&7N-i1k>m36BCD*6!pk94YBS8> z>svcffl5d*^k#9br-OHG2Y2D;c zwkq-k#52qUbf=?~oSP{;6*9Sado{YA+~0h7ckTJs`tV_~jFK7?;*N?=Tr4Q=>pD5F z&rp2=9nk~fIRM5cV#GrGEiH`1AqGXKL`t2dc8;_t4G|IWcAWmCP&J!Bs(MK-G4d_y z&i_Uo7p)|2HQ;|wi3n^u83c1LG&X#)R*ao4aSu_uw1WPAc ztW?J74AG7!GX~=?ZF5b^zsIt{W&`xIuY&dI$I)dAKpIlxXCL9YS+eJEZbrKEgvKz2+jNsT zeechn1pCv!KcK@B@{hv&mwDEdqnelu7>o5A^$TP7X3+CEYR%AOPH7E4jN@1yIi1!0 zJL*?~PUrtN_{wO=r#}5HTkJ0^>k|7q9@rSJOYQc!Jy4XWLX+hQ67b~ zN4>ZryMqqHO3c}pLi)DTy#GVX@$$ajO3CduCmENi^0tC;=ugy&DgUU5*-?liM#;@S2%glnx|9y9J z&5?+-YKclictlo?^YIWHhcmW*nl-r1B7~%jHb*Iux6U02Le&H5iia1d0uceoDT`E% zNFfdLdZQ$>bl8bae;2RTpgEIN-249h5GfYIT!-m}mu=+hE4z2T28R5gYM3ZIjH!q) zGA!cTGpM|342QcgR%fEtIf-K>R61d50sG<|Lr2q#i#eo0HHI3c@w_Nf^0pngt_szV zaMgy1jycUDe?S2mjB8Me?O0)+DT|&^R8DT5o|dL{L$?8HC>L#J0Ftv`>>k~`xgdk2 z&PYlHb_AP9&pBL{97={ieQ#VbB7`!(%^{*8NIFfeFL&{at%(2vbow^e1tQ#0mXaqW zVGGTJm3}k{L5O2G0YsY!1_kfUjENdB2!_sQ=rTmc4MnY{F3?d;h(oKbgaug^EnzR< z$A3X&{)YAHNRby%zd~{sx&+yHE!PeeZBij%I;8^%!_M#Rtu2MWV6UcgiJWOcWALKb@x0Kmatv7|cG86qz@ zQuos`g6ro@--#dHcO3FdXHcQB@g{MH9o$)APrOp z#Ae&hxM2^Vp96Ei2BNbEXdZC}vq5i=>w=4+o>D6rUG)qeV)Xuye8{@^QsBqVIJ=vV^M zmvFGYw`q$n0@%Ya=6HW!oFi8Je~L3;eiLPxX25HQWe9_K`D@fgiau}iF?iHQCEM>y ze6jW{L<((3b<>}QiEY2+YqY7s8?USzo)&y3v}=e&U$z`JZYA;S*GTC4X6Vy)tm{d{ z&Xp1@%H98o{}@uEG5v{)>fw8S$u(2AF>!W4t`l-c5lRFTdJfEYRDID#d41~p4nsxxh;P99@_-Zyy8NxTDB|gJ+4rJ70BX=S-yI>aV(b*4i$RyW zH~!-*th?dTyW?YK{*mI}IHvz8^yi;}349^c-vgP;i;d1_w{xPyX87fKt-G!8JMbL8 zJ(BpjHv*u>r<)7^+1KU8Zfq#xs7d!c9^oqNAFee8(6&hl2+ zPbUo;RD7o`mxP8)`!P;#mSiCEhw(A|K>KA0!bLw&9LBSdI`agAB;s92;WuY`7sEw} zKH6#Q?rT0IT>VuG`GijAJC@>Zh*`~ZNfyY~GCt#R=%C@uJlnx_`^wCEQKtvo`vQc& zi2bwNKdfdtq_}s?S1Z5A{?3_>HYsMa!#Q`vFZk9H`z=>j1rvXKj2YgJmC+h2)lOE@ zJ6I~O#ph7M`VRCgkP&`j#0Tl)F%*gXG+G zA6$&ae|WE}bcs0W&VS{lu8OpgfOKc%h-1)dFKryWeRjTj9Yip*(~2`;>AAA0kfC#Y zwtI7O2Q!AhhJfss%*un+^0kfQ8D;x0uf-%0FEz{|@r1$}=c|rkC$>(g>$DlMpO_9i zAx0)Z#CdSjW^7&BZ(3}PFX31KyuL_p9Eg)^h=mDY-Ub0~KV07Z%js!H0ImEj^>h}W zy<&TYPZ7xZ1$xL7sjrgqmH+k%gM_}fC2w{f)r6`Sf4Vj`b4>`he*u7X?UJsHc@eeu z{0G5z#jJjRclC58#cGQ*0^fDdd`~1bJU}C+O!n??Egv+>S1*Il9o>Mjy#uL_i~&BN zEqm2z^bjI$At*wJ7@DW}N`h&R`WB~L?RmSejWuP>QSE@6A*_rokBfmKhZ@mXJz{5)L`odXB*zI^e9gYw>9 zZ#SqjY&!dCxqTV&_Fo~X zAMDY%O2!QRoDr|W??)1Y@-cCzr;|VZz&Ebn-2>9;Y3$?+1{7o_r43UR&-{<1{26~^ z_WJ+IyV~Bikt6(_zk(=Gzy^GgM9XhMQNYPv6QkE8aNGlm%YmRI%HmlP1(Nbhix&M4 z{fGS}oo8ltxfCgxv;*gW0{P(BnP&%wgWxRapLFegITyS2Kd1L*`2s)d zkHL=4x?Szx4}$1ydOAJvK>Up%~`PQThnF`Fv+qvNwC|dqqlOSj4??glcd*cBd2g zuSnAZacUZDwZBpy$W5gcZ?@^Ia0*p?U|pcu?@N}(4>%|*1-R0SOCd~}P|zY?2@Hq0 z_|*ERw?2b<*DE^jVu_@cvq;CO0fc<3)+oeN{^|1Vip%{@Oo19+q8fxvtgKMSACkI;;EF1c<3ktzRsK z34~Yh+cZ&r4ld_opf#(yqGW^q*F!O=1-M2~oi#4iH}Esfi6I6o-^WkFpJQCtzJ=x) zIQ-P{JU65{2#hp`he5wzeNYwbD2Hq9jVYwl*Zxue_lj}ZG7-9Jr7BfY4)?3Gazvzg zID)G{=aauzptR8f=^c*Q$#a7HU5;m2y5w>v?D2r1Ikil|QCr7-XFq62!BI=!eVqdy z5whKce2xi|WyHJ|n|AkEp0!|p1tL611sqO+7Js0KU$!|(QqdI5#7F+%84GZS&WA{( zg%;5bV_^;c#e9r;abn??$pz9OCiIyLHFE#4(XbIByvhY&c8R1TRBFENjnZOPnOWFQOX$7AarP~!-xVwwegpL2v2=9hf`3DVF3 z!5cn2IvUc7-m(B%-b3!MsRBIW+(@l0}B(4ysuCTtj5R^7Woo z0(u8-CqI0=lJW&kY?ULdZTid9$fh{ZfozkY9w%LyIXy;Gb|Bs z@_gs_`VnEkqJunf}vbaZmuLApjD;Ud2du`DBTzUe%~vUKa8b(D;#L^jn*8IGm3 zd%*)RJj>Hq3^FAOh0MU^_2oO#;mT$oMbOkX&&hm-idZt!`Fdhd73}uf8d0Qgb3i5N zS%b>e2ua6nA%0WyM%)UeAv=WVUII$r0WKM42$X^D~M-$3O?BAjzKl<-3< z7UC!ufV*$Gz)W@)&)x|~4A(&=OFF(MjMXmrScM{SQuc;5#y%^Kj8Vq1U#>iVdoIu= zm*vjkrg6-pio6`pV{k;RJiyz&e8(^xo;eUHLueBOl`&-tQ_XH`k;TR4$bLS{*2^2_ znDC@9DB<-#)k1ncMc+|7>7DWp@E5ce#Ef3+u>>GzKz9=0p${a>8``8CCO(O)5QT2y14K-8G$G_Z7g`Fat0Xm`&veioQ+oXz+NYDv&FVgly0$l z-ig|-jXOFbt0bv&9%aH5v16WToyHc4$^`RNJ~pqJujXVO>y@N)@&)otnLdot>WU!G z`d#GGI4F|LSxdnbY{sE(hT$^4FRI5zWP31|I69$QG#eq38RW<$Dd)gx_{o$rA$xF0 zuz^byWxqVEx#Tv%P}f;Gzv+_BvMm_^*>e$8nPIG$q#c&wt%Kuj_2~lA_)twwYy_!r87u7C?9x z$2WE3F*r{^7j7ANonWUkY{OmM&H_87IXAK8dNoG zk(pXpljs@^twNLQPDg3)TMDsh)SYK2HkUS*hJDW1RwZtA6ug`aHagqgbkM+4j0K}_ zG>=X9=*7)q+lV?F0x+JfECG1ZZ*K~l-R*7*+`?0gfd$ZL4fxBqHHWQ(>TC~!JZ)tW zZ{B^PM;o`HZ)P9$?svBkZR066(jb!G_V%)4tnE!F%O)#2s?3XsX?5}tOcqNUqB8ma zGFN9%Jl6zKm9M7*u{lM`{X*2>K>g`79}xamqpK6nVX& z=-#o0D~BVkNsx{)d;~1h{MMYd!6ZWGf{`_p*ys9Hf6Zgc+83Z8KNmI=j17t z!!$j#iZ@L~r^8%&Wmd>*zJN$CPU2>iWJ1C@m~L4-S&w4}x#E>(Q^HA+gBYzDdTNai z^<6$QQ%c9`#H1G9^c0(F8ZYp29Rw$`k1{$j+}e%mT>cA$vt2Vx6`NVKI86IcVHyDq zWz2Nq^lVQ`o+h3tsfkx zb7qOmV|3gG`VFuPj101{T5Fd9aa1aDIjWa{aDSOz)X1cJrASPvb&Wo_i^3{$?#A!C zh>>v2!&x$k0WH+ZYiIT=l~m-+etk*dz``y{^F7AZK{v7gk8#9x_tYG~hxqZIdiR52 zkF^!@CDMBFJV9(3$aAE@=iCi}Xej{~n;S8tO9$lU5b_%)LLKu=iEO|Rn@o2LU+DE^ z_+L#Z?s%Rd7n{dyBO4Y6FNyZG?PB?ggROP)83UHEcN;U{2s<+Z&n@$tB*V!K_d$xY zUXGRZ&NUQohoSf{ZYa)pJ94hac`+)P*3Us(&ACn;8ACwnpdn=u%4;p}Ncjy{-dvU_ ze}3W$(bXSHqC@41^0)#|a68C{C_#3;W$oPs?P*aGZD#*7ARx;-6`&QS3DUQ*;B)mh zQ@_`)sshuMy|vNQ=wVZqW|hzpDgFCZ%3O5c?wBFF@1lenDox;{_jgG^E__+8j4?5at zT&xXU2Q>o+%QyF2YS+vZ8` z(xeq+gOF{1mB(F1Ql;z%w9=+zpbVm=5!PN-~_x`2PVF) zT%sODW;UA(JMHzYivB6JCLMCVs@`$sGBa9WRlawI@)#H4psm;Si%fdlW=}bd+unfm zvr6C>PgYR{m~dOKdp)z%I^`?n3}Lj1O#BU+^T%JSD7?knY%=nuRdtKLvS8YyVn#77 zMT;oAaops}E*DkF5$*FZ0aGPgUIMsDV4iRC30zfT3R*QI#1PKo*tfU5!hVT;2U%bC zI$G-9cK;Vo3jPO&hbNwXkW>HtK}!cA3MI<-sEZ)8vU2Z=yvdHjW7o_GW~lTJ%=Fvx zMk&P2%wSSHgveGlpcCqQ%=V0Dzt1^Odug#SRyJfJ@x5Az+kUV5F-9qCMekLa2!T0C z$hMwz^){2P&0XmpXMVJI9CMcKeZmhEUdhnTQRt0J8iQlo!^N}^1b`HG?aTpGQu5Va zJR&_$(*&;20yp>#rZm13G*nb>sWOy?)jEV<{9 diff --git a/doc/krillc.1.gz b/doc/krillc.1.gz index 7d4d95ba0abb8a8f75936d5d12d2d1c11ce9ed0b..ad37f9e284178489a93747d4e426d32a28650f13 100755 GIT binary patch literal 3076 zcmV+f4EysRiwFqfW}s;R18Z_=Y;0pLF#zox?QYsi_CHTCTdh{9Hlaz{W~&IUM1ZuY z1XxUZZ&a#kum_lpvE3P)H2t;juy43evggcxV1U1vjP7E1d- z&&^{un?eKvHu)e@gpS^kXYYrdZnyc2JUfE_&)I|=pX`(4!{ZZpJ**!6qk4GYuqC{_ z7up=m)w;!h1< z6LYHIysJqG=ka5%88K&W0Q7?_i1ZhX;5$f0gXc;N>*0LRaDa^xacfZKtq>3@?n;%c zJiwZwl$tgLgCGD>zE7|g6f#Nk8#OGzc}T)TWV#F;;9IeYWYL3|L<;#^6N3Od_7WNa z=e>~IdJcIr5_GnJA;!cyMvFnHn@k0{%bj|y)A}tnqfU$LvFfb4uO{Tt1=}ZV0haL` ziH>7tdS<@J$3jW1F&Yhj)AFQYDC)W)g|z`ad=jA?V!KUqYyDFe`J7QVoTu;%z&U{O zfHae=h7>@LCia}!*Ll!T=~A$)xqoBq-*jg`bGZytpLL;0+gBt6#-nCK=6xywd>%%J zf$M-&1gw<8x7+H0O=``Ck&jL5_(DE1Z+S{oSosVw;q%4S=Vs$mYBD`gjo4K*8>k%U z)r9Ys77#GP7q@vdB23eLM`wAeOhk}HpyPg!o)>Djyqk>7H(cT0$a-zq|J_QPQ7>9d zKpO!Vjv;#s`js5NK73vT$)8Ro3+uK1`MK2|t%1g56mmep%#?eqSyT?(@PBni5lQX1=8XL#tK=ql@g ztSwhtpNcYFv)g*@efuiS)lnj|f-!_j<&iM_lJOq>5)&LV9^1xy&X;XmHg%kITyQ{1 zFqz&_o|aWAmxU|^wWS)Bwy5L+-Y~Pc;o;m7^ah^6$4~ynZ1{`)f_;e?gbu-$zHRcv z-y1^!`Sz4_Ae5;52-4TTJY7a9g6yQ%k!a7k>pgqKIO~b8-hPk;7{(rpVW0jKKPRj z!3$Cww*PtAwnhenaXa-@b2_6ol`V>4c-|y$j^4Z^O(l<^@mPOcbXDzA)-s;d zb12JD8Tg(|vvgG|uQY!^)ndiaj7Q!7#nr+j&-Xk?+;R0dzgS&$6B*J_*Ob}ZZ}-?+ zj(i!(fO+X`)Y7f1jI`M}C6PBi2%?FLY_^#Q`BbgP?aAkHA+J*HUp{eNy5!~a`!a67 z$MIj>-h|ubBoHXym^O7&(wR;Z-#07iOpcQ0LyA+9U6kE+>pz->0C1lG6C+az#LYYG zF7s7Z&%0h5G=|XokkT3|H>m+su4@f|3++|1tHXBvuH(n2yMj#tZ+Z)6s9O`XNlhx3 zJ1WJ}F+ZesuC1D`yhlZL?(wv%=s**vF8{_9Xbsj?xFjB- zaPTzRt_rnefQO=InO)mE2X24_{~T18HRvF{6lC&6M1#GOKX&`OOx8HiWdLaJpCWTC zIxG>Nu|S5V4m3SoT2)kgklJ|IQzbK&R@zxK<92d0=k`2lD{3!Q=Low;^$$(dljeN4 zK({7aFX^RxT2R$0?EQv(jDIRR6cwv4!res)Zn1l%>R7djuTr*Mr7%A=bC??ff>ygb zY7}XsIoOJ)7qEK&~OkHn$1Q3L&j+G zfW^E*yc%9+Hqh=8LKsJ2V9Zc0{e1u)MnWGAI(s_+W>EK5@EzxL5=p1@p@hjSSQk(5sH89WH>#Jc zxd-IAx{QTcU3+*ehAS7$moH&Jcf5^Tz<_?$;s=(!$VU4O+6g#t3Sv3&&%)OI>H2E z+~JO7=r2I!!Q7q>U*lN=j!SFQzsTO3XeQ{2K`XKo70eceY0k0Ar=^*jb1+NmW`(J_ z*eOWBfHGl+`T@R(X{?Fv8_P7MEZDF(x$i)JwD(o zb#S&ZkV)iTXO%dDWC2Qc!Dllj;xlb8fa%&M;Yo=lKDUs$&A(pN^3TYf`vR*(4Nm2l z@)}ve#l7RCI-|e0NRuliGu4NX*y@)s)NR}N;whiPE5l`mJqVdPPqI+~VKTS+H%O=p z!ax?}>NR#egjexT?Hp+m_4akfr`$c^FMYJzq|vqd#?bZ8o8T{-96jI9`?-_5#*n6m zMS*TnfE#L6tMrSMPpK#x0estooHS@?R zcDk&Ay%q}t5nWNS;C^j7{cD3vr~Ys7#!5APsOeX*u?SYuuScI3j4OTfL+zxN?>zlb z(AX$yJo`|q8#?;KZf`iL)qzFz3{4~y?7UVdmQ9d4_?6>I=?t^1D0tU!^zV%leX%u;Ee=(+jqZdZsvz%DPuiqOe>^5SQ!KNxggkQWX3 zzsnrbXdIKX)3Z15cv^3~sW(nswuXnF1^2ucwYvSD4BJ<|+ATgiWO_nW59tj0s?)w1 zj>dN1uGKXQ$!Yy`^ZSSTn_69lz=3(KK95MJrv8lKUtX&}aQ)#I44%#}G_vV@On!r> zg%`}IH$4f$hzGueW|05~}rh6=KwflCrC8F7LY zaLOef_Zi4LVq#7mM&^M~-D)s#DO&}8U4eTy7+tgnWTXJv3gQF?(lzBCoq1sUYNEtU zgHWa4X|_y&@(bXhB$A0W6UXoyp49J_{!<%^V9i~o=NQsxNuq#IGUpyn*^emqxxXMY zD!GFVbH0ed;Q+=u>CfRUE^B%QNT?q~WWm?WKQ?l^!+#=ik5FUDkdd!gQ*)}}yz6NR7x81M8L?zu2=s$2 zhzwSY;4?@@gXc;N+u{78;Q$*Y;?|(dQz;-+;*BcVMSwL$88z()25ugt{Fq=ZC}fi6 zH)>dj^N@r`$aERGz_)U9$)g7`i45|0CdvSI>?Je?&ifH}^c;$2B_jTVDY zcbN)um%FWIzxz{WM*S{1V)aG+SWU>MD|SrS3M}I#5}icF?92jDjD?bBdorhN7QHF~d=W;Mf$Q)jS}BEh_tgW3G&^l0ABVQ^ zf&9k&^*K=?<|D+1k5@MzJM9md$qYd?5?9e_qjI2E6MLOS?DO0*%So1J8$U@0N#U;OHEJFBtgcq?faB#rzH9xs)+joq7RcbHrXne8tM( z&KMXiYc(%=mm{lJ39^1A4Z50A8td9-c<7+$ChLFB} zV+fVXBVqU@(blvu;DVB1Vtt@It*TTWi&zF~S2ZeaQON~7 zVPMR;4k(I_9j>!{$ZCe2H-8tz)C{g(lq_2N@zKl}F zTWEYq&*4ypnS$~K`AKgh(OGiOg}9$Va7BGwbx3-j67|ZgN5Oo4QBF-85WMdXa!D#W zCqD&HlL%n%gBUDGF24vL6H|MbRi3q+$U9QGK!B)XK>l!T8Sl}Ae%Kf+M9I7%@hJi1$EZvmK8_ge6wOC=9 z@u>UXxmud!1-=i7JFXrV7pt3YA|o2fFb-DmI8mcMpa|wVP{g`@cj{wJ^;yo$Wai3>hMIV|t z_4w~hf!1I>g-hb00+-D04_2rZLmrd?>-=ihC2#{I_~)RyszHa@#SxQ1AsXzJ{HYtl zRr08@PK-f&{}j1Y+3|to)B!RybHwNQ(yF4`7pYB#Jy$XlX=R;7Cut{lOYSVwwxV_y zb&ha&RR5u=YucP2maMkquNA#iKuhX8g?-ePPw`J>$Al8~MYxA3!9DhKRAr~OwN=V? zs1z2bW-jw0K+x?CChan9G)FygKOYNQmL}IV^cF*h(XD2?n_JEp`HUooHkGC!7Pn-- z0}TgJq1jxGe#se45wIkuE+Jani`WOMtsCn)x25!u5W+YD0~3a7=^q2|C>Hv#%*DGg zFoSycl9w_e68Ej<#Jc%-J?^$Ax#>^z$=A?eO(UZ2nw#^#Oo%{8q?7w=NF?3L+w`+V zxGkRG;lV)g->F{2^p3w*M_QVvSQ8eg-i1D7!pCD&c7pn zmEhLWAk=Jqg!J$Xs_F{zVo{c`joL}K;{A+DuqR;rGh#v!o~qlcwiATRggcqtY29yY zH4pl28g$jDS=quQbIM~W2om}s;Kq1HP!&I4C$c^=5pGhJi1jzB)6o^TmUw*6(`*MIftk;0NB9TWi-e4Hx)CPoj;bt=4F;_+K{PTsybJv}9@pVW`W zvHEkS|NQVZTt}2bjC;PZjDi)YJeb?_;cGH$z;SI)MpyZp1f3LJ31~%js)E_0FfBM} z_`Ec8cMj%h-K{V+R~^MB4nMUh%>vpc&T1kK&8MKQil~=W_<~o>LgJEwSHYX7NsqejLa`U2>}L>oUlugk zm93`T>x_ooEw`D>NXWOKsv{niQJRDM=Eobl_b2H|he``~9?VK*5t@)R$JZBwzWu(J z+^pP?&`Nb>SA0Or1X$N&l>{OCuE&ylOlr@gYA7l{I=G?DdRUA8nnwqp$bz&lO-X&f z9hJxT!oHd^{Xafr8UAhX!J%`0&X$pVz@f-e>fE4Vq3x_kvp*ER{ilvt8;3%UF4 z+f~iL%b>js1XhU}oXRofEwX}(M+Yo*M*nD$rcg>|s$W9lsBc54`=`lOOg@K4hRX~` z5HfY1Ij9H1Ko%70HFkUnui~HDInpHR?dzORdH99jzUcKxdti@@q3aKR z;I}>+FOQ2p0P@fn(d@7|)GZ2d`+D+zWYvZPA9Qy>XHvCxM&lc+|L*;yc6QqM@xTB6 w4}N^+n50KdUyyg3uPDv#i5oa^gpqPZef5o{JHaC+@Iy-f1E}J1V0%db0N)q&7XSbN diff --git a/doc/krillta.1.gz b/doc/krillta.1.gz index fd5a90861932258d6292706a36716cd053c0e5cf..999a0edc7f8e9a0914d26ef37f3359d2920675f3 100755 GIT binary patch literal 1713 zcmV;i22S}OiwFqfW}s;R18Z_=Y;1I4E-?V@Szm9XMi77JQ>>ysBx;OoUoJgGb|qrt zhQ7Hl40}h6QT=@hLN99vP@B1?snx9wDLj@ z@sJRT6ItRvVSXVTC-_W~k!2%h z9XxG$rBTDGD{6RikN(_g5FVxUELRg<>4l!iQu%&?u_XppqGj+(XEvU8OXwEjhu0#4 z0k2`mxS`GkI=Hm7@z_xg&_M@&NB9F_D((Iwh&)JPLib*DWSiJ628s8)(z9vY)Ka0gbZkx&+p{-lAOkptbUT<4|sX) z^z{>LaA)VaD0gNec(bq02gYQg=tD;M43sFQBnne_z2Sm~sy(nY)6oo_LpYFzg}O9~ zmWZS%OIh+p3Cl+h4s(vY+>Tq1Em`KJ_RJ(%*i;IynB|y~d=4QTJw8+_g38?6xq$UoxCljbyr7qXGKR`S1?K*)5p6z{z^^HjW>K)bp!2_9A-V zYQpl2%vrInYP{2lC0hk|P|(q#7X(Ljp4hUbOCYCl`u>9Svep2G#R5AS>pN(tb5v4EG2We5bFbjgmBNnX|NPU z4}|Fgig;-n`GjAg1@l{Fg1{>!fZT8OIzc^69~rsFNjQu8g0REWTlsDawHP<|H$+Q{ z+(Yk6u5SwA$zG54b$S*D;J=CrSDg62@xjWw!z>5t>c#}tpsMTYpGB=Gb1@fpIAe7V zkr`|&w%|vS()6e{%9(Zz!gz7vyzDM{Q63PXUcfyRM#bh@kx(>zoTlqEuXk|5Xi_%+%$$p{ZFUT@Ta9CfpidB?B=OkR_D6hZK;6kY2 zD?tFK#o@n4N^-%%v%lMh4k5|Hg?C3j_iJq!x-Ad&6kE?ld1=_^uN6V8(E>7y#VAW4 zFw1bu?X5#T0V~XpCxsR9{YhUCYQQz~{^WJ&ryTusjegn}gnIPTX7v4{sv%EV4-W!44mhJ*JA_Zv>0Og&8#nKwlRg|XfJ6BIpHlq zkMt?@Au*Kk)Pga7P**Ma9-e;XEDbm0<{TM?KpFGsQZ!|!zy+moDg<6Qj%n8kj9u#p~cNiQR;}vtJ_EB-v>7{x-HPuN~j<(|2<%R2BE(bw?0g;J->4oGY zB39kj$_vR1`YX2;W~*CP<1}4!G_Y}^=-NoNovD5`IftTGZ6@>SJV!zW5E3~1-$f)% zv+7l332M>OY#i8+eEN}3c|PrV55P^*^pPc9_wYLgo5pdbaQNE|D1Tf*!jq2X_AkBq zA(hcL+SF}E{OB*daE_~o!?}6SwSdydk^s6&;hy}kYdPPlZ}d?Q5_P2gmY<>c9M><1 z4JRme?Krakq${hlo_#%5Riun<aHvn6ppK8lznv9 z$01xCWA8TQ)Ik(KC1k#!Qn%gt@Xud=v(Ng+Xmgi)fK;d^aE|N$Xsz;pa>>%a*ro_9 H^BMpEnNeBu literal 1705 zcmV;a23GkWiwFoM(K=}W18Z_=Y;1I4E-?V@SzlA5NDzPLQ*>GPpml~au3B4Ga#a{n zITPRj_tsjh6vALA6JTNDpSSxC_YLopT+a-UNFc^tulC`#EH(5@|N75#_jD84-GY%9 zL+afTxFa#iJW2uxvt$Mtv1vB<={zHWL!PKC@t-(@ET{W3FYZKfKw{dNk_R${J+>H- zE?c!onq+k3Wp`05x$ZAraNYO|a?CjCMiMNH*o0NCi`~Z!`&v0~q>i+@xOdY)>^2ei zZzQ>}L(@-H-H~K;kzBJ0(u^ESGFUb+>+sWxS6Vf!yP|=&@aV7I7U4-wj|(;7N-y;U zOXd3o#)=qRNv6RYo!NTY9idxH9^Z8L84ed!`<^9pFpgWb>SY68n=RQwY zDrCVL)ELhRU2uSQ3O>nb6h=OlEeHtpqG{d)lwtm~+s9V(h$h)HcWr|@>n+U4*Ez}S zMlxNkvjKeUe*6IG{1(lfkZiGe6~|9v>iKmXdy+hIHE}tN?pQglYrNKp6K-Tm0j8GU!>VZ6d4s+b%vMh2$v)Kj|+PYWkTTonYK^CCtiw zR>23KM?(msDQV8YrcbMi<~UvPr_a*>{Z~`r ziWC1gKA3s;gyld}-5B8-R&`zdv#b?WE*9dpLssV?3UOPxg*-($&3783oN3o0j29Qq z%kGj_Qq(Qf_V(iABRFdA>;Vy0^tT*T2YT-g7R5%5WxxQk1pOC`n1YD4{>L zD7|->Y!*8HB$@^piJPldxe9aaqJ+yFD~;}JzP5dfwFntw07m+tZfcGTeUu^R&gdd$sS17FNyGl#1F^7hm`SPsEq~vrs2H8 zIkxLpmR82L=-&livup}I$GB9>IHt)x!AKUQ6~Ua`BsV91#`^Ju>$uE-bkx&mT?1;N}F4_+E!f|^=A7L0iM%(I-Uj2poBP-`y^*xf}p4f1vE zUQ025-$AET;JfRmX&Ild$J%LeB)q=k0_IY+vZk0;(-SYHqKR&~AO)PInue`8hIKt2 z7`mejoR@+ZV*}>27-IVEtgmXeG=&ssuW5=o;Z0GG{5ke9F_huh!jXQ^R4w@)o_^IV zjW^`-9GS#O8S>~-G-a*838iT+1YS6fX*UVv3oiwVYkAlPoo3MkD<T2|6?-Gv-X~ zlj5l3OZ9qes-wCbZN{_9ix<6I0Ro7DkqOB3O7cY_R^86bi=zsOH_sAWsDbYMU8>1RF_`Ly9Z zfHz6oOO|}m!><`^TF05v;qNz~{BeZ|SIs9Jw_lN4j=pU9tATEVxPD1&IYF^&&5`{lU0IX$?Cr6tf-CJT_~SrrZOWQE)o6uu6Zgb99Ow=E(}Z3hcR9pL+>`{)PZ4MTiW@BBkk{X zKmPOA-|Q9tE-deIx8S_`vg-C3g2bOw_O<|Hj(?T)m_?MZci+;#c4}9f;u-(|PajWW diff --git a/doc/krillup.1.gz b/doc/krillup.1.gz index a26b1cecdcfb054d1d7b408264f47b7522df9392..46b2ed1ca2fb401c56b45a551919fbd6a8d0565d 100755 GIT binary patch delta 860 zcmV-i1Ec)o2I>X}ABzYG;%1<>enU^THL$6>qw z`VM`=K1s)R*k#$EYK4e03B?kgpx@Iaj%8pniBVu8gy;OxHb?n<0C&XB;Sr8N#s||D4pw$_!R5x!aA<|=&O#nid~LF6a1; z(6){+x(TAS(}$kb1eNAjgu*HYTOGc=P~{@Sb16DGdwb^vS4 zVh1J)wk_~JaJ-=16_}gk)6&;VLZ_(J4nAHlqEF%NuYI)8Rx%U$qmwMPI*62eRuwaQ zWC08^ftz5gVL}R-!EC zx!JvSa!`whiY4Ds5_7ls%>VuSH;^d}rP5gYRyD@4L|4fSsLWZ z&x;D{7kJESRdTR4o;we33;aKv-#pHN>)P(^p<3%AkR8^?>4|^4fbm+Yu%TzOx%6H* z8JIp4oE=btMr(UnxN&M^sZx;%o_1mT+;i-?_j&;A6y|5#LDoiT{CH4^jjGz7Zh2-K zg6;gKJvCuFsMxd12e3;)10UCu)orkDr_%(ajUb}%z?GQhG+ROH9Psx9Zg~U4i2LJ$96|$ZH delta 853 zcmV-b1FHP$2IB??ABzYGgUmXS2OWPSR4Y}hYb9VQ8yzGl36)Nl{b7icwHo5caoFC! zzC+)zPtvg+c3C#4S|O6mc;@rWH#0U(2e4)p)U4o8@QN#@c?P+X2T(lLs@>~W@hstg zX-e#`QlJ_=Ik0LZs*G29RPrM)VQdyN?wQqy*HY;!>uGRzGCVV|!QCz5dE|d@5HHcB2v#$8l3(~lShFvtaNg0`B8N>l~xSR?k}oLd(z z1wThG(GVAfNv)C^3UPnr)v(L($vm|BW>DLP8}2@Gz5i(Gpau^aOMjpw=63Ow{>PgS zkV^#zsWA7QY_*LNRR^X)=2m(Y6EShq^Q!i7^602|DQATyB* zp7min-%ISZ(>egG6#D1fLN;0|ykRKBTGm}ncRV)*!E%1rO^x3*D%R}!F!nL1;Zrh= zmW!n8PK{?C*T0^W7h#$a=mG*~2@HE5wvmr|WWVU2Y(x2C0n=3+z8Z7WV%~>gU*Ekb z_m@_hIgQ1gZj(j(Fe1UK!eCZx*I4a5+UD7G;B1XJzm7CnYpj=HmVj+Li<4>jwY;`o5#? diff --git a/doc/manual/source/index.rst b/doc/manual/source/index.rst index 430f99c4a..49f1e965f 100644 --- a/doc/manual/source/index.rst +++ b/doc/manual/source/index.rst @@ -3,18 +3,12 @@ Krill |version| =============== -|discord| - -.. |discord| image:: https://img.shields.io/discord/818584154278199396?label=rpki%20on%20discord&logo=discord - :target: https://discord.gg/8dvKB5Ykhy - Krill is a free, open source Resource Public Key Infrastructure (RPKI) daemon, featuring a Certificate Authority (CA) and publication server, written by `NLnet Labs `_. -You are welcome to ask questions or post comments and ideas on our `RPKI -mailing list `_. If you find a -bug in Krill, feel free to create an issue on GitHub. Krill is distributed +You are welcome to ask questions or post comments and ideas on our +`community forum `_. Krill is distributed under the Mozilla Public License 2.0. .. Note:: For a quick summary of what's new and changed in the latest version diff --git a/pkg/rules/packages-to-build.yml b/pkg/rules/packages-to-build.yml index 275dcd063..411bc7022 100644 --- a/pkg/rules/packages-to-build.yml +++ b/pkg/rules/packages-to-build.yml @@ -5,7 +5,6 @@ pkg: - "krillup" - "krillta" image: - - "ubuntu:focal" # ubuntu/20.04 - "ubuntu:jammy" # ubuntu/22.04 - "ubuntu:noble" # ubuntu/24.04 - "debian:bullseye" # debian/11 @@ -45,9 +44,6 @@ include: - image: "debian:bullseye" deb_extra_lintian_args: "--suppress-tags systemd-service-file-outside-lib" - - image: "ubuntu:focal" - deb_extra_lintian_args: "--suppress-tags systemd-service-file-outside-lib" - # package for the Raspberry Pi 4b as an ARMv7 cross compiled variant of the Debian Bullseye upon which # Raspbian 11 is based. - pkg: "krill" diff --git a/src/api/history.rs b/src/api/history.rs index 16f921466..bb7d3acf2 100644 --- a/src/api/history.rs +++ b/src/api/history.rs @@ -274,15 +274,11 @@ pub struct CommandHistoryCriteria { impl CommandHistoryCriteria { /// Returns whether the given timestamp is included in the criteria. fn matches_timestamp(&self, stamp: i64) -> bool { - if let Some(before) = self.before { - if stamp > before { - return false; - } + if let Some(before) = self.before && stamp > before { + return false; } - if let Some(after) = self.after { - if stamp < after { - return false; - } + if let Some(after) = self.after && stamp < after { + return false; } true } @@ -297,15 +293,17 @@ impl CommandHistoryCriteria { /// Returns whether the given label is included in the criteria. fn matches_label(&self, label: &String) -> bool { - if let Some(includes) = &self.label_includes { - if !includes.contains(label) { - return false; - } + if + let Some(includes) = &self.label_includes + && !includes.contains(label) + { + return false; } - if let Some(excludes) = &self.label_excludes { - if excludes.contains(label) { - return false; - } + if + let Some(excludes) = &self.label_excludes + && excludes.contains(label) + { + return false; } true diff --git a/src/commons/crypto/signing/signers/kmip/signer.rs b/src/commons/crypto/signing/signers/kmip/signer.rs index 6f9469dcf..71146edd2 100644 --- a/src/commons/crypto/signing/signers/kmip/signer.rs +++ b/src/commons/crypto/signing/signers/kmip/signer.rs @@ -324,10 +324,11 @@ impl KmipSigner { } pub fn get_info(&self) -> Option { - if let Ok(status) = self.server.status(Self::probe_server) { - if let Ok(state) = status.state() { - return Some(state.conn_info.clone()); - } + if + let Ok(status) = self.server.status(Self::probe_server) + && let Ok(state) = status.state() + { + return Some(state.conn_info.clone()); } None } diff --git a/src/commons/crypto/signing/signers/pkcs11/signer.rs b/src/commons/crypto/signing/signers/pkcs11/signer.rs index 6486838e5..6a0fe3d8a 100644 --- a/src/commons/crypto/signing/signers/pkcs11/signer.rs +++ b/src/commons/crypto/signing/signers/pkcs11/signer.rs @@ -396,10 +396,11 @@ impl Pkcs11Signer { } pub fn get_info(&self) -> Option { - if let Ok(status) = self.server.status(Self::probe_server) { - if let Ok(state) = status.state() { - return Some(state.conn_info.clone()); - } + if + let Ok(status) = self.server.status(Self::probe_server) + && let Ok(state) = status.state() + { + return Some(state.conn_info.clone()); } None } diff --git a/src/commons/eventsourcing/store.rs b/src/commons/eventsourcing/store.rs index 4e1619bf5..a188e8b5b 100644 --- a/src/commons/eventsourcing/store.rs +++ b/src/commons/eventsourcing/store.rs @@ -448,12 +448,13 @@ impl AggregateStore { // still generate errors, and if they do, then we // return with an error, without saving. let mut opt_err = None; - if let Some(events) = processed.events() { - if let Err(err) = aggregate.pre_save_events( + if + let Some(events) = processed.events() + && let Err(err) = aggregate.pre_save_events( events, context - ) { - opt_err = Some(err); - } + ) + { + opt_err = Some(err); } if let Some(e) = opt_err { diff --git a/src/commons/file.rs b/src/commons/file.rs index 3181257c0..8ffa66a54 100644 --- a/src/commons/file.rs +++ b/src/commons/file.rs @@ -55,19 +55,20 @@ pub fn remove_dir_all(dir: &Path) -> Result<(), KrillIoError> { /// Creates a new File or opens an exiting one. If the file did not exist, the /// path will be created if it did not exist yet. pub fn create_file_with_path(path: &Path) -> Result { - if !path.exists() { - if let Some(parent) = path.parent() { - trace!("Creating path: {}", parent.to_string_lossy()); - fs::create_dir_all(parent).map_err(|e| { - KrillIoError::new( - format!( - "Could not create dir path for: {}", - parent.to_string_lossy() - ), - e, - ) - })?; - } + if + !path.exists() + && let Some(parent) = path.parent() + { + trace!("Creating path: {}", parent.to_string_lossy()); + fs::create_dir_all(parent).map_err(|e| { + KrillIoError::new( + format!( + "Could not create dir path for: {}", + parent.to_string_lossy() + ), + e, + ) + })?; } File::create(path).map_err(|e| { KrillIoError::new( diff --git a/src/commons/queue.rs b/src/commons/queue.rs index 3a552f546..d646415da 100644 --- a/src/commons/queue.rs +++ b/src/commons/queue.rs @@ -230,10 +230,8 @@ impl Queue { if ts > now { return acc } - if let Some((acc_ts, _)) = acc { - if acc_ts < ts { - return acc - } + if let Some((acc_ts, _)) = acc && acc_ts < ts { + return acc } Some((ts, key)) diff --git a/src/commons/storage/backends/disk.rs b/src/commons/storage/backends/disk.rs index 0aef11788..9c13ceaa7 100644 --- a/src/commons/storage/backends/disk.rs +++ b/src/commons/storage/backends/disk.rs @@ -263,14 +263,14 @@ impl Store { )); } }; - if file_type.is_file() { - if let Some(name) = + if + file_type.is_file() + && let Some(name) = item.file_name().into_string().ok().and_then(|name| { Ident::boxed_from_string(name).ok() }) - { - res.push(name) - } + { + res.push(name) } } @@ -320,14 +320,14 @@ impl Store { )); } }; - if file_type.is_dir() { - if let Some(name) = + if + file_type.is_dir() + && let Some(name) = item.file_name().into_string().ok().and_then(|name| { Ident::boxed_from_string(name).ok() }) - { - res.push(name) - } + { + res.push(name) } } diff --git a/src/commons/storage/backends/memory.rs b/src/commons/storage/backends/memory.rs index a18db641e..297062346 100644 --- a/src/commons/storage/backends/memory.rs +++ b/src/commons/storage/backends/memory.rs @@ -331,10 +331,8 @@ impl MemoryScopes { key: key.into() }) }; - if let Some(scope) = scope { - if values.is_empty() { - self.remove(scope); - } + if let Some(scope) = scope && values.is_empty() { + self.remove(scope); } Ok(value) } diff --git a/src/commons/storage/ident.rs b/src/commons/storage/ident.rs index 9927f7fc7..019dba731 100644 --- a/src/commons/storage/ident.rs +++ b/src/commons/storage/ident.rs @@ -243,10 +243,8 @@ impl Ident { if src.is_empty() { return Cow::Borrowed(const { Ident::make("_") }) } - if !src.starts_with('_') { - if let Ok(ident) = Self::from_str(src) { - return Cow::Borrowed(ident) - } + if !src.starts_with('_') && let Ok(ident) = Self::from_str(src) { + return Cow::Borrowed(ident) } let mut res = Vec::with_capacity(src.len() + 1); res.push(b'_'); diff --git a/src/config.rs b/src/config.rs index 2d9f284ca..d0f2b759e 100644 --- a/src/config.rs +++ b/src/config.rs @@ -215,19 +215,21 @@ impl ConfigDefaults { } pub fn roa_aggregate_threshold() -> usize { - if let Ok(from_env) = env::var("KRILL_ROA_AGGREGATE_THRESHOLD") { - if let Ok(nr) = usize::from_str(&from_env) { - return nr; - } + if + let Ok(from_env) = env::var("KRILL_ROA_AGGREGATE_THRESHOLD") + && let Ok(nr) = usize::from_str(&from_env) + { + return nr; } 100 } pub fn roa_deaggregate_threshold() -> usize { - if let Ok(from_env) = env::var("KRILL_ROA_DEAGGREGATE_THRESHOLD") { - if let Ok(nr) = usize::from_str(&from_env) { - return nr; - } + if + let Ok(from_env) = env::var("KRILL_ROA_DEAGGREGATE_THRESHOLD") + && let Ok(nr) = usize::from_str(&from_env) + { + return nr; } 90 } @@ -1622,12 +1624,12 @@ impl Config { )); } - if let Some(threshold) = self.suspend_child_after_inactive_hours { - if threshold < CA_SUSPEND_MIN_HOURS { - return Err(ConfigError::Other(format!( - "suspend_child_after_inactive_hours must be {CA_SUSPEND_MIN_HOURS} or higher (or not set at all)" - ))); - } + if + let Some(threshold) = self.suspend_child_after_inactive_hours + && threshold < CA_SUSPEND_MIN_HOURS { + return Err(ConfigError::Other(format!( + "suspend_child_after_inactive_hours must be {CA_SUSPEND_MIN_HOURS} or higher (or not set at all)" + ))); } if let Some(benchmark) = &self.benchmark { diff --git a/src/daemon/http/auth/providers/openid_connect/provider.rs b/src/daemon/http/auth/providers/openid_connect/provider.rs index 3bbf5251d..09aab2166 100644 --- a/src/daemon/http/auth/providers/openid_connect/provider.rs +++ b/src/daemon/http/auth/providers/openid_connect/provider.rs @@ -828,34 +828,35 @@ impl AuthProvider { } fn get_auth(&self, request: &HyperRequest) -> Option { - if let Some(query) = - urlparse(request.uri().to_string()).get_parsed_query() + if + let Some(query) = urlparse( + request.uri().to_string() + ).get_parsed_query() + && let Some(code) = query.get_first_from_str("code") { - if let Some(code) = query.get_first_from_str("code") { - trace!("OpenID Connect: Processing potential RFC-6749 section 4.1.2 redirected Authorization Response"); - if let Some(state) = query.get_first_from_str("state") { - if let Some(nonce) = - self.extract_cookie(request, NONCE_COOKIE_NAME) + trace!("OpenID Connect: Processing potential RFC-6749 section 4.1.2 redirected Authorization Response"); + if let Some(state) = query.get_first_from_str("state") { + if let Some(nonce) = + self.extract_cookie(request, NONCE_COOKIE_NAME) + { + if let Some(csrf_token_hash) = + self.extract_cookie(request, CSRF_COOKIE_NAME) { - if let Some(csrf_token_hash) = - self.extract_cookie(request, CSRF_COOKIE_NAME) - { - trace!("OpenID Connect: Detected RFC-6749 section 4.1.2 redirected Authorization Response"); - return Some(Auth { - code: Token::from(code), - state, - nonce, - csrf_token_hash, - }); - } else { - debug!("OpenID Connect: Ignoring potential RFC-6749 section 4.1.2 redirected Authorization Response due to missing CSRF token hash cookie."); - } + trace!("OpenID Connect: Detected RFC-6749 section 4.1.2 redirected Authorization Response"); + return Some(Auth { + code: Token::from(code), + state, + nonce, + csrf_token_hash, + }); } else { - debug!("OpenID Connect: Ignoring potential RFC-6749 section 4.1.2 redirected Authorization Response due to missing nonce cookie."); + debug!("OpenID Connect: Ignoring potential RFC-6749 section 4.1.2 redirected Authorization Response due to missing CSRF token hash cookie."); } } else { - debug!("OpenID Connect: Ignoring potential RFC-6749 section 4.1.2 redirected Authorization Response due to missing 'state' query parameter."); + debug!("OpenID Connect: Ignoring potential RFC-6749 section 4.1.2 redirected Authorization Response due to missing nonce cookie."); } + } else { + debug!("OpenID Connect: Ignoring potential RFC-6749 section 4.1.2 redirected Authorization Response due to missing 'state' query parameter."); } } diff --git a/src/daemon/http/dispatch/cas.rs b/src/daemon/http/dispatch/cas.rs index f4f451653..57b1f6c82 100644 --- a/src/daemon/http/dispatch/cas.rs +++ b/src/daemon/http/dispatch/cas.rs @@ -752,15 +752,16 @@ fn extract_parent_ca_req( let req: ParentCaReq = serde_json::from_slice(bytes).map_err( Error::JsonError )?; - if let Some(parent_override) = parent_override { - if req.handle != parent_override { - return Err(Error::Custom(format!( - "Used different parent names on path ({}) and \ - submitted JSON ({}) for adding/updating a parent", - parent_override, - req.handle - ))); - } + if + let Some(parent_override) = parent_override + && req.handle != parent_override + { + return Err(Error::Custom(format!( + "Used different parent names on path ({}) and \ + submitted JSON ({}) for adding/updating a parent", + parent_override, + req.handle + ))); } Ok(req) } diff --git a/src/daemon/start.rs b/src/daemon/start.rs index 13ed9d406..a6381c035 100644 --- a/src/daemon/start.rs +++ b/src/daemon/start.rs @@ -139,7 +139,7 @@ pub fn start_krill_daemon( let (krill, pool) = krill.promote()?; // Create the HTTP server. - let server = HttpServer::new(krill, &tokio.handle())?; + let server = HttpServer::new(krill, tokio.handle())?; // Create self-signed HTTPS cert if configured and not generated earlier. if server.config().https_mode().is_generate_https_cert() { @@ -165,18 +165,19 @@ pub fn start_krill_daemon( // Start a hyper server for the configured unix sockets. #[cfg(unix)] - if server.config().unix_socket_enabled() { - if let Some(path) = server.config().unix_socket() { - join.spawn_on( - single_unix_listener( - server.clone(), - path.clone(), - signal_running.take(), - exit_rx.clone(), - ), - tokio.handle(), - ); - } + if + server.config().unix_socket_enabled() + && let Some(path) = server.config().unix_socket() + { + join.spawn_on( + single_unix_listener( + server.clone(), + path.clone(), + signal_running.take(), + exit_rx.clone(), + ), + tokio.handle(), + ); } tokio.block_on(async { @@ -334,11 +335,9 @@ async fn single_unix_listener( use nix::unistd::{Uid, User}; use tokio::net::UnixListener; - if path.exists() { - if let Err(err) = std::fs::remove_file(&path) { - error!("Failed to remove existing Unix socket file: {err}"); - return; - }; + if path.exists() && let Err(err) = std::fs::remove_file(&path) { + error!("Failed to remove existing Unix socket file: {err}"); + return; } let listener = match UnixListener::bind(&path) { diff --git a/src/server/bgp/analyser.rs b/src/server/bgp/analyser.rs index cae4f8ddd..483117dfa 100644 --- a/src/server/bgp/analyser.rs +++ b/src/server/bgp/analyser.rs @@ -128,12 +128,13 @@ impl BgpAnalyser { // neither goes directly into the `entries` as ‘not held.’ let mut roas_held = Vec::new(); for roa in roas { - if let Some(limit) = limited_scope.as_ref() { - if !limit.contains_roa_address( + if + let Some(limit) = limited_scope.as_ref() + && !limit.contains_roa_address( &roa.roa_configuration.payload.as_roa_ip_address() - ) { - continue - } + ) + { + continue } if resources_held.contains_roa_address( diff --git a/src/server/bgp/riswhois.rs b/src/server/bgp/riswhois.rs index 72ab4b4fe..dc8645beb 100644 --- a/src/server/bgp/riswhois.rs +++ b/src/server/bgp/riswhois.rs @@ -69,13 +69,11 @@ impl RisWhoisLoader { where

::Err: error::Error + Send + Sync + 'static { let uri_clone = uri.clone(); let data = krill.exec_async(async move { - Ok( - reqwest::get(uri_clone.as_ref()).await.map_err(|err| { - RisWhoisError::new(&uri_clone, io::Error::other(err)) - })?.bytes().await.map_err(|err| { - RisWhoisError::new(&uri_clone, io::Error::other(err)) - })? - ) + reqwest::get(uri_clone.as_ref()).await.map_err(|err| { + RisWhoisError::new(&uri_clone, io::Error::other(err)) + })?.bytes().await.map_err(|err| { + RisWhoisError::new(&uri_clone, io::Error::other(err)) + }) }).map_err(|err| RisWhoisError::new(&uri, io::Error::other(err)))??; Self::parse_gz_data(&data).map_err(|err| { RisWhoisError::new(&uri, err) diff --git a/src/server/ca/certauth.rs b/src/server/ca/certauth.rs index db30411cb..587d82245 100644 --- a/src/server/ca/certauth.rs +++ b/src/server/ca/certauth.rs @@ -1178,21 +1178,22 @@ impl CertAuth { )?; // Add a resource class name mapping if applicable - if let Some(name_for_child) = class_name_override { - if name_for_child != my_rcn { - let mapping = ResourceClassNameMapping { - name_in_parent: my_rcn.clone(), - name_for_child, - }; - - events.push( - CertAuthEvent::ChildUpdatedResourceClassNameMapping { - child: child_handle.clone(), - name_in_parent: mapping.name_in_parent, - name_for_child: mapping.name_for_child, - }, - ); - } + if + let Some(name_for_child) = class_name_override + && name_for_child != my_rcn + { + let mapping = ResourceClassNameMapping { + name_in_parent: my_rcn.clone(), + name_for_child, + }; + + events.push( + CertAuthEvent::ChildUpdatedResourceClassNameMapping { + child: child_handle.clone(), + name_in_parent: mapping.name_in_parent, + name_for_child: mapping.name_for_child, + }, + ); } // Issue a certificate for the imported child @@ -1733,10 +1734,11 @@ impl CertAuth { let mut res = HashMap::new(); for (name, rc) in self.resources.iter() { let mut revokes = vec![]; - if let Some(req) = rc.revoke_request() { - if rc.parent_handle() == parent { - revokes.push(req.clone()) - } + if + let Some(req) = rc.revoke_request() + && rc.parent_handle() == parent + { + revokes.push(req.clone()) } res.insert(name.clone(), revokes); } @@ -2549,11 +2551,12 @@ impl CertAuth { let mut keys = HashMap::new(); for (rcn, rc) in self.resources.iter() { - if let Some(rc_resources) = rc.current_resources() { - if !rc_resources.intersection(&request.resources).is_empty() { - let key = signer.create_key()?; - keys.insert(rcn.clone(), key); - } + if + let Some(rc_resources) = rc.current_resources() + && !rc_resources.intersection(&request.resources).is_empty() + { + let key = signer.create_key()?; + keys.insert(rcn.clone(), key); } } diff --git a/src/server/ca/child.rs b/src/server/ca/child.rs index 46f098bf4..af8b6d511 100644 --- a/src/server/ca/child.rs +++ b/src/server/ca/child.rs @@ -132,10 +132,11 @@ impl ChildDetails { let mut res = vec![]; for (ki, used_key_state) in self.used_keys.iter() { - if let UsedKeyState::InUse(found_rcn) = used_key_state { - if found_rcn == parent_rcn { - res.push(*ki) - } + if + let UsedKeyState::InUse(found_rcn) = used_key_state + && found_rcn == parent_rcn + { + res.push(*ki) } } diff --git a/src/server/ca/manager.rs b/src/server/ca/manager.rs index cc6558e9d..fd2830113 100644 --- a/src/server/ca/manager.rs +++ b/src/server/ca/manager.rs @@ -1451,14 +1451,15 @@ impl CaManager { pub fn cas_schedule_suspend_all( &self, krill: &KrillRuntime ) -> KrillResult<()> { - if krill.config().suspend_child_after_inactive_seconds().is_some() { - if let Ok(cas) = self.ca_store.list() { - for ca in cas { - krill.tasks().schedule( - Task::SuspendChildrenIfNeeded { ca_handle: ca }, - now(), - )?; - } + if + krill.config().suspend_child_after_inactive_seconds().is_some() + && let Ok(cas) = self.ca_store.list() + { + for ca in cas { + krill.tasks().schedule( + Task::SuspendChildrenIfNeeded { ca_handle: ca }, + now(), + )?; } } Ok(()) @@ -1484,44 +1485,45 @@ impl CaManager { .filter(|secs| started < Timestamp::now_minus_seconds(*secs)); // suspend inactive children, if so configured - if let Some(threshold_seconds) = threshold_seconds { - if let Ok(ca_status) = self.get_ca_status(ca_handle) { - let connections = ca_status.get_children_connection_stats(); + if + let Some(threshold_seconds) = threshold_seconds + && let Ok(ca_status) = self.get_ca_status(ca_handle) + { + let connections = ca_status.get_children_connection_stats(); + + for child in connections.suspension_candidates( + threshold_seconds + ) { + if log::log_enabled!(log::Level::Info) { + let threshold_string = if threshold_seconds >= 3600 { + format!("{} hours", threshold_seconds / 3600) + } else { + format!("{threshold_seconds} seconds") + }; - for child in connections.suspension_candidates( - threshold_seconds - ) { - if log::log_enabled!(log::Level::Info) { - let threshold_string = if threshold_seconds >= 3600 { - format!("{} hours", threshold_seconds / 3600) - } else { - format!("{threshold_seconds} seconds") - }; - - info!( - "Child '{child}' under CA '{ca_handle}' was inactive for more \ - than {threshold_string}. Will suspend it." - ); - } - if let Err(e) = - self.status_store.set_child_suspended( - ca_handle, &child - ) - { - panic!( - "System level error encountered while updating \ - ca status: {e}" - ); - } + info!( + "Child '{child}' under CA '{ca_handle}' was inactive for more \ + than {threshold_string}. Will suspend it." + ); + } + if let Err(e) = + self.status_store.set_child_suspended( + ca_handle, &child + ) + { + panic!( + "System level error encountered while updating \ + ca status: {e}" + ); + } - let req = UpdateChildRequest::suspend(); - if let Err(e) = self.ca_child_update( - ca_handle, child, req, actor, krill, - ) { - error!( - "Could not suspend inactive child, error: {e}" - ); - } + let req = UpdateChildRequest::suspend(); + if let Err(e) = self.ca_child_update( + ca_handle, child, req, actor, krill, + ) { + error!( + "Could not suspend inactive child, error: {e}" + ); } } } @@ -2499,10 +2501,11 @@ impl CaManager { let service_uri = service_uri.as_str(); let base_uri = base_uri.as_str(); - if let Some(path) = service_uri.strip_prefix(base_uri) { - if let Some(ca_name) = path.strip_prefix("rfc6492/") { - return ParentHandle::from_str(ca_name).ok(); - } + if + let Some(path) = service_uri.strip_prefix(base_uri) + && let Some(ca_name) = path.strip_prefix("rfc6492/") + { + return ParentHandle::from_str(ca_name).ok(); } None diff --git a/src/server/ca/upgrades/pre_0_10_0/migration.rs b/src/server/ca/upgrades/pre_0_10_0/migration.rs index 59da0a482..ee520a338 100644 --- a/src/server/ca/upgrades/pre_0_10_0/migration.rs +++ b/src/server/ca/upgrades/pre_0_10_0/migration.rs @@ -218,10 +218,11 @@ impl UpgradeAggregateStorePre0_14 for CasMigration { // if the new command would be a no-op because no events are // actually migrated, then return // CommandMigrationEffect::Nothing - if let Some(events) = new_command.events() { - if events.is_empty() { - return Ok(CommandMigrationEffect::Nothing); - } + if + let Some(events) = new_command.events() + && events.is_empty() + { + return Ok(CommandMigrationEffect::Nothing); } Ok(CommandMigrationEffect::StoredCommand(new_command)) diff --git a/src/server/ca/upgrades/pre_0_14_0/migration.rs b/src/server/ca/upgrades/pre_0_14_0/migration.rs index 3a6a49679..37927dc82 100644 --- a/src/server/ca/upgrades/pre_0_14_0/migration.rs +++ b/src/server/ca/upgrades/pre_0_14_0/migration.rs @@ -199,10 +199,11 @@ impl UpgradeAggregateStorePre0_14 for CasMigration { // if the new command would be a no-op because no events are // actually migrated, then return // CommandMigrationEffect::Nothing - if let Some(events) = new_command.events() { - if events.is_empty() { - return Ok(CommandMigrationEffect::Nothing); - } + if + let Some(events) = new_command.events() + && events.is_empty() + { + return Ok(CommandMigrationEffect::Nothing); } Ok(CommandMigrationEffect::StoredCommand(new_command)) diff --git a/src/server/pubd/access.rs b/src/server/pubd/access.rs index 2b3885be0..197deebbb 100644 --- a/src/server/pubd/access.rs +++ b/src/server/pubd/access.rs @@ -59,27 +59,25 @@ impl RepositoryAccessProxy { )?; let key = MyHandle::from_str(PUBSERVER_DFLT).unwrap(); - if store.has(&key)? { - if let Err(e) = store.warm() { - // Start to 'warm' the cache. This serves two purposes: - // 1. this ensures that the `RepositoryAccess` struct is - // available in memory - // 2. this ensures that there are no apparent data issues - // - // If there are issues, then we need to bail out. Krill - // 0.14.0+ uses single files for all change - // sets, and files are first completely written to disk, - // and only then renamed. - // - // In other words, if we fail to warm the cache then this - // points at: - // - data corruption - // - user started - error!( - "Could not warm up cache, data seems corrupt. \ - You may need to restore a backup. Error was: {e}" - ); - } + if store.has(&key)? && let Err(e) = store.warm() { + // Start to 'warm' the cache. This serves two purposes: + // 1. this ensures that the `RepositoryAccess` struct is + // available in memory + // 2. this ensures that there are no apparent data issues + // + // If there are issues, then we need to bail out. Krill + // 0.14.0+ uses single files for all change + // sets, and files are first completely written to disk, + // and only then renamed. + // + // In other words, if we fail to warm the cache then this + // points at: + // - data corruption + // - user started + error!( + "Could not warm up cache, data seems corrupt. \ + You may need to restore a backup. Error was: {e}" + ); } Ok(RepositoryAccessProxy { store, key }) diff --git a/src/server/pubd/rrdp.rs b/src/server/pubd/rrdp.rs index 48e560fbf..b2c57a371 100644 --- a/src/server/pubd/rrdp.rs +++ b/src/server/pubd/rrdp.rs @@ -478,16 +478,16 @@ impl RrdpServer { rpki::rrdp::NotificationFile::parse(bytes.as_ref()).ok() }); - if let Some(old_notification) = old_notification_opt.as_ref() { - if old_notification.serial() == self.serial - && old_notification.session_id() == self.session.uuid() - { - debug!( - "Existing notification file matches current session \ - and serial. Nothing to write." - ); - return Ok(()); - } + if + let Some(old_notification) = old_notification_opt.as_ref() + && old_notification.serial() == self.serial + && old_notification.session_id() == self.session.uuid() + { + debug!( + "Existing notification file matches current session \ + and serial. Nothing to write." + ); + return Ok(()); } let deltas = self.write_delta_files(old_notification_opt)?; @@ -575,18 +575,19 @@ impl RrdpServer { let last_written_serial = deltas_from_old_notification.last(); let mut deltas = vec![]; for delta in &self.deltas { - if let Some(last) = last_written_serial { - if delta.serial() <= last.serial() { - // Already included. We can skip this and assume that it - // was written to disk before. - // And no one went in and messed with it.. - debug!( - "Skip writing delta for serial {}. \ - File should exist.", - delta.serial() - ); - continue; - } + if + let Some(last) = last_written_serial + && delta.serial() <= last.serial() + { + // Already included. We can skip this and assume that it + // was written to disk before. + // And no one went in and messed with it.. + debug!( + "Skip writing delta for serial {}. \ + File should exist.", + delta.serial() + ); + continue; } // New delta, write it and add its distinctiveness to deltas // (DeltaInfo vec) to include in the notification file @@ -783,19 +784,19 @@ impl RrdpServer { // random dir as the delta that we still need to keep for // this serial, so we just remove the // file and leave its parent directory in place. - if let Ok(Some(snapshot_file_to_remove)) = - Self::session_dir_snapshot(&session_dir, serial) + if + let Ok(Some(snapshot_file_to_remove)) = + Self::session_dir_snapshot(&session_dir, serial) + && let Err(e) = fs::remove_file( + &snapshot_file_to_remove + ) { - if let Err(e) = - fs::remove_file(&snapshot_file_to_remove) - { - warn!( - "Could not delete snapshot file '{}'. \ - Error was: {}", - snapshot_file_to_remove.to_string_lossy(), - e - ); - } + warn!( + "Could not delete snapshot file '{}'. \ + Error was: {}", + snapshot_file_to_remove.to_string_lossy(), + e + ); } } else { // archiving was enabled, keep the old snapshot file until @@ -1409,15 +1410,14 @@ impl CurrentObjects { for (uri_key, base64) in self.iter() { // Add all manifests - as long as they are syntactically correct - // do not crash on incorrect objects. - if uri_key.as_str().ends_with("mft") { - if let Ok(mft) = - Manifest::decode(base64.to_bytes().as_ref(), false) - { - if let Ok(stats) = PublisherManifestStats::try_from(&mft) - { - manifests.push(stats) - } - } + if + uri_key.as_str().ends_with("mft") + && let Ok(mft) = Manifest::decode( + base64.to_bytes().as_ref(), false + ) + && let Ok(stats) = PublisherManifestStats::try_from(&mft) + { + manifests.push(stats) } } diff --git a/src/server/taproxy.rs b/src/server/taproxy.rs index e80595879..ea6e8bf55 100644 --- a/src/server/taproxy.rs +++ b/src/server/taproxy.rs @@ -355,16 +355,16 @@ impl TrustAnchorProxy { &self, signer: TrustAnchorSignerInfo, ) -> KrillResult> { - if let Some(s) = &self.signer { - if s.ta_cert_details.cert.key_identifier() == + if + let Some(s) = &self.signer + && s.ta_cert_details.cert.key_identifier() == signer.ta_cert_details.cert.key_identifier() - { - // It is not possible to add a signer that has a different - // public key - return Ok(vec![ - TrustAnchorProxyEvent::SignerUpdated(signer) - ]); - } + { + // It is not possible to add a signer that has a different + // public key + return Ok(vec![ + TrustAnchorProxyEvent::SignerUpdated(signer) + ]); } Err(Error::TaProxyHasDifferentSigner) } diff --git a/src/upgrades/mod.rs b/src/upgrades/mod.rs index 08c623a45..3cca8e6a3 100644 --- a/src/upgrades/mod.rs +++ b/src/upgrades/mod.rs @@ -643,14 +643,15 @@ pub trait UpgradeAggregateStorePre0_14 { let code_version = KrillVersion::code_version(); const VERSION: &Ident = Ident::make("version"); - if let Ok(Some(existing_migration_version)) = self - .preparation_key_value_store() - .get::(None, VERSION) + if + let Ok(Some(existing_migration_version)) + = self.preparation_key_value_store().get::( + None, VERSION + ) + && existing_migration_version != code_version { - if existing_migration_version != code_version { - warn!("Found prepared data for Krill version {existing_migration_version}, will remove it and start from scratch for {code_version}"); - self.preparation_key_value_store().wipe()?; - } + warn!("Found prepared data for Krill version {existing_migration_version}, will remove it and start from scratch for {code_version}"); + self.preparation_key_value_store().wipe()?; } self.preparation_key_value_store() From c5bfbb650bb4af1b92d3e9306c195a16f01fd0c9 Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Fri, 27 Feb 2026 12:29:48 +0100 Subject: [PATCH 17/51] Update cargo lock. --- Cargo.lock | 90 +++++++++++++++++++++++++----------------------------- 1 file changed, 42 insertions(+), 48 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f23026f2e..d2c42f7dd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1461,9 +1461,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.88" +version = "0.3.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7e709f3e3d22866f9c25b3aff01af289b18422cc8b4262fb19103ee80fe513d" +checksum = "14dc6f6450b3f6d4ed5b16327f38fed626d375a886159ca555bd7822c0c3a5a6" dependencies = [ "once_cell", "wasm-bindgen", @@ -1505,7 +1505,7 @@ dependencies = [ [[package]] name = "krill" -version = "0.15.1-dev" +version = "0.17.0-dev" dependencies = [ "arc-swap", "backoff", @@ -1656,7 +1656,7 @@ checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" dependencies = [ "bitflags", "libc", - "redox_syscall 0.7.1", + "redox_syscall 0.7.2", ] [[package]] @@ -1762,9 +1762,9 @@ dependencies = [ [[package]] name = "num-conv" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" [[package]] name = "num-integer" @@ -2292,9 +2292,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35985aa610addc02e24fc232012c86fd11f14111180f902b67e2d5331f8ebf2b" +checksum = "6d94dd2f7cd932d4dc02cc8b2b50dfd38bd079a4e5d79198b99743d7fcf9a4b4" dependencies = [ "bitflags", ] @@ -2355,9 +2355,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.9" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" [[package]] name = "reqwest" @@ -2518,9 +2518,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.36" +version = "0.23.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b" +checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" dependencies = [ "aws-lc-rs", "log", @@ -2831,9 +2831,9 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.16.1" +version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fa237f2807440d238e0364a218270b98f767a00d3dada77b1c53ae88940e2e7" +checksum = "381b283ce7bc6b476d903296fb59d0d36633652b633b27f64db4fb46dcbfc3b9" dependencies = [ "base64 0.22.1", "chrono", @@ -2850,9 +2850,9 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.16.1" +version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52a8e3ca0ca629121f70ab50f95249e5a6f925cc0f6ffe8256c45b728875706c" +checksum = "a6d4e30573c8cb306ed6ab1dca8423eec9a463ea0e155f45399455e0368b27e0" dependencies = [ "darling", "proc-macro2", @@ -3164,9 +3164,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.45" +version = "0.3.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9e442fc33d7fdb45aa9bfeb312c095964abdf596f7567261062b2a7107aaabd" +checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" dependencies = [ "deranged", "itoa", @@ -3181,15 +3181,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b36ee98fd31ec7426d599183e8fe26932a8dc1fb76ddb6214d05493377d34ca" +checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" [[package]] name = "time-macros" -version = "0.2.25" +version = "0.2.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71e552d1249bf61ac2a52db88179fd0673def1e1ad8243a00d9ec9ed71fee3dd" +checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" dependencies = [ "num-conv", "time-core", @@ -3514,11 +3514,11 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.1+wasi-0.2.4" +version = "1.0.2+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" dependencies = [ - "wit-bindgen 0.46.0", + "wit-bindgen", ] [[package]] @@ -3527,14 +3527,14 @@ version = "0.4.0+wasi-0.3.0-rc-2026-01-06" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" dependencies = [ - "wit-bindgen 0.51.0", + "wit-bindgen", ] [[package]] name = "wasm-bindgen" -version = "0.2.111" +version = "0.2.113" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec1adf1535672f5b7824f817792b1afd731d7e843d2d04ec8f27e8cb51edd8ac" +checksum = "60722a937f594b7fde9adb894d7c092fc1bb6612897c46368d18e7a20208eff2" dependencies = [ "cfg-if", "once_cell", @@ -3545,9 +3545,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.61" +version = "0.4.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe88540d1c934c4ec8e6db0afa536876c5441289d7f9f9123d4f065ac1250a6b" +checksum = "8a89f4650b770e4521aa6573724e2aed4704372151bd0de9d16a3bbabb87441a" dependencies = [ "cfg-if", "futures-util", @@ -3559,9 +3559,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.111" +version = "0.2.113" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19e638317c08b21663aed4d2b9a2091450548954695ff4efa75bff5fa546b3b1" +checksum = "0fac8c6395094b6b91c4af293f4c79371c163f9a6f56184d2c9a85f5a95f3950" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3569,9 +3569,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.111" +version = "0.2.113" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c64760850114d03d5f65457e96fc988f11f01d38fbaa51b254e4ab5809102af" +checksum = "ab3fabce6159dc20728033842636887e4877688ae94382766e00b180abac9d60" dependencies = [ "bumpalo", "proc-macro2", @@ -3582,9 +3582,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.111" +version = "0.2.113" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60eecd4fe26177cfa3339eb00b4a36445889ba3ad37080c2429879718e20ca41" +checksum = "de0e091bdb824da87dc01d967388880d017a0a9bc4f3bdc0d86ee9f9336e3bb5" dependencies = [ "unicode-ident", ] @@ -3625,9 +3625,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.88" +version = "0.3.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d6bb20ed2d9572df8584f6dc81d68a41a625cadc6f15999d649a70ce7e3597a" +checksum = "705eceb4ce901230f8625bd1d665128056ccbe4b7408faa625eec1ba80f59a97" dependencies = [ "js-sys", "wasm-bindgen", @@ -3990,12 +3990,6 @@ version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" -[[package]] -name = "wit-bindgen" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" - [[package]] name = "wit-bindgen" version = "0.51.0" @@ -4125,18 +4119,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.39" +version = "0.8.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db6d35d663eadb6c932438e763b262fe1a70987f9ae936e60158176d710cae4a" +checksum = "a789c6e490b576db9f7e6b6d661bcc9799f7c0ac8352f56ea20193b2681532e5" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.39" +version = "0.8.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4122cd3169e94605190e77839c9a40d40ed048d305bfdc146e7df40ab0f3e517" +checksum = "f65c489a7071a749c849713807783f70672b28094011623e200cb86dcb835953" dependencies = [ "proc-macro2", "quote", From a3a70d37fce02256d4cab56c6bce926574ae19c7 Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Fri, 6 Mar 2026 12:03:41 +0100 Subject: [PATCH 18/51] Fix CA import test. --- tests/functional_ca_import.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/functional_ca_import.rs b/tests/functional_ca_import.rs index 847d29dc1..d684cef82 100644 --- a/tests/functional_ca_import.rs +++ b/tests/functional_ca_import.rs @@ -8,10 +8,12 @@ mod common; #[cfg(not(any(feature = "hsm-tests-kmip", feature = "hsm-tests-pkcs11")))] #[tokio::test] async fn functional_ca_import() { - let (mut config, _tempdir) = common::TestConfig::mem_storage().finalize(); + let (mut config, tempdir) = common::TestConfig::mem_storage().finalize(); config.ta_support_enabled = true; config.ta_signer_enabled = true; - let server = common::KrillServer::start_with_config(config).await; + let server = common::KrillServer::start_with_config( + config, Some(tempdir) + ).await; eprintln!(">>>> Import CA structure."); // We expect: From 63f0314ef3de5ef387f78eae27f278ed8352a318 Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Fri, 6 Mar 2026 12:14:22 +0100 Subject: [PATCH 19/51] Remove tests directory from CI ignore paths. --- .github/workflows/ci.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4eedd9985..b6e4561f0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,6 @@ on: - 'docker/**' - 'LICENSE' - 'README.md' - - 'tests/e2e/**' # run the tests on creation or update of any pull request pull_request: @@ -33,7 +32,6 @@ on: - 'docker/**' - 'LICENSE' - 'README.md' - - 'tests/e2e/**' jobs: build: From a170c388c2243fc81a9a01db7dca1e3e20329404 Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Mon, 9 Mar 2026 13:57:28 +0100 Subject: [PATCH 20/51] Drop notification file to avoid rename errors. --- src/server/pubd/rrdp.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/server/pubd/rrdp.rs b/src/server/pubd/rrdp.rs index b2c57a371..223e84c9d 100644 --- a/src/server/pubd/rrdp.rs +++ b/src/server/pubd/rrdp.rs @@ -657,6 +657,9 @@ impl RrdpServer { ) })?; + // Drop the notification file to force a sync. + drop(notification); + // Rename the new file so it becomes current. let notification_path = self.notification_path(); fs::rename(¬ification_path_new, ¬ification_path).map_err( From 98ca4775e9653e75715f5d560d9296709073cec5 Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Mon, 9 Mar 2026 14:33:49 +0100 Subject: [PATCH 21/51] Fix worker thread count calculation. --- src/server/runtime.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/server/runtime.rs b/src/server/runtime.rs index 8c1a095b8..dd8a30e66 100644 --- a/src/server/runtime.rs +++ b/src/server/runtime.rs @@ -267,14 +267,22 @@ impl ThreadPool { } } }; - let thread_count = cmp::min(thread_count, 1); + let thread_count = cmp::max(thread_count, 1); let mut join = Vec::new(); for _ in 0..thread_count { let rx = rx.clone(); - join.push(thread::spawn(move || { - Self::worker_thread(rx) - })); + join.push( + thread::Builder::new().name( + "thread-pool".into() + ).spawn(move || { + Self::worker_thread(rx) + }).map_err(|err| { + KrillError::internal( + format_args!("failed to spawn worker thread: {err}") + ) + })? + ); } info!("Created thread pool with {thread_count} threads"); From 3cc1e390c1d3aca0f96cc2c046d06d664f867633 Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Mon, 9 Mar 2026 14:41:10 +0100 Subject: [PATCH 22/51] Fix tests. --- tests/common.rs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/tests/common.rs b/tests/common.rs index 2df97ae93..0b7c3d69b 100644 --- a/tests/common.rs +++ b/tests/common.rs @@ -556,6 +556,14 @@ impl KrillServer { /// Registers a CA with a parent managed by the same server. pub async fn register_ca_with_parent( &self, ca: &CaHandle, parent: &CaHandle, resources: &ResourceSet + ) { + self.register_ca_with_parent_nowait(ca, parent, resources).await; + assert!(self.wait_for_ca_resources(ca, resources).await); + } + + /// Registers a CA with a parent managed by the same server. + async fn register_ca_with_parent_nowait( + &self, ca: &CaHandle, parent: &CaHandle, resources: &ResourceSet ) { let request = self.client().child_request(ca).await.unwrap(); let response = self.add_child( @@ -564,7 +572,16 @@ impl KrillServer { self.client.parent_add( ca, api::admin::ParentCaReq { handle: parent.convert(), response } ).await.unwrap(); - assert!(self.wait_for_ca_resources(ca, resources).await); + } + + /// Creates a CA under testbed with the given resources. + pub async fn create_testbed_ca( + &self, ca: &CaHandle, resources: &ResourceSet + ) { + self.create_ca_with_repo(ca).await; + self.register_ca_with_parent_nowait( + ca, &ca_handle("testbed"), resources + ).await; } /// Add a child to the CA. From 3654bc1ca4019c891594f6f9735ea0858de95b19 Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Mon, 9 Mar 2026 15:47:55 +0100 Subject: [PATCH 23/51] =?UTF-8?q?Don=E2=80=99t=20reschedule=20re-publishin?= =?UTF-8?q?g=20task=20when=20repository=20was=20cleared.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/server/scheduler.rs | 7 +++ tests/client_coverage.rs | 6 ++- tests/common.rs | 6 +++ tests/long_benchmark.rs | 2 +- tests/pubd.rs | 103 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 122 insertions(+), 2 deletions(-) create mode 100644 tests/pubd.rs diff --git a/src/server/scheduler.rs b/src/server/scheduler.rs index 348f25b38..4a5bbf734 100644 --- a/src/server/scheduler.rs +++ b/src/server/scheduler.rs @@ -604,6 +604,13 @@ fn update_snapshots(krill: &KrillRuntime) -> Result { fn update_rrdp_if_needed( krill: &KrillRuntime ) -> Result { + // Because we currently can’t delete tasks, this may be stray if the + // repository was cleared. So, just ignore the task if the repo manager + // isn’t initialized. + if !matches!(krill.repo_manager().is_initialized(), Ok(true)) { + return Ok(TaskResult::Done) + } + match krill.repo_manager().update_rrdp_if_needed() { Err(e) => { error!("Could not update RRDP deltas! Error: {e}"); diff --git a/tests/client_coverage.rs b/tests/client_coverage.rs index 12a4db61c..09c5095d8 100644 --- a/tests/client_coverage.rs +++ b/tests/client_coverage.rs @@ -158,6 +158,8 @@ async fn client_coverage(server: KrillServer) { server.client().publisher_delete(&child.convert()).await.unwrap(); server.client().pubserver_clear().await.unwrap(); + tokio::time::sleep(std::time::Duration::from_secs(1)).await; + // testbed commands tested in testbed // ta_proxy commands tests in functional_ta server.abort().await; @@ -174,9 +176,11 @@ async fn http() { async fn unix() { use std::collections::HashMap; - let (mut config, tempdir) = common::TestConfig::mem_storage() + let (mut config, tempdir) = common::TestConfig::file_storage() .enable_testbed().set_zero_port().enable_ca_refresh().finalize(); + //tempdir.disable_cleanup(true); + // The user that is executing the test gets access to everything let uid = nix::unistd::Uid::current(); let user = nix::unistd::User::from_uid(uid).unwrap().unwrap(); diff --git a/tests/common.rs b/tests/common.rs index 0b7c3d69b..d96a7c7a7 100644 --- a/tests/common.rs +++ b/tests/common.rs @@ -540,6 +540,12 @@ impl KrillServer { // Create the CA self.client.ca_add(ca.clone()).await.unwrap(); + // Add the CA as a publisher + self.register_ca_with_repo(ca).await; + } + + /// Registers a CA with the repository. + pub async fn register_ca_with_repo(&self, ca: &CaHandle) { // Add the CA as a publisher let request = self.client().repo_request(ca).await.unwrap(); self.client().publishers_add(request).await.unwrap(); diff --git a/tests/long_benchmark.rs b/tests/long_benchmark.rs index 0ec125ddd..3f77c8f29 100644 --- a/tests/long_benchmark.rs +++ b/tests/long_benchmark.rs @@ -36,7 +36,7 @@ async fn long_benchmark() { join_all((0..0x30u16).map(|i| async move { let i = i << 8; for j in 0..0xffu16 { - create_ca(&server, i | j).await; + create_ca(server, i | j).await; } })).await; diff --git a/tests/pubd.rs b/tests/pubd.rs new file mode 100644 index 000000000..09362787e --- /dev/null +++ b/tests/pubd.rs @@ -0,0 +1,103 @@ +//! Various test cases for the publication server. + +use std::str::FromStr; +use rpki::uri; +use rpki::ca::idexchange::CaHandle; +use rpki::repository::resources::ResourceSet; +use krill::api::ca::ObjectName; +use krill::api::roa::{RoaConfiguration, RoaConfigurationUpdates}; + +mod common; + +//------------ clear_and_init ------------------------------------------------ + +/// This tests clears and then re-initialises the publication server. +/// +/// The main point is to check that after re-registering a CA as publisher, +/// it publishes again. +#[tokio::test] +async fn clear_and_init() { + let server = common::KrillServer::start_with_testbed().await; + + let ta = common::ca_handle("ta"); + let testbed = common::ca_handle("testbed"); + let ca = common::ca_handle("CA"); + let ca_res = common::resources("AS65000", "10.0.0.0/8", ""); + + let route_resource_set_10_0_0_0_def_1 = + common::roa_conf("10.0.0.0/16-16 => 64496"); + + // Wait for the *testbed* CA to get its certificate. + assert!( + server.wait_for_ca_resources(&testbed, &ResourceSet::all()).await + ); + + eprintln!(">>>> Set up CA under testbed."); + server.create_ca_with_repo(&ca).await; + server.register_ca_with_parent(&ca, &testbed, &ca_res).await; + + eprintln!(">>>> Add ROAs to CA."); + eprintln!(">>>> Add ROAs to CA."); + server.client().roas_update( + &ca, + RoaConfigurationUpdates { + added: vec![ + route_resource_set_10_0_0_0_def_1.clone(), + ], + removed: vec![], + } + ).await.unwrap(); + + assert!( + server.wait_for_objects( + &ca, + &[ + &route_resource_set_10_0_0_0_def_1, + ] + ).await + ); + + // Delete the publisher and clear the repo. + server.client().publisher_delete(&ta.convert()).await.unwrap(); + server.client().publisher_delete(&testbed.convert()).await.unwrap(); + server.client().publisher_delete(&ca.convert()).await.unwrap(); + server.client().pubserver_clear().await.unwrap(); + + tokio::time::sleep(std::time::Duration::from_secs(2)).await; + + server.client().pubserver_init( + uri::Https::from_str("https://localhost/rrdp/").unwrap(), + uri::Rsync::from_str("rsync://localhost/repo/").unwrap(), + ).await.unwrap(); + + server.register_ca_with_repo(&ca).await; + + assert!( + server.wait_for_objects( + &ca, + &[ + &route_resource_set_10_0_0_0_def_1, + ] + ).await + ); +} + + +//------------ Extend KrillServer -------------------------------------------- + +impl common::KrillServer { + pub async fn wait_for_objects( + &self, + ca: &CaHandle, + roas: &[&RoaConfiguration] + ) -> bool { + let mut files = self.expected_objects(ca); + files.push_mft_and_crl(&common::rcn(0)).await; + for roa in roas { + files.push(ObjectName::from(roa.payload).to_string()); + } + files.wait_for_published().await + } +} + + From f3760c4342f5d7018b492b0687fd69517e8ebddb Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Mon, 9 Mar 2026 16:05:04 +0100 Subject: [PATCH 24/51] Pubd tests: Re-sync repo after re-adding. --- src/cli/client.rs | 8 ++++++++ tests/pubd.rs | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/cli/client.rs b/src/cli/client.rs index 7fbd6e0e2..a5c4e5889 100644 --- a/src/cli/client.rs +++ b/src/cli/client.rs @@ -592,6 +592,14 @@ impl KrillClient { ).await } + pub async fn repo_refresh( + &self, ca: &CaHandle, + ) -> Result { + self.post_empty( + ca_path(ca).into_iter().chain(once("sync/repo")), + ).await + } + pub async fn roas_list( &self, ca: &CaHandle ) -> Result { diff --git a/tests/pubd.rs b/tests/pubd.rs index 09362787e..e41e61ed0 100644 --- a/tests/pubd.rs +++ b/tests/pubd.rs @@ -63,7 +63,6 @@ async fn clear_and_init() { server.client().publisher_delete(&ca.convert()).await.unwrap(); server.client().pubserver_clear().await.unwrap(); - tokio::time::sleep(std::time::Duration::from_secs(2)).await; server.client().pubserver_init( uri::Https::from_str("https://localhost/rrdp/").unwrap(), @@ -71,6 +70,7 @@ async fn clear_and_init() { ).await.unwrap(); server.register_ca_with_repo(&ca).await; + server.client().repo_refresh(&ca).await.unwrap(); assert!( server.wait_for_objects( From ae288e57d0627a7d6700011aa342a7136e72a2d1 Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Mon, 9 Mar 2026 16:45:52 +0100 Subject: [PATCH 25/51] Revert attempt to prevent rescheduling of Update RRDP task. --- src/server/scheduler.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/server/scheduler.rs b/src/server/scheduler.rs index 4a5bbf734..348f25b38 100644 --- a/src/server/scheduler.rs +++ b/src/server/scheduler.rs @@ -604,13 +604,6 @@ fn update_snapshots(krill: &KrillRuntime) -> Result { fn update_rrdp_if_needed( krill: &KrillRuntime ) -> Result { - // Because we currently can’t delete tasks, this may be stray if the - // repository was cleared. So, just ignore the task if the repo manager - // isn’t initialized. - if !matches!(krill.repo_manager().is_initialized(), Ok(true)) { - return Ok(TaskResult::Done) - } - match krill.repo_manager().update_rrdp_if_needed() { Err(e) => { error!("Could not update RRDP deltas! Error: {e}"); From f4a1cc9febedb58d3c1d835f42c41d9fd2ee2c2d Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Mon, 9 Mar 2026 16:47:13 +0100 Subject: [PATCH 26/51] Remove test for re-adding repository. --- tests/client_coverage.rs | 2 - tests/pubd.rs | 103 --------------------------------------- 2 files changed, 105 deletions(-) delete mode 100644 tests/pubd.rs diff --git a/tests/client_coverage.rs b/tests/client_coverage.rs index 09c5095d8..0f7a0e751 100644 --- a/tests/client_coverage.rs +++ b/tests/client_coverage.rs @@ -158,8 +158,6 @@ async fn client_coverage(server: KrillServer) { server.client().publisher_delete(&child.convert()).await.unwrap(); server.client().pubserver_clear().await.unwrap(); - tokio::time::sleep(std::time::Duration::from_secs(1)).await; - // testbed commands tested in testbed // ta_proxy commands tests in functional_ta server.abort().await; diff --git a/tests/pubd.rs b/tests/pubd.rs deleted file mode 100644 index e41e61ed0..000000000 --- a/tests/pubd.rs +++ /dev/null @@ -1,103 +0,0 @@ -//! Various test cases for the publication server. - -use std::str::FromStr; -use rpki::uri; -use rpki::ca::idexchange::CaHandle; -use rpki::repository::resources::ResourceSet; -use krill::api::ca::ObjectName; -use krill::api::roa::{RoaConfiguration, RoaConfigurationUpdates}; - -mod common; - -//------------ clear_and_init ------------------------------------------------ - -/// This tests clears and then re-initialises the publication server. -/// -/// The main point is to check that after re-registering a CA as publisher, -/// it publishes again. -#[tokio::test] -async fn clear_and_init() { - let server = common::KrillServer::start_with_testbed().await; - - let ta = common::ca_handle("ta"); - let testbed = common::ca_handle("testbed"); - let ca = common::ca_handle("CA"); - let ca_res = common::resources("AS65000", "10.0.0.0/8", ""); - - let route_resource_set_10_0_0_0_def_1 = - common::roa_conf("10.0.0.0/16-16 => 64496"); - - // Wait for the *testbed* CA to get its certificate. - assert!( - server.wait_for_ca_resources(&testbed, &ResourceSet::all()).await - ); - - eprintln!(">>>> Set up CA under testbed."); - server.create_ca_with_repo(&ca).await; - server.register_ca_with_parent(&ca, &testbed, &ca_res).await; - - eprintln!(">>>> Add ROAs to CA."); - eprintln!(">>>> Add ROAs to CA."); - server.client().roas_update( - &ca, - RoaConfigurationUpdates { - added: vec![ - route_resource_set_10_0_0_0_def_1.clone(), - ], - removed: vec![], - } - ).await.unwrap(); - - assert!( - server.wait_for_objects( - &ca, - &[ - &route_resource_set_10_0_0_0_def_1, - ] - ).await - ); - - // Delete the publisher and clear the repo. - server.client().publisher_delete(&ta.convert()).await.unwrap(); - server.client().publisher_delete(&testbed.convert()).await.unwrap(); - server.client().publisher_delete(&ca.convert()).await.unwrap(); - server.client().pubserver_clear().await.unwrap(); - - - server.client().pubserver_init( - uri::Https::from_str("https://localhost/rrdp/").unwrap(), - uri::Rsync::from_str("rsync://localhost/repo/").unwrap(), - ).await.unwrap(); - - server.register_ca_with_repo(&ca).await; - server.client().repo_refresh(&ca).await.unwrap(); - - assert!( - server.wait_for_objects( - &ca, - &[ - &route_resource_set_10_0_0_0_def_1, - ] - ).await - ); -} - - -//------------ Extend KrillServer -------------------------------------------- - -impl common::KrillServer { - pub async fn wait_for_objects( - &self, - ca: &CaHandle, - roas: &[&RoaConfiguration] - ) -> bool { - let mut files = self.expected_objects(ca); - files.push_mft_and_crl(&common::rcn(0)).await; - for roa in roas { - files.push(ObjectName::from(roa.payload).to_string()); - } - files.wait_for_published().await - } -} - - From 8a6f813ad3d0ca59d2a3d0ca3073c163da909f88 Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Mon, 9 Mar 2026 16:54:58 +0100 Subject: [PATCH 27/51] Switch unix client coverage test back to memory storage. --- tests/client_coverage.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/client_coverage.rs b/tests/client_coverage.rs index 0f7a0e751..dcf5d4dd7 100644 --- a/tests/client_coverage.rs +++ b/tests/client_coverage.rs @@ -174,7 +174,7 @@ async fn http() { async fn unix() { use std::collections::HashMap; - let (mut config, tempdir) = common::TestConfig::file_storage() + let (mut config, tempdir) = common::TestConfig::memory_storage() .enable_testbed().set_zero_port().enable_ca_refresh().finalize(); //tempdir.disable_cleanup(true); From 2bdf90abbd9ff865d7836db37cb24f6a659755ea Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Mon, 9 Mar 2026 16:55:07 +0100 Subject: [PATCH 28/51] Switch unix client coverage test back to memory storage. --- tests/client_coverage.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/client_coverage.rs b/tests/client_coverage.rs index dcf5d4dd7..c8f4ba73a 100644 --- a/tests/client_coverage.rs +++ b/tests/client_coverage.rs @@ -177,8 +177,6 @@ async fn unix() { let (mut config, tempdir) = common::TestConfig::memory_storage() .enable_testbed().set_zero_port().enable_ca_refresh().finalize(); - //tempdir.disable_cleanup(true); - // The user that is executing the test gets access to everything let uid = nix::unistd::Uid::current(); let user = nix::unistd::User::from_uid(uid).unwrap().unwrap(); From a0291ec0145f09e640a97c6f2a76ab06dd82996b Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Mon, 9 Mar 2026 17:03:52 +0100 Subject: [PATCH 29/51] Switch unix client coverage test back to memory storage. --- tests/client_coverage.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/client_coverage.rs b/tests/client_coverage.rs index c8f4ba73a..12a4db61c 100644 --- a/tests/client_coverage.rs +++ b/tests/client_coverage.rs @@ -174,7 +174,7 @@ async fn http() { async fn unix() { use std::collections::HashMap; - let (mut config, tempdir) = common::TestConfig::memory_storage() + let (mut config, tempdir) = common::TestConfig::mem_storage() .enable_testbed().set_zero_port().enable_ca_refresh().finalize(); // The user that is executing the test gets access to everything From 322dc7a14b4faa0f88617e2ec4ada6189ee0c4ea Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Thu, 19 Mar 2026 14:03:59 +0100 Subject: [PATCH 30/51] Fix comment. --- src/server/runtime.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/server/runtime.rs b/src/server/runtime.rs index dd8a30e66..f9961436c 100644 --- a/src/server/runtime.rs +++ b/src/server/runtime.rs @@ -159,8 +159,6 @@ impl KrillRuntime { /// A value of this type is kept by [`KrillRuntime`] behind an arc. struct Components { /// The server configuration. - /// - /// This has to be an arc for now since some components keep a copy. config: Config, /// The base URI for communicating with this server. From 07aec89676f54bc517b38adf038ee54c88ba6101 Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Thu, 19 Mar 2026 14:04:34 +0100 Subject: [PATCH 31/51] Remove unnecessary explicit drop. --- src/server/pubd/rrdp.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/server/pubd/rrdp.rs b/src/server/pubd/rrdp.rs index 223e84c9d..b2c57a371 100644 --- a/src/server/pubd/rrdp.rs +++ b/src/server/pubd/rrdp.rs @@ -657,9 +657,6 @@ impl RrdpServer { ) })?; - // Drop the notification file to force a sync. - drop(notification); - // Rename the new file so it becomes current. let notification_path = self.notification_path(); fs::rename(¬ification_path_new, ¬ification_path).map_err( From 1b6a6c8bbb5cd846428609734fdfa2c0981b9eb8 Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Fri, 20 Mar 2026 13:48:10 +0100 Subject: [PATCH 32/51] Error out if setting up any listener socket fails. --- src/daemon/start.rs | 385 +++++++++++++++++++++++++------------------- 1 file changed, 220 insertions(+), 165 deletions(-) diff --git a/src/daemon/start.rs b/src/daemon/start.rs index a6381c035..482f77c40 100644 --- a/src/daemon/start.rs +++ b/src/daemon/start.rs @@ -6,7 +6,7 @@ //! [`start_krill_daemon`] function. use std::{env, process}; -use std::net::SocketAddr; +use std::net::{SocketAddr, TcpListener as StdTcpListener}; use std::path::Path; use std::sync::Arc; use std::time::Duration; @@ -15,7 +15,8 @@ use log::{error, info, warn}; use hyper_util::rt::{TokioExecutor, TokioIo}; use hyper_util::server::conn; use hyper_util::server::graceful::GracefulShutdown; -use tokio::net::TcpListener; +use tokio::net::{TcpListener as TokioTcpListener}; +use tokio::runtime::{Handle as TokioHandle}; use tokio::sync::{oneshot, watch}; use tokio::task::JoinSet; use tokio_rustls::TlsAcceptor; @@ -152,15 +153,14 @@ pub fn start_krill_daemon( // Start a hyper server for the configured http sockets. for socket_addr in server.config().socket_addresses().into_iter() { - join.spawn_on( - single_http_listener( - server.clone(), - socket_addr, - signal_running.take(), - exit_rx.clone(), - ), + single_http_listener( + server.clone(), + socket_addr, + signal_running.take(), + exit_rx.clone(), + &mut join, tokio.handle(), - ); + )?; } // Start a hyper server for the configured unix sockets. @@ -169,15 +169,14 @@ pub fn start_krill_daemon( server.config().unix_socket_enabled() && let Some(path) = server.config().unix_socket() { - join.spawn_on( - single_unix_listener( - server.clone(), - path.clone(), - signal_running.take(), - exit_rx.clone(), - ), + single_unix_listener( + server.clone(), + path.clone(), + signal_running.take(), + exit_rx.clone(), + &mut join, tokio.handle(), - ); + )?; } tokio.block_on(async { @@ -215,13 +214,35 @@ pub fn start_krill_daemon( /// will also initate closing of all currently open connections. The function /// will return when both the listener and all connections are closed or after /// then seconds. -async fn single_http_listener( +fn single_http_listener( server: Arc, addr: SocketAddr, signal_running: Option>, mut signal_exit: watch::Receiver, -) { - let listener = TcpListener::bind(addr).await.unwrap(); + join: &mut JoinSet<()>, + handle: &TokioHandle, +) -> Result<(), Error> { + let listener = match StdTcpListener::bind(addr) { + Ok(listener) => listener, + Err(err) => { + return Err(Error::Custom(format!( + "Failed to create TCP socket '{addr}': {err}" + ))); + } + }; + if let Err(err) = listener.set_nonblocking(true) { + return Err(Error::Custom(format!( + "Failed to configure TCP socket '{addr}': {err}" + ))); + } + let listener = match TokioTcpListener::from_std(listener) { + Ok(listener) => listener, + Err(err) => { + return Err(Error::Custom(format!( + "Failed to prepare TCP socket '{addr}': {err}" + ))); + } + }; let tls = if server.config().https_mode().is_disable_https() { None @@ -232,8 +253,9 @@ async fn single_http_listener( ) { Ok(config) => Some(TlsAcceptor::from(Arc::new(config))), Err(err) => { - error!("{err}"); - return; + return Err(Error::Custom(format!( + "Failed to create TLS server config: {err}" + ))); } } }; @@ -248,66 +270,74 @@ async fn single_http_listener( let _ = tx.send(()); } - loop { - // Break here already if `signal_exit` is true. - if *signal_exit.borrow_and_update() { - drop(listener); - break; - } - - tokio::select! { - conn = listener.accept() => { - let (stream, _addr) = match conn { - Ok(conn) => conn, - Err(e) => { - warn!("TCP socket accept error: {}", e); - tokio::time::sleep( - Duration::from_millis(100) - ).await; - continue; - } - }; - - let stream = TokioIo::new( - tls::MaybeTlsTcpStream::new( - stream, tls.as_ref() - ) - ); - - let server = weak_server.clone(); - let conn = conn_builder.serve_connection_with_upgrades( - stream, - hyper::service::service_fn(move |req| { - HttpServer::process_request(server.clone(), req) - }) - ); - let conn = graceful.watch(conn.into_owned()); - - tokio::spawn(async move { - if let Err(err) = conn.await { - warn!("TCP connection error: {}", err); - } - }); - }, - - res = signal_exit.changed() => { - // Break if the channel is closed or the new value is `true`. - if res.is_err() || *signal_exit.borrow() { + join.spawn_on( + async move { + loop { + // Break here already if `signal_exit` is true. + if *signal_exit.borrow_and_update() { drop(listener); break; } + + tokio::select! { + conn = listener.accept() => { + let (stream, _addr) = match conn { + Ok(conn) => conn, + Err(e) => { + warn!("TCP socket accept error: {}", e); + tokio::time::sleep( + Duration::from_millis(100) + ).await; + continue; + } + }; + + let stream = TokioIo::new( + tls::MaybeTlsTcpStream::new( + stream, tls.as_ref() + ) + ); + + let server = weak_server.clone(); + let conn = conn_builder.serve_connection_with_upgrades( + stream, + hyper::service::service_fn(move |req| { + HttpServer::process_request(server.clone(), req) + }) + ); + let conn = graceful.watch(conn.into_owned()); + + tokio::spawn(async move { + if let Err(err) = conn.await { + warn!("TCP connection error: {}", err); + } + }); + }, + + res = signal_exit.changed() => { + // Break if the channel is closed or the new value is + // `true`. + if res.is_err() || *signal_exit.borrow() { + drop(listener); + break; + } + } + } } - } - } - tokio::select! { - _ = graceful.shutdown() => { }, - _ = tokio::time::sleep(Duration::from_secs(10)) => { - warn!( - "Waited 10 seconds for TCP listener to shutdown, aborting..." - ); - } - } + tokio::select! { + _ = graceful.shutdown() => { }, + _ = tokio::time::sleep(Duration::from_secs(10)) => { + warn!( + "Waited 10 seconds for TCP listener to shutdown, \ + aborting..." + ); + } + } + }, + handle + ); + Ok(()) } @@ -326,28 +356,46 @@ async fn single_http_listener( /// will return when both the listener and all connections are closed or after /// then seconds. #[cfg(unix)] -async fn single_unix_listener( +fn single_unix_listener( server: Arc, path: std::path::PathBuf, signal_running: Option>, mut signal_exit: watch::Receiver, -) { + join: &mut JoinSet<()>, + handle: &TokioHandle, +) -> Result<(), Error> { + use std::os::unix::net::{UnixListener as StdUnixListener}; use nix::unistd::{Uid, User}; - use tokio::net::UnixListener; + use tokio::net::{UnixListener as TokioUnixListener}; if path.exists() && let Err(err) = std::fs::remove_file(&path) { - error!("Failed to remove existing Unix socket file: {err}"); - return; + return Err(Error::Custom(format!( + "Failed to remove existing Unix socket file: {err}" + ))); } - let listener = match UnixListener::bind(&path) { + let listener = match StdUnixListener::bind(&path) { Ok(listener) => listener, Err(err) => { - error!( + return Err(Error::custom(format!( "Could not bind to Unix socket '{}': {}", - &path.to_string_lossy(), err - ); - return; + path.display(), err + ))); + } + }; + if let Err(err) = listener.set_nonblocking(true) { + return Err(Error::Custom(format!( + "Failed to configure Unix socket '{}': {}", + path.display(), err, + ))); + } + let listener = match TokioUnixListener::from_std(listener) { + Ok(listener) => listener, + Err(err) => { + return Err(Error::Custom(format!( + "Failed to prepare Unix socket '{}': {}", + path.display(), err, + ))); } }; @@ -361,90 +409,97 @@ async fn single_unix_listener( let _ = tx.send(()); } - loop { - // Break here already if `signal_exit` is true. - if *signal_exit.borrow_and_update() { - drop(listener); - break; - } - - tokio::select! { - conn = listener.accept() => { - let (stream, _addr) = match conn { - Ok(stream) => stream, - Err(err) => { - warn!("Unix socket accept error: {}", err); - tokio::time::sleep( - Duration::from_millis(100) - ).await; - continue; - } - }; - + join.spawn_on( + async move { + loop { + // Break here already if `signal_exit` is true. + if *signal_exit.borrow_and_update() { + drop(listener); + break; + } - let uid = match stream.peer_cred() { - Ok(cred) => Uid::from_raw(cred.uid()), - Err(err) => { - warn!( - "Unix socket could not obtain peer credentials: \ - {err}" - ); - continue; - } - }; - let user = match User::from_uid(uid) { - Ok(Some(user)) => user, - Ok(None) => { - error!( - "Unix socket could not obtain user details: \ - unknown user ID." + tokio::select! { + conn = listener.accept() => { + let (stream, _addr) = match conn { + Ok(stream) => stream, + Err(err) => { + warn!("Unix socket accept error: {}", err); + tokio::time::sleep( + Duration::from_millis(100) + ).await; + continue; + } + }; + + + let uid = match stream.peer_cred() { + Ok(cred) => Uid::from_raw(cred.uid()), + Err(err) => { + warn!( + "Unix socket could not obtain peer credentials: \ + {err}" + ); + continue; + } + }; + let user = match User::from_uid(uid) { + Ok(Some(user)) => user, + Ok(None) => { + error!( + "Unix socket could not obtain user details: \ + unknown user ID." + ); + continue; + } + Err(err) => { + error!( + "Unix socket could not obtain user details: {err}" + ); + continue; + }, + }; + + let server = weak_server.clone(); + let conn = conn_builder.serve_connection_with_upgrades( + TokioIo::new(stream), + hyper::service::service_fn(move |mut req| { + let extensions = req.extensions_mut(); + extensions.insert(user.clone()); + HttpServer::process_request(server.clone(), req) + }) ); - continue; - } - Err(err) => { - error!( - "Unix socket could not obtain user details: {err}" - ); - continue; + let conn = graceful.watch(conn.into_owned()); + + tokio::spawn(async move { + if let Err(err) = conn.await { + warn!("Unix connection error: {}", err); + } + }); }, - }; - - let server = weak_server.clone(); - let conn = conn_builder.serve_connection_with_upgrades( - TokioIo::new(stream), - hyper::service::service_fn(move |mut req| { - let extensions = req.extensions_mut(); - extensions.insert(user.clone()); - HttpServer::process_request(server.clone(), req) - }) - ); - let conn = graceful.watch(conn.into_owned()); - - tokio::spawn(async move { - if let Err(err) = conn.await { - warn!("Unix connection error: {}", err); - } - }); - }, - res = signal_exit.changed() => { - // Break if the channel is closed or the new value is `true`. - if res.is_err() || *signal_exit.borrow() { - drop(listener); - break; + res = signal_exit.changed() => { + // Break if the channel is closed or the new value is `true`. + if res.is_err() || *signal_exit.borrow() { + drop(listener); + break; + } + } } } - } - } - tokio::select! { - _ = graceful.shutdown() => { }, - _ = tokio::time::sleep(Duration::from_secs(10)) => { - warn!( - "Waited 10 seconds for TCP listener to shutdown, aborting..." - ); - } - } + tokio::select! { + _ = graceful.shutdown() => { }, + _ = tokio::time::sleep(Duration::from_secs(10)) => { + warn!( + "Waited 10 seconds for Unix listener to \ + shutdown, aborting..." + ); + } + } + }, + handle + ); + Ok(()) } From 399f565964b61b111ac14fed664ebad6e7ce82b0 Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Fri, 20 Mar 2026 14:22:11 +0100 Subject: [PATCH 33/51] Try dropping the notification file only the right one this time. --- src/server/pubd/rrdp.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/server/pubd/rrdp.rs b/src/server/pubd/rrdp.rs index b2c57a371..1d39da518 100644 --- a/src/server/pubd/rrdp.rs +++ b/src/server/pubd/rrdp.rs @@ -656,6 +656,7 @@ impl RrdpServer { e, ) })?; + drop(notification_file_new); // Rename the new file so it becomes current. let notification_path = self.notification_path(); From 151db8adb0a964c91a73d57543b9d7344e44ea80 Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Fri, 20 Mar 2026 14:32:28 +0100 Subject: [PATCH 34/51] Try syncing the file instead of just closing it. --- src/server/pubd/rrdp.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/server/pubd/rrdp.rs b/src/server/pubd/rrdp.rs index 1d39da518..63da5c65a 100644 --- a/src/server/pubd/rrdp.rs +++ b/src/server/pubd/rrdp.rs @@ -651,12 +651,20 @@ impl RrdpServer { KrillIoError::new( format!( "could not write new notification file to {}", - notification_path_new.to_string_lossy() + notification_path_new.display() ), e, ) })?; - drop(notification_file_new); + if let Err(err) = notification_file_new.sync_all() { + return Err(KrillIoError::new( + format!( + "failed to write new notification file '{}'", + notification_path_new.display() + ), + err + ).into()); + } // Rename the new file so it becomes current. let notification_path = self.notification_path(); From 7a34e30ef7e9ddb5d852fbff41a1c45570cea341 Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Fri, 20 Mar 2026 15:49:52 +0100 Subject: [PATCH 35/51] Add some temporary diagnostics. --- src/server/pubd/rrdp.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/server/pubd/rrdp.rs b/src/server/pubd/rrdp.rs index 63da5c65a..c39faa1e0 100644 --- a/src/server/pubd/rrdp.rs +++ b/src/server/pubd/rrdp.rs @@ -668,6 +668,16 @@ impl RrdpServer { // Rename the new file so it becomes current. let notification_path = self.notification_path(); + eprintln!( + "{}: {:?}", + notification_path_new.display(), + fs::exists(¬ification_path_new) + ); + eprintln!( + "{}: {:?}", + notification_path.display(), + fs::exists(¬ification_path) + ); fs::rename(¬ification_path_new, ¬ification_path).map_err( |e| { KrillIoError::new( From 85ccc916788abfad9f049e84e2131fa8268fb414 Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Fri, 20 Mar 2026 16:20:23 +0100 Subject: [PATCH 36/51] Even more diagnostics. --- src/server/pubd/rrdp.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/server/pubd/rrdp.rs b/src/server/pubd/rrdp.rs index c39faa1e0..fa5a98f78 100644 --- a/src/server/pubd/rrdp.rs +++ b/src/server/pubd/rrdp.rs @@ -832,6 +832,7 @@ impl RrdpServer { if path.is_dir() { let _best_effort_rm = fs::remove_dir_all(path); } else { + eprintln!("Deleting stray RRDP file {}", path.display()); let _best_effort_rm = fs::remove_file(path); } } From a5ada4d9ae01d6c87cbc62705906c8dfe96caffb Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Fri, 20 Mar 2026 16:35:08 +0100 Subject: [PATCH 37/51] Different diagnostics. --- src/server/pubd/rrdp.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/server/pubd/rrdp.rs b/src/server/pubd/rrdp.rs index fa5a98f78..50c1bb0f8 100644 --- a/src/server/pubd/rrdp.rs +++ b/src/server/pubd/rrdp.rs @@ -643,6 +643,7 @@ impl RrdpServer { self.session.uuid(), self.serial, snapshot, deltas, ); let notification_path_new = self.notification_path_new(); + eprintln!("Writing notification file."); let mut notification_file_new = file::create_file_with_path(¬ification_path_new)?; notification @@ -665,6 +666,7 @@ impl RrdpServer { err ).into()); } + eprintln!("Done writing notification file."); // Rename the new file so it becomes current. let notification_path = self.notification_path(); @@ -832,7 +834,6 @@ impl RrdpServer { if path.is_dir() { let _best_effort_rm = fs::remove_dir_all(path); } else { - eprintln!("Deleting stray RRDP file {}", path.display()); let _best_effort_rm = fs::remove_file(path); } } From 160ec1dd697a5c3e13490ad228df499b479d9818 Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Mon, 23 Mar 2026 11:57:42 +0100 Subject: [PATCH 38/51] Remove debugging information. --- src/server/pubd/rrdp.rs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/server/pubd/rrdp.rs b/src/server/pubd/rrdp.rs index 50c1bb0f8..63da5c65a 100644 --- a/src/server/pubd/rrdp.rs +++ b/src/server/pubd/rrdp.rs @@ -643,7 +643,6 @@ impl RrdpServer { self.session.uuid(), self.serial, snapshot, deltas, ); let notification_path_new = self.notification_path_new(); - eprintln!("Writing notification file."); let mut notification_file_new = file::create_file_with_path(¬ification_path_new)?; notification @@ -666,20 +665,9 @@ impl RrdpServer { err ).into()); } - eprintln!("Done writing notification file."); // Rename the new file so it becomes current. let notification_path = self.notification_path(); - eprintln!( - "{}: {:?}", - notification_path_new.display(), - fs::exists(¬ification_path_new) - ); - eprintln!( - "{}: {:?}", - notification_path.display(), - fs::exists(¬ification_path) - ); fs::rename(¬ification_path_new, ¬ification_path).map_err( |e| { KrillIoError::new( From 02889c721f78fa1227165764376cc24bb065e597 Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Mon, 23 Mar 2026 17:58:33 +0100 Subject: [PATCH 39/51] Lock access to writing the repository. --- src/server/pubd/content.rs | 38 ++++++++++++++++++++++++++++++++++---- src/server/pubd/manager.rs | 10 +++++++--- 2 files changed, 41 insertions(+), 7 deletions(-) diff --git a/src/server/pubd/content.rs b/src/server/pubd/content.rs index 1fffed833..8c448004b 100644 --- a/src/server/pubd/content.rs +++ b/src/server/pubd/content.rs @@ -3,7 +3,7 @@ use std::fmt; use std::borrow::Cow; use std::path::{Path, PathBuf}; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; use log::{debug, info}; use rpki::uri; use rpki::ca::idexchange::{MyHandle, PublisherHandle}; @@ -39,6 +39,9 @@ pub struct RepositoryContentProxy { /// The handle for the repository content aggregate. default_handle: MyHandle, + + /// A lock for updating the repository. + update_lock: Mutex<()>, } impl RepositoryContentProxy { @@ -52,7 +55,9 @@ impl RepositoryContentProxy { let default_handle = MyHandle::new("0".into()); Ok(RepositoryContentProxy { - store, default_handle, + store, + default_handle, + update_lock: Mutex::new(()), }) } @@ -217,7 +222,32 @@ impl RepositoryContentProxy { &self, rrdp_updates_config: RrdpUpdatesConfig, ) -> KrillResult<()> { - self.read()?.write_repository(rrdp_updates_config) + let content = self.read()?; + self.write_repository_content(content, rrdp_updates_config) + } + + /// Writes the repository using the given content instance. + /// + /// This is similar to [`write_repository`](Self::write_repository) but + /// avoids loading the [`RepositoryContent`] instance when you already + /// have it available. + pub fn write_repository_content( + &self, + content: Arc, + rrdp_updates_config: RrdpUpdatesConfig, + ) -> KrillResult<()> { + // Acquire the guard. Since we don’t actually look at the data, we + // can simply clear a poisoned lock and try locking again. + let _guard = loop { + match self.update_lock.lock() { + Ok(guard) => break guard, + Err(_) => { + self.update_lock.clear_poison(); + } + } + }; + + content.write_repository(rrdp_updates_config) } /// Resets the RRDP session if it is initialized. @@ -425,7 +455,7 @@ impl RepositoryContent { } /// Writes the repository content to disk. - pub fn write_repository( + fn write_repository( &self, config: RrdpUpdatesConfig, ) -> KrillResult<()> { diff --git a/src/server/pubd/manager.rs b/src/server/pubd/manager.rs index 4e43e89bf..61f8cf415 100644 --- a/src/server/pubd/manager.rs +++ b/src/server/pubd/manager.rs @@ -224,7 +224,9 @@ impl RepositoryManager { let content = self.content.update_rrdp( self.rrdp_updates_config )?; - content.write_repository(self.rrdp_updates_config)?; + self.content.write_repository_content( + content, self.rrdp_updates_config + )?; Ok(None) } @@ -245,8 +247,10 @@ impl RepositoryManager { let content = self.content.update_rrdp(self.rrdp_updates_config)?; - // Write the updated repository - NOTE: we no longer lock it. - content.write_repository(self.rrdp_updates_config)?; + // Write the updated repository. + self.content.write_repository_content( + content, self.rrdp_updates_config + )?; Ok(()) } From eccddb9beb8eab35b69afa1602f11f04b08ed4d8 Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Mon, 23 Mar 2026 18:17:43 +0100 Subject: [PATCH 40/51] Add some diagnostics again. --- src/server/pubd/content.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/server/pubd/content.rs b/src/server/pubd/content.rs index 8c448004b..3bcc5b178 100644 --- a/src/server/pubd/content.rs +++ b/src/server/pubd/content.rs @@ -246,8 +246,14 @@ impl RepositoryContentProxy { } } }; - - content.write_repository(rrdp_updates_config) + eprintln!( + "{:?}: Start writing repository content.", self as *const _ + ); + content.write_repository(rrdp_updates_config)?; + eprintln!( + "{:?}: Done writing repository content.", self as *const _ + ); + Ok(()) } /// Resets the RRDP session if it is initialized. From 4fb29bb22c2242ae266d715a6d0b7a9eccdb3c9d Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Mon, 23 Mar 2026 18:30:12 +0100 Subject: [PATCH 41/51] Correctly lock the repository. --- src/server/pubd/content.rs | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/server/pubd/content.rs b/src/server/pubd/content.rs index 3bcc5b178..e02a9e5c6 100644 --- a/src/server/pubd/content.rs +++ b/src/server/pubd/content.rs @@ -246,14 +246,7 @@ impl RepositoryContentProxy { } } }; - eprintln!( - "{:?}: Start writing repository content.", self as *const _ - ); - content.write_repository(rrdp_updates_config)?; - eprintln!( - "{:?}: Done writing repository content.", self as *const _ - ); - Ok(()) + content.write_repository(rrdp_updates_config) } /// Resets the RRDP session if it is initialized. @@ -269,7 +262,7 @@ impl RepositoryContentProxy { self.default_handle.clone(), ) )?; - content.write_repository(rrdp_updates_config) + self.write_repository_content(content, rrdp_updates_config) } else { // repository server was not initialized on this Krill instance. @@ -462,8 +455,7 @@ impl RepositoryContent { /// Writes the repository content to disk. fn write_repository( - &self, - config: RrdpUpdatesConfig, + &self, config: RrdpUpdatesConfig, ) -> KrillResult<()> { self.rrdp.update_rrdp_files(config)?; self.rsync.write(self.rrdp.serial(), self.rrdp.snapshot()) From 6629ffb0a086114ab23697654747eb8f6d03e403 Mon Sep 17 00:00:00 2001 From: Koen Date: Fri, 27 Mar 2026 12:20:02 +0100 Subject: [PATCH 42/51] Add Windows CI --- .github/workflows/ci.yml | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b6e4561f0..ae1e3d70d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,23 +39,41 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-latest] + os: [ubuntu-latest, windows-latest, macOS-latest] # Test against the oldest supported version. # Test against beta Rust to get early warning of any problems that might occur with the upcoming Rust release. # Order: oldest Rust to newest Rust. - rust: [1.88.0, stable, beta] + rust: [1.85.0, stable, beta] # Test with no features and all features. args: ["--no-default-features", "--all-features"] steps: + - if: runner.os == 'Windows' + name: Set git to use LF + run: | + git config --global core.autocrlf false + git config --global core.eol lf - name: Checkout repository uses: actions/checkout@v4 - name: Install Rust uses: hecrj/setup-rust-action@v2 with: rust-version: ${{ matrix.rust }} - - if: matrix.rust == 'stable' && matrix.args == '--all-features' + - if: runner.os == 'Windows' + name: Set VCPKG root + run: echo "VCPKG_ROOT=$env:VCPKG_INSTALLATION_ROOT" | Out-File -FilePath $env:GITHUB_ENV -Append + - if: runner.os == 'Windows' + name: Cache vcpkg + id: cache-vckpg + uses: actions/cache@v4 + with: + path: C:/vcpkg + key: ${{ runner.os }}-vcpkg + - if: runner.os == 'Windows' && steps.cache-vckpg.outputs.cache-hit != 'true' + name: Install OpenSSL for Windows + run: vcpkg install openssl:x64-windows-static-md + - if: matrix.rust == 'stable' && matrix.args == '--all-features' && matrix.os == 'ubuntu-latest' run: cargo clippy ${{ matrix.args }} -- -D warnings - run: cargo build ${{ matrix.args }} --locked - run: cargo test ${{ matrix.args }} -- --test-threads=1 2>&1 @@ -65,7 +83,7 @@ jobs: runs-on: ubuntu-22.04 strategy: matrix: - rust: [1.88.0, stable, beta] + rust: [1.85.0, stable, beta] features: ["hsm", "hsm,hsm-tests-kmip"] steps: - name: Checkout repository @@ -111,7 +129,7 @@ jobs: runs-on: ubuntu-22.04 strategy: matrix: - rust: [1.88.0, stable, beta] + rust: [1.85.0, stable, beta] features: ["hsm,hsm-tests-pkcs11"] steps: - name: Checkout repository @@ -140,4 +158,4 @@ jobs: - name: Dump the SoftHSM2 log if: always() run: | - cat /var/log/syslog + cat /var/log/syslog \ No newline at end of file From c3d08b56c8f2fc32a402e4a0d737b91f0b878523 Mon Sep 17 00:00:00 2001 From: Koen Date: Fri, 27 Mar 2026 12:22:15 +0100 Subject: [PATCH 43/51] Change to Rust 1.88 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ae1e3d70d..5d178d12a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,7 +44,7 @@ jobs: # Test against the oldest supported version. # Test against beta Rust to get early warning of any problems that might occur with the upcoming Rust release. # Order: oldest Rust to newest Rust. - rust: [1.85.0, stable, beta] + rust: [1.88.0, stable, beta] # Test with no features and all features. args: ["--no-default-features", "--all-features"] From 2efdda2b96c2139fff21ab3ae5cf77c017260d1b Mon Sep 17 00:00:00 2001 From: Koen Date: Fri, 27 Mar 2026 12:23:35 +0100 Subject: [PATCH 44/51] Change to Rust 1.88 (really) --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5d178d12a..f384d38d4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -83,7 +83,7 @@ jobs: runs-on: ubuntu-22.04 strategy: matrix: - rust: [1.85.0, stable, beta] + rust: [1.88.0, stable, beta] features: ["hsm", "hsm,hsm-tests-kmip"] steps: - name: Checkout repository @@ -129,7 +129,7 @@ jobs: runs-on: ubuntu-22.04 strategy: matrix: - rust: [1.85.0, stable, beta] + rust: [1.88.0, stable, beta] features: ["hsm,hsm-tests-pkcs11"] steps: - name: Checkout repository From 08df708b06ac44d71da5f9ec6807f4e37d1f82ef Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Fri, 27 Mar 2026 13:58:25 +0100 Subject: [PATCH 45/51] Fix issues flagged during review. --- README.md | 3 +-- src/commons/crypto/signing/signers/pkcs11/context.rs | 2 +- src/daemon/start.rs | 1 - src/server/ca/child.rs | 2 +- src/server/ca/keys.rs | 2 +- src/server/taproxy.rs | 1 - tests/auth_check.rs | 3 --- 7 files changed, 4 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index a42778070..443a8a799 100644 --- a/README.md +++ b/README.md @@ -13,8 +13,7 @@ Krill is a Resource Public Key Infrastructure (RPKI) daemon, featuring a Certificate Authority (CA) and publication server, written in Rust. If you have any feedback, we would love to hear from you. Don’t hesitate to [create an issue on Github](https://github.com/NLnetLabs/krill/issues/new) or post a message on -our [RPKI mailing list](https://lists.nlnetlabs.nl/mailman/listinfo/rpki) or -[Discord server](https://discord.gg/8dvKB5Ykhy). +our [forum](https://community.nlnetlabs.nl/c/rpki/11). For more information please refer to the [documentation](https://krill.docs.nlnetlabs.nl/en/stable/). diff --git a/src/commons/crypto/signing/signers/pkcs11/context.rs b/src/commons/crypto/signing/signers/pkcs11/context.rs index 3869dbdfd..b249f5fd8 100644 --- a/src/commons/crypto/signing/signers/pkcs11/context.rs +++ b/src/commons/crypto/signing/signers/pkcs11/context.rs @@ -262,7 +262,7 @@ impl Pkcs11Context { }) } - fn finalize(&mut self) -> Result<(), Pkcs11Error>{ + fn finalize(&mut self) -> Result<(), Pkcs11Error> { self.logged_cryptoki_call_with_take("Finalize", |cryptoki| { cryptoki.finalize() }) diff --git a/src/daemon/start.rs b/src/daemon/start.rs index 482f77c40..42402c715 100644 --- a/src/daemon/start.rs +++ b/src/daemon/start.rs @@ -110,7 +110,6 @@ pub fn start_krill_daemon( properties_manager.init(KrillVersion::code_version())?; } - // XXX TODO This may need some configuration. let tokio = tokio::runtime::Runtime::new().map_err(|err| { KrillError::custom( format!("Failed to create Tokio runtime: {err}") diff --git a/src/server/ca/child.rs b/src/server/ca/child.rs index af8b6d511..06b3fcd5a 100644 --- a/src/server/ca/child.rs +++ b/src/server/ca/child.rs @@ -396,7 +396,7 @@ pub struct ChildCertificateUpdates { #[serde(skip_serializing_if = "Vec::is_empty", default)] pub suspended: Vec, - /// The certificats that have been unsuspended. + /// The certificates that have been unsuspended. /// /// This is no longer used as of Krill 0.16.0, but kept because it is in /// stored state. diff --git a/src/server/ca/keys.rs b/src/server/ca/keys.rs index a74056219..3cd7ee038 100644 --- a/src/server/ca/keys.rs +++ b/src/server/ca/keys.rs @@ -79,7 +79,7 @@ impl CertifiedKey { /// Updates the certificate received for the key. pub fn set_incoming_cert(&mut self, cert: ReceivedCert) { self.request = None; - self.incoming_cert = cert + self.incoming_cert = cert; } /// Returns the certified key info for this certified key. diff --git a/src/server/taproxy.rs b/src/server/taproxy.rs index ea6e8bf55..7b61b4943 100644 --- a/src/server/taproxy.rs +++ b/src/server/taproxy.rs @@ -784,7 +784,6 @@ impl TrustAnchorProxy { /// a simple call to `into`. #[derive(Clone, Copy)] pub struct TrustAnchorProxyContext<'a> { - #[allow(dead_code)] // XXX remove!! tasks: &'a TaskQueue, signer: &'a KrillSigner, } diff --git a/tests/auth_check.rs b/tests/auth_check.rs index 24d0f5e79..dc3b76f97 100644 --- a/tests/auth_check.rs +++ b/tests/auth_check.rs @@ -12,8 +12,6 @@ mod common; async fn auth_check() { let server = common::KrillServer::start().await; - eprintln!("server is up."); - // Get a client with a changed auth token. let client = KrillClient::new( server.server_uri().clone(), @@ -33,7 +31,6 @@ async fn auth_check() { ) ) ); - eprintln!("back."); } #[tokio::test] From 949b8d5b212ef76f701337c89a28fd4bd2e126a7 Mon Sep 17 00:00:00 2001 From: Koen Date: Fri, 27 Mar 2026 14:13:55 +0100 Subject: [PATCH 46/51] Add sleep to test --- tests/auth_check.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/auth_check.rs b/tests/auth_check.rs index 24d0f5e79..9c49c378b 100644 --- a/tests/auth_check.rs +++ b/tests/auth_check.rs @@ -58,6 +58,8 @@ async fn auth_check_unix() { config, Some(tempdir) ).await; + crate::common::sleep_seconds(3).await; + let client = KrillClient::new( ServerUri::try_from( format!("unix://{}", file_sock.path().display()) From 0fd403eecdaa82cf4d5c944678d4df562c9317e0 Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Fri, 27 Mar 2026 14:19:51 +0100 Subject: [PATCH 47/51] Unroll reqwest error message when printing. --- src/commons/httpclient.rs | 107 +++++++++++++++++++++----------------- 1 file changed, 58 insertions(+), 49 deletions(-) diff --git a/src/commons/httpclient.rs b/src/commons/httpclient.rs index 5ab2a4f56..dc823358c 100644 --- a/src/commons/httpclient.rs +++ b/src/commons/httpclient.rs @@ -474,7 +474,7 @@ pub enum Error { RequestBuild(ErrorUri, ErrorMessage), RequestBuildHttpsCert(RootCertPath, ErrorMessage), - RequestExecute(ErrorUri, ErrorMessage), + RequestExecute(ErrorUri, reqwest::Error), Response(ErrorUri, ErrorMessage), Forbidden(ErrorUri), @@ -483,52 +483,6 @@ pub enum Error { ErrorResponseWithJson(ErrorUri, StatusCode, Box), } -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Error::RequestBuild(uri, msg) => write!( - f, - "Issue creating request for URI: {uri}, error: {msg}" - ), - Error::RequestBuildHttpsCert(path, msg) => { - write!( - f, - "Cannot use configured HTTPS root cert '{path}'. Error: {msg}" - ) - } - - Error::RequestExecute(uri, msg) => { - write!(f, "Issue accessing URI: {uri}, error: {msg}") - } - - Error::Response(uri, msg) => write!( - f, - "Issue processing response from URI: {uri}, error: {msg}" - ), - Error::Forbidden(uri) => { - write!(f, "Got 'Forbidden' response for URI: {uri}") - } - Error::ErrorResponse(uri, code) => { - write!( - f, - "Issue processing response from URI: {uri}, \ - error: unexpected status code {code}" - ) - } - Error::ErrorResponseWithBody(uri, code, e) => { - write!( - f, - "Error response from URI: {uri}, Status: {code}, Error: {e}" - ) - } - Error::ErrorResponseWithJson(uri, code, res) => write!( - f, - "Error response from URI: {uri}, Status: {code}, ErrorResponse: {res}" - ), - } - } -} - impl Error { pub fn request_build(uri: &str, msg: impl fmt::Display) -> Self { Error::RequestBuild(uri.to_string(), msg.to_string()) @@ -548,8 +502,8 @@ impl Error { Error::RequestBuildHttpsCert(path.to_string(), msg.to_string()) } - pub fn execute(uri: &str, msg: impl fmt::Display) -> Self { - Error::RequestExecute(uri.to_string(), msg.to_string()) + pub fn execute(uri: &str, msg: reqwest::Error) -> Self { + Error::RequestExecute(uri.to_string(), msg) } pub fn response(uri: &str, msg: impl fmt::Display) -> Self { @@ -604,3 +558,58 @@ impl Error { } } } + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Error::RequestBuild(uri, msg) => write!( + f, + "Issue creating request for URI: {uri}, error: {msg}" + ), + Error::RequestBuildHttpsCert(path, msg) => { + write!( + f, + "Cannot use configured HTTPS root cert '{path}'. Error: {msg}" + ) + } + + Error::RequestExecute(uri, msg) => { + use std::error::Error as _; + + write!(f, "Issue accessing URI: {uri}, error: {msg}")?; + let mut cause = msg.source(); + while let Some(err) = cause { + write!(f, " - {err}")?; + cause = err.source(); + } + Ok(()) + } + + Error::Response(uri, msg) => write!( + f, + "Issue processing response from URI: {uri}, error: {msg}" + ), + Error::Forbidden(uri) => { + write!(f, "Got 'Forbidden' response for URI: {uri}") + } + Error::ErrorResponse(uri, code) => { + write!( + f, + "Issue processing response from URI: {uri}, \ + error: unexpected status code {code}" + ) + } + Error::ErrorResponseWithBody(uri, code, e) => { + write!( + f, + "Error response from URI: {uri}, Status: {code}, Error: {e}" + ) + } + Error::ErrorResponseWithJson(uri, code, res) => write!( + f, + "Error response from URI: {uri}, Status: {code}, ErrorResponse: {res}" + ), + } + } +} + From 23c1aac4500fdbf0d4d7fd44b080e5d194941010 Mon Sep 17 00:00:00 2001 From: Koen Date: Fri, 27 Mar 2026 15:00:01 +0100 Subject: [PATCH 48/51] Set OPENSSL_DIR explicitly --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f384d38d4..5585d5158 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -73,6 +73,9 @@ jobs: - if: runner.os == 'Windows' && steps.cache-vckpg.outputs.cache-hit != 'true' name: Install OpenSSL for Windows run: vcpkg install openssl:x64-windows-static-md + - if: runner.os == 'Windows' + name: Set VCPKG root + run: echo "OPENSSL_DIR=C:/vcpkg/packages/openssl_x64-windows-static-md" | Out-File -FilePath $env:GITHUB_ENV -Append - if: matrix.rust == 'stable' && matrix.args == '--all-features' && matrix.os == 'ubuntu-latest' run: cargo clippy ${{ matrix.args }} -- -D warnings - run: cargo build ${{ matrix.args }} --locked From 1cc76eabbb6869cb60ce1f0b118abee407f364ff Mon Sep 17 00:00:00 2001 From: Koen Date: Fri, 27 Mar 2026 15:21:06 +0100 Subject: [PATCH 49/51] Pin OpenSSL version to 3.4.0 --- .github/workflows/ci.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5585d5158..fa528125b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -70,12 +70,13 @@ jobs: with: path: C:/vcpkg key: ${{ runner.os }}-vcpkg + - if: runner.os == 'Windows' + name: Make vcpkg.json + run: | + echo '{"dependencies": [ "openssl" ], "overrides": [{ "name": "openssl", "version": "3.4.0" }]}' > vcpkg.json - if: runner.os == 'Windows' && steps.cache-vckpg.outputs.cache-hit != 'true' name: Install OpenSSL for Windows - run: vcpkg install openssl:x64-windows-static-md - - if: runner.os == 'Windows' - name: Set VCPKG root - run: echo "OPENSSL_DIR=C:/vcpkg/packages/openssl_x64-windows-static-md" | Out-File -FilePath $env:GITHUB_ENV -Append + run: vcpkg install --triplet x64-windows-static-md - if: matrix.rust == 'stable' && matrix.args == '--all-features' && matrix.os == 'ubuntu-latest' run: cargo clippy ${{ matrix.args }} -- -D warnings - run: cargo build ${{ matrix.args }} --locked From c7be2e768ce1a128b694fcca29c7e05cf480fa29 Mon Sep 17 00:00:00 2001 From: Koen Date: Fri, 27 Mar 2026 15:25:10 +0100 Subject: [PATCH 50/51] Set builtin-baseline --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fa528125b..e0177f8b4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -73,7 +73,7 @@ jobs: - if: runner.os == 'Windows' name: Make vcpkg.json run: | - echo '{"dependencies": [ "openssl" ], "overrides": [{ "name": "openssl", "version": "3.4.0" }]}' > vcpkg.json + '{"dependencies": ["openssl"], "overrides": [{"name": "openssl", "version": "3.4.0"}], "buitlin-baseline": "4bee3f5aae7aefbc129ca81c33d6a062b02fcf3b"}' | Out-File -FilePath vcpkg.json -Encoding utf8 - if: runner.os == 'Windows' && steps.cache-vckpg.outputs.cache-hit != 'true' name: Install OpenSSL for Windows run: vcpkg install --triplet x64-windows-static-md From 50eff0ff383deefb26f016e90dc1c1b2ddd1c8f9 Mon Sep 17 00:00:00 2001 From: Koen Date: Fri, 27 Mar 2026 15:27:14 +0100 Subject: [PATCH 51/51] Fix spelling --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e0177f8b4..98ba5f470 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -73,7 +73,7 @@ jobs: - if: runner.os == 'Windows' name: Make vcpkg.json run: | - '{"dependencies": ["openssl"], "overrides": [{"name": "openssl", "version": "3.4.0"}], "buitlin-baseline": "4bee3f5aae7aefbc129ca81c33d6a062b02fcf3b"}' | Out-File -FilePath vcpkg.json -Encoding utf8 + '{"dependencies": ["openssl"], "overrides": [{"name": "openssl", "version": "3.4.0"}], "builtin-baseline": "4bee3f5aae7aefbc129ca81c33d6a062b02fcf3b"}' | Out-File -FilePath vcpkg.json -Encoding utf8 - if: runner.os == 'Windows' && steps.cache-vckpg.outputs.cache-hit != 'true' name: Install OpenSSL for Windows run: vcpkg install --triplet x64-windows-static-md