notifier plugin: add support for login succeeded events
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
parent
e8140d7310
commit
456517af87
15 changed files with 58 additions and 24 deletions
|
@ -482,7 +482,7 @@ The configuration file contains the following sections:
|
|||
- `fs_events`, list of strings. Defines the filesystem events that will be notified to this plugin.
|
||||
- `provider_events`, list of strings. Defines the provider events that will be notified to this plugin.
|
||||
- `provider_objects`, list if strings. Defines the provider objects that will be notified to this plugin.
|
||||
- `log_events`, list of integers. Defines the log events that will be notified to this plugin. `1` means "Login failed", `2` means "Login with non-existent user", `3` means "No login tried", `4` means "Algorithm negotiation failed".
|
||||
- `log_events`, list of integers. Defines the log events that will be notified to this plugin. `1` means "Login failed", `2` means "Login with non-existent user", `3` means "No login tried", `4` means "Algorithm negotiation failed", `5` means "Login succeeded".
|
||||
- `retry_max_time`, integer. Defines the maximum number of seconds an event can be late. SFTPGo adds a timestamp to each event and add to an internal queue any events that a the plugin fails to handle (the plugin returns an error or it is not running). If a plugin fails to handle an event that is too late, based on this configuration, it will be discarded. SFTPGo will try to resend queued events every 30 seconds. 0 means no retry.
|
||||
- `retry_queue_max_size`, integer. Defines the maximum number of events that the internal queue can hold. Once the queue is full, the events that cannot be sent to the plugin will be discarded. 0 means no limit.
|
||||
- `kms_options`, struct. Defines the options for kms plugins.
|
||||
|
|
5
go.mod
5
go.mod
|
@ -46,14 +46,14 @@ require (
|
|||
github.com/minio/sio v0.3.1
|
||||
github.com/otiai10/copy v1.14.0
|
||||
github.com/pires/go-proxyproto v0.7.0
|
||||
github.com/pkg/sftp v1.13.6
|
||||
github.com/pkg/sftp v1.13.7-0.20240410063531-637088883317
|
||||
github.com/pquerna/otp v1.4.0
|
||||
github.com/prometheus/client_golang v1.19.0
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/rs/cors v1.10.1
|
||||
github.com/rs/xid v1.5.0
|
||||
github.com/rs/zerolog v1.32.0
|
||||
github.com/sftpgo/sdk v0.1.6-0.20240317102632-f6eb95ea55c3
|
||||
github.com/sftpgo/sdk v0.1.6-0.20240409173349-421b3dff3896
|
||||
github.com/shirou/gopsutil/v3 v3.24.3
|
||||
github.com/spf13/afero v1.11.0
|
||||
github.com/spf13/cobra v1.8.0
|
||||
|
@ -185,7 +185,6 @@ require (
|
|||
replace (
|
||||
github.com/fclairamb/ftpserverlib => github.com/drakkan/ftpserverlib v0.0.0-20240313174824-cf52df3aa8f7
|
||||
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/robfig/cron/v3 => github.com/drakkan/cron/v3 v3.0.0-20230222140221-217a1e4d96c0
|
||||
golang.org/x/crypto => github.com/drakkan/crypto v0.0.0-20240405104909-a6b14455cac6
|
||||
)
|
||||
|
|
4
go.sum
4
go.sum
|
@ -314,6 +314,8 @@ github.com/pires/go-proxyproto v0.7.0/go.mod h1:Vz/1JPY/OACxWGQNIRY2BeyDmpoaWmEP
|
|||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/sftp v1.13.7-0.20240410063531-637088883317 h1:kupFhKi4R3XqKmUmqGSHWn/WZbC9CnwSoW421tL1gGw=
|
||||
github.com/pkg/sftp v1.13.7-0.20240410063531-637088883317/go.mod h1:KMKI0t3T6hfA+lTR/ssZdunHo+uwq7ghoN09/FSu3DY=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
|
@ -353,6 +355,8 @@ github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
|
|||
github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
|
||||
github.com/sftpgo/sdk v0.1.6-0.20240317102632-f6eb95ea55c3 h1:svxTNm3r2kRlpuVSUKi0WKQlsAq8VI0EzDWPNqeNn/o=
|
||||
github.com/sftpgo/sdk v0.1.6-0.20240317102632-f6eb95ea55c3/go.mod h1:AWoY2YYe/P1ymfTlRER/meERQjCcZZTbgVPGcPQgaqc=
|
||||
github.com/sftpgo/sdk v0.1.6-0.20240409173349-421b3dff3896 h1:ykxybS9WKurHTatKJ9WjqYD+WH9YH/2QMxCkxUPTVLY=
|
||||
github.com/sftpgo/sdk v0.1.6-0.20240409173349-421b3dff3896/go.mod h1:AWoY2YYe/P1ymfTlRER/meERQjCcZZTbgVPGcPQgaqc=
|
||||
github.com/shirou/gopsutil/v3 v3.24.3 h1:eoUGJSmdfLzJ3mxIhmOAhgKEKgQkeOwKpz1NbhVnuPE=
|
||||
github.com/shirou/gopsutil/v3 v3.24.3/go.mod h1:JpND7O217xa72ewWz9zN2eIIkPWsDN/3pl0H8Qt0uwg=
|
||||
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
|
||||
|
|
|
@ -415,7 +415,9 @@ func setStartDirectory(startDirectory string, cc ftpserver.ClientContext) {
|
|||
|
||||
func updateLoginMetrics(user *dataprovider.User, ip, loginMethod string, err error) {
|
||||
metric.AddLoginAttempt(loginMethod)
|
||||
if err != nil && err != common.ErrInternalFailure {
|
||||
if err == nil {
|
||||
plugin.Handler.NotifyLogEvent(notifier.LogEventTypeLoginOK, common.ProtocolFTP, user.Username, ip, "", nil)
|
||||
} else if err != common.ErrInternalFailure {
|
||||
logger.ConnectionFailedLog(user.Username, ip, loginMethod,
|
||||
common.ProtocolFTP, err.Error())
|
||||
event := common.HostEventLoginFailed
|
||||
|
|
|
@ -472,6 +472,8 @@ func getLogEventString(event notifier.LogEventType) string {
|
|||
return "No login tried"
|
||||
case notifier.LogEventTypeNotNegotiated:
|
||||
return "Algorithm negotiation failed"
|
||||
case notifier.LogEventTypeLoginOK:
|
||||
return "Login succeeded"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
|
|
|
@ -697,7 +697,9 @@ func updateLoginMetrics(user *dataprovider.User, loginMethod, ip string, err err
|
|||
default:
|
||||
protocol = common.ProtocolHTTP
|
||||
}
|
||||
if err != nil && err != common.ErrInternalFailure && err != common.ErrNoCredentials {
|
||||
if err == nil {
|
||||
plugin.Handler.NotifyLogEvent(notifier.LogEventTypeLoginOK, protocol, user.Username, ip, "", nil)
|
||||
} else if err != common.ErrInternalFailure && err != common.ErrNoCredentials {
|
||||
logger.ConnectionFailedLog(user.Username, ip, loginMethod, protocol, err.Error())
|
||||
err = handleDefenderEventLoginFailed(ip, err)
|
||||
logEv := notifier.LogEventTypeLoginFailed
|
||||
|
|
|
@ -3398,6 +3398,7 @@ func TestGetLogEventString(t *testing.T) {
|
|||
assert.Equal(t, "Login with non-existent user", getLogEventString(notifier.LogEventTypeLoginNoUser))
|
||||
assert.Equal(t, "No login tried", getLogEventString(notifier.LogEventTypeNoLoginTried))
|
||||
assert.Equal(t, "Algorithm negotiation failed", getLogEventString(notifier.LogEventTypeNotNegotiated))
|
||||
assert.Equal(t, "Login succeeded", getLogEventString(notifier.LogEventTypeLoginOK))
|
||||
assert.Empty(t, getLogEventString(0))
|
||||
}
|
||||
|
||||
|
|
|
@ -44,6 +44,9 @@ func (c *NotifierConfig) hasActions() bool {
|
|||
if len(c.ProviderEvents) > 0 && len(c.ProviderObjects) > 0 {
|
||||
return true
|
||||
}
|
||||
if len(c.LogEvents) > 0 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
|
@ -250,10 +253,6 @@ func (p *notifierPlugin) notifyProviderAction(event *notifier.ProviderEvent, obj
|
|||
}
|
||||
|
||||
func (p *notifierPlugin) notifyLogEvent(event *notifier.LogEvent) {
|
||||
if !util.Contains(p.config.NotifierOptions.LogEvents, int(event.Event)) {
|
||||
return
|
||||
}
|
||||
|
||||
go func() {
|
||||
Handler.addTask()
|
||||
defer Handler.removeTask()
|
||||
|
|
|
@ -331,18 +331,28 @@ func (m *Manager) NotifyLogEvent(event notifier.LogEventType, protocol, username
|
|||
m.notifLock.RLock()
|
||||
defer m.notifLock.RUnlock()
|
||||
|
||||
e := ¬ifier.LogEvent{
|
||||
Timestamp: time.Now().UnixNano(),
|
||||
Event: event,
|
||||
Protocol: protocol,
|
||||
Username: username,
|
||||
IP: ip,
|
||||
Message: err.Error(),
|
||||
Role: role,
|
||||
}
|
||||
var e *notifier.LogEvent
|
||||
|
||||
for _, n := range m.notifiers {
|
||||
n.notifyLogEvent(e)
|
||||
if util.Contains(n.config.NotifierOptions.LogEvents, int(event)) {
|
||||
if e == nil {
|
||||
message := ""
|
||||
if err != nil {
|
||||
message = err.Error()
|
||||
}
|
||||
|
||||
e = ¬ifier.LogEvent{
|
||||
Timestamp: time.Now().UnixNano(),
|
||||
Event: event,
|
||||
Protocol: protocol,
|
||||
Username: username,
|
||||
IP: ip,
|
||||
Message: message,
|
||||
Role: role,
|
||||
}
|
||||
}
|
||||
n.notifyLogEvent(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1248,7 +1248,9 @@ func (c *Configuration) validateKeyboardInteractiveCredentials(conn ssh.ConnMeta
|
|||
|
||||
func updateLoginMetrics(user *dataprovider.User, ip, method string, err error) {
|
||||
metric.AddLoginAttempt(method)
|
||||
if err != nil {
|
||||
if err == nil {
|
||||
plugin.Handler.NotifyLogEvent(notifier.LogEventTypeLoginOK, common.ProtocolSSH, user.Username, ip, "", err)
|
||||
} else {
|
||||
logger.ConnectionFailedLog(user.Username, ip, method, common.ProtocolSSH, err.Error())
|
||||
if method != dataprovider.SSHLoginMethodPublicKey {
|
||||
// some clients try all available public keys for a user, we
|
||||
|
|
|
@ -422,7 +422,9 @@ func writeLog(r *http.Request, status int, err error) {
|
|||
|
||||
func updateLoginMetrics(user *dataprovider.User, ip, loginMethod string, err error) {
|
||||
metric.AddLoginAttempt(loginMethod)
|
||||
if err != nil && err != common.ErrInternalFailure && err != common.ErrNoCredentials {
|
||||
if err == nil {
|
||||
plugin.Handler.NotifyLogEvent(notifier.LogEventTypeLoginOK, common.ProtocolWebDAV, user.Username, ip, "", nil)
|
||||
} else if err != common.ErrInternalFailure && err != common.ErrNoCredentials {
|
||||
logger.ConnectionFailedLog(user.Username, ip, loginMethod, common.ProtocolWebDAV, err.Error())
|
||||
event := common.HostEventLoginFailed
|
||||
logEv := notifier.LogEventTypeLoginFailed
|
||||
|
|
|
@ -5214,12 +5214,14 @@ components:
|
|||
- 2
|
||||
- 3
|
||||
- 4
|
||||
- 5
|
||||
description: >
|
||||
Event status:
|
||||
* `1` - Login failed
|
||||
* `2` - Login failed non-existent user
|
||||
* `3` - No login tried
|
||||
* `4` - Algorithm negotiation failed
|
||||
* `5` - Login succeeded
|
||||
FsEventStatus:
|
||||
type: integer
|
||||
enum:
|
||||
|
|
|
@ -900,6 +900,7 @@
|
|||
"add": "Addition",
|
||||
"update": "Update",
|
||||
"login_failed": "Login failed",
|
||||
"login_ok": "Login succeeded",
|
||||
"login_missing_user": "Login with non-existent user",
|
||||
"no_login_tried": "No login tried",
|
||||
"algo_negotiation_failed": "Algorithm negotiation failed",
|
||||
|
|
|
@ -900,6 +900,7 @@
|
|||
"add": "Aggiunta",
|
||||
"update": "Aggiornamento",
|
||||
"login_failed": "Accesso fallito",
|
||||
"login_ok": "Accesso riuscito",
|
||||
"login_missing_user": "Accesso con utente inesistente",
|
||||
"no_login_tried": "Nessun accesso tentato",
|
||||
"algo_negotiation_failed": "Negoziazione algoritmo fallita",
|
||||
|
|
|
@ -385,6 +385,7 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
|||
function selectLogEvents(){
|
||||
let idActions = $('#idActions');
|
||||
idActions.empty();
|
||||
idActions.append(new Option($.t('events.login_ok'),"5",false,false));
|
||||
idActions.append(new Option($.t('events.login_failed'),"1",false,false));
|
||||
idActions.append(new Option($.t('events.login_missing_user'),"2",false,false));
|
||||
idActions.append(new Option($.t('events.no_login_tried'),"3",false,false));
|
||||
|
@ -875,6 +876,8 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
|||
return $.t('events.no_login_tried');
|
||||
case 4:
|
||||
return $.t('events.algo_negotiation_failed');
|
||||
case 5:
|
||||
return $.t('events.login_ok');
|
||||
default:
|
||||
console.log(`unknown log action "${data}"`);
|
||||
return "";
|
||||
|
@ -914,7 +917,9 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
|||
defaultContent: "",
|
||||
render: function(data, type, row) {
|
||||
if (type === 'display') {
|
||||
return escapeHTML(data);
|
||||
if (data){
|
||||
return escapeHTML(data);
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
@ -924,7 +929,9 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
|||
defaultContent: "",
|
||||
render: function(data, type, row) {
|
||||
if (type === 'display') {
|
||||
return escapeHTML(data);
|
||||
if (data){
|
||||
return escapeHTML(data);
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue