mirror of
https://github.com/cargo-bins/cargo-binstall.git
synced 2025-04-23 05:58:42 +00:00
Add opt --root-ceritificates
& env BINSTALL_HTTPS_ROOT_CERTS
(#820)
for specifying root ceritificates used for https connnections. And remove old environment variable `CARGO_HTTP_CAINFO`, `SSL_CERT_FILE` and `SSL_CERT_PATH` to avoid accidentally setting them, especially in CI env. Also: - Rm fn `binstalk_downloader::Certificate::from_env` - Enable feature `env` of dep `clap` in `crates/bin` - Add new dep `file-format` v0.14.0 to `crates/bin` - Use `file-format` to determine pem/der file format when loading root certs - Rm fn `binstalk_downloader::Certificate::open` and enum `binstalk_downloader::OpenCertificateError` Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com>
This commit is contained in:
parent
467ba0d854
commit
7bc4d4a5c6
6 changed files with 67 additions and 62 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
@ -361,6 +361,7 @@ dependencies = [
|
||||||
"crates_io_api",
|
"crates_io_api",
|
||||||
"dirs",
|
"dirs",
|
||||||
"embed-resource",
|
"embed-resource",
|
||||||
|
"file-format",
|
||||||
"fs-lock",
|
"fs-lock",
|
||||||
"log",
|
"log",
|
||||||
"miette",
|
"miette",
|
||||||
|
@ -727,6 +728,12 @@ dependencies = [
|
||||||
"instant",
|
"instant",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "file-format"
|
||||||
|
version = "0.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d645737d3dda11cbf14905e9b943a1bd578cdcb751709b581a924ea9b9b18b5c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "filetime"
|
name = "filetime"
|
||||||
version = "0.2.20"
|
version = "0.2.20"
|
||||||
|
|
|
@ -24,9 +24,10 @@ pkg-fmt = "zip"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
binstalk = { path = "../binstalk", version = "0.8.0", default-features = false }
|
binstalk = { path = "../binstalk", version = "0.8.0", default-features = false }
|
||||||
binstalk-manifests = { path = "../binstalk-manifests", version = "0.3.0" }
|
binstalk-manifests = { path = "../binstalk-manifests", version = "0.3.0" }
|
||||||
clap = { version = "4.1.6", features = ["derive"] }
|
clap = { version = "4.1.6", features = ["derive", "env"] }
|
||||||
crates_io_api = { version = "0.8.1", default-features = false }
|
crates_io_api = { version = "0.8.1", default-features = false }
|
||||||
dirs = "4.0.0"
|
dirs = "4.0.0"
|
||||||
|
file-format = { version = "0.14.0", default-features = false }
|
||||||
fs-lock = { version = "0.1.0", path = "../fs-lock" }
|
fs-lock = { version = "0.1.0", path = "../fs-lock" }
|
||||||
log = { version = "0.4.17", features = ["std"] }
|
log = { version = "0.4.17", features = ["std"] }
|
||||||
miette = "5.5.0"
|
miette = "5.5.0"
|
||||||
|
|
|
@ -203,6 +203,11 @@ pub struct Args {
|
||||||
#[clap(help_heading = "Options", long, value_enum, value_name = "VERSION")]
|
#[clap(help_heading = "Options", long, value_enum, value_name = "VERSION")]
|
||||||
pub min_tls_version: Option<TLSVersion>,
|
pub min_tls_version: Option<TLSVersion>,
|
||||||
|
|
||||||
|
/// Specify the root certificates to use for https connnections,
|
||||||
|
/// in addition to default system-wide ones.
|
||||||
|
#[clap(help_heading = "Options", long, env = "BINSTALL_HTTPS_ROOT_CERTS")]
|
||||||
|
pub root_certificates: Vec<PathBuf>,
|
||||||
|
|
||||||
/// Print logs in json format to be parsable.
|
/// Print logs in json format to be parsable.
|
||||||
#[clap(help_heading = "Options", long)]
|
#[clap(help_heading = "Options", long)]
|
||||||
pub json_output: bool,
|
pub json_output: bool,
|
||||||
|
@ -313,7 +318,7 @@ pub fn parse() -> Args {
|
||||||
// Filter extraneous arg when invoked by cargo
|
// Filter extraneous arg when invoked by cargo
|
||||||
// `cargo run -- --help` gives ["target/debug/cargo-binstall", "--help"]
|
// `cargo run -- --help` gives ["target/debug/cargo-binstall", "--help"]
|
||||||
// `cargo binstall --help` gives ["/home/ryan/.cargo/bin/cargo-binstall", "binstall", "--help"]
|
// `cargo binstall --help` gives ["/home/ryan/.cargo/bin/cargo-binstall", "binstall", "--help"]
|
||||||
let mut args: Vec<OsString> = std::env::args_os().collect();
|
let mut args: Vec<OsString> = env::args_os().collect();
|
||||||
let args = if args.get(1).map(|arg| arg == "binstall").unwrap_or_default() {
|
let args = if args.get(1).map(|arg| arg == "binstall").unwrap_or_default() {
|
||||||
// Equivalent to
|
// Equivalent to
|
||||||
//
|
//
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
use std::{fs, path::PathBuf, sync::Arc, time::Duration};
|
use std::{
|
||||||
|
fs,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
sync::Arc,
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
use binstalk::{
|
use binstalk::{
|
||||||
errors::BinstallError,
|
errors::BinstallError,
|
||||||
|
@ -17,6 +22,7 @@ use binstalk::{
|
||||||
};
|
};
|
||||||
use binstalk_manifests::cargo_toml_binstall::PkgOverride;
|
use binstalk_manifests::cargo_toml_binstall::PkgOverride;
|
||||||
use crates_io_api::AsyncClient as CratesIoApiClient;
|
use crates_io_api::AsyncClient as CratesIoApiClient;
|
||||||
|
use file_format::FileFormat;
|
||||||
use log::LevelFilter;
|
use log::LevelFilter;
|
||||||
use miette::{miette, Result, WrapErr};
|
use miette::{miette, Result, WrapErr};
|
||||||
use tokio::task::block_in_place;
|
use tokio::task::block_in_place;
|
||||||
|
@ -77,15 +83,7 @@ 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"]
|
read_root_certs(args.root_certificates),
|
||||||
.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)?;
|
||||||
|
|
||||||
|
@ -180,6 +178,49 @@ pub async fn install_crates(args: Args, jobserver_client: LazyJobserverClient) -
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn do_read_root_cert(path: &Path) -> Result<Option<Certificate>, BinstallError> {
|
||||||
|
use std::io::{Read, Seek};
|
||||||
|
|
||||||
|
let mut file = fs::File::open(path)?;
|
||||||
|
let file_format = FileFormat::from_reader(&mut file)?;
|
||||||
|
|
||||||
|
let open_cert = match file_format {
|
||||||
|
FileFormat::PemCertificate => Certificate::from_pem,
|
||||||
|
FileFormat::DerCertificate => Certificate::from_der,
|
||||||
|
_ => {
|
||||||
|
warn!(
|
||||||
|
"Unable to load {}: Expected pem or der ceritificate but found {file_format}",
|
||||||
|
path.display()
|
||||||
|
);
|
||||||
|
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Move file back to its head
|
||||||
|
file.rewind()?;
|
||||||
|
|
||||||
|
let mut buffer = Vec::with_capacity(200);
|
||||||
|
file.read_to_end(&mut buffer)?;
|
||||||
|
|
||||||
|
open_cert(&buffer).map_err(From::from).map(Some)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_root_certs(root_certificate_paths: Vec<PathBuf>) -> impl Iterator<Item = Certificate> {
|
||||||
|
root_certificate_paths
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|path| match do_read_root_cert(&path) {
|
||||||
|
Ok(optional_cert) => optional_cert,
|
||||||
|
Err(err) => {
|
||||||
|
warn!(
|
||||||
|
"Failed to load root certificate at {}: {err}",
|
||||||
|
path.display()
|
||||||
|
);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Return (install_path, manifests, temp_dir)
|
/// Return (install_path, manifests, temp_dir)
|
||||||
fn compute_paths_and_load_manifests(
|
fn compute_paths_and_load_manifests(
|
||||||
roots: Option<PathBuf>,
|
roots: Option<PathBuf>,
|
||||||
|
|
|
@ -23,7 +23,7 @@ mod delay_request;
|
||||||
use delay_request::DelayRequest;
|
use delay_request::DelayRequest;
|
||||||
|
|
||||||
mod certificate;
|
mod certificate;
|
||||||
pub use certificate::{Certificate, OpenCertificateError};
|
pub use certificate::Certificate;
|
||||||
|
|
||||||
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;
|
||||||
|
|
|
@ -1,60 +1,11 @@
|
||||||
use std::{env, ffi::OsStr, fs, io, path::Path};
|
|
||||||
|
|
||||||
use compact_str::CompactString;
|
|
||||||
use reqwest::tls;
|
use reqwest::tls;
|
||||||
use thiserror::Error as ThisError;
|
|
||||||
|
|
||||||
use super::ReqwestError;
|
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)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Certificate(pub(super) tls::Certificate);
|
pub struct Certificate(pub(super) tls::Certificate);
|
||||||
|
|
||||||
impl 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
|
/// Create a Certificate from a binary DER encoded certificate
|
||||||
pub fn from_der(der: impl AsRef<[u8]>) -> Result<Self, ReqwestError> {
|
pub fn from_der(der: impl AsRef<[u8]>) -> Result<Self, ReqwestError> {
|
||||||
tls::Certificate::from_der(der.as_ref()).map(Self)
|
tls::Certificate::from_der(der.as_ref()).map(Self)
|
||||||
|
|
Loading…
Add table
Reference in a new issue