Skip to content
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

Support for Proxy on the Provider level #179

Merged
merged 15 commits into from
Apr 7, 2022
Merged

Conversation

detro
Copy link
Contributor

@detro detro commented Mar 31, 2022

Close #96
Supersedes #113

By introducing support for Proxy on the Provider level, we enable the tls_certificate data source (as well as future resources/data sources), to reach out endpoints via the optionally configured proxy.

The Proxy can be configured via a url (with optional username and password), or from_env`: this means that we leverage the excellent https://pkg.go.dev/net/http#ProxyFromEnvironment API to look for the environment variables and configure the proxy that way.

@detro detro requested a review from a team as a code owner March 31, 2022 16:59
@detro detro changed the title Support for HTTP Proxy Support for Proxy on the Provider level Mar 31, 2022
@detro detro modified the milestone: v3.2.0 Mar 31, 2022
@detro detro added this to the v3.3.0 milestone Apr 1, 2022
Copy link
Contributor

@bflad bflad left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall looking pretty good, just some things to consider around the new net/http and from_env handling.

Proxy: config.proxyForRequestFunc(),
},
}
resp, err := client.Get(targetURL.String())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Previously the data source would only open a TLS connection and not attempt to finish an actual HTTP call (in this new code the GET verb) or read the response body from the URL. A few questions:

  • Can we avoid downloading the response body? e.g. should this execute a HEAD request or might a custom request handler function help here?
  • Do we need to do anything to avoid new errors from switching to net/http?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

About using HEAD vs GET comes out of my experience that out there there are still HTTP servers that don't implement HEAD by default. In the past, where I had to do this kind of stuff, I'd even have logic to try HEAD-then-GET. I'm frankly unsure what's the best option here.

About the full HTTP call, I'm also unsure. It's out of this uncertainty that I introduced the support for tls:// vs https:/ schemes: my thinking was, until now we enforced https:/ but actually never executed HTTP in the first place - just opened a socket.

So, I thought that adding tls:// was going to be an easy way to suggest a "if you want to keep the previous behaviour...".

I'm frankly unsure what's the best strategy here. I'm tempted to suggest to make this PR a breaking change, because the only alternative I see is a .mode attribute that allows to chose the way the resource works. And I don't like it much.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to capture some of our out-of-band conversation (please correct me if I misspeak anywhere!):

  1. We are going to try and implement this so the HTTP client is avoided, continuing to use a direct TLS connection instead, unless a HTTP proxy has been configured (currently via the provider proxy configuration block)
  2. Since IANA does not have a URI scheme for strictly a TCP network socket connection, we can use a synthetic tls:// scheme (or potentially use no scheme).
  3. When using a HTTP proxied client, it will first attempt a HEAD call, then fallback to a GET call in case of a proxy that does not support HEAD and blocks the subsequent HTTP connection.

Not discussed, but thinking out loud: I'm presuming we can capture 405 Method Not Allowed or 501 Not Implemented HTTP status codes for the HTTP proxied client fallback. Another thing I'm not sure about is whether we will need special 407 Proxy Authentication Required handling anywhere.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, while I want to handle HEAD then GET, I think we can't give lots of weight to the actual HTTP response: the TLS negotiation happens much earlier then any HTTP Protocol byte has moved.

So, I think it makes sense to handle error response from an http.Get() call, but if the reps.TLS.PeerCertificates are there, this provider shouldn't do much more than process those.

I'll show you what I mean in the next commits I push.

Comment on lines 55 to 64
"from_env": {
Type: schema.TypeBool,
Optional: true,
Default: false,
ConflictsWith: []string{"proxy.0.url", "proxy.0.username", "proxy.0.password"},
Description: "When `true` the provider will discover the proxy configuration from environment variables. " +
"This is based upon [`http.ProxyFromEnvironment`](https://pkg.go.dev/net/http#ProxyFromEnvironment) " +
"and it supports the same environment variables (default: `true`).",
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few things here:

  • It'd be great to introduce an acceptance test for this that utilizes t.Setenv("HTTP_PROXY", /* ... */) with and without from_env set.
  • The description also notes (default: true) but the schema is marked Default: false. Looking at the code below it appears the description should be updated.
  • Should we note that this will be enabled by default in the next major version and add a code comment so that gets done?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the first item, the HTTP server might need a way to reject connections except those from the proxy.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. will add tests to use t.Setenv 👍
  2. Will fix the description
  3. I'm not sure, my intention wasn't to make the from_env default: are you suggesting it should be made the default in a major release?
  4. I can look into ways to orchestrate a "reject if not via proxy": I can try to play with headers injection more, but I have noticed that, for what our provider does, we never get to exchange http body - so requests never actually get to exchange headers. Will give it another pass.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

my intention wasn't to make the from_env default: are you suggesting it should be made the default in a major release?

I think we should certainly consider this for the next major version. 😄 Many pieces of software handling HTTP calls will conventionally support the NO_PROXY/HTTP_PROXY/HTTPS_PROXY environment variables without additional configuration. github.com/hashicorp/go-cleanhttp default clients for example, implement net/http.ProxyFromEnvironment, which I believe is used by the majority of HTTP handling within Terraform CLI itself.

Copy link
Contributor Author

@detro detro Apr 6, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, the last point you make is great, and it plays well with what we decided: to not use an HTTP client unless a Proxy is configured (YET!).

Enabling ProxyFromEnvironment by default would also change the current behaviour, so we can't do it yet.

So, I think now I follow better what you head in mind earlier when you suggested:

Should we note that this will be enabled by default in the next major version and add a code comment so that gets done?

In the light of this, I think I'll do the following:

  • by default, if no proxy config is provided, we keep using tls.Client
  • if a proxy is configured, then we switch the logic to use http.Client
  • from_env is defaulted to false

Then, I'll create a follow up issue for breaking changes that we want to launch in 4.x (similar to this one). In this issue I'll include:

  • from_env becomes defaulted to true
  • the URL scheme defines the client we will use: this means https:// will always use the http.Client, while tls:// will always use tls.Client

Does this strategy sound good? Am I missing something?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here is the issue #183

@detro detro force-pushed the detro/HTTP-proxy-tls_certificate branch from e3ec824 to 47560c4 Compare April 6, 2022 18:33
@detro
Copy link
Contributor Author

detro commented Apr 6, 2022

@bflad I pushed the changes as discussed above, but I need to call out a few things:

I have started accumulating the "major release stuff" in a 4.0.0 milestone.

@detro detro requested a review from bflad April 6, 2022 18:52
Copy link
Contributor

@bflad bflad left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me 🚀

Comment on lines +134 to +136
// TODO remove this branch and default to use `fetchPeerCertificatesViaHTTPS`
// as part of https://github.com/hashicorp/terraform-provider-tls/issues/183
if config.isProxyConfigured() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still think the next major version should still perform this type of check and use the TLS client whenever possible for simplicity -- the check could see if http.ProxyFromEnvironment would actually use a proxy for the request by checking for nil.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason I insist on using an actual HTTP client, is that we were and are requiring a scheme for the url argument. The fact that practitioners have to write https:// is, in my opinion, an implicit implication we are making.

The original implementation simply didn't respect that. It would have made more sense if there was no scheme, and then it only (and exclusively) used tls.Client. Though that would have prevented even talking about supporting HTTP Proxies.

@detro detro merged commit 04da235 into main Apr 7, 2022
@detro detro deleted the detro/HTTP-proxy-tls_certificate branch April 7, 2022 16:36
@detro detro self-assigned this Apr 7, 2022
Copy link

I'm going to lock this pull request because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active contributions.
If you have found a problem that seems related to this change, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators May 28, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[feature request] Support HTTP proxy for tls_certificate data source
2 participants