mirror of
https://github.com/cargo-bins/cargo-binstall.git
synced 2025-04-20 20:48:43 +00:00
Support adding root cert via env CARGO_HTTP_CAINFO
, SSL_CERT_{FILE, PATH}
(#774)
* Support for custom root cert in `binstalk_downloader::remote::Client` * Support adding root cert via env `CARGO_HTTP_CAINFO`, `SSL_CERT_{FILE, PATH}` Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com>
This commit is contained in:
parent
647f02bb59
commit
9635e05d6c
3 changed files with 101 additions and 5 deletions
|
@ -4,7 +4,11 @@ use binstalk::{
|
||||||
errors::BinstallError,
|
errors::BinstallError,
|
||||||
fetchers::{Fetcher, GhCrateMeta, QuickInstall},
|
fetchers::{Fetcher, GhCrateMeta, QuickInstall},
|
||||||
get_desired_targets,
|
get_desired_targets,
|
||||||
helpers::{jobserver_client::LazyJobserverClient, remote::Client, tasks::AutoAbortJoinHandle},
|
helpers::{
|
||||||
|
jobserver_client::LazyJobserverClient,
|
||||||
|
remote::{Certificate, Client},
|
||||||
|
tasks::AutoAbortJoinHandle,
|
||||||
|
},
|
||||||
ops::{
|
ops::{
|
||||||
self,
|
self,
|
||||||
resolve::{CrateName, Resolution, ResolutionFetch, VersionReqExt},
|
resolve::{CrateName, Resolution, ResolutionFetch, VersionReqExt},
|
||||||
|
@ -73,6 +77,15 @@ pub async fn install_crates(args: Args, jobserver_client: LazyJobserverClient) -
|
||||||
args.min_tls_version.map(|v| v.into()),
|
args.min_tls_version.map(|v| v.into()),
|
||||||
Duration::from_millis(rate_limit.duration.get()),
|
Duration::from_millis(rate_limit.duration.get()),
|
||||||
rate_limit.request_count,
|
rate_limit.request_count,
|
||||||
|
["CARGO_HTTP_CAINFO", "SSL_CERT_FILE", "SSL_CERT_PATH"]
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|env_name| match Certificate::from_env(env_name) {
|
||||||
|
Ok(option) => option,
|
||||||
|
Err(err) => {
|
||||||
|
warn!("Failed to load root certificate specified by env {env_name}: {err}",);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}),
|
||||||
)
|
)
|
||||||
.map_err(BinstallError::from)?;
|
.map_err(BinstallError::from)?;
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,9 @@ pub use url::Url;
|
||||||
mod delay_request;
|
mod delay_request;
|
||||||
use delay_request::DelayRequest;
|
use delay_request::DelayRequest;
|
||||||
|
|
||||||
|
mod certificate;
|
||||||
|
pub use certificate::{Certificate, OpenCertificateError};
|
||||||
|
|
||||||
const MAX_RETRY_DURATION: Duration = Duration::from_secs(120);
|
const MAX_RETRY_DURATION: Duration = Duration::from_secs(120);
|
||||||
const MAX_RETRY_COUNT: u8 = 3;
|
const MAX_RETRY_COUNT: u8 = 3;
|
||||||
const DEFAULT_RETRY_DURATION_FOR_RATE_LIMIT: Duration = Duration::from_millis(200);
|
const DEFAULT_RETRY_DURATION_FOR_RATE_LIMIT: Duration = Duration::from_millis(200);
|
||||||
|
@ -66,23 +69,30 @@ impl Client {
|
||||||
min_tls: Option<tls::Version>,
|
min_tls: Option<tls::Version>,
|
||||||
per: Duration,
|
per: Duration,
|
||||||
num_request: NonZeroU64,
|
num_request: NonZeroU64,
|
||||||
|
certificates: impl IntoIterator<Item = Certificate>,
|
||||||
) -> Result<Self, Error> {
|
) -> Result<Self, Error> {
|
||||||
fn inner(
|
fn inner(
|
||||||
user_agent: &str,
|
user_agent: &str,
|
||||||
min_tls: Option<tls::Version>,
|
min_tls: Option<tls::Version>,
|
||||||
per: Duration,
|
per: Duration,
|
||||||
num_request: NonZeroU64,
|
num_request: NonZeroU64,
|
||||||
|
certificates: &mut dyn Iterator<Item = Certificate>,
|
||||||
) -> Result<Client, Error> {
|
) -> Result<Client, Error> {
|
||||||
let tls_ver = min_tls
|
let tls_ver = min_tls
|
||||||
.map(|tls| tls.max(DEFAULT_MIN_TLS))
|
.map(|tls| tls.max(DEFAULT_MIN_TLS))
|
||||||
.unwrap_or(DEFAULT_MIN_TLS);
|
.unwrap_or(DEFAULT_MIN_TLS);
|
||||||
|
|
||||||
let client = reqwest::ClientBuilder::new()
|
let mut builder = reqwest::ClientBuilder::new()
|
||||||
.user_agent(user_agent)
|
.user_agent(user_agent)
|
||||||
.https_only(true)
|
.https_only(true)
|
||||||
.min_tls_version(tls_ver)
|
.min_tls_version(tls_ver)
|
||||||
.tcp_nodelay(false)
|
.tcp_nodelay(false);
|
||||||
.build()?;
|
|
||||||
|
for certificate in certificates {
|
||||||
|
builder = builder.add_root_certificate(certificate.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
let client = builder.build()?;
|
||||||
|
|
||||||
Ok(Client(Arc::new(Inner {
|
Ok(Client(Arc::new(Inner {
|
||||||
client: client.clone(),
|
client: client.clone(),
|
||||||
|
@ -94,7 +104,13 @@ impl Client {
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
inner(user_agent.as_ref(), min_tls, per, num_request)
|
inner(
|
||||||
|
user_agent.as_ref(),
|
||||||
|
min_tls,
|
||||||
|
per,
|
||||||
|
num_request,
|
||||||
|
&mut certificates.into_iter(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return inner reqwest client.
|
/// Return inner reqwest client.
|
||||||
|
|
67
crates/binstalk-downloader/src/remote/certificate.rs
Normal file
67
crates/binstalk-downloader/src/remote/certificate.rs
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
use std::{env, ffi::OsStr, fs, io, path::Path};
|
||||||
|
|
||||||
|
use compact_str::CompactString;
|
||||||
|
use reqwest::tls;
|
||||||
|
use thiserror::Error as ThisError;
|
||||||
|
|
||||||
|
use super::ReqwestError;
|
||||||
|
|
||||||
|
#[derive(Debug, ThisError)]
|
||||||
|
pub enum OpenCertificateError {
|
||||||
|
#[error(transparent)]
|
||||||
|
Reqwest(#[from] ReqwestError),
|
||||||
|
|
||||||
|
#[error(transparent)]
|
||||||
|
Io(#[from] io::Error),
|
||||||
|
|
||||||
|
#[error("Expected extension .pem or .der, but found {0:#?}")]
|
||||||
|
UnknownExtensions(Option<CompactString>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Certificate(pub(super) tls::Certificate);
|
||||||
|
|
||||||
|
impl Certificate {
|
||||||
|
/// Open Certificate with path specified by the environment variable `name`
|
||||||
|
pub fn from_env(name: impl AsRef<OsStr>) -> Result<Option<Self>, OpenCertificateError> {
|
||||||
|
Self::from_env_inner(name.as_ref())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_env_inner(name: &OsStr) -> Result<Option<Self>, OpenCertificateError> {
|
||||||
|
env::var_os(name)
|
||||||
|
.map(|value| Self::open_inner(Path::new(&value)))
|
||||||
|
.transpose()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Open Certificate on disk and automatically detect its format based on
|
||||||
|
/// its extension.
|
||||||
|
pub fn open(path: impl AsRef<Path>) -> Result<Self, OpenCertificateError> {
|
||||||
|
Self::open_inner(path.as_ref())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn open_inner(path: &Path) -> Result<Self, OpenCertificateError> {
|
||||||
|
let ext = path.extension();
|
||||||
|
|
||||||
|
let f = if ext == Some(OsStr::new("pem")) {
|
||||||
|
Self::from_pem
|
||||||
|
} else if ext == Some(OsStr::new("der")) {
|
||||||
|
Self::from_der
|
||||||
|
} else {
|
||||||
|
return Err(OpenCertificateError::UnknownExtensions(
|
||||||
|
ext.map(|os_str| os_str.to_string_lossy().into()),
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(f(fs::read(path)?)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a Certificate from a binary DER encoded certificate
|
||||||
|
pub fn from_der(der: impl AsRef<[u8]>) -> Result<Self, ReqwestError> {
|
||||||
|
tls::Certificate::from_der(der.as_ref()).map(Self)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a Certificate from a PEM encoded certificate
|
||||||
|
pub fn from_pem(pem: impl AsRef<[u8]>) -> Result<Self, ReqwestError> {
|
||||||
|
tls::Certificate::from_pem(pem.as_ref()).map(Self)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue