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(cli): Obtain CA certificates from platform trust store; add NEXTCLADE_EXTRA_CA_CERTS / --extra-ca-certs #1536

Merged
merged 3 commits into from
Oct 16, 2024
Merged
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
111 changes: 24 additions & 87 deletions Cargo.lock

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

3 changes: 1 addition & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,9 @@ owo-colors = "=3.5.0"
pretty_assertions = "=1.3.0"
rayon = "=1.7.0"
regex = "=1.8.4"
reqwest = { version = "=0.12.8", default-features = false, features = ["blocking", "deflate", "gzip", "brotli", "socks", "rustls-tls"] }
reqwest = { version = "=0.12.8", default-features = false, features = ["blocking", "deflate", "gzip", "brotli", "socks", "rustls-tls-native-roots", "rustls-tls-webpki-roots"] }
rstest = "=0.17.0"
rstest_reuse = "=0.5.0"
rustls-platform-verifier = "=0.3.4"
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
3 changes: 3 additions & 0 deletions docs/user/nextclade-cli/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ For short help type: `nextclade -h`, for extended help type: `nextclade --help`.
* `-x`, `--proxy <PROXY>` — Pass all traffic over proxy server. HTTP, HTTPS, and SOCKS5 proxies are supported
* `--proxy-user <PROXY_USER>` — Username for basic authentication on proxy server, if applicable. Only valid when `--proxy` is also supplied. `--proxy-user` and `--proxy-pass` must be either both specified or both omitted
* `--proxy-pass <PROXY_PASS>` — Password for basic authentication on proxy server, if applicable. Only valid when `--proxy` is also supplied. `--proxy-user` and `--proxy-pass` must be either both specified or both omitted
* `--extra-ca-certs <EXTRA_CA_CERTS>` — Path to extra CA certificates as a PEM bundle



Expand All @@ -255,6 +256,7 @@ For short help type: `nextclade -h`, for extended help type: `nextclade --help`.
* `-x`, `--proxy <PROXY>` — Pass all traffic over proxy server. HTTP, HTTPS, and SOCKS5 proxies are supported
* `--proxy-user <PROXY_USER>` — Username for basic authentication on proxy server, if applicable. Only valid when `--proxy` is also supplied. `--proxy-user` and `--proxy-pass` must be either both specified or both omitted
* `--proxy-pass <PROXY_PASS>` — Password for basic authentication on proxy server, if applicable. Only valid when `--proxy` is also supplied. `--proxy-user` and `--proxy-pass` must be either both specified or both omitted
* `--extra-ca-certs <EXTRA_CA_CERTS>` — Path to extra CA certificates as a PEM bundle



Expand Down Expand Up @@ -295,6 +297,7 @@ For short help type: `nextclade -h`, for extended help type: `nextclade --help`.
* `-x`, `--proxy <PROXY>` — Pass all traffic over proxy server. HTTP, HTTPS, and SOCKS5 proxies are supported
* `--proxy-user <PROXY_USER>` — Username for basic authentication on proxy server, if applicable. Only valid when `--proxy` is also supplied. `--proxy-user` and `--proxy-pass` must be either both specified or both omitted
* `--proxy-pass <PROXY_PASS>` — Password for basic authentication on proxy server, if applicable. Only valid when `--proxy` is also supplied. `--proxy-user` and `--proxy-pass` must be either both specified or both omitted
* `--extra-ca-certs <EXTRA_CA_CERTS>` — Path to extra CA certificates as a PEM bundle



Expand Down
1 change: 0 additions & 1 deletion packages/nextclade-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ pretty_assertions = { workspace = true }
rayon = { workspace = true }
regex = { workspace = true }
reqwest = { workspace = true }
rustls-platform-verifier = { workspace = true }
schemars = { workspace = true }
semver = { workspace = true }
serde = { workspace = true }
Expand Down
37 changes: 34 additions & 3 deletions packages/nextclade-cli/src/io/http_client.rs
Original file line number Diff line number Diff line change
@@ -1,11 +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 rustls_platform_verifier;
use std::env;
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::time::Duration;
use url::Url;
Expand All @@ -27,6 +30,15 @@ pub struct ProxyConfig {
#[clap(long)]
#[clap(value_hint = ValueHint::Other)]
pub proxy_pass: Option<String>,

/// 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<PathBuf>,
}

pub struct HttpClient {
Expand Down Expand Up @@ -60,10 +72,16 @@ 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)? {
client_builder = client_builder.add_root_certificate(cert);
}

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

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

fn extra_ca_certs(extra_ca_certs_filepath: Option<impl AsRef<Path>>) -> Result<Vec<Certificate>, 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")
},
)
}