Skip to content

Commit

Permalink
Disable SSL in Git commands for --allow-insecure-host (#11210)
Browse files Browse the repository at this point in the history
## Summary

Closes #11176.

## Test Plan

- Created a self-signed certificate.
- Ran `openssl s_server -cert cert.pem -key key.pem -WWW -port 8443`.
- Verified that `cargo run pip install
git+https://localhost:8443/repo.git` failed with:

```
error: Git operation failed
  Caused by: failed to fetch into: /Users/crmarsh/.cache/uv/git-v0/db/0773914b3ec4a56e
  Caused by: process didn't exit successfully: `/usr/bin/git fetch --force --update-head-ok 'https://localhost:8443/repo.git' '+HEAD:refs/remotes/origin/HEAD'` (exit status: 128)
--- stderr
fatal: unable to access 'https://localhost:8443/repo.git/': SSL certificate problem: self signed certificate
```

- Verified that `cargo run pip install
git+https://localhost:8443/repo.git --allow-insecure-host
https://localhost:8443` continued further.
  • Loading branch information
charliermarsh authored Feb 4, 2025
1 parent d9907f6 commit 748582e
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 16 deletions.
13 changes: 8 additions & 5 deletions crates/uv-client/src/base_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -387,17 +387,20 @@ enum Security {
impl BaseClient {
/// Selects the appropriate client based on the host's trustworthiness.
pub fn for_host(&self, url: &Url) -> &ClientWithMiddleware {
if self
.allow_insecure_host
.iter()
.any(|allow_insecure_host| allow_insecure_host.matches(url))
{
if self.disable_ssl(url) {
&self.dangerous_client
} else {
&self.client
}
}

/// Returns `true` if the host is trusted to use the insecure client.
pub fn disable_ssl(&self, url: &Url) -> bool {
self.allow_insecure_host
.iter()
.any(|allow_insecure_host| allow_insecure_host.matches(url))
}

/// The configured client timeout, in seconds.
pub fn timeout(&self) -> Duration {
self.timeout
Expand Down
5 changes: 5 additions & 0 deletions crates/uv-client/src/registry_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,11 @@ impl RegistryClient {
self.client.uncached().for_host(url)
}

/// Returns `true` if SSL verification is disabled for the given URL.
pub fn disable_ssl(&self, url: &Url) -> bool {
self.client.uncached().disable_ssl(url)
}

/// Return the [`Connectivity`] mode used by this client.
pub fn connectivity(&self) -> Connectivity {
self.connectivity
Expand Down
18 changes: 15 additions & 3 deletions crates/uv-distribution/src/source/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1430,7 +1430,11 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
.git()
.fetch(
resource.git,
client.unmanaged.uncached_client(resource.url).clone(),
client
.unmanaged
.uncached_client(resource.git.repository())
.clone(),
client.unmanaged.disable_ssl(resource.git.repository()),
self.build_context.cache().bucket(CacheBucket::Git),
self.reporter
.clone()
Expand Down Expand Up @@ -1530,7 +1534,10 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
.git()
.github_fast_path(
resource.git,
client.unmanaged.uncached_client(resource.url).clone(),
client
.unmanaged
.uncached_client(resource.git.repository())
.clone(),
)
.await
{
Expand Down Expand Up @@ -1582,7 +1589,11 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
.git()
.fetch(
resource.git,
client.unmanaged.uncached_client(resource.url).clone(),
client
.unmanaged
.uncached_client(resource.git.repository())
.clone(),
client.unmanaged.disable_ssl(resource.git.repository()),
self.build_context.cache().bucket(CacheBucket::Git),
self.reporter
.clone()
Expand Down Expand Up @@ -1812,6 +1823,7 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
.fetch(
git,
client.unmanaged.uncached_client(git.repository()).clone(),
client.unmanaged.disable_ssl(git.repository()),
self.build_context.cache().bucket(CacheBucket::Git),
self.reporter
.clone()
Expand Down
40 changes: 32 additions & 8 deletions crates/uv-git/src/git.rs
Original file line number Diff line number Diff line change
Expand Up @@ -303,14 +303,15 @@ impl GitRemote {
reference: &GitReference,
locked_rev: Option<GitOid>,
client: &ClientWithMiddleware,
disable_ssl: bool,
) -> Result<(GitDatabase, GitOid)> {
let reference = locked_rev
.map(ReferenceOrOid::Oid)
.unwrap_or(ReferenceOrOid::Reference(reference));
let enable_lfs_fetch = env::var(EnvVars::UV_GIT_LFS).is_ok();

if let Some(mut db) = db {
fetch(&mut db.repo, &self.url, reference, client)
fetch(&mut db.repo, &self.url, reference, client, disable_ssl)
.with_context(|| format!("failed to fetch into: {}", into.user_display()))?;

let resolved_commit_hash = match locked_rev {
Expand All @@ -320,7 +321,7 @@ impl GitRemote {

if let Some(rev) = resolved_commit_hash {
if enable_lfs_fetch {
fetch_lfs(&mut db.repo, &self.url, &rev)
fetch_lfs(&mut db.repo, &self.url, &rev, disable_ssl)
.with_context(|| format!("failed to fetch LFS objects at {rev}"))?;
}
return Ok((db, rev));
Expand All @@ -338,14 +339,14 @@ impl GitRemote {

fs_err::create_dir_all(into)?;
let mut repo = GitRepository::init(into)?;
fetch(&mut repo, &self.url, reference, client)
fetch(&mut repo, &self.url, reference, client, disable_ssl)
.with_context(|| format!("failed to clone into: {}", into.user_display()))?;
let rev = match locked_rev {
Some(rev) => rev,
None => reference.resolve(&repo)?,
};
if enable_lfs_fetch {
fetch_lfs(&mut repo, &self.url, &rev)
fetch_lfs(&mut repo, &self.url, &rev, disable_ssl)
.with_context(|| format!("failed to fetch LFS objects at {rev}"))?;
}

Expand Down Expand Up @@ -502,6 +503,7 @@ fn fetch(
remote_url: &Url,
reference: ReferenceOrOid<'_>,
client: &ClientWithMiddleware,
disable_ssl: bool,
) -> Result<()> {
let oid_to_fetch = match github_fast_path(repo, remote_url, reference, client) {
Ok(FastPathRev::UpToDate) => return Ok(()),
Expand Down Expand Up @@ -577,14 +579,21 @@ fn fetch(

debug!("Performing a Git fetch for: {remote_url}");
let result = match refspec_strategy {
RefspecStrategy::All => fetch_with_cli(repo, remote_url, refspecs.as_slice(), tags),
RefspecStrategy::All => {
fetch_with_cli(repo, remote_url, refspecs.as_slice(), tags, disable_ssl)
}
RefspecStrategy::First => {
// Try each refspec
let mut errors = refspecs
.iter()
.map_while(|refspec| {
let fetch_result =
fetch_with_cli(repo, remote_url, std::slice::from_ref(refspec), tags);
let fetch_result = fetch_with_cli(
repo,
remote_url,
std::slice::from_ref(refspec),
tags,
disable_ssl,
);

// Stop after the first success and log failures
match fetch_result {
Expand Down Expand Up @@ -629,12 +638,17 @@ fn fetch_with_cli(
url: &Url,
refspecs: &[String],
tags: bool,
disable_ssl: bool,
) -> Result<()> {
let mut cmd = ProcessBuilder::new(GIT.as_ref()?);
cmd.arg("fetch");
if tags {
cmd.arg("--tags");
}
if disable_ssl {
debug!("Disabling SSL verification for Git fetch");
cmd.env(EnvVars::GIT_SSL_NO_VERIFY, "true");
}
cmd.arg("--force") // handle force pushes
.arg("--update-head-ok") // see discussion in #2078
.arg(url.as_str())
Expand Down Expand Up @@ -674,7 +688,12 @@ static GIT_LFS: LazyLock<Result<ProcessBuilder>> = LazyLock::new(|| {
});

/// Attempts to use `git-lfs` CLI to fetch required LFS objects for a given revision.
fn fetch_lfs(repo: &mut GitRepository, url: &Url, revision: &GitOid) -> Result<()> {
fn fetch_lfs(
repo: &mut GitRepository,
url: &Url,
revision: &GitOid,
disable_ssl: bool,
) -> Result<()> {
let mut cmd = if let Ok(lfs) = GIT_LFS.as_ref() {
debug!("Fetching Git LFS objects");
lfs.clone()
Expand All @@ -684,6 +703,11 @@ fn fetch_lfs(repo: &mut GitRepository, url: &Url, revision: &GitOid) -> Result<(
return Ok(());
};

if disable_ssl {
debug!("Disabling SSL verification for Git LFS");
cmd.env(EnvVars::GIT_SSL_NO_VERIFY, "true");
}

cmd.arg("fetch")
.arg(url.as_str())
.arg(revision.as_str())
Expand Down
9 changes: 9 additions & 0 deletions crates/uv-git/src/resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ impl GitResolver {
&self,
url: &GitUrl,
client: ClientWithMiddleware,
disable_ssl: bool,
cache: PathBuf,
reporter: Option<Arc<dyn Reporter>>,
) -> Result<Fetch, GitResolverError> {
Expand Down Expand Up @@ -139,6 +140,14 @@ impl GitResolver {
} else {
GitSource::new(url.as_ref().clone(), client, cache)
};

// If necessary, disable SSL.
let source = if disable_ssl {
source.dangerous()
} else {
source
};

let fetch = tokio::task::spawn_blocking(move || source.fetch())
.await?
.map_err(GitResolverError::Git)?;
Expand Down
13 changes: 13 additions & 0 deletions crates/uv-git/src/source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ pub struct GitSource {
git: GitUrl,
/// The HTTP client to use for fetching.
client: ClientWithMiddleware,
/// Whether to disable SSL verification.
disable_ssl: bool,
/// The path to the Git source database.
cache: PathBuf,
/// The reporter to use for this source.
Expand All @@ -37,12 +39,22 @@ impl GitSource {
) -> Self {
Self {
git,
disable_ssl: false,
client: client.into(),
cache: cache.into(),
reporter: None,
}
}

/// Disable SSL verification for this [`GitSource`].
#[must_use]
pub fn dangerous(self) -> Self {
Self {
disable_ssl: true,
..self
}
}

/// Set the [`Reporter`] to use for the [`GitSource`].
#[must_use]
pub fn with_reporter(self, reporter: Arc<dyn Reporter>) -> Self {
Expand Down Expand Up @@ -96,6 +108,7 @@ impl GitSource {
&self.git.reference,
locked_rev.map(GitOid::from),
&self.client,
self.disable_ssl,
)?;

(db, actual_rev, task)
Expand Down
4 changes: 4 additions & 0 deletions crates/uv-static/src/env_vars.rs
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,10 @@ impl EnvVars {
#[attr_hidden]
pub const GIT_ALTERNATE_OBJECT_DIRECTORIES: &'static str = "GIT_ALTERNATE_OBJECT_DIRECTORIES";

/// Disables SSL verification for git operations.
#[attr_hidden]
pub const GIT_SSL_NO_VERIFY: &'static str = "GIT_SSL_NO_VERIFY";

/// Used in tests for better git isolation.
///
/// For example, we run some tests in ~/.local/share/uv/tests.
Expand Down

0 comments on commit 748582e

Please sign in to comment.