diff --git a/lib/auth/auth.go b/lib/auth/auth.go index e79c69184793a..588580d371bdd 100644 --- a/lib/auth/auth.go +++ b/lib/auth/auth.go @@ -3291,6 +3291,15 @@ func (a *Server) CreateAuthenticateChallenge(ctx context.Context, req *proto.Cre if err := a.WithUserLock(ctx, username, func() error { return a.checkPasswordWOToken(ctx, username, req.GetUserCredentials().GetPassword()) }); err != nil { + // This is only ever used as a means to acquire a login challenge, so + // let's issue an authentication failure event. + if err := a.emitAuthAuditEvent(ctx, authAuditProps{ + username: username, + authErr: err, + }); err != nil { + log.WithError(err).Warn("Failed to emit login event") + // err swallowed on purpose. + } return nil, trace.Wrap(err) } diff --git a/lib/auth/auth_login_test.go b/lib/auth/auth_login_test.go index add5149bbb758..4f0e6ce3ed159 100644 --- a/lib/auth/auth_login_test.go +++ b/lib/auth/auth_login_test.go @@ -38,6 +38,8 @@ import ( "github.com/gravitational/teleport/lib/auth/mocku2f" wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" "github.com/gravitational/teleport/lib/defaults" + "github.com/gravitational/teleport/lib/events" + "github.com/gravitational/teleport/lib/events/eventstest" "github.com/gravitational/teleport/lib/modules" "github.com/gravitational/teleport/lib/services" ) @@ -608,6 +610,50 @@ func TestCreateAuthenticateChallenge_mfaVerification(t *testing.T) { } } +// TestCreateAuthenticateChallenge_failedLoginAudit tests a password+webauthn +// login scenario where the user types the wrong password. +// This should issue a "Local Login Failed" audit event. +func TestCreateAuthenticateChallenge_failedLoginAudit(t *testing.T) { + t.Parallel() + + testServer := newTestTLSServer(t) + emitter := &eventstest.MockRecorderEmitter{} + authServer := testServer.Auth() + authServer.SetEmitter(emitter) + + ctx := context.Background() + + // Set the cluster to require 2nd factor, create the user, set a password and + // register a webauthn device. + // password+OTP logins go through another route. + mfa := configureForMFA(t, testServer) + + // Proxy identity is used during login. + proxyClient, err := testServer.NewClient(TestBuiltin(types.RoleProxy)) + require.NoError(t, err, "NewClient(RoleProxy) failed") + + t.Run("emits audit event", func(t *testing.T) { + emitter.Reset() + _, err := proxyClient.CreateAuthenticateChallenge(ctx, &proto.CreateAuthenticateChallengeRequest{ + Request: &proto.CreateAuthenticateChallengeRequest_UserCredentials{ + UserCredentials: &proto.UserCredentials{ + Username: mfa.User, + Password: []byte(mfa.Password + "BAD"), + }, + }, + ChallengeExtensions: &mfav1.ChallengeExtensions{ + Scope: mfav1.ChallengeScope_CHALLENGE_SCOPE_LOGIN, + }, + }) + assert.ErrorContains(t, err, "password", "CreateAuthenticateChallenge error mismatch") + + event := emitter.LastEvent() + require.NotNil(t, event, "No audit event emitted") + assert.Equal(t, events.UserLoginEvent, event.GetType(), "event.Type mismatch") + assert.Equal(t, events.UserLocalLoginFailureCode, event.GetCode(), "event.Code mismatch") + }) +} + func TestCreateRegisterChallenge(t *testing.T) { t.Parallel() ctx := context.Background() @@ -1841,7 +1887,6 @@ func configureForMFA(t *testing.T, srv *TestTLSServer) *configureMFAResp { Webauthn: &types.Webauthn{ RPID: "localhost", }, - // Use default Webauthn config. }) require.NoError(t, err) diff --git a/lib/auth/methods.go b/lib/auth/methods.go index 18d571efc0832..5bcac3816e8a9 100644 --- a/lib/auth/methods.go +++ b/lib/auth/methods.go @@ -135,7 +135,7 @@ func (a *Server) authenticateUserLogin(ctx context.Context, req AuthenticateUser clientMetadata: req.ClientMetadata, authErr: err, }); err != nil { - log.WithError(err).Warn("Failed to emit login event.") + log.WithError(err).Warn("Failed to emit login event") } return nil, nil, trace.Wrap(err) } @@ -187,7 +187,7 @@ func (a *Server) authenticateUserLogin(ctx context.Context, req AuthenticateUser checker: checker, authErr: err, }); err != nil { - log.WithError(err).Warn("Failed to emit login event.") + log.WithError(err).Warn("Failed to emit login event") } return nil, nil, trace.Wrap(err) } @@ -199,7 +199,7 @@ func (a *Server) authenticateUserLogin(ctx context.Context, req AuthenticateUser mfaDevice: mfaDev, checker: checker, }); err != nil { - log.WithError(err).Warn("Failed to emit login event.") + log.WithError(err).Warn("Failed to emit login event") } return userState, checker, trace.Wrap(err)