Files
vault/audit/nodes.go
Kuba Wieczorek 17ffe62d0d [VAULT-22481] Add audit filtering feature (#24558)
* VAULT-22481: Audit filter node (#24465)

* Initial commit on adding filter nodes for audit

* tests for audit filter

* test: longer filter - more conditions

* copywrite headers

* Check interface for the right type

* Add audit filtering feature (#24554)

* Support filter nodes in backend factories and add some tests

* More tests and cleanup

* Attempt to move control of registration for nodes and pipelines to the audit broker (#24505)

* invert control of the pipelines/nodes to the audit broker vs. within each backend

* update noop audit test code to implement the pipeliner interface

* noop mount path has trailing slash

* attempting to make NoopAudit more friendly

* NoopAudit uses known salt

* Refactor audit.ProcessManual to support filter nodes

* HasFiltering

* rename the pipeliner

* use exported AuditEvent in Filter

* Add tests for registering and deregistering backends on the audit broker

* Add missing licence header to one file, fix a typo in two tests

---------

Co-authored-by: Peter Wilson <peter.wilson@hashicorp.com>

* Add changelog file

* update bexpr datum to use a strong type

* go docs updates

* test path

* PR review comments

* handle scenarios/outcomes from broker.send

* don't need to re-check the complete sinks

* add extra check to deregister to ensure that re-registering non-filtered device sets sink threshold

* Ensure that the multierror is appended before attempting to return it

---------

Co-authored-by: Peter Wilson <peter.wilson@hashicorp.com>
2023-12-18 18:01:49 +00:00

97 lines
2.6 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package audit
import (
"context"
"errors"
"fmt"
"time"
"github.com/hashicorp/eventlogger"
"github.com/hashicorp/vault/internal/observability/event"
"github.com/hashicorp/vault/sdk/logical"
)
// ProcessManual will attempt to create an (audit) event with the specified data
// and manually iterate over the supplied nodes calling Process on each until the
// event is nil (which indicates the pipeline has completed).
// Order of IDs in the NodeID slice determines the order they are processed.
// (Audit) Event will be of RequestType (as opposed to ResponseType).
// The last node must be a filter node (eventlogger.NodeTypeFilter) or
// sink node (eventlogger.NodeTypeSink).
func ProcessManual(ctx context.Context, data *logical.LogInput, ids []eventlogger.NodeID, nodes map[eventlogger.NodeID]eventlogger.Node) error {
switch {
case data == nil:
return errors.New("data cannot be nil")
case len(ids) < 2:
return errors.New("minimum of 2 ids are required")
case nodes == nil:
return errors.New("nodes cannot be nil")
case len(nodes) == 0:
return errors.New("nodes are required")
}
// Create an audit event.
a, err := NewEvent(RequestType)
if err != nil {
return err
}
// Insert the data into the audit event.
a.Data = data
// Create an eventlogger event with the audit event as the payload.
e := &eventlogger.Event{
Type: eventlogger.EventType(event.AuditType.String()),
CreatedAt: time.Now(),
Formatted: make(map[string][]byte),
Payload: a,
}
var lastSeen eventlogger.NodeType
// Process nodes in order, updating the event with the result.
// This means we *should* do:
// 1. filter (optional if configured)
// 2. formatter (temporary)
// 3. sink
for _, id := range ids {
// If the event is nil, we've completed processing the pipeline (hopefully
// by either a filter node or a sink node).
if e == nil {
break
}
node, ok := nodes[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.(*EntryFormatter); ok && formatNode != nil {
e, err = newTemporaryEntryFormatter(formatNode).Process(ctx, e)
}
default:
e, err = node.Process(ctx, e)
}
if err != nil {
return err
}
// Track the last node we have processed, as we should end with a filter or sink.
lastSeen = node.Type()
}
switch lastSeen {
case eventlogger.NodeTypeSink, eventlogger.NodeTypeFilter:
default:
return errors.New("last node must be a filter or sink")
}
return nil
}