config: rename headers to set_response_headers (#2081)

* config: rename headers to set_response_headers

* Update config/options.go

Co-authored-by: bobby <1544881+desimone@users.noreply.github.com>

Co-authored-by: bobby <1544881+desimone@users.noreply.github.com>
This commit is contained in:
Caleb Doxsey 2021-04-14 12:22:21 -06:00 committed by GitHub
parent f760cdece5
commit 116805acb3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 106 additions and 44 deletions

View file

@ -336,7 +336,7 @@ func (b *Builder) buildMainHTTPConnectionManagerFilter(
// if we're the proxy or authenticate service, add our global headers // if we're the proxy or authenticate service, add our global headers
if config.IsProxy(options.Services) || config.IsAuthenticate(options.Services) { if config.IsProxy(options.Services) || config.IsAuthenticate(options.Services) {
vh.ResponseHeadersToAdd = toEnvoyHeaders(options.Headers) vh.ResponseHeadersToAdd = toEnvoyHeaders(options.SetResponseHeaders)
} }
if len(vh.Routes) > 0 { if len(vh.Routes) > 0 {

View file

@ -170,9 +170,9 @@ type Options struct {
SigningKey string `mapstructure:"signing_key" yaml:"signing_key,omitempty"` SigningKey string `mapstructure:"signing_key" yaml:"signing_key,omitempty"`
SigningKeyAlgorithm string `mapstructure:"signing_key_algorithm" yaml:"signing_key_algorithm,omitempty"` SigningKeyAlgorithm string `mapstructure:"signing_key_algorithm" yaml:"signing_key_algorithm,omitempty"`
// Headers to set on all proxied requests. Add a 'disable' key map to turn off. HeadersEnv string `yaml:",omitempty"`
HeadersEnv string `yaml:",omitempty"` // SetResponseHeaders to set on all proxied requests. Add a 'disable' key map to turn off.
Headers map[string]string `yaml:",omitempty"` SetResponseHeaders map[string]string `yaml:",omitempty"`
// List of JWT claims to insert as x-pomerium-claim-* headers on proxied requests // List of JWT claims to insert as x-pomerium-claim-* headers on proxied requests
JWTClaimsHeaders JWTClaimHeaders `mapstructure:"jwt_claims_headers" yaml:"jwt_claims_headers,omitempty"` JWTClaimsHeaders JWTClaimHeaders `mapstructure:"jwt_claims_headers" yaml:"jwt_claims_headers,omitempty"`
@ -305,7 +305,7 @@ var defaultOptions = Options{
CookieExpire: 14 * time.Hour, CookieExpire: 14 * time.Hour,
CookieName: "_pomerium", CookieName: "_pomerium",
DefaultUpstreamTimeout: 30 * time.Second, DefaultUpstreamTimeout: 30 * time.Second,
Headers: map[string]string{ SetResponseHeaders: map[string]string{
"X-Frame-Options": "SAMEORIGIN", "X-Frame-Options": "SAMEORIGIN",
"X-XSS-Protection": "1; mode=block", "X-XSS-Protection": "1; mode=block",
"Strict-Transport-Security": "max-age=31536000; includeSubDomains; preload", "Strict-Transport-Security": "max-age=31536000; includeSubDomains; preload",
@ -445,12 +445,23 @@ func (o *Options) parseHeaders() error {
} }
} }
o.Headers = headers o.SetResponseHeaders = headers
} else if o.viperIsSet("headers") { return nil
if err := o.viper.UnmarshalKey("headers", &headers); err != nil { }
return fmt.Errorf("header %s failed to parse: %w", o.viper.Get("headers"), err)
if o.viperIsSet("headers") {
log.Warn().Msg("config: headers has been renamed to set_response_headers, it will be removed in v0.16")
}
// option was renamed from `headers` to `set_response_headers`. Both config settings are supported.
headerKeys := []string{"headers", "set_response_headers"}
for _, headerKey := range headerKeys {
if o.viperIsSet(headerKey) {
if err := o.viper.UnmarshalKey(headerKey, &headers); err != nil {
return fmt.Errorf("header %s failed to parse: %w", o.viper.Get(headerKey), err)
}
o.SetResponseHeaders = headers
} }
o.Headers = headers
} }
return nil return nil
} }
@ -594,8 +605,8 @@ func (o *Options) Validate() error {
return fmt.Errorf("config: failed to parse headers: %w", err) return fmt.Errorf("config: failed to parse headers: %w", err)
} }
if _, disable := o.Headers[DisableHeaderKey]; disable { if _, disable := o.SetResponseHeaders[DisableHeaderKey]; disable {
o.Headers = make(map[string]string) o.SetResponseHeaders = make(map[string]string)
} }
hasCert := false hasCert := false
@ -1034,7 +1045,7 @@ func (o *Options) ApplySettings(settings *config.Settings) {
o.SigningKeyAlgorithm = settings.GetSigningKeyAlgorithm() o.SigningKeyAlgorithm = settings.GetSigningKeyAlgorithm()
} }
if settings.Headers != nil && len(settings.Headers) > 0 { if settings.Headers != nil && len(settings.Headers) > 0 {
o.Headers = settings.Headers o.SetResponseHeaders = settings.Headers
} }
if len(settings.JwtClaimsHeaders) > 0 { if len(settings.JwtClaimsHeaders) > 0 {
o.JWTClaimsHeaders = settings.GetJwtClaimsHeaders() o.JWTClaimsHeaders = settings.GetJwtClaimsHeaders()

View file

@ -116,18 +116,69 @@ func Test_bindEnvs(t *testing.T) {
func Test_parseHeaders(t *testing.T) { func Test_parseHeaders(t *testing.T) {
// t.Parallel() // t.Parallel()
tests := []struct { tests := []struct {
name string name string
want map[string]string want map[string]string
envHeaders string envHeaders string
viperHeaders interface{} viperHeaderKey string
wantErr bool viperHeaders interface{}
wantErr bool
}{ }{
{"good env", map[string]string{"X-Custom-1": "foo", "X-Custom-2": "bar"}, `{"X-Custom-1":"foo", "X-Custom-2":"bar"}`, map[string]string{"X": "foo"}, false}, {
{"good env not_json", map[string]string{"X-Custom-1": "foo", "X-Custom-2": "bar"}, `X-Custom-1:foo,X-Custom-2:bar`, map[string]string{"X": "foo"}, false}, "good env",
{"bad env", map[string]string{}, "xyyyy", map[string]string{"X": "foo"}, true}, map[string]string{"X-Custom-1": "foo", "X-Custom-2": "bar"},
{"bad env not_json", map[string]string{"X-Custom-1": "foo", "X-Custom-2": "bar"}, `X-Custom-1:foo,X-Custom-2bar`, map[string]string{"X": "foo"}, true}, `{"X-Custom-1":"foo", "X-Custom-2":"bar"}`,
{"bad viper", map[string]string{}, "", "notaheaderstruct", true}, "headers",
{"good viper", map[string]string{"X-Custom-1": "foo", "X-Custom-2": "bar"}, "", map[string]string{"X-Custom-1": "foo", "X-Custom-2": "bar"}, false}, map[string]string{"X": "foo"},
false,
},
{
"good env not_json",
map[string]string{"X-Custom-1": "foo", "X-Custom-2": "bar"},
`X-Custom-1:foo,X-Custom-2:bar`,
"headers",
map[string]string{"X": "foo"},
false,
},
{
"bad env",
map[string]string{},
"xyyyy",
"headers",
map[string]string{"X": "foo"},
true,
},
{
"bad env not_json",
map[string]string{"X-Custom-1": "foo", "X-Custom-2": "bar"},
`X-Custom-1:foo,X-Custom-2bar`,
"headers",
map[string]string{"X": "foo"},
true,
},
{
"bad viper",
map[string]string{},
"",
"headers",
"notaheaderstruct",
true,
},
{
"good viper",
map[string]string{"X-Custom-1": "foo", "X-Custom-2": "bar"},
"",
"headers",
map[string]string{"X-Custom-1": "foo", "X-Custom-2": "bar"},
false,
},
{
"new field name",
map[string]string{"X-Custom-1": "foo"},
"",
"set_response_headers",
map[string]string{"X-Custom-1": "foo"},
false,
},
} }
for _, tt := range tests { for _, tt := range tests {
@ -148,8 +199,8 @@ func Test_parseHeaders(t *testing.T) {
t.Errorf("Error condition unexpected: err=%s", err) t.Errorf("Error condition unexpected: err=%s", err)
} }
if !tt.wantErr && !cmp.Equal(tt.want, o.Headers) { if !tt.wantErr && !cmp.Equal(tt.want, o.SetResponseHeaders) {
t.Errorf("Did get expected headers: %s", cmp.Diff(tt.want, o.Headers)) t.Errorf("Did get expected headers: %s", cmp.Diff(tt.want, o.SetResponseHeaders))
} }
}) })
} }
@ -259,7 +310,7 @@ func TestOptionsFromViper(t *testing.T) {
GRPCServerMaxConnectionAge: 5 * time.Minute, GRPCServerMaxConnectionAge: 5 * time.Minute,
GRPCServerMaxConnectionAgeGrace: 5 * time.Minute, GRPCServerMaxConnectionAgeGrace: 5 * time.Minute,
AuthenticateCallbackPath: "/oauth2/callback", AuthenticateCallbackPath: "/oauth2/callback",
Headers: map[string]string{ SetResponseHeaders: map[string]string{
"Strict-Transport-Security": "max-age=31536000; includeSubDomains; preload", "Strict-Transport-Security": "max-age=31536000; includeSubDomains; preload",
"X-Frame-Options": "SAMEORIGIN", "X-Frame-Options": "SAMEORIGIN",
"X-XSS-Protection": "1; mode=block", "X-XSS-Protection": "1; mode=block",
@ -286,7 +337,7 @@ func TestOptionsFromViper(t *testing.T) {
InsecureServer: true, InsecureServer: true,
GRPCServerMaxConnectionAge: 5 * time.Minute, GRPCServerMaxConnectionAge: 5 * time.Minute,
GRPCServerMaxConnectionAgeGrace: 5 * time.Minute, GRPCServerMaxConnectionAgeGrace: 5 * time.Minute,
Headers: map[string]string{}, SetResponseHeaders: map[string]string{},
RefreshDirectoryTimeout: 1 * time.Minute, RefreshDirectoryTimeout: 1 * time.Minute,
RefreshDirectoryInterval: 10 * time.Minute, RefreshDirectoryInterval: 10 * time.Minute,
QPS: 1.0, QPS: 1.0,

View file

@ -821,9 +821,9 @@ Be sure to include the intermediary certificate.
Default Upstream Timeout is the default timeout applied to a proxied route when no `timeout` key is specified by the policy. Default Upstream Timeout is the default timeout applied to a proxied route when no `timeout` key is specified by the policy.
### Headers ### Set Response Headers
- Environmental Variable: `HEADERS` - Environmental Variable: `SET_RESPONSE_HEADERS`
- Config File Key: `headers` - Config File Key: `set_response_headers`
- Type: map of `strings` key value pairs - Type: map of `strings` key value pairs
- Examples: - Examples:
@ -832,7 +832,7 @@ Default Upstream Timeout is the default timeout applied to a proxied route when
- YAML: - YAML:
```yaml ```yaml
headers: set_response_headers:
X-Test: X-Value X-Test: X-Value
``` ```
@ -847,7 +847,7 @@ Default Upstream Timeout is the default timeout applied to a proxied route when
Strict-Transport-Security:max-age=31536000; includeSubDomains; preload, Strict-Transport-Security:max-age=31536000; includeSubDomains; preload,
``` ```
Headers specifies a mapping of [HTTP Header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers) to be added globally to all managed routes and pomerium's authenticate service. Set Response Headers specifies a mapping of [HTTP Header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers) to be added globally to all managed routes and pomerium's authenticate service.
By default, conservative [secure HTTP headers](https://www.owasp.org/index.php/OWASP_Secure_Headers_Project) are set. By default, conservative [secure HTTP headers](https://www.owasp.org/index.php/OWASP_Secure_Headers_Project) are set.
@ -972,7 +972,7 @@ The connection string that the databroker service will use to connect to storage
For `redis`, the following URL types are supported: For `redis`, the following URL types are supported:
- simple: `redis://{username}:{password}@{host}:{port}/{db}` - simple: `redis://[username:password@]host:port/[db]`
- sentinel: `redis+sentinel://[:password@]host:port[,host2:port2,...]/[master_name[/db]][?param1=value1[&param2=value2&...]]` - sentinel: `redis+sentinel://[:password@]host:port[,host2:port2,...]/[master_name[/db]][?param1=value1[&param2=value2&...]]`
- cluster: `redis+cluster://[username:password@]host:port[,host2:port2,...]/[?param1=value1[&param2=value=2&...]]` - cluster: `redis+cluster://[username:password@]host:port[,host2:port2,...]/[?param1=value1[&param2=value=2&...]]`

View file

@ -931,11 +931,11 @@ settings:
Default Upstream Timeout is the default timeout applied to a proxied route when no `timeout` key is specified by the policy. Default Upstream Timeout is the default timeout applied to a proxied route when no `timeout` key is specified by the policy.
shortdoc: | shortdoc: |
Default Upstream Timeout is the default timeout applied to a proxied route when no timeout key is specified by the policy. Default Upstream Timeout is the default timeout applied to a proxied route when no timeout key is specified by the policy.
- name: "Headers" - name: "Set Response Headers"
keys: ["headers"] keys: ["set_response_headers"]
attributes: | attributes: |
- Environmental Variable: `HEADERS` - Environmental Variable: `SET_RESPONSE_HEADERS`
- Config File Key: `headers` - Config File Key: `set_response_headers`
- Type: map of `strings` key value pairs - Type: map of `strings` key value pairs
- Examples: - Examples:
@ -944,7 +944,7 @@ settings:
- YAML: - YAML:
```yaml ```yaml
headers: set_response_headers:
X-Test: X-Value X-Test: X-Value
``` ```
@ -959,7 +959,7 @@ settings:
Strict-Transport-Security:max-age=31536000; includeSubDomains; preload, Strict-Transport-Security:max-age=31536000; includeSubDomains; preload,
``` ```
doc: | doc: |
Headers specifies a mapping of [HTTP Header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers) to be added globally to all managed routes and pomerium's authenticate service. Set Response Headers specifies a mapping of [HTTP Header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers) to be added globally to all managed routes and pomerium's authenticate service.
By default, conservative [secure HTTP headers](https://www.owasp.org/index.php/OWASP_Secure_Headers_Project) are set. By default, conservative [secure HTTP headers](https://www.owasp.org/index.php/OWASP_Secure_Headers_Project) are set.
@ -1038,7 +1038,7 @@ settings:
- name: "The number of trusted hops" - name: "The number of trusted hops"
keys: ["xff_num_trusted_hops"] keys: ["xff_num_trusted_hops"]
attributes: | attributes: |
- Environmental Variable: `XFF_NUM_TRUSTED_HOPS - Environmental Variable: `XFF_NUM_TRUSTED_HOPS`
- Config File Key: `xff_num_trusted_hops` - Config File Key: `xff_num_trusted_hops`
- Type: `uint32` - Type: `uint32`
- Default: `0` - Default: `0`

View file

@ -200,7 +200,7 @@ func TestRedirect(t *testing.T) {
src := config.NewStaticSource(&config.Config{ src := config.NewStaticSource(&config.Config{
Options: &config.Options{ Options: &config.Options{
HTTPRedirectAddr: addr, HTTPRedirectAddr: addr,
Headers: map[string]string{ SetResponseHeaders: map[string]string{
"X-Frame-Options": "SAMEORIGIN", "X-Frame-Options": "SAMEORIGIN",
"X-XSS-Protection": "1; mode=block", "X-XSS-Protection": "1; mode=block",
"Strict-Transport-Security": "max-age=31536000; includeSubDomains; preload", "Strict-Transport-Security": "max-age=31536000; includeSubDomains; preload",
@ -229,7 +229,7 @@ func TestRedirect(t *testing.T) {
defer res.Body.Close() defer res.Body.Close()
assert.Equal(t, http.StatusMovedPermanently, res.StatusCode, "should redirect to https") assert.Equal(t, http.StatusMovedPermanently, res.StatusCode, "should redirect to https")
for k, v := range src.GetConfig().Options.Headers { for k, v := range src.GetConfig().Options.SetResponseHeaders {
assert.NotEqual(t, v, res.Header.Get(k), "should ignore options header") assert.NotEqual(t, v, res.Header.Get(k), "should ignore options header")
} }
} }

View file

@ -149,7 +149,7 @@ message Settings {
optional string certificate_authority_file = 35; optional string certificate_authority_file = 35;
optional string signing_key = 36; optional string signing_key = 36;
optional string signing_key_algorithm = 62; optional string signing_key_algorithm = 62;
map<string, string> headers = 69; map<string, string> set_response_headers = 69;
// repeated string jwt_claims_headers = 37; // repeated string jwt_claims_headers = 37;
map<string, string> jwt_claims_headers = 63; map<string, string> jwt_claims_headers = 63;
optional google.protobuf.Duration refresh_cooldown = 38; optional google.protobuf.Duration refresh_cooldown = 38;