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

Conversation

hghaf099
Copy link
Contributor

@hghaf099 hghaf099 commented Sep 2, 2021

  • Setting default security headers: Content-Security-Policy, X-XSS-Protection, X-Frame-Options, X-Content-Type-Options, Strict-Transport-Security, Content-Type
  • Parsing from config file: Parsing the configured custom HTTP response headers from the configuration file defined under "custom_response_headers" sub stanza. The structure of the sub-stanza is a map of status codes to a map of headers and headers valuse.
  • Response headers for all endpoints: Returning custom HTTP headers for all endpoints
  • Status code based response headers: Depending on a response status code, a set of headers is set. The most common headers are defined under the "default" status code. These are set regardless of the status code. Then, the headers defined under a collection status code are set (for example, headers defined under "2xx" are set for all 2 hundred status codes. Finally, the headers defined under the most specific status code are set.
  • Reloading configuration: Making sure Reloading config would not hault the server and changes to the "custom_response_headers" sub stanza are reflected in the response headers.
  • correctly interact with /ui endpoint. As we don’t want to allow setting headers in multiple places, if a custom header is configured in the /ui endpoint that is equal to the one configured in the configuration file, then, an error is returned stating the header already exists in the HCL configuration file. Suppose it happens that a user configures a header X using the /ui endpoint, where X is not present in the configuration file, then Vault is restarted with a modified configuration file where X is present. Then, an error is logged at startup stating that the header is configured both in the /ui endpoint and in the configuration file. However, the response header is set with the value of X set in the configuration file.
  • Restricting some patterns: headers names with "X-Vault-" prefix are not allowed.

@vercel vercel bot temporarily deployed to Preview – vault-storybook September 3, 2021 00:04 Inactive
@vercel vercel bot temporarily deployed to Preview – vault September 3, 2021 00:04 Inactive
@vercel vercel bot temporarily deployed to Preview – vault September 3, 2021 17:55 Inactive
@vercel vercel bot temporarily deployed to Preview – vault-storybook September 3, 2021 17:55 Inactive
@vercel vercel bot temporarily deployed to Preview – vault-storybook September 3, 2021 20:59 Inactive
@vercel vercel bot temporarily deployed to Preview – vault September 3, 2021 20:59 Inactive
@vercel vercel bot temporarily deployed to Preview – vault-storybook September 5, 2021 00:31 Inactive
@vercel vercel bot temporarily deployed to Preview – vault September 5, 2021 00:31 Inactive
@vercel vercel bot temporarily deployed to Preview – vault-storybook September 5, 2021 00:34 Inactive
@vercel vercel bot temporarily deployed to Preview – vault September 5, 2021 00:34 Inactive
@vercel vercel bot temporarily deployed to Preview – vault September 7, 2021 21:12 Inactive
@vercel vercel bot temporarily deployed to Preview – vault-storybook September 7, 2021 21:12 Inactive
@hghaf099 hghaf099 requested a review from a team September 7, 2021 22:29
@vercel vercel bot temporarily deployed to Preview – vault September 8, 2021 23:18 Inactive
@vercel vercel bot temporarily deployed to Preview – vault-storybook September 8, 2021 23:18 Inactive
@vercel vercel bot temporarily deployed to Preview – vault September 9, 2021 15:12 Inactive
@vercel vercel bot temporarily deployed to Preview – vault-storybook September 9, 2021 15:12 Inactive
command/agent.go Outdated
logical.RespondError(w,
http.StatusPreconditionFailed,
errors.New(fmt.Sprintf("missing '%s' header", consts.RequestHeaderName)))
err := errors.New(fmt.Sprintf("missing '%s' header", consts.RequestHeaderName))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could be fmt.Errorf() since you're doing a format string

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

command/agent.go Outdated Show resolved Hide resolved
Copy link
Contributor

@finnigja finnigja left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice to see this feature under development! Added a question re. the Cache-Control header that is currently set separately...

http/logical.go Outdated Show resolved Hide resolved
Copy link
Contributor

@mickael-hc mickael-hc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for working on this @hghaf099 , this definitely be helpful to many organizations with stricter requirements. I've left some comments inline

internalshared/configutil/http_response_headers.go Outdated Show resolved Hide resolved
http/logical.go Outdated Show resolved Hide resolved
vault/custom_response_headers.go Show resolved Hide resolved
"5xx",
}

const (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great - these defaults will allow us to update these over time. Do we offer the ability to unset a header value? Since we are specifying (sensible but arbitrary) defaults, perhaps end-users will want to strip headers?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These headers cannot be unset for now, however, if an operator is not happy with the default values, they can simply configure their desired value in the configuration file. I will discuss the idea of providing a way to unset the defaults or even not set any defaults with our team to see what they think about it! Thanks for raising this up.

internalshared/configutil/http_response_headers.go Outdated Show resolved Hide resolved
@vercel vercel bot temporarily deployed to Preview – vault-storybook September 22, 2021 00:14 Inactive
changelog/12485.txt Outdated Show resolved Hide resolved
Comment on lines +38 to +45
status := resp.Data[logical.HTTPStatusCode].(int)
w.Header().Set("Content-Type", resp.Data[logical.HTTPContentType].(string))
switch v := resp.Data[logical.HTTPRawBody].(type) {
case string:
w.WriteHeader(status)
w.Write([]byte(v))
case []byte:
w.WriteHeader(status)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I might be missing something subtle but the proposed changes look functionally equivalent to the prior logic. A call to w.WriteHeader is being done in both the string and []byte cases. Is there a reason that the outer call to w.WriteHeader on line 38 cannot be kept as is?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, once the WriteHeader is called, setting other headers would be ignored. So, in this case, the "Content-Type" will be ignored which it seems like an old bug. Another pointer is that the responseError function also sets some headers and calls the WriteHeader. So, I made the change to accommodate the two cases here.

internalshared/configutil/http_response_headers.go Outdated Show resolved Hide resolved
internalshared/configutil/http_response_headers.go Outdated Show resolved Hide resolved
for _, crh := range customResponseHeader {
for statusCode, responseHeader := range crh {
if _, ok := responseHeader.([]map[string]interface{}); !ok {
return nil, fmt.Errorf("response headers were not configured correctly. please make sure they're in a map")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment here as above re: type assertion and error message.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

func parseHeaderValues(h interface{}) (string, error) {
var sl []string
if _, ok := h.([]interface{}); !ok {
return "", fmt.Errorf("headers must be given in a list of strings")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this function should always be called with a list of strings, why not specify the type of the argument h as []string instead of interface{}? You'd be able to avoid a lot of type assertions in this function.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is called from ParseCustomResponseHeaders which its input is an interface parsed using HCL config parser. So, the assertion should happen anyways either in this function or the functions down in the stack. This was the most relevant place for those.

vault/core.go Outdated Show resolved Hide resolved
@vercel vercel bot temporarily deployed to Preview – vault October 5, 2021 23:19 Inactive
@vercel vercel bot temporarily deployed to Preview – vault-storybook October 5, 2021 23:19 Inactive
http/handler.go Outdated
Comment on lines 275 to 278
// Checking the validity of the status code
if status >= 600 || status < 100 {
return
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might up above the setter assignment. It will keep all the early return logic based on input validation close together making the function easier to read.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good! Thanks

vault/core.go Outdated Show resolved Hide resolved
@vercel vercel bot temporarily deployed to Preview – vault October 7, 2021 01:29 Inactive
@vercel vercel bot temporarily deployed to Preview – vault-storybook October 7, 2021 01:29 Inactive
http/handler.go Outdated Show resolved Hide resolved
http/handler.go Outdated Show resolved Hide resolved
http/handler.go Show resolved Hide resolved
http/custom_header_test.go Outdated Show resolved Hide resolved
vault/core.go Outdated Show resolved Hide resolved
vault/custom_response_headers.go Show resolved Hide resolved
vault/custom_response_headers.go Show resolved Hide resolved
vault/custom_response_headers.go Outdated Show resolved Hide resolved
vault/custom_response_headers_test.go Show resolved Hide resolved
@vercel vercel bot temporarily deployed to Preview – vault October 8, 2021 20:09 Inactive
@vercel vercel bot temporarily deployed to Preview – vault-storybook October 8, 2021 20:09 Inactive
vault/core.go Outdated Show resolved Hide resolved
@vercel vercel bot temporarily deployed to Preview – vault-storybook October 12, 2021 17:30 Inactive
@vercel vercel bot temporarily deployed to Preview – vault October 12, 2021 17:30 Inactive
@hghaf099 hghaf099 merged commit e0bfb73 into main Oct 13, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

8 participants