VAULT-26466: audit - include correlation ID headers by default (#26777)

* Include correlation ID header by default for audit

* changelog

* casing adjustment
This commit is contained in:
Peter Wilson
2024-05-08 12:36:00 +01:00
committed by GitHub
parent b64b9b15f0
commit d66461ef07
3 changed files with 77 additions and 4 deletions

View File

@@ -163,6 +163,21 @@ func (a *HeadersConfig) Remove(ctx context.Context, header string) error {
return nil
}
// DefaultHeaders can be used to retrieve the set of default headers that will be
// added to HeadersConfig in order to allow them to appear in audit logs in a raw
// format. If the Vault Operator adds their own setting for any of the defaults,
// their setting will be honored.
func (a *HeadersConfig) DefaultHeaders() map[string]*HeaderSettings {
// Support deprecated 'x-' prefix (https://datatracker.ietf.org/doc/html/rfc6648)
const correlationID = "correlation-id"
xCorrelationID := fmt.Sprintf("x-%s", correlationID)
return map[string]*HeaderSettings{
correlationID: {},
xCorrelationID: {},
}
}
// Invalidate attempts to refresh the allowed audit headers and their settings.
// NOTE: Invalidate will acquire a write lock in order to update the underlying headers.
func (a *HeadersConfig) Invalidate(ctx context.Context) error {
@@ -192,6 +207,14 @@ func (a *HeadersConfig) Invalidate(ctx context.Context) error {
lowerHeaders[strings.ToLower(k)] = v
}
// Ensure that we have default headers configured to appear in the audit log.
// Add them if they're missing.
for header, setting := range a.DefaultHeaders() {
if _, ok := lowerHeaders[header]; !ok {
lowerHeaders[header] = setting
}
}
a.headerSettings = lowerHeaders
return nil
}

View File

@@ -458,7 +458,7 @@ func TestAuditedHeaders_invalidate(t *testing.T) {
// Invalidate and check we now see the header we stored
err = ahc.Invalidate(context.Background())
require.NoError(t, err)
require.Len(t, ahc.headerSettings, 1)
require.Equal(t, len(ahc.DefaultHeaders())+1, len(ahc.headerSettings)) // (defaults + 1).
_, ok := ahc.headerSettings["x-magic-header"]
require.True(t, ok)
@@ -475,7 +475,7 @@ func TestAuditedHeaders_invalidate(t *testing.T) {
// Invalidate and check we now see the header we stored
err = ahc.Invalidate(context.Background())
require.NoError(t, err)
require.Len(t, ahc.headerSettings, 2)
require.Equal(t, len(ahc.DefaultHeaders())+2, len(ahc.headerSettings)) // (defaults + 2 new headers)
_, ok = ahc.headerSettings["x-magic-header"]
require.True(t, ok)
_, ok = ahc.headerSettings["x-even-more-magic-header"]
@@ -502,7 +502,7 @@ func TestAuditedHeaders_invalidate_nil_view(t *testing.T) {
// Invalidate and check we now see the header we stored
err = ahc.Invalidate(context.Background())
require.NoError(t, err)
require.Len(t, ahc.headerSettings, 1)
require.Equal(t, len(ahc.DefaultHeaders())+1, len(ahc.headerSettings)) // defaults + 1
_, ok := ahc.headerSettings["x-magic-header"]
require.True(t, ok)
@@ -516,7 +516,7 @@ func TestAuditedHeaders_invalidate_nil_view(t *testing.T) {
// Invalidate should clear out the existing headers without error
err = ahc.Invalidate(context.Background())
require.NoError(t, err)
require.Len(t, ahc.headerSettings, 0)
require.Equal(t, len(ahc.DefaultHeaders()), len(ahc.headerSettings)) // defaults
}
// TestAuditedHeaders_invalidate_bad_data ensures that we correctly error if the
@@ -584,3 +584,49 @@ func TestAuditedHeaders_headers(t *testing.T) {
require.Equal(t, true, s["juan"].HMAC)
require.Equal(t, false, s["john"].HMAC)
}
// TestAuditedHeaders_invalidate_defaults checks that we ensure any 'default' headers
// are present after invalidation, and if they were loaded from storage then they
// do not get overwritten with our defaults.
func TestAuditedHeaders_invalidate_defaults(t *testing.T) {
t.Parallel()
view := newMockStorage(t)
ahc, err := NewHeadersConfig(view)
require.NoError(t, err)
require.Len(t, ahc.headerSettings, 0)
// Store some data using the view.
fakeHeaders1 := map[string]*HeaderSettings{"x-magic-header": {}}
fakeBytes1, err := json.Marshal(fakeHeaders1)
require.NoError(t, err)
err = view.Put(context.Background(), &logical.StorageEntry{Key: auditedHeadersEntry, Value: fakeBytes1})
require.NoError(t, err)
// Invalidate and check we now see the header we stored
err = ahc.Invalidate(context.Background())
require.NoError(t, err)
require.Equal(t, len(ahc.DefaultHeaders())+1, len(ahc.headerSettings)) // (defaults + 1 new header)
_, ok := ahc.headerSettings["x-magic-header"]
require.True(t, ok)
s, ok := ahc.headerSettings["x-correlation-id"]
require.True(t, ok)
require.False(t, s.HMAC)
// Add correlation ID specifically with HMAC and make sure it doesn't get blasted away.
fakeHeaders1 = map[string]*HeaderSettings{"x-magic-header": {}, "X-Correlation-ID": {HMAC: true}}
fakeBytes1, err = json.Marshal(fakeHeaders1)
require.NoError(t, err)
err = view.Put(context.Background(), &logical.StorageEntry{Key: auditedHeadersEntry, Value: fakeBytes1})
require.NoError(t, err)
// Invalidate and check we now see the header we stored
err = ahc.Invalidate(context.Background())
require.NoError(t, err)
require.Equal(t, len(ahc.DefaultHeaders())+1, len(ahc.headerSettings)) // (defaults + 1 new header, 1 is also a default)
_, ok = ahc.headerSettings["x-magic-header"]
require.True(t, ok)
s, ok = ahc.headerSettings["x-correlation-id"]
require.True(t, ok)
require.True(t, s.HMAC)
}

4
changelog/26777.txt Normal file
View File

@@ -0,0 +1,4 @@
```release-note:change
audit: breaking change - Vault now allows audit logs to contain 'correlation-id' and 'x-correlation-id' headers when they
are present in the incoming request. By default they are not HMAC'ed (but can be configured to HMAC by Vault Operators).
```