From 175e31b66ef22f02e237c53e7df4b6d92896e99b Mon Sep 17 00:00:00 2001 From: Thomas Sibley Date: Wed, 16 Oct 2024 09:13:53 -0700 Subject: [PATCH] feat(cli): Support a NEXTCLADE_EXTRA_CA_CERTS environment variable and --extra-ca-certs option Allows adding additional CA certificates to the trust store specifically for Nextclade, for when modifying the system's trust store isn't desirable/possible. Works across all platforms. An option may be preferred to an env var by some users or in some invocations. It also provides a convenient place for documentation of default CA cert handling and point of discovery for users. Co-authored-by: ivan-aksamentov Supersedes: Related-to: Related-to: --- packages/nextclade-cli/src/io/http_client.rs | 35 +++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/packages/nextclade-cli/src/io/http_client.rs b/packages/nextclade-cli/src/io/http_client.rs index 8e3369a06..467dfb676 100644 --- a/packages/nextclade-cli/src/io/http_client.rs +++ b/packages/nextclade-cli/src/io/http_client.rs @@ -1,10 +1,14 @@ use clap::{Parser, ValueHint}; -use eyre::Report; +use eyre::{Report, WrapErr}; use log::info; +use nextclade::io::file::open_file_or_stdin; use nextclade::make_internal_error; use nextclade::utils::info::{this_package_name, this_package_version_str}; use reqwest::blocking::Client; +use reqwest::tls::Certificate; use reqwest::{Method, Proxy}; +use std::env; +use std::path::{Path, PathBuf}; use std::str::FromStr; use std::time::Duration; use url::Url; @@ -26,6 +30,15 @@ pub struct ProxyConfig { #[clap(long)] #[clap(value_hint = ValueHint::Other)] pub proxy_pass: Option, + + /// Path to extra CA certificates as a PEM bundle. + /// + /// You can also provide the path to CA certificates in the environment variable `NEXTCLADE_EXTRA_CA_CERTS`. The argument takes precedence over the environment variable if both are provided. + /// + /// Default CA certificates are those obtained from the platform/OS-level trust store plus those from a baked-in copy of Mozilla's common CA trust store. You can override the certs obtained from the platform trust store by setting `SSL_CERT_FILE` or `SSL_CERT_DIR`. Filenames in the latter must be hashed in the style of OpenSSL's `c_rehash` utility. + #[clap(long)] + #[clap(value_hint = ValueHint::Other)] + pub extra_ca_certs: Option, } pub struct HttpClient { @@ -59,6 +72,13 @@ impl HttpClient { client_builder }; + let extra_ca_certs_filepath = env::var_os("NEXTCLADE_EXTRA_CA_CERTS").map(PathBuf::from); + let extra_ca_certs_filepath = proxy_conf.extra_ca_certs.as_ref().or(extra_ca_certs_filepath.as_ref()); + + for cert in extra_ca_certs(extra_ca_certs_filepath)?.into_iter() { + client_builder = client_builder.add_root_certificate(cert); + } + let user_agent = format!("{} {}", this_package_name(), this_package_version_str()); let client = client_builder @@ -110,3 +130,16 @@ impl HttpClient { Ok(content) } } + +fn extra_ca_certs(extra_ca_certs_filepath: Option>) -> Result, Report> { + extra_ca_certs_filepath.map_or_else( + || Ok(vec![]), + |filename| { + let mut pem = vec![]; + + open_file_or_stdin(&Some(filename))?.read_to_end(&mut pem)?; + + Certificate::from_pem_bundle(&pem).wrap_err("While reading PEM bundle") + }, + ) +}