20260510 增加strict策略模式并隔离多TA失败

This commit is contained in:
2026-05-11 11:32:17 +08:00
parent 265b6f65d0
commit 51e483d924
12 changed files with 1194 additions and 96 deletions

View File

@@ -57,10 +57,42 @@ pub fn discover_root_ca_instance_from_tal_url(
discover_root_ca_instance_from_tal(http_fetcher, tal, Some(tal_url.to_string()))
}
pub fn discover_root_ca_instance_from_tal_url_with_strict_name(
http_fetcher: &dyn Fetcher,
tal_url: &str,
) -> Result<DiscoveredRootCaInstance, FromTalError> {
let tal_bytes = http_fetcher
.fetch(tal_url)
.map_err(FromTalError::TalFetch)?;
let tal = Tal::decode_bytes(&tal_bytes)?;
discover_root_ca_instance_from_tal_with_strict_name(
http_fetcher,
tal,
Some(tal_url.to_string()),
)
}
pub fn discover_root_ca_instance_from_tal(
http_fetcher: &dyn Fetcher,
tal: Tal,
tal_url: Option<String>,
) -> Result<DiscoveredRootCaInstance, FromTalError> {
discover_root_ca_instance_from_tal_impl(http_fetcher, tal, tal_url, false)
}
pub fn discover_root_ca_instance_from_tal_with_strict_name(
http_fetcher: &dyn Fetcher,
tal: Tal,
tal_url: Option<String>,
) -> Result<DiscoveredRootCaInstance, FromTalError> {
discover_root_ca_instance_from_tal_impl(http_fetcher, tal, tal_url, true)
}
fn discover_root_ca_instance_from_tal_impl(
http_fetcher: &dyn Fetcher,
tal: Tal,
tal_url: Option<String>,
strict_name: bool,
) -> Result<DiscoveredRootCaInstance, FromTalError> {
if tal.ta_uris.is_empty() {
return Err(FromTalError::NoTaUris);
@@ -76,13 +108,14 @@ pub fn discover_root_ca_instance_from_tal(
}
};
let trust_anchor = match TrustAnchor::bind_der(tal.clone(), &ta_der, Some(ta_uri)) {
Ok(ta) => ta,
Err(e) => {
last_err = Some(format!("bind {ta_uri} failed: {e}"));
continue;
}
};
let trust_anchor =
match bind_trust_anchor_der(tal.clone(), &ta_der, Some(ta_uri), strict_name) {
Ok(ta) => ta,
Err(e) => {
last_err = Some(format!("bind {ta_uri} failed: {e}"));
continue;
}
};
let ca_instance =
match ca_instance_uris_from_ca_certificate(&trust_anchor.ta_certificate.rc_ca) {
@@ -110,6 +143,37 @@ pub fn discover_root_ca_instance_from_tal_with_fetchers(
rsync_fetcher: &dyn RsyncFetcher,
tal: Tal,
tal_url: Option<String>,
) -> Result<DiscoveredRootCaInstance, FromTalError> {
discover_root_ca_instance_from_tal_with_fetchers_impl(
http_fetcher,
rsync_fetcher,
tal,
tal_url,
false,
)
}
pub fn discover_root_ca_instance_from_tal_with_fetchers_strict_name(
http_fetcher: &dyn Fetcher,
rsync_fetcher: &dyn RsyncFetcher,
tal: Tal,
tal_url: Option<String>,
) -> Result<DiscoveredRootCaInstance, FromTalError> {
discover_root_ca_instance_from_tal_with_fetchers_impl(
http_fetcher,
rsync_fetcher,
tal,
tal_url,
true,
)
}
fn discover_root_ca_instance_from_tal_with_fetchers_impl(
http_fetcher: &dyn Fetcher,
rsync_fetcher: &dyn RsyncFetcher,
tal: Tal,
tal_url: Option<String>,
strict_name: bool,
) -> Result<DiscoveredRootCaInstance, FromTalError> {
if tal.ta_uris.is_empty() {
return Err(FromTalError::NoTaUris);
@@ -127,13 +191,14 @@ pub fn discover_root_ca_instance_from_tal_with_fetchers(
}
};
let trust_anchor = match TrustAnchor::bind_der(tal.clone(), &ta_der, Some(ta_uri)) {
Ok(ta) => ta,
Err(e) => {
last_err = Some(format!("bind {ta_uri} failed: {e}"));
continue;
}
};
let trust_anchor =
match bind_trust_anchor_der(tal.clone(), &ta_der, Some(ta_uri), strict_name) {
Ok(ta) => ta,
Err(e) => {
last_err = Some(format!("bind {ta_uri} failed: {e}"));
continue;
}
};
let ca_instance =
match ca_instance_uris_from_ca_certificate(&trust_anchor.ta_certificate.rc_ca) {
@@ -168,6 +233,19 @@ fn fetch_ta_der(
}
}
fn bind_trust_anchor_der(
tal: Tal,
ta_der: &[u8],
resolved_uri: Option<&Url>,
strict_name: bool,
) -> Result<TrustAnchor, TrustAnchorError> {
if strict_name {
TrustAnchor::bind_der_with_strict_name(tal, ta_der, resolved_uri)
} else {
TrustAnchor::bind_der(tal, ta_der, resolved_uri)
}
}
fn fetch_ta_der_via_rsync(
rsync_fetcher: &dyn RsyncFetcher,
ta_rsync_uri: &str,
@@ -213,9 +291,26 @@ pub fn discover_root_ca_instance_from_tal_and_ta_der(
tal_bytes: &[u8],
ta_der: &[u8],
resolved_ta_uri: Option<&Url>,
) -> Result<DiscoveredRootCaInstance, FromTalError> {
discover_root_ca_instance_from_tal_and_ta_der_impl(tal_bytes, ta_der, resolved_ta_uri, false)
}
pub fn discover_root_ca_instance_from_tal_and_ta_der_with_strict_name(
tal_bytes: &[u8],
ta_der: &[u8],
resolved_ta_uri: Option<&Url>,
) -> Result<DiscoveredRootCaInstance, FromTalError> {
discover_root_ca_instance_from_tal_and_ta_der_impl(tal_bytes, ta_der, resolved_ta_uri, true)
}
fn discover_root_ca_instance_from_tal_and_ta_der_impl(
tal_bytes: &[u8],
ta_der: &[u8],
resolved_ta_uri: Option<&Url>,
strict_name: bool,
) -> Result<DiscoveredRootCaInstance, FromTalError> {
let tal = Tal::decode_bytes(tal_bytes)?;
let trust_anchor = TrustAnchor::bind_der(tal, ta_der, resolved_ta_uri)?;
let trust_anchor = bind_trust_anchor_der(tal, ta_der, resolved_ta_uri, strict_name)?;
let ca_instance = ca_instance_uris_from_ca_certificate(&trust_anchor.ta_certificate.rc_ca)?;
Ok(DiscoveredRootCaInstance {
tal_url: None,

View File

@@ -28,6 +28,17 @@ const RFC_CRLDP: &[RfcRef] = &[RfcRef("RFC 6487 §4.8.6")];
const RFC_CRLDP_AND_LOCKED_PACK: &[RfcRef] =
&[RfcRef("RFC 6487 §4.8.6"), RfcRef("RFC 9286 §4.2.1")];
fn decode_resource_certificate_with_policy(
der: &[u8],
policy: &Policy,
) -> Result<ResourceCertificate, crate::data_model::rc::ResourceCertificateDecodeError> {
if policy.strict.name {
ResourceCertificate::decode_der_with_strict_name(der)
} else {
ResourceCertificate::decode_der(der)
}
}
#[derive(Clone, Debug)]
pub(crate) struct VerifiedIssuerCrl {
crl: crate::data_model::crl::RpkixCrl,
@@ -178,11 +189,15 @@ pub fn process_publication_point_for_issuer_with_options<P: PublicationPointData
let mut audit: Vec<ObjectAuditEntry> = Vec::new();
// Enforce that `manifest_bytes` is actually a manifest object.
let _manifest = ManifestObject::decode_der(manifest_bytes)
.expect("publication point snapshot manifest decodes");
let _manifest = ManifestObject::decode_der_with_strict_options(
manifest_bytes,
policy.strict.cms_der,
policy.strict.name,
)
.expect("publication point snapshot manifest decodes");
// Decode issuer CA once; if it fails we cannot validate ROA/ASPA EE certificates.
let issuer_ca = match ResourceCertificate::decode_der(issuer_ca_der) {
let issuer_ca = match decode_resource_certificate_with_policy(issuer_ca_der, policy) {
Ok(v) => v,
Err(e) => {
stats.publication_point_dropped = true;
@@ -349,6 +364,8 @@ pub fn process_publication_point_for_issuer_with_options<P: PublicationPointData
validation_time,
timing,
collect_vcir_local_outputs,
policy.strict.cms_der,
policy.strict.name,
);
match result.outcome {
Ok(mut ok) => {
@@ -457,6 +474,8 @@ pub fn process_publication_point_for_issuer_with_options<P: PublicationPointData
validation_time,
timing,
collect_vcir_local_outputs,
policy.strict.cms_der,
policy.strict.name,
) {
Ok((att, local_output)) => {
stats.aspa_ok += 1;
@@ -739,6 +758,8 @@ pub(crate) struct OwnedRoaTask {
issuer_effective_as: Option<crate::data_model::rc::AsResourceSet>,
validation_time: time::OffsetDateTime,
collect_vcir_local_outputs: bool,
strict_cms_der: bool,
strict_name: bool,
pub(crate) submitted_at: Option<Instant>,
}
@@ -844,6 +865,8 @@ fn validate_owned_roa_task(worker_index: usize, task: OwnedRoaTask) -> RoaTaskRe
task.validation_time,
None,
task.collect_vcir_local_outputs,
task.strict_cms_der,
task.strict_name,
)
.map(|(vrps, local_outputs)| RoaTaskOk {
vrps,
@@ -881,6 +904,8 @@ pub(crate) struct ParallelObjectsStage {
issuer_effective_as: Option<crate::data_model::rc::AsResourceSet>,
validation_time: time::OffsetDateTime,
collect_vcir_local_outputs: bool,
strict_cms_der: bool,
strict_name: bool,
warnings: Vec<Warning>,
stats: ObjectsStats,
audit: Vec<ObjectAuditEntry>,
@@ -907,6 +932,8 @@ impl ParallelObjectsStage {
issuer_effective_as: self.issuer_effective_as.clone(),
validation_time: self.validation_time,
collect_vcir_local_outputs: self.collect_vcir_local_outputs,
strict_cms_der: self.strict_cms_der,
strict_name: self.strict_name,
submitted_at: None,
})
.collect()
@@ -928,6 +955,7 @@ impl ParallelObjectsStage {
pub(crate) fn prepare_publication_point_for_parallel_roa<P: PublicationPointData>(
publication_point_id: u64,
publication_point: &P,
policy: &Policy,
issuer_ca_der: &[u8],
issuer_ca_rsync_uri: Option<&str>,
issuer_effective_ip: Option<&crate::data_model::rc::IpResourceSet>,
@@ -950,10 +978,14 @@ pub(crate) fn prepare_publication_point_for_parallel_roa<P: PublicationPointData
.count();
let mut audit: Vec<ObjectAuditEntry> = Vec::new();
let _manifest = ManifestObject::decode_der(manifest_bytes)
.expect("publication point snapshot manifest decodes");
let _manifest = ManifestObject::decode_der_with_strict_options(
manifest_bytes,
policy.strict.cms_der,
policy.strict.name,
)
.expect("publication point snapshot manifest decodes");
let issuer_ca = match ResourceCertificate::decode_der(issuer_ca_der) {
let issuer_ca = match decode_resource_certificate_with_policy(issuer_ca_der, policy) {
Ok(v) => v,
Err(e) => {
stats.publication_point_dropped = true;
@@ -1109,6 +1141,8 @@ pub(crate) fn prepare_publication_point_for_parallel_roa<P: PublicationPointData
issuer_effective_as: issuer_effective_as.cloned(),
validation_time,
collect_vcir_local_outputs,
strict_cms_der: policy.strict.cms_der,
strict_name: policy.strict.name,
warnings,
stats,
audit,
@@ -1193,6 +1227,8 @@ pub(crate) fn reduce_parallel_roa_stage(
stage.validation_time,
timing,
stage.collect_vcir_local_outputs,
stage.strict_cms_der,
stage.strict_name,
) {
Ok((att, local_output)) => {
stats.aspa_ok += 1;
@@ -1254,6 +1290,7 @@ fn process_publication_point_for_issuer_parallel_roa_inner<P: PublicationPointDa
let stage = match prepare_publication_point_for_parallel_roa(
0,
publication_point,
_policy,
issuer_ca_der,
issuer_ca_rsync_uri,
issuer_effective_ip,
@@ -1411,6 +1448,8 @@ pub(crate) fn validate_roa_task_serial(
validation_time: time::OffsetDateTime,
timing: Option<&TimingHandle>,
collect_vcir_local_outputs: bool,
strict_cms_der: bool,
strict_name: bool,
) -> RoaTaskResult {
let sha256_hex = sha256_hex_from_32(&task.file.sha256);
let outcome = process_roa_with_issuer(
@@ -1427,6 +1466,8 @@ pub(crate) fn validate_roa_task_serial(
validation_time,
timing,
collect_vcir_local_outputs,
strict_cms_der,
strict_name,
)
.map(|(vrps, local_outputs)| RoaTaskOk {
vrps,
@@ -1459,11 +1500,17 @@ fn process_roa_with_issuer(
validation_time: time::OffsetDateTime,
timing: Option<&TimingHandle>,
collect_vcir_local_outputs: bool,
strict_cms_der: bool,
strict_name: bool,
) -> Result<(Vec<Vrp>, Vec<VcirLocalOutput>), ObjectValidateError> {
let _decode = timing
.as_ref()
.map(|t| t.span_phase("objects_roa_decode_and_validate_total"));
let roa = RoaObject::decode_der(file.bytes().map_err(ObjectValidateError::BytesLoad)?)?;
let roa = RoaObject::decode_der_with_strict_options(
file.bytes().map_err(ObjectValidateError::BytesLoad)?,
strict_cms_der,
strict_name,
)?;
drop(_decode);
let _ee_profile = timing
@@ -1575,11 +1622,17 @@ fn process_roa_with_issuer_parallel_cached(
validation_time: time::OffsetDateTime,
timing: Option<&TimingHandle>,
collect_vcir_local_outputs: bool,
strict_cms_der: bool,
strict_name: bool,
) -> Result<(Vec<Vrp>, Vec<VcirLocalOutput>), ObjectValidateError> {
let _decode = timing
.as_ref()
.map(|t| t.span_phase("objects_roa_decode_and_validate_total"));
let roa = RoaObject::decode_der(file.bytes().map_err(ObjectValidateError::BytesLoad)?)?;
let roa = RoaObject::decode_der_with_strict_options(
file.bytes().map_err(ObjectValidateError::BytesLoad)?,
strict_cms_der,
strict_name,
)?;
drop(_decode);
let _ee_profile = timing
@@ -1697,11 +1750,17 @@ fn process_aspa_with_issuer(
validation_time: time::OffsetDateTime,
timing: Option<&TimingHandle>,
collect_vcir_local_outputs: bool,
strict_cms_der: bool,
strict_name: bool,
) -> Result<(AspaAttestation, Option<VcirLocalOutput>), ObjectValidateError> {
let _decode = timing
.as_ref()
.map(|t| t.span_phase("objects_aspa_decode_and_validate_total"));
let aspa = AspaObject::decode_der(file.bytes().map_err(ObjectValidateError::BytesLoad)?)?;
let aspa = AspaObject::decode_der_with_strict_options(
file.bytes().map_err(ObjectValidateError::BytesLoad)?,
strict_cms_der,
strict_name,
)?;
drop(_decode);
let _ee_profile = timing

View File

@@ -24,7 +24,11 @@ use crate::replay::fetch_rsync::PayloadReplayRsyncFetcher;
use crate::sync::rrdp::Fetcher;
use crate::validation::from_tal::{
DiscoveredRootCaInstance, FromTalError, discover_root_ca_instance_from_tal_and_ta_der,
discover_root_ca_instance_from_tal_url, discover_root_ca_instance_from_tal_with_fetchers,
discover_root_ca_instance_from_tal_and_ta_der_with_strict_name,
discover_root_ca_instance_from_tal_url,
discover_root_ca_instance_from_tal_url_with_strict_name,
discover_root_ca_instance_from_tal_with_fetchers,
discover_root_ca_instance_from_tal_with_fetchers_strict_name,
};
use crate::validation::objects::ParallelRoaWorkerPool;
use crate::validation::tree::{
@@ -86,6 +90,7 @@ pub struct RunTreeFromTalOutput {
pub struct RunTreeFromTalAuditOutput {
pub discovery: DiscoveredRootCaInstance,
pub discoveries: Vec<DiscoveredRootCaInstance>,
pub successful_tal_inputs: Vec<TalInputSpec>,
pub tree: TreeRunOutput,
pub publication_points: Vec<PublicationPointAudit>,
pub downloads: Vec<crate::audit::AuditDownloadEvent>,
@@ -196,19 +201,48 @@ fn root_discovery_from_tal_input(
tal_input: &TalInputSpec,
http_fetcher: &dyn Fetcher,
rsync_fetcher: &dyn crate::fetch::rsync::RsyncFetcher,
strict_name: bool,
) -> Result<DiscoveredRootCaInstance, FromTalError> {
match &tal_input.source {
TalSource::Url(url) => discover_root_ca_instance_from_tal_url(http_fetcher, url),
TalSource::Url(url) => {
if strict_name {
discover_root_ca_instance_from_tal_url_with_strict_name(http_fetcher, url)
} else {
discover_root_ca_instance_from_tal_url(http_fetcher, url)
}
}
TalSource::DerBytes {
tal_bytes, ta_der, ..
} => discover_root_ca_instance_from_tal_and_ta_der(tal_bytes, ta_der, None),
} => {
if strict_name {
discover_root_ca_instance_from_tal_and_ta_der_with_strict_name(
tal_bytes, ta_der, None,
)
} else {
discover_root_ca_instance_from_tal_and_ta_der(tal_bytes, ta_der, None)
}
}
TalSource::FilePath(path) => {
let tal_bytes = std::fs::read(path).map_err(|e| {
FromTalError::TalFetch(format!("read TAL file failed: {}: {e}", path.display()))
})?;
let tal = crate::data_model::tal::Tal::decode_bytes(&tal_bytes)
.map_err(FromTalError::from)?;
discover_root_ca_instance_from_tal_with_fetchers(http_fetcher, rsync_fetcher, tal, None)
if strict_name {
discover_root_ca_instance_from_tal_with_fetchers_strict_name(
http_fetcher,
rsync_fetcher,
tal,
None,
)
} else {
discover_root_ca_instance_from_tal_with_fetchers(
http_fetcher,
rsync_fetcher,
tal,
None,
)
}
}
TalSource::FilePathWithTa { tal_path, ta_path } => {
let tal_bytes = std::fs::read(tal_path).map_err(|e| {
@@ -217,19 +251,76 @@ fn root_discovery_from_tal_input(
let ta_der = std::fs::read(ta_path).map_err(|e| {
FromTalError::TaFetch(format!("read TA file failed: {}: {e}", ta_path.display()))
})?;
discover_root_ca_instance_from_tal_and_ta_der(&tal_bytes, &ta_der, None)
if strict_name {
discover_root_ca_instance_from_tal_and_ta_der_with_strict_name(
&tal_bytes, &ta_der, None,
)
} else {
discover_root_ca_instance_from_tal_and_ta_der(&tal_bytes, &ta_der, None)
}
}
}
}
fn discover_root_ca_instance_from_tal_url_with_policy(
policy: &crate::policy::Policy,
http_fetcher: &dyn Fetcher,
tal_url: &str,
) -> Result<DiscoveredRootCaInstance, FromTalError> {
if policy.strict.name {
discover_root_ca_instance_from_tal_url_with_strict_name(http_fetcher, tal_url)
} else {
discover_root_ca_instance_from_tal_url(http_fetcher, tal_url)
}
}
fn discover_root_ca_instance_from_tal_and_ta_der_with_policy(
policy: &crate::policy::Policy,
tal_bytes: &[u8],
ta_der: &[u8],
resolved_ta_uri: Option<&Url>,
) -> Result<DiscoveredRootCaInstance, FromTalError> {
if policy.strict.name {
discover_root_ca_instance_from_tal_and_ta_der_with_strict_name(
tal_bytes,
ta_der,
resolved_ta_uri,
)
} else {
discover_root_ca_instance_from_tal_and_ta_der(tal_bytes, ta_der, resolved_ta_uri)
}
}
fn discover_multiple_roots_from_tal_inputs(
tal_inputs: &[TalInputSpec],
http_fetcher: &dyn Fetcher,
rsync_fetcher: &dyn crate::fetch::rsync::RsyncFetcher,
strict_name: bool,
) -> Result<Vec<TalRootDiscovery>, RunTreeFromTalError> {
let mut roots = Vec::with_capacity(tal_inputs.len());
for tal_input in tal_inputs {
let discovery = root_discovery_from_tal_input(tal_input, http_fetcher, rsync_fetcher)?;
let discovery = match root_discovery_from_tal_input(
tal_input,
http_fetcher,
rsync_fetcher,
strict_name,
) {
Ok(discovery) => discovery,
Err(error)
if should_isolate_multi_tal_strict_name_failure(
tal_inputs.len(),
strict_name,
&error,
) =>
{
eprintln!(
"warning: skipping TAL '{}' because strict name validation failed during trust anchor discovery: {error}",
tal_input.tal_id
);
continue;
}
Err(error) => return Err(error.into()),
};
let root_handle = root_handle_from_trust_anchor(
&discovery.trust_anchor,
tal_input.tal_id.clone(),
@@ -242,9 +333,25 @@ fn discover_multiple_roots_from_tal_inputs(
root_handle,
});
}
if roots.is_empty() {
return Err(RunTreeFromTalError::Replay(
"multi-TAL root discovery returned no usable roots after strict name filtering"
.to_string(),
));
}
Ok(roots)
}
fn should_isolate_multi_tal_strict_name_failure(
tal_input_count: usize,
strict_name: bool,
error: &FromTalError,
) -> bool {
strict_name
&& tal_input_count > 1
&& error.to_string().contains("Name strict validation failed")
}
#[derive(Debug, thiserror::Error)]
pub enum RunTreeFromTalError {
#[error("{0}")]
@@ -288,7 +395,8 @@ pub fn run_tree_from_tal_url_serial(
validation_time: time::OffsetDateTime,
config: &TreeRunConfig,
) -> Result<RunTreeFromTalOutput, RunTreeFromTalError> {
let discovery = discover_root_ca_instance_from_tal_url(http_fetcher, tal_url)?;
let discovery =
discover_root_ca_instance_from_tal_url_with_policy(policy, http_fetcher, tal_url)?;
let runner = make_live_runner(
store,
@@ -325,7 +433,8 @@ pub fn run_tree_from_tal_url_serial_audit(
validation_time: time::OffsetDateTime,
config: &TreeRunConfig,
) -> Result<RunTreeFromTalAuditOutput, RunTreeFromTalError> {
let discovery = discover_root_ca_instance_from_tal_url(http_fetcher, tal_url)?;
let discovery =
discover_root_ca_instance_from_tal_url_with_policy(policy, http_fetcher, tal_url)?;
let download_log = DownloadLogHandle::new();
let runner = make_live_runner(
@@ -359,6 +468,7 @@ pub fn run_tree_from_tal_url_serial_audit(
Ok(RunTreeFromTalAuditOutput {
discovery: discovery.clone(),
discoveries: vec![discovery],
successful_tal_inputs: Vec::new(),
tree,
publication_points,
downloads,
@@ -379,7 +489,8 @@ pub fn run_tree_from_tal_url_serial_audit_with_timing(
timing: &TimingHandle,
) -> Result<RunTreeFromTalAuditOutput, RunTreeFromTalError> {
let _tal = timing.span_phase("tal_bootstrap");
let discovery = discover_root_ca_instance_from_tal_url(http_fetcher, tal_url)?;
let discovery =
discover_root_ca_instance_from_tal_url_with_policy(policy, http_fetcher, tal_url)?;
drop(_tal);
let download_log = DownloadLogHandle::new();
@@ -415,6 +526,7 @@ pub fn run_tree_from_tal_url_serial_audit_with_timing(
Ok(RunTreeFromTalAuditOutput {
discovery: discovery.clone(),
discoveries: vec![discovery],
successful_tal_inputs: Vec::new(),
tree,
publication_points,
downloads,
@@ -489,6 +601,7 @@ where
Ok(RunTreeFromTalAuditOutput {
discovery: discovery.clone(),
discoveries: vec![discovery],
successful_tal_inputs: Vec::new(),
tree,
publication_points,
downloads,
@@ -523,7 +636,12 @@ where
"multi-TAL run requires at least one TAL input".to_string(),
));
}
let roots = discover_multiple_roots_from_tal_inputs(&tal_inputs, http_fetcher, rsync_fetcher)?;
let roots = discover_multiple_roots_from_tal_inputs(
&tal_inputs,
http_fetcher,
rsync_fetcher,
policy.strict.name,
)?;
let primary = roots.first().cloned().ok_or_else(|| {
RunTreeFromTalError::Replay("multi-TAL root discovery returned no roots".to_string())
})?;
@@ -531,9 +649,13 @@ where
.iter()
.map(|item| item.discovery.clone())
.collect::<Vec<_>>();
let successful_tal_inputs = roots
.iter()
.map(|item| item.tal_input.clone())
.collect::<Vec<_>>();
let root_handles = roots
.into_iter()
.map(|item| item.root_handle)
.iter()
.map(|item| item.root_handle.clone())
.collect::<Vec<_>>();
let download_log = DownloadLogHandle::new();
@@ -545,7 +667,7 @@ where
parallel_config,
None,
Some(download_log.clone()),
tal_inputs,
successful_tal_inputs.clone(),
)?;
let current_repo_index_for_output = current_repo_index.clone();
let runner = make_live_runner(
@@ -583,6 +705,7 @@ where
Ok(RunTreeFromTalAuditOutput {
discovery: primary.discovery.clone(),
discoveries,
successful_tal_inputs,
tree,
publication_points,
downloads,
@@ -610,7 +733,8 @@ where
H: Fetcher + Clone + 'static,
R: crate::fetch::rsync::RsyncFetcher + Clone + 'static,
{
let discovery = discover_root_ca_instance_from_tal_url(http_fetcher, tal_url)?;
let discovery =
discover_root_ca_instance_from_tal_url_with_policy(policy, http_fetcher, tal_url)?;
run_single_root_parallel_audit_inner(
store,
policy,
@@ -643,8 +767,12 @@ where
H: Fetcher + Clone + 'static,
R: crate::fetch::rsync::RsyncFetcher + Clone + 'static,
{
let discovery =
discover_root_ca_instance_from_tal_and_ta_der(tal_bytes, ta_der, resolved_ta_uri)?;
let discovery = discover_root_ca_instance_from_tal_and_ta_der_with_policy(
policy,
tal_bytes,
ta_der,
resolved_ta_uri,
)?;
let derived_tal_id = derive_tal_id(&discovery);
let tal_inputs = vec![TalInputSpec {
tal_id: derived_tal_id.clone(),
@@ -718,7 +846,8 @@ where
H: Fetcher + Clone + 'static,
R: crate::fetch::rsync::RsyncFetcher + Clone + 'static,
{
let discovery = discover_root_ca_instance_from_tal_url(http_fetcher, tal_url)?;
let discovery =
discover_root_ca_instance_from_tal_url_with_policy(policy, http_fetcher, tal_url)?;
run_single_root_parallel_audit_inner(
store,
policy,
@@ -752,8 +881,12 @@ where
H: Fetcher + Clone + 'static,
R: crate::fetch::rsync::RsyncFetcher + Clone + 'static,
{
let discovery =
discover_root_ca_instance_from_tal_and_ta_der(tal_bytes, ta_der, resolved_ta_uri)?;
let discovery = discover_root_ca_instance_from_tal_and_ta_der_with_policy(
policy,
tal_bytes,
ta_der,
resolved_ta_uri,
)?;
let derived_tal_id = derive_tal_id(&discovery);
let tal_inputs = vec![TalInputSpec {
tal_id: derived_tal_id.clone(),
@@ -823,8 +956,12 @@ pub fn run_tree_from_tal_and_ta_der_serial(
validation_time: time::OffsetDateTime,
config: &TreeRunConfig,
) -> Result<RunTreeFromTalOutput, RunTreeFromTalError> {
let discovery =
discover_root_ca_instance_from_tal_and_ta_der(tal_bytes, ta_der, resolved_ta_uri)?;
let discovery = discover_root_ca_instance_from_tal_and_ta_der_with_policy(
policy,
tal_bytes,
ta_der,
resolved_ta_uri,
)?;
let runner = Rpkiv1PublicationPointRunner {
store,
@@ -916,6 +1053,7 @@ pub fn run_tree_from_tal_bytes_serial_audit(
Ok(RunTreeFromTalAuditOutput {
discovery: discovery.clone(),
discoveries: vec![discovery],
successful_tal_inputs: Vec::new(),
tree,
publication_points,
downloads,
@@ -987,6 +1125,7 @@ pub fn run_tree_from_tal_bytes_serial_audit_with_timing(
Ok(RunTreeFromTalAuditOutput {
discovery: discovery.clone(),
discoveries: vec![discovery],
successful_tal_inputs: Vec::new(),
tree,
publication_points,
downloads,
@@ -1007,8 +1146,12 @@ pub fn run_tree_from_tal_and_ta_der_serial_audit(
validation_time: time::OffsetDateTime,
config: &TreeRunConfig,
) -> Result<RunTreeFromTalAuditOutput, RunTreeFromTalError> {
let discovery =
discover_root_ca_instance_from_tal_and_ta_der(tal_bytes, ta_der, resolved_ta_uri)?;
let discovery = discover_root_ca_instance_from_tal_and_ta_der_with_policy(
policy,
tal_bytes,
ta_der,
resolved_ta_uri,
)?;
let download_log = DownloadLogHandle::new();
let runner = Rpkiv1PublicationPointRunner {
@@ -1049,6 +1192,7 @@ pub fn run_tree_from_tal_and_ta_der_serial_audit(
Ok(RunTreeFromTalAuditOutput {
discovery: discovery.clone(),
discoveries: vec![discovery],
successful_tal_inputs: Vec::new(),
tree,
publication_points,
downloads,
@@ -1071,8 +1215,12 @@ pub fn run_tree_from_tal_and_ta_der_serial_audit_with_timing(
timing: &TimingHandle,
) -> Result<RunTreeFromTalAuditOutput, RunTreeFromTalError> {
let _tal = timing.span_phase("tal_bootstrap");
let discovery =
discover_root_ca_instance_from_tal_and_ta_der(tal_bytes, ta_der, resolved_ta_uri)?;
let discovery = discover_root_ca_instance_from_tal_and_ta_der_with_policy(
policy,
tal_bytes,
ta_der,
resolved_ta_uri,
)?;
drop(_tal);
let download_log = DownloadLogHandle::new();
@@ -1115,6 +1263,7 @@ pub fn run_tree_from_tal_and_ta_der_serial_audit_with_timing(
Ok(RunTreeFromTalAuditOutput {
discovery: discovery.clone(),
discoveries: vec![discovery],
successful_tal_inputs: Vec::new(),
tree,
publication_points,
downloads,
@@ -1135,8 +1284,12 @@ pub fn run_tree_from_tal_and_ta_der_payload_replay_serial(
validation_time: time::OffsetDateTime,
config: &TreeRunConfig,
) -> Result<RunTreeFromTalOutput, RunTreeFromTalError> {
let discovery =
discover_root_ca_instance_from_tal_and_ta_der(tal_bytes, ta_der, resolved_ta_uri)?;
let discovery = discover_root_ca_instance_from_tal_and_ta_der_with_policy(
policy,
tal_bytes,
ta_der,
resolved_ta_uri,
)?;
let replay_index = Arc::new(
ReplayArchiveIndex::load_allow_missing_rsync_modules(
payload_archive_root,
@@ -1192,8 +1345,12 @@ pub fn run_tree_from_tal_and_ta_der_payload_replay_serial_audit(
validation_time: time::OffsetDateTime,
config: &TreeRunConfig,
) -> Result<RunTreeFromTalAuditOutput, RunTreeFromTalError> {
let discovery =
discover_root_ca_instance_from_tal_and_ta_der(tal_bytes, ta_der, resolved_ta_uri)?;
let discovery = discover_root_ca_instance_from_tal_and_ta_der_with_policy(
policy,
tal_bytes,
ta_der,
resolved_ta_uri,
)?;
let replay_index = Arc::new(
ReplayArchiveIndex::load_allow_missing_rsync_modules(
payload_archive_root,
@@ -1244,6 +1401,7 @@ pub fn run_tree_from_tal_and_ta_der_payload_replay_serial_audit(
Ok(RunTreeFromTalAuditOutput {
discovery: discovery.clone(),
discoveries: vec![discovery],
successful_tal_inputs: Vec::new(),
tree,
publication_points,
downloads,
@@ -1266,8 +1424,12 @@ pub fn run_tree_from_tal_and_ta_der_payload_replay_serial_audit_with_timing(
timing: &TimingHandle,
) -> Result<RunTreeFromTalAuditOutput, RunTreeFromTalError> {
let _tal = timing.span_phase("tal_bootstrap");
let discovery =
discover_root_ca_instance_from_tal_and_ta_der(tal_bytes, ta_der, resolved_ta_uri)?;
let discovery = discover_root_ca_instance_from_tal_and_ta_der_with_policy(
policy,
tal_bytes,
ta_der,
resolved_ta_uri,
)?;
drop(_tal);
let replay_index = Arc::new(
ReplayArchiveIndex::load_allow_missing_rsync_modules(
@@ -1320,6 +1482,7 @@ pub fn run_tree_from_tal_and_ta_der_payload_replay_serial_audit_with_timing(
Ok(RunTreeFromTalAuditOutput {
discovery: discovery.clone(),
discoveries: vec![discovery],
successful_tal_inputs: Vec::new(),
tree,
publication_points,
downloads,
@@ -1536,6 +1699,7 @@ fn run_payload_delta_replay_audit_inner(
Ok(RunTreeFromTalAuditOutput {
discovery: discovery.clone(),
discoveries: vec![discovery],
successful_tal_inputs: Vec::new(),
tree,
publication_points,
downloads,
@@ -1559,8 +1723,12 @@ pub fn run_tree_from_tal_and_ta_der_payload_delta_replay_serial_audit(
validation_time: time::OffsetDateTime,
config: &TreeRunConfig,
) -> Result<RunTreeFromTalAuditOutput, RunTreeFromTalError> {
let discovery =
discover_root_ca_instance_from_tal_and_ta_der(tal_bytes, ta_der, resolved_ta_uri)?;
let discovery = discover_root_ca_instance_from_tal_and_ta_der_with_policy(
policy,
tal_bytes,
ta_der,
resolved_ta_uri,
)?;
run_payload_delta_replay_audit_inner(
store,
policy,
@@ -1592,8 +1760,12 @@ pub fn run_tree_from_tal_and_ta_der_payload_delta_replay_serial_audit_with_timin
timing: &TimingHandle,
) -> Result<RunTreeFromTalAuditOutput, RunTreeFromTalError> {
let _tal = timing.span_phase("tal_bootstrap");
let discovery =
discover_root_ca_instance_from_tal_and_ta_der(tal_bytes, ta_der, resolved_ta_uri)?;
let discovery = discover_root_ca_instance_from_tal_and_ta_der_with_policy(
policy,
tal_bytes,
ta_der,
resolved_ta_uri,
)?;
drop(_tal);
run_payload_delta_replay_audit_inner(
store,
@@ -1681,6 +1853,7 @@ fn run_payload_delta_replay_step_audit_inner(
Ok(RunTreeFromTalAuditOutput {
discovery: discovery.clone(),
discoveries: vec![discovery],
successful_tal_inputs: Vec::new(),
tree,
publication_points,
downloads,
@@ -1702,8 +1875,12 @@ pub fn run_tree_from_tal_and_ta_der_payload_delta_replay_step_serial_audit(
validation_time: time::OffsetDateTime,
config: &TreeRunConfig,
) -> Result<RunTreeFromTalAuditOutput, RunTreeFromTalError> {
let discovery =
discover_root_ca_instance_from_tal_and_ta_der(tal_bytes, ta_der, resolved_ta_uri)?;
let discovery = discover_root_ca_instance_from_tal_and_ta_der_with_policy(
policy,
tal_bytes,
ta_der,
resolved_ta_uri,
)?;
run_payload_delta_replay_step_audit_inner(
store,
policy,
@@ -1790,6 +1967,7 @@ mod multi_tal_tests {
&tal_inputs,
&RejectingHttpFetcher,
&RejectingRsyncFetcher,
false,
)
.expect("discover roots");
@@ -1803,6 +1981,54 @@ mod multi_tal_tests {
roots[1].root_handle.manifest_rsync_uri
);
}
#[test]
fn discover_multiple_roots_isolates_strict_name_failure() {
let apnic_tal =
std::fs::read("tests/fixtures/tal/apnic-rfc7730-https.tal").expect("read apnic tal");
let apnic_ta = std::fs::read("tests/fixtures/ta/apnic-ta.cer").expect("read apnic ta");
let arin_tal = std::fs::read("tests/fixtures/tal/arin.tal").expect("read arin tal");
let arin_ta = std::fs::read("tests/fixtures/ta/arin-ta.cer").expect("read arin ta");
let tal_inputs = vec![
TalInputSpec::from_ta_der("https://example.test/apnic.tal", apnic_tal, apnic_ta),
TalInputSpec::from_ta_der("https://example.test/arin.tal", arin_tal, arin_ta),
];
let roots = discover_multiple_roots_from_tal_inputs(
&tal_inputs,
&RejectingHttpFetcher,
&RejectingRsyncFetcher,
true,
)
.expect("strict discovery should keep usable roots");
assert_eq!(roots.len(), 1);
assert_eq!(roots[0].tal_input.tal_id, "arin");
assert_eq!(roots[0].root_handle.tal_id, "arin");
}
#[test]
fn discover_single_root_keeps_strict_name_failure_fatal() {
let apnic_tal =
std::fs::read("tests/fixtures/tal/apnic-rfc7730-https.tal").expect("read apnic tal");
let apnic_ta = std::fs::read("tests/fixtures/ta/apnic-ta.cer").expect("read apnic ta");
let tal_inputs = vec![TalInputSpec::from_ta_der(
"https://example.test/apnic.tal",
apnic_tal,
apnic_ta,
)];
let error = discover_multiple_roots_from_tal_inputs(
&tal_inputs,
&RejectingHttpFetcher,
&RejectingRsyncFetcher,
true,
)
.expect_err("single-TAL strict failure should remain fatal");
assert!(error.to_string().contains("Name strict validation failed"));
}
}
#[cfg(test)]

View File

@@ -858,6 +858,7 @@ fn stage_ready_publication_point(
match prepare_publication_point_for_parallel_roa(
ready.node.id,
&fresh_stage.fresh_point,
runner.policy,
&ready.node.handle.ca_certificate_der,
ready.node.handle.ca_certificate_rsync_uri.as_deref(),
ready.node.handle.effective_ip_resources.as_ref(),