mirror of
https://github.com/astral-sh/uv.git
synced 2025-08-04 19:08:04 +00:00
feat: mTLS support (#4171)
## Summary Closes https://github.com/astral-sh/uv/issues/3626 This adds mTLS support to uv via the standard env var `SSL_CLIENT_CERT`. ## Test Plan Tested locally using a [nginx proxy to pypi](https://github.com/hauntsaninja/nginx_pypi_cache) using my own self-signed ca + certs + client certs generated via [mkcert](https://github.com/FiloSottile/mkcert). Used this proxy with both uv and pip to make sure we have feature partity in mTLS functionality.
This commit is contained in:
parent
5f37395f45
commit
68abf85f0d
4 changed files with 38 additions and 0 deletions
|
@ -502,6 +502,9 @@ If a direct path to the certificate is required (e.g., in CI), set the `SSL_CERT
|
|||
variable to the path of the certificate bundle, to instruct uv to use that file instead of the
|
||||
system's trust store.
|
||||
|
||||
If client certificate authentication (mTLS) is desired, set the `SSL_CLIENT_CERT` environment
|
||||
variable to the path of the PEM formatted file containing the certificate followed by the private key.
|
||||
|
||||
## Platform support
|
||||
|
||||
uv has Tier 1 support for the following platforms:
|
||||
|
@ -595,6 +598,8 @@ In addition, uv respects the following environment variables:
|
|||
|
||||
- `SSL_CERT_FILE`: If set, uv will use this file as the certificate bundle instead of the system's
|
||||
trust store.
|
||||
- `SSL_CLIENT_CERT`: If set, uv will use this file for mTLS authentication. This should be a single
|
||||
file containing both the certificate and the private key in PEM format.
|
||||
- `RUST_LOG`: If set, uv will use this value as the log level for its `--verbose` output. Accepts
|
||||
any filter compatible with the `tracing_subscriber` crate. For example, `RUST_LOG=trace` will
|
||||
enable trace-level logging. See the [tracing documentation](https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#example-syntax)
|
||||
|
|
|
@ -23,6 +23,7 @@ use uv_warnings::warn_user_once;
|
|||
|
||||
use crate::linehaul::LineHaul;
|
||||
use crate::middleware::OfflineMiddleware;
|
||||
use crate::tls::read_identity;
|
||||
use crate::Connectivity;
|
||||
|
||||
/// A builder for an [`BaseClient`].
|
||||
|
@ -161,6 +162,19 @@ impl<'a> BaseClientBuilder<'a> {
|
|||
client_core.tls_built_in_webpki_certs(true)
|
||||
};
|
||||
|
||||
// Configure mTLS.
|
||||
let client_core = if let Some(ssl_client_cert) = env::var_os("SSL_CLIENT_CERT") {
|
||||
match read_identity(&ssl_client_cert) {
|
||||
Ok(identity) => client_core.identity(identity),
|
||||
Err(err) => {
|
||||
warn_user_once!("Ignoring invalid `SSL_CLIENT_CERT`: {err}");
|
||||
client_core
|
||||
}
|
||||
}
|
||||
} else {
|
||||
client_core
|
||||
};
|
||||
|
||||
client_core.build().expect("Failed to build HTTP client.")
|
||||
});
|
||||
|
||||
|
|
|
@ -20,3 +20,4 @@ mod middleware;
|
|||
mod registry_client;
|
||||
mod remote_metadata;
|
||||
mod rkyvutil;
|
||||
mod tls;
|
||||
|
|
18
crates/uv-client/src/tls.rs
Normal file
18
crates/uv-client/src/tls.rs
Normal file
|
@ -0,0 +1,18 @@
|
|||
use reqwest::Identity;
|
||||
use std::ffi::OsStr;
|
||||
use std::io::Read;
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub(crate) enum CertificateError {
|
||||
#[error(transparent)]
|
||||
Io(#[from] std::io::Error),
|
||||
#[error(transparent)]
|
||||
Reqwest(#[from] reqwest::Error),
|
||||
}
|
||||
|
||||
/// Return the `Identity` from the provided file.
|
||||
pub(crate) fn read_identity(ssl_client_cert: &OsStr) -> Result<Identity, CertificateError> {
|
||||
let mut buf = Vec::new();
|
||||
fs_err::File::open(ssl_client_cert)?.read_to_end(&mut buf)?;
|
||||
Ok(Identity::from_pem(&buf)?)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue