Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VAULT-23334: CE changes to support exclusion in audit #26615

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions audit/backend.go
Expand Up @@ -19,6 +19,7 @@ import (

const (
optionElideListResponses = "elide_list_responses"
optionExclude = "exclude"
optionFallback = "fallback"
optionFilter = "filter"
optionFormat = "format"
Expand Down Expand Up @@ -253,6 +254,7 @@ func HasInvalidOptions(options map[string]string) bool {
// are only for use in the Enterprise version of Vault.
func hasEnterpriseAuditOptions(options map[string]string) bool {
enterpriseAuditOptions := []string{
optionExclude,
optionFallback,
optionFilter,
}
Expand Down
18 changes: 18 additions & 0 deletions audit/backend_test.go
Expand Up @@ -188,6 +188,15 @@ func TestBackend_hasEnterpriseAuditOptions(t *testing.T) {
},
expected: true,
},
"ent-opt-exclude": {
input: map[string]string{
"exclude": `{
"condition": "\"/request/mount_type\" == transit",
"fields": [ "/request/data", "/response/data" ]
}`,
},
expected: true,
},
}

for name, tc := range tests {
Expand Down Expand Up @@ -241,6 +250,15 @@ func TestBackend_hasInvalidAuditOptions(t *testing.T) {
},
expected: !constants.IsEnterprise,
},
"ent-opt-exclude": {
input: map[string]string{
"exclude": `{
"condition": "\"/request/mount_type\" == transit",
"fields": [ "/request/data", "/response/data" ]
}`,
},
expected: !constants.IsEnterprise,
},
}

for name, tc := range tests {
Expand Down
11 changes: 11 additions & 0 deletions audit/entry_formatter.go
Expand Up @@ -162,6 +162,17 @@ func (f *entryFormatter) Process(ctx context.Context, e *eventlogger.Event) (_ *
return nil, fmt.Errorf("unable to parse %s from audit event: %w", a.Subtype, err)
}

// If this pipeline has been configured with (Enterprise-only) exclusions then
// attempt to exclude the fields from the audit entry.
if f.shouldExclude() {
m, err := f.excludeFields(entry)
if err != nil {
return nil, fmt.Errorf("unable to exclude %s audit data from %q: %w", a.Subtype, f.name, err)
}

entry = m
}

result, err := jsonutil.EncodeJSON(entry)
if err != nil {
return nil, fmt.Errorf("unable to format %s: %w", a.Subtype, err)
Expand Down
18 changes: 18 additions & 0 deletions audit/entry_formatter_ce.go
@@ -0,0 +1,18 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1

//go:build !enterprise

package audit

import (
"errors"
)

func (f *entryFormatter) shouldExclude() bool {
return false
}

func (f *entryFormatter) excludeFields(entry any) (map[string]any, error) {
return nil, errors.New("enterprise-only feature: audit exclusion")
}
37 changes: 37 additions & 0 deletions audit/entry_formatter_ce_test.go
@@ -0,0 +1,37 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1

//go:build !enterprise

package audit

import (
"testing"

"github.com/hashicorp/go-hclog"
"github.com/stretchr/testify/require"
)

// TestEntryFormatter_excludeFields tests that we can exclude data based on the
// pre-configured conditions/fields of the EntryFormatter. It covers some scenarios
// where we expect errors due to invalid input, which is unlikely to happen in reality.
func TestEntryFormatter_excludeFields(t *testing.T) {
// Create the formatter node.
cfg, err := newFormatterConfig(&testHeaderFormatter{}, nil)
require.NoError(t, err)
ss := newStaticSalt(t)

// We intentionally create the EntryFormatter manually, as we wouldn't be
// able to set exclusions via NewEntryFormatter WithExclusions option.
formatter := &entryFormatter{
config: cfg,
salter: ss,
logger: hclog.NewNullLogger(),
name: "juan",
}

res, err := formatter.excludeFields(nil)
require.Error(t, err)
require.EqualError(t, err, "enterprise-only feature: audit exclusion")
require.Nil(t, res)
}
8 changes: 8 additions & 0 deletions audit/entry_formatter_config.go
Expand Up @@ -12,6 +12,8 @@ import (
// formatterConfig is used to provide basic configuration to a formatter.
// Use newFormatterConfig to initialize the formatterConfig struct.
type formatterConfig struct {
formatterConfigEnt

raw bool
hmacAccessor bool

Expand Down Expand Up @@ -101,7 +103,13 @@ func newFormatterConfig(headerFormatter HeaderFormatter, config map[string]strin
return formatterConfig{}, err
}

fmtCfgEnt, err := newFormatterConfigEnt(config)
if err != nil {
return formatterConfig{}, err
}

return formatterConfig{
formatterConfigEnt: fmtCfgEnt,
headerFormatter: headerFormatter,
elideListResponses: opts.withElision,
hmacAccessor: opts.withHMACAccessor,
Expand Down
16 changes: 16 additions & 0 deletions audit/entry_formatter_config_ce.go
@@ -0,0 +1,16 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1

//go:build !enterprise

package audit

// formatterConfigEnt provides extensions to a formatterConfig which behave differently
// for Enterprise and community edition.
// NOTE: Use newFormatterConfigEnt to initialize the formatterConfigEnt struct.
type formatterConfigEnt struct{}

// newFormatterConfigEnt should be used to create formatterConfigEnt.
func newFormatterConfigEnt(config map[string]string) (formatterConfigEnt, error) {
return formatterConfigEnt{}, nil
}