mirror of
https://github.com/NLnetLabs/krill.git
synced 2026-09-22 17:34:56 +02:00
Support (great-) grandchildren under multiple lineages. Closes: #25
This commit is contained in:
+29
-13
@@ -1,6 +1,6 @@
|
||||
use rpki::uri;
|
||||
use std::io;
|
||||
|
||||
use rpki::uri;
|
||||
use serde::de::DeserializeOwned;
|
||||
|
||||
use krill_commons::api::admin::{
|
||||
@@ -81,7 +81,7 @@ impl KrillClient {
|
||||
let uri = self.resolve_uri("api/v1/trustanchor/children");
|
||||
let info: ParentCaContact =
|
||||
httpclient::post_json_with_response(&uri, req, Some(&self.token))?;
|
||||
Ok(ApiResponse::ParentCaInfo(info))
|
||||
Ok(ApiResponse::ParentCaContact(info))
|
||||
}
|
||||
TrustAnchorCommand::UpdateChild(child, req) => {
|
||||
let uri = format!("api/v1/trustanchor/children/{}", child);
|
||||
@@ -94,10 +94,9 @@ impl KrillClient {
|
||||
|
||||
fn certauth(&self, command: CaCommand) -> Result<ApiResponse, Error> {
|
||||
match command {
|
||||
CaCommand::AddParent(handle, parent) => {
|
||||
let uri = format!("api/v1/cas/{}/parents", handle);
|
||||
let uri = self.resolve_uri(&uri);
|
||||
httpclient::post_json(&uri, parent, Some(&self.token))?;
|
||||
CaCommand::Init(init) => {
|
||||
let uri = self.resolve_uri("api/v1/cas");
|
||||
httpclient::post_json(&uri, init, Some(&self.token))?;
|
||||
Ok(ApiResponse::Empty)
|
||||
}
|
||||
CaCommand::ChildRequest(handle) => {
|
||||
@@ -108,16 +107,28 @@ impl KrillClient {
|
||||
let req = rfc8183::ChildRequest::validate(xml.as_bytes())?;
|
||||
Ok(ApiResponse::Rfc8183ChildRequest(req))
|
||||
}
|
||||
CaCommand::Init(init) => {
|
||||
let uri = self.resolve_uri("api/v1/cas");
|
||||
httpclient::post_json(&uri, init, Some(&self.token))?;
|
||||
|
||||
CaCommand::AddParent(handle, parent) => {
|
||||
let uri = format!("api/v1/cas/{}/parents", handle);
|
||||
let uri = self.resolve_uri(&uri);
|
||||
httpclient::post_json(&uri, parent, Some(&self.token))?;
|
||||
Ok(ApiResponse::Empty)
|
||||
}
|
||||
CaCommand::List => {
|
||||
let uri = self.resolve_uri("api/v1/cas");
|
||||
let cas = self.get_json(&uri)?;
|
||||
Ok(ApiResponse::CertAuths(cas))
|
||||
|
||||
CaCommand::AddChild(handle, req) => {
|
||||
let uri = format!("api/v1/cas/{}/children", handle);
|
||||
let uri = self.resolve_uri(&uri);
|
||||
let info: ParentCaContact =
|
||||
httpclient::post_json_with_response(&uri, req, Some(&self.token))?;
|
||||
Ok(ApiResponse::ParentCaContact(info))
|
||||
}
|
||||
CaCommand::UpdateChild(handle, child, req) => {
|
||||
let uri = format!("api/v1/cas/{}/children/{}", handle, child);
|
||||
let uri = self.resolve_uri(&uri);
|
||||
httpclient::post_json(&uri, req, Some(&self.token))?;
|
||||
Ok(ApiResponse::Empty)
|
||||
}
|
||||
|
||||
CaCommand::KeyRollInit(handle) => {
|
||||
let uri = format!("api/v1/cas/{}/keys/roll_init", handle);
|
||||
let uri = self.resolve_uri(&uri);
|
||||
@@ -137,6 +148,11 @@ impl KrillClient {
|
||||
|
||||
Ok(ApiResponse::CertAuthInfo(ca_info))
|
||||
}
|
||||
CaCommand::List => {
|
||||
let uri = self.resolve_uri("api/v1/cas");
|
||||
let cas = self.get_json(&uri)?;
|
||||
Ok(ApiResponse::CertAuths(cas))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+205
-4
@@ -285,8 +285,119 @@ impl Options {
|
||||
|
||||
|
||||
)
|
||||
|
||||
)
|
||||
|
||||
.subcommand(SubCommand::with_name("children")
|
||||
.about("Manage children of this CA")
|
||||
.arg(Arg::with_name("handle")
|
||||
.short("h")
|
||||
.long("handle")
|
||||
.value_name("ca handle")
|
||||
.help("The handle (name) for the CA")
|
||||
.required(true)
|
||||
)
|
||||
|
||||
.subcommand(SubCommand::with_name("add")
|
||||
.about("Add a child to the embedded CA")
|
||||
|
||||
.arg(Arg::with_name("asn")
|
||||
.short("a")
|
||||
.long("asn")
|
||||
.value_name("AS resources")
|
||||
.help("The delegated AS resources: e.g. AS1, AS3-4")
|
||||
.required(false)
|
||||
)
|
||||
.arg(Arg::with_name("ipv4")
|
||||
.short("4")
|
||||
.long("ipv4")
|
||||
.value_name("IPv4 resources")
|
||||
.help("The delegated IPv4 resources: e.g. 192.168.0.0/16")
|
||||
.required(false)
|
||||
)
|
||||
.arg(Arg::with_name("ipv6")
|
||||
.short("6")
|
||||
.long("ipv6")
|
||||
.value_name("IPv6 resources")
|
||||
.help("The delegated IPv6 resources: e.g. 2001:db8::/32")
|
||||
.required(false)
|
||||
)
|
||||
|
||||
.subcommand(SubCommand::with_name("embedded")
|
||||
.about("Add an embedded child")
|
||||
.arg(Arg::with_name("handle")
|
||||
.short("h")
|
||||
.long("handle")
|
||||
.value_name("child-handle")
|
||||
.help("The handle of the child")
|
||||
.required(true)
|
||||
)
|
||||
)
|
||||
|
||||
.subcommand(SubCommand::with_name("rfc6492")
|
||||
.about("Add an RFC 6492 child")
|
||||
.arg(Arg::with_name("handle")
|
||||
.short("h")
|
||||
.long("handle")
|
||||
.value_name("child-handle")
|
||||
.help("Override the handle in the XML")
|
||||
.required(false)
|
||||
)
|
||||
.arg(Arg::with_name("xml")
|
||||
.short("x")
|
||||
.long("xml")
|
||||
.value_name("FILE")
|
||||
.help("RFC 8183 Child Request XML")
|
||||
.required(true)
|
||||
)
|
||||
)
|
||||
)
|
||||
.subcommand(SubCommand::with_name("update")
|
||||
.about("Update details for a child")
|
||||
.arg(Arg::with_name("handle")
|
||||
.short("h")
|
||||
.long("handle")
|
||||
.value_name("child-handle")
|
||||
.help("Override the handle in the XML")
|
||||
.required(false)
|
||||
)
|
||||
.arg(Arg::with_name("xml")
|
||||
.short("x")
|
||||
.long("xml")
|
||||
.value_name("FILE")
|
||||
.help("Update child certificate from RFC 8183 Child Request XML")
|
||||
.required(false)
|
||||
)
|
||||
.arg(Arg::with_name("asn")
|
||||
.short("a")
|
||||
.long("asn")
|
||||
.value_name("AS resources")
|
||||
.help("Update the delegated AS resources: e.g. AS1, AS3-4")
|
||||
.required(false)
|
||||
)
|
||||
.arg(Arg::with_name("ipv4")
|
||||
.short("4")
|
||||
.long("ipv4")
|
||||
.value_name("IPv4 resources")
|
||||
.help("Update the delegated IPv4 resources: e.g. 192.168.0.0/16")
|
||||
.required(false)
|
||||
)
|
||||
.arg(Arg::with_name("ipv6")
|
||||
.short("6")
|
||||
.long("ipv6")
|
||||
.value_name("IPv6 resources")
|
||||
.help("Update the delegated IPv6 resources: e.g. 2001:db8::/32")
|
||||
.required(false)
|
||||
)
|
||||
.arg(Arg::with_name("force")
|
||||
.short("f")
|
||||
.long("force")
|
||||
.takes_value(false)
|
||||
.help("Force resource shrink now.")
|
||||
.required(false)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
)
|
||||
|
||||
.subcommand(SubCommand::with_name("publishers")
|
||||
@@ -486,6 +597,79 @@ impl Options {
|
||||
let handle = Handle::from(m.value_of("handle").unwrap());
|
||||
command = Command::CertAuth(CaCommand::Show(handle));
|
||||
}
|
||||
|
||||
if let Some(m) = m.subcommand_matches("children") {
|
||||
let ca = Handle::from(m.value_of("handle").unwrap());
|
||||
|
||||
if let Some(m) = m.subcommand_matches("add") {
|
||||
let asn = m.value_of("asn").unwrap_or("");
|
||||
let ipv4 = m.value_of("ipv4").unwrap_or("");
|
||||
let ipv6 = m.value_of("ipv6").unwrap_or("");
|
||||
|
||||
if let Some(m) = m.subcommand_matches("embedded") {
|
||||
let handle = Handle::from(m.value_of("handle").unwrap());
|
||||
let res = ResourceSet::from_strs(asn, ipv4, ipv6).unwrap();
|
||||
let auth = ChildAuthRequest::Embedded;
|
||||
|
||||
let req = AddChildRequest::new(handle, res, auth);
|
||||
command = Command::TrustAnchor(TrustAnchorCommand::AddChild(req))
|
||||
}
|
||||
|
||||
if let Some(m) = m.subcommand_matches("rfc6492") {
|
||||
let xml_path = m.value_of("xml").unwrap();
|
||||
let xml = PathBuf::from(xml_path);
|
||||
let bytes = file::read(&xml)?;
|
||||
let cr = rfc8183::ChildRequest::validate(bytes.as_ref())?;
|
||||
|
||||
let handle = {
|
||||
if let Some(handle) = m.value_of("handle") {
|
||||
Handle::from(handle)
|
||||
} else {
|
||||
cr.child_handle().clone()
|
||||
}
|
||||
};
|
||||
|
||||
let res = ResourceSet::from_strs(asn, ipv4, ipv6)?;
|
||||
|
||||
let auth = ChildAuthRequest::Rfc8183(cr);
|
||||
|
||||
let req = AddChildRequest::new(handle, res, auth);
|
||||
command = Command::CertAuth(CaCommand::AddChild(ca, req))
|
||||
}
|
||||
} else if let Some(m) = m.subcommand_matches("update") {
|
||||
let handle = Handle::from(m.value_of("handle").unwrap());
|
||||
let cert = match m.value_of("xml") {
|
||||
Some(xml_path) => {
|
||||
let xml = PathBuf::from(xml_path);
|
||||
let bytes = file::read(&xml)?;
|
||||
let cr = rfc8183::ChildRequest::validate(bytes.as_ref())?;
|
||||
let (_, _, cert) = cr.unwrap();
|
||||
Some(cert)
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
|
||||
let asn = m.value_of("asn").unwrap_or("");
|
||||
let ipv4 = m.value_of("ipv4").unwrap_or("");
|
||||
let ipv6 = m.value_of("ipv6").unwrap_or("");
|
||||
let resources = ResourceSet::from_strs(asn, ipv4, ipv6)?;
|
||||
|
||||
let resources = if resources.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(resources)
|
||||
};
|
||||
|
||||
let req = if m.is_present("force") {
|
||||
UpdateChildRequest::force(cert, resources)
|
||||
} else {
|
||||
UpdateChildRequest::graceful(cert, resources)
|
||||
};
|
||||
|
||||
command = Command::CertAuth(CaCommand::UpdateChild(ca, handle, req))
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(m) = m.subcommand_matches("update") {
|
||||
let handle = Handle::from(m.value_of("handle").unwrap());
|
||||
|
||||
@@ -598,13 +782,30 @@ pub enum TrustAnchorCommand {
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum CaCommand {
|
||||
AddParent(Handle, AddParentRequest),
|
||||
ChildRequest(Handle),
|
||||
// Initialise a CA
|
||||
Init(CertAuthInit),
|
||||
|
||||
// Get the RFC8183 child request
|
||||
ChildRequest(Handle),
|
||||
|
||||
// Add a parent to this CA
|
||||
AddParent(Handle, AddParentRequest),
|
||||
|
||||
// Add a child to this CA
|
||||
AddChild(Handle, AddChildRequest),
|
||||
UpdateChild(Handle, Handle, UpdateChildRequest),
|
||||
|
||||
// Initialise a manual key-roll now
|
||||
KeyRollInit(Handle),
|
||||
|
||||
// Activate all new keys now (finish keyroll, provided new key was certified)
|
||||
KeyRollActivate(Handle),
|
||||
List,
|
||||
|
||||
// Show details for this CA
|
||||
Show(Handle),
|
||||
|
||||
// List all CAs
|
||||
List,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
|
||||
@@ -19,7 +19,7 @@ pub enum ApiResponse {
|
||||
CertAuthInfo(CertAuthInfo),
|
||||
CertAuths(CertAuthList),
|
||||
|
||||
ParentCaInfo(ParentCaContact),
|
||||
ParentCaContact(ParentCaContact),
|
||||
|
||||
PublisherDetails(PublisherDetails),
|
||||
PublisherList(PublisherList),
|
||||
@@ -48,7 +48,7 @@ impl ApiResponse {
|
||||
ApiResponse::TrustAnchorInfo(ta) => Ok(Some(ta.report(fmt)?)),
|
||||
ApiResponse::CertAuths(list) => Ok(Some(list.report(fmt)?)),
|
||||
ApiResponse::CertAuthInfo(info) => Ok(Some(info.report(fmt)?)),
|
||||
ApiResponse::ParentCaInfo(info) => Ok(Some(info.report(fmt)?)),
|
||||
ApiResponse::ParentCaContact(contact) => Ok(Some(contact.report(fmt)?)),
|
||||
ApiResponse::PublisherList(list) => Ok(Some(list.report(fmt)?)),
|
||||
ApiResponse::PublisherDetails(details) => Ok(Some(details.report(fmt)?)),
|
||||
ApiResponse::Rfc8181ClientList(list) => Ok(Some(list.report(fmt)?)),
|
||||
|
||||
@@ -327,7 +327,7 @@ impl AddParentRequest {
|
||||
#[derive(Clone, Debug, Deserialize, Display, Eq, PartialEq, Serialize)]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum ParentCaContact {
|
||||
#[display(fmt = "In this context this CA is a self-signed TA")]
|
||||
#[display(fmt = "This CA is a TA with TAL:\n{}", _0)]
|
||||
Ta(TrustAnchorLocator),
|
||||
|
||||
#[display(fmt = "Embedded parent")]
|
||||
|
||||
+36
-28
@@ -251,7 +251,7 @@ impl<S: Signer> Aggregate for CertAuth<S> {
|
||||
// Key rolls
|
||||
CmdDet::KeyRollInitiate(duration, signer) => self.keyroll_initiate(duration, signer),
|
||||
CmdDet::KeyRollActivate(duration, signer) => self.keyroll_activate(duration, signer),
|
||||
CmdDet::KeyRollFinish(parent, response) => self.keyroll_finish(&parent, response),
|
||||
CmdDet::KeyRollFinish(rcn, response) => self.keyroll_finish(rcn, response),
|
||||
|
||||
// Republish
|
||||
CmdDet::Republish(signer) => self.republish(signer),
|
||||
@@ -351,13 +351,13 @@ impl<S: Signer> CertAuth<S> {
|
||||
|
||||
/// List entitlements (section 3.3.2 of RFC6492). Return an error if
|
||||
/// the child is not authorized -- or unknown etc.
|
||||
///
|
||||
/// Only supported in TAs until issue #25 is implemented.
|
||||
pub fn list(&self, child_handle: &Handle) -> Result<api::Entitlements> {
|
||||
// TODO: Support arbitrary resource classes. See issue #25.
|
||||
let mut classes = vec![];
|
||||
if let Some(class) = self.entitlement_class(child_handle, &ResourceClassName::default()) {
|
||||
classes.push(class);
|
||||
|
||||
for rcn in self.resources.keys() {
|
||||
if let Some(class) = self.entitlement_class(child_handle, rcn) {
|
||||
classes.push(class);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Entitlements::new(classes))
|
||||
@@ -498,8 +498,6 @@ impl<S: Signer> CertAuth<S> {
|
||||
/// = the csr is invalid,
|
||||
/// = the limit exceeds the child allocation,
|
||||
/// = the signer throws up..
|
||||
///
|
||||
/// This CA is not a TA (until #25)
|
||||
fn certify_child(
|
||||
&self,
|
||||
child: Handle,
|
||||
@@ -1018,12 +1016,15 @@ impl<S: Signer> CertAuth<S> {
|
||||
|
||||
/// Get all the current open certificate requests for a parent.
|
||||
/// Returns an empty list if the parent is not found.
|
||||
pub fn cert_requests(&self, parent_handle: &ParentHandle) -> Vec<IssuanceRequest> {
|
||||
let mut res = vec![];
|
||||
pub fn cert_requests(
|
||||
&self,
|
||||
parent_handle: &ParentHandle,
|
||||
) -> HashMap<ResourceClassName, Vec<IssuanceRequest>> {
|
||||
let mut res = HashMap::new();
|
||||
|
||||
for rc in self.resources.values() {
|
||||
for (name, rc) in self.resources.iter() {
|
||||
if rc.parent_handle() == parent_handle {
|
||||
res.append(&mut rc.cert_requests())
|
||||
res.insert(name.clone(), rc.cert_requests());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1049,14 +1050,19 @@ impl<S: Signer> CertAuth<S> {
|
||||
}
|
||||
|
||||
/// Returns the open revocation requests for the given parent.
|
||||
pub fn revoke_requests(&self, parent: &ParentHandle) -> Vec<&RevocationRequest> {
|
||||
let mut res = vec![];
|
||||
for rc in self.resources.values() {
|
||||
pub fn revoke_requests(
|
||||
&self,
|
||||
parent: &ParentHandle,
|
||||
) -> HashMap<ResourceClassName, Vec<RevocationRequest>> {
|
||||
let mut res = HashMap::new();
|
||||
for (name, rc) in self.resources.iter() {
|
||||
let mut revokes = vec![];
|
||||
if let Some(req) = rc.revoke_request() {
|
||||
if rc.parent_handle() == parent {
|
||||
res.push(req)
|
||||
revokes.push(req.clone())
|
||||
}
|
||||
}
|
||||
res.insert(name.clone(), revokes);
|
||||
}
|
||||
res
|
||||
}
|
||||
@@ -1089,10 +1095,13 @@ impl<S: Signer> CertAuth<S> {
|
||||
.iter()
|
||||
.map(|c| c.class_name())
|
||||
.collect();
|
||||
for (name, class) in current_resource_classes
|
||||
.iter()
|
||||
.filter(|(name, _class)| !entitled_classes.contains(name))
|
||||
{
|
||||
|
||||
for (name, class) in current_resource_classes.iter().filter(|(_name, class)| {
|
||||
// Find the classes for this parent, not included
|
||||
// in the entitlements now received.
|
||||
class.parent_handle() == &parent_handle
|
||||
&& !entitled_classes.contains(&class.parent_rc_name())
|
||||
}) {
|
||||
let signer = signer.read().unwrap();
|
||||
|
||||
let delta = class.withdraw(&self.base_repo);
|
||||
@@ -1261,19 +1270,18 @@ impl<S: Signer> CertAuth<S> {
|
||||
|
||||
fn keyroll_finish(
|
||||
&self,
|
||||
parent_h: &ParentHandle,
|
||||
response: RevocationResponse,
|
||||
rcn: ResourceClassName,
|
||||
_response: RevocationResponse,
|
||||
) -> ca::Result<Vec<Evt>> {
|
||||
if self.is_ta() {
|
||||
return Ok(vec![]);
|
||||
}
|
||||
let (parent_rc_name, _key_id) = response.unpack();
|
||||
let my_rc = self
|
||||
.resources
|
||||
.get(&rcn)
|
||||
.ok_or_else(|| Error::unknown_resource_class(&rcn))?;
|
||||
|
||||
let (my_name, my_rc) = self
|
||||
.find_parent_rc(parent_h, &parent_rc_name)
|
||||
.ok_or_else(|| Error::unknown_resource_class(&parent_rc_name))?;
|
||||
|
||||
let finish_details = my_rc.keyroll_finish(my_name.clone(), &self.base_repo)?;
|
||||
let finish_details = my_rc.keyroll_finish(rcn, &self.base_repo)?;
|
||||
|
||||
Ok(vec![StoredEvent::new(
|
||||
self.handle(),
|
||||
|
||||
@@ -67,7 +67,7 @@ pub enum CmdDet<S: Signer> {
|
||||
|
||||
// Finish the keyroll after the parent confirmed that a key for a parent and resource
|
||||
// class has been revoked. I.e. remove the old key, and withdraw the crl and mft for it.
|
||||
KeyRollFinish(ParentHandle, RevocationResponse),
|
||||
KeyRollFinish(ResourceClassName, RevocationResponse),
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// Publishing
|
||||
@@ -81,8 +81,7 @@ impl<S: Signer> eventsourcing::CommandDetails for CmdDet<S> {
|
||||
|
||||
impl<S: Signer> CmdDet<S> {
|
||||
/// Adds a child to this CA. Will return an error in case you try
|
||||
/// to give the child resources not held by the CA. And until issue
|
||||
/// #25 is implemented, returns an error when the CA is not a TA.
|
||||
/// to give the child resources not held by the CA.
|
||||
pub fn child_add(
|
||||
handle: &Handle,
|
||||
child_handle: Handle,
|
||||
@@ -190,10 +189,10 @@ impl<S: Signer> CmdDet<S> {
|
||||
|
||||
pub fn key_roll_finish(
|
||||
handle: &Handle,
|
||||
parent: ParentHandle,
|
||||
rcn: ResourceClassName,
|
||||
res: RevocationResponse,
|
||||
) -> Cmd<S> {
|
||||
eventsourcing::SentCommand::new(handle, None, CmdDet::KeyRollFinish(parent, res))
|
||||
eventsourcing::SentCommand::new(handle, None, CmdDet::KeyRollFinish(rcn, res))
|
||||
}
|
||||
|
||||
pub fn publish(handle: &Handle, signer: Arc<RwLock<S>>) -> Cmd<S> {
|
||||
|
||||
+164
-146
@@ -1,3 +1,4 @@
|
||||
use std::collections::HashMap;
|
||||
use std::ops::Deref;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::{Arc, RwLock};
|
||||
@@ -110,41 +111,41 @@ impl<S: Signer> CaServer<S> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Adds a child under the embedded TA
|
||||
pub fn ta_add_child(
|
||||
/// Adds a child under an embedded CA
|
||||
pub fn ca_add_child(
|
||||
&self,
|
||||
parent: &ParentHandle,
|
||||
req: AddChildRequest,
|
||||
service_uri: &uri::Https,
|
||||
) -> ServerResult<ParentCaContact, S> {
|
||||
let (handle, resources, auth) = req.unwrap();
|
||||
let (child_handle, child_res, child_auth) = req.unwrap();
|
||||
|
||||
debug!("Adding child {} to TA", &handle);
|
||||
info!("Adding child {} to CA {}", &child_handle, &parent);
|
||||
|
||||
let ta = self.get_trust_anchor()?;
|
||||
let ta_handle = ca::ta_handle();
|
||||
let ca = self.get_ca(parent)?;
|
||||
|
||||
let id_cert = match &auth {
|
||||
let id_cert = match &child_auth {
|
||||
ChildAuthRequest::Embedded => None,
|
||||
ChildAuthRequest::Rfc8183(req) => Some(req.id_cert().clone()),
|
||||
};
|
||||
|
||||
let add_child = CmdDet::child_add(&ta_handle, handle.clone(), id_cert, resources);
|
||||
let add_child = CmdDet::child_add(&parent, child_handle.clone(), id_cert, child_res);
|
||||
|
||||
let events = ta.process_command(add_child)?;
|
||||
let ta = self.ca_store.update(&ta_handle, ta, events)?;
|
||||
let events = ca.process_command(add_child)?;
|
||||
let ca = self.ca_store.update(&parent, ca, events)?;
|
||||
|
||||
match auth {
|
||||
match child_auth {
|
||||
ChildAuthRequest::Embedded => Ok(ParentCaContact::Embedded),
|
||||
ChildAuthRequest::Rfc8183(req) => {
|
||||
let service_uri = format!("{}rfc6492/{}", service_uri.to_string(), ta.handle());
|
||||
let service_uri = format!("{}rfc6492/{}", service_uri.to_string(), ca.handle());
|
||||
let service_uri = uri::Https::from_string(service_uri).unwrap();
|
||||
let service_uri = rfc8183::ServiceUri::Https(service_uri);
|
||||
|
||||
let response = rfc8183::ParentResponse::new(
|
||||
req.tag().cloned(),
|
||||
ta.id_cert().clone(),
|
||||
ta.handle().clone(),
|
||||
handle,
|
||||
ca.id_cert().clone(),
|
||||
ca.handle().clone(),
|
||||
child_handle,
|
||||
service_uri,
|
||||
);
|
||||
Ok(ParentCaContact::for_rfc6492(response))
|
||||
@@ -154,11 +155,15 @@ impl<S: Signer> CaServer<S> {
|
||||
|
||||
/// Show details for a child under the TA. Returns Ok(None) if the TA is present,
|
||||
/// but the child is not known.
|
||||
pub fn ta_show_child(&self, child: &ChildHandle) -> ServerResult<Option<ChildCaInfo>, S> {
|
||||
debug!("Finding details for {} under TA", child);
|
||||
pub fn ca_show_child(
|
||||
&self,
|
||||
parent: &ParentHandle,
|
||||
child: &ChildHandle,
|
||||
) -> ServerResult<Option<ChildCaInfo>, S> {
|
||||
debug!("Finding details for CA: {} under parent: {}", child, parent);
|
||||
|
||||
let ta = self.get_trust_anchor()?;
|
||||
let child_opt = match ta.get_child(child) {
|
||||
let ca = self.get_ca(parent)?;
|
||||
let child_opt = match ca.get_child(child) {
|
||||
Err(_) => None,
|
||||
Ok(child_details) => Some(child_details.clone().into()),
|
||||
};
|
||||
@@ -166,30 +171,34 @@ impl<S: Signer> CaServer<S> {
|
||||
Ok(child_opt)
|
||||
}
|
||||
|
||||
pub fn ta_update_child(
|
||||
/// Update a child under this CA.
|
||||
pub fn ca_update_child(
|
||||
&self,
|
||||
parent: &ParentHandle,
|
||||
child: ChildHandle,
|
||||
req: UpdateChildRequest,
|
||||
) -> ServerResult<(), S> {
|
||||
debug!("Updating details for {} under TA", child);
|
||||
let mut ta = self.get_trust_anchor()?;
|
||||
let ta_handle = ca::ta_handle();
|
||||
debug!(
|
||||
"Updating details for CA: {} under parent: {}",
|
||||
child, parent
|
||||
);
|
||||
let mut ca = self.get_ca(parent)?;
|
||||
|
||||
let force = req.is_force();
|
||||
|
||||
let events = ta.process_command(CmdDet::child_update(&ta_handle, child.clone(), req))?;
|
||||
let events = ca.process_command(CmdDet::child_update(parent, child.clone(), req))?;
|
||||
if !events.is_empty() {
|
||||
ta = self.ca_store.update(&ta_handle, ta, events)?;
|
||||
ca = self.ca_store.update(parent, ca, events)?;
|
||||
|
||||
if force {
|
||||
let events = ta.process_command(CmdDet::child_shrink(
|
||||
&ta_handle,
|
||||
let events = ca.process_command(CmdDet::child_shrink(
|
||||
parent,
|
||||
child,
|
||||
Duration::seconds(0),
|
||||
self.signer.clone(),
|
||||
))?;
|
||||
if !events.is_empty() {
|
||||
self.ca_store.update(&ta_handle, ta, events)?;
|
||||
self.ca_store.update(parent, ca, events)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -250,22 +259,15 @@ impl<S: Signer> CaServer<S> {
|
||||
msg: rfc6492::Message,
|
||||
) -> ServerResult<Bytes, S> {
|
||||
debug!("RFC6492 Response wrapping for {}", handle);
|
||||
let ca = self.ca_store.get_latest(handle)?;
|
||||
let res = ca
|
||||
self.get_ca(handle)?
|
||||
.sign_rfc6492_response(msg, self.signer.read().unwrap().deref())
|
||||
.map_err(ServerError::<S>::CertAuth);
|
||||
debug!("RFC6492 Response wrapped for {}", handle);
|
||||
res
|
||||
.map_err(ServerError::<S>::CertAuth)
|
||||
}
|
||||
|
||||
/// List the entitlements for a child: 3.3.2 of RFC6492
|
||||
pub fn list(&self, parent: &Handle, child: &Handle) -> ServerResult<Entitlements, S> {
|
||||
if parent != &ca::ta_handle() {
|
||||
unimplemented!("https://github.com/NLnetLabs/krill/issues/25");
|
||||
} else {
|
||||
let ta = self.get_trust_anchor()?;
|
||||
Ok(ta.list(child)?)
|
||||
}
|
||||
let ca = self.get_ca(parent)?;
|
||||
Ok(ca.list(child)?)
|
||||
}
|
||||
|
||||
/// Issue a Certificate in response to a Certificate Issuance request
|
||||
@@ -277,34 +279,25 @@ impl<S: Signer> CaServer<S> {
|
||||
child: &ChildHandle,
|
||||
issue_req: IssuanceRequest,
|
||||
) -> ServerResult<IssuanceResponse, S> {
|
||||
if parent != &ca::ta_handle() {
|
||||
unimplemented!("https://github.com/NLnetLabs/krill/issues/25");
|
||||
} else {
|
||||
let ta = self.get_trust_anchor()?;
|
||||
let ca = self.get_ca(parent)?;
|
||||
|
||||
let class_name = issue_req.class_name();
|
||||
let pub_key = issue_req.csr().public_key();
|
||||
let class_name = issue_req.class_name();
|
||||
let pub_key = issue_req.csr().public_key();
|
||||
|
||||
if class_name != &ResourceClassName::default() {
|
||||
unimplemented!("Issue for multiple classes from CAs, issue #25")
|
||||
}
|
||||
let cmd = CmdDet::child_certify(
|
||||
parent,
|
||||
child.clone(),
|
||||
issue_req.clone(),
|
||||
self.signer.clone(),
|
||||
);
|
||||
|
||||
let cmd = CmdDet::child_certify(
|
||||
parent,
|
||||
child.clone(),
|
||||
issue_req.clone(),
|
||||
self.signer.clone(),
|
||||
);
|
||||
let events = ca.process_command(cmd)?;
|
||||
let ca = self.ca_store.update(parent, ca, events)?;
|
||||
|
||||
let events = ta.process_command(cmd)?;
|
||||
let ta = self.ca_store.update(parent, ta, events)?;
|
||||
// The updated CA will now include the newly issued certificate.
|
||||
let response = ca.issuance_response(child, &class_name, &pub_key)?;
|
||||
|
||||
// New entitlements will include this resource class, and
|
||||
// the newly issued certificate.
|
||||
let response = ta.issuance_response(child, &class_name, &pub_key)?;
|
||||
|
||||
Ok(response)
|
||||
}
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
/// See: https://tools.ietf.org/html/rfc6492#section3.5.1-2
|
||||
@@ -480,10 +473,12 @@ impl<S: Signer> CaServer<S> {
|
||||
|
||||
let revoke_responses = self.send_revoke_requests(handle, parent, requests)?;
|
||||
|
||||
for response in revoke_responses.into_iter() {
|
||||
let cmd = CmdDet::key_roll_finish(handle, parent.clone(), response);
|
||||
let events = child.process_command(cmd)?;
|
||||
child = self.ca_store.update(handle, child, events)?;
|
||||
for (rcn, revoke_responses) in revoke_responses.into_iter() {
|
||||
for response in revoke_responses.into_iter() {
|
||||
let cmd = CmdDet::key_roll_finish(handle, rcn.clone(), response);
|
||||
let events = child.process_command(cmd)?;
|
||||
child = self.ca_store.update(handle, child, events)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -493,77 +488,82 @@ impl<S: Signer> CaServer<S> {
|
||||
&self,
|
||||
handle: &Handle,
|
||||
parent: &ParentHandle,
|
||||
requests: Vec<&RevocationRequest>,
|
||||
) -> ServerResult<Vec<RevocationResponse>, S> {
|
||||
revoke_requests: HashMap<ResourceClassName, Vec<RevocationRequest>>,
|
||||
) -> ServerResult<HashMap<ResourceClassName, Vec<RevocationResponse>>, S> {
|
||||
let child = self.ca_store.get_latest(handle)?;
|
||||
match child.parent(parent)? {
|
||||
ParentCaContact::Ta(_) => {
|
||||
Err(ca::Error::NotAllowedForTa).map_err(ServerError::CertAuth)
|
||||
}
|
||||
ParentCaContact::Embedded => {
|
||||
self.send_revoke_requests_embedded(requests, handle, parent)
|
||||
self.send_revoke_requests_embedded(revoke_requests, handle, parent)
|
||||
}
|
||||
ParentCaContact::Rfc6492(parent_res) => {
|
||||
self.send_revoke_requests_rfc6492(requests, child.id_key(), parent_res)
|
||||
self.send_revoke_requests_rfc6492(revoke_requests, child.id_key(), parent_res)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn send_revoke_requests_embedded(
|
||||
&self,
|
||||
revoke_requests: Vec<&RevocationRequest>,
|
||||
revoke_requests: HashMap<ResourceClassName, Vec<RevocationRequest>>,
|
||||
handle: &Handle,
|
||||
parent_h: &ParentHandle,
|
||||
) -> ServerResult<Vec<RevocationResponse>, S> {
|
||||
) -> ServerResult<HashMap<ResourceClassName, Vec<RevocationResponse>>, S> {
|
||||
let mut parent = self.ca_store.get_latest(parent_h)?;
|
||||
let mut revocation_responses = vec![];
|
||||
let mut revoke_map = HashMap::new();
|
||||
|
||||
for req in revoke_requests.into_iter() {
|
||||
let cmd = CmdDet::child_revoke_key(
|
||||
parent_h,
|
||||
handle.clone(),
|
||||
req.clone(),
|
||||
self.signer.clone(),
|
||||
);
|
||||
for (rcn, revoke_requests) in revoke_requests.into_iter() {
|
||||
let mut revocations = vec![];
|
||||
for req in revoke_requests.into_iter() {
|
||||
revocations.push((&req).into());
|
||||
|
||||
let events = parent.process_command(cmd)?;
|
||||
parent = self.ca_store.update(parent_h, parent, events)?;
|
||||
let cmd =
|
||||
CmdDet::child_revoke_key(parent_h, handle.clone(), req, self.signer.clone());
|
||||
|
||||
revocation_responses.push(req.into());
|
||||
let events = parent.process_command(cmd)?;
|
||||
parent = self.ca_store.update(parent_h, parent, events)?;
|
||||
}
|
||||
revoke_map.insert(rcn, revocations);
|
||||
}
|
||||
|
||||
Ok(revocation_responses)
|
||||
Ok(revoke_map)
|
||||
}
|
||||
|
||||
fn send_revoke_requests_rfc6492(
|
||||
&self,
|
||||
revoke_requests: Vec<&RevocationRequest>,
|
||||
revoke_requests: HashMap<ResourceClassName, Vec<RevocationRequest>>,
|
||||
signing_key: &KeyId,
|
||||
parent_res: &rfc8183::ParentResponse,
|
||||
) -> ServerResult<Vec<RevocationResponse>, S> {
|
||||
let mut res = vec![];
|
||||
) -> ServerResult<HashMap<ResourceClassName, Vec<RevocationResponse>>, S> {
|
||||
let mut revoke_map = HashMap::new();
|
||||
|
||||
for req in revoke_requests.into_iter() {
|
||||
let sender = parent_res.child_handle().to_string();
|
||||
let recipient = parent_res.parent_handle().to_string();
|
||||
let revoke = rfc6492::Message::revoke(sender, recipient, req.clone());
|
||||
for (rcn, revoke_requests) in revoke_requests.into_iter() {
|
||||
let mut revocations = vec![];
|
||||
for req in revoke_requests.into_iter() {
|
||||
let sender = parent_res.child_handle().to_string();
|
||||
let recipient = parent_res.parent_handle().to_string();
|
||||
let revoke = rfc6492::Message::revoke(sender, recipient, req.clone());
|
||||
|
||||
match self.send_rfc6492_and_validate_response(
|
||||
signing_key,
|
||||
parent_res,
|
||||
revoke.into_bytes(),
|
||||
) {
|
||||
Err(e) => error!("Could not send/validate revoke: {}", e),
|
||||
Ok(response) => match response {
|
||||
rfc6492::Res::Revoke(revoke_response) => res.push(revoke_response),
|
||||
rfc6492::Res::NotPerformed(e) => error!("We got an error response: {}", e),
|
||||
rfc6492::Res::List(_) => error!("List response to revoke request??"),
|
||||
rfc6492::Res::Issue(_) => error!("Issue response to revoke request??"),
|
||||
},
|
||||
match self.send_rfc6492_and_validate_response(
|
||||
signing_key,
|
||||
parent_res,
|
||||
revoke.into_bytes(),
|
||||
) {
|
||||
Err(e) => error!("Could not send/validate revoke: {}", e),
|
||||
Ok(response) => match response {
|
||||
rfc6492::Res::Revoke(revoke_response) => revocations.push(revoke_response),
|
||||
rfc6492::Res::NotPerformed(e) => error!("We got an error response: {}", e),
|
||||
rfc6492::Res::List(_) => error!("List response to revoke request??"),
|
||||
rfc6492::Res::Issue(_) => error!("Issue response to revoke request??"),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
revoke_map.insert(rcn, revocations);
|
||||
}
|
||||
|
||||
Ok(res)
|
||||
Ok(revoke_map)
|
||||
}
|
||||
|
||||
fn send_cert_requests_handle_responses(
|
||||
@@ -586,14 +586,20 @@ impl<S: Signer> CaServer<S> {
|
||||
}
|
||||
}?;
|
||||
|
||||
for (class_name, issued) in issued_certs.into_iter() {
|
||||
let received = RcvdCert::from(issued);
|
||||
for (class_name, issued_certs) in issued_certs.into_iter() {
|
||||
for issued in issued_certs.into_iter() {
|
||||
let received = RcvdCert::from(issued);
|
||||
|
||||
let upd_rcvd_cmd =
|
||||
CmdDet::upd_received_cert(handle, class_name, received, self.signer.clone());
|
||||
let upd_rcvd_cmd = CmdDet::upd_received_cert(
|
||||
handle,
|
||||
class_name.clone(),
|
||||
received,
|
||||
self.signer.clone(),
|
||||
);
|
||||
|
||||
let evts = child.process_command(upd_rcvd_cmd)?;
|
||||
child = self.ca_store.update(handle, child, evts)?;
|
||||
let evts = child.process_command(upd_rcvd_cmd)?;
|
||||
child = self.ca_store.update(handle, child, evts)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -601,64 +607,76 @@ impl<S: Signer> CaServer<S> {
|
||||
|
||||
fn send_cert_requests_embedded(
|
||||
&self,
|
||||
requests: Vec<IssuanceRequest>,
|
||||
requests: HashMap<ResourceClassName, Vec<IssuanceRequest>>,
|
||||
handle: &Handle,
|
||||
parent_h: &ParentHandle,
|
||||
) -> ServerResult<Vec<(ResourceClassName, IssuedCert)>, S> {
|
||||
) -> ServerResult<HashMap<ResourceClassName, Vec<IssuedCert>>, S> {
|
||||
let mut parent = self.ca_store.get_latest(parent_h)?;
|
||||
|
||||
let mut issued_certs: Vec<(ResourceClassName, IssuedCert)> = vec![];
|
||||
let mut issued_map = HashMap::new();
|
||||
|
||||
for req in requests.into_iter() {
|
||||
let class_name = req.class_name().clone();
|
||||
let pub_key = req.csr().public_key().clone();
|
||||
for (rcn, requests) in requests.into_iter() {
|
||||
let mut issued_certs = vec![];
|
||||
for req in requests.into_iter() {
|
||||
let pub_key = req.csr().public_key().clone();
|
||||
let parent_class = req.class_name().clone();
|
||||
|
||||
let cmd = CmdDet::child_certify(parent_h, handle.clone(), req, self.signer.clone());
|
||||
let cmd = CmdDet::child_certify(parent_h, handle.clone(), req, self.signer.clone());
|
||||
|
||||
let events = parent.process_command(cmd)?;
|
||||
parent = self.ca_store.update(parent_h, parent, events)?;
|
||||
let events = parent.process_command(cmd)?;
|
||||
parent = self.ca_store.update(parent_h, parent, events)?;
|
||||
|
||||
let response = parent.issuance_response(handle, &class_name, &pub_key)?;
|
||||
let response = parent.issuance_response(handle, &parent_class, &pub_key)?;
|
||||
|
||||
let (_, _, _, issued) = response.unwrap();
|
||||
let (_, _, _, issued) = response.unwrap();
|
||||
|
||||
issued_certs.push((class_name, issued));
|
||||
issued_certs.push(issued);
|
||||
}
|
||||
|
||||
issued_map.insert(rcn, issued_certs);
|
||||
}
|
||||
Ok(issued_certs)
|
||||
|
||||
Ok(issued_map)
|
||||
}
|
||||
|
||||
fn send_cert_requests_rfc6492(
|
||||
&self,
|
||||
requests: Vec<IssuanceRequest>,
|
||||
requests: HashMap<ResourceClassName, Vec<IssuanceRequest>>,
|
||||
signing_key: &KeyId,
|
||||
parent_res: &rfc8183::ParentResponse,
|
||||
) -> ServerResult<Vec<(ResourceClassName, IssuedCert)>, S> {
|
||||
let mut res = vec![];
|
||||
) -> ServerResult<HashMap<ResourceClassName, Vec<IssuedCert>>, S> {
|
||||
let mut issued_map = HashMap::new();
|
||||
|
||||
for req in requests.into_iter() {
|
||||
let sender = parent_res.child_handle().to_string();
|
||||
let recipient = parent_res.parent_handle().to_string();
|
||||
let issue = rfc6492::Message::issue(sender, recipient, req);
|
||||
for (rcn, requests) in requests.into_iter() {
|
||||
let mut issued_certs = vec![];
|
||||
|
||||
match self.send_rfc6492_and_validate_response(
|
||||
signing_key,
|
||||
parent_res,
|
||||
issue.into_bytes(),
|
||||
) {
|
||||
Err(e) => error!("Could not send/validate csr: {}", e),
|
||||
Ok(response) => match response {
|
||||
rfc6492::Res::NotPerformed(e) => error!("We got an error response: {}", e),
|
||||
rfc6492::Res::Issue(issue_response) => {
|
||||
let (class_name, _, _, issued) = issue_response.unwrap();
|
||||
res.push((class_name, issued));
|
||||
}
|
||||
rfc6492::Res::List(_) => error!("List reply to issue request??"),
|
||||
rfc6492::Res::Revoke(_) => error!("Revoke reply to issue request??"),
|
||||
},
|
||||
for req in requests.into_iter() {
|
||||
let sender = parent_res.child_handle().to_string();
|
||||
let recipient = parent_res.parent_handle().to_string();
|
||||
let issue = rfc6492::Message::issue(sender, recipient, req);
|
||||
|
||||
match self.send_rfc6492_and_validate_response(
|
||||
signing_key,
|
||||
parent_res,
|
||||
issue.into_bytes(),
|
||||
) {
|
||||
Err(e) => error!("Could not send/validate csr: {}", e),
|
||||
Ok(response) => match response {
|
||||
rfc6492::Res::NotPerformed(e) => error!("We got an error response: {}", e),
|
||||
rfc6492::Res::Issue(issue_response) => {
|
||||
let (_, _, _, issued) = issue_response.unwrap();
|
||||
issued_certs.push(issued);
|
||||
}
|
||||
rfc6492::Res::List(_) => error!("List reply to issue request??"),
|
||||
rfc6492::Res::Revoke(_) => error!("Revoke reply to issue request??"),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
issued_map.insert(rcn, issued_certs);
|
||||
}
|
||||
|
||||
Ok(res)
|
||||
Ok(issued_map)
|
||||
}
|
||||
|
||||
/// Updates the CA if entitlements are different from what the CA
|
||||
|
||||
+65
-9
@@ -18,8 +18,7 @@ use krill_pubd::publishers::PublisherError;
|
||||
use krill_pubd::repo::RrdpServerError;
|
||||
|
||||
use crate::auth::Auth;
|
||||
use crate::ca;
|
||||
use crate::ca::ParentHandle;
|
||||
use crate::ca::{self, ta_handle, ParentHandle};
|
||||
use crate::http::server::AppServer;
|
||||
use crate::krillserver;
|
||||
|
||||
@@ -278,19 +277,38 @@ pub fn ta_cer(server: web::Data<AppServer>) -> HttpResponse {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Deprecate!
|
||||
pub fn ta_add_child(
|
||||
server: web::Data<AppServer>,
|
||||
req: Json<AddChildRequest>,
|
||||
auth: Auth,
|
||||
) -> HttpResponse {
|
||||
if_api_allowed(&server, &auth, || {
|
||||
match server.read().ta_add_child(req.into_inner()) {
|
||||
match server.read().ca_add_child(&ta_handle(), req.into_inner()) {
|
||||
Ok(info) => render_json(info),
|
||||
Err(e) => server_error(&Error::ServerError(e)),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn ca_add_child(
|
||||
server: web::Data<AppServer>,
|
||||
parent: Path<ParentHandle>,
|
||||
req: Json<AddChildRequest>,
|
||||
auth: Auth,
|
||||
) -> HttpResponse {
|
||||
if_api_allowed(&server, &auth, || {
|
||||
match server
|
||||
.read()
|
||||
.ca_add_child(&parent.into_inner(), req.into_inner())
|
||||
{
|
||||
Ok(info) => render_json(info),
|
||||
Err(e) => server_error(&Error::ServerError(e)),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: Deprecate
|
||||
pub fn ta_update_child(
|
||||
server: web::Data<AppServer>,
|
||||
child: Path<Handle>,
|
||||
@@ -298,21 +316,59 @@ pub fn ta_update_child(
|
||||
auth: Auth,
|
||||
) -> HttpResponse {
|
||||
if_api_allowed(&server, &auth, || {
|
||||
render_empty_res(
|
||||
server
|
||||
.read()
|
||||
.ta_update_child(child.into_inner(), req.into_inner()),
|
||||
)
|
||||
render_empty_res(server.read().ca_update_child(
|
||||
&ta_handle(),
|
||||
child.into_inner(),
|
||||
req.into_inner(),
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn ca_update_child(
|
||||
server: web::Data<AppServer>,
|
||||
parent: Path<Handle>,
|
||||
child: Path<Handle>,
|
||||
req: Json<UpdateChildRequest>,
|
||||
auth: Auth,
|
||||
) -> HttpResponse {
|
||||
if_api_allowed(&server, &auth, || {
|
||||
render_empty_res(server.read().ca_update_child(
|
||||
&parent.into_inner(),
|
||||
child.into_inner(),
|
||||
req.into_inner(),
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: Deprecate
|
||||
pub fn ta_show_child(
|
||||
server: web::Data<AppServer>,
|
||||
child: Path<Handle>,
|
||||
auth: Auth,
|
||||
) -> HttpResponse {
|
||||
if_api_allowed(&server, &auth, || {
|
||||
match server.read().ta_show_child(&child.into_inner()) {
|
||||
match server
|
||||
.read()
|
||||
.ca_show_child(&ta_handle(), &child.into_inner())
|
||||
{
|
||||
Ok(Some(child)) => render_json(child),
|
||||
Ok(None) => api_not_found(),
|
||||
Err(e) => server_error(&Error::ServerError(e)),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn ca_show_child(
|
||||
server: web::Data<AppServer>,
|
||||
parent: Path<Handle>,
|
||||
child: Path<Handle>,
|
||||
auth: Auth,
|
||||
) -> HttpResponse {
|
||||
if_api_allowed(&server, &auth, || {
|
||||
match server
|
||||
.read()
|
||||
.ca_show_child(&parent.into_inner(), &child.into_inner())
|
||||
{
|
||||
Ok(Some(child)) => render_json(child),
|
||||
Ok(None) => api_not_found(),
|
||||
Err(e) => server_error(&Error::ServerError(e)),
|
||||
|
||||
@@ -82,12 +82,15 @@ pub fn start(config: &Config) -> Result<(), Error> {
|
||||
.route("/trustanchor/children/{handle}", post().to(ta_update_child))
|
||||
.route("/cas", post().to(ca_init))
|
||||
.route("/cas", get().to(cas))
|
||||
.route("/cas/{handle}", get().to(ca_info))
|
||||
.route("/cas/{handle}/child_request", get().to(ca_child_req))
|
||||
.route("/cas/{handle}/parents", post().to(ca_add_parent))
|
||||
.route("/cas/{handle}/keys/roll_init", post().to(ca_keyroll_init))
|
||||
.route("/cas/{ca}", get().to(ca_info))
|
||||
.route("/cas/{ca}/child_request", get().to(ca_child_req))
|
||||
.route("/cas/{ca}/parents", post().to(ca_add_parent))
|
||||
.route("/cas/{ca}/children", post().to(ca_add_child))
|
||||
.route("/cas/{ca}/children/{child}", get().to(ca_show_child))
|
||||
.route("/cas/{ca}/children/{child}", post().to(ca_update_child))
|
||||
.route("/cas/{ca}/keys/roll_init", post().to(ca_keyroll_init))
|
||||
.route(
|
||||
"/cas/{handle}/keys/roll_activate",
|
||||
"/cas/{ca}/keys/roll_activate",
|
||||
post().to(ca_keyroll_activate),
|
||||
)
|
||||
.route("/republish", post().to(republish_all)),
|
||||
|
||||
+23
-10
@@ -27,8 +27,7 @@ use krill_pubd::publishers::Publisher;
|
||||
use krill_pubd::PubServer;
|
||||
|
||||
use crate::auth::{Auth, Authorizer};
|
||||
use crate::ca::ChildHandle;
|
||||
use crate::ca::{self, ta_handle};
|
||||
use crate::ca::{self, ta_handle, ChildHandle, ParentHandle};
|
||||
use crate::config::Config;
|
||||
use crate::mq::EventQueueListener;
|
||||
use crate::scheduler::Scheduler;
|
||||
@@ -310,21 +309,35 @@ impl KrillServer {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Adds a child to the TA and returns the ParentCaInfo that the child
|
||||
/// will to contact this TA for resource requests.
|
||||
pub fn ta_add_child(&self, req: AddChildRequest) -> KrillRes<ParentCaContact> {
|
||||
let contact = self.caserver.ta_add_child(req, &self.service_uri)?;
|
||||
/// Adds a child to a CA and returns the ParentCaInfo that the child
|
||||
/// will need to contact this CA for resource requests.
|
||||
pub fn ca_add_child(
|
||||
&self,
|
||||
parent: &ParentHandle,
|
||||
req: AddChildRequest,
|
||||
) -> KrillRes<ParentCaContact> {
|
||||
let contact = self.caserver.ca_add_child(parent, req, &self.service_uri)?;
|
||||
Ok(contact)
|
||||
}
|
||||
|
||||
pub fn ta_update_child(&self, child: ChildHandle, req: UpdateChildRequest) -> EmptyRes {
|
||||
self.caserver.ta_update_child(child, req)?;
|
||||
/// Update IdCert or resources of a child.
|
||||
pub fn ca_update_child(
|
||||
&self,
|
||||
parent: &ParentHandle,
|
||||
child: ChildHandle,
|
||||
req: UpdateChildRequest,
|
||||
) -> EmptyRes {
|
||||
self.caserver.ca_update_child(parent, child, req)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Show details for a child under the TA.
|
||||
pub fn ta_show_child(&self, child: &ChildHandle) -> KrillRes<Option<ChildCaInfo>> {
|
||||
let child = self.caserver.ta_show_child(child)?;
|
||||
pub fn ca_show_child(
|
||||
&self,
|
||||
parent: &ParentHandle,
|
||||
child: &ChildHandle,
|
||||
) -> KrillRes<Option<ChildCaInfo>> {
|
||||
let child = self.caserver.ca_show_child(parent, child)?;
|
||||
Ok(child)
|
||||
}
|
||||
|
||||
|
||||
+13
-4
@@ -3,11 +3,12 @@
|
||||
//! signed material, or asking a newly added parent for resource
|
||||
//! entitlements.
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
use std::fmt;
|
||||
use std::sync::RwLock;
|
||||
|
||||
use krill_commons::api::admin::Handle;
|
||||
use krill_commons::api::ca::ResourceClassName;
|
||||
use krill_commons::api::publication::PublishDelta;
|
||||
use krill_commons::api::RevocationRequest;
|
||||
use krill_commons::eventsourcing;
|
||||
@@ -24,7 +25,11 @@ pub enum QueueEvent {
|
||||
Delta(Handle, PublishDelta),
|
||||
ParentAdded(Handle, ParentHandle),
|
||||
RequestsPending(Handle),
|
||||
ResourceClassRemoved(Handle, ParentHandle, Vec<RevocationRequest>),
|
||||
ResourceClassRemoved(
|
||||
Handle,
|
||||
ParentHandle,
|
||||
HashMap<ResourceClassName, Vec<RevocationRequest>>,
|
||||
),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -72,12 +77,16 @@ impl<S: Signer> eventsourcing::EventListener<CertAuth<S>> for EventQueueListener
|
||||
let evt = QueueEvent::Delta(handle.clone(), publish_delta);
|
||||
self.push_back(evt);
|
||||
}
|
||||
EvtDet::ResourceClassRemoved(_class_name, delta, parent, revocations) => {
|
||||
EvtDet::ResourceClassRemoved(class_name, delta, parent, revocations) => {
|
||||
self.push_back(QueueEvent::Delta(handle.clone(), delta.clone().into()));
|
||||
|
||||
let mut revocations_map = HashMap::new();
|
||||
revocations_map.insert(class_name.clone(), revocations.clone());
|
||||
|
||||
self.push_back(QueueEvent::ResourceClassRemoved(
|
||||
handle.clone(),
|
||||
parent.clone(),
|
||||
revocations.clone(),
|
||||
revocations_map,
|
||||
))
|
||||
}
|
||||
EvtDet::KeyRollFinished(_class_name, delta) => {
|
||||
|
||||
@@ -64,7 +64,6 @@ fn make_event_sh(
|
||||
publish(&handle, delta, &pubserver);
|
||||
}
|
||||
QueueEvent::ResourceClassRemoved(handle, parent, revocations) => {
|
||||
let revocations = revocations.iter().collect();
|
||||
if caserver
|
||||
.send_revoke_requests(&handle, &parent, revocations)
|
||||
.is_err()
|
||||
|
||||
+25
-7
@@ -15,7 +15,7 @@ use krill_commons::api::ca::{CertAuthInfo, ResourceClassKeysInfo, ResourceClassN
|
||||
use krill_commons::remote::rfc8183;
|
||||
use krill_commons::util::test;
|
||||
|
||||
use crate::ca::ta_handle;
|
||||
use crate::ca::{ta_handle, ChildHandle, ParentHandle};
|
||||
use crate::config::Config;
|
||||
use crate::http::server;
|
||||
|
||||
@@ -101,7 +101,7 @@ pub fn add_child_to_ta_embedded(handle: &Handle, resources: ResourceSet) -> Pare
|
||||
let res = krill_admin(Command::TrustAnchor(TrustAnchorCommand::AddChild(req)));
|
||||
|
||||
match res {
|
||||
ApiResponse::ParentCaInfo(info) => info,
|
||||
ApiResponse::ParentCaContact(info) => info,
|
||||
_ => panic!("Expected ParentCaInfo response"),
|
||||
}
|
||||
}
|
||||
@@ -116,7 +116,23 @@ pub fn add_child_to_ta_rfc6492(
|
||||
let res = krill_admin(Command::TrustAnchor(TrustAnchorCommand::AddChild(req)));
|
||||
|
||||
match res {
|
||||
ApiResponse::ParentCaInfo(info) => info,
|
||||
ApiResponse::ParentCaContact(info) => info,
|
||||
_ => panic!("Expected ParentCaInfo response"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_child_rfc6492(
|
||||
parent: &ParentHandle,
|
||||
child: &ChildHandle,
|
||||
req: rfc8183::ChildRequest,
|
||||
resources: ResourceSet,
|
||||
) -> ParentCaContact {
|
||||
let auth = ChildAuthRequest::Rfc8183(req);
|
||||
let req = AddChildRequest::new(child.clone(), resources, auth);
|
||||
let res = krill_admin(Command::CertAuth(CaCommand::AddChild(parent.clone(), req)));
|
||||
|
||||
match res {
|
||||
ApiResponse::ParentCaContact(info) => info,
|
||||
_ => panic!("Expected ParentCaInfo response"),
|
||||
}
|
||||
}
|
||||
@@ -180,7 +196,7 @@ where
|
||||
panic!(error_msg);
|
||||
}
|
||||
|
||||
pub fn wait_for_resources_on_current_key(handle: &Handle, resources: &ResourceSet) {
|
||||
pub fn wait_for_current_resources(handle: &Handle, resources: &ResourceSet) {
|
||||
wait_for(
|
||||
30,
|
||||
"cms child did not get its resource certificate",
|
||||
@@ -249,17 +265,19 @@ pub fn ta_issued_resources(child: &Handle) -> ResourceSet {
|
||||
pub fn ca_current_resources(handle: &Handle) -> ResourceSet {
|
||||
let ca = ca_details(handle);
|
||||
|
||||
if let Some(rc) = ca.resources().get(&ResourceClassName::default()) {
|
||||
let mut res = ResourceSet::default();
|
||||
|
||||
for rc in ca.resources().values() {
|
||||
match rc.keys() {
|
||||
ResourceClassKeysInfo::Active(current)
|
||||
| ResourceClassKeysInfo::RollPending(_, current)
|
||||
| ResourceClassKeysInfo::RollNew(_, current)
|
||||
| ResourceClassKeysInfo::RollOld(current, _) => {
|
||||
return current.incoming_cert().resources().clone()
|
||||
res = res.union(current.incoming_cert().resources());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
ResourceSet::default()
|
||||
res
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ fn ca_keyroll_under_rfc6492_ta() {
|
||||
};
|
||||
|
||||
add_parent_to_ca(&child, parent);
|
||||
wait_for_resources_on_current_key(&child, &child_resources);
|
||||
wait_for_current_resources(&child, &child_resources);
|
||||
wait_for_ta_to_have_number_of_issued_certs(1);
|
||||
|
||||
ca_roll_init(&child);
|
||||
|
||||
@@ -28,7 +28,7 @@ fn ca_under_embedded_ta() {
|
||||
|
||||
// When the parent is added, a child CA will immediately request a certificate.
|
||||
add_parent_to_ca(&child, parent);
|
||||
wait_for_resources_on_current_key(&child, &child_resources);
|
||||
wait_for_current_resources(&child, &child_resources);
|
||||
wait_for_ta_to_have_number_of_issued_certs(1);
|
||||
|
||||
// When the parent adds resources to a CA, it will allocate them only when the child
|
||||
@@ -36,7 +36,7 @@ fn ca_under_embedded_ta() {
|
||||
let new_child_resources = ResourceSet::from_strs("AS65000", "10.0.0.0/16", "").unwrap();
|
||||
force_update_child(&child, &new_child_resources);
|
||||
assert_eq!(ta_issued_resources(&child), child_resources);
|
||||
wait_for_resources_on_current_key(&child, &new_child_resources);
|
||||
wait_for_current_resources(&child, &new_child_resources);
|
||||
wait_for_ta_to_have_number_of_issued_certs(1);
|
||||
assert_eq!(ta_issued_resources(&child), new_child_resources);
|
||||
|
||||
@@ -45,7 +45,7 @@ fn ca_under_embedded_ta() {
|
||||
let child_resources = ResourceSet::from_strs("", "10.0.0.0/24", "").unwrap();
|
||||
force_update_child(&child, &child_resources);
|
||||
assert_eq!(ta_issued_resources(&child), child_resources);
|
||||
wait_for_resources_on_current_key(&child, &child_resources);
|
||||
wait_for_current_resources(&child, &child_resources);
|
||||
|
||||
// When all resources are removed, the child still gets a chance to clean up if force
|
||||
// is not used.. The child will request that its certificate is revoked, and remove
|
||||
|
||||
@@ -29,7 +29,7 @@ fn ca_under_rfc6492_ta() {
|
||||
|
||||
// When the parent is added, a child CA will immediately request a certificate.
|
||||
add_parent_to_ca(&child, parent);
|
||||
wait_for_resources_on_current_key(&child, &child_resources);
|
||||
wait_for_current_resources(&child, &child_resources);
|
||||
wait_for_ta_to_have_number_of_issued_certs(1);
|
||||
|
||||
// When the parent adds resources to a CA, it will allocate them only when the child
|
||||
@@ -37,7 +37,7 @@ fn ca_under_rfc6492_ta() {
|
||||
let new_child_resources = ResourceSet::from_strs("AS65000", "10.0.0.0/16", "").unwrap();
|
||||
force_update_child(&child, &new_child_resources);
|
||||
assert_eq!(ta_issued_resources(&child), child_resources);
|
||||
wait_for_resources_on_current_key(&child, &new_child_resources);
|
||||
wait_for_current_resources(&child, &new_child_resources);
|
||||
wait_for_ta_to_have_number_of_issued_certs(1);
|
||||
assert_eq!(ta_issued_resources(&child), new_child_resources);
|
||||
|
||||
@@ -46,7 +46,7 @@ fn ca_under_rfc6492_ta() {
|
||||
let child_resources = ResourceSet::from_strs("", "10.0.0.0/24", "").unwrap();
|
||||
force_update_child(&child, &child_resources);
|
||||
assert_eq!(ta_issued_resources(&child), child_resources);
|
||||
wait_for_resources_on_current_key(&child, &child_resources);
|
||||
wait_for_current_resources(&child, &child_resources);
|
||||
|
||||
// When all resources are removed, the child still gets a chance to clean up if force
|
||||
// is not used.. The child will request that its certificate is revoked, and remove
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
extern crate krill_client;
|
||||
extern crate krill_commons;
|
||||
extern crate krill_daemon;
|
||||
extern crate krill_pubc;
|
||||
|
||||
use krill_commons::api::admin::{AddParentRequest, Handle, Token};
|
||||
use krill_commons::api::ca::ResourceSet;
|
||||
use krill_daemon::ca::ta_handle;
|
||||
use krill_daemon::test::*;
|
||||
|
||||
#[test]
|
||||
fn grand_children() {
|
||||
test_with_krill_server(|_d| {
|
||||
// Test that we can delegate from normal CAs to child CAs, and that these child CAs
|
||||
// can have multiple parents.
|
||||
//
|
||||
// TA
|
||||
// / \
|
||||
// CA1 CA2
|
||||
// \ /
|
||||
// CA3 (two resource classes)
|
||||
// | |
|
||||
// CA4 (two resource classes)
|
||||
//
|
||||
|
||||
// -------------------- TA -----------------------------------------------
|
||||
|
||||
let ta_handle = ta_handle();
|
||||
init_ta();
|
||||
|
||||
// -------------------- CA1 -----------------------------------------------
|
||||
let ca1 = Handle::from("CA1");
|
||||
let ca1_res = ResourceSet::from_strs("", "10.0.0.0/16", "").unwrap();
|
||||
|
||||
init_child(&ca1, &Token::from("CA1"));
|
||||
let req = child_request(&ca1);
|
||||
let parent = {
|
||||
let contact = add_child_to_ta_rfc6492(&ca1, req, ca1_res.clone());
|
||||
AddParentRequest::new(ta_handle.clone(), contact)
|
||||
};
|
||||
add_parent_to_ca(&ca1, parent);
|
||||
wait_for_current_resources(&ca1, &ca1_res);
|
||||
|
||||
// -------------------- CA2 -----------------------------------------------
|
||||
let ca2 = Handle::from("CA2");
|
||||
let ca2_res = ResourceSet::from_strs("", "10.1.0.0/16", "").unwrap();
|
||||
|
||||
init_child(&ca2, &Token::from("CA2"));
|
||||
let req = child_request(&ca2);
|
||||
let parent = {
|
||||
let contact = add_child_to_ta_rfc6492(&ca2, req, ca2_res.clone());
|
||||
AddParentRequest::new(ta_handle.clone(), contact)
|
||||
};
|
||||
add_parent_to_ca(&ca2, parent);
|
||||
wait_for_current_resources(&ca2, &ca2_res);
|
||||
|
||||
// -------------------- CA3 -----------------------------------------------
|
||||
let ca3 = Handle::from("CA3");
|
||||
let ca_3_res_under_ca_1 = ResourceSet::from_strs("", "10.0.0.0/16", "").unwrap();
|
||||
|
||||
init_child(&ca3, &Token::from("CA3"));
|
||||
let req = child_request(&ca3);
|
||||
let parent = {
|
||||
let contact = add_child_rfc6492(&ca1, &ca3, req, ca_3_res_under_ca_1.clone());
|
||||
AddParentRequest::new(ca1.clone(), contact)
|
||||
};
|
||||
add_parent_to_ca(&ca3, parent);
|
||||
wait_for_current_resources(&ca3, &ca_3_res_under_ca_1);
|
||||
|
||||
let ca_3_res_under_ca_2 = ResourceSet::from_strs("", "10.1.0.0/24", "").unwrap();
|
||||
let ca_3_res = ca_3_res_under_ca_1.union(&ca_3_res_under_ca_2);
|
||||
let req = child_request(&ca3);
|
||||
let parent = {
|
||||
let contact = add_child_rfc6492(&ca2, &ca3, req, ca_3_res_under_ca_2.clone());
|
||||
AddParentRequest::new(ca2.clone(), contact)
|
||||
};
|
||||
add_parent_to_ca(&ca3, parent);
|
||||
wait_for_current_resources(&ca3, &ca_3_res);
|
||||
|
||||
// -------------------- CA4 -----------------------------------------------
|
||||
let ca4 = Handle::from("CA4");
|
||||
let ca_4_res_under_ca_3 = ResourceSet::from_strs("", "10.0.0.0-10.1.0.255", "").unwrap();
|
||||
|
||||
init_child(&ca4, &Token::from("CA4"));
|
||||
let req = child_request(&ca4);
|
||||
let parent = {
|
||||
let contact = add_child_rfc6492(&ca3, &ca4, req, ca_4_res_under_ca_3.clone());
|
||||
AddParentRequest::new(ca3.clone(), contact)
|
||||
};
|
||||
add_parent_to_ca(&ca4, parent);
|
||||
wait_for_current_resources(&ca4, &ca_4_res_under_ca_3);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user