diff --git a/changelog/24667.txt b/changelog/24667.txt new file mode 100644 index 0000000000..b3e83d71f4 --- /dev/null +++ b/changelog/24667.txt @@ -0,0 +1,6 @@ +```release-note:improvement +agent: Added new namespace top level configuration parameter, which can be used to make requests made by Agent to go to that namespace. +``` +```release-note:improvement +proxy: Added new namespace top level configuration parameter, and prepend_configured_namespace API Proxy configuration parameter, which can be used to make requests made to Proxy get proxied to that namespace. +``` diff --git a/command/agent.go b/command/agent.go index 613fccf426..87988efaf3 100644 --- a/command/agent.go +++ b/command/agent.go @@ -309,14 +309,25 @@ func (c *AgentCommand) Run(args []string) int { } c.metricsHelper = metricsutil.NewMetricsHelper(inmemMetrics, prometheusEnabled) + var templateNamespace string + // This indicates whether the namespace for the client has been set by environment variable. + // If it has, we don't touch it + namespaceSetByEnvironmentVariable := client.Namespace() != "" + + if !namespaceSetByEnvironmentVariable && config.Vault != nil && config.Vault.Namespace != "" { + client.SetNamespace(config.Vault.Namespace) + } + var method auth.AuthMethod var sinks []*sink.SinkConfig - var templateNamespace string if config.AutoAuth != nil { - if client.Headers().Get(consts.NamespaceHeaderName) == "" && config.AutoAuth.Method.Namespace != "" { + // Note: This will only set namespace header to the value in config.AutoAuth.Method.Namespace + // only if it hasn't been set by config.Vault.Namespace above. In that case, the config value + // present at config.AutoAuth.Method.Namespace will still be used for auto-auth. + if !namespaceSetByEnvironmentVariable && config.AutoAuth.Method.Namespace != "" { client.SetNamespace(config.AutoAuth.Method.Namespace) } - templateNamespace = client.Headers().Get(consts.NamespaceHeaderName) + templateNamespace = client.Namespace() sinkClient, err := client.CloneWithHeaders() if err != nil { @@ -707,6 +718,11 @@ func (c *AgentCommand) Run(args []string) int { return 1 } + // Override the set namespace with the auto-auth specific namespace + if !namespaceSetByEnvironmentVariable && config.AutoAuth.Method.Namespace != "" { + ahClient.SetNamespace(config.AutoAuth.Method.Namespace) + } + if config.DisableIdleConnsAutoAuth { ahClient.SetMaxIdleConnections(-1) } diff --git a/command/agent/config/config.go b/command/agent/config/config.go index e8d2bab89a..81e8ff8625 100644 --- a/command/agent/config/config.go +++ b/command/agent/config/config.go @@ -91,6 +91,7 @@ type Vault struct { ClientCert string `hcl:"client_cert"` ClientKey string `hcl:"client_key"` TLSServerName string `hcl:"tls_server_name"` + Namespace string `hcl:"namespace"` Retry *Retry `hcl:"retry"` } diff --git a/command/agentproxyshared/auth/auth.go b/command/agentproxyshared/auth/auth.go index 6cfcd74e32..36f775cb5e 100644 --- a/command/agentproxyshared/auth/auth.go +++ b/command/agentproxyshared/auth/auth.go @@ -262,7 +262,7 @@ func (ah *AuthHandler) Run(ctx context.Context, am AuthMethod) error { } if ah.wrapTTL > 0 { - wrapClient, err := clientToUse.Clone() + wrapClient, err := clientToUse.CloneWithHeaders() if err != nil { ah.logger.Error("error creating client for wrapped call", "error", err, "backoff", backoffCfg) metrics.IncrCounter([]string{ah.metricsSignifier, "auth", "failure"}, 1) @@ -289,7 +289,7 @@ func (ah *AuthHandler) Run(ctx context.Context, am AuthMethod) error { isTokenFileMethod = path == "auth/token/lookup-self" if isTokenFileMethod { token, _ := data["token"].(string) - lookupSelfClient, err := clientToUse.Clone() + lookupSelfClient, err := clientToUse.CloneWithHeaders() if err != nil { ah.logger.Error("failed to clone client to perform token lookup") return err diff --git a/command/agentproxyshared/cache/api_proxy.go b/command/agentproxyshared/cache/api_proxy.go index 7b992ff759..35aea9f1e6 100644 --- a/command/agentproxyshared/cache/api_proxy.go +++ b/command/agentproxyshared/cache/api_proxy.go @@ -12,6 +12,7 @@ import ( hclog "github.com/hashicorp/go-hclog" "github.com/hashicorp/go-retryablehttp" "github.com/hashicorp/vault/api" + "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/http" ) @@ -41,6 +42,10 @@ type APIProxy struct { lastIndexStates []string userAgentString string userAgentStringFunction func(string) string + // clientNamespace is a one-time set representation of the namespace of the client + // (i.e. client.Namespace()) to avoid repeated calls and lock usage. + clientNamespace string + prependConfiguredNamespace bool } var _ Proxier = &APIProxy{} @@ -56,6 +61,9 @@ type APIProxyConfig struct { // UserAgentStringFunction is the function to transform the proxied client's // user agent into one that includes Vault-specific information. UserAgentStringFunction func(string) string + // PrependConfiguredNamespace configures whether the client's namespace + // should be prepended to proxied requests + PrependConfiguredNamespace bool } func NewAPIProxy(config *APIProxyConfig) (Proxier, error) { @@ -63,12 +71,14 @@ func NewAPIProxy(config *APIProxyConfig) (Proxier, error) { return nil, fmt.Errorf("nil API client") } return &APIProxy{ - client: config.Client, - logger: config.Logger, - enforceConsistency: config.EnforceConsistency, - whenInconsistentAction: config.WhenInconsistentAction, - userAgentString: config.UserAgentString, - userAgentStringFunction: config.UserAgentStringFunction, + client: config.Client, + logger: config.Logger, + enforceConsistency: config.EnforceConsistency, + whenInconsistentAction: config.WhenInconsistentAction, + userAgentString: config.UserAgentString, + userAgentStringFunction: config.UserAgentStringFunction, + prependConfiguredNamespace: config.PrependConfiguredNamespace, + clientNamespace: namespace.Canonicalize(config.Client.Namespace()), }, nil } @@ -102,6 +112,11 @@ func (ap *APIProxy) Send(ctx context.Context, req *SendRequest) (*SendResponse, } client.SetHeaders(req.Request.Header) + if ap.prependConfiguredNamespace && ap.clientNamespace != "" { + currentNamespace := namespace.Canonicalize(client.Namespace()) + newNamespace := namespace.Canonicalize(ap.clientNamespace + currentNamespace) + client.SetNamespace(newNamespace) + } fwReq := client.NewRequest(req.Request.Method, req.Request.URL.Path) fwReq.BodyBytes = req.RequestBody diff --git a/command/proxy.go b/command/proxy.go index 90382eaae9..eba9670348 100644 --- a/command/proxy.go +++ b/command/proxy.go @@ -284,10 +284,21 @@ func (c *ProxyCommand) Run(args []string) int { } c.metricsHelper = metricsutil.NewMetricsHelper(inmemMetrics, prometheusEnabled) + // This indicates whether the namespace for the client has been set by environment variable. + // If it has, we don't touch it + namespaceSetByEnvironmentVariable := client.Namespace() != "" + + if !namespaceSetByEnvironmentVariable && config.Vault != nil && config.Vault.Namespace != "" { + client.SetNamespace(config.Vault.Namespace) + } + var method auth.AuthMethod var sinks []*sink.SinkConfig if config.AutoAuth != nil { - if client.Headers().Get(consts.NamespaceHeaderName) == "" && config.AutoAuth.Method.Namespace != "" { + // Note: This will only set namespace header to the value in config.AutoAuth.Method.Namespace + // only if it hasn't been set by config.Vault.Namespace above. In that case, the config value + // present at config.AutoAuth.Method.Namespace will still be used for auto-auth. + if !namespaceSetByEnvironmentVariable && config.AutoAuth.Method.Namespace != "" { client.SetNamespace(config.AutoAuth.Method.Namespace) } @@ -421,12 +432,13 @@ func (c *ProxyCommand) Run(args []string) int { // The API proxy to be used, if listeners are configured apiProxy, err := cache.NewAPIProxy(&cache.APIProxyConfig{ - Client: proxyClient, - Logger: apiProxyLogger, - EnforceConsistency: enforceConsistency, - WhenInconsistentAction: whenInconsistent, - UserAgentStringFunction: useragent.ProxyStringWithProxiedUserAgent, - UserAgentString: useragent.ProxyAPIProxyString(), + Client: proxyClient, + Logger: apiProxyLogger, + EnforceConsistency: enforceConsistency, + WhenInconsistentAction: whenInconsistent, + UserAgentStringFunction: useragent.ProxyStringWithProxiedUserAgent, + UserAgentString: useragent.ProxyAPIProxyString(), + PrependConfiguredNamespace: config.APIProxy != nil && config.APIProxy.PrependConfiguredNamespace, }) if err != nil { c.UI.Error(fmt.Sprintf("Error creating API proxy: %v", err)) @@ -686,6 +698,11 @@ func (c *ProxyCommand) Run(args []string) int { return 1 } + // Override the set namespace with the auto-auth specific namespace + if !namespaceSetByEnvironmentVariable && config.AutoAuth.Method.Namespace != "" { + ahClient.SetNamespace(config.AutoAuth.Method.Namespace) + } + if config.DisableIdleConnsAutoAuth { ahClient.SetMaxIdleConnections(-1) } diff --git a/command/proxy/config/config.go b/command/proxy/config/config.go index a67e00bb36..d22b74fad8 100644 --- a/command/proxy/config/config.go +++ b/command/proxy/config/config.go @@ -77,6 +77,7 @@ type Vault struct { ClientCert string `hcl:"client_cert"` ClientKey string `hcl:"client_key"` TLSServerName string `hcl:"tls_server_name"` + Namespace string `hcl:"namespace"` Retry *Retry `hcl:"retry"` } @@ -92,11 +93,12 @@ type transportDialer interface { // APIProxy contains any configuration needed for proxy mode type APIProxy struct { - UseAutoAuthTokenRaw interface{} `hcl:"use_auto_auth_token"` - UseAutoAuthToken bool `hcl:"-"` - ForceAutoAuthToken bool `hcl:"-"` - EnforceConsistency string `hcl:"enforce_consistency"` - WhenInconsistent string `hcl:"when_inconsistent"` + UseAutoAuthTokenRaw interface{} `hcl:"use_auto_auth_token"` + UseAutoAuthToken bool `hcl:"-"` + ForceAutoAuthToken bool `hcl:"-"` + EnforceConsistency string `hcl:"enforce_consistency"` + WhenInconsistent string `hcl:"when_inconsistent"` + PrependConfiguredNamespace bool `hcl:"prepend_configured_namespace"` } // Cache contains any configuration needed for Cache mode diff --git a/website/content/docs/agent-and-proxy/agent/index.mdx b/website/content/docs/agent-and-proxy/agent/index.mdx index 7ce716eb63..21a8ba2863 100644 --- a/website/content/docs/agent-and-proxy/agent/index.mdx +++ b/website/content/docs/agent-and-proxy/agent/index.mdx @@ -214,6 +214,12 @@ configuration entries: connecting via TLS. This value can be overridden by setting the `VAULT_TLS_SERVER_NAME` environment variable. +- `namespace` `(string: )` - Namespace to use for all of Vault Agent's + requests to Vault. This can also be specified by command line or environment variable. + The order of precedence is: this setting lowest, followed by the environment variable + `VAULT_NAMESPACE`, and then the highest precedence command-line option `-namespace`. + If none of these are specified, defaults to the root namespace. + #### retry stanza The `vault` stanza may contain a `retry` stanza that controls how failing Vault diff --git a/website/content/docs/agent-and-proxy/autoauth/index.mdx b/website/content/docs/agent-and-proxy/autoauth/index.mdx index 1de59e7644..f24a2d03e8 100644 --- a/website/content/docs/agent-and-proxy/autoauth/index.mdx +++ b/website/content/docs/agent-and-proxy/autoauth/index.mdx @@ -128,6 +128,10 @@ These are common configuration values that live within the `method` block: If none of these are specified, defaults to the root namespace. Note that because sink response wrapping and templating are also based on the client created by auto-auth, they use the same namespace. + If specified alongside the `namespace` option in the Vault Stanza of + [Vault Agent](/vault/docs/agent-and-proxy/agent#vault-stanza) or + [Vault Proxy](/vault/docs/agent-and-proxy/proxy#vault-stanza), that + configuration will take precedence on everything except auto-auth. - `wrap_ttl` `(string or integer: optional)` - If specified, the written token will be response-wrapped by auto-auth. This is more secure than wrapping by diff --git a/website/content/docs/agent-and-proxy/proxy/apiproxy.mdx b/website/content/docs/agent-and-proxy/proxy/apiproxy.mdx index 3045ef51b4..2c5c07af02 100644 --- a/website/content/docs/agent-and-proxy/proxy/apiproxy.mdx +++ b/website/content/docs/agent-and-proxy/proxy/apiproxy.mdx @@ -55,6 +55,16 @@ configuration will be overridden and the token in the request will be used to forward the request to the Vault server. If set to `"force"` Proxy will use the auto-auth token, overwriting the attached Vault token if set. +- `prepend_configured_namespace` `(bool: false)` - If set, when Proxy has a + namespace configured, such as through the + [Vault stanza](/vault/docs/agent-and-proxy/proxy#vault-stanza), all requests + proxied to Vault will have the configured namespace prepended to the namespace + header. If Proxy's namespace is set to `ns1` and Proxy is sent a request with the + namespace `ns2`, the request will go to the `ns1/ns2` namespace. Likewise, if Proxy + is sent a request without a namespace, the request will go to the `ns1` namespace. + In essence, what this means is that all proxied requests must go to the configured + namespace or to its child namespaces. + The following two `api_proxy` options are only useful when making requests to a Vault Enterprise cluster, and are documented as part of its [Eventual Consistency](/vault/docs/enterprise/consistency#vault-agent-and-consistency-headers) diff --git a/website/content/docs/agent-and-proxy/proxy/index.mdx b/website/content/docs/agent-and-proxy/proxy/index.mdx index a3239b6dbc..3893d4b405 100644 --- a/website/content/docs/agent-and-proxy/proxy/index.mdx +++ b/website/content/docs/agent-and-proxy/proxy/index.mdx @@ -188,6 +188,12 @@ be overridden by setting the `VAULT_SKIP_VERIFY` environment variable. connecting via TLS. This value can be overridden by setting the `VAULT_TLS_SERVER_NAME` environment variable. +- `namespace` `(string: )` - Namespace to use for all of Vault Proxy's +requests to Vault. This can also be specified by command line or environment variable. +The order of precedence is: this setting lowest, followed by the environment variable +`VAULT_NAMESPACE`, and then the highest precedence command-line option `-namespace`. +If none of these are specified, defaults to the root namespace. + #### retry stanza The `vault` stanza may contain a `retry` stanza that controls how failing Vault