open-vault/vault/audited_headers.go
Brian Kassouf 6701ba8a10 Configure the request headers that are output to the audit log (#2321)
* Add /sys/config/audited-headers endpoint for configuring the headers that will be audited

* Remove some debug lines

* Add a persistant layer and refactor a bit

* update the api endpoints to be more restful

* Add comments and clean up a few functions

* Remove unneeded hash structure functionaility

* Fix existing tests

* Add tests

* Add test for Applying the header config

* Add Benchmark for the ApplyConfig method

* ResetTimer on the benchmark:

* Update the headers comment

* Add test for audit broker

* Use hyphens instead of camel case

* Add size paramater to the allocation of the result map

* Fix the tests for the audit broker

* PR feedback

* update the path and permissions on config/* paths

* Add docs file

* Fix TestSystemBackend_RootPaths test
2017-02-02 11:49:20 -08:00

130 lines
3.1 KiB
Go

package vault
import (
"fmt"
"sync"
"github.com/hashicorp/vault/logical"
)
const (
// 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 = "audited-headers-config/"
)
type auditedHeaderSettings struct {
HMAC bool `json:"hmac"`
}
// 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
view *BarrierView
sync.RWMutex
}
// add adds or overwrites a header in the config and updates the barrier view
func (a *AuditedHeadersConfig) add(header string, hmac bool) error {
if header == "" {
return fmt.Errorf("header value cannot be empty")
}
// Grab a write lock
a.Lock()
defer a.Unlock()
a.Headers[header] = &auditedHeaderSettings{hmac}
entry, err := logical.StorageEntryJSON(auditedHeadersEntry, a.Headers)
if err != nil {
return fmt.Errorf("failed to persist audited headers config: %v", err)
}
if err := a.view.Put(entry); err != nil {
return fmt.Errorf("failed to persist audited headers config: %v", err)
}
return nil
}
// remove deletes a header out of the header config and updates the barrier view
func (a *AuditedHeadersConfig) remove(header string) error {
if header == "" {
return fmt.Errorf("header value cannot be empty")
}
// Grab a write lock
a.Lock()
defer a.Unlock()
delete(a.Headers, header)
entry, err := logical.StorageEntryJSON(auditedHeadersEntry, a.Headers)
if err != nil {
return fmt.Errorf("failed to persist audited headers config: %v", err)
}
if err := a.view.Put(entry); err != nil {
return fmt.Errorf("failed to persist audited headers config: %v", err)
}
return nil
}
// ApplyConfig returns a map of approved headers and their values, either
// hmac'ed or plaintext
func (a *AuditedHeadersConfig) ApplyConfig(headers map[string][]string, hashFunc func(string) string) (result map[string][]string) {
// Grab a read lock
a.RLock()
defer a.RUnlock()
result = make(map[string][]string, len(a.Headers))
for key, settings := range a.Headers {
if val, ok := headers[key]; ok {
// copy the header values so we don't overwrite them
hVals := make([]string, len(val))
copy(hVals, val)
// Optionally hmac the values
if settings.HMAC {
for i, el := range hVals {
hVals[i] = hashFunc(el)
}
}
result[key] = hVals
}
}
return
}
// Initalize the headers config by loading from the barrier view
func (c *Core) setupAuditedHeadersConfig() error {
// Create a sub-view
view := c.systemBarrierView.SubView(auditedHeadersSubPath)
// Create the config
out, err := view.Get(auditedHeadersEntry)
if err != nil {
return fmt.Errorf("failed to read config: %v", err)
}
headers := make(map[string]*auditedHeaderSettings)
if out != nil {
err = out.DecodeJSON(&headers)
if err != nil {
return err
}
}
c.auditedHeaders = &AuditedHeadersConfig{
Headers: headers,
view: view,
}
return nil
}