20260828 收口Feature151 TA constraints优化与验证
This commit is contained in:
parent
b59adc65a8
commit
74cbebbd33
@ -121,6 +121,7 @@ done
|
||||
|
||||
cp -a "$REPO_ROOT/tests/fixtures/tal" "$STAGE_DIR/fixtures/"
|
||||
cp -a "$REPO_ROOT/tests/fixtures/ta" "$STAGE_DIR/fixtures/"
|
||||
cp -a "$REPO_ROOT/tests/fixtures/ta_constraints" "$STAGE_DIR/fixtures/"
|
||||
cp -a "$REPO_ROOT/scripts/periodic" "$STAGE_DIR/scripts/"
|
||||
cp -a "$REPO_ROOT/scripts/cir" "$STAGE_DIR/scripts/"
|
||||
cp -a "$REPO_ROOT/scripts/inter_rp" "$STAGE_DIR/scripts/"
|
||||
|
||||
@ -100,6 +100,15 @@ ENABLE_CHILD_CERTIFICATE_VALIDATION_CACHE=0
|
||||
# 传给 rpki 子进程的额外参数。多个参数用空格分隔。
|
||||
# 示例:RPKI_EXTRA_ARGS="--enable-roa-validation-cache"
|
||||
# 实验性 transport 预热:RPKI_EXTRA_ARGS="--enable-transport-request-prefetch --enable-roa-validation-cache"
|
||||
# TA constraints 的 RIPE 单 TA soak 示例(policy 并非默认启用):
|
||||
# RIRS=ripe
|
||||
# RPKI_EXTRA_ARGS="--ta-constraints ripe-ncc=${PACKAGE_ROOT}/fixtures/ta_constraints/ripe-ncc-afrinic-deny.constraints"
|
||||
# all5 性能 A/B 的完整 AFRINIC IPv4 deny 示例(仅四个非 AFRINIC TA):
|
||||
# RIRS=afrinic,apnic,arin,lacnic,ripe
|
||||
# RPKI_EXTRA_ARGS="--ta-constraints apnic-rfc7730-https=${PACKAGE_ROOT}/fixtures/ta_constraints/afrinic-full-ipv4-deny.constraints --ta-constraints arin=${PACKAGE_ROOT}/fixtures/ta_constraints/afrinic-full-ipv4-deny.constraints --ta-constraints lacnic=${PACKAGE_ROOT}/fixtures/ta_constraints/afrinic-full-ipv4-deny.constraints --ta-constraints ripe-ncc=${PACKAGE_ROOT}/fixtures/ta_constraints/afrinic-full-ipv4-deny.constraints"
|
||||
# M7 current-only AFRINIC holdings(delegated status=allocated/assigned;不含 legacy/ERX):
|
||||
# RIRS=afrinic,apnic,arin,lacnic,ripe
|
||||
# RPKI_EXTRA_ARGS="--ta-constraints apnic-rfc7730-https=${PACKAGE_ROOT}/fixtures/ta_constraints/afrinic-current-ipv4-deny.constraints --ta-constraints arin=${PACKAGE_ROOT}/fixtures/ta_constraints/afrinic-current-ipv4-deny.constraints --ta-constraints lacnic=${PACKAGE_ROOT}/fixtures/ta_constraints/afrinic-current-ipv4-deny.constraints --ta-constraints ripe-ncc=${PACKAGE_ROOT}/fixtures/ta_constraints/afrinic-current-ipv4-deny.constraints"
|
||||
RPKI_EXTRA_ARGS=""
|
||||
|
||||
# 所有出站 HTTP 请求(RRDP / TAL / TA / 死 repo 探针)的 User-Agent。
|
||||
|
||||
178
scripts/ta_constraints/generate_afrinic_current_holdings.py
Executable file
178
scripts/ta_constraints/generate_afrinic_current_holdings.py
Executable file
@ -0,0 +1,178 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generate a current-only AFRINIC IPv4 holdings constraints fixture.
|
||||
|
||||
The AFRINIC delegated extended format contains one IPv4 allocation/assignment
|
||||
per line. This tool keeps only active registry holdings (``allocated`` and
|
||||
``assigned``), computes their exact union, and emits an allow-all policy with
|
||||
the union as IPv4 deny entries. ``reserved`` and ``available`` records are
|
||||
intentionally excluded.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import datetime as dt
|
||||
import hashlib
|
||||
import ipaddress
|
||||
import pathlib
|
||||
import sys
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
from collections import Counter
|
||||
from typing import Iterable
|
||||
|
||||
|
||||
DEFAULT_SOURCE_URL = (
|
||||
"https://ftp.afrinic.net/pub/stats/afrinic/"
|
||||
"delegated-afrinic-extended-latest"
|
||||
)
|
||||
ACTIVE_STATUSES = {"allocated", "assigned"}
|
||||
|
||||
|
||||
def merge_intervals(
|
||||
intervals: Iterable[tuple[int, int]],
|
||||
) -> list[tuple[int, int]]:
|
||||
merged: list[tuple[int, int]] = []
|
||||
for start, end in sorted(intervals):
|
||||
if merged and start <= merged[-1][1] + 1:
|
||||
merged[-1] = (merged[-1][0], max(merged[-1][1], end))
|
||||
else:
|
||||
merged.append((start, end))
|
||||
return merged
|
||||
|
||||
|
||||
def parse_source(raw: bytes) -> tuple[list[tuple[int, int]], Counter[str], Counter[str]]:
|
||||
status_counts: Counter[str] = Counter()
|
||||
ipv4_status_counts: Counter[str] = Counter()
|
||||
active: list[tuple[int, int]] = []
|
||||
for line_number, raw_line in enumerate(raw.decode("utf-8", "replace").splitlines(), 1):
|
||||
if not raw_line or raw_line.startswith("#"):
|
||||
continue
|
||||
fields = raw_line.split("|")
|
||||
if len(fields) < 7 or fields[2] != "ipv4":
|
||||
continue
|
||||
status = fields[6]
|
||||
status_counts[status] += 1
|
||||
ipv4_status_counts[status] += 1
|
||||
if status not in ACTIVE_STATUSES:
|
||||
continue
|
||||
try:
|
||||
start = int(ipaddress.IPv4Address(fields[3]))
|
||||
count = int(fields[4])
|
||||
except (ValueError, TypeError) as error:
|
||||
raise ValueError(f"line {line_number}: invalid IPv4 record: {raw_line}") from error
|
||||
if count <= 0 or start + count > 1 << 32:
|
||||
raise ValueError(f"line {line_number}: invalid IPv4 range: {raw_line}")
|
||||
active.append((start, start + count - 1))
|
||||
return active, status_counts, ipv4_status_counts
|
||||
|
||||
|
||||
def render(
|
||||
source_url: str,
|
||||
retrieved: str,
|
||||
source_sha256: str,
|
||||
source_ipv4_count: int,
|
||||
status_counts: Counter[str],
|
||||
active_source: list[tuple[int, int]],
|
||||
) -> tuple[str, dict[str, int]]:
|
||||
merged = merge_intervals(active_source)
|
||||
networks = [
|
||||
network
|
||||
for start, end in merged
|
||||
for network in ipaddress.summarize_address_range(
|
||||
ipaddress.IPv4Address(start), ipaddress.IPv4Address(end)
|
||||
)
|
||||
]
|
||||
networks.sort(key=lambda network: int(network.network_address))
|
||||
active_addresses = sum(end - start + 1 for start, end in merged)
|
||||
counts = {
|
||||
"source_ipv4_records": source_ipv4_count,
|
||||
"allocated": status_counts["allocated"],
|
||||
"assigned": status_counts["assigned"],
|
||||
"reserved": status_counts["reserved"],
|
||||
"available": status_counts["available"],
|
||||
"selected_source_records": len(active_source),
|
||||
"selected_addresses": active_addresses,
|
||||
"merged_intervals": len(merged),
|
||||
"cidr_rules": len(networks),
|
||||
}
|
||||
lines = [
|
||||
"# Feature #151 M7 current AFRINIC IPv4 holdings deny fixture.",
|
||||
"#",
|
||||
"# Source: AFRINIC delegated extended latest.",
|
||||
f"# URL: {source_url}",
|
||||
f"# Retrieved: {retrieved}",
|
||||
f"# Source SHA-256: {source_sha256}",
|
||||
"#",
|
||||
"# Selection: IPv4 records with status allocated or assigned only.",
|
||||
"# Excluded statuses: reserved and available (not current registry holdings).",
|
||||
(
|
||||
"# Source IPv4 records: {source_ipv4_records}; allocated={allocated}; "
|
||||
"assigned={assigned}; reserved={reserved}; available={available}."
|
||||
).format(**counts),
|
||||
"# Selected source records: {selected_source_records}; selected addresses: {selected_addresses}.".format(
|
||||
**counts
|
||||
),
|
||||
"# Overlap/adjacency union intervals: {merged_intervals}; exact CIDR deny rules: {cidr_rules}.".format(
|
||||
**counts
|
||||
),
|
||||
"# IPv6 and ASN remain allow-all to isolate the current AFRINIC IPv4 policy.",
|
||||
"allow 0.0.0.0/0",
|
||||
"allow ::/0",
|
||||
"allow 0 - 4294967295",
|
||||
"",
|
||||
]
|
||||
lines.extend(f"deny {network}" for network in networks)
|
||||
return "\n".join(lines) + "\n", counts
|
||||
|
||||
|
||||
def fetch_source(url: str, timeout: float) -> bytes:
|
||||
request = urllib.request.Request(
|
||||
url,
|
||||
headers={"User-Agent": "rpki-dev-feature151-afrinic-holdings/1.0"},
|
||||
)
|
||||
with urllib.request.urlopen(request, timeout=timeout) as response:
|
||||
return response.read()
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
source = parser.add_mutually_exclusive_group()
|
||||
source.add_argument("--url", default=DEFAULT_SOURCE_URL, help="AFRINIC source URL")
|
||||
source.add_argument("--input", type=pathlib.Path, help="local delegated file for replay")
|
||||
parser.add_argument("--output", type=pathlib.Path, required=True)
|
||||
parser.add_argument("--retrieved", help="UTC date recorded in the fixture header")
|
||||
parser.add_argument("--timeout", type=float, default=60.0)
|
||||
args = parser.parse_args()
|
||||
|
||||
source_url = args.url if args.input is None else f"file://{args.input.resolve()}"
|
||||
raw = args.input.read_bytes() if args.input is not None else fetch_source(args.url, args.timeout)
|
||||
retrieved = args.retrieved or dt.datetime.now(dt.timezone.utc).date().isoformat()
|
||||
active, status_counts, ipv4_status_counts = parse_source(raw)
|
||||
rendered, counts = render(
|
||||
source_url,
|
||||
retrieved,
|
||||
hashlib.sha256(raw).hexdigest(),
|
||||
sum(ipv4_status_counts.values()),
|
||||
status_counts,
|
||||
active,
|
||||
)
|
||||
args.output.parent.mkdir(parents=True, exist_ok=True)
|
||||
args.output.write_text(rendered, encoding="utf-8")
|
||||
print(
|
||||
"generated {output}: {cidr_rules} CIDRs from {selected_source_records} active "
|
||||
"records ({selected_addresses} addresses); source_sha256={sha256}".format(
|
||||
output=args.output,
|
||||
sha256=hashlib.sha256(raw).hexdigest(),
|
||||
**counts,
|
||||
)
|
||||
)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
raise SystemExit(main())
|
||||
except (OSError, ValueError, urllib.error.URLError) as error:
|
||||
print(f"error: {error}", file=sys.stderr)
|
||||
raise SystemExit(2)
|
||||
34
src/cli.rs
34
src/cli.rs
@ -26,6 +26,7 @@ use crate::parallel::config::{ParallelPhase1Config, ParallelPhase2Config};
|
||||
use crate::parallel::types::TalInputSpec;
|
||||
use crate::policy::{Policy, ResourceValidationMode, StrictPolicy};
|
||||
use crate::storage::{RocksStore, VcirStorageSummary};
|
||||
use crate::ta_constraints::TaConstraintsByTal;
|
||||
use crate::validation::run_tree_from_tal::{
|
||||
RunTreeFromTalAuditOutput, run_tree_from_multiple_tals_parallel_phase2_audit,
|
||||
run_tree_from_multiple_tals_parallel_phase2_audit_with_timing,
|
||||
@ -131,6 +132,7 @@ pub struct CliArgs {
|
||||
pub parallel_phase1_config: ParallelPhase1Config,
|
||||
pub parallel_phase2_config: ParallelPhase2Config,
|
||||
pub tal_inputs: Vec<TalInputSpec>,
|
||||
pub ta_constraints: TaConstraintsByTal,
|
||||
|
||||
pub db_path: PathBuf,
|
||||
pub raw_store_db: Option<PathBuf>,
|
||||
@ -210,6 +212,8 @@ Options:
|
||||
--raw-store-db <path> External raw-by-hash store DB path (optional)
|
||||
--repo-bytes-db <path> External repo object bytes DB path (optional)
|
||||
--policy <path> Policy TOML path (optional)
|
||||
--ta-constraints <tal-id>=<path>
|
||||
Apply local EE-resource constraints to one TAL (repeatable); adjacent <tal>.constraints files are auto-discovered
|
||||
--strict [policies] Enable strict policies (default all; comma list: name,cms-der,signed-attrs; none disables)
|
||||
--resource-validation-mode <validation-update-03|rfc6487>
|
||||
Resource certificate validation mode (default: validation-update-03)
|
||||
@ -318,6 +322,7 @@ pub fn parse_args(argv: &[String]) -> Result<CliArgs, String> {
|
||||
let mut tal_urls: Vec<String> = Vec::new();
|
||||
let mut tal_paths: Vec<PathBuf> = Vec::new();
|
||||
let mut ta_paths: Vec<PathBuf> = Vec::new();
|
||||
let mut ta_constraint_specs: Vec<String> = Vec::new();
|
||||
let mut parallel_phase1_cfg = ParallelPhase1Config::default();
|
||||
let mut parallel_phase2_cfg = ParallelPhase2Config::default();
|
||||
let mut dead_repo_blacklist_path: Option<PathBuf> = None;
|
||||
@ -427,6 +432,13 @@ pub fn parse_args(argv: &[String]) -> Result<CliArgs, String> {
|
||||
let v = argv.get(i).ok_or("--ta-path requires a value")?;
|
||||
ta_paths.push(PathBuf::from(v));
|
||||
}
|
||||
"--ta-constraints" => {
|
||||
i += 1;
|
||||
let v = argv
|
||||
.get(i)
|
||||
.ok_or("--ta-constraints requires <tal-id>=<path>")?;
|
||||
ta_constraint_specs.push(v.clone());
|
||||
}
|
||||
"--parallel-max-repo-sync-workers-global" => {
|
||||
i += 1;
|
||||
let v = argv
|
||||
@ -989,6 +1001,12 @@ pub fn parse_args(argv: &[String]) -> Result<CliArgs, String> {
|
||||
));
|
||||
}
|
||||
}
|
||||
if verification_only && !ta_constraint_specs.is_empty() {
|
||||
return Err(format!(
|
||||
"--ta-constraints is not supported with --verification-only\\n\\n{}",
|
||||
usage()
|
||||
));
|
||||
}
|
||||
let tal_url = tal_urls.first().cloned();
|
||||
let tal_path = tal_paths.first().cloned();
|
||||
let ta_path = ta_paths.first().cloned();
|
||||
@ -1147,6 +1165,7 @@ pub fn parse_args(argv: &[String]) -> Result<CliArgs, String> {
|
||||
tal_inputs.extend(tal_paths.iter().cloned().map(TalInputSpec::from_file_path));
|
||||
}
|
||||
}
|
||||
let ta_constraints = TaConstraintsByTal::load_for_tals(&tal_inputs, &ta_constraint_specs)?;
|
||||
|
||||
if dead_repo_blacklist_fail_threshold.is_some() && dead_repo_blacklist_path.is_none() {
|
||||
return Err(
|
||||
@ -1183,6 +1202,7 @@ pub fn parse_args(argv: &[String]) -> Result<CliArgs, String> {
|
||||
parallel_phase1_config: parallel_phase1_cfg,
|
||||
parallel_phase2_config: parallel_phase2_cfg,
|
||||
tal_inputs,
|
||||
ta_constraints,
|
||||
db_path,
|
||||
raw_store_db,
|
||||
repo_bytes_db,
|
||||
@ -2090,7 +2110,11 @@ where
|
||||
H: crate::sync::rrdp::Fetcher + Clone + 'static,
|
||||
R: crate::fetch::rsync::RsyncFetcher + Clone + 'static,
|
||||
{
|
||||
if args.verification_only || args.tal_inputs.len() > 1 {
|
||||
// The multi-TAL entry point preserves the TAL id supplied by the CLI.
|
||||
// A single-file TAL may otherwise derive its id from the embedded TA URI,
|
||||
// which is intentionally different from the local filename used for
|
||||
// adjacent <tal>.constraints discovery.
|
||||
if args.verification_only || args.tal_inputs.len() > 1 || !policy.ta_constraints.is_empty() {
|
||||
return if let Some(t) = timing {
|
||||
run_tree_from_multiple_tals_parallel_phase2_audit_with_timing(
|
||||
store,
|
||||
@ -2322,6 +2346,10 @@ pub fn run(argv: &[String]) -> Result<(), String> {
|
||||
if args.disable_rrdp {
|
||||
policy.sync_preference = crate::policy::SyncPreference::RsyncOnly;
|
||||
}
|
||||
policy.ta_constraints = args.ta_constraints.clone();
|
||||
for warning in policy.ta_constraints.configuration_warnings() {
|
||||
eprintln!("warning: {warning}");
|
||||
}
|
||||
}
|
||||
let validation_time = args
|
||||
.validation_time
|
||||
@ -3050,6 +3078,10 @@ pub fn run(argv: &[String]) -> Result<(), String> {
|
||||
},
|
||||
args.rsync_scope_policy,
|
||||
)?;
|
||||
if !policy.ta_constraints.is_empty() {
|
||||
contract.ta_constraints_fingerprint =
|
||||
Some(policy.ta_constraints.fingerprint_sha256_hex());
|
||||
}
|
||||
contract.fallback_replay_selections =
|
||||
crate::verification_only::collect_fallback_replay_selections(
|
||||
store.as_ref(),
|
||||
|
||||
@ -23,6 +23,7 @@ fn parse_help_returns_usage() {
|
||||
assert!(err.contains("--memory-trim-after-validation"), "{err}");
|
||||
assert!(err.contains("--enable-roa-validation-cache"), "{err}");
|
||||
assert!(err.contains("--resource-validation-mode"), "{err}");
|
||||
assert!(err.contains("--ta-constraints"), "{err}");
|
||||
assert!(err.contains("--max-ca-depth"), "{err}");
|
||||
assert!(err.contains("default: 32"), "{err}");
|
||||
assert!(
|
||||
@ -37,6 +38,26 @@ fn parse_help_returns_usage() {
|
||||
assert!(!err.contains("--parallel-phase2 "), "{err}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_accepts_explicit_ta_constraints_for_known_tal() {
|
||||
let dir = tempfile::tempdir().expect("tmpdir");
|
||||
let constraints_path = dir.path().join("example.constraints");
|
||||
std::fs::write(&constraints_path, "allow 192.0.2.0/24\n").expect("write constraints");
|
||||
let argv = vec![
|
||||
"rpki".to_string(),
|
||||
"--db".to_string(),
|
||||
"db".to_string(),
|
||||
"--tal-path".to_string(),
|
||||
"example.tal".to_string(),
|
||||
"--ta-path".to_string(),
|
||||
"example.cer".to_string(),
|
||||
"--ta-constraints".to_string(),
|
||||
format!("example={}", constraints_path.display()),
|
||||
];
|
||||
let args = parse_args(&argv).expect("parse args");
|
||||
assert!(!args.ta_constraints.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_rejects_unknown_argument() {
|
||||
let argv = vec![
|
||||
@ -119,6 +140,7 @@ fn verification_policy_preserves_contract_identity_and_selects_only_bound_fallba
|
||||
sync_preference: crate::policy::SyncPreference::RrdpThenRsync,
|
||||
..Policy::default()
|
||||
},
|
||||
ta_constraints_fingerprint: None,
|
||||
max_ca_depth: 32,
|
||||
max_instances: None,
|
||||
cache: crate::verification_only::ValidationCacheContract {
|
||||
|
||||
@ -40,6 +40,8 @@ pub mod storage;
|
||||
#[cfg(feature = "full")]
|
||||
pub mod sync;
|
||||
#[cfg(feature = "full")]
|
||||
pub mod ta_constraints;
|
||||
#[cfg(feature = "full")]
|
||||
pub mod tools;
|
||||
#[cfg(feature = "full")]
|
||||
pub mod validation;
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use crate::ta_constraints::TaConstraintsByTal;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum SyncPreference {
|
||||
@ -133,6 +135,10 @@ pub struct Policy {
|
||||
/// the already contract-bound VCIR fallback branch while replaying a run.
|
||||
#[serde(skip)]
|
||||
pub verification_forced_vcir_reuse_manifest_uris: BTreeSet<String>,
|
||||
/// Locally configured, per-TAL EE certificate resource constraints. They
|
||||
/// are intentionally CLI/runtime-only rather than policy-file input.
|
||||
#[serde(skip)]
|
||||
pub ta_constraints: TaConstraintsByTal,
|
||||
}
|
||||
|
||||
impl Default for Policy {
|
||||
@ -144,6 +150,7 @@ impl Default for Policy {
|
||||
resource_validation_mode: ResourceValidationMode::default(),
|
||||
strict: StrictPolicy::default(),
|
||||
verification_forced_vcir_reuse_manifest_uris: BTreeSet::new(),
|
||||
ta_constraints: TaConstraintsByTal::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1139
src/ta_constraints.rs
Normal file
1139
src/ta_constraints.rs
Normal file
File diff suppressed because it is too large
Load Diff
@ -961,6 +961,38 @@ pub fn process_publication_point_for_issuer_with_cache_options<P: PublicationPoi
|
||||
timing: Option<&TimingHandle>,
|
||||
collect_vcir_local_outputs: bool,
|
||||
roa_cache: RoaValidationCacheInput<'_>,
|
||||
) -> ObjectsOutput {
|
||||
process_publication_point_for_issuer_with_cache_options_and_ta_constraints(
|
||||
publication_point,
|
||||
policy,
|
||||
issuer_ca_der,
|
||||
issuer_ca_rsync_uri,
|
||||
issuer_effective_ip,
|
||||
issuer_effective_as,
|
||||
validation_time,
|
||||
timing,
|
||||
collect_vcir_local_outputs,
|
||||
roa_cache,
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
/// Serial signed-object processing with an optional, locally configured
|
||||
/// constraint set for the TA that owns this publication-point tree.
|
||||
pub fn process_publication_point_for_issuer_with_cache_options_and_ta_constraints<
|
||||
P: PublicationPointData,
|
||||
>(
|
||||
publication_point: &P,
|
||||
policy: &Policy,
|
||||
issuer_ca_der: &[u8],
|
||||
issuer_ca_rsync_uri: Option<&str>,
|
||||
issuer_effective_ip: Option<&crate::data_model::rc::IpResourceSet>,
|
||||
issuer_effective_as: Option<&crate::data_model::rc::AsResourceSet>,
|
||||
validation_time: time::OffsetDateTime,
|
||||
timing: Option<&TimingHandle>,
|
||||
collect_vcir_local_outputs: bool,
|
||||
roa_cache: RoaValidationCacheInput<'_>,
|
||||
ta_constraints: Option<&crate::ta_constraints::TaConstraints>,
|
||||
) -> ObjectsOutput {
|
||||
let manifest_rsync_uri = publication_point.manifest_rsync_uri();
|
||||
let manifest_bytes = publication_point.manifest_bytes();
|
||||
@ -1271,6 +1303,7 @@ pub fn process_publication_point_for_issuer_with_cache_options<P: PublicationPoi
|
||||
policy.strict.cms_der,
|
||||
policy.strict.name,
|
||||
policy.resource_validation_mode,
|
||||
ta_constraints,
|
||||
)
|
||||
}
|
||||
blocked @ (RoaCacheLookupResult::HashBlocked
|
||||
@ -1297,6 +1330,7 @@ pub fn process_publication_point_for_issuer_with_cache_options<P: PublicationPoi
|
||||
policy.strict.cms_der,
|
||||
policy.strict.name,
|
||||
policy.resource_validation_mode,
|
||||
ta_constraints,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -1320,6 +1354,7 @@ pub fn process_publication_point_for_issuer_with_cache_options<P: PublicationPoi
|
||||
policy.strict.cms_der,
|
||||
policy.strict.name,
|
||||
policy.resource_validation_mode,
|
||||
ta_constraints,
|
||||
)
|
||||
};
|
||||
match result.outcome {
|
||||
@ -1437,6 +1472,7 @@ pub fn process_publication_point_for_issuer_with_cache_options<P: PublicationPoi
|
||||
policy.strict.cms_der,
|
||||
policy.strict.name,
|
||||
policy.resource_validation_mode,
|
||||
ta_constraints,
|
||||
) {
|
||||
Ok((att, local_output)) => {
|
||||
stats.aspa_ok += 1;
|
||||
@ -1790,6 +1826,10 @@ pub(crate) struct RoaTaskShared {
|
||||
issuer_effective_ip: Option<Arc<crate::data_model::rc::IpResourceSet>>,
|
||||
issuer_effective_as: Option<Arc<crate::data_model::rc::AsResourceSet>>,
|
||||
resource_validation_mode: ResourceValidationMode,
|
||||
/// Immutable constraints snapshot selected by the owning TAL. Every
|
||||
/// ROA task for a publication point shares this Arc, so workers do not
|
||||
/// need a mutable/global policy lookup or a copy of the interval rules.
|
||||
ta_constraints: Option<Arc<crate::ta_constraints::TaConstraints>>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -1911,6 +1951,7 @@ fn validate_owned_roa_task(worker_index: usize, task: OwnedRoaTask) -> RoaTaskRe
|
||||
task.strict_cms_der,
|
||||
task.strict_name,
|
||||
task.resource_validation_mode,
|
||||
shared.ta_constraints.as_deref(),
|
||||
)
|
||||
.map(|(vrps, local_outputs, cache_object_meta)| RoaTaskOk {
|
||||
vrps,
|
||||
@ -2009,6 +2050,40 @@ pub(crate) fn prepare_publication_point_for_parallel_roa_with_cache<P: Publicati
|
||||
validation_time: time::OffsetDateTime,
|
||||
collect_vcir_local_outputs: bool,
|
||||
roa_cache: RoaValidationCacheInput<'_>,
|
||||
) -> ParallelObjectsPrepare {
|
||||
prepare_publication_point_for_parallel_roa_with_cache_and_ta_constraints(
|
||||
publication_point_id,
|
||||
publication_point,
|
||||
policy,
|
||||
issuer_ca_der,
|
||||
issuer_ca_rsync_uri,
|
||||
issuer_effective_ip,
|
||||
issuer_effective_as,
|
||||
validation_time,
|
||||
collect_vcir_local_outputs,
|
||||
roa_cache,
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
/// Prepare a publication point for parallel ROA validation with the
|
||||
/// immutable constraints snapshot belonging to its TAL. The snapshot is
|
||||
/// moved into the stage-owned shared payload and therefore remains available
|
||||
/// to detached ROA workers after the scoped phase-2 stage worker returns.
|
||||
pub(crate) fn prepare_publication_point_for_parallel_roa_with_cache_and_ta_constraints<
|
||||
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>,
|
||||
issuer_effective_as: Option<&crate::data_model::rc::AsResourceSet>,
|
||||
validation_time: time::OffsetDateTime,
|
||||
collect_vcir_local_outputs: bool,
|
||||
roa_cache: RoaValidationCacheInput<'_>,
|
||||
ta_constraints: Option<Arc<crate::ta_constraints::TaConstraints>>,
|
||||
) -> ParallelObjectsPrepare {
|
||||
let manifest_rsync_uri = publication_point.manifest_rsync_uri();
|
||||
let manifest_bytes = publication_point.manifest_bytes();
|
||||
@ -2323,6 +2398,7 @@ pub(crate) fn prepare_publication_point_for_parallel_roa_with_cache<P: Publicati
|
||||
issuer_effective_ip: issuer_effective_ip.cloned().map(Arc::new),
|
||||
issuer_effective_as: issuer_effective_as.cloned().map(Arc::new),
|
||||
resource_validation_mode: policy.resource_validation_mode,
|
||||
ta_constraints,
|
||||
}),
|
||||
validation_time,
|
||||
collect_vcir_local_outputs,
|
||||
@ -2441,6 +2517,7 @@ pub(crate) fn reduce_parallel_roa_stage(
|
||||
strict_cms_der,
|
||||
strict_name,
|
||||
shared.resource_validation_mode,
|
||||
shared.ta_constraints.as_deref(),
|
||||
) {
|
||||
Ok((att, local_output)) => {
|
||||
stats.aspa_ok += 1;
|
||||
@ -2660,6 +2737,9 @@ pub(crate) enum ObjectValidateError {
|
||||
"EE certificate resources are not a subset of issuer effective resources (RFC 6487 §7.2; RFC 3779)"
|
||||
)]
|
||||
EeResourcesNotSubset,
|
||||
|
||||
#[error("EE certificate violates locally configured TA constraints: {0}")]
|
||||
TaConstraints(#[from] crate::ta_constraints::TaConstraintsViolation),
|
||||
}
|
||||
|
||||
pub(crate) fn validate_roa_task_serial(
|
||||
@ -2679,6 +2759,7 @@ pub(crate) fn validate_roa_task_serial(
|
||||
strict_cms_der: bool,
|
||||
strict_name: bool,
|
||||
resource_validation_mode: ResourceValidationMode,
|
||||
ta_constraints: Option<&crate::ta_constraints::TaConstraints>,
|
||||
) -> RoaTaskResult {
|
||||
let outcome = process_roa_with_issuer(
|
||||
task.file,
|
||||
@ -2697,6 +2778,7 @@ pub(crate) fn validate_roa_task_serial(
|
||||
strict_cms_der,
|
||||
strict_name,
|
||||
resource_validation_mode,
|
||||
ta_constraints,
|
||||
)
|
||||
.map(|(vrps, local_outputs, cache_object_meta)| RoaTaskOk {
|
||||
vrps,
|
||||
@ -2732,6 +2814,7 @@ fn process_roa_with_issuer(
|
||||
strict_cms_der: bool,
|
||||
strict_name: bool,
|
||||
resource_validation_mode: ResourceValidationMode,
|
||||
ta_constraints: Option<&crate::ta_constraints::TaConstraints>,
|
||||
) -> Result<(Vec<Vrp>, Vec<VcirLocalOutput>, Option<RoaCacheObjectMeta>), ObjectValidateError> {
|
||||
let _decode = timing
|
||||
.as_ref()
|
||||
@ -2792,6 +2875,10 @@ fn process_roa_with_issuer(
|
||||
)?;
|
||||
drop(_subset);
|
||||
|
||||
if let Some(ta_constraints) = ta_constraints {
|
||||
ta_constraints.validate_ee_certificate(&ee.resource_cert)?;
|
||||
}
|
||||
|
||||
let vrps = roa_to_vrps_with_vrs(&roa, ee_vrs.ip.as_ref())?;
|
||||
let cache_object_meta = RoaCacheObjectMeta {
|
||||
source_object_uri: file.rsync_uri.clone(),
|
||||
@ -2861,6 +2948,7 @@ fn process_roa_with_issuer_parallel_cached(
|
||||
strict_cms_der: bool,
|
||||
strict_name: bool,
|
||||
resource_validation_mode: ResourceValidationMode,
|
||||
ta_constraints: Option<&crate::ta_constraints::TaConstraints>,
|
||||
) -> Result<(Vec<Vrp>, Vec<VcirLocalOutput>, Option<RoaCacheObjectMeta>), ObjectValidateError> {
|
||||
let _decode = timing
|
||||
.as_ref()
|
||||
@ -2927,6 +3015,10 @@ fn process_roa_with_issuer_parallel_cached(
|
||||
)?;
|
||||
drop(_subset);
|
||||
|
||||
if let Some(ta_constraints) = ta_constraints {
|
||||
ta_constraints.validate_ee_certificate(&ee.resource_cert)?;
|
||||
}
|
||||
|
||||
let vrps = roa_to_vrps_with_vrs(&roa, ee_vrs.ip.as_ref())?;
|
||||
let cache_object_meta = RoaCacheObjectMeta {
|
||||
source_object_uri: file.rsync_uri.clone(),
|
||||
@ -2996,6 +3088,7 @@ fn process_aspa_with_issuer(
|
||||
strict_cms_der: bool,
|
||||
strict_name: bool,
|
||||
resource_validation_mode: ResourceValidationMode,
|
||||
ta_constraints: Option<&crate::ta_constraints::TaConstraints>,
|
||||
) -> Result<(AspaAttestation, Option<VcirLocalOutput>), ObjectValidateError> {
|
||||
let _decode = timing
|
||||
.as_ref()
|
||||
@ -3056,6 +3149,10 @@ fn process_aspa_with_issuer(
|
||||
)?;
|
||||
drop(_subset);
|
||||
|
||||
if let Some(ta_constraints) = ta_constraints {
|
||||
ta_constraints.validate_ee_certificate(&ee.resource_cert)?;
|
||||
}
|
||||
|
||||
validate_aspa_customer_in_vrs(&aspa, ee_vrs.asn.as_ref())?;
|
||||
|
||||
let attestation = AspaAttestation {
|
||||
@ -5505,6 +5602,13 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn parallel_stage_roa_tasks_share_stage_owned_payloads() {
|
||||
let ta_constraints = Arc::new(
|
||||
crate::ta_constraints::TaConstraints::from_file(
|
||||
&std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("tests/fixtures/ta_constraints/afrinic-current-ipv4-deny.constraints"),
|
||||
)
|
||||
.expect("load constraints fixture"),
|
||||
);
|
||||
let stage = ParallelObjectsStage {
|
||||
publication_point_id: 7,
|
||||
shared: Arc::new(RoaTaskShared {
|
||||
@ -5535,6 +5639,7 @@ mod tests {
|
||||
issuer_effective_ip: None,
|
||||
issuer_effective_as: None,
|
||||
resource_validation_mode: ResourceValidationMode::default(),
|
||||
ta_constraints: Some(Arc::clone(&ta_constraints)),
|
||||
}),
|
||||
validation_time: OffsetDateTime::now_utc(),
|
||||
collect_vcir_local_outputs: false,
|
||||
@ -5554,6 +5659,14 @@ mod tests {
|
||||
let tasks = stage.build_roa_tasks();
|
||||
assert_eq!(tasks.len(), 2);
|
||||
assert!(Arc::ptr_eq(&tasks[0].shared, &tasks[1].shared));
|
||||
assert!(Arc::ptr_eq(
|
||||
tasks[0]
|
||||
.shared
|
||||
.ta_constraints
|
||||
.as_ref()
|
||||
.expect("task constraints snapshot"),
|
||||
&ta_constraints
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@ -901,6 +901,10 @@ where
|
||||
H: Fetcher + Clone + 'static,
|
||||
R: crate::fetch::rsync::RsyncFetcher + Clone + 'static,
|
||||
{
|
||||
// Constraints are an immutable per-run policy snapshot. The phase-2
|
||||
// ready-stage binds the snapshot to each CA's TAL and moves an Arc into
|
||||
// ROA/ASPA worker state, so constrained multi-TAL runs retain the same
|
||||
// parallel scheduler as unconstrained runs.
|
||||
let phase2_enabled = phase2_config.is_some();
|
||||
if tal_inputs.is_empty() {
|
||||
return Err(RunTreeFromTalError::Replay(
|
||||
|
||||
@ -16,7 +16,8 @@ use crate::validation::manifest::PublicationPointData;
|
||||
use crate::validation::manifest::PublicationPointSource;
|
||||
use crate::validation::objects::{
|
||||
ObjectsOutput, OwnedRoaTask, ParallelObjectsPrepare, ParallelObjectsStage,
|
||||
RoaValidationCacheInput, prepare_publication_point_for_parallel_roa_with_cache,
|
||||
RoaValidationCacheInput,
|
||||
prepare_publication_point_for_parallel_roa_with_cache_and_ta_constraints,
|
||||
reduce_parallel_roa_stage,
|
||||
};
|
||||
use crate::validation::tree::{
|
||||
@ -1367,7 +1368,16 @@ fn compute_ready_publication_point_stage(
|
||||
} else {
|
||||
RoaValidationCacheInput::disabled()
|
||||
};
|
||||
match prepare_publication_point_for_parallel_roa_with_cache(
|
||||
let ta_constraints = runner
|
||||
.policy
|
||||
.ta_constraints
|
||||
.shared_for_tal(&ready.node.handle.tal_id);
|
||||
if ta_constraints.is_some() {
|
||||
if let Some(timing) = runner.timing.as_ref() {
|
||||
timing.record_count("ta_constraints_parallel_publication_points", 1);
|
||||
}
|
||||
}
|
||||
match prepare_publication_point_for_parallel_roa_with_cache_and_ta_constraints(
|
||||
ready.node.id,
|
||||
&fresh_stage.fresh_point,
|
||||
runner.policy,
|
||||
@ -1378,6 +1388,7 @@ fn compute_ready_publication_point_stage(
|
||||
runner.validation_time,
|
||||
runner.persist_vcir,
|
||||
roa_cache,
|
||||
ta_constraints,
|
||||
) {
|
||||
ParallelObjectsPrepare::Complete(mut objects) => {
|
||||
metrics.prepare_ms = elapsed_ms(prepare_started);
|
||||
|
||||
@ -53,6 +53,7 @@ use crate::validation::objects::{
|
||||
AspaAttestation, ParallelRoaWorkerPool, RoaValidationCacheInput, RoaValidationCacheView,
|
||||
RouterKeyPayload, Vrp, process_publication_point_for_issuer_parallel_roa_with_cache_options,
|
||||
process_publication_point_for_issuer_parallel_roa_with_pool_cache_options,
|
||||
process_publication_point_for_issuer_with_cache_options_and_ta_constraints,
|
||||
};
|
||||
use crate::validation::publication_point::PublicationPointSnapshot;
|
||||
use crate::validation::tree::{
|
||||
@ -1551,12 +1552,30 @@ impl<'a> PublicationPointRunner for Rpkiv1PublicationPointRunner<'a> {
|
||||
RoaValidationCacheInput::disabled()
|
||||
};
|
||||
let objects_processing_started = std::time::Instant::now();
|
||||
let ta_constraints = self.policy.ta_constraints.for_tal(&ca.tal_id);
|
||||
let mut objects = {
|
||||
let _objects_total = self
|
||||
.timing
|
||||
.as_ref()
|
||||
.map(|t| t.span_phase("objects_processing_total"));
|
||||
if let Some(phase2_pool) = self.parallel_roa_worker_pool.as_ref() {
|
||||
if let Some(ta_constraints) = ta_constraints {
|
||||
// This method is the serial/fallback publication-point path. The
|
||||
// phase-2 scheduler uses the stage-specific parallel prepare path,
|
||||
// which carries the same immutable per-TAL snapshot into workers.
|
||||
process_publication_point_for_issuer_with_cache_options_and_ta_constraints(
|
||||
&fresh_point,
|
||||
self.policy,
|
||||
issuer_ca_der.as_ref(),
|
||||
ca.ca_certificate_rsync_uri.as_deref(),
|
||||
ca.effective_ip_resources.as_ref(),
|
||||
ca.effective_as_resources.as_ref(),
|
||||
self.validation_time,
|
||||
self.timing.as_ref(),
|
||||
false,
|
||||
roa_cache,
|
||||
Some(ta_constraints),
|
||||
)
|
||||
} else if let Some(phase2_pool) = self.parallel_roa_worker_pool.as_ref() {
|
||||
process_publication_point_for_issuer_parallel_roa_with_pool_cache_options(
|
||||
&fresh_point,
|
||||
self.policy,
|
||||
@ -1971,13 +1990,13 @@ fn sha256_digest_32(bytes: impl AsRef<[u8]>) -> [u8; 32] {
|
||||
out
|
||||
}
|
||||
|
||||
fn hash_serialized_parts(parts: &[(&str, Vec<u8>)]) -> [u8; 32] {
|
||||
fn hash_serialized_parts(parts: &[(&str, &[u8])]) -> [u8; 32] {
|
||||
let mut hasher = sha2::Sha256::new();
|
||||
for (label, value) in parts {
|
||||
hasher.update((label.len() as u64).to_be_bytes());
|
||||
hasher.update(label.as_bytes());
|
||||
hasher.update((value.len() as u64).to_be_bytes());
|
||||
hasher.update(value);
|
||||
hasher.update(*value);
|
||||
}
|
||||
let digest = hasher.finalize();
|
||||
let mut out = [0u8; 32];
|
||||
@ -1991,41 +2010,34 @@ fn cbor_or_debug_bytes<T: serde::Serialize + std::fmt::Debug>(value: &T) -> Vec<
|
||||
|
||||
fn ta_context_digest_for_ca(ca: &CaInstanceHandle) -> [u8; 32] {
|
||||
hash_serialized_parts(&[
|
||||
("version", b"publication-point-cache-ta-v1".to_vec()),
|
||||
("tal_id", ca.tal_id.as_bytes().to_vec()),
|
||||
("version", b"publication-point-cache-ta-v1"),
|
||||
("tal_id", ca.tal_id.as_bytes()),
|
||||
])
|
||||
}
|
||||
|
||||
pub(crate) fn ca_validation_context_digest_for_ca(ca: &CaInstanceHandle) -> [u8; 32] {
|
||||
let parent_manifest = ca
|
||||
.parent_manifest_rsync_uri
|
||||
.as_deref()
|
||||
.unwrap_or("")
|
||||
.as_bytes();
|
||||
let effective_ip = cbor_or_debug_bytes(&ca.effective_ip_resources);
|
||||
let effective_as = cbor_or_debug_bytes(&ca.effective_as_resources);
|
||||
hash_serialized_parts(&[
|
||||
(
|
||||
"version",
|
||||
b"publication-point-cache-parent-context-v1".to_vec(),
|
||||
),
|
||||
("tal_id", ca.tal_id.as_bytes().to_vec()),
|
||||
(
|
||||
"parent_manifest",
|
||||
ca.parent_manifest_rsync_uri
|
||||
.as_deref()
|
||||
.unwrap_or("")
|
||||
.as_bytes()
|
||||
.to_vec(),
|
||||
),
|
||||
(
|
||||
"effective_ip",
|
||||
cbor_or_debug_bytes(&ca.effective_ip_resources),
|
||||
),
|
||||
(
|
||||
"effective_as",
|
||||
cbor_or_debug_bytes(&ca.effective_as_resources),
|
||||
),
|
||||
("version", b"publication-point-cache-parent-context-v1"),
|
||||
("tal_id", ca.tal_id.as_bytes()),
|
||||
("parent_manifest", parent_manifest),
|
||||
("effective_ip", effective_ip.as_slice()),
|
||||
("effective_as", effective_as.as_slice()),
|
||||
])
|
||||
}
|
||||
|
||||
pub(crate) fn publication_point_cache_policy_fingerprint(policy: &Policy) -> [u8; 32] {
|
||||
let policy_bytes = cbor_or_debug_bytes(policy);
|
||||
hash_serialized_parts(&[
|
||||
("version", b"publication-point-cache-policy-v1".to_vec()),
|
||||
("policy", cbor_or_debug_bytes(policy)),
|
||||
("version", b"publication-point-cache-policy-v3"),
|
||||
("policy", policy_bytes.as_slice()),
|
||||
("ta_constraints", policy.ta_constraints.fingerprint_bytes()),
|
||||
])
|
||||
}
|
||||
|
||||
@ -2084,16 +2096,13 @@ fn child_certificate_cache_key_sha256_hex(
|
||||
policy_fingerprint: &[u8; 32],
|
||||
) -> String {
|
||||
let digest = hash_serialized_parts(&[
|
||||
("version", b"child-certificate-cache-key-v1".to_vec()),
|
||||
("child_cert_uri", child_cert_uri.as_bytes().to_vec()),
|
||||
("child_cert_sha256", child_cert_sha256.to_vec()),
|
||||
("issuer_ca_sha256", issuer_ca_sha256.to_vec()),
|
||||
("version", b"child-certificate-cache-key-v1"),
|
||||
("child_cert_uri", child_cert_uri.as_bytes()),
|
||||
("child_cert_sha256", child_cert_sha256),
|
||||
("issuer_ca_sha256", issuer_ca_sha256),
|
||||
// Keep the persisted cache-key label stable; this change only renames Rust identifiers.
|
||||
(
|
||||
"parent_context_digest",
|
||||
ca_validation_context_digest.to_vec(),
|
||||
),
|
||||
("policy_fingerprint", policy_fingerprint.to_vec()),
|
||||
("parent_context_digest", ca_validation_context_digest),
|
||||
("policy_fingerprint", policy_fingerprint),
|
||||
]);
|
||||
sha256_hex_from_32(&digest)
|
||||
}
|
||||
@ -2986,6 +2995,24 @@ fn discover_children_from_fresh_snapshot_with_audit_cached_with_issuer_der<
|
||||
|
||||
match router_result {
|
||||
Ok(router) => {
|
||||
if let Some(ta_constraints) = policy.ta_constraints.for_tal(&issuer.tal_id)
|
||||
{
|
||||
if let Err(error) =
|
||||
ta_constraints.validate_ee_certificate(&router.resource_cert)
|
||||
{
|
||||
router_error = router_error.saturating_add(1);
|
||||
audits.push(ObjectAuditEntry {
|
||||
rsync_uri: f.rsync_uri.clone(),
|
||||
sha256_hex: sha256_hex_from_32(&f.sha256),
|
||||
kind: AuditObjectKind::RouterCertificate,
|
||||
result: AuditObjectResult::Error,
|
||||
detail: Some(format!(
|
||||
"router certificate violates TA constraints: {error}"
|
||||
)),
|
||||
});
|
||||
continue;
|
||||
}
|
||||
}
|
||||
let router_asns = match router_asns_for_resource_mode(
|
||||
&router.asns,
|
||||
issuer.effective_as_resources.as_ref(),
|
||||
|
||||
@ -73,6 +73,11 @@ pub struct ValidationContract {
|
||||
pub validation_time: String,
|
||||
pub binary_sha256: String,
|
||||
pub policy: Policy,
|
||||
/// Constraints are currently normal-run-only. Persist their semantic
|
||||
/// fingerprint so verification-only can fail closed rather than replay a
|
||||
/// constrained run with an empty runtime-only policy map.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub ta_constraints_fingerprint: Option<String>,
|
||||
pub max_ca_depth: usize,
|
||||
pub max_instances: Option<usize>,
|
||||
pub cache: ValidationCacheContract,
|
||||
@ -140,6 +145,7 @@ impl ValidationContract {
|
||||
validation_time: format_validation_time(validation_time)?,
|
||||
binary_sha256: current_binary_sha256()?,
|
||||
policy,
|
||||
ta_constraints_fingerprint: None,
|
||||
max_ca_depth,
|
||||
max_instances,
|
||||
cache,
|
||||
@ -163,6 +169,9 @@ impl ValidationContract {
|
||||
}
|
||||
self.validation_time()?;
|
||||
validate_sha256_hex("validation contract binarySha256", &self.binary_sha256)?;
|
||||
if let Some(fingerprint) = self.ta_constraints_fingerprint.as_deref() {
|
||||
validate_sha256_hex("validation contract taConstraintsFingerprint", fingerprint)?;
|
||||
}
|
||||
if self.max_ca_depth == 0 {
|
||||
return Err("validation contract maxCaDepth must be greater than zero".to_string());
|
||||
}
|
||||
@ -600,6 +609,12 @@ pub fn prepare_verification(
|
||||
|
||||
let cir = read_cir(&artifacts.source_cir)?;
|
||||
let contract = read_validation_contract(&artifacts.source_contract)?;
|
||||
if contract.ta_constraints_fingerprint.is_some() {
|
||||
return Err(
|
||||
"verification-only is not supported for a run with TA constraints; rerun normal validation with the same constraints"
|
||||
.to_string(),
|
||||
);
|
||||
}
|
||||
if cir.validation_time.to_offset(time::UtcOffset::UTC) != contract.validation_time()? {
|
||||
return Err(format!(
|
||||
"source CIR validation time differs from validation contract: cir={}, contract={}",
|
||||
@ -895,6 +910,7 @@ mod tests {
|
||||
validation_time: "2026-07-16T00:00:00Z".to_string(),
|
||||
binary_sha256: "ab".repeat(32),
|
||||
policy: Policy::default(),
|
||||
ta_constraints_fingerprint: None,
|
||||
max_ca_depth: 32,
|
||||
max_instances: None,
|
||||
cache: ValidationCacheContract {
|
||||
|
||||
1842
tests/fixtures/ta_constraints/afrinic-current-ipv4-deny.constraints
vendored
Normal file
1842
tests/fixtures/ta_constraints/afrinic-current-ipv4-deny.constraints
vendored
Normal file
File diff suppressed because it is too large
Load Diff
394
tests/fixtures/ta_constraints/afrinic-full-ipv4-deny.constraints
vendored
Normal file
394
tests/fixtures/ta_constraints/afrinic-full-ipv4-deny.constraints
vendored
Normal file
@ -0,0 +1,394 @@
|
||||
# Feature #151 remote all5 performance fixture.
|
||||
#
|
||||
# Source: official rpki-client 9.8 afrinic.constraints
|
||||
# ($OpenBSD: afrinic.constraints,v 1.4 2026/03/11 21:46:36 job Exp $).
|
||||
# Every IPv4 allow entry from that file (including recovered and transfer
|
||||
# ranges) is converted to deny. IPv6 and ASN entries are intentionally not
|
||||
# included because this fixture measures the full AFRINIC IPv4 policy.
|
||||
allow 0.0.0.0/0
|
||||
allow ::/0
|
||||
allow 0 - 4294967295
|
||||
|
||||
deny 41.0.0.0/8
|
||||
deny 102.0.0.0/8
|
||||
deny 105.0.0.0/8
|
||||
deny 154.0.0.0/16
|
||||
deny 154.16.0.0/16
|
||||
deny 154.65.0.0 - 154.255.255.255
|
||||
deny 196.0.0.0 - 196.1.0.255
|
||||
deny 196.1.4.0/24
|
||||
deny 196.1.7.0 - 196.1.63.255
|
||||
deny 196.1.71.0/24
|
||||
deny 196.1.74.0 - 196.1.103.255
|
||||
deny 196.1.115.0 - 196.1.133.255
|
||||
deny 196.1.137.0/24
|
||||
deny 196.1.143.0 - 196.1.159.255
|
||||
deny 196.1.176.0 - 196.1.255.255
|
||||
deny 196.2.2.0/23
|
||||
deny 196.2.8.0 - 196.2.255.255
|
||||
deny 196.3.14.0/23
|
||||
deny 196.3.57.0 - 196.3.64.255
|
||||
deny 196.3.90.0/24
|
||||
deny 196.3.92.0 - 196.3.94.255
|
||||
deny 196.3.96.0/21
|
||||
deny 196.3.105.0/24
|
||||
deny 196.3.107.0 - 196.3.131.255
|
||||
deny 196.3.148.0/22
|
||||
deny 196.3.154.0 - 196.3.183.255
|
||||
deny 196.3.224.0 - 196.4.45.255
|
||||
deny 196.4.71.0 - 196.11.171.255
|
||||
deny 196.11.174.0 - 196.11.239.255
|
||||
deny 196.11.248.0/21
|
||||
deny 196.12.10.0 - 196.12.31.255
|
||||
deny 196.12.128.0/19
|
||||
deny 196.12.192.0 - 196.15.15.255
|
||||
deny 196.15.64.0 - 196.26.255.255
|
||||
deny 196.27.64.0 - 196.28.47.255
|
||||
deny 196.28.64.0 - 196.29.63.255
|
||||
deny 196.29.96.0 - 196.31.255.255
|
||||
deny 196.32.8.0 - 196.32.31.255
|
||||
deny 196.32.96.0/19
|
||||
deny 196.32.160.0 - 196.39.255.255
|
||||
deny 196.40.96.0 - 196.41.255.255
|
||||
deny 196.42.64.0 - 196.216.0.255
|
||||
deny 196.216.2.0 - 197.255.255.255
|
||||
deny 45.96.0.0 - 45.111.255.255
|
||||
deny 45.192.0.0 - 45.222.255.255
|
||||
deny 45.240.0.0 - 45.247.255.255
|
||||
deny 66.251.128.0 - 66.251.191.255
|
||||
deny 139.26.0.0 - 139.26.255.255
|
||||
deny 146.196.128.0 - 146.196.255.255
|
||||
deny 160.19.36.0 - 160.19.39.255
|
||||
deny 160.19.60.0 - 160.19.63.255
|
||||
deny 160.19.96.0 - 160.19.103.255
|
||||
deny 160.19.112.0 - 160.19.143.255
|
||||
deny 160.19.152.0 - 160.19.155.255
|
||||
deny 160.19.188.0 - 160.19.191.255
|
||||
deny 160.19.192.0 - 160.19.199.255
|
||||
deny 160.19.232.0 - 160.19.239.255
|
||||
deny 160.20.24.0 - 160.20.31.255
|
||||
deny 160.20.112.0 - 160.20.115.255
|
||||
deny 160.20.213.0 - 160.20.213.255
|
||||
deny 160.20.217.0 - 160.20.217.255
|
||||
deny 160.20.221.0 - 160.20.221.255
|
||||
deny 160.20.226.0 - 160.20.227.255
|
||||
deny 160.20.252.0 - 160.20.255.255
|
||||
deny 160.238.11.0 - 160.238.11.255
|
||||
deny 160.238.48.0 - 160.238.49.255
|
||||
deny 160.238.50.0 - 160.238.50.255
|
||||
deny 160.238.57.0 - 160.238.57.255
|
||||
deny 160.238.101.0 - 160.238.101.255
|
||||
deny 161.123.0.0 - 161.123.255.255
|
||||
deny 164.160.0.0 - 164.160.255.255
|
||||
deny 192.12.110.0 - 192.12.111.255
|
||||
deny 192.12.116.0 - 192.12.117.255
|
||||
deny 192.47.36.0 - 192.47.36.255
|
||||
deny 192.51.240.0 - 192.51.240.255
|
||||
deny 192.70.200.0 - 192.70.201.255
|
||||
deny 192.75.236.0 - 192.75.236.255
|
||||
deny 192.83.208.0 - 192.83.215.255
|
||||
deny 192.91.200.0 - 192.91.200.255
|
||||
deny 192.142.0.0 - 192.143.255.255
|
||||
deny 192.145.128.0 - 192.145.191.255
|
||||
deny 192.145.230.0 - 192.145.230.255
|
||||
deny 204.8.204.0 - 204.8.207.255
|
||||
deny 208.85.156.0 - 208.85.159.255
|
||||
deny 83.143.24.0 - 83.143.31.255
|
||||
deny 84.205.96.0 - 84.205.127.255
|
||||
deny 131.176.0.0 - 131.176.255.255
|
||||
deny 163.121.0.0 - 163.121.255.255
|
||||
deny 165.231.0.0 - 165.231.255.255
|
||||
deny 192.52.232.0 - 192.52.232.255
|
||||
deny 193.17.215.0 - 193.17.215.255
|
||||
deny 193.19.232.0 - 193.19.235.255
|
||||
deny 193.41.146.0 - 193.41.147.255
|
||||
deny 193.108.23.0 - 193.108.23.255
|
||||
deny 193.108.28.0 - 193.108.28.255
|
||||
deny 193.109.66.0 - 193.109.67.255
|
||||
deny 193.110.104.0 - 193.110.105.255
|
||||
deny 193.194.128.0 - 193.194.128.255
|
||||
deny 193.227.128.0 - 193.227.128.255
|
||||
deny 194.9.64.0 - 194.9.65.255
|
||||
deny 194.9.82.0 - 194.9.83.255
|
||||
deny 195.24.80.0 - 195.24.87.255
|
||||
deny 195.39.218.0 - 195.39.219.255
|
||||
deny 195.234.120.0 - 195.234.123.255
|
||||
deny 195.234.168.0 - 195.234.168.255
|
||||
deny 195.234.185.0 - 195.234.185.255
|
||||
deny 195.234.252.0 - 195.234.255.255
|
||||
deny 193.188.7.0/24
|
||||
deny 193.189.0.0/18
|
||||
deny 193.189.128.0/24
|
||||
deny 193.194.160.0/19
|
||||
deny 193.221.218.0/24
|
||||
deny 64.57.112.0 - 64.57.127.255
|
||||
deny 66.8.0.0 - 66.8.127.255
|
||||
deny 66.18.64.0 - 66.18.95.255
|
||||
deny 69.63.64.0 - 69.63.79.255
|
||||
deny 69.67.32.0 - 69.67.47.255
|
||||
deny 137.158.0.0 - 137.158.255.255
|
||||
deny 137.214.0.0 - 137.214.255.255
|
||||
deny 137.215.0.0 - 137.215.255.255
|
||||
deny 139.53.0.0 - 139.53.255.255
|
||||
deny 143.128.0.0 - 143.128.255.255
|
||||
deny 143.160.0.0 - 143.160.255.255
|
||||
deny 146.64.0.0 - 146.64.255.255
|
||||
deny 146.141.0.0 - 146.141.255.255
|
||||
deny 146.182.0.0 - 146.182.255.255
|
||||
deny 146.230.0.0 - 146.230.255.255
|
||||
deny 146.231.0.0 - 146.231.255.255
|
||||
deny 146.232.0.0 - 146.232.255.255
|
||||
deny 147.110.0.0 - 147.110.255.255
|
||||
deny 152.106.0.0 - 152.106.255.255
|
||||
deny 152.107.0.0 - 152.107.255.255
|
||||
deny 152.108.0.0 - 152.108.255.255
|
||||
deny 152.109.0.0 - 152.109.255.255
|
||||
deny 152.110.0.0 - 152.110.255.255
|
||||
deny 152.111.0.0 - 152.111.255.255
|
||||
deny 152.112.0.0 - 152.112.255.255
|
||||
deny 155.159.0.0 - 155.159.255.255
|
||||
deny 155.232.0.0 - 155.232.255.255
|
||||
deny 155.233.0.0 - 155.233.255.255
|
||||
deny 155.234.0.0 - 155.234.255.255
|
||||
deny 155.235.0.0 - 155.235.255.255
|
||||
deny 155.236.0.0 - 155.236.255.255
|
||||
deny 155.237.0.0 - 155.237.255.255
|
||||
deny 155.238.0.0 - 155.238.255.255
|
||||
deny 155.239.0.0 - 155.239.255.255
|
||||
deny 155.240.0.0 - 155.240.255.255
|
||||
deny 156.8.0.0 - 156.8.255.255
|
||||
deny 160.115.0.0 - 160.115.255.255
|
||||
deny 160.116.0.0 - 160.116.255.255
|
||||
deny 160.117.0.0 - 160.117.255.255
|
||||
deny 160.118.0.0 - 160.118.255.255
|
||||
deny 160.119.0.0 - 160.119.255.255
|
||||
deny 160.120.0.0 - 160.120.255.255
|
||||
deny 160.121.0.0 - 160.121.255.255
|
||||
deny 160.122.0.0 - 160.122.255.255
|
||||
deny 160.123.0.0 - 160.123.255.255
|
||||
deny 160.124.0.0 - 160.124.255.255
|
||||
deny 163.195.0.0 - 163.195.255.255
|
||||
deny 163.196.0.0 - 163.196.255.255
|
||||
deny 163.197.0.0 - 163.197.255.255
|
||||
deny 163.198.0.0 - 163.198.255.255
|
||||
deny 163.199.0.0 - 163.199.255.255
|
||||
deny 163.200.0.0 - 163.200.255.255
|
||||
deny 163.201.0.0 - 163.201.255.255
|
||||
deny 163.202.0.0 - 163.202.255.255
|
||||
deny 163.203.0.0 - 163.203.255.255
|
||||
deny 164.88.0.0 - 164.88.255.255
|
||||
deny 164.146.0.0 - 164.151.255.255
|
||||
deny 164.155.0.0 - 164.155.255.255
|
||||
deny 165.3.0.0 - 165.5.255.255
|
||||
deny 165.8.0.0 - 165.11.255.255
|
||||
deny 165.25.0.0 - 165.25.255.255
|
||||
deny 165.143.0.0 - 165.149.255.255
|
||||
deny 165.165.0.0 - 165.165.255.255
|
||||
deny 165.180.0.0 - 165.180.255.255
|
||||
deny 165.233.0.0 - 165.233.255.255
|
||||
deny 166.85.0.0 - 166.85.255.255
|
||||
deny 168.76.0.0 - 168.76.255.255
|
||||
deny 168.80.0.0 - 168.81.255.255
|
||||
deny 168.89.0.0 - 168.89.255.255
|
||||
deny 168.128.0.0 - 168.128.255.255
|
||||
deny 168.142.0.0 - 168.142.255.255
|
||||
deny 168.155.0.0 - 168.155.255.255
|
||||
deny 168.164.0.0 - 168.164.255.255
|
||||
deny 168.167.0.0 - 168.167.255.255
|
||||
deny 168.172.0.0 - 168.172.255.255
|
||||
deny 168.206.0.0 - 168.206.255.255
|
||||
deny 168.209.0.0 - 168.210.255.255
|
||||
deny 169.129.0.0 - 169.129.255.255
|
||||
deny 169.202.0.0 - 169.202.255.255
|
||||
deny 192.33.10.0 - 192.33.10.255
|
||||
deny 192.42.99.0 - 192.42.99.255
|
||||
deny 192.48.253.0 - 192.48.253.255
|
||||
deny 192.68.138.0 - 192.68.138.255
|
||||
deny 192.70.237.0 - 192.70.237.255
|
||||
deny 192.82.142.0 - 192.82.142.255
|
||||
deny 192.84.244.0 - 192.84.244.255
|
||||
deny 192.94.61.0 - 192.94.61.255
|
||||
deny 192.94.210.0 - 192.94.210.255
|
||||
deny 192.94.240.0 - 192.94.240.255
|
||||
deny 192.94.241.0 - 192.94.241.255
|
||||
deny 192.94.246.0 - 192.94.246.255
|
||||
deny 192.96.0.0 - 192.96.255.255
|
||||
deny 192.100.1.0 - 192.100.1.255
|
||||
deny 192.101.142.0 - 192.101.142.255
|
||||
deny 192.102.9.0 - 192.102.9.255
|
||||
deny 192.133.250.0 - 192.133.250.255
|
||||
deny 192.136.55.0 - 192.136.55.255
|
||||
deny 192.136.56.0 - 192.136.56.255
|
||||
deny 192.136.57.0 - 192.136.57.255
|
||||
deny 192.157.190.0 - 192.157.190.255
|
||||
deny 192.188.164.0 - 192.188.167.255
|
||||
deny 192.189.75.0 - 192.189.75.255
|
||||
deny 192.189.139.0 - 192.189.140.255
|
||||
deny 192.231.237.0 - 192.231.237.255
|
||||
deny 192.231.254.0 - 192.231.254.255
|
||||
deny 192.245.148.0 - 192.245.148.255
|
||||
deny 192.251.202.0 - 192.251.202.255
|
||||
deny 198.54.0.0 - 198.54.255.255
|
||||
deny 200.16.8.0 - 200.16.15.255
|
||||
deny 204.12.128.0 - 204.12.143.255
|
||||
deny 204.87.179.0 - 204.87.179.255
|
||||
deny 204.152.14.0 - 204.152.15.255
|
||||
deny 204.235.32.0 - 204.235.43.255
|
||||
deny 205.159.79.0 - 205.159.79.255
|
||||
deny 206.223.136.0 - 206.223.136.255
|
||||
deny 209.203.0.0 - 209.203.63.255
|
||||
deny 209.212.96.0 - 209.212.127.255
|
||||
deny 216.236.176.0 - 216.236.191.255
|
||||
deny 202.123.0.0/19
|
||||
deny 62.8.64.0/19
|
||||
deny 62.12.96.0/19
|
||||
deny 62.24.96.0/19
|
||||
deny 62.61.192.0/18
|
||||
deny 62.68.32.0/19
|
||||
deny 62.68.224.0/19
|
||||
deny 62.114.0.0/16
|
||||
deny 62.117.32.0/19
|
||||
deny 62.135.0.0/17
|
||||
deny 62.139.0.0/16
|
||||
deny 62.140.64.0/18
|
||||
deny 62.173.32.0/19
|
||||
deny 62.193.64.0/18
|
||||
deny 62.193.160.0/19
|
||||
deny 62.240.32.0/19
|
||||
deny 62.240.96.0/19
|
||||
deny 62.241.128.0/19
|
||||
deny 62.251.128.0/17
|
||||
deny 77.220.0.0/19
|
||||
deny 80.67.128.0/20
|
||||
deny 80.72.96.0/20
|
||||
deny 80.75.160.0/19
|
||||
deny 80.87.64.0/19
|
||||
deny 80.88.0.0/20
|
||||
deny 80.95.0.0/20
|
||||
deny 80.240.192.0/20
|
||||
deny 80.246.0.0/20
|
||||
deny 80.248.0.0/20
|
||||
deny 80.248.64.0/20
|
||||
deny 80.249.64.0/20
|
||||
deny 80.250.32.0/20
|
||||
deny 81.4.0.0/18
|
||||
deny 81.10.0.0/17
|
||||
deny 81.21.96.0/20
|
||||
deny 81.22.64.0/19
|
||||
deny 81.26.64.0/20
|
||||
deny 81.29.96.0/20
|
||||
deny 81.91.224.0/20
|
||||
deny 81.192.0.0/16
|
||||
deny 82.101.128.0/18
|
||||
deny 82.128.0.0/17
|
||||
deny 82.129.128.0/17
|
||||
deny 82.151.64.0/19
|
||||
deny 82.201.128.0/17
|
||||
deny 84.36.0.0/16
|
||||
deny 84.233.0.0/17
|
||||
deny 87.255.96.0/19
|
||||
deny 193.95.0.0/17
|
||||
deny 193.108.214.0/24
|
||||
deny 193.108.252.0/22
|
||||
deny 193.189.64.0 - 193.189.65.255
|
||||
deny 193.194.1.0 - 193.194.5.255
|
||||
deny 193.194.32.0 - 193.194.95.255
|
||||
deny 193.227.0.0/18
|
||||
deny 194.6.224.0/24
|
||||
deny 194.79.96.0/19
|
||||
deny 194.204.192.0/18
|
||||
deny 195.24.192.0/19
|
||||
deny 195.43.0.0/19
|
||||
deny 195.166.224.0/19
|
||||
deny 195.202.64.0/19
|
||||
deny 195.246.32.0/19
|
||||
deny 212.0.128.0/19
|
||||
deny 212.12.224.0/19
|
||||
deny 212.22.160.0/19
|
||||
deny 212.49.64.0/19
|
||||
deny 212.52.128.0/19
|
||||
deny 212.60.64.0/19
|
||||
deny 212.85.192.0/19
|
||||
deny 212.88.96.0/19
|
||||
deny 212.96.0.0/19
|
||||
deny 212.100.64.0/19
|
||||
deny 212.103.160.0/19
|
||||
deny 212.122.224.0/19
|
||||
deny 212.217.0.0/17
|
||||
deny 213.55.64.0/18
|
||||
deny 213.131.64.0/19
|
||||
deny 213.136.96.0/19
|
||||
deny 213.147.64.0/19
|
||||
deny 213.150.96.0/19
|
||||
deny 213.150.160.0 - 213.150.223.255
|
||||
deny 213.152.64.0/19
|
||||
deny 213.154.32.0 - 213.154.95.255
|
||||
deny 213.158.160.0/19
|
||||
deny 213.172.128.0/19
|
||||
deny 213.179.160.0/19
|
||||
deny 213.181.224.0/19
|
||||
deny 213.193.32.0/19
|
||||
deny 213.212.192.0/18
|
||||
deny 213.247.0.0/19
|
||||
deny 213.255.128.0/19
|
||||
deny 217.14.80.0/20
|
||||
deny 217.20.224.0/20
|
||||
deny 217.21.112.0/20
|
||||
deny 217.29.128.0/20
|
||||
deny 217.29.208.0/20
|
||||
deny 217.52.0.0/14
|
||||
deny 217.64.96.0/20
|
||||
deny 217.77.64.0/20
|
||||
deny 217.78.64.0/20
|
||||
deny 217.117.0.0/20
|
||||
deny 217.139.0.0/16
|
||||
deny 217.170.144.0/20
|
||||
deny 217.199.144.0/20
|
||||
deny 129.0.0.0/16
|
||||
deny 129.18.0.0/16
|
||||
deny 129.45.0.0/16
|
||||
deny 129.56.0.0/16
|
||||
deny 129.122.0.0/16
|
||||
deny 129.140.0.0/16
|
||||
deny 129.205.0.0/16
|
||||
deny 129.232.0.0/16
|
||||
deny 137.63.0.0 - 137.64.255.255
|
||||
deny 137.115.0.0/16
|
||||
deny 137.171.0.0/16
|
||||
deny 137.196.0.0/16
|
||||
deny 137.255.0.0/16
|
||||
deny 155.0.0.0/16
|
||||
deny 155.11.0.0 - 155.12.255.255
|
||||
deny 155.89.0.0/16
|
||||
deny 155.93.0.0/16
|
||||
deny 155.196.0.0/16
|
||||
deny 155.251.0.0/16
|
||||
deny 155.255.0.0 - 156.0.255.255
|
||||
deny 156.38.0.0/16
|
||||
deny 156.155.0.0 - 156.255.255.255
|
||||
deny 160.0.0.0/16
|
||||
deny 160.77.0.0/16
|
||||
deny 160.89.0.0 - 160.90.255.255
|
||||
deny 160.105.0.0/16
|
||||
deny 160.113.0.0/16
|
||||
deny 160.152.0.0/16
|
||||
deny 160.154.0.0 - 160.179.255.255
|
||||
deny 160.181.0.0 - 160.184.255.255
|
||||
deny 160.224.0.0 - 160.226.255.255
|
||||
deny 160.242.0.0/16
|
||||
deny 160.255.0.0/16
|
||||
deny 165.0.0.0/16
|
||||
deny 165.16.0.0/16
|
||||
deny 165.49.0.0 - 165.63.255.255
|
||||
deny 165.73.0.0/16
|
||||
deny 165.90.0.0/16
|
||||
deny 165.169.0.0/16
|
||||
deny 165.210.0.0/15
|
||||
deny 165.255.0.0/16
|
||||
deny 168.211.0.0 - 168.211.255.255
|
||||
deny 168.253.0.0/16
|
||||
deny 169.0.0.0/15
|
||||
deny 169.159.0.0/16
|
||||
deny 169.239.0.0/16
|
||||
deny 169.255.0.0/16
|
||||
deny 192.109.242.0/24
|
||||
2
tests/fixtures/ta_constraints/local-custom-allow.constraints
vendored
Normal file
2
tests/fixtures/ta_constraints/local-custom-allow.constraints
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
# local baseline-v1: the single valid ROA EE certificate has this IPv4 block
|
||||
allow 203.0.113.0/24
|
||||
3
tests/fixtures/ta_constraints/local-custom-deny.constraints
vendored
Normal file
3
tests/fixtures/ta_constraints/local-custom-deny.constraints
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
# local baseline-v1 negative control: deny wins over a covering allow rule
|
||||
allow 0.0.0.0/0
|
||||
deny 203.0.113.0/24
|
||||
12
tests/fixtures/ta_constraints/ripe-ncc-afrinic-deny.constraints
vendored
Normal file
12
tests/fixtures/ta_constraints/ripe-ncc-afrinic-deny.constraints
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
# Remote RIPE TA soak policy for feature #151.
|
||||
#
|
||||
# Start permissive for every INR kind, then carve out several /8 blocks
|
||||
# allocated to AFRINIC. These denies should not match normal RIPE NCC
|
||||
# EE resources, while exercising deny-overrides-allow in a live run.
|
||||
allow 0.0.0.0/0
|
||||
allow ::/0
|
||||
allow 0 - 4294967295
|
||||
|
||||
deny 41.0.0.0/8
|
||||
deny 102.0.0.0/8
|
||||
deny 105.0.0.0/8
|
||||
Loading…
x
Reference in New Issue
Block a user