diff --git a/src/cli/report.rs b/src/cli/report.rs index fea61620..4f0e7a19 100644 --- a/src/cli/report.rs +++ b/src/cli/report.rs @@ -2,11 +2,11 @@ use std::str::{from_utf8_unchecked, FromStr}; use crate::commons::api::{ CaRepoDetails, CertAuthHistory, CertAuthInfo, CertAuthList, ChildCaInfo, CurrentObjects, - ParentCaContact, PublisherDetails, PublisherList, RepositoryContact, RoaDefinition, + CurrentRepoState, ParentCaContact, PublisherDetails, PublisherList, RepositoryContact, + RoaDefinition, }; use crate::commons::remote::api::ClientInfo; use crate::commons::remote::rfc8183; -use commons::api::CurrentRepoState; //------------ ApiResponse --------------------------------------------------- @@ -148,14 +148,19 @@ impl Report for CertAuthInfo { ReportFormat::Text => { let mut res = String::new(); - let base_uri = self.repo_repo().base_uri(); - let rrdp_uri = self.repo_repo().rpki_notify(); - res.push_str(&format!("Name: {}\n", self.handle())); res.push_str("\n"); - res.push_str(&format!("Base uri: {}\n", base_uri)); - res.push_str(&format!("RRDP uri: {}\n", rrdp_uri)); + + if let Some(repo_info) = self.repo_info() { + let base_uri = repo_info.base_uri(); + let rrdp_uri = repo_info.rpki_notify(); + res.push_str(&format!("Base uri: {}\n", base_uri)); + res.push_str(&format!("RRDP uri: {}\n", rrdp_uri)); + } else { + res.push_str("No repository configured.") + } res.push_str("\n"); + res.push_str(&format!("ID cert PEM:\n{}\n", self.id_cert().pem())); res.push_str(&format!("Hash: {}\n", self.id_cert().hash())); res.push_str("\n"); diff --git a/src/commons/api/ca.rs b/src/commons/api/ca.rs index 8351d55f..f7a79ca5 100644 --- a/src/commons/api/ca.rs +++ b/src/commons/api/ca.rs @@ -1473,7 +1473,7 @@ impl fmt::Display for ParentInfo { pub struct CertAuthInfo { handle: Handle, id_cert: IdCertPem, - repo_info: RepoInfo, + repo_info: Option, parents: Vec, resources: ResourceSet, resource_classes: HashMap, @@ -1484,7 +1484,7 @@ impl CertAuthInfo { pub fn new( handle: Handle, id_cert: IdCertPem, - repo_info: RepoInfo, + repo_info: Option, parents: HashMap, resource_classes: HashMap, children: Vec, @@ -1521,8 +1521,8 @@ impl CertAuthInfo { &self.id_cert } - pub fn repo_repo(&self) -> &RepoInfo { - &self.repo_info + pub fn repo_info(&self) -> Option<&RepoInfo> { + self.repo_info.as_ref() } pub fn parents(&self) -> &Vec { @@ -1543,10 +1543,14 @@ impl CertAuthInfo { pub fn published_objects(&self) -> Vec { let mut res = vec![]; - for (_rc_name, rc) in self.resource_classes.iter() { - let name_space = rc.name_space(); - res.append(&mut rc.current_objects().publish(self.repo_repo(), name_space)); + + if let Some(repo_info) = &self.repo_info { + for (_rc_name, rc) in self.resource_classes.iter() { + let name_space = rc.name_space(); + res.append(&mut rc.current_objects().publish(repo_info, name_space)); + } } + res } } diff --git a/src/commons/api/mod.rs b/src/commons/api/mod.rs index 8861afb0..e56bb68f 100644 --- a/src/commons/api/mod.rs +++ b/src/commons/api/mod.rs @@ -330,6 +330,9 @@ pub enum ErrorCode { #[display(fmt = "No known parent for handle")] UnknownParent, + #[display(fmt = "No repository configured yet for CA")] + NoRepositorySet, + #[display(fmt = "Invalid ROA delta: adding a definition which is already present")] RoaUpdateInvalidDuplicate, @@ -408,6 +411,7 @@ impl From for ErrorCode { 2304 => ErrorCode::DuplicateParent, 2305 => ErrorCode::UnknownChild, 2306 => ErrorCode::UnknownParent, + 2307 => ErrorCode::NoRepositorySet, // 2400s -> ROA issues 2401 => ErrorCode::RoaUpdateInvalidDuplicate, @@ -468,6 +472,7 @@ impl Into for ErrorCode { ErrorCode::DuplicateParent => 2304, ErrorCode::UnknownChild => 2305, ErrorCode::UnknownParent => 2306, + ErrorCode::NoRepositorySet => 2307, // roa errors ErrorCode::RoaUpdateInvalidDuplicate => 2401, @@ -525,7 +530,7 @@ mod tests { test_code(n) } - for n in 2301..2307 { + for n in 2301..2308 { test_code(n) } diff --git a/src/daemon/ca/certauth.rs b/src/daemon/ca/certauth.rs index a3bbb599..44856d17 100644 --- a/src/daemon/ca/certauth.rs +++ b/src/daemon/ca/certauth.rs @@ -7,9 +7,9 @@ use std::sync::{Arc, RwLock}; use bytes::Bytes; use chrono::Duration; -use rpki::cert::Cert; +use rpki::cert::{Cert, KeyUsage, Overclaim, TbsCert}; use rpki::crypto::{KeyIdentifier, PublicKey, PublicKeyFormat}; -use rpki::x509::Time; +use rpki::x509::{Serial, Time, Validity}; use crate::commons::api::rrdp::PublishElement; use crate::commons::api::{ @@ -32,6 +32,8 @@ use crate::daemon::ca::{ self, ta_handle, ChildDetails, Cmd, CmdDet, CurrentObjectSetDelta, Error, Evt, EvtDet, Ini, ResourceClass, Result, RouteAuthorization, RouteAuthorizationUpdates, Routes, Signer, }; +use commons::api::{TaCertDetails, TrustAnchorLocator}; +use rpki::uri; //------------ Rfc8183Id --------------------------------------------------- @@ -69,7 +71,7 @@ pub struct CertAuth { id: Rfc8183Id, // Used for RFC 6492 (up-down) and RFC 8181 (publication) - repository: RepositoryContact, + repository: Option, repository_pending_withdraw: Option, parents: HashMap, @@ -94,12 +96,13 @@ impl Aggregate for CertAuth { let (handle, _version, details) = event.unwrap(); let (id, repo_info, ta_opt) = details.unwrap(); - let pubserver = RepositoryContact::embedded(repo_info); - let mut parents = HashMap::new(); let mut resources = HashMap::new(); let mut next_class_name = 0; + let children = HashMap::new(); + let routes = Routes::default(); + if let Some(ta_details) = ta_opt { let key_id = ta_details.cert().subject_key_identifier(); parents.insert(ta_handle(), ParentCaContact::Ta(ta_details)); @@ -109,8 +112,7 @@ impl Aggregate for CertAuth { resources.insert(rcn.clone(), ResourceClass::for_ta(rcn, key_id)); } - let children = HashMap::new(); - let routes = Routes::default(); + let repository = repo_info.map(RepositoryContact::embedded); Ok(CertAuth { handle, @@ -118,7 +120,7 @@ impl Aggregate for CertAuth { id, - repository: pubserver, + repository, repository_pending_withdraw: None, parents, @@ -141,6 +143,19 @@ impl Aggregate for CertAuth { fn apply(&mut self, event: Evt) { self.version += 1; match event.into_details() { + //----------------------------------------------------------------------- + // Being a trust anchor + //----------------------------------------------------------------------- + EvtDet::TrustAnchorMade(details) => { + let key_id = details.cert().subject_key_identifier(); + self.parents + .insert(ta_handle(), ParentCaContact::Ta(details)); + let rcn = ResourceClassName::from(self.next_class_name); + self.next_class_name += 1; + self.resources + .insert(rcn.clone(), ResourceClass::for_ta(rcn, key_id)); + } + //----------------------------------------------------------------------- // Being a parent //----------------------------------------------------------------------- @@ -288,8 +303,10 @@ impl Aggregate for CertAuth { } } EvtDet::RepoUpdated(contact) => { - self.repository_pending_withdraw = Some(self.repository.clone()); - self.repository = contact; + if let Some(current) = &self.repository { + self.repository_pending_withdraw = Some(current.clone()) + } + self.repository = Some(contact); } EvtDet::RepoCleaned(_) => { self.repository_pending_withdraw = None; @@ -304,6 +321,9 @@ impl Aggregate for CertAuth { ); match command.into_details() { + // trust anchor + CmdDet::MakeTrustAnchor(uris, signer) => self.trust_anchor_make(uris, signer), + // being a parent CmdDet::ChildAdd(child, id_cert_opt, resources) => { self.child_add(child, id_cert_opt, resources) @@ -353,7 +373,10 @@ impl Aggregate for CertAuth { impl CertAuth { pub fn as_ca_info(&self) -> CertAuthInfo { let handle = self.handle.clone(); - let repo_info = self.repository.repo_info().clone(); + let repo_info = self + .repository + .as_ref() + .map(|repo| repo.repo_info().clone()); let parents = self.parents.clone(); @@ -411,14 +434,20 @@ impl CertAuth { impl CertAuth { pub fn all_objects(&self) -> Vec { let mut res = vec![]; - for rc in self.resources.values() { - res.append(&mut rc.all_objects(self.repository.repo_info())); + if let Some(repo_info) = self.repository.as_ref().map(|r| r.repo_info()) { + for rc in self.resources.values() { + res.append(&mut rc.all_objects(repo_info)); + } } res } - pub fn repository_contact(&self) -> &RepositoryContact { - &self.repository + pub fn repository_contact(&self) -> Option<&RepositoryContact> { + self.repository.as_ref() + } + + fn get_repository_contact(&self) -> Result<&RepositoryContact> { + self.repository.as_ref().ok_or(Error::RepoNotSet) } pub fn old_repository_contact(&self) -> Option<&RepositoryContact> { @@ -426,6 +455,74 @@ impl CertAuth { } } +/// # Being a trustanchor +/// +impl CertAuth { + fn trust_anchor_make( + &self, + uris: Vec, + signer: Arc>, + ) -> ca::Result> { + let mut signer = signer.write().unwrap(); + + if !self.resources.is_empty() { + return Err(Error::custom("Cannot turn CA with resources into TA")); + } + + let repo_info = self.get_repository_contact()?.repo_info(); + + let key = signer + .create_key(PublicKeyFormat::default()) + .map_err(Error::signer)?; + + let resources = ResourceSet::all_resources(); + + let cert = { + let serial: Serial = Serial::random(signer.deref()).map_err(Error::signer)?; + + let pub_key = signer.get_key_info(&key).map_err(Error::signer)?; + let name = pub_key.to_subject_name(); + + let mut cert = TbsCert::new( + serial, + name.clone(), + Validity::new(Time::five_minutes_ago(), Time::years_from_now(100)), + Some(name), + pub_key.clone(), + KeyUsage::Ca, + Overclaim::Refuse, + ); + + cert.set_basic_ca(Some(true)); + + let ns = ResourceClassName::default().to_string(); + + cert.set_ca_repository(Some(repo_info.ca_repository(&ns))); + cert.set_rpki_manifest(Some( + repo_info.rpki_manifest(&ns, &pub_key.key_identifier()), + )); + cert.set_rpki_notify(Some(repo_info.rpki_notify())); + + cert.set_as_resources(Some(resources.to_as_resources())); + cert.set_v4_resources(Some(resources.to_ip_resources_v4())); + cert.set_v6_resources(Some(resources.to_ip_resources_v6())); + + cert.into_cert(signer.deref(), &key) + .map_err(Error::signer)? + }; + + let tal = TrustAnchorLocator::new(uris, &cert); + + let ta_details = TaCertDetails::new(cert, resources, tal); + + Ok(vec![StoredEvent::new( + &self.handle, + self.version, + EvtDet::TrustAnchorMade(ta_details), + )]) + } +} + /// # Being a parent /// impl CertAuth { @@ -656,15 +753,12 @@ impl CertAuth { removed_certs: &[&Cert], signer: &S, ) -> Result> { + let repo = self.get_repository_contact()?; + self.resources .get(&class_name) .ok_or_else(|| Error::unknown_resource_class(&class_name))? - .republish_certs( - issued_certs, - removed_certs, - self.repository.repo_info(), - signer, - ) + .republish_certs(issued_certs, removed_certs, repo.repo_info(), signer) } /// Updates child IdCert and/or Resource entitlements. @@ -855,7 +949,9 @@ impl CertAuth { /// Adds a parent. This method will return an error in case a parent /// by this name (handle) is already known. fn add_parent(&self, parent: Handle, info: ParentCaContact) -> ca::Result> { - if self.has_parent(&parent) { + if self.repository.is_none() { + Err(Error::RepoNotSet) + } else if self.has_parent(&parent) { Err(Error::DuplicateParent(parent)) } else if self.is_ta() { Err(Error::NotAllowedForTa) @@ -872,6 +968,7 @@ impl CertAuth { /// Removes a parent. Returns an error if it doesn't exist. fn remove_parent(&self, parent: Handle) -> ca::Result> { let _parent = self.parent(&parent)?; + let repo = self.get_repository_contact()?; // remove the parent, the RCs and un-publish everything. let mut deltas = vec![]; @@ -880,7 +977,7 @@ impl CertAuth { .values() .filter(|rc| rc.parent_handle() == &parent) { - deltas.push(rc.withdraw(self.repository.repo_info())); + deltas.push(rc.withdraw(repo.repo_info())); } Ok(vec![EvtDet::parent_removed( @@ -947,9 +1044,9 @@ impl CertAuth { rc: &ResourceClass, signer: &S, ) -> Result> { + let repo = self.get_repository_contact()?; let parent_class_name = entitlement.class_name().clone(); - let req_details_list = - rc.make_request_events(entitlement, self.repository.repo_info(), signer)?; + let req_details_list = rc.make_request_events(entitlement, repo.repo_info(), signer)?; let mut res = vec![]; for details in req_details_list.into_iter() { @@ -1027,7 +1124,8 @@ impl CertAuth { }) { let signer = signer.read().unwrap(); - let delta = rc.withdraw(self.repository.repo_info()); + let repo = self.get_repository_contact()?; + let delta = rc.withdraw(repo.repo_info()); let revocations = rc.revoke(signer.deref())?; debug!( @@ -1135,8 +1233,10 @@ impl CertAuth { .resources .get(&rcn) .ok_or_else(|| Error::unknown_resource_class(&rcn))?; - let evt_details = - rc.update_received_cert(rcvd_cert, self.repository.repo_info(), signer.deref())?; + + let repo = self.get_repository_contact()?; + + let evt_details = rc.update_received_cert(rcvd_cert, repo.repo_info(), signer.deref())?; let mut res = vec![]; let mut version = self.version; @@ -1164,8 +1264,9 @@ impl CertAuth { for (rcn, rc) in self.resources.iter() { let mut started = false; + let repo = self.get_repository_contact()?; for details in rc - .keyroll_initiate(self.repository.repo_info(), duration, signer.deref_mut())? + .keyroll_initiate(repo.repo_info(), duration, signer.deref_mut())? .into_iter() { started = true; @@ -1193,8 +1294,10 @@ impl CertAuth { for (rcn, rc) in self.resources.iter() { let mut activated = false; + let repo = self.get_repository_contact()?; + for details in rc - .keyroll_activate(self.repository.repo_info(), staging, signer.deref())? + .keyroll_activate(repo.repo_info(), staging, signer.deref())? .into_iter() { activated = true; @@ -1223,7 +1326,9 @@ impl CertAuth { .get(&rcn) .ok_or_else(|| Error::unknown_resource_class(&rcn))?; - let finish_details = my_rc.keyroll_finish(self.repository.repo_info())?; + let repo = self.get_repository_contact()?; + + let finish_details = my_rc.keyroll_finish(repo.repo_info())?; info!("Finished key roll for ca: {}, rc: {}", &self.handle, rcn); @@ -1269,7 +1374,7 @@ impl CertAuth { let repo_info = if let PublishMode::NewRepo(info) = mode { info } else { - self.repository.repo_info() + self.get_repository_contact()?.repo_info() }; res.append(&mut rc.republish(auths.as_slice(), repo_info, mode, signer)?); @@ -1296,9 +1401,12 @@ impl CertAuth { let signer = signer.deref(); // check that it is indeed different - if self.repository == new_contact { - return Err(Error::NewRepoUpdateNoChange); + if let Some(contact) = &self.repository { + if contact == &new_contact { + return Err(Error::NewRepoUpdateNoChange); + } } + let info = new_contact.repo_info().clone(); let mut evt_dts = vec![]; @@ -1356,6 +1464,8 @@ impl CertAuth { let signer = signer.read().unwrap(); let mode = PublishMode::Normal; + let repo = self.get_repository_contact()?; + let mut res = vec![]; let mut version = self.version; let all_resources = self.all_resources(); @@ -1410,8 +1520,7 @@ impl CertAuth { for (rcn, rc) in self.resources.iter() { let updates = rc.update_roas(current_auths.as_slice(), &mode, signer.deref())?; if updates.contains_changes() { - let mut delta = - ObjectsDelta::new(self.repository.repo_info().ca_repository(rc.name_space())); + let mut delta = ObjectsDelta::new(repo.repo_info().ca_repository(rc.name_space())); for added in updates.added().into_iter() { delta.add(added); @@ -1440,13 +1549,8 @@ impl CertAuth { for (rcn, (delta, revocations)) in deltas.into_iter() { let rc = self.resources.get(&rcn).unwrap(); - let pub_detail = rc.publish_objects( - self.repository.repo_info(), - delta, - revocations, - &mode, - signer.deref(), - )?; + let pub_detail = + rc.publish_objects(repo.repo_info(), delta, revocations, &mode, signer.deref())?; res.push(StoredEvent::new(&self.handle, version, pub_detail)); version += 1; diff --git a/src/daemon/ca/commands.rs b/src/daemon/ca/commands.rs index a910d481..098d0ff4 100644 --- a/src/daemon/ca/commands.rs +++ b/src/daemon/ca/commands.rs @@ -3,6 +3,8 @@ use std::sync::{Arc, RwLock}; use chrono::Duration; +use rpki::uri; + use crate::commons::api::{ ChildHandle, Entitlements, Handle, IssuanceRequest, ParentCaContact, ParentHandle, RcvdCert, RepositoryContact, ResourceClassName, ResourceSet, RevocationRequest, RevocationResponse, @@ -21,6 +23,11 @@ pub type Cmd = eventsourcing::SentCommand>; #[derive(Clone, Debug)] #[allow(clippy::large_enum_variant)] pub enum CmdDet { + // ------------------------------------------------------------ + // Being a TA + // ------------------------------------------------------------ + MakeTrustAnchor(Vec, Arc>), + // ------------------------------------------------------------ // Being a parent // ------------------------------------------------------------ @@ -108,6 +115,11 @@ pub enum CmdDet { impl fmt::Display for CmdDet { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { + // ------------------------------------------------------------ + // Becoming a trust anchor + // ------------------------------------------------------------ + CmdDet::MakeTrustAnchor(_, _) => write!(f, "Turn into Trust Anchor"), + // ------------------------------------------------------------ // Being a parent // ------------------------------------------------------------ @@ -196,6 +208,15 @@ impl eventsourcing::CommandDetails for CmdDet { } impl CmdDet { + /// Turns this CA into a TrustAnchor + pub fn make_trust_anchor( + handle: &Handle, + uris: Vec, + signer: Arc>, + ) -> Cmd { + eventsourcing::SentCommand::new(handle, None, CmdDet::MakeTrustAnchor(uris, signer)) + } + /// Adds a child to this CA. Will return an error in case you try /// to give the child resources not held by the CA. pub fn child_add( diff --git a/src/daemon/ca/error.rs b/src/daemon/ca/error.rs index e8fcb35b..ba32db41 100644 --- a/src/daemon/ca/error.rs +++ b/src/daemon/ca/error.rs @@ -110,6 +110,9 @@ pub enum Error { #[display(fmt = "Error getting list query from new repository: {}", _0)] NewRepoUpdateNotResponsive(String), + #[display(fmt = "No repository configured.")] + RepoNotSet, + #[display(fmt = "{}", _0)] Custom(String), } @@ -138,6 +141,10 @@ impl Error { pub fn unknown_resource_class(class: impl Display) -> Self { Error::UnknownResourceClass(class.to_string()) } + + pub fn custom(msg: impl fmt::Display) -> Self { + Error::Custom(msg.to_string()) + } } impl std::error::Error for Error {} diff --git a/src/daemon/ca/events.rs b/src/daemon/ca/events.rs index 76b48063..36d091b2 100644 --- a/src/daemon/ca/events.rs +++ b/src/daemon/ca/events.rs @@ -1,25 +1,22 @@ use std::collections::HashMap; use std::fmt; -use std::ops::{Deref, DerefMut}; +use std::ops::DerefMut; use std::sync::{Arc, RwLock}; -use rpki::cert::{Cert, KeyUsage, Overclaim, TbsCert}; -use rpki::crypto::{KeyIdentifier, PublicKeyFormat}; -use rpki::uri; -use rpki::x509::{Serial, Time, Validity}; +use rpki::crypto::KeyIdentifier; use crate::commons::api::{ AddedObject, ChildHandle, Handle, IssuanceRequest, IssuedCert, ObjectName, ObjectsDelta, ParentCaContact, ParentHandle, RcvdCert, RepoInfo, RepositoryContact, ResourceClassName, - ResourceSet, Revocation, RevocationRequest, RevokedObject, TaCertDetails, TrustAnchorLocator, - UpdatedObject, WithdrawnObject, + ResourceSet, Revocation, RevocationRequest, RevokedObject, TaCertDetails, UpdatedObject, + WithdrawnObject, }; use crate::commons::eventsourcing::StoredEvent; use crate::commons::remote::id::IdCert; use crate::daemon::ca::signing::Signer; use crate::daemon::ca::{ - CertifiedKey, ChildDetails, CurrentObjectSetDelta, Error, ResourceClass, Result, Rfc8183Id, - RoaInfo, RouteAuthorization, + CertifiedKey, ChildDetails, CurrentObjectSetDelta, ResourceClass, Result, Rfc8183Id, RoaInfo, + RouteAuthorization, }; //------------ Ini ----------------------------------------------------------- @@ -31,18 +28,27 @@ pub type Ini = StoredEvent; #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] pub struct IniDet { id: Rfc8183Id, - info: RepoInfo, + + // The following two fields need to be kept to maintain data compatibility + // with Krill 0.4.2 installations. + // + // Newer versions of krill will no longer include these fields. I.e. there + // will be no default embedded repository, and trust anchors will be created + // through an explicit command and events. + #[serde(skip_serializing_if = "Option::is_none")] + info: Option, + #[serde(skip_serializing_if = "Option::is_none")] ta_details: Option, } impl IniDet { - pub fn unwrap(self) -> (Rfc8183Id, RepoInfo, Option) { + pub fn unwrap(self) -> (Rfc8183Id, Option, Option) { (self.id, self.info, self.ta_details) } } impl IniDet { - pub fn init(handle: &Handle, info: RepoInfo, signer: Arc>) -> Result { + pub fn init(handle: &Handle, signer: Arc>) -> Result { let mut signer = signer.write().unwrap(); let id = Rfc8183Id::generate(signer.deref_mut())?; Ok(Ini::new( @@ -50,98 +56,16 @@ impl IniDet { 0, IniDet { id, - info, + info: None, ta_details: None, }, )) } - - pub fn init_ta( - handle: &Handle, - info: RepoInfo, - ta_uris: Vec, - signer: Arc>, - ) -> Result { - let mut signer = signer.write().unwrap(); - let id = Rfc8183Id::generate(signer.deref_mut())?; - - let ta = { - let resources = ResourceSet::all_resources(); - let ta_cert = { - let key = signer - .create_key(PublicKeyFormat::default()) - .map_err(|e| Error::SignerError(e.to_string()))?; - - Self::mk_ta_cer(&info, &resources, &key, signer.deref())? - }; - - let tal = TrustAnchorLocator::new(ta_uris, &ta_cert); - - TaCertDetails::new(ta_cert, resources, tal) - }; - - Ok(Ini::new( - handle, - 0, - IniDet { - id, - info, - ta_details: Some(ta), - }, - )) - } - - fn mk_ta_cer( - repo_info: &RepoInfo, - resources: &ResourceSet, - key: &S::KeyId, - signer: &S, - ) -> Result { - let serial: Serial = Serial::random(signer).map_err(Error::signer)?; - - let pub_key = signer.get_key_info(&key).map_err(Error::signer)?; - let name = pub_key.to_subject_name(); - - let mut cert = TbsCert::new( - serial, - name.clone(), - Validity::new(Time::now(), Time::years_from_now(100)), - Some(name), - pub_key.clone(), - KeyUsage::Ca, - Overclaim::Refuse, - ); - - cert.set_basic_ca(Some(true)); - - let ns = ResourceClassName::default().to_string(); - - cert.set_ca_repository(Some(repo_info.ca_repository(&ns))); - cert.set_rpki_manifest(Some( - repo_info.rpki_manifest(&ns, &pub_key.key_identifier()), - )); - cert.set_rpki_notify(Some(repo_info.rpki_notify())); - - cert.set_as_resources(Some(resources.to_as_resources())); - cert.set_v4_resources(Some(resources.to_ip_resources_v4())); - cert.set_v6_resources(Some(resources.to_ip_resources_v6())); - - cert.into_cert(signer.deref(), key).map_err(Error::signer) - } } impl fmt::Display for IniDet { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "Initialised with cert (hash): {}, base_uri: {}, rpki notify: {}", - self.id.key_hash(), - self.info.base_uri(), - self.info.rpki_notify() - )?; - if self.ta_details.is_some() { - write!(f, " AS TA")?; - } + write!(f, "Initialised with ID key hash: {}", self.id.key_hash())?; Ok(()) } } @@ -288,6 +212,9 @@ pub type Evt = StoredEvent; #[allow(clippy::large_enum_variant)] #[serde(rename_all = "snake_case")] pub enum EvtDet { + // Being a Trust Anchor + TrustAnchorMade(TaCertDetails), + // Being a parent Events ChildAdded(ChildHandle, ChildDetails), ChildCertificateIssued(ChildHandle, ResourceClassName, KeyIdentifier), @@ -489,6 +416,11 @@ impl EvtDet { impl fmt::Display for EvtDet { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { + // Being a Trust Anchor + EvtDet::TrustAnchorMade(details) => { + write!(f, "turn into TA with key (hash) {}", details.cert().subject_key_identifier()) + }, + // Being a parent Events EvtDet::ChildAdded(child, details) => { write!( diff --git a/src/daemon/ca/server.rs b/src/daemon/ca/server.rs index 51ec404e..1dfa02c5 100644 --- a/src/daemon/ca/server.rs +++ b/src/daemon/ca/server.rs @@ -75,21 +75,30 @@ impl CaServer { if self.ca_store.has(&handle) { Err(ServerError::TrustAnchorInitialisedError) } else { - let init = IniDet::init_ta(&handle, info, ta_uris, self.signer.clone())?; + // init normal CA + let init = IniDet::init(&handle, self.signer.clone())?; + self.ca_store.add(init)?; - let ta = self.ca_store.add(init)?; + // add embedded repo + let embedded = RepositoryContact::embedded(info); + let upd_repo_cmd = CmdDet::update_repo(&handle, embedded, self.signer.clone()); + self.ca_store.command(upd_repo_cmd)?; + // make trust anchor + let make_ta_cmd = CmdDet::make_trust_anchor(&handle, ta_uris, self.signer.clone()); + let ta = self.ca_store.command(make_ta_cmd)?; + + // receive the self signed cert (now as child of self) let ta_cert = ta.parent(&handle).unwrap().to_ta_cert(); let rcvd_cert = RcvdCert::new(ta_cert.clone(), ta_aia, ResourceSet::all_resources()); - let command = CmdDet::upd_received_cert( + let rcv_cert = CmdDet::upd_received_cert( &handle, ResourceClassName::default(), rcvd_cert, self.signer.clone(), ); - - self.ca_store.command(command)?; + self.ca_store.command(rcv_cert)?; Ok(()) } @@ -392,12 +401,12 @@ impl CaServer { ) } - /// Initialises an embedded CA, without any parents (for now). - pub fn init_ca(&self, handle: &Handle, repo_info: RepoInfo) -> ServerResult<()> { + /// Initialises a CA without a repo, no parents, no children, no nothing + pub fn init_ca(&self, handle: &Handle) -> ServerResult<()> { if self.ca_store.has(handle) { Err(ServerError::DuplicateCa(handle.to_string())) } else { - let init = IniDet::init(handle, repo_info, self.signer.clone())?; + let init = IniDet::init(handle, self.signer.clone())?; self.ca_store.add(init)?; Ok(()) } diff --git a/src/daemon/endpoints.rs b/src/daemon/endpoints.rs index 7c1f5baf..9c4307ed 100644 --- a/src/daemon/endpoints.rs +++ b/src/daemon/endpoints.rs @@ -786,6 +786,7 @@ impl ToErrorCode for ca::Error { ca::Error::AuthorisationInvalidMaxlength(_, _) => ErrorCode::RoaUpdateInvalidMaxlength, ca::Error::NewRepoUpdateNoChange => ErrorCode::NewRepoNoChange, ca::Error::NewRepoUpdateNotResponsive(_) => ErrorCode::NewRepoNoResponse, + ca::Error::RepoNotSet => ErrorCode::NoRepositorySet, _ => ErrorCode::CaServerError, } } diff --git a/src/daemon/krillserver.rs b/src/daemon/krillserver.rs index 6e359ead..badd1e7a 100644 --- a/src/daemon/krillserver.rs +++ b/src/daemon/krillserver.rs @@ -168,7 +168,7 @@ impl KrillServer { /// Adds the publishers, blows up if it already existed. pub fn add_publisher( - &mut self, + &self, req: rfc8183::PublisherRequest, ) -> KrillRes { let publisher_handle = req.publisher_handle().clone(); @@ -381,64 +381,64 @@ impl KrillServer { pub fn ca_init(&mut self, init: CertAuthInit) -> EmptyRes { let handle = init.unpack(); - let repo_info = self.pubserver.repo_info_for(&handle)?; - // Create CA - self.caserver.init_ca(&handle, repo_info)?; - - let ca = self.caserver.get_ca(&handle)?; - let id_cert = ca.id_cert().clone(); - - // Add publisher - let req = rfc8183::PublisherRequest::new(None, handle.clone(), id_cert); - self.add_publisher(req)?; + self.caserver.init_ca(&handle)?; Ok(()) } /// 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, handle: &Handle) -> KrillRes { + pub fn ca_repo_details(&self, handle: &Handle) -> KrillRes> { self.caserver .get_ca(handle) .map(|ca| { - let contact = ca.repository_contact().clone(); - CaRepoDetails::new(contact) + ca.repository_contact() + .map(|repo| CaRepoDetails::new(repo.clone())) }) .map_err(Error::CaServerError) } /// Returns the state of the current configured repo for a ca - pub fn ca_repo_state(&self, handle: &Handle) -> KrillRes { + pub fn ca_repo_state(&self, handle: &Handle) -> KrillRes> { self.caserver .get_ca(handle) .map(|ca| { - let contact = ca.repository_contact().clone(); - let repo_opt = contact.as_reponse_opt(); - self.repo_state(handle, repo_opt) + ca.repository_contact() + .map(|repo| self.repo_state(handle, repo.as_reponse_opt())) }) .map_err(Error::CaServerError) } /// Update the repository for a CA, or return an error. (see `CertAuth::repo_update`) pub fn ca_update_repo(&self, handle: Handle, update: RepositoryUpdate) -> EmptyRes { - // first check that the new repo can be contacted - let repo = update.as_response_opt(); + let contact = match update { + RepositoryUpdate::Embedded => { + // Add to embedded publication server if not present + if self.pubserver.get_publisher_details(&handle).is_err() { + let ca = self.caserver.get_ca(&handle)?; + let id_cert = ca.id_cert().clone(); - if let CurrentRepoState::Error(msg) = self.repo_state(&handle, repo) { - Err(Error::CaServerError(ca::ServerError::CertAuth( - ca::Error::NewRepoUpdateNotResponsive(msg), - ))) - } else { - let contact = match update { - RepositoryUpdate::Embedded => { - RepositoryContact::embedded(self.pubserver.repo_info_for(&handle)?) + // Add publisher + let req = rfc8183::PublisherRequest::new(None, handle.clone(), id_cert); + self.add_publisher(req)?; } - RepositoryUpdate::Rfc8181(res) => RepositoryContact::Rfc8181(res), - }; - Ok(self.caserver.update_repo(handle, contact)?) - } + RepositoryContact::embedded(self.pubserver.repo_info_for(&handle)?) + } + RepositoryUpdate::Rfc8181(response) => { + // first check that the new repo can be contacted + if let CurrentRepoState::Error(msg) = self.repo_state(&handle, Some(&response)) { + return Err(Error::CaServerError(ca::ServerError::CertAuth( + ca::Error::NewRepoUpdateNotResponsive(msg), + ))); + } + + RepositoryContact::Rfc8181(response) + } + }; + + Ok(self.caserver.update_repo(handle, contact)?) } fn repo_state( diff --git a/src/daemon/test.rs b/src/daemon/test.rs index 8c82aa3a..62107fba 100644 --- a/src/daemon/test.rs +++ b/src/daemon/test.rs @@ -11,7 +11,7 @@ use crate::cli::{Error, KrillClient}; use crate::commons::api::{ AddChildRequest, CertAuthInfo, CertAuthInit, CertifiedKeyInfo, ChildAuthRequest, ChildHandle, Handle, ParentCaContact, ParentCaReq, ParentHandle, Publish, PublisherDetails, PublisherHandle, - ResourceClassKeysInfo, ResourceClassName, ResourceSet, RoaDefinitionUpdates, + RepositoryUpdate, ResourceClassKeysInfo, ResourceClassName, ResourceSet, RoaDefinitionUpdates, UpdateChildRequest, }; use crate::commons::remote::rfc8183; @@ -155,9 +155,14 @@ fn refresh_all() { krill_admin(Command::Bulk(BulkCaCommand::Refresh)); } -pub fn init_child(handle: &Handle) { - let init = CertAuthInit::new(handle.clone()); - krill_admin(Command::CertAuth(CaCommand::Init(init))); +pub fn init_child_with_embedded_repo(handle: &Handle) { + krill_admin(Command::CertAuth(CaCommand::Init(CertAuthInit::new( + handle.clone(), + )))); + krill_admin(Command::CertAuth(CaCommand::RepoUpdate( + handle.clone(), + RepositoryUpdate::Embedded, + ))); } pub fn generate_new_id(handle: &Handle) { diff --git a/src/publish/mod.rs b/src/publish/mod.rs index e15451d6..7abdff03 100644 --- a/src/publish/mod.rs +++ b/src/publish/mod.rs @@ -34,7 +34,14 @@ impl CaPublisher { pub fn publish(&self, ca_handle: &Handle) -> Result<(), Error> { let ca = self.caserver.get_ca(ca_handle)?; - let list_reply = match ca.repository_contact() { + // Since this is called by the schedular, this should act as a no-op for + // new CAs which do not yet have any repository configured. + let repo_contact = match ca.repository_contact() { + Some(repo) => repo, + None => return Ok(()), + }; + + let list_reply = match &repo_contact { RepositoryContact::Embedded(_) => self.pubserver.list(ca_handle)?, RepositoryContact::Rfc8181(repo) => self.caserver.send_rfc8181_list(ca_handle, repo)?, }; @@ -69,7 +76,7 @@ impl CaPublisher { PublishDelta::new(publishes, updates, withdraws) }; - match ca.repository_contact() { + match &repo_contact { RepositoryContact::Embedded(_) => self.pubserver.publish(ca_handle.clone(), delta)?, RepositoryContact::Rfc8181(repo) => { self.caserver.send_rfc8181_delta(ca_handle, repo, delta)? diff --git a/tests/ca_embedded.rs b/tests/ca_embedded.rs index 56103d77..5a00876f 100644 --- a/tests/ca_embedded.rs +++ b/tests/ca_embedded.rs @@ -12,7 +12,7 @@ fn ca_embedded() { let child = Handle::from_str_unsafe("child"); let child_resources = ResourceSet::from_strs("", "10.0.0.0/16", "").unwrap(); - init_child(&child); + init_child_with_embedded_repo(&child); // Embedded parent -------------------------------------------------------------------- let parent = { diff --git a/tests/ca_grandchildren.rs b/tests/ca_grandchildren.rs index e4b27670..1c95e645 100644 --- a/tests/ca_grandchildren.rs +++ b/tests/ca_grandchildren.rs @@ -36,7 +36,7 @@ fn ca_grandchildren() { let ca1 = Handle::from_str_unsafe("CA1"); let ca1_res = ResourceSet::from_strs("", "10.0.0.0/16", "").unwrap(); - init_child(&ca1); + init_child_with_embedded_repo(&ca1); let req = child_request(&ca1); let parent = { let contact = add_child_to_ta_rfc6492(&ca1, req, ca1_res.clone()); @@ -60,7 +60,7 @@ fn ca_grandchildren() { let ca2 = Handle::from_str_unsafe("CA2"); let ca2_res = ResourceSet::from_strs("", "10.1.0.0/16", "").unwrap(); - init_child(&ca2); + init_child_with_embedded_repo(&ca2); let req = child_request(&ca2); let parent = { let contact = add_child_to_ta_rfc6492(&ca2, req, ca2_res.clone()); @@ -87,7 +87,7 @@ fn ca_grandchildren() { let ca3 = Handle::from_str_unsafe("CA3"); let ca_3_res_under_ca_1 = ResourceSet::from_strs("", "10.0.0.0/16", "").unwrap(); - init_child(&ca3); + init_child_with_embedded_repo(&ca3); let req = child_request(&ca3); let parent = { let contact = add_child_rfc6492(&ca1, &ca3, req, ca_3_res_under_ca_1.clone()); @@ -132,7 +132,7 @@ fn ca_grandchildren() { let ca4 = Handle::from_str_unsafe("CA4"); let ca_4_res_under_ca_3 = ResourceSet::from_strs("", "10.0.0.0-10.1.0.255", "").unwrap(); - init_child(&ca4); + init_child_with_embedded_repo(&ca4); let req = child_request(&ca4); let parent = { let contact = add_child_rfc6492(&ca3, &ca4, req, ca_4_res_under_ca_3.clone()); diff --git a/tests/ca_keyroll_rfc6492.rs b/tests/ca_keyroll_rfc6492.rs index b51ee1ed..360fa753 100644 --- a/tests/ca_keyroll_rfc6492.rs +++ b/tests/ca_keyroll_rfc6492.rs @@ -12,7 +12,7 @@ fn ca_keyroll_rfc6492() { let child = Handle::from_str_unsafe("rfc6492"); let child_resources = ResourceSet::from_strs("", "10.0.0.0/16", "").unwrap(); - init_child(&child); + init_child_with_embedded_repo(&child); let req = child_request(&child); // RFC6492 parent -------------------------------------------------------------------- diff --git a/tests/ca_rfc6492.rs b/tests/ca_rfc6492.rs index 7972ef93..e160e25a 100644 --- a/tests/ca_rfc6492.rs +++ b/tests/ca_rfc6492.rs @@ -12,7 +12,7 @@ fn ca_rfc6492() { let child = Handle::from_str_unsafe("rfc6492"); let child_resources = ResourceSet::from_strs("", "10.0.0.0/16", "").unwrap(); - init_child(&child); + init_child_with_embedded_repo(&child); // Add child to parent (ta) let parent = { diff --git a/tests/ca_roas.rs b/tests/ca_roas.rs index ae3df480..c04efccd 100644 --- a/tests/ca_roas.rs +++ b/tests/ca_roas.rs @@ -18,7 +18,7 @@ fn ca_roas() { let child = Handle::from_str_unsafe("child"); let child_resources = ResourceSet::from_strs("", "10.0.0.0/16", "2001:DB8::/32").unwrap(); - init_child(&child); + init_child_with_embedded_repo(&child); // Set up under parent ---------------------------------------------------------------- { diff --git a/tests/remote_publication.rs b/tests/remote_publication.rs index 4acb430b..46fbabc4 100644 --- a/tests/remote_publication.rs +++ b/tests/remote_publication.rs @@ -17,9 +17,9 @@ use krill::commons::api::{ use krill::commons::remote::rfc8183; use krill::daemon::ca::ta_handle; use krill::daemon::test::{ - add_child_to_ta_embedded, add_parent_to_ca, ca_route_authorizations_update, init_child, - krill_admin, krill_pubd_admin, start_krill_pubd_server, test_with_krill_server, wait_for, - wait_for_current_resources, PubdTestContext, + add_child_to_ta_embedded, add_parent_to_ca, ca_route_authorizations_update, + init_child_with_embedded_repo, krill_admin, krill_pubd_admin, start_krill_pubd_server, + test_with_krill_server, wait_for, wait_for_current_resources, PubdTestContext, }; fn repository_response( @@ -90,7 +90,7 @@ fn remote_publication() { // Set up child as a child of the TA { - init_child(&child); + init_child_with_embedded_repo(&child); let child_resources = ResourceSet::from_strs("", "10.0.0.0/16", "").unwrap(); let parent = {