Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Support a NEXTCLADE_EXTRA_CA_CERTS environment variable and --extra-ca-certs option #1527

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 19 additions & 7 deletions Cargo.lock

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

6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,11 @@ regex = "=1.8.4"
reqwest = { version = "=0.12.8", default-features = false, features = ["blocking", "deflate", "gzip", "brotli", "socks", "rustls-tls"] }
rstest = "=0.17.0"
rstest_reuse = "=0.5.0"
rustls-platform-verifier = "=0.3.4"
rustls = { version = "=0.23.14", default-features = false, features = ["ring", "logging", "std", "tls12"] }
rustls-platform-verifier = { git = "https://github.com/rustls/rustls-platform-verifier.git", rev = "ca87571fe47730665917b8c29f14a3989f3cbe57" }
rustls-pemfile = "=2.2.0"
rustls-pki-types = "=1.9.0"
rustls-webpki = { version = "=0.102.8", features = ["ring"] }
schemars = { version = "=0.8.12", features = ["chrono", "either", "enumset", "indexmap"] }
semver = { version = "=1.0.17", features = ["serde"] }
serde = { version = "=1.0.164", features = ["derive"] }
Expand Down
4 changes: 4 additions & 0 deletions packages/nextclade-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,11 @@ pretty_assertions = { workspace = true }
rayon = { workspace = true }
regex = { workspace = true }
reqwest = { workspace = true }
rustls = { workspace = true }
rustls-platform-verifier = { workspace = true }
rustls-pemfile = { workspace = true }
rustls-pki-types = { workspace = true }
rustls-webpki = { workspace = true }
schemars = { workspace = true }
semver = { workspace = true }
serde = { workspace = true }
Expand Down
45 changes: 42 additions & 3 deletions packages/nextclade-cli/src/io/http_client.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
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::{Method, Proxy};
use rustls_platform_verifier;
use rustls::ClientConfig;
use rustls_pemfile;
use rustls_pki_types::CertificateDer;
use rustls_platform_verifier::Verifier;
use std::env;
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::sync::Arc;
use std::time::Duration;
use url::Url;

Expand All @@ -27,6 +34,13 @@ pub struct ProxyConfig {
#[clap(long)]
#[clap(value_hint = ValueHint::Other)]
pub proxy_pass: Option<String>,

/// Path to extra CA certificates
///
/// You can also provide path to CA certificates in environment variable `NEXTCLADE_EXTRA_CA_CERTS`. The argument takes precedence over environment variable if both are provided.
#[clap(long)]
#[clap(value_hint = ValueHint::Other)]
pub extra_ca_certs: Option<PathBuf>,
}

pub struct HttpClient {
Expand Down Expand Up @@ -62,8 +76,16 @@ impl HttpClient {

let user_agent = format!("{} {}", this_package_name(), this_package_version_str());

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());

let tls_config = ClientConfig::builder()
.dangerous() // …but the rustls_platform_verifier::Verifier is safe
.with_custom_certificate_verifier(Arc::new(Verifier::new_with_extra_roots(extra_ca_certs(extra_ca_certs_filepath)?)?))
.with_no_client_auth();
ivan-aksamentov marked this conversation as resolved.
Show resolved Hide resolved

let client = client_builder
.use_preconfigured_tls(rustls_platform_verifier::tls_config())
.use_preconfigured_tls(tls_config)
.connection_verbose(verbose)
.connect_timeout(Some(Duration::from_secs(60)))
.user_agent(user_agent)
Expand Down Expand Up @@ -112,3 +134,20 @@ impl HttpClient {
Ok(content)
}
}

fn extra_ca_certs<'a>(
extra_ca_certs_filepath: Option<impl AsRef<Path>>,
) -> Result<impl IntoIterator<Item = CertificateDer<'a>>, Report> {
extra_ca_certs_filepath.map_or_else(
|| Ok(vec![]),
|filename| {
let mut file = open_file_or_stdin(&Some(filename))?;

let certs = rustls_pemfile::certs(&mut file)
.map(|c| c.wrap_err("When parsing an extra CA certificate"))
ivan-aksamentov marked this conversation as resolved.
Show resolved Hide resolved
.collect::<Result<Vec<_>, Report>>()?;

Ok(certs)
},
)
}
Loading