Skip to content

Commit

Permalink
Introduce a limit for ASPA provider ASNs. (#989)
Browse files Browse the repository at this point in the history
This PR introduces a new configuration option aspa-provider-limit that
limits the number of provider ASNs allowed in an ASPA object. If the number
is exceeded, all ASPA assertions for the customer ASN are removed from the
dataset to avoid false rejections. The default value is 10,000.
  • Loading branch information
partim authored Jan 7, 2025
1 parent 2020d8f commit 19208cc
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 4 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ pin-project-lite = "0.2.4"
rand = "0.8.1"
reqwest = { version = "0.12.4", default-features = false, features = ["blocking", "rustls-tls" ] }
ring = "0.17"
rpki = { version = "0.18.3", features = [ "repository", "rrdp", "rtr", "serde", "slurm" ], git = "https://github.com/NLnetLabs/rpki-rs.git" }
rpki = { version = "0.18.3", features = [ "repository", "rrdp", "rtr", "serde", "slurm" ], git = "https://github.com/NLnetLabs/rpki-rs.git", branch = "aspa-provider-count" }
rustls-pemfile = "2.1.2"
serde = { version = "1.0.95", features = [ "derive" ] }
serde_json = "1.0.57"
Expand Down
14 changes: 14 additions & 0 deletions doc/manual/source/manual-page.rst
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,13 @@ The available options are:
If this option is present, BGPsec router keys will be processed
during validation and included in the produced data set.

.. option:: --aspa-provider-limit

Limits the number of provider ASNs allowed in an ASPA object. If more
providers are given, all ASPA assertions for the customer ASN are
dropped to avoid false rejections. The default value if not changed
via configuration or this option is 10,000 provider ASNs.

.. option:: --dirty

If this option is present, unused files and directories will not be
Expand Down Expand Up @@ -1190,6 +1197,13 @@ All values can be overridden via the command line options.
included in the published dataset. If false or missing, no router
keys will be included.

aspa_provider_limit
An integer value specifying the maximum number of provider ASNs
allowed in an ASPA object. If more providers are given, all ASPA
assertions for the customer ASN are dropped to avoid false
rejections. If the option is missing, a default of 10,000
provider ASNs is used.

dirty
A boolean value which, if true, specifies that unused files and
directories should not be deleted from the repository directory
Expand Down
25 changes: 25 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ const DEFAULT_MAX_CA_DEPTH: usize = 32;
#[cfg(unix)]
const DEFAULT_SYSLOG_FACILITY: Facility = Facility::LOG_DAEMON;

/// The default limit of provider ASNs in ASPA objects.
const DEFAULT_ASPA_PROVIDER_LIMIT: usize = 10_000;


//------------ Config --------------------------------------------------------

Expand Down Expand Up @@ -269,6 +272,12 @@ pub struct Config {
/// Whether to process ASPA objects.
pub enable_aspa: bool,

/// The maximum number of provider ASNs accepted in ASPA objects.
///
/// If this number is exceeded, all ASPAs for the customer ASN of the
/// offending object are dropped.
pub aspa_provider_limit: usize,

/// Whether to not cleanup the repository directory after a validation run.
///
/// If this is `false` and update has not been disabled otherwise, all
Expand Down Expand Up @@ -622,6 +631,11 @@ impl Config {
self.enable_aspa = true
}

// aspa_provider_limit
if let Some(value) = args.aspa_provider_limit {
self.aspa_provider_limit = value;
}

// dirty_repository
if args.dirty_repository {
self.dirty_repository = true
Expand Down Expand Up @@ -979,6 +993,11 @@ impl Config {
#[cfg(not(feature = "aspa"))]
enable_aspa: false,

aspa_provider_limit: {
file.take_usize("aspa-provider-limit")?
.unwrap_or(DEFAULT_ASPA_PROVIDER_LIMIT)
},

dirty_repository: file.take_bool("dirty")?.unwrap_or(false),
validation_threads: {
file.take_small_usize(
Expand Down Expand Up @@ -1188,6 +1207,7 @@ impl Config {
max_ca_depth: DEFAULT_MAX_CA_DEPTH,
enable_bgpsec: false,
enable_aspa: false,
aspa_provider_limit: DEFAULT_ASPA_PROVIDER_LIMIT,
dirty_repository: DEFAULT_DIRTY_REPOSITORY,
validation_threads: Config::default_validation_threads(),
refresh: Duration::from_secs(DEFAULT_REFRESH),
Expand Down Expand Up @@ -1415,6 +1435,7 @@ impl Config {
insert(&mut res, "enable-bgpsec", self.enable_bgpsec);
#[cfg(feature = "aspa")]
insert(&mut res, "enable-aspa", self.enable_aspa);
insert_int(&mut res, "aspa-provider-limit", self.aspa_provider_limit);
insert(&mut res, "dirty", self.dirty_repository);
insert_int(&mut res, "validation-threads", self.validation_threads);
insert_int(&mut res, "refresh", self.refresh.as_secs());
Expand Down Expand Up @@ -1875,6 +1896,10 @@ struct GlobalArgs {
#[arg(long)]
enable_aspa: bool,

/// Maximum number of provider ASNs in ASPA objects
#[arg(long, value_name = "COUNT")]
aspa_provider_limit: Option<usize>,

/// Do not clean up repository directory after validation
#[arg(long)]
dirty_repository: bool,
Expand Down
30 changes: 28 additions & 2 deletions src/payload/validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
use std::cmp;
use std::collections::hash_map;
use std::collections::HashMap;
use std::collections::{HashMap, HashSet};
use std::sync::Arc;
use crossbeam_queue::SegQueue;
use log::{info, warn};
Expand Down Expand Up @@ -75,6 +75,9 @@ pub struct ValidationReport {

/// How are we dealing with unsafe VRPs?
unsafe_vrps: FilterPolicy,

/// Maximum number of provider ASNs on ASPA objects.
aspa_provider_limit: usize,
}

impl ValidationReport {
Expand All @@ -89,6 +92,7 @@ impl ValidationReport {
limit_v4_len: config.limit_v4_len,
limit_v6_len: config.limit_v6_len,
unsafe_vrps: config.unsafe_vrps,
aspa_provider_limit: config.aspa_provider_limit,
}
}

Expand Down Expand Up @@ -271,14 +275,26 @@ impl ProcessPubPoint for PubPointProcessor<'_> {

fn process_aspa(
&mut self,
_uri: &uri::Rsync,
uri: &uri::Rsync,
cert: ResourceCert,
aspa: AsProviderAttestation
) -> Result<(), Failed> {
if !self.report.enable_aspa {
return Ok(())
}
self.pub_point.update_refresh(cert.validity().not_after());
if aspa.provider_as_set().len() > self.report.aspa_provider_limit {
warn!(
"{}: {} provider ASNs is over the limit of {}. \
Skipping ASPA for {}.",
uri,
aspa.provider_as_set().len(),
self.report.aspa_provider_limit,
aspa.customer_as()
);
self.report.rejected.aspa_customers.push(aspa.customer_as());
return Ok(())
}
self.pub_point.add_aspa(
aspa,
Arc::new(PublishInfo::signed_object(
Expand Down Expand Up @@ -513,6 +529,7 @@ pub struct PubAspa {
pub struct RejectedResources {
v4: IpBlocks,
v6: IpBlocks,
aspa_customers: HashSet<Asn>,
}

impl RejectedResources {
Expand Down Expand Up @@ -543,6 +560,9 @@ struct RejectedResourcesBuilder {

/// The queue of rejected AS blocks.
asns: SegQueue<AsBlock>,

/// The queue of rejected ASPA customer ASNs.
aspa_customers: SegQueue<Asn>,
}

impl RejectedResourcesBuilder {
Expand Down Expand Up @@ -578,6 +598,7 @@ impl RejectedResourcesBuilder {
RejectedResources {
v4: v4.finalize(),
v6: v6.finalize(),
aspa_customers: self.aspa_customers.into_iter().collect(),
}
}
}
Expand Down Expand Up @@ -753,6 +774,11 @@ impl<'a> SnapshotBuilder<'a> {
fn process_aspa(&mut self, aspa: PubAspa, metrics: &mut AllVrpMetrics) {
metrics.update(|m| m.aspas.valid += 1);

if self.rejected.aspa_customers.contains(&aspa.customer) {
metrics.update(|m| m.aspas.marked_unsafe += 1);
return
}

// SLURM filtering goes here ...

match self.aspas.entry(aspa.customer) {
Expand Down

0 comments on commit 19208cc

Please sign in to comment.