Add IdCert to Publishers so we can verify RFC8183 messages there. (#107)

This commit is contained in:
Tim Bruijnzeels
2019-10-22 09:31:30 +02:00
parent 7ad3a66647
commit 6ceec7efbf
12 changed files with 139 additions and 71 deletions
-11
View File
@@ -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.
+2 -2
View File
@@ -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))
+46 -1
View File
@@ -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<IdCert>,
pub base_uri: uri::Rsync,
}
+5 -3
View File
@@ -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());
+22 -23
View File
@@ -159,12 +159,17 @@ impl fmt::Display for Token {
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct PublisherRequest {
handle: Handle,
id_cert: Option<IdCert>,
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<IdCert>, 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<IdCert>, 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<IdCert>,
base_uri: uri::Rsync,
current_files: Vec<PublishElement>,
}
impl PublisherDetails {
pub fn new(
handle: &str,
handle: &Handle,
deactivated: bool,
id_cert: Option<&IdCert>,
base_uri: &uri::Rsync,
current_files: Vec<PublishElement>,
) -> 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<PublishElement> {
&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 -----------------------------------------------
+2 -5
View File
@@ -32,7 +32,6 @@ pub struct ProxyServer {
signer: OpenSslSigner,
clients_store: Arc<DiskAggregateStore<ClientManager>>,
responder_store: Arc<DiskAggregateStore<Responder>>,
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<Self, Error> {
pub fn init(work_dir: &PathBuf) -> Result<Self, Error> {
let mut signer = OpenSslSigner::build(work_dir)?;
let clients_store = Arc::new(DiskAggregateStore::<ClientManager>::new(work_dir, "proxy")?);
let responder_store = Arc::new(DiskAggregateStore::<Responder>::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");
+3 -2
View File
@@ -42,13 +42,13 @@ impl<S: Signer> CaServer<S> {
pub fn build(
work_dir: &PathBuf,
events_queue: Arc<EventQueueListener>,
signer: S,
signer: Arc<RwLock<S>>,
) -> ServerResult<Self, S> {
let mut ca_store = DiskAggregateStore::<CertAuth<S>>::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());
+22 -10
View File
@@ -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(())
+1 -3
View File
@@ -360,9 +360,7 @@ pub fn ca_current_objects(handle: &Handle) -> Vec<Publish> {
}
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"),
}
+10 -3
View File
@@ -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<InitPublisherDetails>;
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct InitPublisherDetails {
id_cert: Option<IdCert>,
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<IdCert>,
/// 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(),
}
+22 -5
View File
@@ -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<DiskAggregateStore<RrdpServer>>,
rsyncd_store: RsyncdStore,
store: Arc<DiskAggregateStore<Publisher>>,
base_rsync_uri: uri::Rsync, // jail for the publishers,
command_lock: Mutex<()>, // Only one command at the time.
signer: Arc<RwLock<OpenSslSigner>>,
}
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<RwLock<OpenSslSigner>>,
) -> Result<Self, Error> {
let rrdp_store = Arc::new(DiskAggregateStore::<RrdpServer>::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()
}
+4 -3
View File
@@ -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"),