20260707 支持本地HTTP root证书
This commit is contained in:
parent
b0f76738e5
commit
63ac3ee254
19
src/cli.rs
19
src/cli.rs
@ -163,6 +163,7 @@ pub struct CliArgs {
|
||||
pub rsync_command: Option<PathBuf>,
|
||||
|
||||
pub http_timeout_secs: u64,
|
||||
pub http_root_cert_paths: Vec<PathBuf>,
|
||||
pub rsync_timeout_secs: u64,
|
||||
pub rsync_mirror_root: Option<PathBuf>,
|
||||
pub rsync_scope_policy: RsyncScopePolicy,
|
||||
@ -254,6 +255,7 @@ Options:
|
||||
--disable-rrdp Disable RRDP and synchronize only via rsync
|
||||
--rsync-command <path> Use this rsync command instead of the default rsync binary
|
||||
--http-timeout-secs <n> HTTP fetch timeout seconds (default: 20)
|
||||
--http-root-cert <path> Extra PEM root certificate trusted by HTTPS fetches (repeatable)
|
||||
--rsync-timeout-secs <n> rsync I/O timeout seconds (default: 60)
|
||||
--rsync-mirror-root <path> Persist rsync mirrors under this directory (default: disabled)
|
||||
--rsync-scope <policy> rsync scope policy: host, publication-point, or module-root (default: module-root)
|
||||
@ -313,6 +315,7 @@ pub fn parse_args(argv: &[String]) -> Result<CliArgs, String> {
|
||||
let mut disable_rrdp: bool = false;
|
||||
let mut rsync_command: Option<PathBuf> = None;
|
||||
let mut http_timeout_secs: u64 = 30;
|
||||
let mut http_root_cert_paths: Vec<PathBuf> = Vec::new();
|
||||
let mut rsync_timeout_secs: u64 = 30;
|
||||
let mut rsync_mirror_root: Option<PathBuf> = None;
|
||||
let mut rsync_scope_policy = RsyncScopePolicy::default();
|
||||
@ -636,6 +639,11 @@ pub fn parse_args(argv: &[String]) -> Result<CliArgs, String> {
|
||||
.parse::<u64>()
|
||||
.map_err(|_| format!("invalid --http-timeout-secs: {v}"))?;
|
||||
}
|
||||
"--http-root-cert" => {
|
||||
i += 1;
|
||||
let v = argv.get(i).ok_or("--http-root-cert requires a value")?;
|
||||
http_root_cert_paths.push(PathBuf::from(v));
|
||||
}
|
||||
"--rsync-timeout-secs" => {
|
||||
i += 1;
|
||||
let v = argv.get(i).ok_or("--rsync-timeout-secs requires a value")?;
|
||||
@ -977,6 +985,7 @@ pub fn parse_args(argv: &[String]) -> Result<CliArgs, String> {
|
||||
disable_rrdp,
|
||||
rsync_command,
|
||||
http_timeout_secs,
|
||||
http_root_cert_paths,
|
||||
rsync_timeout_secs,
|
||||
rsync_mirror_root,
|
||||
rsync_scope_policy,
|
||||
@ -2001,6 +2010,14 @@ pub fn run(argv: &[String]) -> Result<(), String> {
|
||||
let validation_time = args
|
||||
.validation_time
|
||||
.unwrap_or_else(time::OffsetDateTime::now_utc);
|
||||
let http_root_certificates_pem = args
|
||||
.http_root_cert_paths
|
||||
.iter()
|
||||
.map(|path| {
|
||||
std::fs::read(path)
|
||||
.map_err(|e| format!("read HTTP root certificate failed: {}: {e}", path.display()))
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
let store = if args.raw_store_db.is_some() || args.repo_bytes_db.is_some() {
|
||||
Arc::new(
|
||||
@ -2221,6 +2238,7 @@ pub fn run(argv: &[String]) -> Result<(), String> {
|
||||
} else if let Some(dir) = args.rsync_local_dir.as_ref() {
|
||||
let http = BlockingHttpFetcher::new(HttpFetcherConfig {
|
||||
timeout: std::time::Duration::from_secs(args.http_timeout_secs.max(1)),
|
||||
extra_root_certificates_pem: http_root_certificates_pem.clone(),
|
||||
..HttpFetcherConfig::default()
|
||||
})
|
||||
.map_err(|e| e.to_string())?;
|
||||
@ -2239,6 +2257,7 @@ pub fn run(argv: &[String]) -> Result<(), String> {
|
||||
} else {
|
||||
let http = BlockingHttpFetcher::new(HttpFetcherConfig {
|
||||
timeout: std::time::Duration::from_secs(args.http_timeout_secs.max(1)),
|
||||
extra_root_certificates_pem: http_root_certificates_pem.clone(),
|
||||
..HttpFetcherConfig::default()
|
||||
})
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
@ -289,6 +289,29 @@ fn parse_accepts_host_rsync_scope_policy() {
|
||||
assert_eq!(args.rsync_scope_policy, RsyncScopePolicy::Host);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_accepts_repeatable_http_root_cert_paths() {
|
||||
let argv = vec![
|
||||
"rpki".to_string(),
|
||||
"--db".to_string(),
|
||||
"db".to_string(),
|
||||
"--tal-url".to_string(),
|
||||
"https://example.test/x.tal".to_string(),
|
||||
"--http-root-cert".to_string(),
|
||||
"local-ca-1.pem".to_string(),
|
||||
"--http-root-cert".to_string(),
|
||||
"local-ca-2.pem".to_string(),
|
||||
];
|
||||
let args = parse_args(&argv).expect("parse args");
|
||||
assert_eq!(
|
||||
args.http_root_cert_paths,
|
||||
vec![
|
||||
PathBuf::from("local-ca-1.pem"),
|
||||
PathBuf::from("local-ca-2.pem")
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_rejects_invalid_rsync_scope_policy() {
|
||||
let argv = vec![
|
||||
|
||||
@ -29,6 +29,8 @@ pub struct HttpFetcherConfig {
|
||||
/// Larger timeout used for RRDP snapshot / delta bodies.
|
||||
pub large_body_timeout: Duration,
|
||||
pub user_agent: String,
|
||||
/// Extra PEM trust anchors for HTTPS transport tests or private RRDP endpoints.
|
||||
pub extra_root_certificates_pem: Vec<Vec<u8>>,
|
||||
}
|
||||
|
||||
impl Default for HttpFetcherConfig {
|
||||
@ -38,6 +40,7 @@ impl Default for HttpFetcherConfig {
|
||||
timeout: Duration::from_secs(30),
|
||||
large_body_timeout: Duration::from_secs(180),
|
||||
user_agent: "rpki-dev/0.1 (stage2)".to_string(),
|
||||
extra_root_certificates_pem: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -61,24 +64,30 @@ impl BlockingHttpFetcher {
|
||||
let short_timeout = config.timeout;
|
||||
let large_body_timeout = std::cmp::max(config.large_body_timeout, config.timeout);
|
||||
let connect_timeout = std::cmp::min(config.connect_timeout, config.timeout);
|
||||
let short_client = Client::builder()
|
||||
.connect_timeout(connect_timeout)
|
||||
.timeout(config.timeout)
|
||||
.user_agent(config.user_agent.clone())
|
||||
.build()
|
||||
.map_err(|e| e.to_string())?;
|
||||
let large_body_client = Client::builder()
|
||||
.connect_timeout(connect_timeout)
|
||||
.timeout(large_body_timeout)
|
||||
.user_agent(config.user_agent)
|
||||
.build()
|
||||
.map_err(|e| e.to_string())?;
|
||||
let retry_short_client = Client::builder()
|
||||
.connect_timeout(Duration::from_secs(1))
|
||||
.timeout(Duration::from_secs(1))
|
||||
.user_agent("rpki-dev/0.1 (stage2)")
|
||||
.build()
|
||||
.map_err(|e| e.to_string())?;
|
||||
let short_client = Self::client_builder(
|
||||
&config,
|
||||
connect_timeout,
|
||||
config.timeout,
|
||||
config.user_agent.clone(),
|
||||
)?
|
||||
.build()
|
||||
.map_err(|e| e.to_string())?;
|
||||
let large_body_client = Self::client_builder(
|
||||
&config,
|
||||
connect_timeout,
|
||||
large_body_timeout,
|
||||
config.user_agent.clone(),
|
||||
)?
|
||||
.build()
|
||||
.map_err(|e| e.to_string())?;
|
||||
let retry_short_client = Self::client_builder(
|
||||
&config,
|
||||
Duration::from_secs(1),
|
||||
Duration::from_secs(1),
|
||||
"rpki-dev/0.1 (stage2)".to_string(),
|
||||
)?
|
||||
.build()
|
||||
.map_err(|e| e.to_string())?;
|
||||
Ok(Self {
|
||||
short_client,
|
||||
large_body_client,
|
||||
@ -88,11 +97,29 @@ impl BlockingHttpFetcher {
|
||||
})
|
||||
}
|
||||
|
||||
fn client_builder(
|
||||
config: &HttpFetcherConfig,
|
||||
connect_timeout: Duration,
|
||||
timeout: Duration,
|
||||
user_agent: String,
|
||||
) -> Result<reqwest::blocking::ClientBuilder, String> {
|
||||
let mut builder = Client::builder()
|
||||
.connect_timeout(connect_timeout)
|
||||
.timeout(timeout)
|
||||
.user_agent(user_agent);
|
||||
for (idx, pem) in config.extra_root_certificates_pem.iter().enumerate() {
|
||||
let certificate = reqwest::Certificate::from_pem(pem)
|
||||
.map_err(|e| format!("parse HTTP root certificate #{idx} failed: {e}"))?;
|
||||
builder = builder.add_root_certificate(certificate);
|
||||
}
|
||||
Ok(builder)
|
||||
}
|
||||
|
||||
pub fn fetch_bytes(&self, uri: &str) -> Result<Vec<u8>, String> {
|
||||
let started = std::time::Instant::now();
|
||||
let (client, timeout_profile, timeout_value) = self.client_for_uri(uri);
|
||||
let resp = client.get(uri).send().map_err(|e| {
|
||||
let msg = format!("http request failed: {e}");
|
||||
let msg = format!("http request failed: {e:?}");
|
||||
crate::progress_log::emit(
|
||||
"http_fetch_failed",
|
||||
serde_json::json!({
|
||||
@ -222,7 +249,7 @@ impl Fetcher for BlockingHttpFetcher {
|
||||
let started = std::time::Instant::now();
|
||||
let (client, timeout_profile, timeout_value) = self.client_for_uri(uri);
|
||||
let resp = client.get(uri).send().map_err(|e| {
|
||||
let msg = format!("http request failed: {e}");
|
||||
let msg = format!("http request failed: {e:?}");
|
||||
crate::progress_log::emit(
|
||||
"http_fetch_failed",
|
||||
serde_json::json!({
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user