From 27921fc4c292c711a9347a8e2a9da9ac8d5f429b Mon Sep 17 00:00:00 2001 From: messense Date: Tue, 14 Feb 2023 17:41:28 +0800 Subject: [PATCH 1/2] refactor: split feature-gated http agent into smaller functions --- src/upload.rs | 60 ++++++++++++++++++++++++++++----------------------- 1 file changed, 33 insertions(+), 27 deletions(-) diff --git a/src/upload.rs b/src/upload.rs index 80589acdc..c9ab5d179 100644 --- a/src/upload.rs +++ b/src/upload.rs @@ -262,6 +262,38 @@ fn canonicalize_name(name: &str) -> String { .to_lowercase() } +fn http_proxy() -> Result { + env::var("HTTPS_PROXY") + .or_else(|_| env::var("https_proxy")) + .or_else(|_| env::var("HTTP_PROXY")) + .or_else(|_| env::var("http_proxy")) +} + +#[cfg(feature = "native-tls")] +#[allow(clippy::result_large_err)] +fn http_agent() -> Result { + use std::sync::Arc; + + let mut builder = + ureq::builder().tls_connector(Arc::new(native_tls_crate::TlsConnector::new()?)); + if let Ok(proxy) = http_proxy() { + let proxy = ureq::Proxy::new(proxy)?; + builder = builder.proxy(proxy); + }; + Ok(builder.build()) +} + +#[cfg(not(feature = "native-tls"))] +#[allow(clippy::result_large_err)] +fn http_agent() -> Result { + let mut builder = ureq::builder(); + if let Ok(proxy) = http_proxy() { + let proxy = ureq::Proxy::new(proxy)?; + builder = builder.proxy(proxy); + }; + Ok(builder.build()) +} + /// Uploads a single wheel to the registry #[allow(clippy::result_large_err)] pub fn upload(registry: &Registry, wheel_path: &Path) -> Result<(), UploadError> { @@ -339,35 +371,9 @@ pub fn upload(registry: &Registry, wheel_path: &Path) -> Result<(), UploadError> form.add_stream("content", &wheel, Some(wheel_name), None); let multipart_data = form.prepare().map_err(|e| e.error)?; - let encoded = base64::encode(format!("{}:{}", registry.username, registry.password)); - let http_proxy = env::var("HTTPS_PROXY") - .or_else(|_| env::var("https_proxy")) - .or_else(|_| env::var("HTTP_PROXY")) - .or_else(|_| env::var("http_proxy")); - - #[cfg(not(feature = "native-tls"))] - let agent = { - let mut builder = ureq::builder(); - if let Ok(proxy) = http_proxy { - let proxy = ureq::Proxy::new(proxy)?; - builder = builder.proxy(proxy); - }; - builder.build() - }; - - #[cfg(feature = "native-tls")] - let agent = { - use std::sync::Arc; - let mut builder = - ureq::builder().tls_connector(Arc::new(native_tls_crate::TlsConnector::new()?)); - if let Ok(proxy) = http_proxy { - let proxy = ureq::Proxy::new(proxy)?; - builder = builder.proxy(proxy); - }; - builder.build() - }; + let agent = http_agent()?; let response = agent .post(registry.url.as_str()) From 57193fc20b66a2aa3ae2b9a787da728a0cd7f3ad Mon Sep 17 00:00:00 2001 From: messense Date: Tue, 14 Feb 2023 18:24:45 +0800 Subject: [PATCH 2/2] Add support for custom TLS certificate authority bundle * Custom TLS CA can be configured via one of the `MATURIN_CA_BUNDLE`, `REQUESTS_CA_BUNDLE` and `CURL_CA_BUNDLE` env vars. --- Cargo.lock | 11 ++++++++++ Cargo.toml | 8 +++++--- Changelog.md | 1 + src/upload.rs | 56 ++++++++++++++++++++++++++++++++++++++++++++------- 4 files changed, 66 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 588f8d224..1a3de7d60 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1394,6 +1394,8 @@ dependencies = [ "python-pkginfo", "regex", "rustc_version", + "rustls", + "rustls-pemfile", "rustversion", "semver", "serde", @@ -2172,6 +2174,15 @@ dependencies = [ "webpki", ] +[[package]] +name = "rustls-pemfile" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0864aeff53f8c05aa08d86e5ef839d3dfcf07aeba2db32f12db0ef716e87bd55" +dependencies = [ + "base64", +] + [[package]] name = "rustversion" version = "1.0.11" diff --git a/Cargo.toml b/Cargo.toml index 9e154e791..f9fc76950 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -81,7 +81,9 @@ bytesize = { version = "1.0.1", optional = true } configparser = { version = "3.0.0", optional = true } multipart = { version = "0.18.0", features = ["client"], default-features = false, optional = true } ureq = { version = "2.6.1", features = ["gzip", "socks-proxy"], default-features = false, optional = true } -native-tls-crate = { package = "native-tls", version = "0.2.8", optional = true } +native-tls = { version = "0.2.8", optional = true } +rustls = { version = "0.20.8", optional = true } +rustls-pemfile = { version = "1.0.1", optional = true } keyring = { version = "1.1.1", optional = true } [dev-dependencies] @@ -105,8 +107,8 @@ upload = ["ureq", "multipart", "configparser", "bytesize", "dialoguer/password"] # keyring doesn't support *BSD so it's not enabled in `full` by default password-storage = ["upload", "keyring"] -rustls = ["ureq/tls", "cargo-xwin/rustls-tls"] -native-tls = ["ureq/native-tls", "native-tls-crate", "cargo-xwin/native-tls"] +rustls = ["dep:rustls", "ureq?/tls", "cargo-xwin?/rustls-tls", "dep:rustls-pemfile"] +native-tls = ["dep:native-tls", "ureq?/native-tls", "cargo-xwin?/native-tls", "dep:rustls-pemfile"] # cross compile using zig or xwin cross-compile = ["zig", "xwin"] diff --git a/Changelog.md b/Changelog.md index 38491b60d..7052dfbc2 100644 --- a/Changelog.md +++ b/Changelog.md @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Respect `rustflags` settings in cargo configuration file in [#1405](https://github.com/PyO3/maturin/pull/1405) * Bump MSRV to 1.63.0 in [#1407](https://github.com/PyO3/maturin/pull/1407) * Add support for uniffi 0.23 in [#1481](https://github.com/PyO3/maturin/pull/1481) +* Add support for custom TLS certificate authority bundle in [#1483](https://github.com/PyO3/maturin/pull/1483) * Add support for Emscripten in `generate-ci` command in [#1484](https://github.com/PyO3/maturin/pull/1484) ## [0.14.13] - 2023-02-12 diff --git a/src/upload.rs b/src/upload.rs index c9ab5d179..ff891656a 100644 --- a/src/upload.rs +++ b/src/upload.rs @@ -10,6 +10,8 @@ use fs_err::File; use multipart::client::lazy::Multipart; use regex::Regex; use std::env; +#[cfg(any(feature = "native-tls", feature = "rustls"))] +use std::ffi::OsString; use std::io; use std::path::{Path, PathBuf}; use thiserror::Error; @@ -84,7 +86,7 @@ pub enum UploadError { /// TLS error #[cfg(feature = "native-tls")] #[error("TLS Error")] - TlsError(#[source] native_tls_crate::Error), + TlsError(#[source] native_tls::Error), } impl From for UploadError { @@ -100,8 +102,8 @@ impl From for UploadError { } #[cfg(feature = "native-tls")] -impl From for UploadError { - fn from(error: native_tls_crate::Error) -> Self { +impl From for UploadError { + fn from(error: native_tls::Error) -> Self { UploadError::TlsError(error) } } @@ -269,21 +271,61 @@ fn http_proxy() -> Result { .or_else(|_| env::var("http_proxy")) } -#[cfg(feature = "native-tls")] +#[cfg(any(feature = "native-tls", feature = "rustls"))] +fn tls_ca_bundle() -> Option { + env::var_os("MATURIN_CA_BUNDLE") + .or_else(|| env::var_os("REQUESTS_CA_BUNDLE")) + .or_else(|| env::var_os("CURL_CA_BUNDLE")) +} + +// Prefer rustls if both native-tls and rustls features are enabled +#[cfg(all(feature = "native-tls", not(feature = "rustls")))] #[allow(clippy::result_large_err)] fn http_agent() -> Result { use std::sync::Arc; - let mut builder = - ureq::builder().tls_connector(Arc::new(native_tls_crate::TlsConnector::new()?)); + let mut builder = ureq::builder(); if let Ok(proxy) = http_proxy() { let proxy = ureq::Proxy::new(proxy)?; builder = builder.proxy(proxy); }; + let mut tls_builder = native_tls::TlsConnector::builder(); + if let Some(ca_bundle) = tls_ca_bundle() { + let mut reader = io::BufReader::new(File::open(ca_bundle)?); + for cert in rustls_pemfile::certs(&mut reader)? { + tls_builder.add_root_certificate(native_tls::Certificate::from_pem(&cert)?); + } + } + builder = builder.tls_connector(Arc::new(tls_builder.build()?)); Ok(builder.build()) } -#[cfg(not(feature = "native-tls"))] +#[cfg(feature = "rustls")] +#[allow(clippy::result_large_err)] +fn http_agent() -> Result { + use std::sync::Arc; + + let mut builder = ureq::builder(); + if let Ok(proxy) = http_proxy() { + let proxy = ureq::Proxy::new(proxy)?; + builder = builder.proxy(proxy); + }; + if let Some(ca_bundle) = tls_ca_bundle() { + let mut reader = io::BufReader::new(File::open(ca_bundle)?); + let certs = rustls_pemfile::certs(&mut reader)?; + let mut root_certs = rustls::RootCertStore::empty(); + root_certs.add_parsable_certificates(&certs); + let client_config = rustls::ClientConfig::builder() + .with_safe_defaults() + .with_root_certificates(root_certs) + .with_no_client_auth(); + Ok(builder.tls_config(Arc::new(client_config)).build()) + } else { + Ok(builder.build()) + } +} + +#[cfg(not(any(feature = "native-tls", feature = "rustls")))] #[allow(clippy::result_large_err)] fn http_agent() -> Result { let mut builder = ureq::builder();