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

Customizing HTTP headers in the config file #12485

Merged
merged 26 commits into from Oct 13, 2021
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
5bf301c
Customizing HTTP headers in the config file
hghaf099 Sep 2, 2021
9dfa689
Add changelog, fix bad imports
hghaf099 Sep 3, 2021
65f3d3a
fixing some bugs
hghaf099 Sep 3, 2021
61c12eb
fixing interaction of custom headers and /ui
hghaf099 Sep 3, 2021
f4232cd
Defining a member in core to set custom response headers
hghaf099 Sep 5, 2021
5ec2510
missing additional file
hghaf099 Sep 5, 2021
6982b8a
Some refactoring
hghaf099 Sep 7, 2021
f804b26
Adding automated tests for the feature
hghaf099 Sep 8, 2021
e52db45
Changing some error messages based on some recommendations
hghaf099 Sep 9, 2021
06ef62f
Incorporating custom response headers struct into the request context
hghaf099 Sep 14, 2021
ad93253
removing some unused references
hghaf099 Sep 14, 2021
0905ab2
fixing a test
hghaf099 Sep 14, 2021
33a4aa5
changing some error messages, removing a default header value from /ui
hghaf099 Sep 14, 2021
92867b4
fixing a test
hghaf099 Sep 14, 2021
2ae11ea
wrapping ResponseWriter to set the custom headers
hghaf099 Sep 16, 2021
434d8cb
adding a new test
hghaf099 Sep 16, 2021
6a106f0
some cleanup
hghaf099 Sep 16, 2021
7881067
removing some extra lines
hghaf099 Sep 16, 2021
e266713
Addressing comments
hghaf099 Sep 21, 2021
879d489
fixing some agent tests
hghaf099 Sep 21, 2021
2b7dd50
skipping custom headers from agent listener config,
hghaf099 Sep 22, 2021
0964ef8
Removing default custom headers, and renaming some function varibles
hghaf099 Oct 5, 2021
96cb6df
Merge branch 'main' into hghaf099-VAULT-3190-Parsing-Custom-HTTP-Headers
hghaf099 Oct 5, 2021
b6eedd1
some refacotring
hghaf099 Oct 7, 2021
08d0157
Refactoring and addressing comments
hghaf099 Oct 8, 2021
5734ab1
removing a function and fixing comments
hghaf099 Oct 12, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions changelog/12485.txt
@@ -0,0 +1,3 @@
```release-note:feature
**Customizable HTTP Headers**: Add support to define custom HTTP headers for root path (`/`) and also on API endpoints (`/v1/*`)
```
7 changes: 7 additions & 0 deletions command/agent/config/config.go
Expand Up @@ -35,6 +35,7 @@ func (c *Config) Prune() {
l.RawConfig = nil
l.Profiling.UnusedKeys = nil
l.Telemetry.UnusedKeys = nil
l.CustomResponseHeaders = nil
}
c.FoundKeys = nil
c.UnusedKeys = nil
Expand Down Expand Up @@ -172,6 +173,12 @@ func LoadConfig(path string) (*Config, error) {
if err != nil {
return nil, err
}

// Pruning custom headers for Agent for now
for _, ln := range sharedConfig.Listeners {
ln.CustomResponseHeaders = nil
}

result.SharedConfig = sharedConfig

list, ok := obj.Node.(*ast.ObjectList)
Expand Down
2 changes: 0 additions & 2 deletions command/agent/config/config_test.go
Expand Up @@ -536,7 +536,6 @@ func TestLoadConfigFile_AgentCache_PersistMissingType(t *testing.T) {
}

func TestLoadConfigFile_TemplateConfig(t *testing.T) {

testCases := map[string]struct {
fixturePath string
expectedTemplateConfig TemplateConfig
Expand Down Expand Up @@ -586,7 +585,6 @@ func TestLoadConfigFile_TemplateConfig(t *testing.T) {
}
})
}

}

// TestLoadConfigFile_Template tests template definitions in Vault Agent
Expand Down
6 changes: 6 additions & 0 deletions command/server.go
Expand Up @@ -1541,6 +1541,12 @@ func (c *ServerCommand) Run(args []string) int {

core.SetConfig(config)

// reloading custom response headers to make sure we have
// the most up to date headers after reloading the config file
if err = core.ReloadCustomResponseHeaders(); err != nil {
c.logger.Error(err.Error())
}

if config.LogLevel != "" {
configLogLevel := strings.ToLower(strings.TrimSpace(config.LogLevel))
switch configLogLevel {
Expand Down
109 changes: 109 additions & 0 deletions command/server/config_custom_response_headers_test.go
@@ -0,0 +1,109 @@
package server

import (
"fmt"
"testing"

"github.com/go-test/deep"
)

var defaultCustomHeaders = map[string]string{
"Strict-Transport-Security": "max-age=1; domains",
"Content-Security-Policy": "default-src 'others'",
"X-Vault-Ignored": "ignored",
"X-Custom-Header": "Custom header value default",
}

var customHeaders307 = map[string]string{
"X-Custom-Header": "Custom header value 307",
}

var customHeader3xx = map[string]string{
"X-Vault-Ignored-3xx": "Ignored 3xx",
"X-Custom-Header": "Custom header value 3xx",
}

var customHeaders200 = map[string]string{
"Someheader-200": "200",
"X-Custom-Header": "Custom header value 200",
}

var customHeader2xx = map[string]string{
"X-Custom-Header": "Custom header value 2xx",
}

var customHeader400 = map[string]string{
"Someheader-400": "400",
}

var defaultCustomHeadersMultiListener = map[string]string{
"Strict-Transport-Security": "max-age=31536000; includeSubDomains",
"Content-Security-Policy": "default-src 'others'",
"X-Vault-Ignored": "ignored",
"X-Custom-Header": "Custom header value default",
}

var defaultSTS = map[string]string{
"Strict-Transport-Security": "max-age=31536000; includeSubDomains",
}

func TestCustomResponseHeadersConfigs(t *testing.T) {
expectedCustomResponseHeader := map[string]map[string]string{
"default": defaultCustomHeaders,
"307": customHeaders307,
"3xx": customHeader3xx,
"200": customHeaders200,
"2xx": customHeader2xx,
"400": customHeader400,
}

config, err := LoadConfigFile("./test-fixtures/config_custom_response_headers_1.hcl")
if err != nil {
t.Fatalf("Error encountered when loading config %+v", err)
}
if diff := deep.Equal(expectedCustomResponseHeader, config.Listeners[0].CustomResponseHeaders); diff != nil {
t.Fatalf(fmt.Sprintf("parsed custom headers do not match the expected ones, difference: %v", diff))
}
}

func TestCustomResponseHeadersConfigsMultipleListeners(t *testing.T) {
expectedCustomResponseHeader := map[string]map[string]string{
"default": defaultCustomHeadersMultiListener,
"307": customHeaders307,
"3xx": customHeader3xx,
"200": customHeaders200,
"2xx": customHeader2xx,
"400": customHeader400,
}

config, err := LoadConfigFile("./test-fixtures/config_custom_response_headers_multiple_listeners.hcl")
if err != nil {
t.Fatalf("Error encountered when loading config %+v", err)
}
if diff := deep.Equal(expectedCustomResponseHeader, config.Listeners[0].CustomResponseHeaders); diff != nil {
t.Fatalf(fmt.Sprintf("parsed custom headers do not match the expected ones, difference: %v", diff))
}

if diff := deep.Equal(expectedCustomResponseHeader, config.Listeners[1].CustomResponseHeaders); diff == nil {
t.Fatalf(fmt.Sprintf("parsed custom headers do not match the expected ones, difference: %v", diff))
}
if diff := deep.Equal(expectedCustomResponseHeader["default"], config.Listeners[1].CustomResponseHeaders["default"]); diff != nil {
t.Fatalf(fmt.Sprintf("parsed custom headers do not match the expected ones, difference: %v", diff))
}

if diff := deep.Equal(expectedCustomResponseHeader, config.Listeners[2].CustomResponseHeaders); diff == nil {
t.Fatalf(fmt.Sprintf("parsed custom headers do not match the expected ones, difference: %v", diff))
}

if diff := deep.Equal(defaultSTS, config.Listeners[2].CustomResponseHeaders["default"]); diff != nil {
t.Fatalf(fmt.Sprintf("parsed custom headers do not match the expected ones, difference: %v", diff))
}

if diff := deep.Equal(expectedCustomResponseHeader, config.Listeners[3].CustomResponseHeaders); diff == nil {
ncabatoff marked this conversation as resolved.
Show resolved Hide resolved
t.Fatalf(fmt.Sprintf("parsed custom headers do not match the expected ones, difference: %v", diff))
}

if diff := deep.Equal(defaultSTS, config.Listeners[3].CustomResponseHeaders["default"]); diff != nil {
t.Fatalf(fmt.Sprintf("parsed custom headers do not match the expected ones, difference: %v", diff))
}
}
19 changes: 18 additions & 1 deletion command/server/config_test_helpers.go
Expand Up @@ -16,6 +16,12 @@ import (
"github.com/hashicorp/vault/internalshared/configutil"
)

var DefaultCustomHeaders = map[string]map[string]string {
"default": {
"Strict-Transport-Security": configutil.StrictTransportSecurity,
},
}

func boolPointer(x bool) *bool {
return &x
}
Expand All @@ -32,6 +38,7 @@ func testConfigRaftRetryJoin(t *testing.T) {
{
Type: "tcp",
Address: "127.0.0.1:8200",
CustomResponseHeaders: DefaultCustomHeaders,
},
},
DisableMlock: true,
Expand Down Expand Up @@ -64,6 +71,7 @@ func testLoadConfigFile_topLevel(t *testing.T, entropy *configutil.Entropy) {
{
Type: "tcp",
Address: "127.0.0.1:443",
CustomResponseHeaders: DefaultCustomHeaders,
},
},

Expand Down Expand Up @@ -174,10 +182,12 @@ func testLoadConfigFile_json2(t *testing.T, entropy *configutil.Entropy) {
{
Type: "tcp",
Address: "127.0.0.1:443",
CustomResponseHeaders: DefaultCustomHeaders,
},
{
Type: "tcp",
Address: "127.0.0.1:444",
CustomResponseHeaders: DefaultCustomHeaders,
},
},

Expand Down Expand Up @@ -336,6 +346,7 @@ func testLoadConfigFileIntegerAndBooleanValuesCommon(t *testing.T, path string)
{
Type: "tcp",
Address: "127.0.0.1:8200",
CustomResponseHeaders: DefaultCustomHeaders,
},
},
DisableMlock: true,
Expand Down Expand Up @@ -379,6 +390,7 @@ func testLoadConfigFile(t *testing.T) {
{
Type: "tcp",
Address: "127.0.0.1:443",
CustomResponseHeaders: DefaultCustomHeaders,
},
},

Expand Down Expand Up @@ -486,7 +498,7 @@ func testUnknownFieldValidation(t *testing.T) {
for _, er1 := range errors {
found := false
if strings.Contains(er1.String(), "sentinel") {
//This happens on OSS, and is fine
// This happens on OSS, and is fine
continue
}
for _, ex := range expected {
Expand Down Expand Up @@ -525,6 +537,7 @@ func testLoadConfigFile_json(t *testing.T) {
{
Type: "tcp",
Address: "127.0.0.1:443",
CustomResponseHeaders: DefaultCustomHeaders,
},
},

Expand Down Expand Up @@ -610,6 +623,7 @@ func testLoadConfigDir(t *testing.T) {
{
Type: "tcp",
Address: "127.0.0.1:443",
CustomResponseHeaders: DefaultCustomHeaders,
},
},

Expand Down Expand Up @@ -818,6 +832,7 @@ listener "tcp" {
Profiling: configutil.ListenerProfiling{
UnauthenticatedPProfAccess: true,
},
CustomResponseHeaders: DefaultCustomHeaders,
},
},
},
Expand Down Expand Up @@ -845,6 +860,7 @@ func testParseSeals(t *testing.T) {
{
Type: "tcp",
Address: "127.0.0.1:443",
CustomResponseHeaders: DefaultCustomHeaders,
},
},
Seals: []*configutil.KMS{
Expand Down Expand Up @@ -898,6 +914,7 @@ func testLoadConfigFileLeaseMetrics(t *testing.T) {
{
Type: "tcp",
Address: "127.0.0.1:443",
CustomResponseHeaders: DefaultCustomHeaders,
},
},

Expand Down
31 changes: 31 additions & 0 deletions command/server/test-fixtures/config_custom_response_headers_1.hcl
@@ -0,0 +1,31 @@
storage "inmem" {}
listener "tcp" {
address = "127.0.0.1:8200"
tls_disable = true
custom_response_headers {
"default" = {
"Strict-Transport-Security" = ["max-age=1","domains"],
"Content-Security-Policy" = ["default-src 'others'"],
"X-Vault-Ignored" = ["ignored"],
"X-Custom-Header" = ["Custom header value default"],
}
"307" = {
"X-Custom-Header" = ["Custom header value 307"],
}
"3xx" = {
"X-Vault-Ignored-3xx" = ["Ignored 3xx"],
"X-Custom-Header" = ["Custom header value 3xx"]
}
"200" = {
"someheader-200" = ["200"],
"X-Custom-Header" = ["Custom header value 200"]
}
"2xx" = {
"X-Custom-Header" = ["Custom header value 2xx"]
}
"400" = {
"someheader-400" = ["400"]
}
}
}
disable_mlock = true
@@ -0,0 +1,56 @@
storage "inmem" {}
listener "tcp" {
address = "127.0.0.1:8200"
tls_disable = true
custom_response_headers {
"default" = {
"Content-Security-Policy" = ["default-src 'others'"],
"X-Vault-Ignored" = ["ignored"],
"X-Custom-Header" = ["Custom header value default"],
}
"307" = {
"X-Custom-Header" = ["Custom header value 307"],
}
"3xx" = {
"X-Vault-Ignored-3xx" = ["Ignored 3xx"],
"X-Custom-Header" = ["Custom header value 3xx"]
}
"200" = {
"someheader-200" = ["200"],
"X-Custom-Header" = ["Custom header value 200"]
}
"2xx" = {
"X-Custom-Header" = ["Custom header value 2xx"]
}
"400" = {
"someheader-400" = ["400"]
}
}
}
listener "tcp" {
address = "127.0.0.2:8200"
tls_disable = true
custom_response_headers {
"default" = {
"Content-Security-Policy" = ["default-src 'others'"],
"X-Vault-Ignored" = ["ignored"],
"X-Custom-Header" = ["Custom header value default"],
}
}
}
listener "tcp" {
address = "127.0.0.3:8200"
tls_disable = true
custom_response_headers {
"2xx" = {
"X-Custom-Header" = ["Custom header value 2xx"]
}
}
}
listener "tcp" {
address = "127.0.0.4:8200"
tls_disable = true
}


disable_mlock = true