botguard

package
v1.21.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 14, 2026 License: MPL-2.0 Imports: 18 Imported by: 0

Documentation

Index

Constants

View Source
const (
	ModuleName    = "botguard"
	ModuleVersion = "1.0.0"
)

Variables

This section is empty.

Functions

func Descriptor

func Descriptor() module.Descriptor

Descriptor returns the module descriptor for registration.

Types

type BatchSignal

type BatchSignal struct {
	IP      string   `json:"ip"`      // IP address
	Score   int      `json:"score"`   // Behavioral score (0-100)
	Reasons []string `json:"reasons"` // Why flagged (e.g., "404_flood", "wp_probe")
	Action  string   `json:"action"`  // "ban", "grey", "allow_demote", "allow_extend"
	TS      int64    `json:"ts"`      // Unix timestamp
}

BatchSignal represents a signal from the shell botscan (Clock 3). Written as JSONL to /var/lib/nftban/botguard/batch_signals.jsonl

type BotConfig added in v1.21.0

type BotConfig struct {
	Name       string   // e.g. "GOOGLEBOT"
	Domains    []string // Suffix match domains (e.g. ["googlebot.com", "google.com"])
	VerifyMode string   // Verification method: "fcrdns"
	MaxConn    int      // Max concurrent connections allowed
	MaxRate    string   // nft rate string (e.g. "80/second")
	Burst      int      // nft burst value
}

BotConfig describes an allowed or known crawler identity. Loaded from allowed_crawlers.conf (pipe-delimited).

type Classifier added in v1.21.0

type Classifier struct {
	// contains filtered or unexported fields
}

Classifier performs bot classification using FCrDNS verification and allowed/denied crawler configuration.

func NewClassifier added in v1.21.0

func NewClassifier(cfg *Config, verifier *FCrDNSVerifier) (*Classifier, error)

NewClassifier creates a classifier with loaded crawler configs.

func (*Classifier) AllowedBots added in v1.21.0

func (c *Classifier) AllowedBots() []BotConfig

AllowedBots returns a copy of the allowed bot list.

func (*Classifier) Classify added in v1.21.0

func (c *Classifier) Classify(r *IPRecord, cfg *Config) ClassifyResult

Classify determines the classification for a suspect IP record. Returns a ClassifyResult with the target state and reason.

func (*Classifier) ProcessVerification added in v1.21.0

func (c *Classifier) ProcessVerification(result VerifyResult) ClassifyResult

ProcessVerification processes a completed FCrDNS result and returns the classification decision for the verified IP.

func (*Classifier) ReloadConfigs added in v1.21.0

func (c *Classifier) ReloadConfigs() error

ReloadConfigs reloads allowed and denied crawler configs from disk.

type ClassifyResult added in v1.21.0

type ClassifyResult struct {
	State   IPState      // Target state (allow, ban, grey, pending)
	Reason  string       // Why this decision was made
	BotName string       // Identified bot name (if any)
	Verify  VerifyStatus // FCrDNS verification result
}

ClassifyResult is the decision returned by the Classifier.

type Config

type Config struct {
	// Module control
	Enabled bool

	// Classification loop intervals
	LoopInterval         time.Duration // Normal: 60s
	LoopPressureInterval time.Duration // Under pressure: 40s

	// Hysteresis thresholds (per IP connection count)
	TripConn      int // Start analyzing (default 200)
	ClearConn     int // Stop analyzing (default 150)
	EmergencyConn int // Immediate block (default 500)

	// Kernel suspect marking config (informational — actual values in nft rules)
	SuspectRate    string        // e.g. "30/second"
	SuspectBurst   int           // e.g. 60
	SuspectTimeout time.Duration // e.g. 5m

	// TTLs for classification sets
	AllowTTL     time.Duration // 24h initial
	BanTTL       time.Duration // 96h
	GreyTTL      time.Duration // 30m
	EmergencyTTL time.Duration // 30m
	PendingTTL   time.Duration // 60s

	// Auto-tuning
	AutoTune bool

	// Batch integration
	BatchSignalFile string
	BatchInterval   time.Duration

	// FCrDNS verification
	VerifyWorkers   int           // Max concurrent DNS workers (default 8)
	VerifyRateLimit int           // Max new lookups per second (default 10)
	VerifyTimeout   time.Duration // Timeout per DNS lookup (default 3s)
	VerifyCacheTTL  time.Duration // Positive cache TTL (default 24h)
	VerifyNegTTL    time.Duration // Negative cache TTL (default 1h)

	// Crawler config files
	AllowedCrawlersFile string
	DeniedCrawlersFile  string

	// Logging
	LogLevel     string
	LogDecisions bool
}

Config holds all bot guard configuration.

func DefaultConfig

func DefaultConfig() *Config

DefaultConfig returns production-safe defaults.

func LoadConfig

func LoadConfig(path string) (*Config, error)

LoadConfig reads configuration from a shell-style config file. Supports KEY="VALUE" format (same as all NFTBan config files).

type Enforcer

type Enforcer struct {
	// contains filtered or unexported fields
}

Enforcer writes classification decisions to nftables enforcement sets. All writes go through the OpQueue for safe async netlink batching.

func NewEnforcer

func NewEnforcer(queue *opqueue.OpQueue, config *Config) *Enforcer

NewEnforcer creates an enforcer that writes to nft sets via OpQueue.

func (*Enforcer) Apply

func (e *Enforcer) Apply(ip netip.Addr, oldState, newState IPState, reason string) error

Apply writes a classification decision to the appropriate nft set. It removes the IP from the old set (if any) and adds it to the new set.

func (*Enforcer) ApplyEmergency

func (e *Enforcer) ApplyEmergency(ip netip.Addr, reason string) error

ApplyEmergency adds an IP directly to the emergency set (fast path).

func (*Enforcer) Remove

func (e *Enforcer) Remove(ip netip.Addr, state IPState) error

Remove removes an IP from its current set (e.g., when state expires).

type FCrDNSVerifier added in v1.21.0

type FCrDNSVerifier struct {
	// contains filtered or unexported fields
}

FCrDNSVerifier performs Forward-Confirmed Reverse DNS verification using a bounded worker pool with rate limiting and caching.

func NewFCrDNSVerifier added in v1.21.0

func NewFCrDNSVerifier(cfg *Config, allowedBots []BotConfig) *FCrDNSVerifier

NewFCrDNSVerifier creates a new verifier with the given config and bot list.

func (*FCrDNSVerifier) Enqueue added in v1.21.0

func (v *FCrDNSVerifier) Enqueue(ip netip.Addr)

Enqueue submits an IP for FCrDNS verification. Non-blocking: drops the request if the queue is full.

func (*FCrDNSVerifier) Results added in v1.21.0

func (v *FCrDNSVerifier) Results() <-chan VerifyResult

Results returns the channel of completed verifications.

func (*FCrDNSVerifier) Start added in v1.21.0

func (v *FCrDNSVerifier) Start(ctx context.Context)

Start launches the worker pool and rate limiter. Blocks until ctx is cancelled.

type GuardStats

type GuardStats struct {
	TickCount             int64         // Total classification ticks
	SuspectsFound         int64         // Total unique IPs found in suspect set
	Classified            int64         // Total IPs classified
	AllowCount            int64         // IPs classified as allow
	GreyCount             int64         // IPs classified as grey
	BanCount              int64         // IPs classified as ban
	EmergencyCount        int64         // IPs classified as emergency
	PendingCount          int64         // IPs currently pending
	TrackedIPs            int64         // Currently tracked IPs in state map
	LastTickDuration      time.Duration // Duration of last tick
	LastTickTime          time.Time     // When last tick completed
	PressureMode          bool          // Whether currently in pressure mode
	VerifyEnqueued        int64         // IPs enqueued for FCrDNS verification
	VerifyCompleted       int64         // FCrDNS verifications completed
	VerifyVerified        int64         // IPs that passed FCrDNS
	VerifyFailed          int64         // IPs that failed FCrDNS
	BatchSignalsProcessed int64         // Batch signals consumed from Clock 3
}

GuardStats holds runtime statistics for the bot guard module.

type IPRecord

type IPRecord struct {
	IP           netip.Addr   // Parsed IP address (IPv4 or IPv6)
	State        IPState      // Current classification
	Score        int32        // Accumulated threat score
	FirstSeen    time.Time    // When first detected as suspect
	LastSeen     time.Time    // Last time seen in suspect set
	ExpiresAt    time.Time    // When current state expires
	HitCount     int64        // Number of times seen in suspect set
	Reasons      []string     // Why this IP was flagged (for logging)
	BotName      string       // Identified bot name (empty if unknown)
	VerifiedAt   time.Time    // When FCrDNS verification completed (zero if not verified)
	VerifyStatus VerifyStatus // Result of FCrDNS verification
}

IPRecord tracks the classification state and history for one IP.

func (*IPRecord) Expired

func (r *IPRecord) Expired() bool

Expired returns true if the record has expired.

func (*IPRecord) IsIPv6

func (r *IPRecord) IsIPv6() bool

IsIPv6 returns true if this record tracks an IPv6 address.

type IPState

type IPState int

IPState represents the classification state for an IP address. State machine: UNKNOWN → PENDING → ALLOW|GREY|BAN|EMERGENCY

const (
	StateUnknown   IPState = iota // Not yet seen by Go classifier
	StatePending                  // Awaiting classification (light throttle)
	StateAllow                    // Verified allowed crawler (bypass throttle)
	StateGrey                     // Suspicious, throttled via penalty ladder
	StateBan                      // Denied/malicious bot (full drop)
	StateEmergency                // Emergency pressure block (immediate drop)
)

func (IPState) SetName

func (s IPState) SetName() string

SetName returns the nftables set name for this state (IPv4). Returns empty string for states that don't map to a set.

func (IPState) SetName6

func (s IPState) SetName6() string

SetName6 returns the nftables set name for this state (IPv6).

func (IPState) String

func (s IPState) String() string

String returns the human-readable state name.

type Logger added in v1.21.0

type Logger struct {
	// contains filtered or unexported fields
}

Logger writes structured log entries for the bot guard module. Thread-safe: all writes are mutex-protected.

func NewLogger added in v1.21.0

func NewLogger(logDir string) (*Logger, error)

NewLogger creates a new file logger for botguard. Creates the log directory if it doesn't exist.

func (*Logger) Close added in v1.21.0

func (l *Logger) Close() error

Close closes both log files.

func (*Logger) LogClassification added in v1.21.0

func (l *Logger) LogClassification(ip string, oldState, newState IPState, reason, botName string, hitCount int64)

LogClassification logs a state transition to botguard.log. Format: TIMESTAMP|IP|OLD_STATE|NEW_STATE|REASON|BOT_NAME|HIT_COUNT

func (*Logger) LogDecision added in v1.21.0

func (l *Logger) LogDecision(ip string, state IPState, reason, botName string, verifyStatus VerifyStatus, hostname string)

LogDecision logs a verification-based decision to decisions.log. Format: TIMESTAMP|IP|STATE|REASON|BOT_NAME|VERIFY_STATUS|HOSTNAME

func (*Logger) LogError added in v1.21.0

func (l *Logger) LogError(context string, err error)

LogError logs an error to botguard.log.

func (*Logger) LogEvent added in v1.21.0

func (l *Logger) LogEvent(level, message string)

LogEvent logs a general event to botguard.log.

type Module

type Module struct {
	// contains filtered or unexported fields
}

Module implements the bot guard module (module.Module interface).

func New

func New() *Module

New creates a new bot guard module.

func (*Module) GetIPRecord

func (m *Module) GetIPRecord(ip netip.Addr) (*IPRecord, bool)

GetIPRecord returns the current record for an IP (for CLI status queries).

func (*Module) GetStats

func (m *Module) GetStats() GuardStats

GetStats returns a copy of the current statistics.

func (*Module) Init

func (m *Module) Init(bus *eventbus.Bus) error

Init initializes the module with the event bus.

func (*Module) InitEnforcer

func (m *Module) InitEnforcer(queue *opqueue.OpQueue)

InitEnforcer sets up the enforcer with the OpQueue. Called after daemon creates the OpQueue.

func (*Module) Name

func (m *Module) Name() string

Name returns the module identifier.

func (*Module) Start

func (m *Module) Start(ctx context.Context) error

Start begins the classification loop.

func (*Module) Status

func (m *Module) Status() module.Status

Status returns the current module status.

func (*Module) Stop

func (m *Module) Stop() error

Stop gracefully shuts down the module.

type SuspectReader

type SuspectReader struct {
	// contains filtered or unexported fields
}

SuspectReader reads kernel-populated suspect sets.

func NewSuspectReader

func NewSuspectReader() *SuspectReader

NewSuspectReader creates a reader for the kernel suspect sets.

func (*SuspectReader) ReadSuspects

func (r *SuspectReader) ReadSuspects(ctx context.Context) ([]netip.Addr, error)

ReadSuspects reads both IPv4 and IPv6 suspect sets. Returns a combined list of suspect IPs (typically 20-100 entries). This operation completes in microseconds since the sets are small.

type VerifyResult added in v1.21.0

type VerifyResult struct {
	IP       netip.Addr   // The IP that was verified
	Status   VerifyStatus // Verification outcome
	BotName  string       // Matched bot name (empty if not matched)
	Hostname string       // Reverse DNS hostname (empty if lookup failed)
}

VerifyResult holds the outcome of an FCrDNS verification.

type VerifyStatus added in v1.21.0

type VerifyStatus int

VerifyStatus represents the result of an FCrDNS verification.

const (
	VerifyNone     VerifyStatus = iota // Not yet verified
	VerifyVerified                     // FCrDNS confirmed: rDNS matches allowed domain + fwd confirms
	VerifyFailed                       // FCrDNS failed: rDNS does not match or fwd mismatch
	VerifyTimeout                      // DNS lookup timed out
	VerifyNoRDNS                       // No reverse DNS record found
)

func (VerifyStatus) String added in v1.21.0

func (v VerifyStatus) String() string

String returns the human-readable verify status name.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL