mirror of
https://github.com/hashicorp/vault.git
synced 2025-11-14 15:21:11 +01:00
Passthrough request headers (#4172)
* Add passthrough request headers for secret/auth mounts * Update comments * Fix SyncCache deletion of passthrough_request_headers * Remove debug line * Case-insensitive header comparison * Remove unnecessary allocation * Short-circuit filteredPassthroughHeaders if there's nothing to filter * Add whitelistedHeaders list * Update router logic after merge * Add whitelist test * Add lowercase x-vault-kv-client to whitelist * Add back const * Refactor whitelist logic
This commit is contained in:
parent
a03a722fa9
commit
c54c9519c8
@ -92,12 +92,13 @@ type EnableAuthOptions struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type AuthConfigInput struct {
|
type AuthConfigInput struct {
|
||||||
DefaultLeaseTTL string `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"`
|
DefaultLeaseTTL string `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"`
|
||||||
MaxLeaseTTL string `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"`
|
MaxLeaseTTL string `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"`
|
||||||
PluginName string `json:"plugin_name,omitempty" structs:"plugin_name,omitempty" mapstructure:"plugin_name"`
|
PluginName string `json:"plugin_name,omitempty" structs:"plugin_name,omitempty" mapstructure:"plugin_name"`
|
||||||
AuditNonHMACRequestKeys []string `json:"audit_non_hmac_request_keys,omitempty" structs:"audit_non_hmac_request_keys" mapstructure:"audit_non_hmac_request_keys"`
|
AuditNonHMACRequestKeys []string `json:"audit_non_hmac_request_keys,omitempty" structs:"audit_non_hmac_request_keys" mapstructure:"audit_non_hmac_request_keys"`
|
||||||
AuditNonHMACResponseKeys []string `json:"audit_non_hmac_response_keys,omitempty" structs:"audit_non_hmac_response_keys" mapstructure:"audit_non_hmac_response_keys"`
|
AuditNonHMACResponseKeys []string `json:"audit_non_hmac_response_keys,omitempty" structs:"audit_non_hmac_response_keys" mapstructure:"audit_non_hmac_response_keys"`
|
||||||
ListingVisibility string `json:"listing_visibility,omitempty" structs:"listing_visibility" mapstructure:"listing_visibility"`
|
ListingVisibility string `json:"listing_visibility,omitempty" structs:"listing_visibility" mapstructure:"listing_visibility"`
|
||||||
|
PassthroughRequestHeaders []string `json:"passthrough_request_headers,omitempty" structs:"passthrough_request_headers" mapstructure:"passthrough_request_headers"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AuthMount struct {
|
type AuthMount struct {
|
||||||
@ -111,10 +112,11 @@ type AuthMount struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type AuthConfigOutput struct {
|
type AuthConfigOutput struct {
|
||||||
DefaultLeaseTTL int `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"`
|
DefaultLeaseTTL int `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"`
|
||||||
MaxLeaseTTL int `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"`
|
MaxLeaseTTL int `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"`
|
||||||
PluginName string `json:"plugin_name,omitempty" structs:"plugin_name,omitempty" mapstructure:"plugin_name"`
|
PluginName string `json:"plugin_name,omitempty" structs:"plugin_name,omitempty" mapstructure:"plugin_name"`
|
||||||
AuditNonHMACRequestKeys []string `json:"audit_non_hmac_request_keys,omitempty" structs:"audit_non_hmac_request_keys" mapstructure:"audit_non_hmac_request_keys"`
|
AuditNonHMACRequestKeys []string `json:"audit_non_hmac_request_keys,omitempty" structs:"audit_non_hmac_request_keys" mapstructure:"audit_non_hmac_request_keys"`
|
||||||
AuditNonHMACResponseKeys []string `json:"audit_non_hmac_response_keys,omitempty" structs:"audit_non_hmac_response_keys" mapstructure:"audit_non_hmac_response_keys"`
|
AuditNonHMACResponseKeys []string `json:"audit_non_hmac_response_keys,omitempty" structs:"audit_non_hmac_response_keys" mapstructure:"audit_non_hmac_response_keys"`
|
||||||
ListingVisibility string `json:"listing_visibility,omitempty" structs:"listing_visibility" mapstructure:"listing_visibility"`
|
ListingVisibility string `json:"listing_visibility,omitempty" structs:"listing_visibility" mapstructure:"listing_visibility"`
|
||||||
|
PassthroughRequestHeaders []string `json:"passthrough_request_headers,omitempty" structs:"passthrough_request_headers" mapstructure:"passthrough_request_headers"`
|
||||||
}
|
}
|
||||||
|
|||||||
@ -130,14 +130,15 @@ type MountInput struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type MountConfigInput struct {
|
type MountConfigInput struct {
|
||||||
Options map[string]string `json:"options" structs:"options" mapstructure:"options"`
|
Options map[string]string `json:"options" structs:"options" mapstructure:"options"`
|
||||||
DefaultLeaseTTL string `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"`
|
DefaultLeaseTTL string `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"`
|
||||||
MaxLeaseTTL string `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"`
|
MaxLeaseTTL string `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"`
|
||||||
ForceNoCache bool `json:"force_no_cache" structs:"force_no_cache" mapstructure:"force_no_cache"`
|
ForceNoCache bool `json:"force_no_cache" structs:"force_no_cache" mapstructure:"force_no_cache"`
|
||||||
PluginName string `json:"plugin_name,omitempty" structs:"plugin_name,omitempty" mapstructure:"plugin_name"`
|
PluginName string `json:"plugin_name,omitempty" structs:"plugin_name,omitempty" mapstructure:"plugin_name"`
|
||||||
AuditNonHMACRequestKeys []string `json:"audit_non_hmac_request_keys,omitempty" structs:"audit_non_hmac_request_keys" mapstructure:"audit_non_hmac_request_keys"`
|
AuditNonHMACRequestKeys []string `json:"audit_non_hmac_request_keys,omitempty" structs:"audit_non_hmac_request_keys" mapstructure:"audit_non_hmac_request_keys"`
|
||||||
AuditNonHMACResponseKeys []string `json:"audit_non_hmac_response_keys,omitempty" structs:"audit_non_hmac_response_keys" mapstructure:"audit_non_hmac_response_keys"`
|
AuditNonHMACResponseKeys []string `json:"audit_non_hmac_response_keys,omitempty" structs:"audit_non_hmac_response_keys" mapstructure:"audit_non_hmac_response_keys"`
|
||||||
ListingVisibility string `json:"listing_visibility,omitempty" structs:"listing_visibility" mapstructure:"listing_visibility"`
|
ListingVisibility string `json:"listing_visibility,omitempty" structs:"listing_visibility" mapstructure:"listing_visibility"`
|
||||||
|
PassthroughRequestHeaders []string `json:"passthrough_request_headers,omitempty" structs:"passthrough_request_headers" mapstructure:"passthrough_request_headers"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type MountOutput struct {
|
type MountOutput struct {
|
||||||
@ -151,12 +152,13 @@ type MountOutput struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type MountConfigOutput struct {
|
type MountConfigOutput struct {
|
||||||
Options map[string]string `json:"options" structs:"options" mapstructure:"options"`
|
Options map[string]string `json:"options" structs:"options" mapstructure:"options"`
|
||||||
DefaultLeaseTTL int `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"`
|
DefaultLeaseTTL int `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"`
|
||||||
MaxLeaseTTL int `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"`
|
MaxLeaseTTL int `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"`
|
||||||
ForceNoCache bool `json:"force_no_cache" structs:"force_no_cache" mapstructure:"force_no_cache"`
|
ForceNoCache bool `json:"force_no_cache" structs:"force_no_cache" mapstructure:"force_no_cache"`
|
||||||
PluginName string `json:"plugin_name,omitempty" structs:"plugin_name,omitempty" mapstructure:"plugin_name"`
|
PluginName string `json:"plugin_name,omitempty" structs:"plugin_name,omitempty" mapstructure:"plugin_name"`
|
||||||
AuditNonHMACRequestKeys []string `json:"audit_non_hmac_request_keys,omitempty" structs:"audit_non_hmac_request_keys" mapstructure:"audit_non_hmac_request_keys"`
|
AuditNonHMACRequestKeys []string `json:"audit_non_hmac_request_keys,omitempty" structs:"audit_non_hmac_request_keys" mapstructure:"audit_non_hmac_request_keys"`
|
||||||
AuditNonHMACResponseKeys []string `json:"audit_non_hmac_response_keys,omitempty" structs:"audit_non_hmac_response_keys" mapstructure:"audit_non_hmac_response_keys"`
|
AuditNonHMACResponseKeys []string `json:"audit_non_hmac_response_keys,omitempty" structs:"audit_non_hmac_response_keys" mapstructure:"audit_non_hmac_response_keys"`
|
||||||
ListingVisibility string `json:"listing_visibility,omitempty" structs:"listing_visibility" mapstructure:"listing_visibility"`
|
ListingVisibility string `json:"listing_visibility,omitempty" structs:"listing_visibility" mapstructure:"listing_visibility"`
|
||||||
|
PassthroughRequestHeaders []string `json:"passthrough_request_headers,omitempty" structs:"passthrough_request_headers" mapstructure:"passthrough_request_headers"`
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,17 +17,18 @@ var _ cli.CommandAutocomplete = (*AuthEnableCommand)(nil)
|
|||||||
type AuthEnableCommand struct {
|
type AuthEnableCommand struct {
|
||||||
*BaseCommand
|
*BaseCommand
|
||||||
|
|
||||||
flagDescription string
|
flagDescription string
|
||||||
flagPath string
|
flagPath string
|
||||||
flagDefaultLeaseTTL time.Duration
|
flagDefaultLeaseTTL time.Duration
|
||||||
flagMaxLeaseTTL time.Duration
|
flagMaxLeaseTTL time.Duration
|
||||||
flagAuditNonHMACRequestKeys []string
|
flagAuditNonHMACRequestKeys []string
|
||||||
flagAuditNonHMACResponseKeys []string
|
flagAuditNonHMACResponseKeys []string
|
||||||
flagListingVisibility string
|
flagListingVisibility string
|
||||||
flagPluginName string
|
flagPassthroughRequestHeaders []string
|
||||||
flagOptions map[string]string
|
flagPluginName string
|
||||||
flagLocal bool
|
flagOptions map[string]string
|
||||||
flagSealWrap bool
|
flagLocal bool
|
||||||
|
flagSealWrap bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *AuthEnableCommand) Synopsis() string {
|
func (c *AuthEnableCommand) Synopsis() string {
|
||||||
@ -121,6 +122,13 @@ func (c *AuthEnableCommand) Flags() *FlagSets {
|
|||||||
Usage: "Determines the visibility of the mount in the UI-specific listing endpoint.",
|
Usage: "Determines the visibility of the mount in the UI-specific listing endpoint.",
|
||||||
})
|
})
|
||||||
|
|
||||||
|
f.StringSliceVar(&StringSliceVar{
|
||||||
|
Name: flagNamePassthroughRequestHeaders,
|
||||||
|
Target: &c.flagPassthroughRequestHeaders,
|
||||||
|
Usage: "Comma-separated string or list of request header values that " +
|
||||||
|
"will be sent to the backend",
|
||||||
|
})
|
||||||
|
|
||||||
f.StringVar(&StringVar{
|
f.StringVar(&StringVar{
|
||||||
Name: "plugin-name",
|
Name: "plugin-name",
|
||||||
Target: &c.flagPluginName,
|
Target: &c.flagPluginName,
|
||||||
@ -229,6 +237,10 @@ func (c *AuthEnableCommand) Run(args []string) int {
|
|||||||
if fl.Name == flagNameListingVisibility {
|
if fl.Name == flagNameListingVisibility {
|
||||||
authOpts.Config.ListingVisibility = c.flagListingVisibility
|
authOpts.Config.ListingVisibility = c.flagListingVisibility
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if fl.Name == flagNamePassthroughRequestHeaders {
|
||||||
|
authOpts.Config.PassthroughRequestHeaders = c.flagPassthroughRequestHeaders
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
if err := client.Sys().EnableAuthWithOptions(authPath, authOpts); err != nil {
|
if err := client.Sys().EnableAuthWithOptions(authPath, authOpts); err != nil {
|
||||||
|
|||||||
@ -79,6 +79,8 @@ const (
|
|||||||
flagNameAuditNonHMACResponseKeys = "audit-non-hmac-response-keys"
|
flagNameAuditNonHMACResponseKeys = "audit-non-hmac-response-keys"
|
||||||
// flagListingVisibility is the flag to toggle whether to show the mount in the UI-specific listing endpoint
|
// flagListingVisibility is the flag to toggle whether to show the mount in the UI-specific listing endpoint
|
||||||
flagNameListingVisibility = "listing-visibility"
|
flagNameListingVisibility = "listing-visibility"
|
||||||
|
// flagNamePassthroughRequestHeaders is the flag name used to set passthrough request headers to the backend
|
||||||
|
flagNamePassthroughRequestHeaders = "passthrough-request-headers"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|||||||
@ -17,18 +17,19 @@ var _ cli.CommandAutocomplete = (*SecretsEnableCommand)(nil)
|
|||||||
type SecretsEnableCommand struct {
|
type SecretsEnableCommand struct {
|
||||||
*BaseCommand
|
*BaseCommand
|
||||||
|
|
||||||
flagDescription string
|
flagDescription string
|
||||||
flagPath string
|
flagPath string
|
||||||
flagDefaultLeaseTTL time.Duration
|
flagDefaultLeaseTTL time.Duration
|
||||||
flagMaxLeaseTTL time.Duration
|
flagMaxLeaseTTL time.Duration
|
||||||
flagAuditNonHMACRequestKeys []string
|
flagAuditNonHMACRequestKeys []string
|
||||||
flagAuditNonHMACResponseKeys []string
|
flagAuditNonHMACResponseKeys []string
|
||||||
flagListingVisibility string
|
flagListingVisibility string
|
||||||
flagForceNoCache bool
|
flagPassthroughRequestHeaders []string
|
||||||
flagPluginName string
|
flagForceNoCache bool
|
||||||
flagOptions map[string]string
|
flagPluginName string
|
||||||
flagLocal bool
|
flagOptions map[string]string
|
||||||
flagSealWrap bool
|
flagLocal bool
|
||||||
|
flagSealWrap bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *SecretsEnableCommand) Synopsis() string {
|
func (c *SecretsEnableCommand) Synopsis() string {
|
||||||
@ -129,6 +130,13 @@ func (c *SecretsEnableCommand) Flags() *FlagSets {
|
|||||||
Usage: "Determines the visibility of the mount in the UI-specific listing endpoint.",
|
Usage: "Determines the visibility of the mount in the UI-specific listing endpoint.",
|
||||||
})
|
})
|
||||||
|
|
||||||
|
f.StringSliceVar(&StringSliceVar{
|
||||||
|
Name: flagNamePassthroughRequestHeaders,
|
||||||
|
Target: &c.flagPassthroughRequestHeaders,
|
||||||
|
Usage: "Comma-separated string or list of request header values that " +
|
||||||
|
"will be sent to the backend",
|
||||||
|
})
|
||||||
|
|
||||||
f.BoolVar(&BoolVar{
|
f.BoolVar(&BoolVar{
|
||||||
Name: "force-no-cache",
|
Name: "force-no-cache",
|
||||||
Target: &c.flagForceNoCache,
|
Target: &c.flagForceNoCache,
|
||||||
@ -249,6 +257,10 @@ func (c *SecretsEnableCommand) Run(args []string) int {
|
|||||||
if fl.Name == flagNameListingVisibility {
|
if fl.Name == flagNameListingVisibility {
|
||||||
mountInput.Config.ListingVisibility = c.flagListingVisibility
|
mountInput.Config.ListingVisibility = c.flagListingVisibility
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if fl.Name == flagNamePassthroughRequestHeaders {
|
||||||
|
mountInput.Config.PassthroughRequestHeaders = c.flagPassthroughRequestHeaders
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
if err := client.Sys().Mount(mountPath, mountInput); err != nil {
|
if err := client.Sys().Mount(mountPath, mountInput); err != nil {
|
||||||
|
|||||||
@ -1287,6 +1287,7 @@ func TestSysTuneMount_nonHMACKeys(t *testing.T) {
|
|||||||
resp = testHttpPost(t, token, addr+"/v1/sys/mounts/secret/tune", map[string]interface{}{
|
resp = testHttpPost(t, token, addr+"/v1/sys/mounts/secret/tune", map[string]interface{}{
|
||||||
"audit_non_hmac_response_keys": "",
|
"audit_non_hmac_response_keys": "",
|
||||||
})
|
})
|
||||||
|
testResponseStatus(t, resp, 204)
|
||||||
|
|
||||||
// Check results
|
// Check results
|
||||||
resp = testHttpGet(t, token, addr+"/v1/sys/mounts/secret/tune")
|
resp = testHttpGet(t, token, addr+"/v1/sys/mounts/secret/tune")
|
||||||
@ -1318,7 +1319,7 @@ func TestSysTuneMount_nonHMACKeys(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSysTuneMount_showUIMount(t *testing.T) {
|
func TestSysTuneMount_listingVisibility(t *testing.T) {
|
||||||
core, _, token := vault.TestCoreUnsealed(t)
|
core, _, token := vault.TestCoreUnsealed(t)
|
||||||
ln, addr := TestServer(t, core)
|
ln, addr := TestServer(t, core)
|
||||||
defer ln.Close()
|
defer ln.Close()
|
||||||
@ -1390,3 +1391,78 @@ func TestSysTuneMount_showUIMount(t *testing.T) {
|
|||||||
t.Fatalf("bad:\nExpected: %#v\nActual:%#v", expected, actual)
|
t.Fatalf("bad:\nExpected: %#v\nActual:%#v", expected, actual)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSysTuneMount_passthroughRequestHeaders(t *testing.T) {
|
||||||
|
core, _, token := vault.TestCoreUnsealed(t)
|
||||||
|
ln, addr := TestServer(t, core)
|
||||||
|
defer ln.Close()
|
||||||
|
TestServerAuth(t, addr, token)
|
||||||
|
|
||||||
|
// Mount-tune the audit_non_hmac_request_keys
|
||||||
|
resp := testHttpPost(t, token, addr+"/v1/sys/mounts/secret/tune", map[string]interface{}{
|
||||||
|
"passthrough_request_headers": "X-Vault-Foo",
|
||||||
|
})
|
||||||
|
testResponseStatus(t, resp, 204)
|
||||||
|
|
||||||
|
// Check results
|
||||||
|
resp = testHttpGet(t, token, addr+"/v1/sys/mounts/secret/tune")
|
||||||
|
testResponseStatus(t, resp, 200)
|
||||||
|
|
||||||
|
actual := map[string]interface{}{}
|
||||||
|
expected := map[string]interface{}{
|
||||||
|
"lease_id": "",
|
||||||
|
"renewable": false,
|
||||||
|
"lease_duration": json.Number("0"),
|
||||||
|
"wrap_info": nil,
|
||||||
|
"warnings": nil,
|
||||||
|
"auth": nil,
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"default_lease_ttl": json.Number("2764800"),
|
||||||
|
"max_lease_ttl": json.Number("2764800"),
|
||||||
|
"force_no_cache": false,
|
||||||
|
"passthrough_request_headers": []interface{}{"X-Vault-Foo"},
|
||||||
|
},
|
||||||
|
"default_lease_ttl": json.Number("2764800"),
|
||||||
|
"max_lease_ttl": json.Number("2764800"),
|
||||||
|
"force_no_cache": false,
|
||||||
|
"passthrough_request_headers": []interface{}{"X-Vault-Foo"},
|
||||||
|
}
|
||||||
|
testResponseBody(t, resp, &actual)
|
||||||
|
expected["request_id"] = actual["request_id"]
|
||||||
|
if !reflect.DeepEqual(actual, expected) {
|
||||||
|
t.Fatalf("bad:\nExpected: %#v\nActual:%#v", expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unset the mount tune value
|
||||||
|
resp = testHttpPost(t, token, addr+"/v1/sys/mounts/secret/tune", map[string]interface{}{
|
||||||
|
"passthrough_request_headers": "",
|
||||||
|
})
|
||||||
|
testResponseStatus(t, resp, 204)
|
||||||
|
|
||||||
|
// Check results
|
||||||
|
resp = testHttpGet(t, token, addr+"/v1/sys/mounts/secret/tune")
|
||||||
|
testResponseStatus(t, resp, 200)
|
||||||
|
|
||||||
|
actual = map[string]interface{}{}
|
||||||
|
expected = map[string]interface{}{
|
||||||
|
"lease_id": "",
|
||||||
|
"renewable": false,
|
||||||
|
"lease_duration": json.Number("0"),
|
||||||
|
"wrap_info": nil,
|
||||||
|
"warnings": nil,
|
||||||
|
"auth": nil,
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"default_lease_ttl": json.Number("2764800"),
|
||||||
|
"max_lease_ttl": json.Number("2764800"),
|
||||||
|
"force_no_cache": false,
|
||||||
|
},
|
||||||
|
"default_lease_ttl": json.Number("2764800"),
|
||||||
|
"max_lease_ttl": json.Number("2764800"),
|
||||||
|
"force_no_cache": false,
|
||||||
|
}
|
||||||
|
testResponseBody(t, resp, &actual)
|
||||||
|
expected["request_id"] = actual["request_id"]
|
||||||
|
if !reflect.DeepEqual(actual, expected) {
|
||||||
|
t.Fatalf("bad:\nExpected: %#v\nActual:%#v", expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -2276,3 +2276,88 @@ func TestCore_Standby_Rotate(t *testing.T) {
|
|||||||
t.Fatalf("bad: %#v", resp)
|
t.Fatalf("bad: %#v", resp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure that InternalData is never returned
|
||||||
|
func TestCore_HandleRequest_Headers(t *testing.T) {
|
||||||
|
noop := &NoopBackend{
|
||||||
|
Response: &logical.Response{
|
||||||
|
Data: map[string]interface{}{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
c, _, root := TestCoreUnsealed(t)
|
||||||
|
c.logicalBackends["noop"] = func(context.Context, *logical.BackendConfig) (logical.Backend, error) {
|
||||||
|
return noop, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable the backend
|
||||||
|
req := logical.TestRequest(t, logical.UpdateOperation, "sys/mounts/foo")
|
||||||
|
req.Data["type"] = "noop"
|
||||||
|
req.ClientToken = root
|
||||||
|
_, err := c.HandleRequest(req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mount tune
|
||||||
|
req = logical.TestRequest(t, logical.UpdateOperation, "sys/mounts/foo/tune")
|
||||||
|
req.Data["passthrough_request_headers"] = []string{"Should-Passthrough", "should-passthrough-case-insensitive"}
|
||||||
|
req.ClientToken = root
|
||||||
|
_, err = c.HandleRequest(req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to read
|
||||||
|
lreq := &logical.Request{
|
||||||
|
Operation: logical.ReadOperation,
|
||||||
|
Path: "foo/test",
|
||||||
|
ClientToken: root,
|
||||||
|
Headers: map[string][]string{
|
||||||
|
"X-Vault-Kv-Client": []string{"foo"},
|
||||||
|
"Should-Passthrough": []string{"foo"},
|
||||||
|
"Should-Passthrough-Case-Insensitive": []string{"baz"},
|
||||||
|
"Should-Not-Passthrough": []string{"bar"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
_, err = c.HandleRequest(lreq)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the headers
|
||||||
|
headers := noop.Requests[0].Headers
|
||||||
|
|
||||||
|
// Test whitelisted values
|
||||||
|
if val, ok := headers["X-Vault-Kv-Client"]; ok {
|
||||||
|
expected := []string{"foo"}
|
||||||
|
if !reflect.DeepEqual(val, expected) {
|
||||||
|
t.Fatalf("expected: %v, got: %v", expected, val)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
t.Fatalf("expected 'X-Vault-Kv-Client' to be present in the headers map")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test passthrough values
|
||||||
|
if val, ok := headers["Should-Passthrough"]; ok {
|
||||||
|
expected := []string{"foo"}
|
||||||
|
if !reflect.DeepEqual(val, expected) {
|
||||||
|
t.Fatalf("expected: %v, got: %v", expected, val)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
t.Fatalf("expected 'Should-Passthrough' to be present in the headers map")
|
||||||
|
}
|
||||||
|
|
||||||
|
if val, ok := headers["Should-Passthrough-Case-Insensitive"]; ok {
|
||||||
|
expected := []string{"baz"}
|
||||||
|
if !reflect.DeepEqual(val, expected) {
|
||||||
|
t.Fatalf("expected: %v, got: %v", expected, val)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
t.Fatalf("expected 'Should-Passthrough-Case-Insensitive' to be present in the headers map")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := headers["Should-Not-Passthrough"]; ok {
|
||||||
|
t.Fatalf("did not expect 'Should-Not-Passthrough' to be in the headers map")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -275,6 +275,10 @@ func NewSystemBackend(core *Core) *SystemBackend {
|
|||||||
Type: framework.TypeString,
|
Type: framework.TypeString,
|
||||||
Description: strings.TrimSpace(sysHelp["listing_visibility"][0]),
|
Description: strings.TrimSpace(sysHelp["listing_visibility"][0]),
|
||||||
},
|
},
|
||||||
|
"passthrough_request_headers": &framework.FieldSchema{
|
||||||
|
Type: framework.TypeCommaStringSlice,
|
||||||
|
Description: strings.TrimSpace(sysHelp["passthrough_request_headers"][0]),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||||
logical.ReadOperation: b.handleAuthTuneRead,
|
logical.ReadOperation: b.handleAuthTuneRead,
|
||||||
@ -320,6 +324,10 @@ func NewSystemBackend(core *Core) *SystemBackend {
|
|||||||
Type: framework.TypeString,
|
Type: framework.TypeString,
|
||||||
Description: strings.TrimSpace(sysHelp["listing_visibility"][0]),
|
Description: strings.TrimSpace(sysHelp["listing_visibility"][0]),
|
||||||
},
|
},
|
||||||
|
"passthrough_request_headers": &framework.FieldSchema{
|
||||||
|
Type: framework.TypeCommaStringSlice,
|
||||||
|
Description: strings.TrimSpace(sysHelp["passthrough_request_headers"][0]),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||||
@ -1498,6 +1506,10 @@ func (b *SystemBackend) handleMountTable(ctx context.Context, req *logical.Reque
|
|||||||
if len(entry.Config.ListingVisibility) > 0 {
|
if len(entry.Config.ListingVisibility) > 0 {
|
||||||
entryConfig["listing_visibility"] = entry.Config.ListingVisibility
|
entryConfig["listing_visibility"] = entry.Config.ListingVisibility
|
||||||
}
|
}
|
||||||
|
if rawVal, ok := entry.synthesizedConfigCache.Load("passthrough_request_headers"); ok {
|
||||||
|
entryConfig["passthrough_request_headers"] = rawVal.([]string)
|
||||||
|
}
|
||||||
|
|
||||||
info["config"] = entryConfig
|
info["config"] = entryConfig
|
||||||
resp.Data[entry.Path] = info
|
resp.Data[entry.Path] = info
|
||||||
}
|
}
|
||||||
@ -1612,6 +1624,9 @@ func (b *SystemBackend) handleMount(ctx context.Context, req *logical.Request, d
|
|||||||
if len(apiConfig.AuditNonHMACResponseKeys) > 0 {
|
if len(apiConfig.AuditNonHMACResponseKeys) > 0 {
|
||||||
config.AuditNonHMACResponseKeys = apiConfig.AuditNonHMACResponseKeys
|
config.AuditNonHMACResponseKeys = apiConfig.AuditNonHMACResponseKeys
|
||||||
}
|
}
|
||||||
|
if len(apiConfig.PassthroughRequestHeaders) > 0 {
|
||||||
|
config.PassthroughRequestHeaders = apiConfig.PassthroughRequestHeaders
|
||||||
|
}
|
||||||
|
|
||||||
// Create the mount entry
|
// Create the mount entry
|
||||||
me := &MountEntry{
|
me := &MountEntry{
|
||||||
@ -1782,6 +1797,10 @@ func (b *SystemBackend) handleTuneReadCommon(path string) (*logical.Response, er
|
|||||||
resp.Data["listing_visibility"] = mountEntry.Config.ListingVisibility
|
resp.Data["listing_visibility"] = mountEntry.Config.ListingVisibility
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if rawVal, ok := mountEntry.synthesizedConfigCache.Load("passthrough_request_headers"); ok {
|
||||||
|
resp.Data["passthrough_request_headers"] = rawVal.([]string)
|
||||||
|
}
|
||||||
|
|
||||||
if len(mountEntry.Options) > 0 {
|
if len(mountEntry.Options) > 0 {
|
||||||
resp.Data["options"] = mountEntry.Options
|
resp.Data["options"] = mountEntry.Options
|
||||||
}
|
}
|
||||||
@ -2000,6 +2019,32 @@ func (b *SystemBackend) handleTuneWriteCommon(ctx context.Context, path string,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if rawVal, ok := data.GetOk("passthrough_request_headers"); ok {
|
||||||
|
headers := rawVal.([]string)
|
||||||
|
|
||||||
|
oldVal := mountEntry.Config.PassthroughRequestHeaders
|
||||||
|
mountEntry.Config.PassthroughRequestHeaders = headers
|
||||||
|
|
||||||
|
// Update the mount table
|
||||||
|
var err error
|
||||||
|
switch {
|
||||||
|
case strings.HasPrefix(path, "auth/"):
|
||||||
|
err = b.Core.persistAuth(ctx, b.Core.auth, mountEntry.Local)
|
||||||
|
default:
|
||||||
|
err = b.Core.persistMounts(ctx, b.Core.mounts, mountEntry.Local)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
mountEntry.Config.PassthroughRequestHeaders = oldVal
|
||||||
|
return handleError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
mountEntry.SyncCache()
|
||||||
|
|
||||||
|
if b.Core.logger.IsInfo() {
|
||||||
|
b.Core.logger.Info("core: mount tuning of passthrough_request_headers successful", "path", path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var resp *logical.Response
|
var resp *logical.Response
|
||||||
if optionsRaw, ok := data.GetOk("options"); ok {
|
if optionsRaw, ok := data.GetOk("options"); ok {
|
||||||
b.Core.logger.Info("core: mount tuning of options", "path", path)
|
b.Core.logger.Info("core: mount tuning of options", "path", path)
|
||||||
@ -2050,6 +2095,7 @@ func (b *SystemBackend) handleTuneWriteCommon(ctx context.Context, path string,
|
|||||||
|
|
||||||
delete(mountEntry.Options, "upgrade")
|
delete(mountEntry.Options, "upgrade")
|
||||||
}
|
}
|
||||||
|
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2214,6 +2260,10 @@ func (b *SystemBackend) handleAuthTable(ctx context.Context, req *logical.Reques
|
|||||||
if len(entry.Config.ListingVisibility) > 0 {
|
if len(entry.Config.ListingVisibility) > 0 {
|
||||||
entryConfig["listing_visibility"] = entry.Config.ListingVisibility
|
entryConfig["listing_visibility"] = entry.Config.ListingVisibility
|
||||||
}
|
}
|
||||||
|
if rawVal, ok := entry.synthesizedConfigCache.Load("passthrough_request_headers"); ok {
|
||||||
|
entryConfig["passthrough_request_headers"] = rawVal.([]string)
|
||||||
|
}
|
||||||
|
|
||||||
info["config"] = entryConfig
|
info["config"] = entryConfig
|
||||||
resp.Data[entry.Path] = info
|
resp.Data[entry.Path] = info
|
||||||
}
|
}
|
||||||
@ -2320,6 +2370,9 @@ func (b *SystemBackend) handleEnableAuth(ctx context.Context, req *logical.Reque
|
|||||||
if len(apiConfig.AuditNonHMACResponseKeys) > 0 {
|
if len(apiConfig.AuditNonHMACResponseKeys) > 0 {
|
||||||
config.AuditNonHMACResponseKeys = apiConfig.AuditNonHMACResponseKeys
|
config.AuditNonHMACResponseKeys = apiConfig.AuditNonHMACResponseKeys
|
||||||
}
|
}
|
||||||
|
if len(apiConfig.PassthroughRequestHeaders) > 0 {
|
||||||
|
config.PassthroughRequestHeaders = apiConfig.PassthroughRequestHeaders
|
||||||
|
}
|
||||||
|
|
||||||
// Create the mount entry
|
// Create the mount entry
|
||||||
me := &MountEntry{
|
me := &MountEntry{
|
||||||
@ -3820,4 +3873,7 @@ This path responds to the following HTTP methods.
|
|||||||
"listing_visibility": {
|
"listing_visibility": {
|
||||||
"Determines the visibility of the mount in the UI-specific listing endpoint.",
|
"Determines the visibility of the mount in the UI-specific listing endpoint.",
|
||||||
},
|
},
|
||||||
|
"passthrough_request_headers": {
|
||||||
|
"A list of headers to whitelist and pass from the request to the backend.",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -184,30 +184,35 @@ type MountEntry struct {
|
|||||||
SealWrap bool `json:"seal_wrap"` // Whether to wrap CSPs
|
SealWrap bool `json:"seal_wrap"` // Whether to wrap CSPs
|
||||||
Tainted bool `json:"tainted,omitempty"` // Set as a Write-Ahead flag for unmount/remount
|
Tainted bool `json:"tainted,omitempty"` // Set as a Write-Ahead flag for unmount/remount
|
||||||
|
|
||||||
// synthesizedConfigCache is used to cache configuration values
|
// synthesizedConfigCache is used to cache configuration values. These
|
||||||
|
// particular values are cached since we want to get them at a point-in-time
|
||||||
|
// without separately managing their locks individually. See SyncCache() for
|
||||||
|
// the specific values that are being cached.
|
||||||
synthesizedConfigCache sync.Map
|
synthesizedConfigCache sync.Map
|
||||||
}
|
}
|
||||||
|
|
||||||
// MountConfig is used to hold settable options
|
// MountConfig is used to hold settable options
|
||||||
type MountConfig struct {
|
type MountConfig struct {
|
||||||
DefaultLeaseTTL time.Duration `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"` // Override for global default
|
DefaultLeaseTTL time.Duration `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"` // Override for global default
|
||||||
MaxLeaseTTL time.Duration `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"` // Override for global default
|
MaxLeaseTTL time.Duration `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"` // Override for global default
|
||||||
ForceNoCache bool `json:"force_no_cache" structs:"force_no_cache" mapstructure:"force_no_cache"` // Override for global default
|
ForceNoCache bool `json:"force_no_cache" structs:"force_no_cache" mapstructure:"force_no_cache"` // Override for global default
|
||||||
PluginName string `json:"plugin_name,omitempty" structs:"plugin_name,omitempty" mapstructure:"plugin_name"`
|
PluginName string `json:"plugin_name,omitempty" structs:"plugin_name,omitempty" mapstructure:"plugin_name"`
|
||||||
AuditNonHMACRequestKeys []string `json:"audit_non_hmac_request_keys,omitempty" structs:"audit_non_hmac_request_keys" mapstructure:"audit_non_hmac_request_keys"`
|
AuditNonHMACRequestKeys []string `json:"audit_non_hmac_request_keys,omitempty" structs:"audit_non_hmac_request_keys" mapstructure:"audit_non_hmac_request_keys"`
|
||||||
AuditNonHMACResponseKeys []string `json:"audit_non_hmac_response_keys,omitempty" structs:"audit_non_hmac_response_keys" mapstructure:"audit_non_hmac_response_keys"`
|
AuditNonHMACResponseKeys []string `json:"audit_non_hmac_response_keys,omitempty" structs:"audit_non_hmac_response_keys" mapstructure:"audit_non_hmac_response_keys"`
|
||||||
ListingVisibility ListingVisiblityType `json:"listing_visibility,omitempty" structs:"listing_visibility" mapstructure:"listing_visibility"`
|
ListingVisibility ListingVisiblityType `json:"listing_visibility,omitempty" structs:"listing_visibility" mapstructure:"listing_visibility"`
|
||||||
|
PassthroughRequestHeaders []string `json:"passthrough_request_headers,omitempty" structs:"passthrough_request_headers" mapstructure:"passthrough_request_headers"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// APIMountConfig is an embedded struct of api.MountConfigInput
|
// APIMountConfig is an embedded struct of api.MountConfigInput
|
||||||
type APIMountConfig struct {
|
type APIMountConfig struct {
|
||||||
DefaultLeaseTTL string `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"`
|
DefaultLeaseTTL string `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"`
|
||||||
MaxLeaseTTL string `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"`
|
MaxLeaseTTL string `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"`
|
||||||
ForceNoCache bool `json:"force_no_cache" structs:"force_no_cache" mapstructure:"force_no_cache"`
|
ForceNoCache bool `json:"force_no_cache" structs:"force_no_cache" mapstructure:"force_no_cache"`
|
||||||
PluginName string `json:"plugin_name,omitempty" structs:"plugin_name,omitempty" mapstructure:"plugin_name"`
|
PluginName string `json:"plugin_name,omitempty" structs:"plugin_name,omitempty" mapstructure:"plugin_name"`
|
||||||
AuditNonHMACRequestKeys []string `json:"audit_non_hmac_request_keys,omitempty" structs:"audit_non_hmac_request_keys" mapstructure:"audit_non_hmac_request_keys"`
|
AuditNonHMACRequestKeys []string `json:"audit_non_hmac_request_keys,omitempty" structs:"audit_non_hmac_request_keys" mapstructure:"audit_non_hmac_request_keys"`
|
||||||
AuditNonHMACResponseKeys []string `json:"audit_non_hmac_response_keys,omitempty" structs:"audit_non_hmac_response_keys" mapstructure:"audit_non_hmac_response_keys"`
|
AuditNonHMACResponseKeys []string `json:"audit_non_hmac_response_keys,omitempty" structs:"audit_non_hmac_response_keys" mapstructure:"audit_non_hmac_response_keys"`
|
||||||
ListingVisibility ListingVisiblityType `json:"listing_visibility,omitempty" structs:"listing_visibility" mapstructure:"listing_visibility"`
|
ListingVisibility ListingVisiblityType `json:"listing_visibility,omitempty" structs:"listing_visibility" mapstructure:"listing_visibility"`
|
||||||
|
PassthroughRequestHeaders []string `json:"passthrough_request_headers,omitempty" structs:"passthrough_request_headers" mapstructure:"passthrough_request_headers"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clone returns a deep copy of the mount entry
|
// Clone returns a deep copy of the mount entry
|
||||||
@ -219,7 +224,9 @@ func (e *MountEntry) Clone() (*MountEntry, error) {
|
|||||||
return cp.(*MountEntry), nil
|
return cp.(*MountEntry), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SyncCache syncs tunable configuration values to the cache
|
// SyncCache syncs tunable configuration values to the cache. In the case of
|
||||||
|
// cached values, they should be retrieved via synthesizedConfigCache.Load()
|
||||||
|
// instead of accessing them directly through MountConfig.
|
||||||
func (e *MountEntry) SyncCache() {
|
func (e *MountEntry) SyncCache() {
|
||||||
if len(e.Config.AuditNonHMACRequestKeys) == 0 {
|
if len(e.Config.AuditNonHMACRequestKeys) == 0 {
|
||||||
e.synthesizedConfigCache.Delete("audit_non_hmac_request_keys")
|
e.synthesizedConfigCache.Delete("audit_non_hmac_request_keys")
|
||||||
@ -232,6 +239,12 @@ func (e *MountEntry) SyncCache() {
|
|||||||
} else {
|
} else {
|
||||||
e.synthesizedConfigCache.Store("audit_non_hmac_response_keys", e.Config.AuditNonHMACResponseKeys)
|
e.synthesizedConfigCache.Store("audit_non_hmac_response_keys", e.Config.AuditNonHMACResponseKeys)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(e.Config.PassthroughRequestHeaders) == 0 {
|
||||||
|
e.synthesizedConfigCache.Delete("passthrough_request_headers")
|
||||||
|
} else {
|
||||||
|
e.synthesizedConfigCache.Store("passthrough_request_headers", e.Config.PassthroughRequestHeaders)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mount is used to mount a new backend to the mount table.
|
// Mount is used to mount a new backend to the mount table.
|
||||||
|
|||||||
@ -15,6 +15,12 @@ import (
|
|||||||
"github.com/hashicorp/vault/logical"
|
"github.com/hashicorp/vault/logical"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
whitelistedHeaders = []string{
|
||||||
|
consts.VaultKVCLIClientHeader,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
// Router is used to do prefix based routing of a request to a logical backend
|
// Router is used to do prefix based routing of a request to a logical backend
|
||||||
type Router struct {
|
type Router struct {
|
||||||
l sync.RWMutex
|
l sync.RWMutex
|
||||||
@ -473,16 +479,15 @@ func (r *Router) routeCommon(ctx context.Context, req *logical.Request, existenc
|
|||||||
originalClientTokenRemainingUses := req.ClientTokenRemainingUses
|
originalClientTokenRemainingUses := req.ClientTokenRemainingUses
|
||||||
req.ClientTokenRemainingUses = 0
|
req.ClientTokenRemainingUses = 0
|
||||||
|
|
||||||
// Cache the headers and hide them from backends
|
// Cache the headers
|
||||||
headers := req.Headers
|
headers := req.Headers
|
||||||
req.Headers = nil
|
|
||||||
|
|
||||||
// Whitelist the X-Vault-Kv-Client header for use in the kv backend.
|
// Filter and add passthrough headers to the backend
|
||||||
if val, ok := headers[consts.VaultKVCLIClientHeader]; ok {
|
var passthroughRequestHeaders []string
|
||||||
req.Headers = map[string][]string{
|
if rawVal, ok := re.mountEntry.synthesizedConfigCache.Load("passthrough_request_headers"); ok {
|
||||||
consts.VaultKVCLIClientHeader: val,
|
passthroughRequestHeaders = rawVal.([]string)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
req.Headers = filteredPassthroughHeaders(headers, passthroughRequestHeaders)
|
||||||
|
|
||||||
// Cache the wrap info of the request
|
// Cache the wrap info of the request
|
||||||
var wrapInfo *logical.RequestWrapInfo
|
var wrapInfo *logical.RequestWrapInfo
|
||||||
@ -624,3 +629,48 @@ func pathsToRadix(paths []string) *radix.Tree {
|
|||||||
|
|
||||||
return tree
|
return tree
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// filteredPassthroughHeaders returns a headers map[string][]string that
|
||||||
|
// contains the filtered values contained in passthroughHeaders, as well as the
|
||||||
|
// values in whitelistedHeaders. Filtering of passthroughHeaders from the
|
||||||
|
// origHeaders is done is a case-insensitive manner.
|
||||||
|
func filteredPassthroughHeaders(origHeaders map[string][]string, passthroughHeaders []string) map[string][]string {
|
||||||
|
retHeaders := make(map[string][]string)
|
||||||
|
|
||||||
|
// Handle whitelisted values
|
||||||
|
for _, header := range whitelistedHeaders {
|
||||||
|
if val, ok := origHeaders[header]; ok {
|
||||||
|
retHeaders[header] = val
|
||||||
|
} else {
|
||||||
|
// Try to check if a lowercased version of the header exists in the
|
||||||
|
// originating request. The header key that gets used is the one from the
|
||||||
|
// whitelist.
|
||||||
|
if val, ok := origHeaders[strings.ToLower(header)]; ok {
|
||||||
|
retHeaders[header] = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Short-circuit if there's nothing to filter
|
||||||
|
if len(passthroughHeaders) == 0 {
|
||||||
|
return retHeaders
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a map that uses lowercased header values as the key and the original
|
||||||
|
// header naming as the value for comparison down below.
|
||||||
|
lowerHeadersRef := make(map[string]string, len(origHeaders))
|
||||||
|
for key := range origHeaders {
|
||||||
|
lowerHeadersRef[strings.ToLower(key)] = key
|
||||||
|
}
|
||||||
|
|
||||||
|
// Case-insensitive compare of passthrough headers against originating
|
||||||
|
// headers. The returned headers will be the same casing as the originating
|
||||||
|
// header name.
|
||||||
|
for _, ph := range passthroughHeaders {
|
||||||
|
if header, ok := lowerHeadersRef[strings.ToLower(ph)]; ok {
|
||||||
|
retHeaders[header] = origHeaders[header]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return retHeaders
|
||||||
|
}
|
||||||
|
|||||||
@ -92,6 +92,12 @@ For example, enable the "foo" auth method will make it accessible at
|
|||||||
- `audit_non_hmac_response_keys` `(array: [])` - Comma-separated list of keys
|
- `audit_non_hmac_response_keys` `(array: [])` - Comma-separated list of keys
|
||||||
that will not be HMAC'd by audit devices in the response data object.
|
that will not be HMAC'd by audit devices in the response data object.
|
||||||
|
|
||||||
|
- `listing_visibility` `(string: "")` - Speficies whether to show this mount
|
||||||
|
in the UI-specific listing endpoint.
|
||||||
|
|
||||||
|
- `passthrough_request_headers` `(array: [])` - Comma-separated list of headers
|
||||||
|
to whitelist and pass from the request to the backend.
|
||||||
|
|
||||||
The plugin_name can be provided in the config map or as a top-level option,
|
The plugin_name can be provided in the config map or as a top-level option,
|
||||||
with the former taking precedence.
|
with the former taking precedence.
|
||||||
|
|
||||||
@ -215,6 +221,12 @@ can be achieved without `sudo` via `sys/mounts/auth/[auth-path]/tune`._
|
|||||||
list of keys that will not be HMAC'd by audit devices in the response data
|
list of keys that will not be HMAC'd by audit devices in the response data
|
||||||
object.
|
object.
|
||||||
|
|
||||||
|
- `listing_visibility` `(string: "")` - Speficies whether to show this mount
|
||||||
|
in the UI-specific listing endpoint.
|
||||||
|
|
||||||
|
- `passthrough_request_headers` `(array: [])` - Comma-separated list of headers
|
||||||
|
to whitelist and pass from the request to the backend.
|
||||||
|
|
||||||
### Sample Payload
|
### Sample Payload
|
||||||
|
|
||||||
```json
|
```json
|
||||||
|
|||||||
@ -97,6 +97,12 @@ This endpoint enables a new secrets engine at the given path.
|
|||||||
- `audit_non_hmac_response_keys` `(array: [])` - Comma-separated list of keys
|
- `audit_non_hmac_response_keys` `(array: [])` - Comma-separated list of keys
|
||||||
that will not be HMAC'd by audit devices in the response data object.
|
that will not be HMAC'd by audit devices in the response data object.
|
||||||
|
|
||||||
|
- `listing_visibility` `(string: "")` - Speficies whether to show this mount
|
||||||
|
in the UI-specific listing endpoint.
|
||||||
|
|
||||||
|
- `passthrough_request_headers` `(array: [])` - Comma-separated list of headers
|
||||||
|
to whitelist and pass from the request to the backend.
|
||||||
|
|
||||||
These control the default and maximum lease time-to-live, force
|
These control the default and maximum lease time-to-live, force
|
||||||
disabling backend caching, and option plugin name for plugin backends
|
disabling backend caching, and option plugin name for plugin backends
|
||||||
respectively. The first three options override the global defaults if
|
respectively. The first three options override the global defaults if
|
||||||
@ -216,6 +222,12 @@ This endpoint tunes configuration parameters for a given mount point.
|
|||||||
list of keys that will not be HMAC'd by audit devices in the response data
|
list of keys that will not be HMAC'd by audit devices in the response data
|
||||||
object.
|
object.
|
||||||
|
|
||||||
|
- `listing_visibility` `(string: "")` - Speficies whether to show this mount
|
||||||
|
in the UI-specific listing endpoint.
|
||||||
|
|
||||||
|
- `passthrough_request_headers` `(array: [])` - Comma-separated list of headers
|
||||||
|
to whitelist and pass from the request to the backend.
|
||||||
|
|
||||||
### Sample Payload
|
### Sample Payload
|
||||||
|
|
||||||
```json
|
```json
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user