diff --git a/audit/entry_formatter.go b/audit/entry_formatter.go index af404e1d10..a6b836d0a4 100644 --- a/audit/entry_formatter.go +++ b/audit/entry_formatter.go @@ -29,7 +29,7 @@ var ( ) // NewEntryFormatter should be used to create an EntryFormatter. -// Accepted options: WithPrefix. +// Accepted options: WithPrefix, WithHeaderFormatter. func NewEntryFormatter(config FormatterConfig, salter Salter, opt ...Option) (*EntryFormatter, error) { const op = "audit.NewEntryFormatter" @@ -80,7 +80,7 @@ func (f *EntryFormatter) Process(ctx context.Context, e *eventlogger.Event) (*ev return nil, fmt.Errorf("%s: event is nil: %w", op, event.ErrInvalidParameter) } - a, ok := e.Payload.(*auditEvent) + a, ok := e.Payload.(*AuditEvent) if !ok { return nil, fmt.Errorf("%s: cannot parse event payload: %w", op, event.ErrInvalidParameter) } diff --git a/audit/event.go b/audit/event.go index d7b60b3df5..6ea3f18491 100644 --- a/audit/event.go +++ b/audit/event.go @@ -12,7 +12,7 @@ import ( // NewEvent should be used to create an audit event. The subtype field is needed // for audit events. It will generate an ID if no ID is supplied. Supported // options: WithID, WithNow. -func NewEvent(s subtype, opt ...Option) (*auditEvent, error) { +func NewEvent(s subtype, opt ...Option) (*AuditEvent, error) { const op = "audit.newEvent" // Get the default options @@ -30,7 +30,7 @@ func NewEvent(s subtype, opt ...Option) (*auditEvent, error) { } } - audit := &auditEvent{ + audit := &AuditEvent{ ID: opts.withID, Timestamp: opts.withNow, Version: version, @@ -44,8 +44,8 @@ func NewEvent(s subtype, opt ...Option) (*auditEvent, error) { } // validate attempts to ensure the audit event in its present state is valid. -func (a *auditEvent) validate() error { - const op = "audit.(auditEvent).validate" +func (a *AuditEvent) validate() error { + const op = "audit.(AuditEvent).validate" if a == nil { return fmt.Errorf("%s: event is nil: %w", op, event.ErrInvalidParameter) diff --git a/audit/event_test.go b/audit/event_test.go index d6249fd1b8..7a520e3483 100644 --- a/audit/event_test.go +++ b/audit/event_test.go @@ -29,14 +29,14 @@ func TestAuditEvent_new(t *testing.T) { Subtype: subtype(""), Format: format(""), IsErrorExpected: true, - ExpectedErrorMessage: "audit.newEvent: audit.(auditEvent).validate: audit.(subtype).validate: '' is not a valid event subtype: invalid parameter", + ExpectedErrorMessage: "audit.newEvent: audit.(AuditEvent).validate: audit.(subtype).validate: '' is not a valid event subtype: invalid parameter", }, "empty-Option": { Options: []Option{}, Subtype: subtype(""), Format: format(""), IsErrorExpected: true, - ExpectedErrorMessage: "audit.newEvent: audit.(auditEvent).validate: audit.(subtype).validate: '' is not a valid event subtype: invalid parameter", + ExpectedErrorMessage: "audit.newEvent: audit.(AuditEvent).validate: audit.(subtype).validate: '' is not a valid event subtype: invalid parameter", }, "bad-id": { Options: []Option{WithID("")}, @@ -108,22 +108,22 @@ func TestAuditEvent_new(t *testing.T) { // TestAuditEvent_Validate exercises the validation for an audit event. func TestAuditEvent_Validate(t *testing.T) { tests := map[string]struct { - Value *auditEvent + Value *AuditEvent IsErrorExpected bool ExpectedErrorMessage string }{ "nil": { Value: nil, IsErrorExpected: true, - ExpectedErrorMessage: "audit.(auditEvent).validate: event is nil: invalid parameter", + ExpectedErrorMessage: "audit.(AuditEvent).validate: event is nil: invalid parameter", }, "default": { - Value: &auditEvent{}, + Value: &AuditEvent{}, IsErrorExpected: true, - ExpectedErrorMessage: "audit.(auditEvent).validate: missing ID: invalid parameter", + ExpectedErrorMessage: "audit.(AuditEvent).validate: missing ID: invalid parameter", }, "id-empty": { - Value: &auditEvent{ + Value: &AuditEvent{ ID: "", Version: version, Subtype: RequestType, @@ -131,10 +131,10 @@ func TestAuditEvent_Validate(t *testing.T) { Data: nil, }, IsErrorExpected: true, - ExpectedErrorMessage: "audit.(auditEvent).validate: missing ID: invalid parameter", + ExpectedErrorMessage: "audit.(AuditEvent).validate: missing ID: invalid parameter", }, "version-fiddled": { - Value: &auditEvent{ + Value: &AuditEvent{ ID: "audit_123", Version: "magic-v2", Subtype: RequestType, @@ -142,10 +142,10 @@ func TestAuditEvent_Validate(t *testing.T) { Data: nil, }, IsErrorExpected: true, - ExpectedErrorMessage: "audit.(auditEvent).validate: event version unsupported: invalid parameter", + ExpectedErrorMessage: "audit.(AuditEvent).validate: event version unsupported: invalid parameter", }, "subtype-fiddled": { - Value: &auditEvent{ + Value: &AuditEvent{ ID: "audit_123", Version: version, Subtype: subtype("moon"), @@ -153,10 +153,10 @@ func TestAuditEvent_Validate(t *testing.T) { Data: nil, }, IsErrorExpected: true, - ExpectedErrorMessage: "audit.(auditEvent).validate: audit.(subtype).validate: 'moon' is not a valid event subtype: invalid parameter", + ExpectedErrorMessage: "audit.(AuditEvent).validate: audit.(subtype).validate: 'moon' is not a valid event subtype: invalid parameter", }, "default-time": { - Value: &auditEvent{ + Value: &AuditEvent{ ID: "audit_123", Version: version, Subtype: ResponseType, @@ -164,10 +164,10 @@ func TestAuditEvent_Validate(t *testing.T) { Data: nil, }, IsErrorExpected: true, - ExpectedErrorMessage: "audit.(auditEvent).validate: event timestamp cannot be the zero time instant: invalid parameter", + ExpectedErrorMessage: "audit.(AuditEvent).validate: event timestamp cannot be the zero time instant: invalid parameter", }, "valid": { - Value: &auditEvent{ + Value: &AuditEvent{ ID: "audit_123", Version: version, Subtype: ResponseType, diff --git a/audit/sink_wrapper.go b/audit/sink_wrapper.go index 9edf5c1783..3cf79c7095 100644 --- a/audit/sink_wrapper.go +++ b/audit/sink_wrapper.go @@ -12,7 +12,7 @@ import ( ) // SinkWrapper is a wrapper for any kind of Sink Node that processes events -// containing an auditEvent payload. +// containing an AuditEvent payload. type SinkWrapper struct { Name string Sink eventlogger.Node @@ -23,7 +23,7 @@ type SinkWrapper struct { // once this method returns. func (s *SinkWrapper) Process(ctx context.Context, e *eventlogger.Event) (*eventlogger.Event, error) { defer func() { - auditEvent, ok := e.Payload.(*auditEvent) + auditEvent, ok := e.Payload.(*AuditEvent) if ok { metrics.MeasureSince([]string{"audit", s.Name, auditEvent.Subtype.MetricTag()}, e.CreatedAt) } diff --git a/audit/types.go b/audit/types.go index 0b0f3982a2..af5a5830df 100644 --- a/audit/types.go +++ b/audit/types.go @@ -35,8 +35,8 @@ type subtype string // format defines types of format audit events support. type format string -// auditEvent is the audit event. -type auditEvent struct { +// AuditEvent is the audit event. +type AuditEvent struct { ID string `json:"id"` Version string `json:"version"` Subtype subtype `json:"subtype"` // the subtype of the audit event. diff --git a/helper/testhelpers/corehelpers/corehelpers.go b/helper/testhelpers/corehelpers/corehelpers.go index 8e0e449ad5..b8d1def9ca 100644 --- a/helper/testhelpers/corehelpers/corehelpers.go +++ b/helper/testhelpers/corehelpers/corehelpers.go @@ -6,9 +6,10 @@ package corehelpers import ( - "bytes" "context" "crypto/sha256" + "encoding/json" + "errors" "fmt" "io" "os" @@ -29,6 +30,11 @@ import ( "github.com/mitchellh/go-testing-interface" ) +var ( + _ audit.Backend = (*NoopAudit)(nil) + _ eventlogger.Node = (*noopWrapper)(nil) +) + var externalPlugins = []string{"transform", "kmip", "keymgmt"} // RetryUntil runs f until it returns a nil result or the timeout is reached. @@ -210,52 +216,51 @@ func (m *mockBuiltinRegistry) DeprecationStatus(name string, pluginType consts.P return consts.Unknown, false } -func TestNoopAudit(t testing.T, config map[string]string) *NoopAudit { - n, err := NewNoopAudit(config) +func TestNoopAudit(t testing.T, path string, config map[string]string, opts ...audit.Option) *NoopAudit { + cfg := &audit.BackendConfig{Config: config, MountPath: path} + n, err := NewNoopAudit(cfg, opts...) if err != nil { t.Fatal(err) } return n } -func NewNoopAudit(config map[string]string) (*NoopAudit, error) { +// NewNoopAudit should be used to create a NoopAudit as it handles creation of a +// predictable salt and wraps eventlogger nodes so information can be retrieved on +// what they've seen or formatted. +func NewNoopAudit(config *audit.BackendConfig, opts ...audit.Option) (*NoopAudit, error) { view := &logical.InmemStorage{} - err := view.Put(context.Background(), &logical.StorageEntry{ - Key: "salt", - Value: []byte("foo"), - }) + + // Create the salt with a known key for predictable hmac values. + se := &logical.StorageEntry{Key: "salt", Value: []byte("foo")} + err := view.Put(context.Background(), se) if err != nil { return nil, err } - n := &NoopAudit{ - Config: &audit.BackendConfig{ - SaltView: view, - SaltConfig: &salt.Config{ - HMAC: sha256.New, - HMACType: "hmac-sha256", - }, - Config: config, + // Override the salt related config settings. + backendConfig := &audit.BackendConfig{ + SaltView: view, + SaltConfig: &salt.Config{ + HMAC: sha256.New, + HMACType: "hmac-sha256", }, + Config: config.Config, + MountPath: config.MountPath, } + n := &NoopAudit{Config: backendConfig} + cfg, err := audit.NewFormatterConfig() if err != nil { return nil, err } - f, err := audit.NewEntryFormatter(cfg, n) + f, err := audit.NewEntryFormatter(cfg, n, opts...) if err != nil { return nil, fmt.Errorf("error creating formatter: %w", err) } - fw, err := audit.NewEntryFormatterWriter(cfg, f, &audit.JSONWriter{}) - if err != nil { - return nil, fmt.Errorf("error creating formatter writer: %w", err) - } - - n.formatter = fw - n.nodeIDList = make([]eventlogger.NodeID, 2) n.nodeMap = make(map[eventlogger.NodeID]eventlogger.Node, 2) @@ -264,8 +269,11 @@ func NewNoopAudit(config map[string]string) (*NoopAudit, error) { return nil, fmt.Errorf("error generating random NodeID for formatter node: %w", err) } + // Wrap the formatting node, so we can get any bytes that were formatted etc. + wrappedFormatter := &noopWrapper{format: "json", node: f, backend: n} + n.nodeIDList[0] = formatterNodeID - n.nodeMap[formatterNodeID] = f + n.nodeMap[formatterNodeID] = wrappedFormatter sinkNode := event.NewNoopSink() sinkNodeID, err := event.GenerateNodeID() @@ -279,9 +287,12 @@ func NewNoopAudit(config map[string]string) (*NoopAudit, error) { return n, nil } +// NoopAuditFactory should be used when the test needs a way to access bytes that +// have been formatted by the pipeline during audit requests. +// The records parameter will be repointed to the one used within the pipeline. func NoopAuditFactory(records **[][]byte) audit.Factory { - return func(_ context.Context, config *audit.BackendConfig, _ bool, _ audit.HeaderFormatter) (audit.Backend, error) { - n, err := NewNoopAudit(config.Config) + return func(_ context.Context, config *audit.BackendConfig, _ bool, headerFormatter audit.HeaderFormatter) (audit.Backend, error) { + n, err := NewNoopAudit(config, audit.WithHeaderFormatter(headerFormatter)) if err != nil { return nil, err } @@ -293,8 +304,19 @@ func NoopAuditFactory(records **[][]byte) audit.Factory { } } +// noopWrapper is designed to wrap a formatter node in order to allow access to +// bytes formatted, headers formatted and parts of the logical.LogInput. +// Some older tests relied on being able to query this information so while those +// tests stick around we should look after them. +type noopWrapper struct { + format string + node eventlogger.Node + backend *NoopAudit +} + type NoopAudit struct { - Config *audit.BackendConfig + Config *audit.BackendConfig + ReqErr error ReqAuth []*logical.Auth Req []*logical.Request @@ -309,81 +331,164 @@ type NoopAudit struct { RespNonHMACKeys [][]string RespReqNonHMACKeys [][]string RespErrs []error - - formatter *audit.EntryFormatterWriter - records [][]byte - l sync.RWMutex - salt *salt.Salt - saltMutex sync.RWMutex + records [][]byte + l sync.RWMutex + salt *salt.Salt + saltMutex sync.RWMutex nodeIDList []eventlogger.NodeID nodeMap map[eventlogger.NodeID]eventlogger.Node } +// Process handles the contortions required by older test code to ensure behavior. +// It will attempt to do some pre/post processing of the logical.LogInput that should +// form part of the event's payload data, as well as capturing the resulting headers +// that were formatted and track the overall bytes that a formatted event uses when +// it's ready to head down the pipeline to the sink node (a noop for us). +func (n *noopWrapper) Process(ctx context.Context, e *eventlogger.Event) (*eventlogger.Event, error) { + n.backend.l.Lock() + defer n.backend.l.Unlock() + + var err error + + // We're expecting audit events since this is an audit device. + a, ok := e.Payload.(*audit.AuditEvent) + if !ok { + return nil, errors.New("cannot parse payload as an audit event") + } + + in := a.Data + + // Depending on the type of the audit event (request or response) we need to + // track different things. + switch a.Subtype { + case audit.RequestType: + n.backend.ReqAuth = append(n.backend.ReqAuth, in.Auth) + n.backend.Req = append(n.backend.Req, in.Request) + n.backend.ReqNonHMACKeys = in.NonHMACReqDataKeys + n.backend.ReqErrs = append(n.backend.ReqErrs, in.OuterErr) + + if n.backend.ReqErr != nil { + return nil, n.backend.ReqErr + } + case audit.ResponseType: + n.backend.RespAuth = append(n.backend.RespAuth, in.Auth) + n.backend.RespReq = append(n.backend.RespReq, in.Request) + n.backend.Resp = append(n.backend.Resp, in.Response) + n.backend.RespErrs = append(n.backend.RespErrs, in.OuterErr) + + if in.Response != nil { + n.backend.RespNonHMACKeys = append(n.backend.RespNonHMACKeys, in.NonHMACRespDataKeys) + n.backend.RespReqNonHMACKeys = append(n.backend.RespReqNonHMACKeys, in.NonHMACReqDataKeys) + } + + if n.backend.RespErr != nil { + return nil, n.backend.RespErr + } + default: + return nil, fmt.Errorf("unknown audit event type: %q", a.Subtype) + } + + // Once we've taken note of the relevant properties of the event, we get the + // underlying (wrapped) node to process it as normal. + e, err = n.node.Process(ctx, e) + if err != nil { + return nil, fmt.Errorf("error processing wrapped node: %w", err) + } + + // Once processing has been carried out, the underlying node (a formatter node) + // should contain the output ready for the sink node. We'll get that in order + // to track how many bytes we formatted. + b, ok := e.Format(n.format) + if ok { + n.backend.records = append(n.backend.records, b) + } + + // Finally, the last bit of post-processing is to make sure that we track the + // formatted headers that would have made it to the logs via the sink node. + // They only appear in requests. + if a.Subtype == audit.RequestType { + reqEntry := &audit.RequestEntry{} + err = json.Unmarshal(b, &reqEntry) + if err != nil { + return nil, fmt.Errorf("unable to parse formatted audit entry data: %w", err) + } + + n.backend.ReqHeaders = append(n.backend.ReqHeaders, reqEntry.Request.Headers) + } + + // Return the event and no error in order to let the pipeline continue on. + return e, nil +} + +func (n *noopWrapper) Reopen() error { + return n.node.Reopen() +} + +func (n *noopWrapper) Type() eventlogger.NodeType { + return n.node.Type() +} + +// Deprecated: use eventlogger. func (n *NoopAudit) LogRequest(ctx context.Context, in *logical.LogInput) error { - n.l.Lock() - defer n.l.Unlock() - - if n.formatter != nil { - var w bytes.Buffer - err := n.formatter.FormatAndWriteRequest(ctx, &w, in) - if err != nil { - return err - } - n.records = append(n.records, w.Bytes()) - } - - n.ReqAuth = append(n.ReqAuth, in.Auth) - n.Req = append(n.Req, in.Request) - n.ReqHeaders = append(n.ReqHeaders, in.Request.Headers) - n.ReqNonHMACKeys = in.NonHMACReqDataKeys - n.ReqErrs = append(n.ReqErrs, in.OuterErr) - - return n.ReqErr + return nil } +// Deprecated: use eventlogger. func (n *NoopAudit) LogResponse(ctx context.Context, in *logical.LogInput) error { - n.l.Lock() - defer n.l.Unlock() - - if n.formatter != nil { - var w bytes.Buffer - err := n.formatter.FormatAndWriteResponse(ctx, &w, in) - if err != nil { - return err - } - n.records = append(n.records, w.Bytes()) - } - - n.RespAuth = append(n.RespAuth, in.Auth) - n.RespReq = append(n.RespReq, in.Request) - n.Resp = append(n.Resp, in.Response) - n.RespErrs = append(n.RespErrs, in.OuterErr) - - if in.Response != nil { - n.RespNonHMACKeys = append(n.RespNonHMACKeys, in.NonHMACRespDataKeys) - n.RespReqNonHMACKeys = append(n.RespReqNonHMACKeys, in.NonHMACReqDataKeys) - } - - return n.RespErr + return nil } +// LogTestMessage will manually crank the handle on the nodes associated with this backend. func (n *NoopAudit) LogTestMessage(ctx context.Context, in *logical.LogInput, config map[string]string) error { n.l.Lock() defer n.l.Unlock() - var w bytes.Buffer - tempFormatter, err := audit.NewTemporaryFormatter(config["format"], config["prefix"]) - if err != nil { - return err + // Fake event for test purposes. + e := &eventlogger.Event{ + Type: eventlogger.EventType(event.AuditType.String()), + CreatedAt: time.Now(), + Formatted: make(map[string][]byte), + Payload: in, } - err = tempFormatter.FormatAndWriteResponse(ctx, &w, in) - if err != nil { - return err + // Try to get the required format from config and default to JSON. + format, ok := config["format"] + if !ok { + format = "json" } + cfg, err := audit.NewFormatterConfig(audit.WithFormat(format)) + if err != nil { + return fmt.Errorf("cannot create config for formatter node: %w", err) + } + // Create a temporary formatter node for reuse. + f, err := audit.NewEntryFormatter(cfg, n, audit.WithPrefix(config["prefix"])) - n.records = append(n.records, w.Bytes()) + // Go over each node in order from our list. + for _, id := range n.nodeIDList { + node, ok := n.nodeMap[id] + if !ok { + return fmt.Errorf("node not found: %v", id) + } + + switch node.Type() { + case eventlogger.NodeTypeFormatter: + // Use a temporary formatter node which doesn't persist its salt anywhere. + if formatNode, ok := node.(*audit.EntryFormatter); ok && formatNode != nil { + e, err = f.Process(ctx, e) + + // Housekeeping, we should update that we processed some bytes. + if e != nil { + b, ok := e.Format(format) + if ok { + n.records = append(n.records, b) + } + } + } + default: + e, err = node.Process(ctx, e) + } + } return nil } diff --git a/http/logical_test.go b/http/logical_test.go index e5b0caf222..01e6762ee0 100644 --- a/http/logical_test.go +++ b/http/logical_test.go @@ -569,10 +569,8 @@ func TestLogical_RespondWithStatusCode(t *testing.T) { } func TestLogical_Audit_invalidWrappingToken(t *testing.T) { - t.Setenv("VAULT_AUDIT_DISABLE_EVENTLOGGER", "true") - // Create a noop audit backend - noop := corehelpers.TestNoopAudit(t, nil) + noop := corehelpers.TestNoopAudit(t, "noop", nil) c, _, root := vault.TestCoreUnsealedWithConfig(t, &vault.CoreConfig{ AuditBackends: map[string]audit.Factory{ "noop": func(ctx context.Context, config *audit.BackendConfig, _ bool, _ audit.HeaderFormatter) (audit.Backend, error) { @@ -584,7 +582,6 @@ func TestLogical_Audit_invalidWrappingToken(t *testing.T) { defer ln.Close() // Enable the audit backend - resp := testHttpPost(t, root, addr+"/v1/sys/audit/noop", map[string]interface{}{ "type": "noop", }) diff --git a/http/sys_generate_root_test.go b/http/sys_generate_root_test.go index 04352859d4..8358d18a53 100644 --- a/http/sys_generate_root_test.go +++ b/http/sys_generate_root_test.go @@ -247,8 +247,6 @@ func testServerWithAudit(t *testing.T, records **[][]byte) (net.Listener, string } func TestSysGenerateRoot_badKey(t *testing.T) { - t.Setenv("VAULT_AUDIT_DISABLE_EVENTLOGGER", "true") - var records *[][]byte ln, addr, token, _ := testServerWithAudit(t, &records) defer ln.Close() diff --git a/vault/audit_test.go b/vault/audit_test.go index a7dd44539a..60f9f45e1c 100644 --- a/vault/audit_test.go +++ b/vault/audit_test.go @@ -12,6 +12,8 @@ import ( "testing" "time" + "github.com/stretchr/testify/require" + "github.com/hashicorp/vault/helper/testhelpers/corehelpers" "github.com/hashicorp/errwrap" @@ -340,14 +342,16 @@ func verifyDefaultAuditTable(t *testing.T, table *MountTable) { func TestAuditBroker_LogRequest(t *testing.T) { l := logging.NewVaultLogger(log.Trace) - b, err := NewAuditBroker(l, false) + b, err := NewAuditBroker(l, true) if err != nil { t.Fatal(err) } - a1 := corehelpers.TestNoopAudit(t, nil) - a2 := corehelpers.TestNoopAudit(t, nil) - b.Register("foo", a1, false) - b.Register("bar", a2, false) + a1 := corehelpers.TestNoopAudit(t, "foo", nil) + a2 := corehelpers.TestNoopAudit(t, "bar", nil) + err = b.Register("foo", a1, false) + require.NoError(t, err) + err = b.Register("bar", a2, false) + require.NoError(t, err) auth := &logical.Auth{ ClientToken: "foo", @@ -423,21 +427,23 @@ func TestAuditBroker_LogRequest(t *testing.T) { // Should FAIL work with both failing backends a2.ReqErr = fmt.Errorf("failed") - if err := b.LogRequest(ctx, logInput, headersConf); !errwrap.Contains(err, "no audit backend succeeded in logging the request") { + if err := b.LogRequest(ctx, logInput, headersConf); !errwrap.Contains(err, "event not processed by enough 'sink' nodes") { t.Fatalf("err: %v", err) } } func TestAuditBroker_LogResponse(t *testing.T) { l := logging.NewVaultLogger(log.Trace) - b, err := NewAuditBroker(l, false) + b, err := NewAuditBroker(l, true) if err != nil { t.Fatal(err) } - a1 := corehelpers.TestNoopAudit(t, nil) - a2 := corehelpers.TestNoopAudit(t, nil) - b.Register("foo", a1, false) - b.Register("bar", a2, false) + a1 := corehelpers.TestNoopAudit(t, "foo", nil) + a2 := corehelpers.TestNoopAudit(t, "bar", nil) + err = b.Register("foo", a1, false) + require.NoError(t, err) + err = b.Register("bar", a2, false) + require.NoError(t, err) auth := &logical.Auth{ NumUses: 10, @@ -531,23 +537,36 @@ func TestAuditBroker_LogResponse(t *testing.T) { // Should FAIL work with both failing backends a2.RespErr = fmt.Errorf("failed") err = b.LogResponse(ctx, logInput, headersConf) - if !strings.Contains(err.Error(), "no audit backend succeeded in logging the response") { + if !strings.Contains(err.Error(), "event not processed by enough 'sink' nodes") { t.Fatalf("err: %v", err) } } func TestAuditBroker_AuditHeaders(t *testing.T) { logger := logging.NewVaultLogger(log.Trace) - b, err := NewAuditBroker(logger, false) + + b, err := NewAuditBroker(logger, true) if err != nil { t.Fatal(err) } _, barrier, _ := mockBarrier(t) view := NewBarrierView(barrier, "headers/") - a1 := corehelpers.TestNoopAudit(t, nil) - a2 := corehelpers.TestNoopAudit(t, nil) - b.Register("foo", a1, false) - b.Register("bar", a2, false) + + headersConf := &AuditedHeadersConfig{ + view: view, + } + err = headersConf.add(context.Background(), "X-Test-Header", false) + require.NoError(t, err) + err = headersConf.add(context.Background(), "X-Vault-Header", false) + require.NoError(t, err) + + a1 := corehelpers.TestNoopAudit(t, "foo", nil, audit.WithHeaderFormatter(headersConf)) + a2 := corehelpers.TestNoopAudit(t, "bar", nil, audit.WithHeaderFormatter(headersConf)) + + err = b.Register("foo", a1, false) + require.NoError(t, err) + err = b.Register("bar", a2, false) + require.NoError(t, err) auth := &logical.Auth{ ClientToken: "foo", @@ -575,19 +594,13 @@ func TestAuditBroker_AuditHeaders(t *testing.T) { } reqCopy := reqCopyRaw.(*logical.Request) - headersConf := &AuditedHeadersConfig{ - view: view, - } - headersConf.add(context.Background(), "X-Test-Header", false) - headersConf.add(context.Background(), "X-Vault-Header", false) - logInput := &logical.LogInput{ Auth: auth, Request: reqCopy, OuterErr: respErr, } ctx := namespace.RootContext(context.Background()) - err = b.LogRequest(ctx, logInput, headersConf) + err = b.LogRequest(ctx, logInput, nil) if err != nil { t.Fatalf("err: %v", err) } @@ -599,7 +612,7 @@ func TestAuditBroker_AuditHeaders(t *testing.T) { for _, a := range []*corehelpers.NoopAudit{a1, a2} { if !reflect.DeepEqual(a.ReqHeaders[0], expected) { - t.Fatalf("Bad audited headers: %#v", a.Req[0].Headers) + t.Fatalf("Bad audited headers: %#v", a.ReqHeaders[0]) } } @@ -618,7 +631,7 @@ func TestAuditBroker_AuditHeaders(t *testing.T) { // Should FAIL work with both failing backends a2.ReqErr = fmt.Errorf("failed") err = b.LogRequest(ctx, logInput, headersConf) - if !errwrap.Contains(err, "no audit backend succeeded in logging the request") { + if !errwrap.Contains(err, "event not processed by enough 'sink' nodes") { t.Fatalf("err: %v", err) } } diff --git a/vault/core_test.go b/vault/core_test.go index 097ea51b4a..3f16e825ac 100644 --- a/vault/core_test.go +++ b/vault/core_test.go @@ -1464,16 +1464,13 @@ func TestCore_HandleLogin_Token(t *testing.T) { } func TestCore_HandleRequest_AuditTrail(t *testing.T) { - t.Setenv("VAULT_AUDIT_DISABLE_EVENTLOGGER", "true") - // Create a noop audit backend - noop := &corehelpers.NoopAudit{} + var noop *corehelpers.NoopAudit c, _, root := TestCoreUnsealed(t) - c.auditBackends["noop"] = func(ctx context.Context, config *audit.BackendConfig, _ bool, _ audit.HeaderFormatter) (audit.Backend, error) { - noop = &corehelpers.NoopAudit{ - Config: config, - } - return noop, nil + c.auditBackends["noop"] = func(ctx context.Context, config *audit.BackendConfig, _ bool, headerFormatter audit.HeaderFormatter) (audit.Backend, error) { + var err error + noop, err = corehelpers.NewNoopAudit(config, audit.WithHeaderFormatter(headerFormatter)) + return noop, err } // Enable the audit backend @@ -1530,16 +1527,13 @@ func TestCore_HandleRequest_AuditTrail(t *testing.T) { } func TestCore_HandleRequest_AuditTrail_noHMACKeys(t *testing.T) { - t.Setenv("VAULT_AUDIT_DISABLE_EVENTLOGGER", "true") - // Create a noop audit backend var noop *corehelpers.NoopAudit c, _, root := TestCoreUnsealed(t) - c.auditBackends["noop"] = func(ctx context.Context, config *audit.BackendConfig, _ bool, _ audit.HeaderFormatter) (audit.Backend, error) { - noop = &corehelpers.NoopAudit{ - Config: config, - } - return noop, nil + c.auditBackends["noop"] = func(ctx context.Context, config *audit.BackendConfig, _ bool, headerFormatter audit.HeaderFormatter) (audit.Backend, error) { + var err error + noop, err = corehelpers.NewNoopAudit(config, audit.WithHeaderFormatter(headerFormatter)) + return noop, err } // Specify some keys to not HMAC @@ -1636,10 +1630,8 @@ func TestCore_HandleRequest_AuditTrail_noHMACKeys(t *testing.T) { } func TestCore_HandleLogin_AuditTrail(t *testing.T) { - t.Setenv("VAULT_AUDIT_DISABLE_EVENTLOGGER", "true") - // Create a badass credential backend that always logs in as armon - noop := &corehelpers.NoopAudit{} + var noop *corehelpers.NoopAudit noopBack := &NoopBackend{ Login: []string{"login"}, Response: &logical.Response{ @@ -1659,11 +1651,10 @@ func TestCore_HandleLogin_AuditTrail(t *testing.T) { c.credentialBackends["noop"] = func(context.Context, *logical.BackendConfig) (logical.Backend, error) { return noopBack, nil } - c.auditBackends["noop"] = func(ctx context.Context, config *audit.BackendConfig, _ bool, _ audit.HeaderFormatter) (audit.Backend, error) { - noop = &corehelpers.NoopAudit{ - Config: config, - } - return noop, nil + c.auditBackends["noop"] = func(ctx context.Context, config *audit.BackendConfig, _ bool, headerFormatter audit.HeaderFormatter) (audit.Backend, error) { + var err error + noop, err = corehelpers.NewNoopAudit(config, audit.WithHeaderFormatter(headerFormatter)) + return noop, err } // Enable the credential backend diff --git a/vault/external_tests/identity/login_mfa_totp_test.go b/vault/external_tests/identity/login_mfa_totp_test.go index 7951adc3a5..d975f13f51 100644 --- a/vault/external_tests/identity/login_mfa_totp_test.go +++ b/vault/external_tests/identity/login_mfa_totp_test.go @@ -51,9 +51,7 @@ func doTwoPhaseLogin(t *testing.T, client *api.Client, totpCodePath, methodID, u } func TestLoginMfaGenerateTOTPTestAuditIncluded(t *testing.T) { - t.Setenv("VAULT_AUDIT_DISABLE_EVENTLOGGER", "true") - - noop := corehelpers.TestNoopAudit(t, nil) + noop := corehelpers.TestNoopAudit(t, "noop", nil) cluster := vault.NewTestCluster(t, &vault.CoreConfig{ CredentialBackends: map[string]logical.Factory{