Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 40 additions & 1 deletion src/server/mq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ pub enum Task {
parent: ParentHandle,
},

SyncChildren {
ca_handle: CaHandle,
ca_version: u64
},

ResourceClassRemoved {
ca_handle: CaHandle,
ca_version: u64,
Expand Down Expand Up @@ -120,6 +125,20 @@ impl Task {
).finish()
)
}
Task::SyncChildren {
ca_handle: ca,
..
} => {
Cow::Owned(
Ident::builder(
const { Ident::make("sync_") }
).push_handle(
ca
).push_ident(
const { Ident::make("_children") }
).finish()
)
},
Task::SuspendChildrenIfNeeded { ca_handle: ca } => {
Cow::Owned(
Ident::builder(
Expand Down Expand Up @@ -234,6 +253,12 @@ impl fmt::Display for Task {
} => {
write!(f, "synchronize CA '{ca}' with parent '{parent}'")
}
Task::SyncChildren {
ca_handle: ca,
..
} => {
write!(f, "synchronize CA '{ca}' with children")
}
Task::RenewTestbedTa => write!(f, "renew testbed TA"),
Task::SyncTrustAnchorProxySignerIfPossible => {
write!(f, "sync TA Proxy and Signer if both in this server.")
Expand Down Expand Up @@ -624,7 +649,21 @@ impl eventsourcing::PostSaveEventListener<CertAuth> for TaskQueue {
);
}
}

CertAuthEvent::ChildCertificateIssued { child, .. } => {
if let Err(e) = self.schedule(
Task::SyncChildren {
ca_handle: child.convert(),
ca_version: 0
},
now()
) {
error!(
"Could not schedule sync from {}. Restart Krill or run 'krillc bulk refresh'. Error was: {}",
child,
e
);
}
}
_ => {
// nothing to do
}
Expand Down
29 changes: 29 additions & 0 deletions src/server/scheduler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,11 @@ impl Scheduler {
parent,
} => self.sync_parent(ca, ca_version, parent).await,

Task::SyncChildren {
ca_handle: ca,
ca_version,
} => self.sync_children(ca, ca_version).await,

Task::RenewTestbedTa => self.renew_testbed_ta().await,

Task::SyncTrustAnchorProxySignerIfPossible => {
Expand Down Expand Up @@ -419,6 +424,30 @@ impl Scheduler {
}
}

async fn sync_children(
&self,
ca: CaHandle,
ca_version: u64,
) -> Result<TaskResult, FatalError> {
if let Ok(parent) = self.ca_manager.get_ca(&ca) {
for child in parent.children() {
if let Ok(child) = self.ca_manager.get_ca(&child.convert()) {
debug!(
"Syncing child '{}' for parent {}",
child.handle(),
parent.handle()
);
self.tasks.schedule(Task::SyncParent {
ca_handle: child.handle().convert(),
ca_version,
parent: parent.handle().convert()
}, now()).map_err(FatalError)?;
}
}
}
Ok(TaskResult::Done)
}

/// Resync the testbed TA signer and proxy
async fn renew_testbed_ta(&self) -> Result<TaskResult, FatalError> {
if let Err(e) = self.ca_manager.ta_renew_testbed_ta() {
Expand Down
182 changes: 182 additions & 0 deletions tests/functional_grandchild_resources.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
//! Tests grandchild resources updated.

use krill::api;
use rpki::repository::resources::ResourceSet;

use crate::common::sleep_seconds;

mod common;


//------------ Test Function -------------------------------------------------

/// Test Krill parent/child/grandchild interactions.
///
/// The setup is:
///
/// ```text
/// TA
/// |
/// parent
/// |
/// child
/// |
/// grandchild
/// |
/// grandgrandchild
/// ```
///
/// The test verifies that:
/// * Grandchild gets the resources from child,
/// * Parent can restrict the resources on child,
/// * Grandchild will learn about those resource changes and adjust the
/// effective resources accordingly

#[tokio::test]
async fn functional_resource_updates() {
let (server, _tmpdir)
= common::KrillServer::start_with_file_storage_and_testbed().await;

let testbed = common::ca_handle("testbed");

let ca_parent = common::ca_handle("parent");
let ca_parent_res = common::resources("AS65000", "10.0.0.0/16", "");

let ca_child = common::ca_handle("child");
let ca_child_res = common::resources("AS65000", "10.0.0.0/16", "");

let ca_grandchild = common::ca_handle("grandchild");
let ca_grandchild_res = common::resources("AS65000", "10.0.0.0/23", "");

let ca_grandgrandchild = common::ca_handle("grandgrandchild");
let ca_grandgrandchild_res = common::resources("AS65000", "10.0.0.0/23", "");

let rcn0 = common::rcn(0);

// Wait for the *testbed* CA to get its certificate, this means
// that all CAs which are set up as part of krill_start under the
// testbed config have been set up.
assert!(
server.wait_for_ca_resources(&testbed, &ResourceSet::all()).await
);

eprintln!(">>>> Set up CA parent under testbed.");
server.create_ca_with_repo(&ca_parent).await;
server.register_ca_with_parent(&ca_parent, &testbed, &ca_parent_res).await;

eprintln!(">>>> Set up CA child under parent.");
server.create_ca_with_repo(&ca_child).await;
server.register_ca_with_parent(&ca_child, &ca_parent, &ca_child_res).await;

eprintln!(">>>> Set up CA grandchild under child.");
server.create_ca_with_repo(&ca_grandchild).await;
server.register_ca_with_parent(&ca_grandchild, &ca_child, &ca_grandchild_res).await;

eprintln!(">>>> Set up CA grandgrandchild under grandchild.");
server.create_ca_with_repo(&ca_grandgrandchild).await;
server.register_ca_with_parent(&ca_grandgrandchild, &ca_grandchild, &ca_grandgrandchild_res).await;

eprintln!(">>>> Expect that CA child publishes the certificate for CA grandchild.");
let mut files = server.expected_objects(&ca_child);
files.push_mft_and_crl(&rcn0).await;
files.push_cer(&ca_grandchild, &rcn0).await;
assert!(files.wait_for_published().await);

sleep_seconds(3).await;

eprintln!(">>>> Update resources given to the child.");
let ca_child_new_res = common::resources("65000", "10.0.0.0/24", "");
let ca_grandchild_new_res =
ca_grandchild_res.clone().intersection(&ca_child_new_res);
let ca_grandgrandchild_new_res =
ca_grandgrandchild_res.clone().intersection(&ca_grandchild_new_res);
eprintln!("New resources: {}", &ca_grandchild_new_res);
server.client().child_update(
&ca_parent,
&ca_child.convert(),
api::admin::UpdateChildRequest::resources(ca_child_new_res.clone())
).await.unwrap();
assert!(server.wait_for_ca_resources(&ca_child, &ca_child_new_res).await);

// Wait before the child gets aware of the new resources
sleep_seconds(3).await;

eprintln!(">>>> Check new resources given to the child.");
assert_eq!(
server.client().ca_details(&ca_child).await.unwrap().resources,
ca_child_new_res.clone()
);
// We want the 'parent' and 'child' to agree on the new resources.
// i.e. that the 'child' learnt that the new resource set it smaller.
assert_eq!(
server.client().ca_details(&ca_child).await.unwrap().resources,
server.client().child_details(
&ca_parent, &ca_child.convert()
).await.unwrap().entitled_resources,
);

sleep_seconds(3).await;

eprintln!(">>>> Check resources updated on grandchild.");
// We do not want the 'child' to update the entitled resources of the
// 'grandchild'. The resources of the grandchild will shrink automatically
// and grow again when the child gets more resources again.
// This checks the entitled resources have not changed.
assert_eq!(
server.client().child_details(
&ca_child, &ca_grandchild.convert()
).await.unwrap().entitled_resources,
ca_grandchild_res.clone()
);
// We do want 'grandchild' to learn of the effective resources after
// shrinking.
assert_ne!(
server.client().ca_details(&ca_grandchild).await.unwrap().resources,
ca_grandchild_res.clone()
);
assert_eq!(
server.client().ca_details(&ca_grandchild).await.unwrap().resources,
ca_grandchild_new_res.clone()
);
// We also want the grandgrandchild to learn of the new effective resources
assert_eq!(
server.client().child_details(
&ca_grandchild, &ca_grandgrandchild.convert()
).await.unwrap().entitled_resources,
ca_grandgrandchild_res.clone()
);
assert_eq!(
server.client().ca_details(&ca_grandgrandchild).await.unwrap().resources,
ca_grandgrandchild_new_res.clone()
);

eprintln!(">>>> Revert the resources given to the child.");
server.client().child_update(
&ca_parent,
&ca_child.convert(),
api::admin::UpdateChildRequest::resources(ca_child_res.clone())
).await.unwrap();
assert!(server.wait_for_ca_resources(&ca_child, &ca_child_new_res).await);

sleep_seconds(3).await;

// Everything should be back to the original state again
assert_eq!(
server.client().ca_details(&ca_child).await.unwrap().resources,
ca_child_res.clone()
);
assert_eq!(
server.client().child_details(
&ca_child, &ca_grandchild.convert()
).await.unwrap().entitled_resources,
ca_grandchild_res.clone()
);
assert_eq!(
server.client().ca_details(&ca_grandchild).await.unwrap().resources,
ca_grandchild_res.clone()
);
assert_eq!(
server.client().ca_details(&ca_grandgrandchild).await.unwrap().resources,
ca_grandgrandchild_res.clone()
);
}