Skip to content

Commit

Permalink
stable_unix_user.create event
Browse files Browse the repository at this point in the history
  • Loading branch information
espadolini committed Jan 17, 2025
1 parent 9341c96 commit e356eb2
Show file tree
Hide file tree
Showing 13 changed files with 1,790 additions and 1,127 deletions.
26 changes: 26 additions & 0 deletions api/proto/teleport/legacy/types/events/events.proto
Original file line number Diff line number Diff line change
Expand Up @@ -4749,6 +4749,7 @@ message OneOf {
events.GitCommand GitCommand = 197;
events.UserLoginAccessListInvalid UserLoginAccessListInvalid = 198;
events.AccessRequestExpire AccessRequestExpire = 199;
events.StableUNIXUserCreate StableUNIXUserCreate = 200;
}
}

Expand Down Expand Up @@ -7950,3 +7951,28 @@ message UserLoginAccessListInvalid {
(gogoproto.jsontag) = ""
];
}

// StableUNIXUserCreate is emitted whenever a new stable UNIX user is written in
// the cluster state storage.
message StableUNIXUserCreate {
// Metadata is common event metadata
Metadata Metadata = 1 [
(gogoproto.nullable) = false,
(gogoproto.embed) = true,
(gogoproto.jsontag) = ""
];

// User is a common user event metadata
UserMetadata User = 2 [
(gogoproto.nullable) = false,
(gogoproto.embed) = true,
(gogoproto.jsontag) = ""
];

StableUNIXUser stable_unix_user = 3;
}

message StableUNIXUser {
string username = 1;
int32 uid = 2;
}
5 changes: 5 additions & 0 deletions api/types/events/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -2434,3 +2434,8 @@ func (m *WorkloadIdentityDelete) TrimToMaxSize(_ int) AuditEvent {
func (m *GitCommand) TrimToMaxSize(_ int) AuditEvent {
return m
}

// TrimToMaxSize implements [AuditEvent].
func (m *StableUNIXUserCreate) TrimToMaxSize(int) AuditEvent {
return m
}
2,816 changes: 1,693 additions & 1,123 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 @@ -823,6 +823,10 @@ func ToOneOf(in AuditEvent) (*OneOf, error) {
out.Event = &OneOf_GitCommand{
GitCommand: e,
}
case *StableUNIXUserCreate:
out.Event = &OneOf_StableUNIXUserCreate{
StableUNIXUserCreate: e,
}
default:
slog.ErrorContext(context.Background(), "Attempted to convert dynamic event of unknown type into protobuf event.", "event_type", in.GetType())
unknown := &Unknown{}
Expand Down
2 changes: 2 additions & 0 deletions lib/auth/grpcserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -5284,6 +5284,8 @@ func NewGRPCServer(cfg GRPCServerConfig) (*GRPCServer, error) {

stableUNIXUsersServiceServer, err := stableunixusers.New(stableunixusers.Params{
Authorizer: cfg.Authorizer,
Emitter: cfg.Emitter,
Logger: cfg.AuthServer.logger.With(teleport.ComponentKey, "stable_unix_users"),

Backend: cfg.AuthServer.bk,
ReadOnlyCache: cfg.AuthServer.ReadOnlyCache,
Expand Down
36 changes: 32 additions & 4 deletions lib/auth/stableunixusers/stableunixusers.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,18 @@ package stableunixusers

import (
"context"
"log/slog"
"time"

"github.com/gravitational/trace"
"github.com/jonboulle/clockwork"

stableunixusersv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/stableunixusers/v1"
"github.com/gravitational/teleport/api/types"
apievents "github.com/gravitational/teleport/api/types/events"
"github.com/gravitational/teleport/lib/authz"
"github.com/gravitational/teleport/lib/backend"
"github.com/gravitational/teleport/lib/events"
"github.com/gravitational/teleport/lib/services"
"github.com/gravitational/teleport/lib/services/readonly"
"github.com/gravitational/teleport/lib/utils"
Expand All @@ -37,6 +40,8 @@ const uidCacheTTL = 30 * time.Second
// Params contains the parameters for [New].
type Params struct {
Authorizer authz.Authorizer
Emitter apievents.Emitter
Logger *slog.Logger

Backend backend.Backend
ReadOnlyCache *readonly.Cache
Expand Down Expand Up @@ -66,6 +71,8 @@ func New(params Params) (stableunixusersv1.StableUNIXUsersServiceServer, error)

return &server{
authorizer: params.Authorizer,
emitter: params.Emitter,
logger: params.Logger,

backend: params.Backend,
readOnlyCache: params.ReadOnlyCache,
Expand All @@ -85,6 +92,8 @@ type server struct {
stableunixusersv1.UnsafeStableUNIXUsersServiceServer

authorizer authz.Authorizer
emitter apievents.Emitter
logger *slog.Logger

backend backend.Backend
readOnlyCache *readonly.Cache
Expand Down Expand Up @@ -160,9 +169,17 @@ func (s *server) ObtainUIDForUsername(ctx context.Context, req *stableunixusersv
// obtainUIDForUsernameCached calls [*server.obtainUIDForUsernameUncached]
// through the UID FnCache.
func (s *server) obtainUIDForUsernameCached(ctx context.Context, username string) (int32, error) {
idGetter, err := authz.UserFromContext(ctx)
if err != nil {
idGetter = nil
}

uid, err := utils.FnCacheGet(ctx, s.uidCache, username, func(ctx context.Context) (int32, error) {
ctx, cancel := context.WithTimeout(ctx, uidCacheTTL)
defer cancel()
if idGetter != nil {
ctx = authz.ContextWithUser(ctx, idGetter)
}
return s.obtainUIDForUsernameUncached(ctx, username)
})
if err != nil {
Expand Down Expand Up @@ -191,8 +208,6 @@ func (s *server) obtainUIDForUsernameUncached(ctx context.Context, username stri

uid, err := s.stableUNIXUsers.GetUIDForUsername(ctx, username)
if err == nil {
// TODO(espadolini): _potentially_ emit an audit log event with
// username and UID (it might spam the audit log unnecessarily)
return uid, nil
}
if !trace.IsNotFound(err) {
Expand Down Expand Up @@ -232,8 +247,21 @@ func (s *server) obtainUIDForUsernameUncached(ctx context.Context, username stri
return 0, trace.Wrap(err)
}

// TODO(espadolini): emit an audit log event with the username and UID
// that was just created
if s.emitter != nil {
if err := s.emitter.EmitAuditEvent(ctx, &apievents.StableUNIXUserCreate{
Metadata: apievents.Metadata{
Type: events.StableUNIXUserCreateEvent,
Code: events.StableUNIXUserCreateCode,
},
UserMetadata: authz.ClientUserMetadata(ctx),
StableUnixUser: &apievents.StableUNIXUser{
Username: username,
Uid: uid,
},
}); err != nil {
s.logger.WarnContext(ctx, "Failed to emit stable_unix_user.create", "error", err)
}
}

return uid, nil
}
Expand Down
3 changes: 3 additions & 0 deletions lib/events/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -861,6 +861,9 @@ const (

// GitCommandEvent is emitted when a Git command is executed.
GitCommandEvent = "git.command"

// StableUNIXUserCreateEvent is emitted when a stable UNIX user is created.
StableUNIXUserCreateEvent = "stable_unix_user.create"
)

// Add an entry to eventsMap in lib/events/events_test.go when you add
Expand Down
3 changes: 3 additions & 0 deletions lib/events/codes.go
Original file line number Diff line number Diff line change
Expand Up @@ -693,6 +693,9 @@ const (
// GitCommandFailureCode is the git command feature event code.
GitCommandFailureCode = "TGIT001E"

// StableUNIXUserCreateCode is the stable UNIX user create event code.
StableUNIXUserCreateCode = "TSUU001I"

// UnknownCode is used when an event of unknown type is encountered.
UnknownCode = apievents.UnknownCode
)
Expand Down
3 changes: 3 additions & 0 deletions lib/events/dynamic.go
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,9 @@ func FromEventFields(fields EventFields) (events.AuditEvent, error) {
case WorkloadIdentityDeleteEvent:
e = &events.WorkloadIdentityDelete{}

case StableUNIXUserCreateEvent:
e = &events.StableUNIXUserCreate{}

default:
slog.ErrorContext(context.Background(), "Attempted to convert dynamic event of unknown type into protobuf event.", "event_type", eventType)
unknown := &events.Unknown{}
Expand Down
1 change: 1 addition & 0 deletions lib/events/events_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@ var eventsMap = map[string]apievents.AuditEvent{
WorkloadIdentityUpdateEvent: &apievents.WorkloadIdentityUpdate{},
WorkloadIdentityDeleteEvent: &apievents.WorkloadIdentityDelete{},
AccessRequestExpireEvent: &apievents.AccessRequestExpire{},
StableUNIXUserCreateEvent: &apievents.StableUNIXUserCreate{},
}

// TestJSON tests JSON marshal events
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,7 @@ const EventIconMap: Record<EventCode, any> = {
[eventCodes.UNKNOWN]: Icons.Question,
[eventCodes.GIT_COMMAND]: Icons.GitHub,
[eventCodes.GIT_COMMAND_FAILURE]: Icons.GitHub,
[eventCodes.STABLE_UNIX_USER_CREATE]: Icons.Info,
};

export default function renderTypeCell(event: Event) {
Expand Down
7 changes: 7 additions & 0 deletions web/packages/teleport/src/services/audit/makeEvent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2022,6 +2022,13 @@ export const formatters: Formatters = {
return `User [${user}] Git Command [${service}] at [${path}] failed [${exitError}]`;
},
},
[eventCodes.STABLE_UNIX_USER_CREATE]: {
type: 'stable_unix_user.create',
desc: 'Stable UNIX user created',
format: ({ stable_unix_user: { username } }) => {
return `Stable UNIX user for username [${username}] was created`;
},
},
};

const unknownFormatter = {
Expand Down
10 changes: 10 additions & 0 deletions web/packages/teleport/src/services/audit/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,7 @@ export const eventCodes = {
CONTACT_DELETE: 'TCTC002I',
GIT_COMMAND: 'TGIT001I',
GIT_COMMAND_FAILURE: 'TGIT001E',
STABLE_UNIX_USER_CREATE: 'TSUU001I',
} as const;

/**
Expand Down Expand Up @@ -1785,6 +1786,15 @@ export type RawEvents = {
exitError: string;
}
>;
[eventCodes.STABLE_UNIX_USER_CREATE]: RawEvent<
typeof eventCodes.STABLE_UNIX_USER_CREATE,
{
stable_unix_user: {
username: string;
uid: number;
}
}
>;
};

/**
Expand Down

0 comments on commit e356eb2

Please sign in to comment.