-
Notifications
You must be signed in to change notification settings - Fork 2.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proxy auth with custom HTTP transport #11433
Conversation
Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @ehuss (or someone else) soon. Please see the contribution instructions for more information. |
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<String>? 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/util/config/mod.rs
Outdated
#[serde(default)] | ||
pub proxy_username: String, | ||
#[serde(default)] | ||
pub proxy_password: String, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd prefer the username and password be Option
s rather than empty strings.
#[serde(default)] | |
pub proxy_username: String, | |
#[serde(default)] | |
pub proxy_password: String, | |
pub proxy_username: Option<String>, | |
pub proxy_password: Option<String>, |
src/cargo/util/config/mod.rs
Outdated
impl CargoHttpProxyAuth { | ||
pub fn to_easy(&self) -> Auth { | ||
let mut easy = Auth::new(); | ||
let easy = match self { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid the easy.clone()
below by not redefining easy
here. Also consider calling it auth
instead of easy
let easy = match self { | |
match self { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not possible to avoid the clone because match self { .. }
is a &mut Auth
. Also Auth
does not implement Copy
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't need to capture the result of the match
in a variable. The basic()
, digest()
, etc methods mutate the Auth
struct, so you can return the one you created with Auth::new()
without cloning.
pub fn to_easy(&self) -> Auth {
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),
};
auth
}
#[serde(rename_all = "kebab-case")] | ||
pub enum CargoHttpProxyAuth { | ||
#[default] | ||
Auto, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does the default of Auto
introduce any additional HTTP requests for the non-proxy case?
The curl docs indicate that it might:
If more than one bit is set, libcurl will first query the site to see what authentication methods it supports and then pick the best one you allow it to use. For some methods, this will induce an extra network round-trip
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change focuses on proxy authentication specifically, not authentication to arbitrary HTTP servers. The Auth
API is used for both problems, thus this paragraph I think. In the non-proxy case there is no additional request to be made.
This could well send an additional request to a proxy that doesn't need authentication, however I don't think it would make a noticeable difference in performance compared to, e.g. the time it takes to update the registry or download kilobytes of crates (?).
this is true! my bad! <rust-lang#11433 (comment)>
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") <rust-lang#11433 (comment)>
@@ -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())?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this is CargoHttpProxyAuth::Disable
should we avoid calling any of the proxy_
methods?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not calling handle.proxy_auth
is the same as calling it with Auth::new().basic(true)
[0,1] (support only basic authentication). The idea with "disable" is to tell curl to not negotiate even if the user has a login/pwd in cargo config file.
We could avoid calling proxy_username
and proxy_password
but I don't think it would change anything, at least from the official curl docs.
[0] https://curl.se/libcurl/c/CURLOPT_PROXYAUTH.html
[1] https://docs.rs/curl/latest/curl/easy/struct.Easy2.html#method.proxy_auth
@@ -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.as_deref().unwrap_or(""))?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a comment about why these need to be called with empty string (instead of just not calling them)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment is in the commit message [0] (second paragraph). Do you want another one in the code?
[0] e7a5905
Unfortunately this seems like a relatively meaty change, so it can be difficult to nail everything down. @hhirtz Is there a specific authentication scheme that you personally are interested in using? I was wondering if there is some way to narrow the scope of what is being added here. For example, if cargo needs to move to a different backend, then it would need to support all the mechanisms added here, which can be a large burden. Additionally, it looks like building curl with all the appropriate support looks potentially complicated. Exactly what will be supported on all of Cargo's platforms with the current build system? Another issue is that we try to avoid including secrets in the configuration files. Adding the password here seems like an anti-pattern, so I think we may need to look at some alternate approach. I'm not sure if that involves using In general, it can also be difficult for me to review and understand a change like this since I am not deeply familiar with all the different proxy authentication mechanisms. I think to move forward, I would need some kind of ELI5 introduction to what is being added here to help evaluate how these will be used and the consequences. |
☔ The latest upstream changes (presumably #11646) made this pull request unmergeable. Please resolve the merge conflicts. |
I'm going to close since this hasn't seen any movement in a while. If you want to pick this back up, I would suggest following up on #7330 with a specific design proposal that addresses any of the concerns raised above. Thanks! |
What does this PR try to resolve?
This PR makes cargo authenticate against proxies when needed.
Closes #7330
Add three settings:
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
vsOption<String>
?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.
The official cargo builds seems to be made with vendored curl.
How should we test and review this PR?
Complete testing is a bit tedious since you'd need e.g. squid [1] to test each authentication mechanism. With such proxy, run
cargo update
with http-proxy environment variables (plus--config http.proxy-{username,password}=
if needed).[0] https://docs.rs/curl/0.4/curl/easy/struct.Auth.html#method.gssnegotiate
[1] http://www.squid-cache.org/