From e7a590516c58b85dfaa98bfcdfb7f77c9b551173 Mon Sep 17 00:00:00 2001 From: Hubert Hirtz Date: Mon, 28 Nov 2022 09:57:24 +0100 Subject: [PATCH 1/3] Proxy auth with custom HTTP transport Add three settings: - http.proxy-auth - http.proxy-username - http.proxy-password proxy-auth defaults to "auto" so that most use cases work out of the box. I don't know why you would disable authentication support, but I added a "disable" option in this case. You can also force the use of a mechanism and abort if the proxy doesn't advertise it. proxy-username and proxy-password: String vs Option? curl doesn't negotiate (SPNEGO, here "gss") if handle.proxy_username and handle.proxy_password aren't called. This authentication mechanism doesn't require a user/pass in cargo config. This shouldn't hurt (?) when the proxy doesn't require authentication. About curl-rust's spnego and ntlm features: https://github.com/alexcrichton/curl-rust/blob/b50c5d355aab69483752db51815c5a679b29d6c4/curl-sys/Cargo.toml#L54-L60 **This is the last blocker I think.** If we require those features, then vendored curl builds will require "a suitable GSS-API library or SSPI on Windows" [0] for the "gss" setting to work (which is what corporate proxies are using most of the time). I think this PR is blocked until we can either vendor those dependencies, or we change the official cargo builds to link dynamically to libcurl. --- src/cargo/ops/registry.rs | 3 ++ src/cargo/util/config/mod.rs | 35 ++++++++++++++++++- src/doc/src/reference/config.md | 25 +++++++++++++ .../src/reference/environment-variables.md | 6 ++++ 4 files changed, 68 insertions(+), 1 deletion(-) diff --git a/src/cargo/ops/registry.rs b/src/cargo/ops/registry.rs index 3346e5b41a6..bd5ab528324 100644 --- a/src/cargo/ops/registry.rs +++ b/src/cargo/ops/registry.rs @@ -560,6 +560,9 @@ pub fn configure_http_handle(config: &Config, handle: &mut Easy) -> CargoResult< if let Some(proxy) = http_proxy(config)? { handle.proxy(&proxy)?; } + handle.proxy_auth(&http.proxy_auth.to_easy())?; + handle.proxy_username(&http.proxy_username)?; + handle.proxy_password(&http.proxy_password)?; if let Some(cainfo) = &http.cainfo { let cainfo = cainfo.resolve_path(config); handle.cainfo(&cainfo)?; diff --git a/src/cargo/util/config/mod.rs b/src/cargo/util/config/mod.rs index 5743f9baf3f..3cc246d73c6 100644 --- a/src/cargo/util/config/mod.rs +++ b/src/cargo/util/config/mod.rs @@ -77,7 +77,7 @@ use crate::util::{internal, toml as cargo_toml}; use crate::util::{FileLock, Filesystem, IntoUrl, IntoUrlWithBase, Rustc}; use anyhow::{anyhow, bail, format_err, Context as _}; use cargo_util::paths; -use curl::easy::Easy; +use curl::easy::{Auth, Easy}; use lazycell::LazyCell; use serde::Deserialize; use toml_edit::{easy as toml, Item}; @@ -2215,10 +2215,43 @@ impl Drop for PackageCacheLock<'_> { } } +#[derive(Debug, Default, Deserialize, PartialEq)] +#[serde(rename_all = "kebab-case")] +pub enum CargoHttpProxyAuth { + #[default] + Auto, + Disable, + Basic, + Digest, + Gss, + Ntlm, +} + +impl CargoHttpProxyAuth { + pub fn to_easy(&self) -> Auth { + let mut easy = Auth::new(); + let easy = match self { + Self::Auto => easy.basic(true).digest(true).gssnegotiate(true).ntlm(true), + Self::Disable => &mut easy, + Self::Basic => easy.basic(true), + Self::Digest => easy.digest(true), + Self::Gss => easy.gssnegotiate(true), + Self::Ntlm => easy.ntlm(true), + }; + easy.clone() + } +} + #[derive(Debug, Default, Deserialize, PartialEq)] #[serde(rename_all = "kebab-case")] pub struct CargoHttpConfig { pub proxy: Option, + #[serde(default)] + pub proxy_auth: CargoHttpProxyAuth, + #[serde(default)] + pub proxy_username: String, + #[serde(default)] + pub proxy_password: String, pub low_speed_limit: Option, pub timeout: Option, pub cainfo: Option, diff --git a/src/doc/src/reference/config.md b/src/doc/src/reference/config.md index 1b4243ffa34..942929c3066 100644 --- a/src/doc/src/reference/config.md +++ b/src/doc/src/reference/config.md @@ -96,6 +96,9 @@ vcs = "none" # VCS to use ('git', 'hg', 'pijul', 'fossil', 'none') [http] debug = false # HTTP debugging proxy = "host:port" # HTTP proxy in libcurl format +proxy-auth = "auto" # HTTP proxy authentication mechanism +proxy-username = "" # HTTP proxy username +proxy-password = "" # HTTP proxy password ssl-version = "tlsv1.3" # TLS version to use ssl-version.max = "tlsv1.3" # maximum TLS version ssl-version.min = "tlsv1.1" # minimum TLS version @@ -627,6 +630,28 @@ setting in your global git configuration. If none of those are set, the `HTTPS_PROXY` or `https_proxy` environment variables set the proxy for HTTPS requests, and `http_proxy` sets it for HTTP requests. +##### `http.proxy-auth` +* Type: string +* Default: "auto" +* Environment: `CARGO_HTTP_PROXY_AUTH` + +Sets a mechanism to authenticate against the proxy. +Possible values are: "auto", "disable", "basic", "digest", "gss" and "ntlm". + +##### `http.proxy-username` +* Type: string +* Default: "" +* Environment: `CARGO_HTTP_PROXY_USERNAME` + +Authenticate against the proxy using the given username. + +##### `http.proxy-password` +* Type: string +* Default: "" +* Environment: `CARGO_HTTP_PROXY_PASSWORD` + +Authenticate against the proxy using the given password. + ##### `http.timeout` * Type: integer * Default: 30 diff --git a/src/doc/src/reference/environment-variables.md b/src/doc/src/reference/environment-variables.md index d49922afbfd..f5b8764fb0b 100644 --- a/src/doc/src/reference/environment-variables.md +++ b/src/doc/src/reference/environment-variables.md @@ -101,6 +101,9 @@ In summary, the supported environment variables are: * `CARGO_FUTURE_INCOMPAT_REPORT_FREQUENCY` - How often we should generate a future incompat report notification, see [`future-incompat-report.frequency`]. * `CARGO_HTTP_DEBUG` — Enables HTTP debugging, see [`http.debug`]. * `CARGO_HTTP_PROXY` — Enables HTTP proxy, see [`http.proxy`]. +* `CARGO_HTTP_PROXY_AUTH` — The proxy authentication mechanism, see [`http.proxy-auth`]. +* `CARGO_HTTP_PROXY_USERNAME` — The proxy username, see [`http.proxy-username`]. +* `CARGO_HTTP_PROXY_PASSWORD` — The proxy password, see [`http.proxy-password`]. * `CARGO_HTTP_TIMEOUT` — The HTTP timeout, see [`http.timeout`]. * `CARGO_HTTP_CAINFO` — The TLS certificate Certificate Authority file, see [`http.cainfo`]. * `CARGO_HTTP_CHECK_REVOKE` — Disables TLS certificate revocation checks, see [`http.check-revoke`]. @@ -163,6 +166,9 @@ In summary, the supported environment variables are: [`future-incompat-report.frequency`]: config.md#future-incompat-reportfrequency [`http.debug`]: config.md#httpdebug [`http.proxy`]: config.md#httpproxy +[`http.proxy-auth`]: config.md#httpproxy-auth +[`http.proxy-username`]: config.md#httpproxy-username +[`http.proxy-password`]: config.md#httpproxy-password [`http.timeout`]: config.md#httptimeout [`http.cainfo`]: config.md#httpcainfo [`http.check-revoke`]: config.md#httpcheck-revoke From b50ce9ba1bfd64276c4197352ffaae4f5fd6c0d4 Mon Sep 17 00:00:00 2001 From: Hubert Hirtz Date: Wed, 7 Dec 2022 09:35:26 +0100 Subject: [PATCH 2/3] remove clone this is true! my bad! --- src/cargo/util/config/mod.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/cargo/util/config/mod.rs b/src/cargo/util/config/mod.rs index 3cc246d73c6..a89dcf6fa87 100644 --- a/src/cargo/util/config/mod.rs +++ b/src/cargo/util/config/mod.rs @@ -2229,16 +2229,16 @@ pub enum CargoHttpProxyAuth { impl CargoHttpProxyAuth { pub fn to_easy(&self) -> Auth { - let mut easy = Auth::new(); - let easy = match self { - Self::Auto => easy.basic(true).digest(true).gssnegotiate(true).ntlm(true), - Self::Disable => &mut easy, - Self::Basic => easy.basic(true), - Self::Digest => easy.digest(true), - Self::Gss => easy.gssnegotiate(true), - Self::Ntlm => easy.ntlm(true), + let mut auth = Auth::new(); + match self { + Self::Auto => auth.basic(true).digest(true).gssnegotiate(true).ntlm(true), + Self::Disable => &auth, + Self::Basic => auth.basic(true), + Self::Digest => auth.digest(true), + Self::Gss => auth.gssnegotiate(true), + Self::Ntlm => auth.ntlm(true), }; - easy.clone() + auth } } From ebaa00954dd4b2299ca81431394ee070a39f18d9 Mon Sep 17 00:00:00 2001 From: Hubert Hirtz Date: Wed, 7 Dec 2022 09:39:29 +0100 Subject: [PATCH 3/3] use options instead of empty strings handle.proxy_username and handle.proxy_password still need to be called when options are "None", otherwise curl won't negotiate in cases it should (eg when using "gss") --- src/cargo/ops/registry.rs | 4 ++-- src/cargo/util/config/mod.rs | 6 ++---- src/doc/src/reference/config.md | 4 ++-- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/cargo/ops/registry.rs b/src/cargo/ops/registry.rs index bd5ab528324..4bc5b56f837 100644 --- a/src/cargo/ops/registry.rs +++ b/src/cargo/ops/registry.rs @@ -561,8 +561,8 @@ pub fn configure_http_handle(config: &Config, handle: &mut Easy) -> CargoResult< handle.proxy(&proxy)?; } handle.proxy_auth(&http.proxy_auth.to_easy())?; - handle.proxy_username(&http.proxy_username)?; - handle.proxy_password(&http.proxy_password)?; + handle.proxy_username(http.proxy_username.as_deref().unwrap_or(""))?; + handle.proxy_password(http.proxy_password.as_deref().unwrap_or(""))?; if let Some(cainfo) = &http.cainfo { let cainfo = cainfo.resolve_path(config); handle.cainfo(&cainfo)?; diff --git a/src/cargo/util/config/mod.rs b/src/cargo/util/config/mod.rs index a89dcf6fa87..3c52cdb15cd 100644 --- a/src/cargo/util/config/mod.rs +++ b/src/cargo/util/config/mod.rs @@ -2248,10 +2248,8 @@ pub struct CargoHttpConfig { pub proxy: Option, #[serde(default)] pub proxy_auth: CargoHttpProxyAuth, - #[serde(default)] - pub proxy_username: String, - #[serde(default)] - pub proxy_password: String, + pub proxy_username: Option, + pub proxy_password: Option, pub low_speed_limit: Option, pub timeout: Option, pub cainfo: Option, diff --git a/src/doc/src/reference/config.md b/src/doc/src/reference/config.md index 942929c3066..75bf886218d 100644 --- a/src/doc/src/reference/config.md +++ b/src/doc/src/reference/config.md @@ -640,14 +640,14 @@ Possible values are: "auto", "disable", "basic", "digest", "gss" and "ntlm". ##### `http.proxy-username` * Type: string -* Default: "" +* Default: none * Environment: `CARGO_HTTP_PROXY_USERNAME` Authenticate against the proxy using the given username. ##### `http.proxy-password` * Type: string -* Default: "" +* Default: none * Environment: `CARGO_HTTP_PROXY_PASSWORD` Authenticate against the proxy using the given password.