Provide (scoped) ROA suggestions. (#289)

This commit is contained in:
Tim Bruijnzeels
2020-07-28 11:16:14 +02:00
parent cc41dde274
commit 3fb4d164b3
12 changed files with 490 additions and 19 deletions
+12
View File
@@ -229,6 +229,18 @@ impl KrillClient {
Ok(ApiResponse::BgpAnalysisRoas(report.into()))
}
CaCommand::BgpAnalysisSuggest(handle, resources) => {
let uri = format!("api/v1/cas/{}/routes/analysis/suggest", handle);
let suggestions = if let Some(resources) = resources {
self.post_json_with_response(&uri, resources).await?
} else {
self.get_json(&uri).await?
};
Ok(ApiResponse::BgpAnalysisSuggestions(suggestions))
}
CaCommand::Show(handle) => {
let uri = format!("api/v1/cas/{}", handle);
let ca_info = self.get_json(&uri).await?;
+51
View File
@@ -611,6 +611,33 @@ impl Options {
app.subcommand(sub)
}
fn make_cas_routes_bgp_suggestions_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> {
let mut sub = SubCommand::with_name("suggest").about("Show ROA suggestions.");
sub = Self::add_general_args(sub);
sub = Self::add_my_ca_arg(sub);
sub = sub
.arg(
Arg::with_name("ipv4")
.short("4")
.long("ipv4")
.value_name("IPv4 resources")
.help("Scope to these IPv4 resources")
.required(false),
)
.arg(
Arg::with_name("ipv6")
.short("6")
.long("ipv6")
.value_name("IPv6 resources")
.help("Scope to these IPv6 resources")
.required(false),
);
app.subcommand(sub)
}
fn make_cas_routes_bgp_sc<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> {
let mut sub =
SubCommand::with_name("bgp").about("Show current authorizations in relation to known announcements.");
@@ -618,6 +645,7 @@ impl Options {
sub = Self::make_cas_routes_bgp_full_sc(sub);
sub = Self::make_cas_routes_bgp_announcements_sc(sub);
sub = Self::make_cas_routes_bgp_roas_sc(sub);
sub = Self::make_cas_routes_bgp_suggestions_sc(sub);
app.subcommand(sub)
}
@@ -1321,6 +1349,24 @@ impl Options {
))
}
fn parse_matches_cas_routes_bgp_suggest(matches: &ArgMatches) -> Result<Options, Error> {
let general_args = GeneralArgs::from_matches(matches)?;
let my_ca = Self::parse_my_ca(matches)?;
let v4 = matches.value_of("ipv4").unwrap_or("");
let v6 = matches.value_of("ipv6").unwrap_or("");
let resources = ResourceSet::from_strs("", v4, v6)
.map_err(|e| Error::GeneralArgumentError(format!("Could not parse IP resources: {}", e)))?;
let resources = if resources.is_empty() { None } else { Some(resources) };
Ok(Options::make(
general_args,
Command::CertAuth(CaCommand::BgpAnalysisSuggest(my_ca, resources)),
))
}
fn parse_matches_cas_routes_bgp(matches: &ArgMatches) -> Result<Options, Error> {
if let Some(m) = matches.subcommand_matches("full") {
Self::parse_matches_cas_routes_bgp_full(m)
@@ -1328,6 +1374,8 @@ impl Options {
Self::parse_matches_cas_routes_bgp_announcements(m)
} else if let Some(m) = matches.subcommand_matches("roas") {
Self::parse_matches_cas_routes_bgp_roas(m)
} else if let Some(m) = matches.subcommand_matches("suggest") {
Self::parse_matches_cas_routes_bgp_suggest(m)
} else {
Err(Error::UnrecognisedSubCommand)
}
@@ -1722,6 +1770,9 @@ pub enum CaCommand {
#[display(fmt = "Show ROA centric summary of ROA vs BGP analysis for ca: '{}'", _0)]
BgpAnalysisRoas(Handle),
#[display(fmt = "Show ROA suggestions based on BGP analysis for ca: '{}'", _0)]
BgpAnalysisSuggest(Handle, Option<ResourceSet>),
// Show details for this CA
#[display(fmt = "Show details for ca: '{}'", _0)]
Show(Handle),
+9 -1
View File
@@ -10,7 +10,7 @@ use crate::commons::api::{
CertAuthList, ChildCaInfo, CommandHistory, CurrentObjects, CurrentRepoState, ParentCaContact, PublisherDetails,
PublisherList, RepositoryContact, RoaDefinition, ServerInfo, StoredEffect,
};
use crate::commons::bgp::{AnnouncementReport, BgpAnalysisReport, RoaReport};
use crate::commons::bgp::{AnnouncementReport, BgpAnalysisReport, BgpAnalysisSuggestion, RoaReport};
use crate::commons::eventsourcing::WithStorableDetails;
use crate::commons::remote::api::ClientInfo;
use crate::commons::remote::rfc8183;
@@ -34,6 +34,7 @@ pub enum ApiResponse {
BgpAnalysisFull(BgpAnalysisReport),
BgpAnalysisAnnouncements(AnnouncementReport),
BgpAnalysisRoas(RoaReport),
BgpAnalysisSuggestions(BgpAnalysisSuggestion),
ParentCaContact(ParentCaContact),
@@ -78,6 +79,7 @@ impl ApiResponse {
ApiResponse::BgpAnalysisFull(table) => Ok(Some(table.report(fmt)?)),
ApiResponse::BgpAnalysisAnnouncements(summary) => Ok(Some(summary.report(fmt)?)),
ApiResponse::BgpAnalysisRoas(summary) => Ok(Some(summary.report(fmt)?)),
ApiResponse::BgpAnalysisSuggestions(suggestions) => Ok(Some(suggestions.report(fmt)?)),
ApiResponse::ParentCaContact(contact) => Ok(Some(contact.report(fmt)?)),
ApiResponse::ChildInfo(info) => Ok(Some(info.report(fmt)?)),
ApiResponse::PublisherList(list) => Ok(Some(list.report(fmt)?)),
@@ -430,6 +432,12 @@ impl Report for RoaReport {
}
}
impl Report for BgpAnalysisSuggestion {
fn text(&self) -> Result<String, ReportError> {
Ok(self.to_string())
}
}
impl Report for CaRepoDetails {
fn text(&self) -> Result<String, ReportError> {
let mut res = String::new();
+4
View File
@@ -278,6 +278,10 @@ pub struct RoaDefinitionUpdates {
}
impl RoaDefinitionUpdates {
pub fn is_empty(&self) -> bool {
self.added.is_empty() && self.removed.is_empty()
}
pub fn new(added: HashSet<RoaDefinition>, removed: HashSet<RoaDefinition>) -> Self {
RoaDefinitionUpdates { added, removed }
}
+85 -7
View File
@@ -10,7 +10,8 @@ use rpki::x509::Time;
use crate::commons::api::{ResourceSet, RoaDefinition, TypedPrefix};
use crate::commons::bgp::{
make_roa_tree, make_validated_announcement_tree, Announcement, AnnouncementValidity, Announcements,
BgpAnalysisEntry, BgpAnalysisReport, IpRange, RisDumpError, RisDumpLoader, ValidatedAnnouncement,
BgpAnalysisEntry, BgpAnalysisReport, BgpAnalysisState, BgpAnalysisSuggestion, IpRange, RisDumpError, RisDumpLoader,
ValidatedAnnouncement,
};
use crate::constants::{BGP_RIS_REFRESH_MINUTES, KRILL_ENV_TEST_ANN};
@@ -66,13 +67,19 @@ impl BgpAnalyser {
let seen = self.seen.read().unwrap();
let mut entries = vec![];
let roas: Vec<RoaDefinition> = roas
.iter()
.filter(|roa| scope.contains_roa_address(&roa.as_roa_ip_address()))
.cloned()
.collect();
if seen.last_updated().is_none() {
// nothing to analyse, just push all ROAs as 'no announcement info'
for roa in roas {
entries.push(BgpAnalysisEntry::roa_no_announcement_info(*roa));
entries.push(BgpAnalysisEntry::roa_no_announcement_info(roa));
}
} else {
let roa_tree = make_roa_tree(roas);
let roa_tree = make_roa_tree(roas.as_ref());
let (v4_scope, v6_scope) = IpRange::for_resource_set(&scope);
@@ -97,7 +104,7 @@ impl BgpAnalyser {
let covered = validated_tree.matching_or_more_specific(&roa.prefix());
if covered.is_empty() {
entries.push(BgpAnalysisEntry::roa_unseen(*roa))
entries.push(BgpAnalysisEntry::roa_unseen(roa))
} else {
let authorizes: Vec<Announcement> = covered
.iter()
@@ -140,16 +147,16 @@ impl BgpAnalyser {
.collect();
if authorizes.is_empty() && disallows.is_empty() {
entries.push(BgpAnalysisEntry::roa_unseen(*roa))
entries.push(BgpAnalysisEntry::roa_unseen(roa))
} else if !authorizes_excess.is_empty() {
entries.push(BgpAnalysisEntry::roa_too_permissive(
*roa,
roa,
authorizes,
disallows,
authorizes_excess,
))
} else {
entries.push(BgpAnalysisEntry::roa_seen(*roa, authorizes, disallows))
entries.push(BgpAnalysisEntry::roa_seen(roa, authorizes, disallows))
}
}
}
@@ -185,6 +192,33 @@ impl BgpAnalyser {
BgpAnalysisReport::new(entries)
}
pub fn suggest(&self, roas: &[RoaDefinition], scope: &ResourceSet) -> BgpAnalysisSuggestion {
let mut suggestion = BgpAnalysisSuggestion::default();
// perform analysis
for entry in self.analyse(roas, scope).into_entries() {
match entry.state() {
BgpAnalysisState::RoaUnseen => suggestion.add_stale(entry.into_definition()),
BgpAnalysisState::RoaTooPermissive => {
let replace_with = entry
.authorizes()
.iter()
.map(|auth| RoaDefinition::from(*auth))
.collect();
suggestion.add_too_permissive(entry.into_definition(), replace_with);
}
BgpAnalysisState::AnnouncementNotFound => suggestion.add_not_found(entry.into_definition()),
BgpAnalysisState::AnnouncementInvalidAsn => suggestion.add_invalid_asn(entry.into_definition()),
BgpAnalysisState::AnnouncementInvalidLength => suggestion.add_invalid_length(entry.into_definition()),
BgpAnalysisState::RoaSeen => suggestion.add_keep(entry.into_definition()),
BgpAnalysisState::RoaNoAnnouncementInfo => suggestion.add_keep(entry.into_definition()),
_ => {}
}
}
suggestion
}
fn test_announcements() -> Vec<Announcement> {
use crate::test::announcement;
@@ -306,4 +340,48 @@ mod tests {
assert_eq!(roas_no_info.as_slice(), &[&roa1, &roa2, &roa3]);
}
#[test]
fn make_bgp_analysis_suggestion() {
let roa_too_permissive = definition("10.0.0.0/22-23 => 64496");
let roa_disallowing = definition("10.0.4.0/24 => 0");
let roa_unseen_completely = definition("10.0.3.0/24 => 64497");
let roa_authorizing_single = definition("192.168.1.0/24 => 64497");
let analyser = BgpAnalyser::with_test_announcements();
let scope = ResourceSet::from_strs("", "10.0.0.0/22", "").unwrap();
let suggestion_resource_subset = analyser.suggest(
&[
roa_too_permissive,
roa_disallowing,
roa_unseen_completely,
roa_authorizing_single,
],
&scope,
);
let expected: BgpAnalysisSuggestion = serde_json::from_str(include_str!(
"../../../test-resources/bgp/expected_suggestion_some_roas.json"
))
.unwrap();
assert_eq!(suggestion_resource_subset, expected);
let scope = ResourceSet::from_strs("", "10.0.0.0/8,192.168.0.0/16", "").unwrap();
let suggestion_all_roas_in_scope = analyser.suggest(
&[
roa_too_permissive,
roa_disallowing,
roa_unseen_completely,
roa_authorizing_single,
],
&scope,
);
let expected: BgpAnalysisSuggestion = serde_json::from_str(include_str!(
"../../../test-resources/bgp/expected_suggestion_all_roas.json"
))
.unwrap();
assert_eq!(suggestion_all_roas_in_scope, expected);
}
}
+168 -2
View File
@@ -1,10 +1,168 @@
use std::cmp::Ordering;
use std::collections::HashMap;
use std::collections::{HashMap, HashSet};
use std::fmt;
use crate::commons::api::{BgpStats, RoaDefinition};
use crate::commons::api::{BgpStats, RoaDefinition, RoaDefinitionUpdates};
use crate::commons::bgp::Announcement;
//------------ BgpAnalysisSuggestion ---------------------------------------
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct BgpAnalysisSuggestion {
stale: Vec<RoaDefinition>,
not_found: Vec<RoaDefinition>,
invalid_asn: Vec<RoaDefinition>,
invalid_length: Vec<RoaDefinition>,
too_permissive: Vec<ReplacementRoaSuggestion>,
keep: Vec<RoaDefinition>,
}
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct ReplacementRoaSuggestion {
current: RoaDefinition,
new: Vec<RoaDefinition>,
}
impl From<BgpAnalysisSuggestion> for RoaDefinitionUpdates {
fn from(suggestion: BgpAnalysisSuggestion) -> Self {
let (stale, not_found, invalid_asn, invalid_length, too_permissive) = (
suggestion.stale,
suggestion.not_found,
suggestion.invalid_asn,
suggestion.invalid_length,
suggestion.too_permissive,
);
let mut added = HashSet::new();
let mut removed = HashSet::new();
for auth in not_found
.into_iter()
.chain(invalid_asn.into_iter())
.chain(invalid_length.into_iter())
{
added.insert(auth);
}
for auth in stale.into_iter() {
removed.insert(auth);
}
for suggestion in too_permissive.into_iter() {
removed.insert(suggestion.current);
for auth in suggestion.new.into_iter() {
added.insert(auth);
}
}
RoaDefinitionUpdates::new(added, removed)
}
}
impl Default for BgpAnalysisSuggestion {
fn default() -> Self {
BgpAnalysisSuggestion {
stale: vec![],
not_found: vec![],
invalid_asn: vec![],
invalid_length: vec![],
too_permissive: vec![],
keep: vec![],
}
}
}
impl BgpAnalysisSuggestion {
pub fn add_stale(&mut self, authorization: RoaDefinition) {
self.stale.push(authorization)
}
pub fn add_too_permissive(&mut self, current: RoaDefinition, new: Vec<RoaDefinition>) {
let replacement = ReplacementRoaSuggestion { current, new };
self.too_permissive.push(replacement)
}
pub fn add_not_found(&mut self, authorization: RoaDefinition) {
self.not_found.push(authorization)
}
pub fn add_invalid_asn(&mut self, authorization: RoaDefinition) {
self.invalid_asn.push(authorization)
}
pub fn add_invalid_length(&mut self, authorization: RoaDefinition) {
self.invalid_length.push(authorization)
}
pub fn add_keep(&mut self, authorization: RoaDefinition) {
self.keep.push(authorization)
}
}
#[allow(clippy::cognitive_complexity)]
impl fmt::Display for BgpAnalysisSuggestion {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if !self.stale.is_empty() {
writeln!(f, "Remove the following stale entries:")?;
for auth in &self.stale {
writeln!(f, " {}", auth)?;
}
writeln!(f)?;
}
if !self.too_permissive.is_empty() {
writeln!(f, "Replace the following too permissive entries:")?;
for entry in &self.too_permissive {
writeln!(f, " Remove: {}", entry.current)?;
for replace in &entry.new {
writeln!(f, " Add: {}", replace)?;
}
writeln!(f)?;
}
}
if !self.not_found.is_empty() {
writeln!(f, "Authorize these announcements which are currently not covered:")?;
for auth in &self.not_found {
writeln!(f, " {}", auth)?;
}
writeln!(f)?;
}
if !self.invalid_length.is_empty() {
writeln!(
f,
"Authorize these announcements which are currently invalid because they are too specific:"
)?;
for auth in &self.invalid_length {
writeln!(f, " {}", auth)?;
}
writeln!(f)?;
}
if !self.invalid_asn.is_empty() {
writeln!(
f,
"Authorize these announcements which are currently invalid because they are not allowed for these ASNs:"
)?;
for auth in &self.invalid_asn {
writeln!(f, " {}", auth)?;
}
writeln!(f)?;
}
if !self.keep.is_empty() {
writeln!(f, "Keep the following entries:")?;
for auth in &self.keep {
writeln!(f, " {}", auth)?;
}
writeln!(f)?;
}
Ok(())
}
}
//------------ BgpAnalysisReport -------------------------------------------
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
@@ -20,6 +178,10 @@ impl BgpAnalysisReport {
&self.0
}
pub fn into_entries(self) -> Vec<BgpAnalysisEntry> {
self.0
}
pub fn matching_defs(&self, state: BgpAnalysisState) -> Vec<&RoaDefinition> {
self.matching_entries(state)
.into_iter()
@@ -208,6 +370,10 @@ impl BgpAnalysisEntry {
&self.definition
}
pub fn into_definition(self) -> RoaDefinition {
self.definition
}
pub fn state(&self) -> BgpAnalysisState {
self.state
}
+14 -4
View File
@@ -589,10 +589,7 @@ async fn api_ca_routes(req: Request, path: &mut RequestPath, ca: Handle) -> Rout
Method::POST => ca_routes_update(req, ca).await,
_ => render_unknown_method(),
},
Some("analysis") => match *req.method() {
Method::GET => ca_routes_analysis(req, path, ca).await,
_ => render_unknown_method(),
},
Some("analysis") => ca_routes_analysis(req, path, ca).await,
_ => render_unknown_method(),
}
}
@@ -1078,6 +1075,19 @@ async fn ca_routes_show(req: Request, handle: Handle) -> RoutingResult {
async fn ca_routes_analysis(req: Request, path: &mut RequestPath, handle: Handle) -> RoutingResult {
match path.next() {
Some("full") => render_json_res(req.state().read().await.ca_routes_bgp_analysis(&handle)),
Some("suggest") => match *req.method() {
Method::GET => render_json_res(req.state().read().await.ca_routes_bgp_suggest(&handle, None)),
Method::POST => {
let server = req.state().clone();
match req.json().await {
Err(e) => render_error(e),
Ok(resources) => {
render_json_res(server.read().await.ca_routes_bgp_suggest(&handle, Some(resources)))
}
}
}
_ => render_unknown_method(),
},
_ => render_unknown_method(),
}
}
+19 -3
View File
@@ -14,10 +14,10 @@ use crate::commons::api::{
AddChildRequest, AllCertAuthIssues, CaCommandDetails, CaRepoDetails, CertAuthInfo, CertAuthInit, CertAuthIssues,
CertAuthList, CertAuthStats, ChildCaInfo, ChildHandle, CommandHistory, CommandHistoryCriteria, CurrentRepoState,
Handle, ListReply, ParentCaContact, ParentCaReq, ParentHandle, PublishDelta, PublisherDetails, PublisherHandle,
RepoInfo, RepositoryContact, RepositoryUpdate, RoaDefinition, RoaDefinitionUpdates, ServerInfo, TaCertDetails,
UpdateChildRequest,
RepoInfo, RepositoryContact, RepositoryUpdate, ResourceSet, RoaDefinition, RoaDefinitionUpdates, ServerInfo,
TaCertDetails, UpdateChildRequest,
};
use crate::commons::bgp::{BgpAnalyser, BgpAnalysisReport};
use crate::commons::bgp::{BgpAnalyser, BgpAnalysisReport, BgpAnalysisSuggestion};
use crate::commons::error::Error;
use crate::commons::eventsourcing::CommandKey;
use crate::commons::remote::rfc8183;
@@ -621,6 +621,22 @@ impl KrillServer {
let resources = ca.all_resources();
Ok(self.bgp_analyser.analyse(definitions.as_slice(), &resources))
}
pub fn ca_routes_bgp_suggest(
&self,
handle: &Handle,
scope: Option<ResourceSet>,
) -> KrillResult<BgpAnalysisSuggestion> {
let ca = self.caserver.get_ca(handle)?;
let definitions = ca.roa_definitions();
let mut resources = ca.all_resources();
if let Some(scope) = scope {
resources = resources.intersection(&scope);
}
Ok(self.bgp_analyser.suggest(definitions.as_slice(), &resources))
}
}
/// # Handle publication requests
+12 -2
View File
@@ -25,11 +25,11 @@ use crate::commons::api::{
ResourceClassKeysInfo, ResourceClassName, ResourceSet, RoaDefinition, RoaDefinitionUpdates, TypedPrefix,
UpdateChildRequest,
};
use crate::commons::bgp::Announcement;
use crate::commons::bgp::{Announcement, BgpAnalysisSuggestion};
use crate::commons::remote::rfc8183;
use crate::commons::remote::rfc8183::ChildRequest;
use crate::commons::util::httpclient;
use crate::constants::KRILL_ENV_TEST_UNIT_DATA;
use crate::constants::{KRILL_ENV_TEST_ANN, KRILL_ENV_TEST_UNIT_DATA};
use crate::daemon::ca::{ta_handle, ResourceTaggedAttestation, RtaRequest, SignSupport};
use crate::daemon::http::server;
@@ -62,6 +62,7 @@ pub async fn start_krill() -> PathBuf {
let data_dir = sub_dir(&dir);
env::set_var(KRILL_ENV_TEST_UNIT_DATA, data_dir.to_string_lossy().to_string());
env::set_var(KRILL_ENV_TEST_ANN, "1");
tokio::spawn(server::start());
@@ -93,6 +94,7 @@ pub async fn init_child(handle: &Handle) {
krill_admin(Command::CertAuth(CaCommand::Init(CertAuthInit::new(handle.clone())))).await;
}
// We use embedded when not testing RFC 8181 - so that the CMS signing/verification overhead can be reduced.
pub async fn init_child_with_embedded_repo(handle: &Handle) {
krill_admin(Command::CertAuth(CaCommand::Init(CertAuthInit::new(handle.clone())))).await;
krill_admin(Command::CertAuth(CaCommand::RepoUpdate(
@@ -125,6 +127,7 @@ pub async fn child_request(handle: &Handle) -> rfc8183::ChildRequest {
}
}
// We use embedded when not testing RFC 6492 - so that the CMS signing/verification overhead can be reduced.
pub async fn add_child_to_ta_embedded(handle: &Handle, resources: ResourceSet) -> ParentCaContact {
let auth = ChildAuthRequest::Embedded;
let req = AddChildRequest::new(handle.clone(), resources, auth);
@@ -236,6 +239,13 @@ pub async fn ca_route_authorizations_update_expect_error(handle: &Handle, update
.await;
}
pub async fn ca_route_authorizations_suggestions(handle: &Handle) -> BgpAnalysisSuggestion {
match krill_admin(Command::CertAuth(CaCommand::BgpAnalysisSuggest(handle.clone(), None))).await {
ApiResponse::BgpAnalysisSuggestions(suggestion) => suggestion,
_ => panic!("Expected ROA suggestion"),
}
}
pub async fn ca_details(handle: &Handle) -> CertAuthInfo {
match krill_admin(Command::CertAuth(CaCommand::Show(handle.clone()))).await {
ApiResponse::CertAuthInfo(inf) => inf,
@@ -0,0 +1,63 @@
{
"stale": [
{
"asn": 64497,
"prefix": "10.0.3.0/24"
},
{
"asn": 0,
"prefix": "10.0.4.0/24"
}
],
"not_found": [
{
"asn": 64497,
"prefix": "10.0.0.0/21"
},
{
"asn": 64496,
"prefix": "192.168.0.0/24"
},
{
"asn": 64497,
"prefix": "192.168.0.0/24"
}
],
"invalid_asn": [
{
"asn": 64497,
"prefix": "10.0.0.0/22"
}
],
"invalid_length": [
{
"asn": 64496,
"prefix": "10.0.0.0/24"
}
],
"too_permissive": [
{
"current": {
"asn": 64496,
"prefix": "10.0.0.0/22",
"max_length": 23
},
"new": [
{
"asn": 64496,
"prefix": "10.0.0.0/22"
},
{
"asn": 64496,
"prefix": "10.0.2.0/23"
}
]
}
],
"keep": [
{
"asn": 64497,
"prefix": "192.168.1.0/24"
}
]
}
@@ -0,0 +1,41 @@
{
"stale": [
{
"asn": 64497,
"prefix": "10.0.3.0/24"
}
],
"not_found": [],
"invalid_asn": [
{
"asn": 64497,
"prefix": "10.0.0.0/22"
}
],
"invalid_length": [
{
"asn": 64496,
"prefix": "10.0.0.0/24"
}
],
"too_permissive": [
{
"current": {
"asn": 64496,
"prefix": "10.0.0.0/22",
"max_length": 23
},
"new": [
{
"asn": 64496,
"prefix": "10.0.0.0/22"
},
{
"asn": 64496,
"prefix": "10.0.2.0/23"
}
]
}
],
"keep": []
}
+12
View File
@@ -129,5 +129,17 @@ async fn ca_roas() {
ca_route_authorizations_update(&child, updates).await;
assert!(will_publish_objects(&child, &[crl_file, mft_file, route1_file],).await);
// Get ROA suggestions, expect that we can submit the suggestions as an update, and then we
// should see no more suggestions.
let suggestion = ca_route_authorizations_suggestions(&child).await;
let updates: RoaDefinitionUpdates = suggestion.into();
assert!(!updates.is_empty());
ca_route_authorizations_update(&child, updates).await;
let remaining_suggestion = ca_route_authorizations_suggestions(&child).await;
let updates: RoaDefinitionUpdates = remaining_suggestion.into();
assert!(updates.is_empty());
let _ = fs::remove_dir_all(dir);
}