Ver código fonte

defender: implement logging of events and bans

Signed-off-by: Anthrazz <25553648+Anthrazz@users.noreply.github.com>
Anthrazz 1 ano atrás
pai
commit
e187b77f29

+ 1 - 0
docs/full-configuration.md

@@ -103,6 +103,7 @@ The configuration file contains the following sections:
     - `observation_time`, integer. Defines the time window, in minutes, for tracking client errors. A host is banned if it has exceeded the defined threshold during the last observation time minutes. Default: `30`.
     - `entries_soft_limit`, integer. Ignored for `provider` driver. Default: `100`.
     - `entries_hard_limit`, integer. The number of banned IPs and host scores kept in memory will vary between the soft and hard limit for `memory` driver. If you use the `provider` driver, this setting will limit the number of entries to return when you ask for the entire host list from the defender. Default: `150`.
+    - `log_events`, boolean. Set to true if defender events and banned IPs should be logged. Default: `false`.
   - `rate_limiters`, list of structs containing the rate limiters configuration. Take a look [here](./rate-limiting.md) for more details. Each struct has the following fields:
     - `average`, integer. Average defines the maximum rate allowed. 0 means disabled. Default: 0
     - `period`, integer. Period defines the period as milliseconds. The rate is actually defined by dividing average by period Default: 1000 (1 second).

+ 44 - 5
internal/common/defender.go

@@ -19,17 +19,18 @@ import (
 	"time"
 
 	"github.com/drakkan/sftpgo/v2/internal/dataprovider"
+	"github.com/drakkan/sftpgo/v2/internal/logger"
 )
 
 // HostEvent is the enumerable for the supported host events
-type HostEvent int
+type HostEvent string
 
 // Supported host events
 const (
-	HostEventLoginFailed HostEvent = iota
-	HostEventUserNotFound
-	HostEventNoLoginTried
-	HostEventLimitExceeded
+	HostEventLoginFailed   HostEvent = "LoginFailed"
+	HostEventUserNotFound            = "UserNotFound"
+	HostEventNoLoginTried            = "NoLoginTried"
+	HostEventLimitExceeded           = "LimitExceeded"
 )
 
 // Supported defender drivers
@@ -89,6 +90,8 @@ type DefenderConfig struct {
 	// to return when you request for the entire host list from the defender
 	EntriesSoftLimit int `json:"entries_soft_limit" mapstructure:"entries_soft_limit"`
 	EntriesHardLimit int `json:"entries_hard_limit" mapstructure:"entries_hard_limit"`
+	// LogEvents controls if Defender events should be logged
+	LogEvents bool `json:"log_events" mapstructure:"log_events"`
 }
 
 type baseDefender struct {
@@ -132,6 +135,42 @@ func (d *baseDefender) getScore(event HostEvent) int {
 	return score
 }
 
+func (d *baseDefender) logEvent(ip, protocol string, event HostEvent, totalScore int) {
+	if !d.config.LogEvents {
+		return
+	}
+
+	// ignore events which do not change the host score
+	eventScore := d.getScore(event)
+	if eventScore == 0 {
+		return
+	}
+
+	logger.GetLogger().Info().
+		Timestamp().
+		Str("sender", "defender").
+		Str("client_ip", ip).
+		Str("protocol", protocol).
+		Str("event", string(event)).
+		Int("increase_score_by", eventScore).
+		Int("score", totalScore).
+		Send()
+}
+
+func (d *baseDefender) logBan(ip, protocol string) {
+	if !d.config.LogEvents {
+		return
+	}
+
+	logger.GetLogger().Info().
+		Timestamp().
+		Str("sender", "defender").
+		Str("client_ip", ip).
+		Str("protocol", protocol).
+		Str("event", "banned").
+		Send()
+}
+
 type hostEvent struct {
 	dateTime time.Time
 	score    int

+ 2 - 0
internal/common/defenderdb.go

@@ -100,7 +100,9 @@ func (d *dbDefender) AddEvent(ip, protocol string, event HostEvent) {
 	if err != nil {
 		return
 	}
+	d.baseDefender.logEvent(ip, protocol, event, host.Score)
 	if host.Score > d.config.Threshold {
+		d.baseDefender.logBan(ip, protocol)
 		banTime := time.Now().Add(time.Duration(d.config.BanTime) * time.Minute)
 		err = dataprovider.SetDefenderBanTime(ip, util.GetTimeAsMsSinceEpoch(banTime))
 		if err == nil {

+ 3 - 0
internal/common/defendermem.go

@@ -206,9 +206,11 @@ func (d *memoryDefender) AddEvent(ip, protocol string, event HostEvent) {
 				idx++
 			}
 		}
+		d.baseDefender.logEvent(ip, protocol, event, hs.TotalScore)
 
 		hs.Events = hs.Events[:idx]
 		if hs.TotalScore >= d.config.Threshold {
+			d.baseDefender.logBan(ip, protocol)
 			d.banned[ip] = time.Now().Add(time.Duration(d.config.BanTime) * time.Minute)
 			delete(d.hosts, ip)
 			d.cleanupBanned()
@@ -222,6 +224,7 @@ func (d *memoryDefender) AddEvent(ip, protocol string, event HostEvent) {
 			d.hosts[ip] = hs
 		}
 	} else {
+		d.baseDefender.logEvent(ip, protocol, event, ev.score)
 		d.hosts[ip] = hostScore{
 			TotalScore: ev.score,
 			Events:     []hostEvent{ev},