diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..cf2c04e0d --- /dev/null +++ b/.dockerignore @@ -0,0 +1,2 @@ +/target +.git diff --git a/Cargo.toml b/Cargo.toml index e097ea4c3..aebd32b52 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,15 +1,12 @@ [package] name = "krill" -version = "0.4.2-pre" +version = "0.4.2" authors = [ "The NLnet Labs RPKI team " ] description = "Resource Public Key Infrastructure (RPKI) daemon" license = "MPL-2.0" [dependencies] -actix-identity = "0.1.0" actix-web = { version = "1.0.3", features = ["ssl"] } -actix-session = "0.1.0" -actix-service = "0.4.0" base64 = "^0.10" bcder = "0.4.0" bytes = "^0.4" @@ -26,7 +23,7 @@ openssl = { version = "^0.10", features = ["v110"] } pretty = "0.5.2" rand = "^0.5" reqwest = "^0.9.17" -rpki = "0.8.2" +rpki = "0.8.3" serde = { version = "^1.0", features = ["derive"] } serde_json = "^1.0" syslog = "^4.0" @@ -45,7 +42,3 @@ ignore = "^0.4" [features] default = [] extra-debug = [ "rpki/extra-debug" ] - -# Used when depending on development branches of rpki-rs or bcder -#[patch.crates-io] -#rpki = { git = "https://github.com/NLnetLabs/rpki-rs.git", branch = "resource-set-fix" } diff --git a/Changelog.md b/Changelog.md index 645a52293..b04f1b1f5 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,7 +1,21 @@ # Change Log Please see [here](https://github.com/NLnetLabs/krill/projects?query=is%3Aopen+sort%3Aname-asc) -for planned releases. +for planned releases. + +## 0.4.2 'Finer Things' + +This release fixes a bug, and introduces minor usability improvements: +* Certain adjacent resources were encoded incorrectly (#161) +* Let users explicitly specify a repository before adding a parent (#160) +* Allow timezone to be set on the Docker container (#156) +* Improve error messaging when failing to start Krill (#155) +* Improve readability for CLI error responses (#162) +* Introduce configurable size limits for data submitted to Krill (#158) + +Note that contrary to previous versions a new CA is set up without a default repository. For most +users we recommend that a remote (RFC 8181) repository is used, e.g. provided by their RIR or NIR. +A repository MUST be configured before a parent can be added to a CA. ## 0.4.1 'Fogo de Krill' @@ -82,7 +96,6 @@ Known issues: Work for the next release has already started. [Release 0.3](https://github.com/NLnetLabs/krill/projects/6) will focus on (remote) publication, and will also solve the out-of-sync issue. - ## 0.1.0 'A View to a Krill' This is the first version of Krill that we are testing in the real world. Please note that the diff --git a/defaults/krill.conf b/defaults/krill.conf index d53afe59e..e637b503b 100644 --- a/defaults/krill.conf +++ b/defaults/krill.conf @@ -93,4 +93,22 @@ # # Defaults to 10 minutes # -#ca_refresh = 600 \ No newline at end of file +#ca_refresh = 600 + +# Restrict size of messages sent to the API +# +# Default 256 kB +# +# post_limit_api = 262144 + +# Restrict size of messages sent to the RFC 8181 publication protocol +# +# Default 32MB (enough for a keyroll with about 8000 issued certificates) +# +# post_limit_rfc8181 = 33554432 + +# Restrict size of messages sent to the RFC 6492 up-down protocol +# +# Default 1MB (enough for a keyroll with certs of ~400kb, the biggest known cert is 220kB) +# +# post_limit_rfc6492 = 1048576 \ No newline at end of file diff --git a/doc/openapi.yaml b/doc/openapi.yaml index 2534d0705..643fe9363 100644 --- a/doc/openapi.yaml +++ b/doc/openapi.yaml @@ -593,6 +593,9 @@ paths: will be used. In principle CAs can also use this to talk to a local parent CA in the same krill server, but this is inefficient. Therefore it is also possible to add an 'embedded' parent in this case. + + Note that you MUST specify a repository for your CA before you are + allowed to add a parent to it. parameters: - $ref: '#/components/parameters/ca_handle' requestBody: @@ -607,7 +610,14 @@ paths: '403': $ref: '#/components/responses/Forbidden' '400': - $ref: '#/components/schemas/ParentWithHandleExists' + description: Bad request parameters. + content: + application/json: + schema: + oneOf: + - $ref: '#/components/schemas/ParentWithHandleExists' + - $ref: '#/components/responses/ParentNoResponse' + - $ref: '#/components/schemas/NoRepositoryConfiguredYetForCA' '404': $ref: '#/components/responses/UnknownCA' '500': @@ -655,7 +665,13 @@ paths: '200': $ref: '#/components/responses/Success' '400': - $ref: '#/components/responses/UnknownParent' + description: Bad request parameters. + content: + application/json: + schema: + oneOf: + - $ref: '#/components/responses/UnknownParent' + - $ref: '#/components/responses/ParentNoResponse' '403': $ref: '#/components/responses/Forbidden' '404': @@ -719,8 +735,12 @@ paths: - Request new certificates with SIA entries pointing to the new locations. - (best effort) Clean up of the old repository. + The new repository can be embedded, or remote. To use a remote repository, the RFC 8181 Repository Response must be encoded into JSON. + + Note: for most users it's better to use a remote repository, e.g. provided + by your RIR or NIR. parameters: - $ref: '#/components/parameters/ca_handle' requestBody: @@ -1436,6 +1456,18 @@ components: msg: type: string example: Parent with handle exists. + ParentNoResponse: + type: object + required: + - code + - msg + properties: + code: + type: integer + enum: [2308] + msg: + type: string + example: No response from parent. UnknownChild: type: object required: @@ -1460,6 +1492,18 @@ components: msg: type: string example: No known parent for handle. + NoRepositoryConfiguredYetForCA: + type: object + required: + - code + - msg + properties: + code: + type: integer + enum: [2307] + msg: + type: string + example: No repository configured yet for CA. InvalidROADeltaAddingDefinitionAlreadyPresent: type: object required: @@ -1588,6 +1632,12 @@ components: application/json: schema: $ref: '#/components/schemas/UnknownParent' + ParentNoResponse: + description: No response from parent. + content: + application/json: + schema: + $ref: '#/components/schemas/ParentNoResponse' GeneralPublicationServerError: description: General Publication Server error. content: diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh index 0756c9370..e1e742765 100755 --- a/docker/entrypoint.sh +++ b/docker/entrypoint.sh @@ -61,8 +61,7 @@ if [ "$1" == "krill" ]; then # RSYNC and RRDP endpoints to the correct FQDN. We cannot know know the # FQDN which clients use to reach us so the operator must inform this # script via a "-e KRILL_FQDN=some.domain.name" argument to - # "docker run". If KRILL_FQDN is not set assume that the user is - # managing the Krill configuration themselves. + # "docker run". cat << EOF >> ${KRILL_CONF} rsync_base = "rsync://${KRILL_FQDN}/repo/" ${MAGIC} service_uri = "https://${KRILL_FQDN}/" ${MAGIC} @@ -81,4 +80,4 @@ fi # to ensure krill runs as PID 1 as required by Docker for proper signal # handling. This also allows this Docker image to be used to run krill_admin # instead of krill. -exec "$@" \ No newline at end of file +exec "$@" diff --git a/src/bin/krill.rs b/src/bin/krill.rs index 38fe926de..24957a68b 100644 --- a/src/bin/krill.rs +++ b/src/bin/krill.rs @@ -5,9 +5,14 @@ use krill::daemon::http::server; fn main() { match Config::create() { - Ok(config) => server::start(&config).unwrap(), + Ok(config) => { + if let Err(e) = server::start(&config) { + eprintln!("Krill failed to start: {}", e); + ::std::process::exit(1); + } + } Err(e) => { - eprintln!("{}", e); + eprintln!("Krill failed to start: {}", e); ::std::process::exit(1); } } diff --git a/src/bin/krillc.rs b/src/bin/krillc.rs index 0169dd929..fffbb62c4 100644 --- a/src/bin/krillc.rs +++ b/src/bin/krillc.rs @@ -2,7 +2,8 @@ extern crate krill; use krill::cli::options::Options; use krill::cli::report::ReportFormat; -use krill::cli::KrillClient; +use krill::cli::{Error, KrillClient}; +use krill::commons::util::httpclient; fn main() { match Options::from_args() { @@ -12,7 +13,21 @@ fn main() { Ok(()) => {} //, Err(e) => { if format != ReportFormat::None { - eprintln!("{}", e); + match &e { + Error::HttpClientError(httpclient::Error::ErrorWithJson( + _code, + res, + )) => { + if format == ReportFormat::Json { + eprintln!("{}", e); + } else { + eprintln!("Error {}: {}", res.code(), res.msg()); + } + } + _ => { + eprintln!("{}", e); + } + } } ::std::process::exit(1); } diff --git a/src/cli/report.rs b/src/cli/report.rs index fea61620d..4f0e7a19f 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/admin.rs b/src/commons/api/admin.rs index e0db3ad15..5af806fdf 100644 --- a/src/commons/api/admin.rs +++ b/src/commons/api/admin.rs @@ -373,7 +373,7 @@ impl fmt::Display for RepositoryContact { /// This type defines all parent ca details needed to add a parent to a CA #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] pub struct ParentCaReq { - handle: Handle, // the local name the child gave to the parent + handle: ParentHandle, // the local name the child gave to the parent contact: ParentCaContact, // where the parent can be contacted } @@ -382,6 +382,14 @@ impl ParentCaReq { ParentCaReq { handle, contact } } + pub fn handle(&self) -> &ParentHandle { + &self.handle + } + + pub fn contact(&self) -> &ParentCaContact { + &self.contact + } + pub fn unwrap(self) -> (Handle, ParentCaContact) { (self.handle, self.contact) } diff --git a/src/commons/api/ca.rs b/src/commons/api/ca.rs index 8351d55f0..cee083f55 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 } } @@ -1958,9 +1962,9 @@ mod test { let parent_resources_json = include_str!("../../../test-resources/resources/parent_resources.json"); - let parent_resouces: ResourceSet = serde_json::from_str(parent_resources_json).unwrap(); + let parent_resources: ResourceSet = serde_json::from_str(parent_resources_json).unwrap(); - let intersection = parent_resouces.intersection(&child_resources); + let intersection = parent_resources.intersection(&child_resources); assert_eq!(intersection, child_resources); } diff --git a/src/commons/api/mod.rs b/src/commons/api/mod.rs index 8861afb06..4d286b0d7 100644 --- a/src/commons/api/mod.rs +++ b/src/commons/api/mod.rs @@ -330,6 +330,12 @@ pub enum ErrorCode { #[display(fmt = "No known parent for handle")] UnknownParent, + #[display(fmt = "No repository configured yet for CA")] + NoRepositorySet, + + #[display(fmt = "No response from parent")] + ParentNoResponse, + #[display(fmt = "Invalid ROA delta: adding a definition which is already present")] RoaUpdateInvalidDuplicate, @@ -408,6 +414,8 @@ impl From for ErrorCode { 2304 => ErrorCode::DuplicateParent, 2305 => ErrorCode::UnknownChild, 2306 => ErrorCode::UnknownParent, + 2307 => ErrorCode::NoRepositorySet, + 2308 => ErrorCode::ParentNoResponse, // 2400s -> ROA issues 2401 => ErrorCode::RoaUpdateInvalidDuplicate, @@ -468,6 +476,8 @@ impl Into for ErrorCode { ErrorCode::DuplicateParent => 2304, ErrorCode::UnknownChild => 2305, ErrorCode::UnknownParent => 2306, + ErrorCode::NoRepositorySet => 2307, + ErrorCode::ParentNoResponse => 2308, // roa errors ErrorCode::RoaUpdateInvalidDuplicate => 2401, @@ -525,7 +535,7 @@ mod tests { test_code(n) } - for n in 2301..2307 { + for n in 2301..2309 { test_code(n) } diff --git a/src/constants.rs b/src/constants.rs index 372c16e51..c84ec92c5 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -1,4 +1,4 @@ -pub const KRILL_VERSION: &str = "0.4.1"; +pub const KRILL_VERSION: &str = "0.4.2"; pub const KRILL_SERVER_APP: &str = "Krill"; pub const KRILL_CLIENT_APP: &str = "Krill Client"; diff --git a/src/daemon/auth.rs b/src/daemon/auth.rs index 7fa8c2e58..7df4812d6 100644 --- a/src/daemon/auth.rs +++ b/src/daemon/auth.rs @@ -1,13 +1,8 @@ //! Authorization for the API -use actix_identity::Identity; use actix_web::dev::Payload; -use actix_web::web::{self, Json}; use actix_web::{Error, FromRequest, HttpRequest, HttpResponse, ResponseError}; use crate::commons::api::Token; -use crate::daemon::http::server::AppServer; - -pub const AUTH_COOKIE_NAME: &str = "krill_auth"; //------------ Authorizer ---------------------------------------------------- @@ -25,43 +20,14 @@ impl Authorizer { } } - pub fn is_api_allowed(&self, token: &Token) -> bool { - &self.krill_auth_token == token - } -} - -#[derive(Deserialize)] -pub struct Credentials { - token: Token, -} - -pub fn login(server: web::Data, cred: Json, id: Identity) -> HttpResponse { - if server.read().login(cred.token.clone()) { - id.remember("admin".to_string()); - HttpResponse::Ok().finish() - } else { - info!("Failed login attempt {}", cred.token.as_ref()); - HttpResponse::Forbidden().finish() - } -} - -pub fn logout(id: Identity) -> HttpResponse { - id.forget(); - HttpResponse::Ok().finish() -} - -pub fn is_logged_in(id: Identity) -> HttpResponse { - if id.identity().is_some() { - HttpResponse::Ok().finish() - } else { - HttpResponse::Forbidden().finish() + pub fn is_api_allowed(&self, auth: &Auth) -> bool { + match auth { + Auth::Bearer(token) => &self.krill_auth_token == token, + } } } -pub type UserName = String; - pub enum Auth { - User(UserName), Bearer(Token), } @@ -88,11 +54,8 @@ impl FromRequest for Auth { type Future = Result; type Config = (); - fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future { - if let Some(identity) = Identity::from_request(req, payload)?.identity() { - info!("Found user: {}", &identity); - Ok(Auth::User(identity)) - } else if let Some(header) = req.headers().get("Authorization") { + fn from_request(req: &HttpRequest, _payload: &mut Payload) -> Self::Future { + if let Some(header) = req.headers().get("Authorization") { let token = Auth::extract_bearer_token(header.to_str().map_err(|_| AuthError::InvalidToken)?)?; @@ -105,7 +68,7 @@ impl FromRequest for Auth { #[derive(Debug, Display)] pub enum AuthError { - #[display(fmt = "Neither logged in user, nor bearer token found")] + #[display(fmt = "No bearer token found")] Unauthorised, #[display(fmt = "Invalid token")] diff --git a/src/daemon/ca/certauth.rs b/src/daemon/ca/certauth.rs index a3bbb599e..fd7ff5917 100644 --- a/src/daemon/ca/certauth.rs +++ b/src/daemon/ca/certauth.rs @@ -7,16 +7,18 @@ 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::uri; +use rpki::x509::{Serial, Time, Validity}; use crate::commons::api::rrdp::PublishElement; use crate::commons::api::{ self, CertAuthInfo, ChildHandle, EntitlementClass, Entitlements, Handle, IdCertPem, IssuanceRequest, IssuedCert, ObjectsDelta, ParentCaContact, ParentHandle, RcvdCert, RepositoryContact, RequestResourceLimit, ResourceClassName, ResourceSet, RevocationRequest, - RevocationResponse, RoaDefinition, SigningCert, UpdateChildRequest, + RevocationResponse, RoaDefinition, SigningCert, TaCertDetails, TrustAnchorLocator, + UpdateChildRequest, }; use crate::commons::eventsourcing::{Aggregate, StoredEvent}; use crate::commons::remote::builder::{IdCertBuilder, SignedMessageBuilder}; @@ -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,16 @@ 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 get_repository_contact(&self) -> Result<&RepositoryContact> { + self.repository.as_ref().ok_or(Error::RepoNotSet) } pub fn old_repository_contact(&self) -> Option<&RepositoryContact> { @@ -426,6 +451,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 +749,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 +945,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 +964,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 +973,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 +1040,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 +1120,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 +1229,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 +1260,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 +1290,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 +1322,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 +1370,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 +1397,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 +1460,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 +1516,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 +1545,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 a910d4813..098d0ff4f 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 e8fcb35be..6eb48a426 100644 --- a/src/daemon/ca/error.rs +++ b/src/daemon/ca/error.rs @@ -110,6 +110,12 @@ pub enum Error { #[display(fmt = "Error getting list query from new repository: {}", _0)] NewRepoUpdateNotResponsive(String), + #[display(fmt = "Error getting entitlements from parent: {}", _0)] + ParentNotResponsive(String), + + #[display(fmt = "No repository configured.")] + RepoNotSet, + #[display(fmt = "{}", _0)] Custom(String), } @@ -138,6 +144,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 76b480633..36d091b20 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 51ec404e8..b3c4692bc 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)?; + + // 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)?; - let ta = self.ca_store.add(init)?; + // 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(()) } @@ -409,7 +418,7 @@ impl CaServer { } /// Adds a parent to a CA - pub fn ca_add_parent(&self, handle: Handle, parent: ParentCaReq) -> ServerResult<()> { + pub fn ca_parent_add(&self, handle: Handle, parent: ParentCaReq) -> ServerResult<()> { let (parent_handle, parent_contact) = parent.unwrap(); let add = CmdDet::add_parent(&handle, parent_handle, parent_contact); @@ -417,7 +426,7 @@ impl CaServer { } /// Updates a parent of a CA - pub fn ca_update_parent( + pub fn ca_parent_update( &self, handle: Handle, parent: ParentHandle, @@ -428,7 +437,7 @@ impl CaServer { } /// Removes a parent from a CA - pub fn ca_remove_parent(&self, handle: Handle, parent: ParentHandle) -> ServerResult<()> { + pub fn ca_parent_remove(&self, handle: Handle, parent: ParentHandle) -> ServerResult<()> { let upd = CmdDet::remove_parent(&handle, parent); self.send_command(upd) } @@ -750,7 +759,25 @@ impl CaServer { handle: &Handle, parent: &ParentHandle, ) -> ServerResult { - match self.get_ca(&handle)?.parent(parent)? { + let ca = self.get_ca(&handle)?; + let contact = ca.parent(parent)?; + self.get_entitlements_from_parent_and_contact(handle, parent, contact) + // match self.get_ca(&handle)?.parent(parent)? { + // ParentCaContact::Ta(_) => { + // Err(ca::Error::NotAllowedForTa).map_err(ServerError::CertAuth) + // } + // ParentCaContact::Embedded => self.get_entitlements_embedded(handle, parent), + // ParentCaContact::Rfc6492(res) => self.get_entitlements_rfc6492(handle, res), + // } + } + + pub fn get_entitlements_from_parent_and_contact( + &self, + handle: &Handle, + parent: &ParentHandle, + contact: &ParentCaContact, + ) -> ServerResult { + match contact { ParentCaContact::Ta(_) => { Err(ca::Error::NotAllowedForTa).map_err(ServerError::CertAuth) } diff --git a/src/daemon/config.rs b/src/daemon/config.rs index e10c3bac0..2ef40e779 100644 --- a/src/daemon/config.rs +++ b/src/daemon/config.rs @@ -75,6 +75,18 @@ impl ConfigDefaults { fn ca_refresh() -> u32 { 600 } + + fn post_limit_api() -> usize { + 256 * 1024 // 256kB + } + + fn post_limit_rfc8181() -> usize { + 32 * 1024 * 1024 // 32MB (roughly 8000 issued certificates, so a key roll for nicbr and 100% uptake should be okay) + } + + fn post_limit_rfc6492() -> usize { + 1024 * 1024 // 1MB (for ref. the NIC br cert is about 200kB) + } } //------------ Config -------------------------------------------------------- @@ -129,6 +141,15 @@ pub struct Config { #[serde(default = "ConfigDefaults::ca_refresh")] pub ca_refresh: u32, + + #[serde(default = "ConfigDefaults::post_limit_api")] + pub post_limit_api: usize, + + #[serde(default = "ConfigDefaults::post_limit_rfc8181")] + pub post_limit_rfc8181: usize, + + #[serde(default = "ConfigDefaults::post_limit_rfc6492")] + pub post_limit_rfc6492: usize, } /// # Accessors @@ -181,7 +202,7 @@ impl Config { let ip = ConfigDefaults::ip(); let port = ConfigDefaults::port(); let use_ta = true; - let use_ssl = HttpsMode::Generate; + let https_mode = HttpsMode::Generate; let data_dir = data_dir.clone(); let rsync_base = ConfigDefaults::rsync_base(); let service_uri = ConfigDefaults::service_uri(); @@ -193,12 +214,15 @@ impl Config { let syslog_facility = ConfigDefaults::syslog_facility(); let auth_token = Token::from("secret"); let ca_refresh = 3600; + let post_limit_api = ConfigDefaults::post_limit_api(); + let post_limit_rfc8181 = ConfigDefaults::post_limit_rfc8181(); + let post_limit_rfc6492 = ConfigDefaults::post_limit_rfc6492(); Config { ip, port, use_ta, - https_mode: use_ssl, + https_mode, data_dir, rsync_base, service_uri, @@ -209,6 +233,9 @@ impl Config { syslog_facility, auth_token, ca_refresh, + post_limit_api, + post_limit_rfc8181, + post_limit_rfc6492, } } diff --git a/src/daemon/endpoints.rs b/src/daemon/endpoints.rs index 7c1f5baf0..ddc0dface 100644 --- a/src/daemon/endpoints.rs +++ b/src/daemon/endpoints.rs @@ -498,7 +498,7 @@ pub fn ca_add_parent( render_empty_res( server .read() - .ca_add_parent(handle.into_inner(), parent.into_inner()), + .ca_parent_add(handle.into_inner(), parent.into_inner()), ) }) } @@ -514,7 +514,7 @@ pub fn ca_update_parent( render_empty_res( server .read() - .ca_update_parent(ca, parent, contact.into_inner()), + .ca_parent_update(ca, parent, contact.into_inner()), ) }) } @@ -526,7 +526,7 @@ pub fn ca_remove_parent( ) -> HttpResponse { let (ca, parent) = ca_and_parent.into_inner(); if_api_allowed(&server, &auth, || { - render_empty_res(server.read().ca_remove_parent(ca, parent)) + render_empty_res(server.read().ca_parent_remove(ca, parent)) }) } @@ -786,6 +786,8 @@ 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, + ca::Error::ParentNotResponsive(_) => ErrorCode::ParentNoResponse, _ => ErrorCode::CaServerError, } } diff --git a/src/daemon/http/server.rs b/src/daemon/http/server.rs index 7032859f6..bfeb7a5c5 100644 --- a/src/daemon/http/server.rs +++ b/src/daemon/http/server.rs @@ -7,16 +7,14 @@ use std::fs::File; use std::io; use std::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard}; -use actix_session::CookieSession; use actix_web::http::StatusCode; use actix_web::web::{delete, get, post, scope, Path}; -use actix_web::{guard, middleware, web}; +use actix_web::{guard, middleware, web, Resource}; use actix_web::{App, HttpResponse, HttpServer}; use openssl::ssl::{SslAcceptor, SslAcceptorBuilder, SslFiletype, SslMethod}; use bcder::decode; -use crate::daemon::auth::{is_logged_in, login, logout, AUTH_COOKIE_NAME}; use crate::daemon::config::Config; use crate::daemon::endpoints; use crate::daemon::endpoints::*; @@ -47,20 +45,20 @@ pub fn start(config: &Config) -> Result<(), Error> { let https_builder = https_builder(config)?; + let post_limit_api = config.post_limit_api; + let post_limit_rfc8181 = config.post_limit_rfc8181; + let post_limit_rfc6492 = config.post_limit_rfc6492; + HttpServer::new(move || { App::new() .data(server.clone()) .wrap(middleware::Logger::default()) - .wrap( - CookieSession::signed(&[0; 32]) - .name(AUTH_COOKIE_NAME) - .secure(true), - ) .route("/health", get().to(endpoints::health)) // API end-points .service( scope("/api/v1") - // Health + .data(web::JsonConfig::default().limit(post_limit_api)) + // Let the UI check if it's authorized .route("/authorized", get().to(api_authorized)) // Repositories and their publishers (both embedded and remote) .route("/publishers", get().to(list_pbl)) @@ -124,14 +122,18 @@ pub fn start(config: &Config) -> Result<(), Error> { // Methods that are not found should return a bad request and some explanation .default_service(web::route().to(api_bad_request)), ) - // Logged in users for the API - .route("/ui/is_logged_in", get().to(is_logged_in)) - .route("/ui/login", post().to(login)) - .route("/ui/logout", post().to(logout)) - // Identity exchanges for remote publishers - .route("/rfc8181/{handle}", post().to(rfc8181)) - // Provisioning for remote krill clients - .route("/rfc6492/{handle}", post().to(rfc6492)) + // Publication Protocol (RFC 8181) + .service( + Resource::new("/rfc8181/{handle}") + .data(web::PayloadConfig::default().limit(post_limit_rfc8181)) + .route(post().to(rfc8181)), + ) + // Uo-Down Protocol (RFC 6492) + .service( + Resource::new("/rfc6492/{handle}") + .data(web::PayloadConfig::default().limit(post_limit_rfc6492)) + .route(post().to(rfc6492)), + ) // Public TA related methods .route("/ta/ta.tal", get().to(tal)) .route("/ta/ta.cer", get().to(ta_cer)) diff --git a/src/daemon/krillserver.rs b/src/daemon/krillserver.rs index 6e359ead3..fc070322f 100644 --- a/src/daemon/krillserver.rs +++ b/src/daemon/krillserver.rs @@ -12,8 +12,7 @@ use crate::commons::api::{ AddChildRequest, CaRepoDetails, CertAuthHistory, CertAuthInfo, CertAuthInit, CertAuthList, ChildCaInfo, ChildHandle, CurrentRepoState, Handle, ListReply, ParentCaContact, ParentCaReq, ParentHandle, PublishDelta, PublisherDetails, PublisherHandle, RepoInfo, RepositoryContact, - RepositoryUpdate, RoaDefinition, RoaDefinitionUpdates, TaCertDetails, Token, - UpdateChildRequest, + RepositoryUpdate, RoaDefinition, RoaDefinitionUpdates, TaCertDetails, UpdateChildRequest, }; use crate::commons::remote::rfc8183; use crate::commons::util::softsigner::{OpenSslSigner, SignerError}; @@ -147,15 +146,8 @@ impl KrillServer { /// # Authentication impl KrillServer { - pub fn login(&self, token: Token) -> bool { - self.authorizer.is_api_allowed(&token) - } - pub fn is_api_allowed(&self, auth: &Auth) -> bool { - match auth { - Auth::Bearer(token) => self.authorizer.is_api_allowed(token), - Auth::User(_) => true, - } + self.authorizer.is_api_allowed(auth) } } @@ -168,7 +160,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(); @@ -223,7 +215,7 @@ impl KrillServer { } } -/// # Admin CA as parent +/// # Being a parent /// impl KrillServer { pub fn ta(&self) -> KrillRes { @@ -300,7 +292,60 @@ impl KrillServer { let child = self.caserver.ca_show_child(parent, child)?; Ok(child) } +} + +/// # Being a child +/// +impl KrillServer { + /// Returns the child request for a CA, or NONE if the CA cannot be found. + pub fn ca_child_req(&self, handle: &Handle) -> KrillRes { + self.caserver + .get_ca(handle) + .map(|ca| ca.child_request()) + .map_err(Error::CaServerError) + } + + /// Adds a parent to a CA, will check first if the parent can be reached. + pub fn ca_parent_add(&self, handle: Handle, parent: ParentCaReq) -> EmptyRes { + self.ca_parent_reachable(&handle, parent.handle(), parent.contact())?; + Ok(self.caserver.ca_parent_add(handle, parent)?) + } + + /// Updates a parent contact for a CA + pub fn ca_parent_update( + &self, + handle: Handle, + parent: ParentHandle, + contact: ParentCaContact, + ) -> EmptyRes { + self.ca_parent_reachable(&handle, &parent, &contact)?; + Ok(self.caserver.ca_parent_update(handle, parent, contact)?) + } + + fn ca_parent_reachable( + &self, + handle: &Handle, + parent: &ParentHandle, + contact: &ParentCaContact, + ) -> EmptyRes { + self.caserver + .get_entitlements_from_parent_and_contact(handle, parent, contact) + .map_err(|e| { + Error::CaServerError(ca::ServerError::CertAuth(ca::Error::ParentNotResponsive( + e.to_string(), + ))) + })?; + Ok(()) + } + pub fn ca_parent_remove(&self, handle: Handle, parent: ParentHandle) -> EmptyRes { + Ok(self.caserver.ca_parent_remove(handle, parent)?) + } +} + +/// # Bulk background operations CAS +/// +impl KrillServer { /// Republish all CAs that need it. pub fn republish_all(&self) -> EmptyRes { self.caserver.republish_all()?; @@ -362,14 +407,6 @@ impl KrillServer { self.caserver.get_ca_history(handle).ok() } - /// Returns the child request for a CA, or NONE if the CA cannot be found. - pub fn ca_child_req(&self, handle: &Handle) -> KrillRes { - self.caserver - .get_ca(handle) - .map(|ca| ca.child_request()) - .map_err(Error::CaServerError) - } - /// Returns the publisher request for a CA, or NONE of the CA cannot be found. pub fn ca_publisher_req(&self, handle: &Handle) -> Option { self.caserver @@ -381,17 +418,8 @@ 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(()) } @@ -399,46 +427,51 @@ impl KrillServer { /// 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 { - self.caserver - .get_ca(handle) - .map(|ca| { - let contact = ca.repository_contact().clone(); - CaRepoDetails::new(contact) - }) - .map_err(Error::CaServerError) + let ca = self.caserver.get_ca(handle).map_err(Error::CaServerError)?; + let contact = ca + .get_repository_contact() + .map_err(|e| Error::CaServerError(ca::ServerError::CertAuth(e)))?; + Ok(CaRepoDetails::new(contact.clone())) } /// Returns the state of the current configured repo for a ca 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) - }) - .map_err(Error::CaServerError) + let ca = self.caserver.get_ca(handle).map_err(Error::CaServerError)?; + let contact = ca + .get_repository_contact() + .map_err(|e| Error::CaServerError(ca::ServerError::CertAuth(e)))?; + Ok(self.repo_state(handle, contact.as_reponse_opt())) } /// 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(); + + // Add publisher + let req = rfc8183::PublisherRequest::new(None, handle.clone(), id_cert); + self.add_publisher(req)?; + } - 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)?) + 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), + ))); } - RepositoryUpdate::Rfc8181(res) => RepositoryContact::Rfc8181(res), - }; - Ok(self.caserver.update_repo(handle, contact)?) - } + RepositoryContact::Rfc8181(response) + } + }; + + Ok(self.caserver.update_repo(handle, contact)?) } fn repo_state( @@ -462,23 +495,6 @@ impl KrillServer { Ok(self.caserver.ca_update_id(handle)?) } - pub fn ca_add_parent(&self, handle: Handle, parent: ParentCaReq) -> EmptyRes { - Ok(self.caserver.ca_add_parent(handle, parent)?) - } - - pub fn ca_update_parent( - &self, - handle: Handle, - parent: ParentHandle, - contact: ParentCaContact, - ) -> EmptyRes { - Ok(self.caserver.ca_update_parent(handle, parent, contact)?) - } - - pub fn ca_remove_parent(&self, handle: Handle, parent: ParentHandle) -> EmptyRes { - Ok(self.caserver.ca_remove_parent(handle, parent)?) - } - pub fn ca_keyroll_init(&self, handle: Handle) -> EmptyRes { Ok(self .caserver diff --git a/src/daemon/test.rs b/src/daemon/test.rs index 8c82aa3a0..62107fba7 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/lib.rs b/src/lib.rs index 4b6844871..cf4041fdf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,9 +13,6 @@ extern crate futures; extern crate hex; #[macro_use] extern crate log; -extern crate actix_identity; -extern crate actix_service; -extern crate actix_session; extern crate actix_web; extern crate clokwerk; extern crate openssl; diff --git a/src/publish/mod.rs b/src/publish/mod.rs index e15451d68..66dc8bbe7 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 scheduler, this should act as a no-op for + // new CAs which do not yet have any repository configured. + let repo_contact = match ca.get_repository_contact() { + Ok(repo) => repo, + Err(_) => 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/test-resources/resources/child_resources.json b/test-resources/resources/child_resources.json index a0a12b039..1b5edcee7 100644 --- a/test-resources/resources/child_resources.json +++ b/test-resources/resources/child_resources.json @@ -1,5 +1,5 @@ { "asn": "AS10906, AS11284, AS11644, AS11752, AS12136, AS14026, AS14650, AS22548, AS26162, AS53035, AS61580", - "v4": "45.6.52.0/22, 45.184.144.0/22, 45.227.0.0/22, 168.181.20.0/22, 187.16.192.0/19, 189.76.96.0/19, 200.160.0.0/20, 200.189.40.0/22, 200.192.104.0/24, 200.192.108.0/22, 200.192.232.0/22, 200.194.128.0/19, 200.219.130.0/23, 200.219.138.0-200.219.141.255, 200.219.143.0-200.219.148.255, 200.219.154.0-200.219.157.255, 200.219.158.0/23, 200.229.248.0/23", + "v4": "45.6.53.0/24, 45.6.52.0/24, 45.6.54.0/23, 45.184.144.0/22, 45.227.0.0/22, 168.181.20.0/22, 187.16.192.0/19, 189.76.96.0/19, 200.160.0.0/20, 200.189.40.0/22, 200.192.104.0/24, 200.192.108.0/22, 200.192.232.0/22, 200.194.128.0/19, 200.219.130.0/23, 200.219.138.0-200.219.141.255, 200.219.143.0-200.219.148.255, 200.219.154.0-200.219.157.255, 200.219.158.0/23, 200.229.248.0/23", "v6": "2001:12f8::/48, 2001:12f8:2::-2001:12f8:d:ffff:ffff:ffff:ffff:ffff, 2001:12fe::/31, 2801:80:1700::/40, 2801:80:1e00::/40" } \ No newline at end of file diff --git a/tests/ca_embedded.rs b/tests/ca_embedded.rs index 56103d776..5a00876fe 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 e4b27670a..1c95e6453 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 b51ee1edb..360fa7539 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 7972ef938..e160e25a8 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 ae3df480f..c04efccd0 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 4acb430b8..46fbabc4e 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 = {