diff --git a/changelog/28751.txt b/changelog/28751.txt new file mode 100644 index 000000000000..05c086597361 --- /dev/null +++ b/changelog/28751.txt @@ -0,0 +1,3 @@ +```release-note:bug +audit: Prevent users from enabling multiple audit devices of file type with the same file_path to write to. +``` diff --git a/vault/audit.go b/vault/audit.go index 9d3c57c65f73..5e2473b13971 100644 --- a/vault/audit.go +++ b/vault/audit.go @@ -115,6 +115,13 @@ func (c *Core) enableAudit(ctx context.Context, entry *MountEntry, updateStorage case strings.HasPrefix(entry.Path, ent.Path): return fmt.Errorf("path already in use: %w", audit.ErrExternalOptions) } + + // Ensure that the provided file_path argument isn't already used for another audit device's file_path. + if entry.Type == "file" { + if entry.Options["file_path"] == ent.Options["file_path"] { + return fmt.Errorf("file_path already in use: %w", audit.ErrExternalOptions) + } + } } // Generate a new UUID and view diff --git a/vault/audit_test.go b/vault/audit_test.go index 23d39723fb09..60b2bd27ee1c 100644 --- a/vault/audit_test.go +++ b/vault/audit_test.go @@ -105,6 +105,49 @@ func TestCore_EnableAudit(t *testing.T) { } } +// TestCore_EnableExistingAudit ensures that we don't allow enabling a file audit device +// with the same `file_path` as one of the existing ones. +func TestCore_EnableExistingAudit(t *testing.T) { + c, _, _ := TestCoreUnsealed(t) + + // First audit backend entry + me := &MountEntry{ + Table: auditTableType, + Path: "foo", + Type: audit.TypeFile, + Options: map[string]string{ + "file_path": "stdout", + }, + } + + // Second audit backend entry + me2 := &MountEntry{ + Table: auditTableType, + Path: "foo2", + Type: audit.TypeFile, + Options: map[string]string{ + "file_path": "stdout", + }, + } + + // Enable first audit backend + err := c.enableAudit(namespace.RootContext(context.Background()), me, true) + if err != nil { + t.Errorf("failed to enable audit for path 'foo': %v", err) + } + + // Check if the first audit backend is registered + if !c.auditBroker.IsRegistered("foo/") { + t.Errorf("audit backend for path 'foo/' is not registered") + } + + // Enable second audit backend + err = c.enableAudit(namespace.RootContext(context.Background()), me2, true) + if err == nil { + t.Errorf("Should not be able to enable audit for path 'foo2' due to duplication: %v", err) + } +} + func TestCore_EnableAudit_MixedFailures(t *testing.T) { c, _, _ := TestCoreUnsealed(t)