ssh: add username to sftp auth errors

Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
Nicola Murino 2024-02-25 15:45:50 +01:00
parent 92911bda2b
commit be2ed1089c
No known key found for this signature in database
GPG key ID: 935D2952DEC4EECF
4 changed files with 25 additions and 16 deletions

2
go.mod
View file

@ -187,5 +187,5 @@ replace (
github.com/jlaffaye/ftp => github.com/drakkan/ftp v0.0.0-20240210102745-f1ffc43f78d2 github.com/jlaffaye/ftp => github.com/drakkan/ftp v0.0.0-20240210102745-f1ffc43f78d2
github.com/pkg/sftp => github.com/drakkan/sftp v0.0.0-20240214104840-fbb0b8bdb30c github.com/pkg/sftp => github.com/drakkan/sftp v0.0.0-20240214104840-fbb0b8bdb30c
github.com/robfig/cron/v3 => github.com/drakkan/cron/v3 v3.0.0-20230222140221-217a1e4d96c0 github.com/robfig/cron/v3 => github.com/drakkan/cron/v3 v3.0.0-20230222140221-217a1e4d96c0
golang.org/x/crypto => github.com/drakkan/crypto v0.0.0-20240224191538-9f4629f0732c golang.org/x/crypto => github.com/drakkan/crypto v0.0.0-20240225143451-17f1441a9706
) )

4
go.sum
View file

@ -109,8 +109,8 @@ github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI=
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
github.com/drakkan/cron/v3 v3.0.0-20230222140221-217a1e4d96c0 h1:EW9gIJRmt9lzk66Fhh4S8VEtURA6QHZqGeSRE9Nb2/U= github.com/drakkan/cron/v3 v3.0.0-20230222140221-217a1e4d96c0 h1:EW9gIJRmt9lzk66Fhh4S8VEtURA6QHZqGeSRE9Nb2/U=
github.com/drakkan/cron/v3 v3.0.0-20230222140221-217a1e4d96c0/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/drakkan/cron/v3 v3.0.0-20230222140221-217a1e4d96c0/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/drakkan/crypto v0.0.0-20240224191538-9f4629f0732c h1:Jwxc0vhD7t484x2GDv8B5WbvrSwxML9V+A8esl7vkh8= github.com/drakkan/crypto v0.0.0-20240225143451-17f1441a9706 h1:udyaktWAkqPhMN9iVkpZ4kxdW7clkq7Ia4etPilRMRk=
github.com/drakkan/crypto v0.0.0-20240224191538-9f4629f0732c/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= github.com/drakkan/crypto v0.0.0-20240225143451-17f1441a9706/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
github.com/drakkan/ftp v0.0.0-20240210102745-f1ffc43f78d2 h1:ufiGMPFBjndWSQOst9FNP11IuMqPblI2NXbpRMUWNhk= github.com/drakkan/ftp v0.0.0-20240210102745-f1ffc43f78d2 h1:ufiGMPFBjndWSQOst9FNP11IuMqPblI2NXbpRMUWNhk=
github.com/drakkan/ftp v0.0.0-20240210102745-f1ffc43f78d2/go.mod h1:4p8lUl4vQ80L598CygL+3IFtm+3nggvvW/palOlViwE= github.com/drakkan/ftp v0.0.0-20240210102745-f1ffc43f78d2/go.mod h1:4p8lUl4vQ80L598CygL+3IFtm+3nggvvW/palOlViwE=
github.com/drakkan/ftpserverlib v0.0.0-20240212100826-a241365cb085 h1:LAKYR9z9USKeyEQK91sRWldmMOjEHLOt2NuLDx+x1UQ= github.com/drakkan/ftpserverlib v0.0.0-20240212100826-a241365cb085 h1:LAKYR9z9USKeyEQK91sRWldmMOjEHLOt2NuLDx+x1UQ=

View file

@ -2227,23 +2227,26 @@ func TestCanReadSymlink(t *testing.T) {
func TestAuthenticationErrors(t *testing.T) { func TestAuthenticationErrors(t *testing.T) {
loginMethod := dataprovider.SSHLoginMethodPassword loginMethod := dataprovider.SSHLoginMethodPassword
err := newAuthenticationError(fmt.Errorf("cannot validate credentials: %w", util.NewRecordNotFoundError("not found")), loginMethod) username := "test user"
err := newAuthenticationError(fmt.Errorf("cannot validate credentials: %w", util.NewRecordNotFoundError("not found")),
loginMethod, username)
assert.ErrorIs(t, err, sftpAuthError) assert.ErrorIs(t, err, sftpAuthError)
assert.ErrorIs(t, err, util.ErrNotFound) assert.ErrorIs(t, err, util.ErrNotFound)
var sftpAuthErr *authenticationError var sftpAuthErr *authenticationError
if assert.ErrorAs(t, err, &sftpAuthErr) { if assert.ErrorAs(t, err, &sftpAuthErr) {
assert.Equal(t, loginMethod, sftpAuthErr.getLoginMethod()) assert.Equal(t, loginMethod, sftpAuthErr.getLoginMethod())
assert.Equal(t, username, sftpAuthErr.getUsername())
} }
err = newAuthenticationError(fmt.Errorf("cannot validate credentials: %w", fs.ErrPermission), loginMethod) err = newAuthenticationError(fmt.Errorf("cannot validate credentials: %w", fs.ErrPermission), loginMethod, username)
assert.ErrorIs(t, err, sftpAuthError) assert.ErrorIs(t, err, sftpAuthError)
assert.NotErrorIs(t, err, util.ErrNotFound) assert.NotErrorIs(t, err, util.ErrNotFound)
err = newAuthenticationError(fmt.Errorf("cert has wrong type %d", ssh.HostCert), loginMethod) err = newAuthenticationError(fmt.Errorf("cert has wrong type %d", ssh.HostCert), loginMethod, username)
assert.ErrorIs(t, err, sftpAuthError) assert.ErrorIs(t, err, sftpAuthError)
assert.NotErrorIs(t, err, util.ErrNotFound) assert.NotErrorIs(t, err, util.ErrNotFound)
err = newAuthenticationError(errors.New("ssh: certificate signed by unrecognized authority"), loginMethod) err = newAuthenticationError(errors.New("ssh: certificate signed by unrecognized authority"), loginMethod, username)
assert.ErrorIs(t, err, sftpAuthError) assert.ErrorIs(t, err, sftpAuthError)
assert.NotErrorIs(t, err, util.ErrNotFound) assert.NotErrorIs(t, err, util.ErrNotFound)
err = newAuthenticationError(nil, loginMethod) err = newAuthenticationError(nil, loginMethod, username)
assert.ErrorIs(t, err, sftpAuthError) assert.ErrorIs(t, err, sftpAuthError)
assert.NotErrorIs(t, err, util.ErrNotFound) assert.NotErrorIs(t, err, util.ErrNotFound)
} }

View file

@ -77,7 +77,7 @@ var (
certs: map[string]bool{}, certs: map[string]bool{},
} }
sftpAuthError = newAuthenticationError(nil, "") sftpAuthError = newAuthenticationError(nil, "", "")
) )
// Binding defines the configuration for a network listener // Binding defines the configuration for a network listener
@ -192,6 +192,7 @@ type Configuration struct {
type authenticationError struct { type authenticationError struct {
err error err error
loginMethod string loginMethod string
username string
} }
func (e *authenticationError) Error() string { func (e *authenticationError) Error() string {
@ -213,8 +214,12 @@ func (e *authenticationError) getLoginMethod() string {
return e.loginMethod return e.loginMethod
} }
func newAuthenticationError(err error, loginMethod string) *authenticationError { func (e *authenticationError) getUsername() string {
return &authenticationError{err: err, loginMethod: loginMethod} return e.username
}
func newAuthenticationError(err error, loginMethod, username string) *authenticationError {
return &authenticationError{err: err, loginMethod: loginMethod, username: username}
} }
// ShouldBind returns true if there is at least a valid binding // ShouldBind returns true if there is at least a valid binding
@ -240,7 +245,7 @@ func (c *Configuration) getServerConfig() *ssh.ServerConfig {
} }
if err != nil { if err != nil {
return nil, newAuthenticationError(fmt.Errorf("could not validate public key credentials: %w", err), return nil, newAuthenticationError(fmt.Errorf("could not validate public key credentials: %w", err),
dataprovider.SSHLoginMethodPublicKey) dataprovider.SSHLoginMethodPublicKey, conn.User())
} }
return sp, nil return sp, nil
@ -751,7 +756,8 @@ func discardAllChannels(in <-chan ssh.NewChannel, message, connectionID string)
} }
func checkAuthError(ip string, err error) { func checkAuthError(ip string, err error) {
if authErrors, ok := err.(*ssh.ServerAuthError); ok { var authErrors *ssh.ServerAuthError
if errors.As(err, &authErrors) {
// check public key auth errors here // check public key auth errors here
for _, err := range authErrors.Errors { for _, err := range authErrors.Errors {
var sftpAuthErr *authenticationError var sftpAuthErr *authenticationError
@ -764,7 +770,7 @@ func checkAuthError(ip string, err error) {
logEv = notifier.LogEventTypeLoginNoUser logEv = notifier.LogEventTypeLoginNoUser
} }
common.AddDefenderEvent(ip, common.ProtocolSSH, event) common.AddDefenderEvent(ip, common.ProtocolSSH, event)
plugin.Handler.NotifyLogEvent(logEv, common.ProtocolSSH, "", ip, "", err) plugin.Handler.NotifyLogEvent(logEv, common.ProtocolSSH, sftpAuthErr.getUsername(), ip, "", err)
return return
} }
} }
@ -1215,7 +1221,7 @@ func (c *Configuration) validatePasswordCredentials(conn ssh.ConnMetadata, pass
user.Username = conn.User() user.Username = conn.User()
updateLoginMetrics(&user, ipAddr, method, err) updateLoginMetrics(&user, ipAddr, method, err)
if err != nil { if err != nil {
return nil, newAuthenticationError(fmt.Errorf("could not validate password credentials: %w", err), method) return nil, newAuthenticationError(fmt.Errorf("could not validate password credentials: %w", err), method, conn.User())
} }
return sshPerm, nil return sshPerm, nil
} }
@ -1235,7 +1241,7 @@ func (c *Configuration) validateKeyboardInteractiveCredentials(conn ssh.ConnMeta
user.Username = conn.User() user.Username = conn.User()
updateLoginMetrics(&user, ipAddr, method, err) updateLoginMetrics(&user, ipAddr, method, err)
if err != nil { if err != nil {
return nil, newAuthenticationError(fmt.Errorf("could not validate keyboard interactive credentials: %w", err), method) return nil, newAuthenticationError(fmt.Errorf("could not validate keyboard interactive credentials: %w", err), method, conn.User())
} }
return sshPerm, nil return sshPerm, nil
} }