From 6ceec7efbfdf20253bae0ae36a2b9ab0ffadd5ff Mon Sep 17 00:00:00 2001 From: Tim Bruijnzeels Date: Tue, 22 Oct 2019 09:13:36 +0200 Subject: [PATCH] Add IdCert to Publishers so we can verify RFC8183 messages there. (#107) --- defaults/krill.conf | 11 --------- src/cli/client.rs | 4 ++-- src/cli/options.rs | 47 ++++++++++++++++++++++++++++++++++++- src/cli/report.rs | 8 ++++--- src/commons/api/admin.rs | 45 +++++++++++++++++------------------ src/commons/remote/proxy.rs | 7 ++---- src/daemon/ca/server.rs | 5 ++-- src/daemon/krillserver.rs | 32 +++++++++++++++++-------- src/daemon/test.rs | 4 +--- src/pubd/publishers.rs | 13 +++++++--- src/pubd/pubserver.rs | 27 +++++++++++++++++---- tests/admin_publishers.rs | 7 +++--- 12 files changed, 139 insertions(+), 71 deletions(-) diff --git a/defaults/krill.conf b/defaults/krill.conf index 5b0f9f60..13b8dce9 100644 --- a/defaults/krill.conf +++ b/defaults/krill.conf @@ -8,17 +8,6 @@ # # use_ta = false -# Specify whether https is to be used instead of plain http. Allowed values are -# "yes" and "test". Defaults to "test". -# -# "yes" : The server will look for a 'cert.pem' and 'key.pem' file, under the -# 'ssl' sub-folder of the 'data_dir' specified below. This option is -# untested. We recommend that you use 'tesst" and a proxy server for now! -# -# "test" : The server will generate a key pair and a long-lived self-signed -# certificate if no 'cert.pem' or 'key.pem' file can be found. -#use_ssl = "test" - # Specify the directory where the publication server will store its data. # Note that clustering through a shared data directory is not supported. # But, we plan to look into a proper clustering solution later. diff --git a/src/cli/client.rs b/src/cli/client.rs index 4959edf6..e88335e1 100644 --- a/src/cli/client.rs +++ b/src/cli/client.rs @@ -183,7 +183,7 @@ impl KrillClient { Ok(ApiResponse::PublisherList(list)) } PublishersCommand::Add(add) => { - let pbl = PublisherRequest::new(add.handle, add.base_uri); + let pbl = PublisherRequest::new(add.handle, add.id_cert, add.base_uri); self.add_publisher(pbl) } PublishersCommand::Deactivate(handle) => { @@ -191,7 +191,7 @@ impl KrillClient { self.delete(&uri)?; Ok(ApiResponse::Empty) } - PublishersCommand::Details(handle) => { + PublishersCommand::Show(handle) => { let uri = format!("api/v1/publishers/{}", handle); let details: PublisherDetails = self.get_json(&uri)?; Ok(ApiResponse::PublisherDetails(details)) diff --git a/src/cli/options.rs b/src/cli/options.rs index bc60c73d..28129853 100644 --- a/src/cli/options.rs +++ b/src/cli/options.rs @@ -484,6 +484,47 @@ impl Options { app.subcommand(sub) } + fn make_publishers_list_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { + let mut sub = SubCommand::with_name("list").about("List all publishers."); + sub = Self::add_general_args(sub); + app.subcommand(sub) + } + + fn add_publisher_arg<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { + app.arg( + Arg::with_name("publisher") + .value_name("handle") + .short("p") + .long("publisher") + .help("The handle (name) of the publisher.") + .required(true), + ) + } + + fn make_publishers_add_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { + let mut sub = SubCommand::with_name("add").about("Add a publisher."); + sub = Self::add_general_args(sub); + sub = Self::add_publisher_arg(sub); + app.subcommand(sub) + } + + fn make_publishers_show_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { + let mut sub = SubCommand::with_name("show").about("Show details for a publisher."); + sub = Self::add_general_args(sub); + sub = Self::add_publisher_arg(sub); + app.subcommand(sub) + } + + fn make_publishers_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { + let mut sub = SubCommand::with_name("publishers").about("Manage publishers in Krill."); + + sub = Self::make_publishers_list_sc(sub); + sub = Self::make_publishers_add_sc(sub); + sub = Self::make_publishers_show_sc(sub); + + app.subcommand(sub) + } + fn make_health_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { app.subcommand( SubCommand::with_name("health").about("Perform an authenticated health check"), @@ -502,6 +543,8 @@ impl Options { app = Self::make_cas_keyroll_sc(app); app = Self::make_cas_routes_sc(app); + app = Self::make_publishers_sc(app); + app = Self::make_health_sc(app); app.get_matches() @@ -900,9 +943,10 @@ pub enum CaCommand { } #[derive(Clone, Debug, Eq, PartialEq)] +#[allow(clippy::large_enum_variant)] pub enum PublishersCommand { Add(AddPublisher), - Details(Handle), + Show(Handle), Deactivate(Handle), List, } @@ -910,6 +954,7 @@ pub enum PublishersCommand { #[derive(Clone, Debug, Eq, PartialEq)] pub struct AddPublisher { pub handle: Handle, + pub id_cert: Option, pub base_uri: uri::Rsync, } diff --git a/src/cli/report.rs b/src/cli/report.rs index 97116a32..ba2c089a 100644 --- a/src/cli/report.rs +++ b/src/cli/report.rs @@ -259,9 +259,11 @@ impl Report for PublisherDetails { ReportFormat::Text => { let mut res = String::new(); - res.push_str("handle: "); - res.push_str(self.handle()); - res.push_str("\n"); + res.push_str(&format!("handle: {}\n", self.handle())); + + if let Some(id_cert) = self.id_cert() { + res.push_str(&format!("id: {}", id_cert.ski_hex())) + } res.push_str("base uri: "); res.push_str(self.base_uri().to_string().as_str()); diff --git a/src/commons/api/admin.rs b/src/commons/api/admin.rs index 50817174..cbf37ba8 100644 --- a/src/commons/api/admin.rs +++ b/src/commons/api/admin.rs @@ -159,12 +159,17 @@ impl fmt::Display for Token { #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] pub struct PublisherRequest { handle: Handle, + id_cert: Option, base_uri: uri::Rsync, } impl PublisherRequest { - pub fn new(handle: Handle, base_uri: uri::Rsync) -> Self { - PublisherRequest { handle, base_uri } + pub fn new(handle: Handle, id_cert: Option, base_uri: uri::Rsync) -> Self { + PublisherRequest { + handle, + id_cert, + base_uri, + } } } @@ -178,8 +183,8 @@ impl PublisherRequest { } /// Return all the values (handle, base_uri). - pub fn unwrap(self) -> (Handle, uri::Rsync) { - (self.handle, self.base_uri) + pub fn unwrap(self) -> (Handle, Option, uri::Rsync) { + (self.handle, self.id_cert, self.base_uri) } } @@ -240,57 +245,49 @@ impl PublisherList { /// This type defines the publisher details for: /// /api/v1/publishers/{handle} -#[derive(Clone, Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] pub struct PublisherDetails { - handle: String, + handle: Handle, deactivated: bool, + id_cert: Option, base_uri: uri::Rsync, current_files: Vec, } impl PublisherDetails { pub fn new( - handle: &str, + handle: &Handle, deactivated: bool, + id_cert: Option<&IdCert>, base_uri: &uri::Rsync, current_files: Vec, ) -> Self { PublisherDetails { - handle: handle.to_string(), + handle: handle.clone(), deactivated, + id_cert: id_cert.cloned(), base_uri: base_uri.clone(), current_files, } } - pub fn handle(&self) -> &str { + pub fn handle(&self) -> &Handle { &self.handle } - pub fn deactivated(&self) -> bool { self.deactivated } - + pub fn id_cert(&self) -> Option<&IdCert> { + self.id_cert.as_ref() + } pub fn base_uri(&self) -> &uri::Rsync { &self.base_uri } - pub fn current_files(&self) -> &Vec { &self.current_files } } -impl PartialEq for PublisherDetails { - fn eq(&self, other: &PublisherDetails) -> bool { - match (serde_json::to_string(self), serde_json::to_string(other)) { - (Ok(ser_self), Ok(ser_other)) => ser_self == ser_other, - _ => false, - } - } -} - -impl Eq for PublisherDetails {} - //------------ PublisherClientRequest ---------------------------------------- /// This type defines request for a new Publisher client, i.e. the proxy that @@ -445,8 +442,10 @@ impl CertAuthInit { } #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[allow(clippy::large_enum_variant)] pub enum CertAuthPubMode { Embedded, + Rfc8181(IdCert), } //------------ AddChildRequest ----------------------------------------------- diff --git a/src/commons/remote/proxy.rs b/src/commons/remote/proxy.rs index 0bbb3ca3..1d529552 100644 --- a/src/commons/remote/proxy.rs +++ b/src/commons/remote/proxy.rs @@ -32,7 +32,6 @@ pub struct ProxyServer { signer: OpenSslSigner, clients_store: Arc>, responder_store: Arc>, - krill_uri: uri::Https, } /// # Server Life Cycle @@ -41,7 +40,7 @@ impl ProxyServer { /// Initialises the Proxy Server. This will re-use the existing clients and /// responder (i.e. server certificate and all), if they exist for this work_dir. /// If they do not exist, they will be initialised as well. - pub fn init(work_dir: &PathBuf, krill_uri: &uri::Https) -> Result { + pub fn init(work_dir: &PathBuf) -> Result { let mut signer = OpenSslSigner::build(work_dir)?; let clients_store = Arc::new(DiskAggregateStore::::new(work_dir, "proxy")?); let responder_store = Arc::new(DiskAggregateStore::::new(work_dir, "proxy")?); @@ -62,7 +61,6 @@ impl ProxyServer { signer, clients_store, responder_store, - krill_uri: krill_uri.clone(), }) } @@ -475,8 +473,7 @@ mod tests { #[test] fn should_init() { test::test_under_tmp(|d| { - let krill_uri = test::https("https://localhost:3000/"); - let server = ProxyServer::init(&d, &krill_uri).unwrap(); + let server = ProxyServer::init(&d).unwrap(); let add_alice = clients::tests::add_client(&d, "alice"); diff --git a/src/daemon/ca/server.rs b/src/daemon/ca/server.rs index 4d5386bf..0cdc8a79 100644 --- a/src/daemon/ca/server.rs +++ b/src/daemon/ca/server.rs @@ -42,13 +42,13 @@ impl CaServer { pub fn build( work_dir: &PathBuf, events_queue: Arc, - signer: S, + signer: Arc>, ) -> ServerResult { let mut ca_store = DiskAggregateStore::>::new(work_dir, CA_NS)?; ca_store.add_listener(events_queue); Ok(CaServer { - signer: Arc::new(RwLock::new(signer)), + signer, ca_store: Arc::new(ca_store), }) } @@ -936,6 +936,7 @@ mod tests { fn add_ta() { test::test_under_tmp(|d| { let signer = OpenSslSigner::build(&d).unwrap(); + let signer = Arc::new(RwLock::new(signer)); let event_queue = Arc::new(EventQueueListener::in_mem()); diff --git a/src/daemon/krillserver.rs b/src/daemon/krillserver.rs index 6068dc51..712d9c76 100644 --- a/src/daemon/krillserver.rs +++ b/src/daemon/krillserver.rs @@ -1,6 +1,6 @@ //! An RPKI publication protocol server. use std::path::PathBuf; -use std::sync::Arc; +use std::sync::{Arc, RwLock}; use std::{io, thread}; use bcder::Captured; @@ -91,16 +91,24 @@ impl KrillServer { let mut repo_dir = work_dir.clone(); repo_dir.push("repo"); + let signer = OpenSslSigner::build(work_dir)?; + let signer = Arc::new(RwLock::new(signer)); + let authorizer = Authorizer::new(token); let pubserver = Arc::new( - PubServer::build(base_uri.clone(), rrdp_base_uri.clone(), repo_dir, work_dir) - .map_err(Error::PubServer)?, + PubServer::build( + base_uri.clone(), + rrdp_base_uri.clone(), + repo_dir, + work_dir, + signer.clone(), + ) + .map_err(Error::PubServer)?, ); - let proxy_server = ProxyServer::init(work_dir, &service_uri)?; + let proxy_server = ProxyServer::init(work_dir)?; - let signer = OpenSslSigner::build(work_dir)?; let event_queue = Arc::new(EventQueueListener::in_mem()); let caserver = Arc::new(ca::CaServer::build(work_dir, event_queue.clone(), signer)?); @@ -117,7 +125,8 @@ impl KrillServer { let ta_aia = uri::Rsync::from_string(ta_aia).unwrap(); // Add publisher - let req = PublisherRequest::new(ta_handle.clone(), repo_info.base_uri().clone()); + let req = + PublisherRequest::new(ta_handle.clone(), None, repo_info.base_uri().clone()); pubserver.create_publisher(req).map_err(Error::PubServer)?; @@ -378,16 +387,19 @@ impl KrillServer { pub fn ca_init(&mut self, init: CertAuthInit) -> EmptyRes { let (handle, pub_mode) = init.unwrap(); - let repo_info = match pub_mode { - CertAuthPubMode::Embedded => self.pubserver.repo_info_for(&handle)?, - }; + let repo_info = self.pubserver.repo_info_for(&handle)?; let base_uri = repo_info.ca_repository(""); // Create CA self.caserver.init_ca(&handle, repo_info)?; + let id_cert = match pub_mode { + CertAuthPubMode::Embedded => None, + CertAuthPubMode::Rfc8181(id_cert) => Some(id_cert), + }; + // Add publisher - let req = PublisherRequest::new(handle.clone(), base_uri); + let req = PublisherRequest::new(handle.clone(), id_cert, base_uri); self.add_publisher(req)?; Ok(()) diff --git a/src/daemon/test.rs b/src/daemon/test.rs index e3e70eb8..ed1a1f57 100644 --- a/src/daemon/test.rs +++ b/src/daemon/test.rs @@ -360,9 +360,7 @@ pub fn ca_current_objects(handle: &Handle) -> Vec { } pub fn publisher_details(handle: &Handle) -> PublisherDetails { - match krill_admin(Command::Publishers(PublishersCommand::Details( - handle.clone(), - ))) { + match krill_admin(Command::Publishers(PublishersCommand::Show(handle.clone()))) { ApiResponse::PublisherDetails(pub_details) => pub_details, _ => panic!("Expected publisher details"), } diff --git a/src/pubd/publishers.rs b/src/pubd/publishers.rs index 9312f719..f579498a 100644 --- a/src/pubd/publishers.rs +++ b/src/pubd/publishers.rs @@ -5,6 +5,7 @@ use rpki::uri; use crate::commons::api::rrdp::{CurrentObjects, DeltaElements, VerificationError}; use crate::commons::api::{Handle, ListReply, PublishDelta, PublisherDetails, PublisherRequest}; use crate::commons::eventsourcing::{Aggregate, CommandDetails, SentCommand, StoredEvent}; +use commons::remote::id::IdCert; //------------ PublisherInit ------------------------------------------------- @@ -12,13 +13,14 @@ pub type PublisherInit = StoredEvent; #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] pub struct InitPublisherDetails { + id_cert: Option, base_uri: uri::Rsync, } impl InitPublisherDetails { pub fn for_request(req: PublisherRequest) -> PublisherInit { - let (handle, base_uri) = req.unwrap(); // (self - let details = InitPublisherDetails { base_uri }; + let (handle, id_cert, base_uri) = req.unwrap(); // (self + let details = InitPublisherDetails { id_cert, base_uri }; StoredEvent::new(&handle, 0, details) } } @@ -107,6 +109,9 @@ pub struct Publisher { version: u64, deactivated: bool, + /// Used by remote RFC8181 publishers + id_cert: Option, + /// Publication jail for this publisher base_uri: uri::Rsync, @@ -130,8 +135,9 @@ impl Publisher { pub fn as_api_details(&self) -> PublisherDetails { PublisherDetails::new( - self.handle.as_str(), + &self.handle, self.deactivated, + self.id_cert.as_ref(), &self.base_uri, self.current_objects .elements() @@ -151,6 +157,7 @@ impl Publisher { handle, version: 1, deactivated: false, + id_cert: init.id_cert, base_uri: init.base_uri, current_objects: CurrentObjects::default(), } diff --git a/src/pubd/pubserver.rs b/src/pubd/pubserver.rs index fbd37ffa..55a16a51 100644 --- a/src/pubd/pubserver.rs +++ b/src/pubd/pubserver.rs @@ -1,6 +1,6 @@ use std::io; use std::path::PathBuf; -use std::sync::{Arc, Mutex}; +use std::sync::{Arc, Mutex, RwLock}; use chrono::Duration; @@ -18,18 +18,28 @@ use crate::pubd::publishers::{ use crate::pubd::repo::{ self, RrdpCommandDetails, RrdpInitDetails, RrdpServer, RrdpServerError, RsyncdStore, }; +use commons::util::softsigner::OpenSslSigner; //------------ PubServer ----------------------------------------------------- -/// This server manages all publishers. I.e. finds them, adds them, dispatches -/// commands to them, stores them.. also publishes the combined snapshots and -/// deltas, and manages the files on disk for rsync. +/// The Publication Server. +/// +/// This component is responsible for: +/// * managing allowed publishers +/// * verifying requests from remote RFC8183 publishers +/// * verifying requests from local (embedded) publishers +/// * updating the RRDP server with any deltas +/// * updating the contents on disk for Rsync +/// * responding to publishers +/// * wrapping responses in RFC8183 for remote publishers +/// pub struct PubServer { rrdp_store: Arc>, rsyncd_store: RsyncdStore, store: Arc>, base_rsync_uri: uri::Rsync, // jail for the publishers, command_lock: Mutex<()>, // Only one command at the time. + signer: Arc>, } impl PubServer { @@ -38,6 +48,7 @@ impl PubServer { base_http_uri: uri::Https, // for the RRDP files repo_dir: PathBuf, // for the RRDP and rsync files work_dir: &PathBuf, // for the aggregate stores + signer: Arc>, ) -> Result { let rrdp_store = Arc::new(DiskAggregateStore::::new( work_dir, @@ -64,6 +75,7 @@ impl PubServer { store, base_rsync_uri, command_lock, + signer, }; Ok(pubserver) @@ -331,11 +343,15 @@ mod tests { fn make_publisher_req(handle: &str, uri: &str) -> PublisherRequest { let base_uri = test::rsync(uri); let handle = Handle::from_str_unsafe(handle); + let id_cert = None; // embedded - PublisherRequest::new(handle, base_uri) + PublisherRequest::new(handle, id_cert, base_uri) } fn make_server(work_dir: &PathBuf) -> PubServer { + let signer = OpenSslSigner::build(work_dir).unwrap(); + let signer = Arc::new(RwLock::new(signer)); + let mut base_dir = work_dir.clone(); base_dir.push("repo"); @@ -344,6 +360,7 @@ mod tests { server_base_http_uri(), base_dir, work_dir, + signer, ) .unwrap() } diff --git a/tests/admin_publishers.rs b/tests/admin_publishers.rs index 795b4f0e..64abf73e 100644 --- a/tests/admin_publishers.rs +++ b/tests/admin_publishers.rs @@ -9,6 +9,7 @@ use krill::daemon::test::{krill_admin, test_with_krill_server}; fn add_publisher(handle: &Handle, base_uri: &str) { let command = Command::Publishers(PublishersCommand::Add(AddPublisher { handle: handle.clone(), + id_cert: None, // embedded for test base_uri: test::rsync(base_uri), })); krill_admin(command); @@ -27,7 +28,7 @@ fn list_publishers() -> ApiResponse { } fn details_publisher(handle: &Handle) -> ApiResponse { - let command = Command::Publishers(PublishersCommand::Details(handle.clone())); + let command = Command::Publishers(PublishersCommand::Show(handle.clone())); krill_admin(command) } @@ -56,7 +57,7 @@ fn admin_publishers() { let details_res = details_publisher(&handle); match details_res { ApiResponse::PublisherDetails(details) => { - assert_eq!("alice", details.handle()); + assert_eq!(&handle, details.handle()); assert_eq!(false, details.deactivated()); } _ => panic!("Expected details"), @@ -69,7 +70,7 @@ fn admin_publishers() { let details_res = details_publisher(&handle); match details_res { ApiResponse::PublisherDetails(details) => { - assert_eq!("alice", details.handle()); + assert_eq!(&handle, details.handle()); assert_eq!(true, details.deactivated()); } _ => panic!("Expected details"),