mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-11-02 19:47:54 +00:00
Specify headers by environment variable (#21993)
* Specify headers by environment var * Add changelog entry * Add tests, docs * Formatting --------- Co-authored-by: Violet Hynes <violet.hynes@hashicorp.com>
This commit is contained in:
@@ -10,6 +10,7 @@ import (
|
|||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -41,6 +42,7 @@ const (
|
|||||||
EnvVaultClientCert = "VAULT_CLIENT_CERT"
|
EnvVaultClientCert = "VAULT_CLIENT_CERT"
|
||||||
EnvVaultClientKey = "VAULT_CLIENT_KEY"
|
EnvVaultClientKey = "VAULT_CLIENT_KEY"
|
||||||
EnvVaultClientTimeout = "VAULT_CLIENT_TIMEOUT"
|
EnvVaultClientTimeout = "VAULT_CLIENT_TIMEOUT"
|
||||||
|
EnvVaultHeaders = "VAULT_HEADERS"
|
||||||
EnvVaultSRVLookup = "VAULT_SRV_LOOKUP"
|
EnvVaultSRVLookup = "VAULT_SRV_LOOKUP"
|
||||||
EnvVaultSkipVerify = "VAULT_SKIP_VERIFY"
|
EnvVaultSkipVerify = "VAULT_SKIP_VERIFY"
|
||||||
EnvVaultNamespace = "VAULT_NAMESPACE"
|
EnvVaultNamespace = "VAULT_NAMESPACE"
|
||||||
@@ -665,6 +667,30 @@ func NewClient(c *Config) (*Client, error) {
|
|||||||
client.setNamespace(namespace)
|
client.setNamespace(namespace)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if envHeaders := os.Getenv(EnvVaultHeaders); envHeaders != "" {
|
||||||
|
var result map[string]any
|
||||||
|
err := json.Unmarshal([]byte(envHeaders), &result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not unmarshal environment-supplied headers")
|
||||||
|
}
|
||||||
|
var forbiddenHeaders []string
|
||||||
|
for key, value := range result {
|
||||||
|
if strings.HasPrefix(key, "X-Vault-") {
|
||||||
|
forbiddenHeaders = append(forbiddenHeaders, key)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
value, ok := value.(string)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("environment-supplied headers include non-string values")
|
||||||
|
}
|
||||||
|
client.AddHeader(key, value)
|
||||||
|
}
|
||||||
|
if len(forbiddenHeaders) > 0 {
|
||||||
|
return nil, fmt.Errorf("failed to setup Headers[%s]: Header starting by 'X-Vault-' are for internal usage only", strings.Join(forbiddenHeaders, ", "))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return client, nil
|
return client, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -374,6 +374,61 @@ func TestDefaulRetryPolicy(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestClientEnvHeaders(t *testing.T) {
|
||||||
|
oldHeaders := os.Getenv(EnvVaultHeaders)
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
os.Setenv(EnvVaultHeaders, oldHeaders)
|
||||||
|
}()
|
||||||
|
|
||||||
|
cases := []struct {
|
||||||
|
Input string
|
||||||
|
Valid bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"{}",
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"{\"foo\": \"bar\"}",
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"{\"foo\": 1}", // Values must be strings
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"{\"X-Vault-Foo\": \"bar\"}", // X-Vault-* not allowed
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range cases {
|
||||||
|
os.Setenv(EnvVaultHeaders, tc.Input)
|
||||||
|
config := DefaultConfig()
|
||||||
|
config.ReadEnvironment()
|
||||||
|
_, err := NewClient(config)
|
||||||
|
if err != nil {
|
||||||
|
if tc.Valid {
|
||||||
|
t.Fatalf("unexpected error reading headers from environment: %v", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if !tc.Valid {
|
||||||
|
t.Fatal("no error reading headers from environment when error was expected")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Setenv(EnvVaultHeaders, "{\"foo\": \"bar\"}")
|
||||||
|
config := DefaultConfig()
|
||||||
|
config.ReadEnvironment()
|
||||||
|
cli, _ := NewClient(config)
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(cli.Headers().Values("foo"), []string{"bar"}) {
|
||||||
|
t.Error("Environment-supplied headers not set in CLI client")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestClientEnvSettings(t *testing.T) {
|
func TestClientEnvSettings(t *testing.T) {
|
||||||
cwd, _ := os.Getwd()
|
cwd, _ := os.Getwd()
|
||||||
|
|
||||||
|
|||||||
3
changelog/21993.txt
Normal file
3
changelog/21993.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
```release-note:improvement
|
||||||
|
cli: Allow vault CLI HTTP headers to be specified using the JSON-encoded VAULT_HEADERS environment variable
|
||||||
|
```
|
||||||
@@ -439,6 +439,12 @@ Prevents the Vault client from following redirects. By default, the Vault client
|
|||||||
|
|
||||||
~> **Note:** Disabling redirect following behavior could cause issues with commands such as 'vault operator raft snapshot' as this command redirects the request to the cluster's primary node.
|
~> **Note:** Disabling redirect following behavior could cause issues with commands such as 'vault operator raft snapshot' as this command redirects the request to the cluster's primary node.
|
||||||
|
|
||||||
|
### `VAULT_HEADERS`
|
||||||
|
|
||||||
|
JSON-encoded headers to include in Vault HTTP requests performed by the CLI. For example: `{"FOO": "BAR"}`.
|
||||||
|
|
||||||
|
Like the `-header` CLI parameter, headers starting with `X-Vault-` are forbidden.
|
||||||
|
|
||||||
## Flags
|
## Flags
|
||||||
|
|
||||||
There are different CLI flags that are available depending on subcommands. Some
|
There are different CLI flags that are available depending on subcommands. Some
|
||||||
|
|||||||
Reference in New Issue
Block a user