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

Configure the request headers that are output to the audit log #2321

Merged
merged 20 commits into from
Feb 2, 2017

Conversation

briankassouf
Copy link
Contributor

@briankassouf briankassouf commented Feb 1, 2017

Adds a header object to the audit log's request object:

"request": {
        "client_token": "",
        "data": null,
        "operation": "read",
        "path": "auth/token/lookup-self",
        "remote_address": "192.168.5.5",
        "headers": {
           "X-Forwarded-For: "127.0.0.1",
           "Content-Type": "application/json"
         }
  },

implements #1451

@briankassouf briankassouf added this to the 0.6.5 milestone Feb 1, 2017
// Key used in the BarrierView to store and retrieve the header config
auditedHeadersEntry = "audited_headers"
// Path used to create a sub view off of BarrierView
auditedHeadersSubPath = "auditedHeadersConfig/"
Copy link
Member

@jefferai jefferai Feb 2, 2017

Choose a reason for hiding this comment

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

Minor nitpick but we don't usually camelcase paths in the data store, but rather use hyphens. So it'd be sys/audited-headers-config/audited-headers.

a.RLock()
defer a.RUnlock()

result = make(map[string][]string)
Copy link
Member

Choose a reason for hiding this comment

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

You can make this a tad faster by using a second argument to make and use the length of the existing map, so in this case len(a.Headers) would be a hard upper bound -- might be a bit overallocated but better than a second allocation.

// AuditedHeadersConfig is used by the Audit Broker to write only approved
// headers to the audit logs. It uses a BarrierView to persist the settings.
type AuditedHeadersConfig struct {
Headers map[string]*auditedHeaderSettings
Copy link
Member

Choose a reason for hiding this comment

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

Can we rename this to HeaderSettings or something, to better indicate what this actually holds?

func (a *AuditedHeadersConfig) add(header string, hmac bool) error {
a.Lock()
defer a.Unlock()

Copy link
Member

Choose a reason for hiding this comment

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

Can we check for header being ""?

func (a *AuditedHeadersConfig) ApplyConfig(headers map[string][]string, hashFunc func(string) string) (result map[string][]string) {
a.RLock()
defer a.RUnlock()

Copy link
Member

Choose a reason for hiding this comment

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

Can we add more comments in this function?

}

// add adds or overwrites a header in the config and updates the barrier view
func (a *AuditedHeadersConfig) add(header string, hmac bool) error {
Copy link
Member

Choose a reason for hiding this comment

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

[Optional] Since there is only one setting and since this is an internal implementation, I guess this looks okay. I was wondering if this should take in *auditedHeaderSettings.

t.Fatalf("Error decoding header view: %s", err)
}

expected["X-Vault-Header"] = &auditedHeaderSettings{
Copy link
Member

Choose a reason for hiding this comment

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

Correct me if I am missing something here. Shouldn't we expect two header entries at this point? One for X-Test-Header and one for X-Vault-Header?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Correct! This line is adding a "X-Vault-Header" key to the existing expected map


}

func BenchmarkAuditedHeaderConfig_ApplyConfig(b *testing.B) {
Copy link
Member

Choose a reason for hiding this comment

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

This is super nice! I guess we should adopt more of such functions while writing tests. 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

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

👍 It's nice to know how much time we could potentially be adding to each request

return logical.ErrorResponse("missing header name"), nil
}

headerConfig.add(header, hmac)
Copy link
Member

Choose a reason for hiding this comment

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

Can we handle the error responded by add here?

if header == "" {
return logical.ErrorResponse("missing header name"), nil
}
headerConfig.remove(header)
Copy link
Member

Choose a reason for hiding this comment

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

Can we handle the return values of remove?


// handleAuditedHeaderRead returns the header configuration for the given header name
func (b *SystemBackend) handleAuditedHeaderRead(req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
headerConfig := b.Core.AuditedHeadersConfig()
Copy link
Member

Choose a reason for hiding this comment

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

In all the functions, this can be moved after the input validation.

@@ -621,6 +622,38 @@ func NewSystemBackend(core *Core, config *logical.BackendConfig) (logical.Backen
HelpSynopsis: strings.TrimSpace(sysHelp["rewrap"][0]),
HelpDescription: strings.TrimSpace(sysHelp["rewrap"][1]),
},

&framework.Path{
Pattern: "config/audited-headers/(?P<header>.+)",
Copy link
Member

Choose a reason for hiding this comment

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

Thinking about this, I wonder if we should actually have any public-facing parts of this refer to auditing configuration at a top-level and header configuration at a lower level. So this would become config/auditing/headers (or maybe config/auditing/request-headers to be explicit). That way if we want to add any other auditing-related configuration at a later time we don't need a totally separate path. (For instance, if we want to configure the system default for HMAC-ing accessors.)

@@ -40,6 +40,7 @@ func NewSystemBackend(core *Core, config *logical.BackendConfig) (logical.Backen
"audit/*",
"raw/*",
"rotate",
"config/*",
Copy link
Member

Choose a reason for hiding this comment

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

I wonder if this really needs sudo. e.g. for config/cors it probably won't be needed. Maybe just sudo on the relevant auditing paths under config/?

Copy link
Member

@jefferai jefferai left a comment

Choose a reason for hiding this comment

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

There's one test to fix (root paths), other than that LGTM

Copy link
Member

@vishalnayak vishalnayak left a comment

Choose a reason for hiding this comment

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

LGTM!

@briankassouf briankassouf merged commit 590b568 into master Feb 2, 2017
@briankassouf briankassouf deleted the audit-headers branch February 2, 2017 19:49
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.

3 participants