Skip to content

Commit

Permalink
Add an audit event for creating provisioning tokens (#28769)
Browse files Browse the repository at this point in the history
* Add a provision token creation event on the backend

* Support provision token creation audit event on frontend

* Modify the deprecated GenerateToken function

Also:
* Add some missing tests.
* Add error logging in emitTokenEvent().
* Fix lint errors.

* Fix import order

* Address review comments

* Fix final review comments, add docs

* Fix the broken test

* Revert "Fix the broken test"

This reverts commit e1b8d2e.

* Properly fix emitting duplicated events
  • Loading branch information
bl-nero authored Jul 11, 2023
1 parent 072519b commit 800e79b
Show file tree
Hide file tree
Showing 15 changed files with 1,701 additions and 925 deletions.
3 changes: 3 additions & 0 deletions api/proto/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Teleport API Protocol Buffers

This folder contains Teleport API data structures and RPC declarations. Each time you modify any of the `.proto` files, you need to run `make grpc` to rebuild the autogenerated source files.
39 changes: 37 additions & 2 deletions api/proto/teleport/legacy/types/events/events.proto
Original file line number Diff line number Diff line change
Expand Up @@ -1864,8 +1864,42 @@ message TrustedClusterDelete {
];
}

// TrustedClusterTokenCreate is the event for
// creating new join token for a trusted cluster.
// ProvisionTokenCreate event is emitted when a provisioning token (a.k.a. join
// token) of any role is created.
message ProvisionTokenCreate {
Metadata Metadata = 1 [
(gogoproto.nullable) = false,
(gogoproto.embed) = true,
(gogoproto.jsontag) = ""
];

ResourceMetadata Resource = 2 [
(gogoproto.nullable) = false,
(gogoproto.embed) = true,
(gogoproto.jsontag) = ""
];

UserMetadata User = 3 [
(gogoproto.nullable) = false,
(gogoproto.embed) = true,
(gogoproto.jsontag) = ""
];

repeated string Roles = 4 [
(gogoproto.jsontag) = "roles,omitempty",
(gogoproto.casttype) = "github.com/gravitational/teleport/api/types.SystemRole"
];

string JoinMethod = 5 [
(gogoproto.jsontag) = "join_method,omitempty",
(gogoproto.casttype) = "github.com/gravitational/teleport/api/types.JoinMethod"
];
}

// TrustedClusterTokenCreate event is emitted (in addition to
// ProvisionTokenCreate) when a token of a "Trusted_cluster" role is created.
//
// Deprecated: redundant, since we also emit ProvisionTokenCreate.
message TrustedClusterTokenCreate {
// Metadata is a common event metadata
Metadata Metadata = 1 [
Expand Down Expand Up @@ -3365,6 +3399,7 @@ message OneOf {
events.OktaResourcesUpdate OktaResourcesUpdate = 122;
events.OktaSyncFailure OktaSyncFailure = 123;
events.OktaAssignmentResult OktaAssignmentResult = 124;
events.ProvisionTokenCreate ProvisionTokenCreate = 125;
}
}

Expand Down
2,145 changes: 1,299 additions & 846 deletions api/types/events/events.pb.go

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions api/types/events/oneof.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,10 @@ func ToOneOf(in AuditEvent) (*OneOf, error) {
out.Event = &OneOf_TrustedClusterTokenCreate{
TrustedClusterTokenCreate: e,
}
case *ProvisionTokenCreate:
out.Event = &OneOf_ProvisionTokenCreate{
ProvisionTokenCreate: e,
}
case *GithubConnectorCreate:
out.Event = &OneOf_GithubConnectorCreate{
GithubConnectorCreate: e,
Expand Down
1 change: 1 addition & 0 deletions docs/pages/reference/audit.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ read [Exporting Teleport Audit Events](../management/export-audit-events.mdx).
| user.login | A user logged into web UI or via tsh. The following fields will be logged: `{"user": "[email protected]", "method": "local"}` . |
| app.session.start | A user accessed an application |
| app.session.chunk | A record of activity during an app session |
| join_token.created | A new join token has been created. Adds the following fields: `{"roles": ["Node", "Db"], "join_method": "token"}` |

## Recorded sessions

Expand Down
15 changes: 0 additions & 15 deletions lib/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -3241,21 +3241,6 @@ func (a *Server) GenerateToken(ctx context.Context, req *proto.GenerateTokenRequ
return "", trace.Wrap(err)
}

userMetadata := authz.ClientUserMetadata(ctx)
for _, role := range req.Roles {
if role == types.RoleTrustedCluster {
if err := a.emitter.EmitAuditEvent(ctx, &apievents.TrustedClusterTokenCreate{
Metadata: apievents.Metadata{
Type: events.TrustedClusterTokenCreateEvent,
Code: events.TrustedClusterTokenCreateCode,
},
UserMetadata: userMetadata,
}); err != nil {
log.WithError(err).Warn("Failed to emit trusted cluster token create event.")
}
}
}

return req.Token, nil
}

Expand Down
21 changes: 0 additions & 21 deletions lib/auth/auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -760,27 +760,6 @@ func TestLocalControlStream(t *testing.T) {
}
}

func TestGenerateTokenEventsEmitted(t *testing.T) {
t.Parallel()
s := newAuthSuite(t)

ctx := context.Background()
// test trusted cluster token emit
_, err := s.a.GenerateToken(ctx, &proto.GenerateTokenRequest{Roles: types.SystemRoles{types.RoleTrustedCluster}})
require.NoError(t, err)
require.Equal(t, s.mockEmitter.LastEvent().GetType(), events.TrustedClusterTokenCreateEvent)
s.mockEmitter.Reset()

// test emit with multiple roles
_, err = s.a.GenerateToken(ctx, &proto.GenerateTokenRequest{Roles: types.SystemRoles{
types.RoleNode,
types.RoleTrustedCluster,
types.RoleAuth,
}})
require.NoError(t, err)
require.Equal(t, s.mockEmitter.LastEvent().GetType(), events.TrustedClusterTokenCreateEvent)
}

func TestUpdateConfig(t *testing.T) {
t.Parallel()
s := newAuthSuite(t)
Expand Down
37 changes: 31 additions & 6 deletions lib/auth/auth_with_roles.go
Original file line number Diff line number Diff line change
Expand Up @@ -921,7 +921,14 @@ func (a *ServerWithRoles) GenerateToken(ctx context.Context, req *proto.Generate
if err := a.action(apidefaults.Namespace, types.KindToken, types.VerbCreate); err != nil {
return "", trace.Wrap(err)
}
return a.authServer.GenerateToken(ctx, req)

token, err := a.authServer.GenerateToken(ctx, req)
if err != nil {
return "", trace.Wrap(err)
}

emitTokenEvent(ctx, a.authServer.emitter, req.Roles, types.JoinMethodToken)
return token, nil
}

func (a *ServerWithRoles) RegisterUsingToken(ctx context.Context, req *types.RegisterUsingTokenRequest) (*proto.Certs, error) {
Expand Down Expand Up @@ -2053,11 +2060,28 @@ func enforceEnterpriseJoinMethodCreation(token types.ProvisionToken) error {
}

// emitTokenEvent is called by Create/Upsert Token in order to emit any relevant
// events. For now, this just emits trusted_cluster_token.create.
func emitTokenEvent(ctx context.Context, e apievents.Emitter, token types.ProvisionToken) {
// events.
func emitTokenEvent(
ctx context.Context,
e apievents.Emitter,
roles types.SystemRoles,
joinMethod types.JoinMethod,
) {
userMetadata := authz.ClientUserMetadata(ctx)
for _, role := range token.GetRoles() {
if err := e.EmitAuditEvent(ctx, &apievents.ProvisionTokenCreate{
Metadata: apievents.Metadata{
Type: events.ProvisionTokenCreateEvent,
Code: events.ProvisionTokenCreateCode,
},
UserMetadata: userMetadata,
Roles: roles,
JoinMethod: joinMethod,
}); err != nil {
log.WithError(err).Warn("Failed to emit join token create event.")
}
for _, role := range roles {
if role == types.RoleTrustedCluster {
//nolint:staticcheck // Emit a deprecated event.
if err := e.EmitAuditEvent(ctx, &apievents.TrustedClusterTokenCreate{
Metadata: apievents.Metadata{
Type: events.TrustedClusterTokenCreateEvent,
Expand All @@ -2081,11 +2105,12 @@ func (a *ServerWithRoles) UpsertToken(ctx context.Context, token types.Provision
if err := a.authServer.UpsertToken(ctx, token); err != nil {
return trace.Wrap(err)
}
emitTokenEvent(ctx, a.authServer.emitter, token)
emitTokenEvent(ctx, a.authServer.emitter, token.GetRoles(), token.GetJoinMethod())
return nil
}

func (a *ServerWithRoles) CreateToken(ctx context.Context, token types.ProvisionToken) error {
jm := token.GetJoinMethod()
if err := a.action(apidefaults.Namespace, types.KindToken, types.VerbCreate); err != nil {
return trace.Wrap(err)
}
Expand All @@ -2095,7 +2120,7 @@ func (a *ServerWithRoles) CreateToken(ctx context.Context, token types.Provision
if err := a.authServer.CreateToken(ctx, token); err != nil {
return trace.Wrap(err)
}
emitTokenEvent(ctx, a.authServer.emitter, token)
emitTokenEvent(ctx, a.authServer.emitter, token.GetRoles(), jm)
return nil
}

Expand Down
Loading

0 comments on commit 800e79b

Please sign in to comment.