diff --git a/go.mod b/go.mod index 2620e050d..ca0ae7414 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,7 @@ require ( github.com/hashicorp/cap v0.1.1 github.com/hashicorp/dawdle v0.4.0 github.com/hashicorp/dbassert v0.0.0-20210708202608-ecf920cf1ed8 - github.com/hashicorp/eventlogger v0.1.0 + github.com/hashicorp/eventlogger v0.1.1-0.20211104100552-e1e801e50144 github.com/hashicorp/eventlogger/filters/encrypt v0.1.6-0.20211027211326-5db60a48f239 github.com/hashicorp/go-bexpr v0.1.10 github.com/hashicorp/go-cleanhttp v0.5.2 diff --git a/go.sum b/go.sum index 783ca4491..b96d2769f 100644 --- a/go.sum +++ b/go.sum @@ -456,8 +456,9 @@ github.com/hashicorp/dbassert v0.0.0-20210708202608-ecf920cf1ed8/go.mod h1:+B5eZ github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/eventlogger v0.1.0 h1:S6xc4gZVzewuDUP4R4Ngko419h/CGDuV/b4ADL3XLik= github.com/hashicorp/eventlogger v0.1.0/go.mod h1:a3IXf1aEJfpCPzseTOrwKj4fVW/Qn3oEmpQeaIznzH0= +github.com/hashicorp/eventlogger v0.1.1-0.20211104100552-e1e801e50144 h1:PCl0HtlVnIIloIozAKvjICu6K4IghKXAKNny3R3b2nI= +github.com/hashicorp/eventlogger v0.1.1-0.20211104100552-e1e801e50144/go.mod h1:a3IXf1aEJfpCPzseTOrwKj4fVW/Qn3oEmpQeaIznzH0= github.com/hashicorp/eventlogger/filters/encrypt v0.1.6-0.20211027211326-5db60a48f239 h1:Yh9tY0lige+y0trmjQeT9NRDo6+YvtNAzbmUNOsIUzI= github.com/hashicorp/eventlogger/filters/encrypt v0.1.6-0.20211027211326-5db60a48f239/go.mod h1:8rcez7Kw1zanB0/074qnOuGu7zxmNh9Xr2ZI+K4xVIA= github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= diff --git a/internal/cmd/base/option.go b/internal/cmd/base/option.go index f528d6e28..d1f006475 100644 --- a/internal/cmd/base/option.go +++ b/internal/cmd/base/option.go @@ -1,6 +1,9 @@ package base -import "github.com/hashicorp/boundary/internal/observability/event" +import ( + "github.com/hashicorp/boundary/internal/observability/event" + wrapping "github.com/hashicorp/go-kms-wrapping" +) // getOpts - iterate the inbound Options and return a struct. func getOpts(opt ...Option) Options { @@ -31,6 +34,7 @@ type Options struct { withDatabaseTemplate string withEventerConfig *event.EventerConfig withEventFlags *EventFlags + withEventWrapper wrapping.Wrapper withAttributeFieldPrefix string withStatusCode int } @@ -136,6 +140,12 @@ func WithEventFlags(flags *EventFlags) Option { } } +func WithEventAuditWrapper(w wrapping.Wrapper) Option { + return func(o *Options) { + o.withEventWrapper = w + } +} + // WithAttributeFieldPrefix tells the command what prefix // to attach to attribute fields when they are returned as errors. func WithAttributeFieldPrefix(p string) Option { diff --git a/internal/cmd/base/servers.go b/internal/cmd/base/servers.go index 965d49716..ba2555aed 100644 --- a/internal/cmd/base/servers.go +++ b/internal/cmd/base/servers.go @@ -192,7 +192,7 @@ func (b *Server) SetupEventing(logger hclog.Logger, serializationLock *sync.Mute } } - e, err := event.NewEventer(logger, serializationLock, serverName, *opts.withEventerConfig) + e, err := event.NewEventer(logger, serializationLock, serverName, *opts.withEventerConfig, event.WithAuditWrapper(opts.withEventWrapper)) if err != nil { return berrors.WrapDeprecated(err, op, berrors.WithMsg("unable to create eventer")) } diff --git a/internal/cmd/commands/dev/dev.go b/internal/cmd/commands/dev/dev.go index 0fae56c09..37bd8f229 100644 --- a/internal/cmd/commands/dev/dev.go +++ b/internal/cmd/commands/dev/dev.go @@ -490,10 +490,6 @@ func (c *Command) Run(args []string) int { c.UI.Error(err.Error()) return base.CommandUserError } - if err := c.SetupEventing(c.Logger, c.StderrLock, serverName, base.WithEventerConfig(c.Config.Eventing), base.WithEventFlags(eventFlags)); err != nil { - c.UI.Error(err.Error()) - return base.CommandCliError - } // Initialize status grace period (0 denotes using env or default // here) @@ -512,6 +508,15 @@ func (c *Command) Run(args []string) int { c.UI.Error("Controller KMS not found after parsing KMS blocks") return base.CommandUserError } + if err := c.SetupEventing( + c.Logger, + c.StderrLock, + serverName, + base.WithEventerConfig(c.Config.Eventing), + base.WithEventFlags(eventFlags)); err != nil { + c.UI.Error(err.Error()) + return base.CommandCliError + } if c.WorkerAuthKms == nil { c.UI.Error("Worker Auth KMS not found after parsing KMS blocks") return base.CommandUserError diff --git a/internal/libs/crypto/error.go b/internal/libs/crypto/error.go index 6e97908cc..c59844260 100644 --- a/internal/libs/crypto/error.go +++ b/internal/libs/crypto/error.go @@ -4,6 +4,4 @@ import ( "errors" ) -var ( - ErrInvalidParameter = errors.New("Invalid parameter") -) +var ErrInvalidParameter = errors.New("Invalid parameter") diff --git a/internal/observability/event/cloudevents_formatter_node.go b/internal/observability/event/cloudevents_formatter_node.go index c56227624..6bcd56e51 100644 --- a/internal/observability/event/cloudevents_formatter_node.go +++ b/internal/observability/event/cloudevents_formatter_node.go @@ -4,11 +4,13 @@ import ( "context" "fmt" "net/url" + "sync" filterpkg "github.com/hashicorp/boundary/internal/filter" "github.com/hashicorp/eventlogger" "github.com/hashicorp/eventlogger/formatter_filters/cloudevents" "github.com/hashicorp/go-bexpr" + wrapping "github.com/hashicorp/go-kms-wrapping" ) // cloudEventsFormatterFilter represents an eventlogger.cloudEventsFormatterFilter which filters events based on allow and @@ -17,6 +19,7 @@ type cloudEventsFormatterFilter struct { *cloudevents.FormatterFilter allow []*filter deny []*filter + l sync.RWMutex } // newCloudEventsFormatterFilter creates a new filter node using the optional allow and deny filters @@ -67,6 +70,23 @@ func newCloudEventsFormatterFilter(source *url.URL, format cloudevents.Format, o return &n, nil } +// Rotate supports rotating the filter's wrapper. No options are currently +// supported. +func (f *cloudEventsFormatterFilter) Rotate(w wrapping.Wrapper, _ ...Option) error { + const op = "event.(cloudEventsFormatterFilter).Rotate" + if w == nil { + return fmt.Errorf("%s: missing wrapper: %w", op, ErrInvalidParameter) + } + f.l.Lock() + defer f.l.Unlock() + h, err := newSigner(context.Background(), w, nil, nil) + if err != nil { + return err + } + f.Signer = cloudevents.Signer(h) + return nil +} + func newPredicate(allow, deny []*filter) func(ctx context.Context, ce interface{}) (bool, error) { return func(ctx context.Context, ce interface{}) (bool, error) { if len(allow) == 0 && len(deny) == 0 { diff --git a/internal/observability/event/cloudevents_formatter_node_test.go b/internal/observability/event/cloudevents_formatter_node_test.go index fa1090098..75dd9a714 100644 --- a/internal/observability/event/cloudevents_formatter_node_test.go +++ b/internal/observability/event/cloudevents_formatter_node_test.go @@ -10,6 +10,7 @@ import ( "github.com/hashicorp/eventlogger" "github.com/hashicorp/eventlogger/formatter_filters/cloudevents" + wrapping "github.com/hashicorp/go-kms-wrapping" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -343,3 +344,41 @@ func TestNode_Process(t *testing.T) { }) } } + +func Test_cloudEventsFormatter_Rotate(t *testing.T) { + tests := []struct { + name string + f *cloudEventsFormatterFilter + w wrapping.Wrapper + wantIsError error + wantErrContains string + }{ + { + name: "missing-wrapper", + f: &cloudEventsFormatterFilter{FormatterFilter: &cloudevents.FormatterFilter{}}, + wantIsError: ErrInvalidParameter, + wantErrContains: "missing wrapper", + }, + { + name: "valid", + f: &cloudEventsFormatterFilter{FormatterFilter: &cloudevents.FormatterFilter{}}, + w: testWrapper(t), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + err := tt.f.Rotate(tt.w) + if tt.wantIsError != nil { + require.Error(err) + assert.ErrorIs(err, tt.wantIsError) + if tt.wantErrContains != "" { + assert.Contains(err.Error(), tt.wantErrContains) + } + return + } + assert.NotNil(tt.f.Signer) + }) + } +} diff --git a/internal/observability/event/eventer.go b/internal/observability/event/eventer.go index 4bf82eab4..bddc53f91 100644 --- a/internal/observability/event/eventer.go +++ b/internal/observability/event/eventer.go @@ -21,6 +21,7 @@ import ( "github.com/hashicorp/eventlogger/formatter_filters/cloudevents" "github.com/hashicorp/eventlogger/sinks/writer" "github.com/hashicorp/go-hclog" + wrapping "github.com/hashicorp/go-kms-wrapping" "google.golang.org/protobuf/types/known/fieldmaskpb" ) @@ -67,6 +68,7 @@ type Eventer struct { auditPipelines []pipeline observationPipelines []pipeline errPipelines []pipeline + auditWrapperNodes []interface{} } type pipeline struct { @@ -187,9 +189,10 @@ func NewEventer(log hclog.Logger, serializationLock *sync.Mutex, serverName stri } e := &Eventer{ - logger: log, - conf: c, - broker: b, + logger: log, + conf: c, + broker: b, + auditWrapperNodes: []interface{}{}, } if !opts.withNow.IsZero() { @@ -208,7 +211,8 @@ func NewEventer(log hclog.Logger, serializationLock *sync.Mutex, serverName stri allSinkFilenames := map[string]bool{} for _, s := range c.Sinks { - fmtId, fmtNode, err := newFmtFilterNode(serverName, *s) + fmtId, fmtNode, err := newFmtFilterNode(serverName, *s, opt...) + e.auditWrapperNodes = append(e.auditWrapperNodes, fmtNode) if err != nil { return nil, fmt.Errorf("%s: %w", op, err) } @@ -287,6 +291,7 @@ func NewEventer(log hclog.Logger, serializationLock *sync.Mutex, serverName stri if err != nil { return nil, fmt.Errorf("%s: %w", op, err) } + e.auditWrapperNodes = append(e.auditWrapperNodes, encryptFilter) if len(s.AuditConfig.FilterOverrides) > 0 { overrides := encrypt.DefaultFilterOperations() for k, v := range s.AuditConfig.FilterOverrides { @@ -464,11 +469,12 @@ func NewEventer(log hclog.Logger, serializationLock *sync.Mutex, serverName stri return e, nil } -func newFmtFilterNode(serverName string, c SinkConfig) (eventlogger.NodeID, eventlogger.Node, error) { +func newFmtFilterNode(serverName string, c SinkConfig, opt ...Option) (eventlogger.NodeID, eventlogger.Node, error) { const op = "newFmtFilterNode" if serverName == "" { return "", nil, fmt.Errorf("%s: missing server name: %w", op, ErrInvalidParameter) } + opts := getOpts(opt...) var fmtId eventlogger.NodeID var fmtNode eventlogger.Node switch c.Format { @@ -479,7 +485,7 @@ func newFmtFilterNode(serverName string, c SinkConfig) (eventlogger.NodeID, even } fmtId = eventlogger.NodeID(id) - fmtNode, err = newHclogFormatterFilter(c.Format == JSONHclogSinkFormat, WithAllow(c.AllowFilters...), WithDeny(c.DenyFilters...)) + fmtNode, err = newHclogFormatterFilter(c.Format == JSONHclogSinkFormat, WithAllow(c.AllowFilters...), WithDeny(c.DenyFilters...), WithAuditWrapper(opts.withAuditWrapper)) if err != nil { return "", nil, fmt.Errorf("%s: %w", op, err) } @@ -531,6 +537,26 @@ func DefaultSink() *SinkConfig { } } +func (e *Eventer) RotateAuditWrapper(ctx context.Context, newWrapper wrapping.Wrapper) error { + const op = "event.(Eventer).RotateAuditWrapper" + if newWrapper == nil { + return fmt.Errorf("%s: missing wrapper: %w", op, ErrInvalidParameter) + } + for _, n := range e.auditWrapperNodes { + switch w := n.(type) { + case *hclogFormatterFilter: + w.Rotate(newWrapper) + case *cloudEventsFormatterFilter: + w.Rotate(newWrapper) + case *encrypt.Filter: + w.Rotate(encrypt.WithWrapper(newWrapper)) + default: + return fmt.Errorf("%s: unsupported node type (%s): %w", op, reflect.TypeOf(w), ErrInvalidParameter) + } + } + return nil +} + // writeObservation writes/sends an Observation event. func (e *Eventer) writeObservation(ctx context.Context, event *observation) error { const op = "event.(Eventer).writeObservation" diff --git a/internal/observability/event/eventer_test.go b/internal/observability/event/eventer_test.go index 8b0151f2d..35353b51e 100644 --- a/internal/observability/event/eventer_test.go +++ b/internal/observability/event/eventer_test.go @@ -13,8 +13,10 @@ import ( "testing" "github.com/hashicorp/eventlogger" + "github.com/hashicorp/eventlogger/filters/encrypt" "github.com/hashicorp/eventlogger/formatter_filters/cloudevents" "github.com/hashicorp/go-hclog" + wrapping "github.com/hashicorp/go-kms-wrapping" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -149,6 +151,7 @@ func Test_InitSysEventer(t *testing.T) { tt.want.auditPipelines = got.auditPipelines tt.want.errPipelines = got.errPipelines tt.want.observationPipelines = got.observationPipelines + tt.want.auditWrapperNodes = got.auditWrapperNodes assert.Equal(tt.want, got) }) } @@ -716,6 +719,7 @@ func Test_NewEventer(t *testing.T) { tt.want.auditPipelines = got.auditPipelines tt.want.errPipelines = got.errPipelines tt.want.observationPipelines = got.observationPipelines + tt.want.auditWrapperNodes = got.auditWrapperNodes assert.Equal(tt.want, got) assert.Lenf(testBroker.registeredNodeIds, len(tt.wantRegistered), "got nodes: %q", testBroker.registeredNodeIds) @@ -1184,3 +1188,69 @@ func Test_logAdapter_Write(t *testing.T) { }) } } + +func TestEventer_RotateAuditWrapper(t *testing.T) { + // this test and its subtests cannot be run in parallel because of it's + // dependency on the sysEventer + + cloudEventsConfig := TestEventerConfig(t, "TestEventer_RotateAuditWrapper") + hclogConfig := TestEventerConfig(t, "TestEventer_RotateAuditWrapper", testWithSinkFormat(t, JSONHclogSinkFormat)) + + testLock := &sync.Mutex{} + testLogger := hclog.New(&hclog.LoggerOptions{ + Mutex: testLock, + Name: "test", + }) + + testCtx := context.Background() + tests := []struct { + name string + w wrapping.Wrapper + config TestConfig + wantIsError error + wantErrContains string + }{ + { + name: "missing-wrapper", + config: cloudEventsConfig, + wantIsError: ErrInvalidParameter, + wantErrContains: "missing wrapper", + }, + { + name: "valid-cloudevents", + w: testWrapper(t), + config: cloudEventsConfig, + }, + { + name: "valid-hclog", + w: testWrapper(t), + config: hclogConfig, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + require.NoError(InitSysEventer(testLogger, testLock, "TestEventer_RotateAuditWrapper", WithEventerConfig(&tt.config.EventerConfig))) + eventer := SysEventer() + err := eventer.RotateAuditWrapper(testCtx, tt.w) + if tt.wantIsError != nil { + require.Error(err) + assert.ErrorIs(err, tt.wantIsError) + if tt.wantErrContains != "" { + assert.Contains(err.Error(), tt.wantErrContains) + } + return + } + for _, n := range eventer.auditWrapperNodes { + switch w := n.(type) { + case *hclogFormatterFilter: + assert.NotNil(w.signer) + case *cloudEventsFormatterFilter: + assert.NotNil(w.Signer) + case *encrypt.Filter: + assert.NotNil(w.Wrapper) + } + } + }) + } +} diff --git a/internal/observability/event/hclog_formatter_node.go b/internal/observability/event/hclog_formatter_node.go index 69af2b2ac..8704f6128 100644 --- a/internal/observability/event/hclog_formatter_node.go +++ b/internal/observability/event/hclog_formatter_node.go @@ -3,13 +3,16 @@ package event import ( "bytes" "context" + "encoding/base64" "errors" "fmt" "reflect" + "sync" "github.com/fatih/structs" "github.com/hashicorp/eventlogger" "github.com/hashicorp/go-hclog" + wrapping "github.com/hashicorp/go-kms-wrapping" ) const ( @@ -28,14 +31,18 @@ type hclogFormatterFilter struct { predicate func(ctx context.Context, i interface{}) (bool, error) allow []*filter deny []*filter + signer signer + l sync.RWMutex } func newHclogFormatterFilter(jsonFormat bool, opt ...Option) (*hclogFormatterFilter, error) { const op = "event.NewHclogFormatter" + opts := getOpts(opt...) + var s signer n := hclogFormatterFilter{ jsonFormat: jsonFormat, + signer: s, } - opts := getOpts(opt...) // intentionally not checking if allow and/or deny optional filters were // supplied since having a filter node with no filters is okay. @@ -64,6 +71,23 @@ func newHclogFormatterFilter(jsonFormat bool, opt ...Option) (*hclogFormatterFil return &n, nil } +// Rotate supports rotating the filter's wrapper. No options are currently +// supported. +func (f *hclogFormatterFilter) Rotate(w wrapping.Wrapper, _ ...Option) error { + const op = "event.(hclogFormatterFilter).Rotate" + if w == nil { + return fmt.Errorf("%s: missing wrapper: %w", op, ErrInvalidParameter) + } + f.l.Lock() + defer f.l.Unlock() + h, err := newSigner(context.Background(), w, nil, nil) + if err != nil { + return err + } + f.signer = h + return nil +} + // Reopen is a no op func (_ *hclogFormatterFilter) Reopen() error { return nil } @@ -154,23 +178,20 @@ func (f *hclogFormatterFilter) Process(ctx context.Context, e *eventlogger.Event args = append(args, k, v) } - var buf bytes.Buffer - logger := hclog.New(&hclog.LoggerOptions{ - Output: &buf, - Level: hclog.Trace, - JSONFormat: f.jsonFormat, - }) - const eventMarker = " event" - switch string(e.Type) { - case string(ErrorType): - logger.Error(string(e.Type)+eventMarker, args...) - case string(ObservationType), string(SystemType), string(AuditType): - logger.Info(string(e.Type)+eventMarker, args...) - default: - // well, we should ever hit this, since we should be specific about the - // event type we're processing, but adding this default to just be sure - // we haven't missed anything. - logger.Trace(string(e.Type)+eventMarker, args...) + buf, err := hclogBytes(e.Type, f.jsonFormat, args) + if err != nil { + return nil, fmt.Errorf("%s: unable to format: %w", op, err) + } + if f.signer != nil && string(e.Type) == string(AuditType) { + bufHmac, err := f.signer(ctx, buf.Bytes()) + if err != nil { + return nil, fmt.Errorf("%s: unable to hmac-sha256: %w", op, err) + } + args = append(args, "serialized", base64.RawURLEncoding.EncodeToString(buf.Bytes()), "serialized_hmac", bufHmac) + buf, err = hclogBytes(e.Type, f.jsonFormat, args) + if err != nil { + return nil, fmt.Errorf("%s: unable to format after hmac-sha256: %w", op, err) + } } switch f.jsonFormat { case true: @@ -181,3 +202,25 @@ func (f *hclogFormatterFilter) Process(ctx context.Context, e *eventlogger.Event return e, nil } + +func hclogBytes(eventType eventlogger.EventType, jsonFormat bool, args []interface{}) (*bytes.Buffer, error) { + var buf bytes.Buffer + logger := hclog.New(&hclog.LoggerOptions{ + Output: &buf, + Level: hclog.Trace, + JSONFormat: jsonFormat, + }) + const eventMarker = " event" + switch string(eventType) { + case string(ErrorType): + logger.Error(string(eventType)+eventMarker, args...) + case string(ObservationType), string(SystemType), string(AuditType): + logger.Info(string(eventType)+eventMarker, args...) + default: + // well, we should ever hit this, since we should be specific about the + // event type we're processing, but adding this default to just be sure + // we haven't missed anything. + logger.Trace(string(eventType)+eventMarker, args...) + } + return &buf, nil +} diff --git a/internal/observability/event/hclog_formatter_node_test.go b/internal/observability/event/hclog_formatter_node_test.go index 7834e0fd6..cf7ca1a4d 100644 --- a/internal/observability/event/hclog_formatter_node_test.go +++ b/internal/observability/event/hclog_formatter_node_test.go @@ -2,9 +2,11 @@ package event import ( "context" + "encoding/json" "testing" "github.com/hashicorp/eventlogger" + wrapping "github.com/hashicorp/go-kms-wrapping" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -246,6 +248,32 @@ func TestHclogFormatter_Process(t *testing.T) { } }) } + t.Run("with-signing", func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + wrapper := testWrapper(t) + f, err := newHclogFormatterFilter(true) // produce json formatted events + require.NoError(err) + require.NoError(f.Rotate(wrapper)) + require.NotNil(f.signer) + + e := &eventlogger.Event{ + Type: eventlogger.EventType(AuditType), + Payload: &audit{ + Id: "1", + Version: auditVersion, + Auth: &Auth{UserName: "alice"}, + }, + } + + gotEvent, err := f.Process(ctx, e) + require.NoError(err) + b, ok := gotEvent.Format(string(JSONHclogSinkFormat)) + require.True(ok) + var rep map[string]interface{} + require.NoError(json.Unmarshal(b, &rep)) + assert.NotEmpty(rep["serialized"]) + assert.NotEmpty(rep["serialized_hmac"]) + }) } func Test_hclogFormatterFilter_Name(t *testing.T) { @@ -366,3 +394,41 @@ func Test_newHclogFormatterFilter(t *testing.T) { }) } } + +func Test_hclogFormatterFilter_Rotate(t *testing.T) { + tests := []struct { + name string + f *hclogFormatterFilter + w wrapping.Wrapper + wantIsError error + wantErrContains string + }{ + { + name: "missing-wrapper", + f: &hclogFormatterFilter{}, + wantIsError: ErrInvalidParameter, + wantErrContains: "missing wrapper", + }, + { + name: "valid", + f: &hclogFormatterFilter{}, + w: testWrapper(t), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + err := tt.f.Rotate(tt.w) + if tt.wantIsError != nil { + require.Error(err) + assert.ErrorIs(err, tt.wantIsError) + if tt.wantErrContains != "" { + assert.Contains(err.Error(), tt.wantErrContains) + } + return + } + assert.NotNil(tt.f.signer) + }) + } +} diff --git a/internal/observability/event/signer.go b/internal/observability/event/signer.go new file mode 100644 index 000000000..05c9359c4 --- /dev/null +++ b/internal/observability/event/signer.go @@ -0,0 +1,17 @@ +package event + +import ( + "context" + + "github.com/hashicorp/boundary/internal/libs/crypto" + wrapping "github.com/hashicorp/go-kms-wrapping" +) + +type signer func(context.Context, []byte) (string, error) + +func newSigner(_ context.Context, w wrapping.Wrapper, info, salt []byte) (signer, error) { + const op = "event.hmacHclogSigner" + return func(requestCtx context.Context, data []byte) (string, error) { + return crypto.HmacSha256(requestCtx, data, w, info, salt, crypto.WithPrefix("hmac-sha256:"), crypto.WithBase64Encoding()) + }, nil +} diff --git a/internal/servers/controller/controller.go b/internal/servers/controller/controller.go index 064cd4264..e1071ac93 100644 --- a/internal/servers/controller/controller.go +++ b/internal/servers/controller/controller.go @@ -24,6 +24,7 @@ import ( "github.com/hashicorp/boundary/internal/servers/controller/common" "github.com/hashicorp/boundary/internal/session" "github.com/hashicorp/boundary/internal/target" + "github.com/hashicorp/boundary/internal/types/scope" "github.com/hashicorp/go-hclog" "github.com/hashicorp/go-secure-stdlib/mlock" ua "go.uber.org/atomic" @@ -127,6 +128,16 @@ func New(ctx context.Context, conf *Config) (*Controller, error) { ); err != nil { return nil, fmt.Errorf("error adding config keys to kms: %w", err) } + // now that the kms is configured, we can get the audit wrapper and rotate + // the eventer audit wrapper, so the emitted events can include encrypt and + // hmac-sha256 data + auditWrapper, err := c.kms.GetWrapper(ctx, scope.Global.String(), kms.KeyPurposeAudit) + if err != nil { + return nil, fmt.Errorf("error getting audit wrapper from kms: %w", err) + } + if c.conf.Eventer.RotateAuditWrapper(ctx, auditWrapper); err != nil { + return nil, fmt.Errorf("error rotating eventer audit wrapper: %w", err) + } jobRepoFn := func() (*job.Repository, error) { return job.NewRepository(dbase, dbase, c.kms) } diff --git a/sdk/go.mod b/sdk/go.mod index 90c808787..10ffb9b44 100644 --- a/sdk/go.mod +++ b/sdk/go.mod @@ -6,7 +6,7 @@ require ( github.com/fatih/color v1.12.0 // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/google/go-cmp v0.5.6 // indirect - github.com/hashicorp/eventlogger v0.1.0 + github.com/hashicorp/eventlogger v0.1.1-0.20211104100552-e1e801e50144 github.com/hashicorp/eventlogger/filters/encrypt v0.1.6-0.20211027211326-5db60a48f239 github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-kms-wrapping v0.6.6 diff --git a/sdk/go.sum b/sdk/go.sum index 146017010..1961c5a95 100644 --- a/sdk/go.sum +++ b/sdk/go.sum @@ -235,8 +235,9 @@ github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB7 github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/eventlogger v0.1.0 h1:S6xc4gZVzewuDUP4R4Ngko419h/CGDuV/b4ADL3XLik= github.com/hashicorp/eventlogger v0.1.0/go.mod h1:a3IXf1aEJfpCPzseTOrwKj4fVW/Qn3oEmpQeaIznzH0= +github.com/hashicorp/eventlogger v0.1.1-0.20211104100552-e1e801e50144 h1:PCl0HtlVnIIloIozAKvjICu6K4IghKXAKNny3R3b2nI= +github.com/hashicorp/eventlogger v0.1.1-0.20211104100552-e1e801e50144/go.mod h1:a3IXf1aEJfpCPzseTOrwKj4fVW/Qn3oEmpQeaIznzH0= github.com/hashicorp/eventlogger/filters/encrypt v0.1.6-0.20211027211326-5db60a48f239 h1:Yh9tY0lige+y0trmjQeT9NRDo6+YvtNAzbmUNOsIUzI= github.com/hashicorp/eventlogger/filters/encrypt v0.1.6-0.20211027211326-5db60a48f239/go.mod h1:8rcez7Kw1zanB0/074qnOuGu7zxmNh9Xr2ZI+K4xVIA= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=