-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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-24798: audit - improve error messages #26312
Merged
peteski22
merged 18 commits into
main
from
peteski22/VAULT-24798/audit-error-messages-again
Apr 11, 2024
Merged
Changes from all commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
b49f220
audit: remove 'op' from error messages and do some clean up
peteski22 c0cd8f6
copyright headers
peteski22 521f463
Allow early error checking to be concerned with vault/Core vs. audit
peteski22 36c1a19
Merge branch 'main' into peteski22/VAULT-24798/audit-error-messages-a…
peteski22 0b18b2e
test fixes
peteski22 0098eef
msg: invalid internal parameter
peteski22 1279b02
test fixes
peteski22 7181b1a
Merge branch 'main' into peteski22/VAULT-24798/audit-error-messages-a…
peteski22 92fcfbf
more error msg fixes/test fixes
peteski22 fd54c93
more test fixing
peteski22 91aaf9c
Merge branch 'main' into peteski22/VAULT-24798/audit-error-messages-a…
peteski22 f31bd8b
go mod tidy
peteski22 19d4362
Remove nested/wrapped error when we parse incoming params
peteski22 5744359
Strip off info on the current device we're attempting for error
peteski22 79b6db8
test fixes
peteski22 c5b3977
Merge branch 'main' into peteski22/VAULT-24798/audit-error-messages-a…
peteski22 04aec7e
Removed unused func
peteski22 ed3b927
more error tweaks
peteski22 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,7 +18,6 @@ import ( | |
"github.com/hashicorp/go-hclog" | ||
"github.com/hashicorp/go-multierror" | ||
"github.com/hashicorp/vault/helper/namespace" | ||
"github.com/hashicorp/vault/internal/observability/event" | ||
"github.com/hashicorp/vault/sdk/helper/jsonutil" | ||
"github.com/hashicorp/vault/sdk/helper/salt" | ||
"github.com/hashicorp/vault/sdk/logical" | ||
|
@@ -36,34 +35,92 @@ type timeProvider interface { | |
formattedTime() string | ||
} | ||
|
||
// FormatterConfig is used to provide basic configuration to a formatter. | ||
// Use NewFormatterConfig to initialize the FormatterConfig struct. | ||
type FormatterConfig struct { | ||
Raw bool | ||
HMACAccessor bool | ||
|
||
// Vault lacks pagination in its APIs. As a result, certain list operations can return **very** large responses. | ||
// The user's chosen audit sinks may experience difficulty consuming audit records that swell to tens of megabytes | ||
// of JSON. The responses of list operations are typically not very interesting, as they are mostly lists of keys, | ||
// or, even when they include a "key_info" field, are not returning confidential information. They become even less | ||
// interesting once HMAC-ed by the audit system. | ||
// | ||
// Some example Vault "list" operations that are prone to becoming very large in an active Vault installation are: | ||
// auth/token/accessors/ | ||
// identity/entity/id/ | ||
// identity/entity-alias/id/ | ||
// pki/certs/ | ||
// | ||
// This option exists to provide such users with the option to have response data elided from audit logs, only when | ||
// the operation type is "list". For added safety, the elision only applies to the "keys" and "key_info" fields | ||
// within the response data - these are conventionally the only fields present in a list response - see | ||
// logical.ListResponse, and logical.ListResponseWithInfo. However, other fields are technically possible if a | ||
// plugin author writes unusual code, and these will be preserved in the audit log even with this option enabled. | ||
// The elision replaces the values of the "keys" and "key_info" fields with an integer count of the number of | ||
// entries. This allows even the elided audit logs to still be useful for answering questions like | ||
// "Was any data returned?" or "How many records were listed?". | ||
ElideListResponses bool | ||
|
||
// This should only ever be used in a testing context | ||
OmitTime bool | ||
|
||
// The required/target format for the event (supported: JSONFormat and JSONxFormat). | ||
RequiredFormat format | ||
|
||
// headerFormatter specifies the formatter used for headers that existing in any incoming audit request. | ||
headerFormatter HeaderFormatter | ||
|
||
// Prefix specifies a Prefix that should be prepended to any formatted request or response before serialization. | ||
Prefix string | ||
} | ||
|
||
// EntryFormatter should be used to format audit requests and responses. | ||
// NOTE: Use NewEntryFormatter to initialize the EntryFormatter struct. | ||
type EntryFormatter struct { | ||
config FormatterConfig | ||
salter Salter | ||
logger hclog.Logger | ||
name string | ||
} | ||
|
||
// NewFormatterConfig should be used to create a FormatterConfig. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
// Accepted options: WithElision, WithFormat, WithHMACAccessor, WithOmitTime, WithPrefix, WithRaw. | ||
func NewFormatterConfig(headerFormatter HeaderFormatter, opt ...Option) (FormatterConfig, error) { | ||
if headerFormatter == nil || reflect.ValueOf(headerFormatter).IsNil() { | ||
return FormatterConfig{}, fmt.Errorf("header formatter is required: %w", ErrInvalidParameter) | ||
} | ||
|
||
opts, err := getOpts(opt...) | ||
if err != nil { | ||
return FormatterConfig{}, err | ||
} | ||
|
||
return FormatterConfig{ | ||
headerFormatter: headerFormatter, | ||
ElideListResponses: opts.withElision, | ||
HMACAccessor: opts.withHMACAccessor, | ||
OmitTime: opts.withOmitTime, | ||
Prefix: opts.withPrefix, | ||
Raw: opts.withRaw, | ||
RequiredFormat: opts.withFormat, | ||
}, nil | ||
} | ||
|
||
// NewEntryFormatter should be used to create an EntryFormatter. | ||
func NewEntryFormatter(name string, config FormatterConfig, salter Salter, logger hclog.Logger) (*EntryFormatter, error) { | ||
const op = "audit.NewEntryFormatter" | ||
|
||
name = strings.TrimSpace(name) | ||
if name == "" { | ||
return nil, fmt.Errorf("%s: name is required: %w", op, event.ErrInvalidParameter) | ||
return nil, fmt.Errorf("name is required: %w", ErrInvalidParameter) | ||
} | ||
|
||
if salter == nil { | ||
return nil, fmt.Errorf("%s: cannot create a new audit formatter with nil salter: %w", op, event.ErrInvalidParameter) | ||
return nil, fmt.Errorf("cannot create a new audit formatter with nil salter: %w", ErrInvalidParameter) | ||
} | ||
|
||
if logger == nil || reflect.ValueOf(logger).IsNil() { | ||
return nil, fmt.Errorf("%s: cannot create a new audit formatter with nil logger: %w", op, event.ErrInvalidParameter) | ||
} | ||
|
||
// We need to ensure that the format isn't just some default empty string. | ||
if err := config.RequiredFormat.validate(); err != nil { | ||
return nil, fmt.Errorf("%s: format not valid: %w", op, err) | ||
return nil, fmt.Errorf("cannot create a new audit formatter with nil logger: %w", ErrInvalidParameter) | ||
} | ||
|
||
return &EntryFormatter{ | ||
|
@@ -87,8 +144,6 @@ func (*EntryFormatter) Type() eventlogger.NodeType { | |
// Process will attempt to parse the incoming event data into a corresponding | ||
// audit Request/Response which is serialized to JSON/JSONx and stored within the event. | ||
func (f *EntryFormatter) Process(ctx context.Context, e *eventlogger.Event) (_ *eventlogger.Event, retErr error) { | ||
const op = "audit.(EntryFormatter).Process" | ||
|
||
// Return early if the context was cancelled, eventlogger will not carry on | ||
// asking nodes to process, so any sink node in the pipeline won't be called. | ||
select { | ||
|
@@ -100,16 +155,16 @@ func (f *EntryFormatter) Process(ctx context.Context, e *eventlogger.Event) (_ * | |
// Perform validation on the event, then retrieve the underlying AuditEvent | ||
// and LogInput (from the AuditEvent Data). | ||
if e == nil { | ||
return nil, fmt.Errorf("%s: event is nil: %w", op, event.ErrInvalidParameter) | ||
return nil, fmt.Errorf("event is nil: %w", ErrInvalidParameter) | ||
} | ||
|
||
a, ok := e.Payload.(*AuditEvent) | ||
if !ok { | ||
return nil, fmt.Errorf("%s: cannot parse event payload: %w", op, event.ErrInvalidParameter) | ||
return nil, fmt.Errorf("cannot parse event payload: %w", ErrInvalidParameter) | ||
} | ||
|
||
if a.Data == nil { | ||
return nil, fmt.Errorf("%s: cannot audit event (%s) with no data: %w", op, a.Subtype, event.ErrInvalidParameter) | ||
return nil, fmt.Errorf("cannot audit event (%s) with no data: %w", a.Subtype, ErrInvalidParameter) | ||
} | ||
|
||
// Handle panics | ||
|
@@ -126,13 +181,13 @@ func (f *EntryFormatter) Process(ctx context.Context, e *eventlogger.Event) (_ * | |
"stacktrace", string(debug.Stack())) | ||
|
||
// Ensure that we add this error onto any pre-existing error that was being returned. | ||
retErr = multierror.Append(retErr, fmt.Errorf("%s: panic generating audit log: %q", op, f.name)).ErrorOrNil() | ||
retErr = multierror.Append(retErr, fmt.Errorf("panic generating audit log: %q", f.name)).ErrorOrNil() | ||
}() | ||
|
||
// Take a copy of the event data before we modify anything. | ||
data, err := a.Data.Clone() | ||
if err != nil { | ||
return nil, fmt.Errorf("%s: unable to copy audit event data: %w", op, err) | ||
return nil, fmt.Errorf("unable to clone audit event data: %w", err) | ||
} | ||
|
||
// If the request is present in the input data, apply header configuration | ||
|
@@ -144,7 +199,7 @@ func (f *EntryFormatter) Process(ctx context.Context, e *eventlogger.Event) (_ * | |
// e.g. via: /sys/config/auditing/request-headers/:name | ||
data.Request.Headers, err = f.config.headerFormatter.ApplyConfig(ctx, data.Request.Headers, f.salter) | ||
if err != nil { | ||
return nil, fmt.Errorf("%s: unable to transform headers for auditing: %w", op, err) | ||
return nil, fmt.Errorf("unable to transform headers for auditing: %w", err) | ||
} | ||
} | ||
|
||
|
@@ -165,25 +220,25 @@ func (f *EntryFormatter) Process(ctx context.Context, e *eventlogger.Event) (_ * | |
case ResponseType: | ||
entry, err = f.FormatResponse(ctx, data, a) | ||
default: | ||
return nil, fmt.Errorf("%s: unknown audit event subtype: %q", op, a.Subtype) | ||
return nil, fmt.Errorf("unknown audit event subtype: %q", a.Subtype) | ||
} | ||
if err != nil { | ||
return nil, fmt.Errorf("%s: unable to parse %s from audit event: %w", op, a.Subtype.String(), err) | ||
return nil, fmt.Errorf("unable to parse %s from audit event: %w", a.Subtype, err) | ||
} | ||
|
||
result, err := jsonutil.EncodeJSON(entry) | ||
if err != nil { | ||
return nil, fmt.Errorf("%s: unable to format %s: %w", op, a.Subtype.String(), err) | ||
return nil, fmt.Errorf("unable to format %s: %w", a.Subtype, err) | ||
} | ||
|
||
if f.config.RequiredFormat == JSONxFormat { | ||
var err error | ||
result, err = jsonx.EncodeJSONBytes(result) | ||
if err != nil { | ||
return nil, fmt.Errorf("%s: unable to encode JSONx using JSON data: %w", op, err) | ||
return nil, fmt.Errorf("unable to encode JSONx using JSON data: %w", err) | ||
} | ||
if result == nil { | ||
return nil, fmt.Errorf("%s: encoded JSONx was nil: %w", op, err) | ||
return nil, fmt.Errorf("encoded JSONx was nil: %w", err) | ||
} | ||
} | ||
|
||
|
@@ -569,31 +624,6 @@ func (f *EntryFormatter) FormatResponse(ctx context.Context, in *logical.LogInpu | |
return respEntry, nil | ||
} | ||
|
||
// NewFormatterConfig should be used to create a FormatterConfig. | ||
// Accepted options: WithElision, WithFormat, WithHMACAccessor, WithOmitTime, WithPrefix, WithRaw. | ||
func NewFormatterConfig(headerFormatter HeaderFormatter, opt ...Option) (FormatterConfig, error) { | ||
const op = "audit.NewFormatterConfig" | ||
|
||
if headerFormatter == nil || reflect.ValueOf(headerFormatter).IsNil() { | ||
return FormatterConfig{}, fmt.Errorf("%s: header formatter is required: %w", op, event.ErrInvalidParameter) | ||
} | ||
|
||
opts, err := getOpts(opt...) | ||
if err != nil { | ||
return FormatterConfig{}, fmt.Errorf("%s: error applying options: %w", op, err) | ||
} | ||
|
||
return FormatterConfig{ | ||
headerFormatter: headerFormatter, | ||
ElideListResponses: opts.withElision, | ||
HMACAccessor: opts.withHMACAccessor, | ||
OmitTime: opts.withOmitTime, | ||
Prefix: opts.withPrefix, | ||
Raw: opts.withRaw, | ||
RequiredFormat: opts.withFormat, | ||
}, nil | ||
} | ||
|
||
// getRemoteAddr safely gets the remote address avoiding a nil pointer | ||
func getRemoteAddr(req *logical.Request) string { | ||
if req != nil && req.Connection != nil { | ||
|
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FormatterConfig
is moved fromaudit/types.go