mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-10-31 18:48:08 +00:00
VAULT-17078: Implement Register and Deregister Audit Devices for EventLogger Framework (#21898)
* begin refactoring of event package into audit package * audit options additions * rename option structs * Trying to remove 'audit' from the start of names. * typo * typo * typo * newEvent required params * typo * comments on noop sink * more refactoring - merge json/jsonx formatters * fix file backend and tests * Moved unexported funcs to formatter, fixed file tests * typos, comments, moved func * fix corehelpers * fix backends (syslog, socket) * Moved some sinks back to generic event package. * return of the file sink * remove unneeded sink params/return vars * Implement Register and Deregister Audit Devices for EventLogger Framework (#21940) * add function to create StdoutSinkNode * add boolean argument to audit Factory function * create eventlogger nodes in backend factory functions * simplify NewNoopSink function and remove DiscardSinkNode * make the sanity test in the file backend mutually exclusive based on useEventLogger value * remove test cases that no longer made sense and were failing * NewFileSink attempts to open file for sanity check * fix FileSink tests and update FileSink to remove discard, stdout but add /dev/null * Moved WithPrefix from FileSink to EventFormatter * move prefix in backend * NewFormatterConfig and Options (tests fixed) * Little tidy up * add test where audit file is created with useEventLogger set to true * only create eventlogger.Node instances when useEventLogger is true fix failing test due to invalid string conversion of FileMode value * moved variable definition to more appropriate scope --------- Co-authored-by: Marc Boudreau <marc.boudreau@hashicorp.com>
This commit is contained in:
@@ -8,6 +8,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/hashicorp/eventlogger"
|
||||||
"github.com/hashicorp/vault/sdk/helper/salt"
|
"github.com/hashicorp/vault/sdk/helper/salt"
|
||||||
|
|
||||||
"github.com/hashicorp/vault/sdk/logical"
|
"github.com/hashicorp/vault/sdk/logical"
|
||||||
@@ -282,6 +283,12 @@ type Backend interface {
|
|||||||
|
|
||||||
// Invalidate is called for path invalidation
|
// Invalidate is called for path invalidation
|
||||||
Invalidate(context.Context)
|
Invalidate(context.Context)
|
||||||
|
|
||||||
|
// RegisterNodesAndPipeline provides an eventlogger.Broker pointer so that
|
||||||
|
// the Backend can call its RegisterNode and RegisterPipeline methods with
|
||||||
|
// the nodes and the pipeline that were created in the corresponding
|
||||||
|
// Factory function.
|
||||||
|
RegisterNodesAndPipeline(*eventlogger.Broker, string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// BackendConfig contains configuration parameters used in the factory func to
|
// BackendConfig contains configuration parameters used in the factory func to
|
||||||
|
|||||||
@@ -15,7 +15,9 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
|
"github.com/hashicorp/eventlogger"
|
||||||
"github.com/hashicorp/vault/audit"
|
"github.com/hashicorp/vault/audit"
|
||||||
|
"github.com/hashicorp/vault/internal/observability/event"
|
||||||
"github.com/hashicorp/vault/sdk/helper/salt"
|
"github.com/hashicorp/vault/sdk/helper/salt"
|
||||||
"github.com/hashicorp/vault/sdk/logical"
|
"github.com/hashicorp/vault/sdk/logical"
|
||||||
)
|
)
|
||||||
@@ -46,10 +48,10 @@ func Factory(ctx context.Context, conf *audit.BackendConfig, useEventLogger bool
|
|||||||
|
|
||||||
format, ok := conf.Config["format"]
|
format, ok := conf.Config["format"]
|
||||||
if !ok {
|
if !ok {
|
||||||
format = "json"
|
format = audit.JSONFormat.String()
|
||||||
}
|
}
|
||||||
switch format {
|
switch format {
|
||||||
case "json", "jsonx":
|
case audit.JSONFormat.String(), audit.JSONxFormat.String():
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unknown format type %q", format)
|
return nil, fmt.Errorf("unknown format type %q", format)
|
||||||
}
|
}
|
||||||
@@ -102,9 +104,7 @@ func Factory(ctx context.Context, conf *audit.BackendConfig, useEventLogger bool
|
|||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
mode = os.FileMode(m)
|
mode = os.FileMode(m)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg, err := audit.NewFormatterConfig(
|
cfg, err := audit.NewFormatterConfig(
|
||||||
@@ -149,15 +149,54 @@ func Factory(ctx context.Context, conf *audit.BackendConfig, useEventLogger bool
|
|||||||
}
|
}
|
||||||
b.formatter = fw
|
b.formatter = fw
|
||||||
|
|
||||||
switch path {
|
if useEventLogger {
|
||||||
case "stdout", "discard":
|
b.nodeIDList = make([]eventlogger.NodeID, 2)
|
||||||
// no need to test opening file if outputting to stdout or discarding
|
b.nodeMap = make(map[eventlogger.NodeID]eventlogger.Node)
|
||||||
default:
|
|
||||||
// Ensure that the file can be successfully opened for writing;
|
formatterNodeID, err := event.GenerateNodeID()
|
||||||
// otherwise it will be too late to catch later without problems
|
if err != nil {
|
||||||
// (ref: https://github.com/hashicorp/vault/issues/550)
|
return nil, fmt.Errorf("error generating random NodeID for formatter node: %w", err)
|
||||||
if err := b.open(); err != nil {
|
}
|
||||||
return nil, fmt.Errorf("sanity check failed; unable to open %q for writing: %w", path, err)
|
|
||||||
|
b.nodeIDList[0] = formatterNodeID
|
||||||
|
b.nodeMap[formatterNodeID] = f
|
||||||
|
|
||||||
|
var sinkNode eventlogger.Node
|
||||||
|
|
||||||
|
switch path {
|
||||||
|
case "stdout":
|
||||||
|
sinkNode = event.NewStdoutSinkNode(format)
|
||||||
|
case "discard":
|
||||||
|
sinkNode = event.NewNoopSink()
|
||||||
|
default:
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// The NewFileSink function attempts to open the file and will
|
||||||
|
// return an error if it can't.
|
||||||
|
sinkNode, err = event.NewFileSink(b.path, format, event.WithFileMode(strconv.FormatUint(uint64(mode), 8)))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("file sink creation failed for path %q: %w", path, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sinkNodeID, err := event.GenerateNodeID()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error generating random NodeID for sink node: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
b.nodeIDList[1] = sinkNodeID
|
||||||
|
b.nodeMap[sinkNodeID] = sinkNode
|
||||||
|
} else {
|
||||||
|
switch path {
|
||||||
|
case "stdout":
|
||||||
|
case "discard":
|
||||||
|
default:
|
||||||
|
// Ensure that the file can be successfully opened for writing;
|
||||||
|
// otherwise it will be too late to catch later without problems
|
||||||
|
// (ref: https://github.com/hashicorp/vault/issues/550)
|
||||||
|
if err := b.open(); err != nil {
|
||||||
|
return nil, fmt.Errorf("sanity check failed; unable to open %q for writing: %w", path, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,6 +222,9 @@ type Backend struct {
|
|||||||
salt *atomic.Value
|
salt *atomic.Value
|
||||||
saltConfig *salt.Config
|
saltConfig *salt.Config
|
||||||
saltView logical.Storage
|
saltView logical.Storage
|
||||||
|
|
||||||
|
nodeIDList []eventlogger.NodeID
|
||||||
|
nodeMap map[eventlogger.NodeID]eventlogger.Node
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ audit.Backend = (*Backend)(nil)
|
var _ audit.Backend = (*Backend)(nil)
|
||||||
@@ -238,7 +280,7 @@ func (b *Backend) LogRequest(ctx context.Context, in *logical.LogInput) error {
|
|||||||
return b.log(ctx, buf, writer)
|
return b.log(ctx, buf, writer)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Backend) log(ctx context.Context, buf *bytes.Buffer, writer io.Writer) error {
|
func (b *Backend) log(_ context.Context, buf *bytes.Buffer, writer io.Writer) error {
|
||||||
reader := bytes.NewReader(buf.Bytes())
|
reader := bytes.NewReader(buf.Bytes())
|
||||||
|
|
||||||
b.fileLock.Lock()
|
b.fileLock.Lock()
|
||||||
@@ -304,6 +346,7 @@ func (b *Backend) LogTestMessage(ctx context.Context, in *logical.LogInput, conf
|
|||||||
}
|
}
|
||||||
|
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
|
|
||||||
temporaryFormatter, err := audit.NewTemporaryFormatter(config["format"], config["prefix"])
|
temporaryFormatter, err := audit.NewTemporaryFormatter(config["format"], config["prefix"])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -376,3 +419,21 @@ func (b *Backend) Invalidate(_ context.Context) {
|
|||||||
defer b.saltMutex.Unlock()
|
defer b.saltMutex.Unlock()
|
||||||
b.salt.Store((*salt.Salt)(nil))
|
b.salt.Store((*salt.Salt)(nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RegisterNodesAndPipeline registers the nodes and a pipeline as required by
|
||||||
|
// the audit.Backend interface.
|
||||||
|
func (b *Backend) RegisterNodesAndPipeline(broker *eventlogger.Broker, name string) error {
|
||||||
|
for id, node := range b.nodeMap {
|
||||||
|
if err := broker.RegisterNode(id, node); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pipeline := eventlogger.Pipeline{
|
||||||
|
PipelineID: eventlogger.PipelineID(name),
|
||||||
|
EventType: eventlogger.EventType("audit"),
|
||||||
|
NodeIDs: b.nodeIDList,
|
||||||
|
}
|
||||||
|
|
||||||
|
return broker.RegisterPipeline(pipeline)
|
||||||
|
}
|
||||||
|
|||||||
@@ -25,15 +25,7 @@ func TestAuditFile_fileModeNew(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
path, err := ioutil.TempDir("", "vault-test_audit_file-file_mode_new")
|
file := filepath.Join(t.TempDir(), "auditTest.txt")
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
defer os.RemoveAll(path)
|
|
||||||
|
|
||||||
file := filepath.Join(path, "auditTest.txt")
|
|
||||||
|
|
||||||
config := map[string]string{
|
config := map[string]string{
|
||||||
"path": file,
|
"path": file,
|
||||||
"mode": modeStr,
|
"mode": modeStr,
|
||||||
@@ -136,6 +128,40 @@ func TestAuditFile_fileMode0000(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestAuditFile_EventLogger_fileModeNew verifies that the Factory function
|
||||||
|
// correctly sets the file mode when the useEventLogger argument is set to
|
||||||
|
// true.
|
||||||
|
func TestAuditFile_EventLogger_fileModeNew(t *testing.T) {
|
||||||
|
modeStr := "0777"
|
||||||
|
mode, err := strconv.ParseUint(modeStr, 8, 32)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
file := filepath.Join(t.TempDir(), "auditTest.txt")
|
||||||
|
config := map[string]string{
|
||||||
|
"path": file,
|
||||||
|
"mode": modeStr,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = Factory(context.Background(), &audit.BackendConfig{
|
||||||
|
SaltConfig: &salt.Config{},
|
||||||
|
SaltView: &logical.InmemStorage{},
|
||||||
|
Config: config,
|
||||||
|
}, true)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
info, err := os.Stat(file)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot retrieve file mode from `Stat`")
|
||||||
|
}
|
||||||
|
if info.Mode() != os.FileMode(mode) {
|
||||||
|
t.Fatalf("File mode does not match.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func BenchmarkAuditFile_request(b *testing.B) {
|
func BenchmarkAuditFile_request(b *testing.B) {
|
||||||
config := map[string]string{
|
config := map[string]string{
|
||||||
"path": "/dev/null",
|
"path": "/dev/null",
|
||||||
@@ -174,7 +200,7 @@ func BenchmarkAuditFile_request(b *testing.B) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := namespace.RootContext(nil)
|
ctx := namespace.RootContext(context.Background())
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.RunParallel(func(pb *testing.PB) {
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
for pb.Next() {
|
for pb.Next() {
|
||||||
|
|||||||
@@ -12,9 +12,11 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/hashicorp/eventlogger"
|
||||||
"github.com/hashicorp/go-multierror"
|
"github.com/hashicorp/go-multierror"
|
||||||
"github.com/hashicorp/go-secure-stdlib/parseutil"
|
"github.com/hashicorp/go-secure-stdlib/parseutil"
|
||||||
"github.com/hashicorp/vault/audit"
|
"github.com/hashicorp/vault/audit"
|
||||||
|
"github.com/hashicorp/vault/internal/observability/event"
|
||||||
"github.com/hashicorp/vault/sdk/helper/salt"
|
"github.com/hashicorp/vault/sdk/helper/salt"
|
||||||
"github.com/hashicorp/vault/sdk/logical"
|
"github.com/hashicorp/vault/sdk/logical"
|
||||||
)
|
)
|
||||||
@@ -48,10 +50,10 @@ func Factory(ctx context.Context, conf *audit.BackendConfig, useEventLogger bool
|
|||||||
|
|
||||||
format, ok := conf.Config["format"]
|
format, ok := conf.Config["format"]
|
||||||
if !ok {
|
if !ok {
|
||||||
format = "json"
|
format = audit.JSONFormat.String()
|
||||||
}
|
}
|
||||||
switch format {
|
switch format {
|
||||||
case "json", "jsonx":
|
case audit.JSONFormat.String(), audit.JSONxFormat.String():
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unknown format type %q", format)
|
return nil, fmt.Errorf("unknown format type %q", format)
|
||||||
}
|
}
|
||||||
@@ -112,9 +114,9 @@ func Factory(ctx context.Context, conf *audit.BackendConfig, useEventLogger bool
|
|||||||
}
|
}
|
||||||
var w audit.Writer
|
var w audit.Writer
|
||||||
switch format {
|
switch format {
|
||||||
case "json":
|
case audit.JSONFormat.String():
|
||||||
w = &audit.JSONWriter{Prefix: conf.Config["prefix"]}
|
w = &audit.JSONWriter{Prefix: conf.Config["prefix"]}
|
||||||
case "jsonx":
|
case audit.JSONxFormat.String():
|
||||||
w = &audit.JSONxWriter{Prefix: conf.Config["prefix"]}
|
w = &audit.JSONxWriter{Prefix: conf.Config["prefix"]}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,6 +127,29 @@ func Factory(ctx context.Context, conf *audit.BackendConfig, useEventLogger bool
|
|||||||
|
|
||||||
b.formatter = fw
|
b.formatter = fw
|
||||||
|
|
||||||
|
if useEventLogger {
|
||||||
|
b.nodeIDList = make([]eventlogger.NodeID, 2)
|
||||||
|
b.nodeMap = make(map[eventlogger.NodeID]eventlogger.Node)
|
||||||
|
|
||||||
|
formatterNodeID, err := event.GenerateNodeID()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error generating random NodeID for formatter node: %w", err)
|
||||||
|
}
|
||||||
|
b.nodeIDList[0] = formatterNodeID
|
||||||
|
b.nodeMap[formatterNodeID] = f
|
||||||
|
|
||||||
|
sinkNode, err := event.NewSocketSink(format, address, event.WithSocketType(socketType), event.WithMaxDuration(writeDuration.String()))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error creating socket sink node: %w", err)
|
||||||
|
}
|
||||||
|
sinkNodeID, err := event.GenerateNodeID()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error generating random NodeID for sink node: %w", err)
|
||||||
|
}
|
||||||
|
b.nodeIDList[1] = sinkNodeID
|
||||||
|
b.nodeMap[sinkNodeID] = sinkNode
|
||||||
|
}
|
||||||
|
|
||||||
return b, nil
|
return b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,6 +170,9 @@ type Backend struct {
|
|||||||
salt *salt.Salt
|
salt *salt.Salt
|
||||||
saltConfig *salt.Config
|
saltConfig *salt.Config
|
||||||
saltView logical.Storage
|
saltView logical.Storage
|
||||||
|
|
||||||
|
nodeIDList []eventlogger.NodeID
|
||||||
|
nodeMap map[eventlogger.NodeID]eventlogger.Node
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ audit.Backend = (*Backend)(nil)
|
var _ audit.Backend = (*Backend)(nil)
|
||||||
@@ -306,3 +334,21 @@ func (b *Backend) Invalidate(_ context.Context) {
|
|||||||
defer b.saltMutex.Unlock()
|
defer b.saltMutex.Unlock()
|
||||||
b.salt = nil
|
b.salt = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RegisterNodesAndPipeline registers the nodes and a pipeline as required by
|
||||||
|
// the audit.Backend interface.
|
||||||
|
func (b *Backend) RegisterNodesAndPipeline(broker *eventlogger.Broker, name string) error {
|
||||||
|
for id, node := range b.nodeMap {
|
||||||
|
if err := broker.RegisterNode(id, node); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pipeline := eventlogger.Pipeline{
|
||||||
|
PipelineID: eventlogger.PipelineID(name),
|
||||||
|
EventType: eventlogger.EventType("audit"),
|
||||||
|
NodeIDs: b.nodeIDList,
|
||||||
|
}
|
||||||
|
|
||||||
|
return broker.RegisterPipeline(pipeline)
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,8 +10,10 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/hashicorp/eventlogger"
|
||||||
gsyslog "github.com/hashicorp/go-syslog"
|
gsyslog "github.com/hashicorp/go-syslog"
|
||||||
"github.com/hashicorp/vault/audit"
|
"github.com/hashicorp/vault/audit"
|
||||||
|
"github.com/hashicorp/vault/internal/observability/event"
|
||||||
"github.com/hashicorp/vault/sdk/helper/salt"
|
"github.com/hashicorp/vault/sdk/helper/salt"
|
||||||
"github.com/hashicorp/vault/sdk/logical"
|
"github.com/hashicorp/vault/sdk/logical"
|
||||||
)
|
)
|
||||||
@@ -20,6 +22,7 @@ func Factory(ctx context.Context, conf *audit.BackendConfig, useEventLogger bool
|
|||||||
if conf.SaltConfig == nil {
|
if conf.SaltConfig == nil {
|
||||||
return nil, fmt.Errorf("nil salt config")
|
return nil, fmt.Errorf("nil salt config")
|
||||||
}
|
}
|
||||||
|
|
||||||
if conf.SaltView == nil {
|
if conf.SaltView == nil {
|
||||||
return nil, fmt.Errorf("nil salt view")
|
return nil, fmt.Errorf("nil salt view")
|
||||||
}
|
}
|
||||||
@@ -38,10 +41,10 @@ func Factory(ctx context.Context, conf *audit.BackendConfig, useEventLogger bool
|
|||||||
|
|
||||||
format, ok := conf.Config["format"]
|
format, ok := conf.Config["format"]
|
||||||
if !ok {
|
if !ok {
|
||||||
format = "json"
|
format = audit.JSONFormat.String()
|
||||||
}
|
}
|
||||||
switch format {
|
switch format {
|
||||||
case "json", "jsonx":
|
case audit.JSONFormat.String(), audit.JSONxFormat.String():
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unknown format type %q", format)
|
return nil, fmt.Errorf("unknown format type %q", format)
|
||||||
}
|
}
|
||||||
@@ -106,9 +109,9 @@ func Factory(ctx context.Context, conf *audit.BackendConfig, useEventLogger bool
|
|||||||
|
|
||||||
var w audit.Writer
|
var w audit.Writer
|
||||||
switch format {
|
switch format {
|
||||||
case "json":
|
case audit.JSONFormat.String():
|
||||||
w = &audit.JSONWriter{Prefix: conf.Config["prefix"]}
|
w = &audit.JSONWriter{Prefix: conf.Config["prefix"]}
|
||||||
case "jsonx":
|
case audit.JSONxFormat.String():
|
||||||
w = &audit.JSONxWriter{Prefix: conf.Config["prefix"]}
|
w = &audit.JSONxWriter{Prefix: conf.Config["prefix"]}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,6 +122,29 @@ func Factory(ctx context.Context, conf *audit.BackendConfig, useEventLogger bool
|
|||||||
|
|
||||||
b.formatter = fw
|
b.formatter = fw
|
||||||
|
|
||||||
|
if useEventLogger {
|
||||||
|
b.nodeIDList = make([]eventlogger.NodeID, 2)
|
||||||
|
b.nodeMap = make(map[eventlogger.NodeID]eventlogger.Node)
|
||||||
|
|
||||||
|
formatterNodeID, err := event.GenerateNodeID()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error generating random NodeID for formatter node: %w", err)
|
||||||
|
}
|
||||||
|
b.nodeIDList[0] = formatterNodeID
|
||||||
|
b.nodeMap[formatterNodeID] = f
|
||||||
|
|
||||||
|
sinkNode, err := event.NewSyslogSink(format, event.WithFacility(facility), event.WithTag(tag))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error creating syslog sink node: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sinkNodeID, err := event.GenerateNodeID()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error generating random NodeID for sink node: %w", err)
|
||||||
|
}
|
||||||
|
b.nodeIDList[1] = sinkNodeID
|
||||||
|
b.nodeMap[sinkNodeID] = sinkNode
|
||||||
|
}
|
||||||
return b, nil
|
return b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,6 +159,9 @@ type Backend struct {
|
|||||||
salt *salt.Salt
|
salt *salt.Salt
|
||||||
saltConfig *salt.Config
|
saltConfig *salt.Config
|
||||||
saltView logical.Storage
|
saltView logical.Storage
|
||||||
|
|
||||||
|
nodeIDList []eventlogger.NodeID
|
||||||
|
nodeMap map[eventlogger.NodeID]eventlogger.Node
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ audit.Backend = (*Backend)(nil)
|
var _ audit.Backend = (*Backend)(nil)
|
||||||
@@ -213,3 +242,21 @@ func (b *Backend) Invalidate(_ context.Context) {
|
|||||||
defer b.saltMutex.Unlock()
|
defer b.saltMutex.Unlock()
|
||||||
b.salt = nil
|
b.salt = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RegisterNodesAndPipeline registers the nodes and a pipeline as required by
|
||||||
|
// the audit.Backend interface.
|
||||||
|
func (b *Backend) RegisterNodesAndPipeline(broker *eventlogger.Broker, name string) error {
|
||||||
|
for id, node := range b.nodeMap {
|
||||||
|
if err := broker.RegisterNode(id, node); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pipeline := eventlogger.Pipeline{
|
||||||
|
PipelineID: eventlogger.PipelineID(name),
|
||||||
|
EventType: eventlogger.EventType("audit"),
|
||||||
|
NodeIDs: b.nodeIDList,
|
||||||
|
}
|
||||||
|
|
||||||
|
return broker.RegisterPipeline(pipeline)
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ package event
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/hashicorp/eventlogger"
|
||||||
|
"github.com/hashicorp/go-uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
// EventType represents the event's type
|
// EventType represents the event's type
|
||||||
@@ -24,3 +27,11 @@ func (et EventType) Validate() error {
|
|||||||
return fmt.Errorf("%s: '%s' is not a valid event type: %w", op, et, ErrInvalidParameter)
|
return fmt.Errorf("%s: '%s' is not a valid event type: %w", op, et, ErrInvalidParameter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GenerateNodeID generates a new UUID that it casts to the eventlogger.NodeID
|
||||||
|
// type.
|
||||||
|
func GenerateNodeID() (eventlogger.NodeID, error) {
|
||||||
|
id, err := uuid.GenerateUUID()
|
||||||
|
|
||||||
|
return eventlogger.NodeID(id), err
|
||||||
|
}
|
||||||
|
|||||||
@@ -156,7 +156,7 @@ func (c *Core) enableAudit(ctx context.Context, entry *MountEntry, updateStorage
|
|||||||
c.audit = newTable
|
c.audit = newTable
|
||||||
|
|
||||||
// Register the backend
|
// Register the backend
|
||||||
c.auditBroker.Register(entry.Path, backend, entry.Local, c.IsExperimentEnabled(experiments.VaultExperimentCoreAuditEventsAlpha1))
|
c.auditBroker.Register(entry.Path, backend, entry.Local)
|
||||||
if c.logger.IsInfo() {
|
if c.logger.IsInfo() {
|
||||||
c.logger.Info("enabled audit backend", "path", entry.Path, "type", entry.Type)
|
c.logger.Info("enabled audit backend", "path", entry.Path, "type", entry.Type)
|
||||||
}
|
}
|
||||||
@@ -208,8 +208,9 @@ func (c *Core) disableAudit(ctx context.Context, path string, updateStorage bool
|
|||||||
|
|
||||||
c.audit = newTable
|
c.audit = newTable
|
||||||
|
|
||||||
// Unmount the backend
|
// Unmount the backend, any returned error can be ignored since the
|
||||||
c.auditBroker.Deregister(path, c.IsExperimentEnabled(experiments.VaultExperimentCoreAuditEventsAlpha1))
|
// Backend will already have been removed from the AuditBroker's map.
|
||||||
|
c.auditBroker.Deregister(ctx, path)
|
||||||
if c.logger.IsInfo() {
|
if c.logger.IsInfo() {
|
||||||
c.logger.Info("disabled audit backend", "path", path)
|
c.logger.Info("disabled audit backend", "path", path)
|
||||||
}
|
}
|
||||||
@@ -383,7 +384,10 @@ func (c *Core) persistAudit(ctx context.Context, table *MountTable, localOnly bo
|
|||||||
func (c *Core) setupAudits(ctx context.Context) error {
|
func (c *Core) setupAudits(ctx context.Context) error {
|
||||||
brokerLogger := c.baseLogger.Named("audit")
|
brokerLogger := c.baseLogger.Named("audit")
|
||||||
c.AddLogger(brokerLogger)
|
c.AddLogger(brokerLogger)
|
||||||
broker := NewAuditBroker(brokerLogger)
|
broker, err := NewAuditBroker(brokerLogger, c.IsExperimentEnabled(experiments.VaultExperimentCoreAuditEventsAlpha1))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
c.auditLock.Lock()
|
c.auditLock.Lock()
|
||||||
defer c.auditLock.Unlock()
|
defer c.auditLock.Unlock()
|
||||||
@@ -417,7 +421,7 @@ func (c *Core) setupAudits(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Mount the backend
|
// Mount the backend
|
||||||
broker.Register(entry.Path, backend, entry.Local, c.IsExperimentEnabled(experiments.VaultExperimentCoreAuditEventsAlpha1))
|
broker.Register(entry.Path, backend, entry.Local)
|
||||||
|
|
||||||
successCount++
|
successCount++
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
metrics "github.com/armon/go-metrics"
|
metrics "github.com/armon/go-metrics"
|
||||||
|
"github.com/hashicorp/eventlogger"
|
||||||
log "github.com/hashicorp/go-hclog"
|
log "github.com/hashicorp/go-hclog"
|
||||||
multierror "github.com/hashicorp/go-multierror"
|
multierror "github.com/hashicorp/go-multierror"
|
||||||
"github.com/hashicorp/vault/audit"
|
"github.com/hashicorp/vault/audit"
|
||||||
@@ -28,46 +29,82 @@ type AuditBroker struct {
|
|||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
backends map[string]backendEntry
|
backends map[string]backendEntry
|
||||||
logger log.Logger
|
logger log.Logger
|
||||||
|
|
||||||
|
broker *eventlogger.Broker
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAuditBroker creates a new audit broker
|
// NewAuditBroker creates a new audit broker
|
||||||
func NewAuditBroker(log log.Logger) *AuditBroker {
|
func NewAuditBroker(log log.Logger, useEventLogger bool) (*AuditBroker, error) {
|
||||||
|
var eventBroker *eventlogger.Broker
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if useEventLogger {
|
||||||
|
// Ignoring the second error return value since an error will only occur
|
||||||
|
// if an unrecognized eventlogger.RegistrationPolicy is provided to an
|
||||||
|
// eventlogger.Option function.
|
||||||
|
eventBroker, err = eventlogger.NewBroker(eventlogger.WithNodeRegistrationPolicy(eventlogger.DenyOverwrite), eventlogger.WithPipelineRegistrationPolicy(eventlogger.DenyOverwrite))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error creating event broker for audit events: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
b := &AuditBroker{
|
b := &AuditBroker{
|
||||||
backends: make(map[string]backendEntry),
|
backends: make(map[string]backendEntry),
|
||||||
logger: log,
|
logger: log,
|
||||||
|
broker: eventBroker,
|
||||||
}
|
}
|
||||||
return b
|
return b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register is used to add new audit backend to the broker
|
// Register is used to add new audit backend to the broker
|
||||||
func (a *AuditBroker) Register(name string, b audit.Backend, local bool, useEventLogger bool) {
|
func (a *AuditBroker) Register(name string, b audit.Backend, local bool) error {
|
||||||
if useEventLogger {
|
a.Lock()
|
||||||
// TODO: Coming soon
|
defer a.Unlock()
|
||||||
} else {
|
|
||||||
a.Lock()
|
if a.broker != nil {
|
||||||
defer a.Unlock()
|
err := b.RegisterNodesAndPipeline(a.broker, name)
|
||||||
a.backends[name] = backendEntry{
|
if err != nil {
|
||||||
backend: b,
|
return err
|
||||||
local: local,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a.backends[name] = backendEntry{
|
||||||
|
backend: b,
|
||||||
|
local: local,
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deregister is used to remove an audit backend from the broker
|
// Deregister is used to remove an audit backend from the broker
|
||||||
func (a *AuditBroker) Deregister(name string, useEventLogger bool) {
|
func (a *AuditBroker) Deregister(ctx context.Context, name string) error {
|
||||||
if useEventLogger {
|
a.Lock()
|
||||||
// TODO: Coming soon
|
defer a.Unlock()
|
||||||
} else {
|
|
||||||
a.Lock()
|
// Remove the Backend from the map first, so that if an error occurs while
|
||||||
defer a.Unlock()
|
// removing the pipeline and nodes, we can quickly exit this method with
|
||||||
delete(a.backends, name)
|
// the error.
|
||||||
|
delete(a.backends, name)
|
||||||
|
|
||||||
|
if a.broker != nil {
|
||||||
|
// The first return value, a bool, indicates whether
|
||||||
|
// RemovePipelineAndNodes encountered the error while evaluating
|
||||||
|
// pre-conditions (false) or once it started removing the pipeline and
|
||||||
|
// the nodes (true). This code doesn't care either way.
|
||||||
|
_, err := a.broker.RemovePipelineAndNodes(ctx, eventlogger.EventType("audit"), eventlogger.PipelineID(name))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsRegistered is used to check if a given audit backend is registered
|
// IsRegistered is used to check if a given audit backend is registered
|
||||||
func (a *AuditBroker) IsRegistered(name string) bool {
|
func (a *AuditBroker) IsRegistered(name string) bool {
|
||||||
a.RLock()
|
a.RLock()
|
||||||
defer a.RUnlock()
|
defer a.RUnlock()
|
||||||
|
|
||||||
_, ok := a.backends[name]
|
_, ok := a.backends[name]
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
@@ -99,8 +136,10 @@ func (a *AuditBroker) GetHash(ctx context.Context, name string, input string) (s
|
|||||||
// log the given request and that *at least one* succeeds.
|
// log the given request and that *at least one* succeeds.
|
||||||
func (a *AuditBroker) LogRequest(ctx context.Context, in *logical.LogInput, headersConfig *AuditedHeadersConfig) (ret error) {
|
func (a *AuditBroker) LogRequest(ctx context.Context, in *logical.LogInput, headersConfig *AuditedHeadersConfig) (ret error) {
|
||||||
defer metrics.MeasureSince([]string{"audit", "log_request"}, time.Now())
|
defer metrics.MeasureSince([]string{"audit", "log_request"}, time.Now())
|
||||||
|
|
||||||
a.RLock()
|
a.RLock()
|
||||||
defer a.RUnlock()
|
defer a.RUnlock()
|
||||||
|
|
||||||
if in.Request.InboundSSCToken != "" {
|
if in.Request.InboundSSCToken != "" {
|
||||||
if in.Auth != nil {
|
if in.Auth != nil {
|
||||||
reqAuthToken := in.Auth.ClientToken
|
reqAuthToken := in.Auth.ClientToken
|
||||||
|
|||||||
@@ -340,11 +340,14 @@ func verifyDefaultAuditTable(t *testing.T, table *MountTable) {
|
|||||||
|
|
||||||
func TestAuditBroker_LogRequest(t *testing.T) {
|
func TestAuditBroker_LogRequest(t *testing.T) {
|
||||||
l := logging.NewVaultLogger(log.Trace)
|
l := logging.NewVaultLogger(log.Trace)
|
||||||
b := NewAuditBroker(l)
|
b, err := NewAuditBroker(l, false)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
a1 := corehelpers.TestNoopAudit(t, nil)
|
a1 := corehelpers.TestNoopAudit(t, nil)
|
||||||
a2 := corehelpers.TestNoopAudit(t, nil)
|
a2 := corehelpers.TestNoopAudit(t, nil)
|
||||||
b.Register("foo", a1, false, false)
|
b.Register("foo", a1, false)
|
||||||
b.Register("bar", a2, false, false)
|
b.Register("bar", a2, false)
|
||||||
|
|
||||||
auth := &logical.Auth{
|
auth := &logical.Auth{
|
||||||
ClientToken: "foo",
|
ClientToken: "foo",
|
||||||
@@ -427,11 +430,14 @@ func TestAuditBroker_LogRequest(t *testing.T) {
|
|||||||
|
|
||||||
func TestAuditBroker_LogResponse(t *testing.T) {
|
func TestAuditBroker_LogResponse(t *testing.T) {
|
||||||
l := logging.NewVaultLogger(log.Trace)
|
l := logging.NewVaultLogger(log.Trace)
|
||||||
b := NewAuditBroker(l)
|
b, err := NewAuditBroker(l, false)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
a1 := corehelpers.TestNoopAudit(t, nil)
|
a1 := corehelpers.TestNoopAudit(t, nil)
|
||||||
a2 := corehelpers.TestNoopAudit(t, nil)
|
a2 := corehelpers.TestNoopAudit(t, nil)
|
||||||
b.Register("foo", a1, false, false)
|
b.Register("foo", a1, false)
|
||||||
b.Register("bar", a2, false, false)
|
b.Register("bar", a2, false)
|
||||||
|
|
||||||
auth := &logical.Auth{
|
auth := &logical.Auth{
|
||||||
NumUses: 10,
|
NumUses: 10,
|
||||||
@@ -532,13 +538,16 @@ func TestAuditBroker_LogResponse(t *testing.T) {
|
|||||||
|
|
||||||
func TestAuditBroker_AuditHeaders(t *testing.T) {
|
func TestAuditBroker_AuditHeaders(t *testing.T) {
|
||||||
logger := logging.NewVaultLogger(log.Trace)
|
logger := logging.NewVaultLogger(log.Trace)
|
||||||
b := NewAuditBroker(logger)
|
b, err := NewAuditBroker(logger, false)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
_, barrier, _ := mockBarrier(t)
|
_, barrier, _ := mockBarrier(t)
|
||||||
view := NewBarrierView(barrier, "headers/")
|
view := NewBarrierView(barrier, "headers/")
|
||||||
a1 := corehelpers.TestNoopAudit(t, nil)
|
a1 := corehelpers.TestNoopAudit(t, nil)
|
||||||
a2 := corehelpers.TestNoopAudit(t, nil)
|
a2 := corehelpers.TestNoopAudit(t, nil)
|
||||||
b.Register("foo", a1, false, false)
|
b.Register("foo", a1, false)
|
||||||
b.Register("bar", a2, false, false)
|
b.Register("bar", a2, false)
|
||||||
|
|
||||||
auth := &logical.Auth{
|
auth := &logical.Auth{
|
||||||
ClientToken: "foo",
|
ClientToken: "foo",
|
||||||
|
|||||||
@@ -2365,7 +2365,11 @@ func (s standardUnsealStrategy) unseal(ctx context.Context, logger log.Logger, c
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
c.auditBroker = NewAuditBroker(c.logger)
|
var err error
|
||||||
|
c.auditBroker, err = NewAuditBroker(c.logger, c.IsExperimentEnabled(experiments.VaultExperimentCoreAuditEventsAlpha1))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !c.ReplicationState().HasState(consts.ReplicationPerformanceSecondary | consts.ReplicationDRSecondary) {
|
if !c.ReplicationState().HasState(consts.ReplicationPerformanceSecondary | consts.ReplicationDRSecondary) {
|
||||||
|
|||||||
Reference in New Issue
Block a user