fix: restore recurring GeoIP checks

This commit is contained in:
Patrick Asmus
2026-06-10 13:22:12 +02:00
parent 0716458426
commit 86db4935b8
11 changed files with 137 additions and 22 deletions

View File

@@ -40,7 +40,7 @@ type Daemon struct {
mu sync.Mutex
seen map[string]time.Time
events []queryEvent
geoSeen map[string]bool
geoSeen map[string]time.Time
wl map[string]bool
serviceMu sync.Mutex
@@ -93,7 +93,7 @@ func New(c *config.Config) (*Daemon, error) {
d := &Daemon{
Config: c, Store: st, FW: fw, Logger: logger,
Client: &http.Client{Timeout: 20 * time.Second, Transport: tr},
seen: map[string]time.Time{}, geoSeen: map[string]bool{},
seen: map[string]time.Time{}, geoSeen: map[string]time.Time{},
}
d.Geo = geoip.New(c.GeoIPMMDBPath, c.GeoIPLicenseKey, filepath.Join(filepath.Dir(c.Path), "geoip"), c.GeoIPCacheTTL, st)
return d, nil
@@ -409,13 +409,6 @@ func (d *Daemon) checkGeoIP(ctx context.Context, ip string) {
if d.Config.GeoIPSkipPrivate && geoip.IsPrivateIP(ip) {
return
}
d.mu.Lock()
if d.geoSeen[ip] {
d.mu.Unlock()
return
}
d.geoSeen[ip] = true
d.mu.Unlock()
if d.isWhitelisted(ip) {
return
}
@@ -423,6 +416,9 @@ func (d *Daemon) checkGeoIP(ctx context.Context, ip string) {
if exists {
return
}
if !d.shouldCheckGeoIP(ip, time.Now()) {
return
}
cc, err := d.Geo.Lookup(ip)
if err != nil || cc == "" {
return
@@ -432,6 +428,41 @@ func (d *Daemon) checkGeoIP(ctx context.Context, ip string) {
}
}
func (d *Daemon) shouldCheckGeoIP(ip string, now time.Time) bool {
interval := d.geoIPCheckInterval()
d.mu.Lock()
defer d.mu.Unlock()
if last, ok := d.geoSeen[ip]; ok && now.Sub(last) < interval {
return false
}
d.geoSeen[ip] = now
d.pruneGeoSeenLocked(now, interval)
return true
}
func (d *Daemon) geoIPCheckInterval() time.Duration {
seconds := 0
if d.Config != nil {
seconds = d.Config.GeoIPCheckInterval
if seconds <= 0 {
seconds = d.Config.CheckInterval
}
}
if seconds <= 0 {
seconds = 1
}
return time.Duration(seconds) * time.Second
}
func (d *Daemon) pruneGeoSeenLocked(now time.Time, interval time.Duration) {
cutoff := now.Add(-2 * interval)
for ip, last := range d.geoSeen {
if last.Before(cutoff) {
delete(d.geoSeen, ip)
}
}
}
func (d *Daemon) Ban(ctx context.Context, ip, domain string, count int, proto, reason, source, country string, permanent bool) error {
if d.isWhitelisted(ip) {
return nil

View File

@@ -363,3 +363,37 @@ func TestDryRunDoesNotInsertActiveBan(t *testing.T) {
t.Fatal("dry-run must not create an active ban")
}
}
func TestGeoIPCheckGateUsesConfiguredInterval(t *testing.T) {
now := time.Unix(1000, 0)
d := &Daemon{
Config: &config.Config{CheckInterval: 10, GeoIPCheckInterval: 30},
geoSeen: map[string]time.Time{},
}
if !d.shouldCheckGeoIP("203.0.113.7", now) {
t.Fatal("first GeoIP check should be allowed")
}
if d.shouldCheckGeoIP("203.0.113.7", now.Add(29*time.Second)) {
t.Fatal("GeoIP check inside configured interval should be skipped")
}
if !d.shouldCheckGeoIP("203.0.113.7", now.Add(30*time.Second)) {
t.Fatal("GeoIP check after configured interval should be allowed")
}
}
func TestGeoIPCheckGateFallsBackToPollInterval(t *testing.T) {
now := time.Unix(1000, 0)
d := &Daemon{
Config: &config.Config{CheckInterval: 10},
geoSeen: map[string]time.Time{},
}
if !d.shouldCheckGeoIP("203.0.113.7", now) {
t.Fatal("first GeoIP check should be allowed")
}
if d.shouldCheckGeoIP("203.0.113.7", now.Add(9*time.Second)) {
t.Fatal("GeoIP check inside poll interval should be skipped")
}
if !d.shouldCheckGeoIP("203.0.113.7", now.Add(10*time.Second)) {
t.Fatal("GeoIP check after poll interval should be allowed")
}
}