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

Specify headers by environment variable #21993

Merged
merged 5 commits into from
Jun 19, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions api/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"crypto/tls"
"encoding/base64"
"encoding/hex"
"encoding/json"
"fmt"
"net"
"net/http"
Expand Down Expand Up @@ -41,6 +42,7 @@ const (
EnvVaultClientCert = "VAULT_CLIENT_CERT"
EnvVaultClientKey = "VAULT_CLIENT_KEY"
EnvVaultClientTimeout = "VAULT_CLIENT_TIMEOUT"
EnvVaultHeaders = "VAULT_HEADERS"
EnvVaultSRVLookup = "VAULT_SRV_LOOKUP"
EnvVaultSkipVerify = "VAULT_SKIP_VERIFY"
EnvVaultNamespace = "VAULT_NAMESPACE"
Expand Down Expand Up @@ -665,6 +667,30 @@ func NewClient(c *Config) (*Client, error) {
client.setNamespace(namespace)
}

if envHeaders := os.Getenv(EnvVaultHeaders); envHeaders != "" {
var result map[string]any
err := json.Unmarshal([]byte(envHeaders), &result)
JacobHenner marked this conversation as resolved.
Show resolved Hide resolved
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
}

Expand Down
56 changes: 56 additions & 0 deletions api/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,62 @@ 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) {
cwd, _ := os.Getwd()

Expand Down
3 changes: 3 additions & 0 deletions changelog/21993.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:improvement
cli: Allow vault CLI HTTP headers to be specified using the JSON-encoded VAULT_HEADERS environment variable
```
6 changes: 6 additions & 0 deletions website/content/docs/commands/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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.

### `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

There are different CLI flags that are available depending on subcommands. Some
Expand Down
Loading