mirror of
https://github.com/netbirdio/netbird.git
synced 2026-04-18 00:06:38 +00:00
161 lines
3.7 KiB
Go
161 lines
3.7 KiB
Go
package server
|
|
|
|
import (
|
|
"hash/fnv"
|
|
"math"
|
|
"sync"
|
|
"time"
|
|
|
|
nbpeer "github.com/netbirdio/netbird/management/server/peer"
|
|
)
|
|
|
|
const (
|
|
reconnThreshold = 5 * time.Minute
|
|
baseBlockDuration = 10 * time.Minute // Duration for which a peer is banned after exceeding the reconnection limit
|
|
reconnLimitForBan = 30 // Number of reconnections within the reconnTreshold that triggers a ban
|
|
metaChangeLimit = 3 // Number of reconnections with different metadata that triggers a ban of one peer
|
|
)
|
|
|
|
type lfConfig struct {
|
|
reconnThreshold time.Duration
|
|
baseBlockDuration time.Duration
|
|
reconnLimitForBan int
|
|
metaChangeLimit int
|
|
}
|
|
|
|
func initCfg() *lfConfig {
|
|
return &lfConfig{
|
|
reconnThreshold: reconnThreshold,
|
|
baseBlockDuration: baseBlockDuration,
|
|
reconnLimitForBan: reconnLimitForBan,
|
|
metaChangeLimit: metaChangeLimit,
|
|
}
|
|
}
|
|
|
|
type loginFilter struct {
|
|
mu sync.RWMutex
|
|
cfg *lfConfig
|
|
logged map[string]*peerState
|
|
}
|
|
|
|
type peerState struct {
|
|
currentHash uint64
|
|
sessionCounter int
|
|
sessionStart time.Time
|
|
lastSeen time.Time
|
|
isBanned bool
|
|
banLevel int
|
|
banExpiresAt time.Time
|
|
metaChangeCounter int
|
|
metaChangeWindowStart time.Time
|
|
}
|
|
|
|
func newLoginFilter() *loginFilter {
|
|
return newLoginFilterWithCfg(initCfg())
|
|
}
|
|
|
|
func newLoginFilterWithCfg(cfg *lfConfig) *loginFilter {
|
|
return &loginFilter{
|
|
logged: make(map[string]*peerState),
|
|
cfg: cfg,
|
|
}
|
|
}
|
|
|
|
func (l *loginFilter) allowLogin(wgPubKey string, metaHash uint64) bool {
|
|
l.mu.RLock()
|
|
defer func() {
|
|
l.mu.RUnlock()
|
|
}()
|
|
state, ok := l.logged[wgPubKey]
|
|
if !ok {
|
|
return true
|
|
}
|
|
if state.isBanned && time.Now().Before(state.banExpiresAt) {
|
|
return false
|
|
}
|
|
if metaHash != state.currentHash {
|
|
if time.Now().Before(state.metaChangeWindowStart.Add(l.cfg.reconnThreshold)) && state.metaChangeCounter >= l.cfg.metaChangeLimit {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (l *loginFilter) addLogin(wgPubKey string, metaHash uint64) {
|
|
now := time.Now()
|
|
l.mu.Lock()
|
|
defer func() {
|
|
l.mu.Unlock()
|
|
}()
|
|
|
|
state, ok := l.logged[wgPubKey]
|
|
|
|
if !ok {
|
|
l.logged[wgPubKey] = &peerState{
|
|
currentHash: metaHash,
|
|
sessionCounter: 1,
|
|
sessionStart: now,
|
|
lastSeen: now,
|
|
metaChangeWindowStart: now,
|
|
metaChangeCounter: 1,
|
|
}
|
|
return
|
|
}
|
|
|
|
if state.isBanned && now.After(state.banExpiresAt) {
|
|
state.isBanned = false
|
|
}
|
|
|
|
if state.banLevel > 0 && now.Sub(state.lastSeen) > (2*l.cfg.baseBlockDuration) {
|
|
state.banLevel = 0
|
|
}
|
|
|
|
if metaHash != state.currentHash {
|
|
if now.After(state.metaChangeWindowStart.Add(l.cfg.reconnThreshold)) {
|
|
state.metaChangeWindowStart = now
|
|
state.metaChangeCounter = 1
|
|
} else {
|
|
state.metaChangeCounter++
|
|
}
|
|
state.currentHash = metaHash
|
|
state.sessionCounter = 1
|
|
state.sessionStart = now
|
|
state.lastSeen = now
|
|
return
|
|
}
|
|
|
|
state.sessionCounter++
|
|
if state.sessionCounter > l.cfg.reconnLimitForBan && now.Sub(state.sessionStart) < l.cfg.reconnThreshold {
|
|
state.isBanned = true
|
|
state.banLevel++
|
|
|
|
backoffFactor := math.Pow(2, float64(state.banLevel-1))
|
|
duration := time.Duration(float64(l.cfg.baseBlockDuration) * backoffFactor)
|
|
state.banExpiresAt = now.Add(duration)
|
|
|
|
state.sessionCounter = 0
|
|
state.sessionStart = now
|
|
}
|
|
state.lastSeen = now
|
|
}
|
|
|
|
func metaHash(meta nbpeer.PeerSystemMeta, pubip string) uint64 {
|
|
h := fnv.New64a()
|
|
|
|
h.Write([]byte(meta.WtVersion))
|
|
h.Write([]byte(meta.OSVersion))
|
|
h.Write([]byte(meta.KernelVersion))
|
|
h.Write([]byte(meta.Hostname))
|
|
h.Write([]byte(meta.SystemSerialNumber))
|
|
h.Write([]byte(pubip))
|
|
|
|
macs := uint64(0)
|
|
for _, na := range meta.NetworkAddresses {
|
|
for _, r := range na.Mac {
|
|
macs += uint64(r)
|
|
}
|
|
}
|
|
|
|
return h.Sum64() + macs
|
|
}
|