diff --git a/api/client.go b/api/client.go index feff95ae0..b5f7e9bb8 100644 --- a/api/client.go +++ b/api/client.go @@ -51,6 +51,7 @@ const ( EnvVaultMFA = "VAULT_MFA" EnvRateLimit = "VAULT_RATE_LIMIT" EnvHTTPProxy = "VAULT_HTTP_PROXY" + EnvVaultProxyAddr = "VAULT_PROXY_ADDR" HeaderIndex = "X-Vault-Index" HeaderForward = "X-Vault-Forward" HeaderInconsistent = "X-Vault-Inconsistent" @@ -338,7 +339,7 @@ func (c *Config) ReadEnvironment() error { var envMaxRetries *uint64 var envSRVLookup bool var limit *rate.Limiter - var envHTTPProxy string + var envVaultProxy string // Parse the environment variables if v := os.Getenv(EnvVaultAddress); v != "" { @@ -411,7 +412,12 @@ func (c *Config) ReadEnvironment() error { } if v := os.Getenv(EnvHTTPProxy); v != "" { - envHTTPProxy = v + envVaultProxy = v + } + + // VAULT_PROXY_ADDR supersedes VAULT_HTTP_PROXY + if v := os.Getenv(EnvVaultProxyAddr); v != "" { + envVaultProxy = v } // Configure the HTTP clients TLS configuration. @@ -451,14 +457,14 @@ func (c *Config) ReadEnvironment() error { c.Timeout = envClientTimeout } - if envHTTPProxy != "" { - url, err := url.Parse(envHTTPProxy) + if envVaultProxy != "" { + u, err := url.Parse(envVaultProxy) if err != nil { return err } transport := c.HttpClient.Transport.(*http.Transport) - transport.Proxy = http.ProxyURL(url) + transport.Proxy = http.ProxyURL(u) } return nil diff --git a/api/client_test.go b/api/client_test.go index 383932b9e..74f1b9354 100644 --- a/api/client_test.go +++ b/api/client_test.go @@ -19,7 +19,6 @@ import ( "github.com/go-test/deep" "github.com/hashicorp/go-hclog" - "github.com/hashicorp/vault/sdk/helper/consts" ) @@ -1201,3 +1200,87 @@ func TestClientWithNamespace(t *testing.T) { t.Fatalf("Expected original namespace: \"%s\", got \"%s\"", ogNS, client.Namespace()) } } + +func TestVaultProxy(t *testing.T) { + const NoProxy string = "NO_PROXY" + + tests := map[string]struct { + name string + vaultHttpProxy string + vaultProxyAddr string + noProxy string + requestUrl string + expectedResolvedProxyUrl string + }{ + "VAULT_HTTP_PROXY used when NO_PROXY env var doesn't include request host": { + vaultHttpProxy: "https://hashicorp.com", + vaultProxyAddr: "", + noProxy: "terraform.io", + requestUrl: "https://vaultproject.io", + }, + "VAULT_HTTP_PROXY used when NO_PROXY env var includes request host": { + vaultHttpProxy: "https://hashicorp.com", + vaultProxyAddr: "", + noProxy: "terraform.io,vaultproject.io", + requestUrl: "https://vaultproject.io", + }, + "VAULT_PROXY_ADDR used when NO_PROXY env var doesn't include request host": { + vaultHttpProxy: "", + vaultProxyAddr: "https://hashicorp.com", + noProxy: "terraform.io", + requestUrl: "https://vaultproject.io", + }, + "VAULT_PROXY_ADDR used when NO_PROXY env var includes request host": { + vaultHttpProxy: "", + vaultProxyAddr: "https://hashicorp.com", + noProxy: "terraform.io,vaultproject.io", + requestUrl: "https://vaultproject.io", + }, + "VAULT_PROXY_ADDR used when VAULT_HTTP_PROXY env var also supplied": { + vaultHttpProxy: "https://hashicorp.com", + vaultProxyAddr: "https://terraform.io", + noProxy: "", + requestUrl: "https://vaultproject.io", + expectedResolvedProxyUrl: "https://terraform.io", + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + if tc.vaultHttpProxy != "" { + oldVaultHttpProxy := os.Getenv(EnvHTTPProxy) + os.Setenv(EnvHTTPProxy, tc.vaultHttpProxy) + defer os.Setenv(EnvHTTPProxy, oldVaultHttpProxy) + } + + if tc.vaultProxyAddr != "" { + oldVaultProxyAddr := os.Getenv(EnvVaultProxyAddr) + os.Setenv(EnvVaultProxyAddr, tc.vaultProxyAddr) + defer os.Setenv(EnvVaultProxyAddr, oldVaultProxyAddr) + } + + if tc.noProxy != "" { + oldNoProxy := os.Getenv(NoProxy) + os.Setenv(NoProxy, tc.noProxy) + defer os.Setenv(NoProxy, oldNoProxy) + } + + c := DefaultConfig() + if c.Error != nil { + t.Fatalf("Expected no error reading config, found error %v", c.Error) + } + + r, _ := http.NewRequest("GET", tc.requestUrl, nil) + proxyUrl, err := c.HttpClient.Transport.(*http.Transport).Proxy(r) + if err != nil { + t.Fatalf("Expected no error resolving proxy, found error %v", err) + } + if proxyUrl == nil || proxyUrl.String() == "" { + t.Fatalf("Expected proxy to be resolved but no proxy returned") + } + if tc.expectedResolvedProxyUrl != "" && proxyUrl.String() != tc.expectedResolvedProxyUrl { + t.Fatalf("Expected resolved proxy URL to be %v but was %v", tc.expectedResolvedProxyUrl, proxyUrl.String()) + } + }) + } +} diff --git a/changelog/15377.txt b/changelog/15377.txt new file mode 100644 index 000000000..c7547102c --- /dev/null +++ b/changelog/15377.txt @@ -0,0 +1,3 @@ +```release-note:improvement +api: Support VAULT_PROXY_ADDR environment variable to allow overriding the Vault client's HTTP proxy. +``` \ No newline at end of file diff --git a/website/content/docs/commands/index.mdx b/website/content/docs/commands/index.mdx index 14df29770..55e9d0fcf 100644 --- a/website/content/docs/commands/index.mdx +++ b/website/content/docs/commands/index.mdx @@ -379,9 +379,26 @@ used. ### `VAULT_HTTP_PROXY` -HTTP proxy location which should be used to access Vault. When present, this -overrides any other proxies found in the environment. Format should be -`http://server:port`. +HTTP or HTTPS proxy location which should be used by all requests to access Vault. + When present, this overrides the default proxy resolution behavior. +Format should be `http://server:port` or `https://server:port`. + +(See `VAULT_PROXY_ADDR` below). + +### `VAULT_PROXY_ADDR` + +HTTP or HTTPS proxy location which should be used by all requests to access Vault. + When present, this overrides the default proxy resolution behavior. +Format should be `http://server:port` or `https://server:port`. + +~> Note: When using `VAULT_HTTP_PROXY` or `VAULT_PROXY_ADDR` any of the standard + proxy variables found in the environment will be ignored. +Specifically `HTTP_PROXY`, `HTTPS_PROXY` and `NO_PROXY`. +All requests will resolve the specified proxy; there is no way to exclude + domains from consulting the proxy server. + +~> Note: If both `VAULT_HTTP_PROXY` and `VAULT_PROXY_ADDR` environment +variables are supplied, `VAULT_PROXY_ADDR` will be prioritized and preferred. ## Flags