Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions api/proto/teleport/legacy/types/events/events.proto
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ message SessionMetadata {
string SessionID = 1 [(gogoproto.jsontag) = "sid"];
// WithMFA is a UUID of an MFA device used to start this session.
string WithMFA = 2 [(gogoproto.jsontag) = "with_mfa,omitempty"];
// PrivateKeyPolicy is the private key policy of the private key used to start this session.
string PrivateKeyPolicy = 3 [(gogoproto.jsontag) = "private_key_policy,omitempty"];
}

// UserMetadata is a common user event metadata
Expand Down Expand Up @@ -87,6 +89,9 @@ message UserMetadata {
// TrustedDevice contains information about the users' trusted device.
// Requires a registered and enrolled device to be used during authentication.
DeviceMetadata TrustedDevice = 8 [(gogoproto.jsontag) = "trusted_device,omitempty"];

// RequiredPrivateKeyPolicy is the private key policy enforced for this login.
string RequiredPrivateKeyPolicy = 9 [(gogoproto.jsontag) = "required_private_key_policy,omitempty"];
}

// Server is a server metadata
Expand Down Expand Up @@ -3624,6 +3629,8 @@ message Identity {
repeated string AzureIdentities = 24 [(gogoproto.jsontag) = "azure_identities,omitempty"];
// GCPServiceAccounts is a list of allowed GCP service accounts user can assume.
repeated string GCPServiceAccounts = 25 [(gogoproto.jsontag) = "gcp_service_accounts,omitempty"];
// PrivateKeyPolicy is the private key policy of the user's private key.
string PrivateKeyPolicy = 26 [(gogoproto.jsontag) = "private_key_policy,omitempty"];
}

// RouteToApp contains parameters for application access certificate requests.
Expand Down
1,701 changes: 922 additions & 779 deletions api/types/events/events.pb.go

Large diffs are not rendered by default.

146 changes: 94 additions & 52 deletions lib/auth/methods.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,11 +113,22 @@ type SessionCreds struct {

// AuthenticateUser authenticates user based on the request type.
// Returns the username of the authenticated user.
func (s *Server) AuthenticateUser(ctx context.Context, req AuthenticateUserRequest) (services.UserState, error) {
func (s *Server) AuthenticateUser(ctx context.Context, req AuthenticateUserRequest) (services.UserState, services.AccessChecker, error) {
username := req.Username

mfaDev, actualUsername, err := s.authenticateUser(ctx, req)
// err is handled below.
if err != nil {
// Log event after authentication failure
if err := s.emitAuthAuditEvent(ctx, authAuditProps{
username: req.Username,
clientMetadata: req.ClientMetadata,
authErr: err,
}); err != nil {
log.WithError(err).Warn("Failed to emit login event.")
}
return nil, nil, trace.Wrap(err)
}

switch {
case username != "" && actualUsername != "" && username != actualUsername:
log.Warnf("Authenticate user mismatch (%q vs %q). Using request user (%q)", username, actualUsername, username)
Expand All @@ -126,61 +137,103 @@ func (s *Server) AuthenticateUser(ctx context.Context, req AuthenticateUserReque
username = actualUsername
}

user, err := s.GetUser(username, false /* withSecrets */)
if err != nil {
return nil, nil, trace.Wrap(err)
}

// After we're sure that the user has been logged in successfully, we should call
// the registered login hooks. Login hooks can be registered by other processes to
// execute arbitrary operations after a successful login.
if err := s.CallLoginHooks(ctx, user); err != nil {
return nil, nil, trace.Wrap(err)
}

userState, err := s.GetUserOrLoginState(ctx, user.GetName())
if err != nil {
return nil, nil, trace.Wrap(err)
}

clusterName, err := s.GetClusterName()
if err != nil {
return nil, nil, trace.Wrap(err)
}
accessInfo := services.AccessInfoFromUserState(userState)
checker, err := services.NewAccessChecker(accessInfo, clusterName.GetClusterName(), s)
if err != nil {
return nil, nil, trace.Wrap(err)
}

// Log event after authentication success
if err := s.emitAuthAuditEvent(ctx, authAuditProps{
username: username,
clientMetadata: req.ClientMetadata,
mfaDevice: mfaDev,
checker: checker,
}); err != nil {
log.WithError(err).Warn("Failed to emit login event.")
}

return userState, checker, trace.Wrap(err)
}

type authAuditProps struct {
username string
clientMetadata *ForwardedClientMetadata
mfaDevice *types.MFADevice
checker services.AccessChecker
authErr error
}

func (s *Server) emitAuthAuditEvent(ctx context.Context, props authAuditProps) error {
event := &apievents.UserLogin{
Metadata: apievents.Metadata{
Type: events.UserLoginEvent,
Code: events.UserLocalLoginFailureCode,
Code: events.UserLocalLoginCode,
},
Status: apievents.Status{
Success: true,
},
UserMetadata: apievents.UserMetadata{
User: username,
User: props.username,
},
Method: events.LoginMethodLocal,
}
if mfaDev != nil {
m := mfaDeviceEventMetadata(mfaDev)
event.MFADevice = &m

if props.authErr != nil {
event.Code = events.UserLocalLoginFailureCode
event.Status.Success = false
event.Status.Error = props.authErr.Error()
}
if req.ClientMetadata != nil {
event.RemoteAddr = req.ClientMetadata.RemoteAddr
if len(req.ClientMetadata.UserAgent) > maxUserAgentLen {
event.UserAgent = req.ClientMetadata.UserAgent[:maxUserAgentLen-3] + "..."

if props.clientMetadata != nil {
event.RemoteAddr = props.clientMetadata.RemoteAddr
if len(props.clientMetadata.UserAgent) > maxUserAgentLen {
event.UserAgent = props.clientMetadata.UserAgent[:maxUserAgentLen-3] + "..."
} else {
event.UserAgent = req.ClientMetadata.UserAgent
event.UserAgent = props.clientMetadata.UserAgent
}
}

var userState services.UserState
if err != nil {
event.Code = events.UserLocalLoginFailureCode
event.Status.Success = false
event.Status.Error = err.Error()
} else {
event.Code = events.UserLocalLoginCode
event.Status.Success = true
if props.mfaDevice != nil {
m := mfaDeviceEventMetadata(props.mfaDevice)
event.MFADevice = &m
}

var err error
user, err := s.GetUser(username, false /* withSecrets */)
// Add required key policy to the event.
if props.checker != nil {
authPref, err := s.GetAuthPreference(ctx)
if err != nil {
return nil, trace.Wrap(err)
}

// After we're sure that the user has been logged in successfully, we should call
// the registered login hooks. Login hooks can be registered by other processes to
// execute arbitrary operations after a successful login.
if err := s.CallLoginHooks(ctx, user); err != nil {
return nil, trace.Wrap(err)
return trace.Wrap(err)
}

userState, err = s.GetUserOrLoginState(ctx, username)
privateKeyPolicy, err := props.checker.PrivateKeyPolicy(authPref.GetPrivateKeyPolicy())
if err != nil {
return nil, trace.Wrap(err)
return trace.Wrap(err)
}
}
if err := s.emitter.EmitAuditEvent(s.closeCtx, event); err != nil {
log.WithError(err).Warn("Failed to emit login event.")
event.RequiredPrivateKeyPolicy = string(privateKeyPolicy)
}

return userState, trace.Wrap(err)
return trace.Wrap(s.emitter.EmitAuditEvent(s.closeCtx, event))
}

var (
Expand Down Expand Up @@ -487,7 +540,7 @@ func (s *Server) AuthenticateWebUser(ctx context.Context, req AuthenticateUserRe
return session, nil
}

user, err := s.AuthenticateUser(ctx, req)
user, _, err := s.AuthenticateUser(ctx, req)
if err != nil {
return nil, trace.Wrap(err)
}
Expand Down Expand Up @@ -622,18 +675,7 @@ func (s *Server) AuthenticateSSHUser(ctx context.Context, req AuthenticateSSHReq

// It's safe to extract the roles and traits directly from services.User as
// this endpoint is only used for local accounts.
user, err := s.AuthenticateUser(ctx, req.AuthenticateUserRequest)
if err != nil {
return nil, trace.Wrap(err)
}

userState, err := s.GetUserOrLoginState(ctx, user.GetName())
if err != nil {
return nil, trace.Wrap(err)
}

accessInfo := services.AccessInfoFromUserState(userState)
checker, err := services.NewAccessChecker(accessInfo, clusterName.GetClusterName(), s)
user, checker, err := s.AuthenticateUser(ctx, req.AuthenticateUserRequest)
if err != nil {
return nil, trace.Wrap(err)
}
Expand Down Expand Up @@ -663,12 +705,12 @@ func (s *Server) AuthenticateSSHUser(ctx context.Context, req AuthenticateSSHReq
}

certReq := certRequest{
user: userState,
user: user,
ttl: req.TTL,
publicKey: req.PublicKey,
compatibility: req.CompatibilityMode,
checker: checker,
traits: userState.GetTraits(),
traits: user.GetTraits(),
routeToCluster: req.RouteToCluster,
kubernetesCluster: req.KubernetesCluster,
loginIP: clientIP,
Expand Down
5 changes: 1 addition & 4 deletions lib/kube/proxy/forwarder.go
Original file line number Diff line number Diff line change
Expand Up @@ -1465,10 +1465,7 @@ func (f *Forwarder) execNonInteractive(ctx *authContext, w http.ResponseWriter,
ServerAddr: sess.kubeAddress,
}

sessionMetadata := apievents.SessionMetadata{
SessionID: uuid.NewString(),
WithMFA: ctx.Identity.GetIdentity().MFAVerified,
}
sessionMetadata := ctx.Identity.GetIdentity().GetSessionMetadata(uuid.NewString())

connectionMetdata := apievents.ConnectionMetadata{
RemoteAddr: req.RemoteAddr,
Expand Down
34 changes: 12 additions & 22 deletions lib/kube/proxy/sess.go
Original file line number Diff line number Diff line change
Expand Up @@ -547,11 +547,8 @@ func (s *session) launch() error {
ServerHostname: s.sess.teleportCluster.name,
ServerAddr: s.sess.kubeAddress,
},
SessionMetadata: apievents.SessionMetadata{
SessionID: s.id.String(),
WithMFA: s.ctx.Identity.GetIdentity().MFAVerified,
},
UserMetadata: s.ctx.eventUserMeta(),
SessionMetadata: s.getSessionMetadata(),
UserMetadata: s.ctx.eventUserMeta(),
ConnectionMetadata: apievents.ConnectionMetadata{
RemoteAddr: s.req.RemoteAddr,
LocalAddr: s.sess.kubeAddress,
Expand Down Expand Up @@ -657,10 +654,7 @@ func (s *session) lockedSetupLaunch(request *remoteCommandRequest, q url.Values,
ServerMetadata: apievents.ServerMetadata{
ServerNamespace: s.forwarder.cfg.Namespace,
},
SessionMetadata: apievents.SessionMetadata{
SessionID: s.id.String(),
WithMFA: s.ctx.Identity.GetIdentity().MFAVerified,
},
SessionMetadata: s.getSessionMetadata(),
UserMetadata: s.ctx.eventUserMeta(),
TerminalSize: params.Serialize(),
KubernetesClusterMetadata: s.ctx.eventClusterMeta(s.req),
Expand Down Expand Up @@ -751,10 +745,7 @@ func (s *session) lockedSetupLaunch(request *remoteCommandRequest, q url.Values,
ServerNamespace: s.forwarder.cfg.Namespace,
}

sessionMetadata := apievents.SessionMetadata{
SessionID: s.id.String(),
WithMFA: s.ctx.Identity.GetIdentity().MFAVerified,
}
sessionMetadata := s.getSessionMetadata()

conMetadata := apievents.ConnectionMetadata{
RemoteAddr: s.req.RemoteAddr,
Expand Down Expand Up @@ -783,7 +774,6 @@ func (s *session) lockedSetupLaunch(request *remoteCommandRequest, q url.Values,
if errExec != nil {
execEvent.Code = events.ExecFailureCode
execEvent.Error, execEvent.ExitCode = exitCode(err)

}

if err := s.emitter.EmitAuditEvent(s.forwarder.ctx, execEvent); err != nil {
Expand Down Expand Up @@ -885,10 +875,8 @@ func (s *session) join(p *party) error {
KubernetesGroups: []string{},
KubernetesLabels: s.ctx.kubeClusterLabels,
},
SessionMetadata: apievents.SessionMetadata{
SessionID: s.id.String(),
},
UserMetadata: p.Ctx.eventUserMetaWithLogin("root"),
SessionMetadata: s.getSessionMetadata(),
UserMetadata: p.Ctx.eventUserMetaWithLogin("root"),
ConnectionMetadata: apievents.ConnectionMetadata{
RemoteAddr: s.params.ByName("podName"),
},
Expand Down Expand Up @@ -1021,10 +1009,8 @@ func (s *session) unlockedLeave(id uuid.UUID) (bool, error) {
Code: events.SessionJoinCode,
ClusterName: s.ctx.teleportCluster.name,
},
SessionMetadata: apievents.SessionMetadata{
SessionID: s.id.String(),
},
UserMetadata: party.Ctx.eventUserMetaWithLogin("root"),
SessionMetadata: s.getSessionMetadata(),
UserMetadata: party.Ctx.eventUserMetaWithLogin("root"),
ConnectionMetadata: apievents.ConnectionMetadata{
RemoteAddr: s.params.ByName("podName"),
},
Expand Down Expand Up @@ -1247,3 +1233,7 @@ func (s *session) trackSession(p *party, policySet []*types.SessionTrackerPolicy

return nil
}

func (s *session) getSessionMetadata() apievents.SessionMetadata {
return s.ctx.Identity.GetIdentity().GetSessionMetadata(s.id.String())
}
29 changes: 14 additions & 15 deletions lib/srv/app/common/audit.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,14 @@ func NewAudit(config AuditConfig) (Audit, error) {
}, nil
}

func getSessionMetadata(identity *tlsca.Identity) apievents.SessionMetadata {
return apievents.SessionMetadata{
SessionID: identity.RouteToApp.SessionID,
WithMFA: identity.MFAVerified,
PrivateKeyPolicy: string(identity.PrivateKeyPolicy),
}
}

// OnSessionStart is called when new app session starts.
func (a *audit) OnSessionStart(ctx context.Context, serverID string, identity *tlsca.Identity, app types.Application) error {
event := &apievents.AppSessionStart{
Expand All @@ -98,11 +106,8 @@ func (a *audit) OnSessionStart(ctx context.Context, serverID string, identity *t
ServerID: serverID,
ServerNamespace: apidefaults.Namespace,
},
SessionMetadata: apievents.SessionMetadata{
SessionID: identity.RouteToApp.SessionID,
WithMFA: identity.MFAVerified,
},
UserMetadata: identity.GetUserMetadata(),
SessionMetadata: getSessionMetadata(identity),
UserMetadata: identity.GetUserMetadata(),
ConnectionMetadata: apievents.ConnectionMetadata{
RemoteAddr: identity.LoginIP,
},
Expand All @@ -127,11 +132,8 @@ func (a *audit) OnSessionEnd(ctx context.Context, serverID string, identity *tls
ServerID: serverID,
ServerNamespace: apidefaults.Namespace,
},
SessionMetadata: apievents.SessionMetadata{
SessionID: identity.RouteToApp.SessionID,
WithMFA: identity.MFAVerified,
},
UserMetadata: identity.GetUserMetadata(),
SessionMetadata: getSessionMetadata(identity),
UserMetadata: identity.GetUserMetadata(),
ConnectionMetadata: apievents.ConnectionMetadata{
RemoteAddr: identity.LoginIP,
},
Expand All @@ -156,11 +158,8 @@ func (a *audit) OnSessionChunk(ctx context.Context, serverID, chunkID string, id
ServerID: serverID,
ServerNamespace: apidefaults.Namespace,
},
SessionMetadata: apievents.SessionMetadata{
SessionID: identity.RouteToApp.SessionID,
WithMFA: identity.MFAVerified,
},
UserMetadata: identity.GetUserMetadata(),
SessionMetadata: getSessionMetadata(identity),
UserMetadata: identity.GetUserMetadata(),
AppMetadata: apievents.AppMetadata{
AppURI: app.GetURI(),
AppPublicAddr: app.GetPublicAddr(),
Expand Down
Loading