firewall

package
v0.0.0-...-c292553 Latest Latest
Warning

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

Go to latest
Published: Aug 2, 2026 License: AGPL-3.0 Imports: 24 Imported by: 0

Documentation

Index

Constants

View Source
const (
	SourceUnknown      = "unknown"
	SourceWebUI        = "web_ui"
	SourceCLI          = "cli"
	SourceAutoResponse = "auto_response"
	SourceChallenge    = "challenge"
	SourceWhitelist    = "whitelist"
	SourceDynDNS       = "dyndns"
	SourceSystem       = "system"
)

Variables

View Source
var ErrIPProtected = errors.New("ip is protected from auto-block")

ErrIPProtected wraps the guard rejections for IPs that automated firewall actions must never block: the host's own interface addresses and operator infra_ips. Callers check errors.Is(err, ErrIPProtected) to treat the refusal as an expected no-op; they neither record a block nor log it as a failure. The triggering finding or incident is unaffected, so suspicious activity attributed to a protected address is still surfaced.

Functions

func AppendAudit

func AppendAudit(statePath, action, ip, reason, source string, duration time.Duration)

AppendAudit writes an audit entry to the JSONL audit log. Rotates the log when it exceeds 10 MB.

func EffectiveDOSExemptNets

func EffectiveDOSExemptNets(cfg *FirewallConfig, providerNets []*net.IPNet) (v4, v6 []*net.IPNet)

EffectiveDOSExemptNets returns the union of operator ranges and (when enabled) provider ranges, split into IPv4 and IPv6 *net.IPNet slices. providerNets may be nil. Invalid operator entries are skipped (validation already rejected them at load; this is defense in depth).

func FetchCloudflareIPs

func FetchCloudflareIPs() (ipv4, ipv6 []string, err error)

FetchCloudflareIPs downloads the current Cloudflare IP ranges.

func InferProvenance

func InferProvenance(action, reason string) string

InferProvenance classifies a firewall entry source from structured action/reason text. This keeps provenance logic centralized instead of spreading fragile string checks throughout the web UI and firewall call sites.

func LoadCFRefreshTime

func LoadCFRefreshTime(statePath string) time.Time

LoadCFRefreshTime reads the last CF refresh time from state.

func LoadCFState

func LoadCFState(statePath string) (ipv4, ipv6 []string)

LoadCFState reads the cached Cloudflare CIDRs.

func LookupIP

func LookupIP(dbPath string, ip string) []string

LookupIP finds which country CIDR files contain the given IP. Returns matching country codes.

func SaveCFState

func SaveCFState(statePath string, ipv4, ipv6 []string, refreshed time.Time)

SaveCFState persists the Cloudflare CIDRs for status display.

func UpdateGeoIPDB

func UpdateGeoIPDB(dbPath string, countryCodes []string) (int, error)

UpdateGeoIPDB downloads country CIDR lists from a public source. Creates one file per country code per family: {dbPath}/{CC}.cidr (IPv4) and {dbPath}/{CC}.cidr6 (IPv6). IPv6 is best-effort so a country with no v6 allocation does not fail the update. The return value is the number of CIDR files refreshed.

Types

type AllowedEntry

type AllowedEntry struct {
	IP        string    `json:"ip"`
	Reason    string    `json:"reason"`
	Source    string    `json:"source,omitempty"`
	Port      int       `json:"port,omitempty"`       // 0 = all ports
	ExpiresAt time.Time `json:"expires_at,omitempty"` // zero = permanent
}

AllowedEntry represents an allowed IP with metadata.

type AuditEntry

type AuditEntry struct {
	Timestamp time.Time `json:"timestamp"`
	Action    string    `json:"action"` // block, unblock, allow, remove_allow, flush, apply
	IP        string    `json:"ip,omitempty"`
	Reason    string    `json:"reason,omitempty"`
	Source    string    `json:"source,omitempty"`
	Duration  string    `json:"duration,omitempty"`
}

AuditEntry records a firewall modification for compliance and forensics.

func ReadAuditLog

func ReadAuditLog(statePath string, limit int) []AuditEntry

ReadAuditLog returns the last N audit entries from the log.

type BlockOutcome

type BlockOutcome string

BlockOutcome reports what the firewall engine actually did in response to a BlockIPOutcome call. Auto-response callers consult it to decide whether to apply local side effects (state.IPs append, BlocksThisHour++, permanent threat-db insert, AUTO-BLOCK finding) - those should only fire when the kernel firewall was mutated.

Returned values:

  • BlockOutcomeLive: nftables was mutated. Caller should record the block locally and emit the operator-facing Critical "AUTO-BLOCK" finding.
  • BlockOutcomeDryRun: auto_response.dry_run intercepted the call. No kernel mutation occurred. Caller should NOT record a real block; emit a Warning-level dry-run notice instead so operators can see what would have been blocked without believing the block landed.
  • BlockOutcomeAllowed: the verdict callback returned "allow", so CSM intentionally did not block. Caller should NOT record a block and should NOT emit an AUTO-BLOCK finding (the panel already knows it downgraded the decision).
  • BlockOutcomeAllowlisted: the IP is on a soft-allow list (operator allowed_ips, a port-specific operator allow, or a verified-bot range), so the auto-block path declined to block it. The nftables input chain drops @blocked_ips before it accepts operator allows, so an allowlisted IP added to blocked_ips would still be dropped; keeping it out of the set is the only safe fix. Operator `firewall deny` uses BlockIPForce, which bypasses this gate, so an explicit deny still wins. Caller should NOT record a block.
  • BlockOutcomeNoop: the IP was already blocked, or a guard (infra-IP, malformed IP, deny-limit) rejected the call. Caller should treat the call as a no-op locally.
const (
	BlockOutcomeLive        BlockOutcome = "live"
	BlockOutcomeDryRun      BlockOutcome = "dry_run"
	BlockOutcomeAllowed     BlockOutcome = "allowed"
	BlockOutcomeAllowlisted BlockOutcome = "allowlisted"
	BlockOutcomeNoop        BlockOutcome = "noop"
)

type BlockedEntry

type BlockedEntry struct {
	IP        string    `json:"ip"`
	Reason    string    `json:"reason"`
	Source    string    `json:"source,omitempty"`
	BlockedAt time.Time `json:"blocked_at"`
	ExpiresAt time.Time `json:"expires_at"` // zero = permanent
}

BlockedEntry represents a blocked IP with metadata.

type DynDNSResolver

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

DynDNSResolver periodically resolves hostnames and updates the firewall allowed set.

func NewDynDNSResolver

func NewDynDNSResolver(hosts []string, engine interface {
	AllowIP(ip string, reason string) error
	RemoveAllowIPBySource(ip string, source string) error
}) *DynDNSResolver

NewDynDNSResolver creates a resolver for the given hostnames.

func (*DynDNSResolver) AddHost

func (d *DynDNSResolver) AddHost(host string)

AddHost appends a hostname to the resolver's host list. It is safe to call concurrently. Used in tests to add hosts after construction.

func (*DynDNSResolver) RegisterInfraHost

func (d *DynDNSResolver) RegisterInfraHost(host string)

RegisterInfraHost marks host as an infra hostname. Every subsequent successful resolution will, in addition to AllowIP, call engine.UpdateInfraResolved so the resolved IPs feed the infra-block guard. Idempotent. Wire an infra engine via SetInfraEngine before the resolver's first tick to make this effective.

func (*DynDNSResolver) Run

func (d *DynDNSResolver) Run(stopCh <-chan struct{})

Run starts the periodic resolver. Blocks until stopCh is closed.

func (*DynDNSResolver) SetFindingSink

func (d *DynDNSResolver) SetFindingSink(sink func(host string))

SetFindingSink installs the callback invoked when a host has been unresolvable for longer than gracePeriod. Called from the daemon at startup, after the alert pipeline is wired.

func (*DynDNSResolver) SetInfraEngine

func (d *DynDNSResolver) SetInfraEngine(eng interface {
	UpdateInfraResolved(host string, ips []string)
	DropInfraResolved(host string)
})

SetInfraEngine wires the engine that receives infra-mode resolution updates. Setting it to nil disables infra routing without affecting the regular allowed-IPs path.

func (*DynDNSResolver) UnresolvableHosts

func (d *DynDNSResolver) UnresolvableHosts() []string

UnresolvableHosts lists infra_ips hostnames currently failing to resolve beyond the grace period.

type Engine

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

Engine manages the nftables firewall ruleset. Manages the nftables ruleset via netlink.

func ConnectExisting

func ConnectExisting(cfg *FirewallConfig, statePath string) (*Engine, error)

ConnectExisting connects to an already-running CSM firewall. Used by CLI commands to modify the live ruleset without reapplying all rules.

func NewEngine

func NewEngine(cfg *FirewallConfig, statePath string) (*Engine, error)

NewEngine creates a new nftables firewall engine.

func (*Engine) AllowIP

func (e *Engine) AllowIP(ip string, reason string) error

AllowIP adds an IP to the allowed set and persists it. If the IP is currently blocked, the block is removed first.

func (*Engine) AllowIPPort

func (e *Engine) AllowIPPort(ip string, port int, proto string, reason string) error

AllowIPPort adds a port-specific IP allow. The rule is persisted to state and applied on the next Apply(). For immediate effect, call Apply() after.

func (*Engine) Apply

func (e *Engine) Apply() error

Apply builds and atomically applies the complete nftables ruleset. All operations (delete old table + create new table/rules + populate persisted block/allow entries) are batched into a single netlink transaction. If the flush fails, the kernel keeps whatever ruleset was running before - the server is never left without a firewall. Equally important: the new ruleset never appears with EMPTY blocked sets between the table-swap and the persisted-state load; an attacker IP from state.json is blocked from the moment the new table becomes the live one.

func (*Engine) BlockIP

func (e *Engine) BlockIP(ip string, reason string, timeout time.Duration) error

BlockIP adds an IP to the blocked set with optional timeout. timeout 0 = permanent block.

Thin wrapper over BlockIPOutcome that discards the outcome. Existing callers that only need success/error semantics keep working; auto- response callers should use BlockIPOutcome so they can suppress local side effects (state mutation, AUTO-BLOCK alert) when the kernel was not actually touched.

func (*Engine) BlockIPForce

func (e *Engine) BlockIPForce(ip string, reason string, timeout time.Duration) error

BlockIPForce adds an IP to the blocked set unconditionally, bypassing the auto_response.dry_run gate. Use this for operator-initiated commands (CLI, Web UI manual block) where the operator has explicitly decided to block.

func (*Engine) BlockIPOutcome

func (e *Engine) BlockIPOutcome(ip string, reason string, timeout time.Duration) (BlockOutcome, error)

BlockIPOutcome is the AUTO-RESPONSE entry point. It performs the same guards, verdict-callback consultation, and dry-run gating as BlockIP, but additionally reports which path was taken via BlockOutcome so the caller can decide whether to record local state. See the BlockOutcome godoc for the meaning of each return value.

Operator-initiated commands (csm firewall block, Web UI manual block) must call BlockIPForce instead, which skips the dry-run gate unconditionally.

func (*Engine) BlockSubnet

func (e *Engine) BlockSubnet(cidr string, reason string, timeout time.Duration) error

BlockSubnet adds a CIDR range to the blocked subnets set (IPv4 or IPv6). timeout 0 = permanent block.

func (*Engine) BlockedCount

func (e *Engine) BlockedCount() int

BlockedCount returns the number of live blocked IP entries the engine is enforcing. Sourced from the same state file Status() uses, so `/api/v1/status` and `csm firewall status` agree on the number. Expired entries are pruned by loadStateFile before being counted.

func (*Engine) BlockedSubnetCovering

func (e *Engine) BlockedSubnetCovering(ip string) (string, bool)

BlockedSubnetCovering reports the blocked CIDR (if any) that contains ip. The input-chain drops blocked_nets before the allowed_ips accept, so an allow on an IP inside a blocked subnet has no effect: the subnet drop still fires. Callers surface this so an operator is not told an IP is reachable when a subnet rule still blocks it. The subnet block stays authoritative by design (see subnetSafetyGuardLocked); this only reports, it does not unblock.

func (*Engine) BlockedSubnets

func (e *Engine) BlockedSubnets() []SubnetEntry

BlockedSubnets returns a snapshot of the active persisted subnet blocks. The returned slice is a fresh copy: loadStateFile reuses the warm shared state cache, so the slice it returns can alias internal engine state. Copy it here so a caller mutating the result cannot corrupt the cache. SubnetEntry is a value type (strings + time.Time, no reference fields), so a slice copy is a sufficient deep copy.

func (*Engine) CleanExpiredAllows

func (e *Engine) CleanExpiredAllows() int

CleanExpiredAllows removes expired temporary allows from the set and state. An IP is only removed from nftables if no non-expired entries remain for it. Called periodically by the daemon.

func (*Engine) CleanExpiredSubnets

func (e *Engine) CleanExpiredSubnets() int

CleanExpiredSubnets removes expired temporary subnet blocks from nftables and state.

func (*Engine) CloudflareIPs

func (e *Engine) CloudflareIPs() (ipv4, ipv6 []string)

CloudflareIPs returns the currently configured Cloudflare CIDRs from the cached state.

func (*Engine) DropInfraResolved

func (e *Engine) DropInfraResolved(host string)

DropInfraResolved clears all resolved IPs for a host. Equivalent to UpdateInfraResolved(host, nil); separate name surfaces operator intent at call sites that purposefully retire a hostname.

func (*Engine) FlushBlocked

func (e *Engine) FlushBlocked() error

FlushBlocked removes all IPs from the blocked set and clears persisted state.

func (*Engine) IsAllowed

func (e *Engine) IsAllowed(ip string) bool

IsAllowed reports whether ip is on the operator allowed_ips set, read from the in-memory cache built from state.json. Mirrors IsBlocked, including the canonical-form retry for IPv4-mapped IPv6 callers.

func (*Engine) IsBlocked

func (e *Engine) IsBlocked(ip string) bool

IsBlocked returns true if the IP is currently in the engine's blocked state. Uses the persisted state file (which is cleaned of expired entries on load).

The lookup is O(1) via the blockedIPIndex map populated from the cached state. Linear scans over the parsed slice are gone -- on hosts with hundreds of persisted blocks the scan was the dominant cost of every connection-handler IsBlocked check.

func (*Engine) IsBlockedLive

func (e *Engine) IsBlockedLive(ip string) (bool, error)

IsBlockedLive queries the live nftables set, not the in-memory cache built from state.json. The cache can drift from the kernel when nft auto-expires entries faster than CSM rewrites state.json, or when an out-of-band flush happens. Reconcile loops should consult this method so the local tracker shrinks in lock-step with the kernel; per-packet hot paths should stay on IsBlocked since this issues a netlink RTT.

Malformed IPs are reported as absent. Netlink and engine-initialization failures are returned so callers can keep their cached answer instead of deleting local state on a transient lookup failure.

func (*Engine) IsSubnetBlocked

func (e *Engine) IsSubnetBlocked(cidr string) bool

IsSubnetBlocked returns true if the CIDR is present in the persisted subnet block state.

func (*Engine) PromoteToPermanentBlock

func (e *Engine) PromoteToPermanentBlock(ip, reason string) error

PromoteToPermanentBlock upgrades an existing temporary block on ip to a permanent one: it clears the kernel timeout by deleting the timed element and re-adding it without a timeout, and zeroes ExpiresAt in state. The ordinary block path cannot do this during PermBlock escalation because it skips an already-blocked IP, so the kernel timeout would otherwise expire the block the operator wanted made permanent. Returns an error if the IP is not currently blocked (nothing to promote).

func (*Engine) RefreshDOSExemptSets

func (e *Engine) RefreshDOSExemptSets(providerNets []*net.IPNet) error

RefreshDOSExemptSets repopulates the dos_exempt_nets[6] interval sets with a new provider overlay in a single batched kernel transaction. If the kernel batch fails the previous set contents remain active and dosExemptProviderNets is not updated, preserving the last-known good state.

func (*Engine) RemoveAllowIP

func (e *Engine) RemoveAllowIP(ip string) error

RemoveAllowIP removes an IP from the allowed set and state.

func (*Engine) RemoveAllowIPBySource

func (e *Engine) RemoveAllowIPBySource(ip, source string) error

RemoveAllowIPBySource removes only allow entries from a specific source. The IP is only removed from the nftables set if no other sources remain.

func (*Engine) RemoveAllowIPPort

func (e *Engine) RemoveAllowIPPort(ip string, port int, proto string) error

RemoveAllowIPPort removes a port-specific IP allow from state.

func (*Engine) RuleCounts

func (e *Engine) RuleCounts() RuleCounts

RuleCounts returns the cardinality of every firewall rule category from the engine state file with expired temp bans pruned. Callers needing a live count (e.g. Prometheus gauges) must use this rather than the bbolt store, which holds only the migration-time snapshot.

func (*Engine) SetDOSExemptProviderNets

func (e *Engine) SetDOSExemptProviderNets(nets []*net.IPNet)

SetDOSExemptProviderNets stores the mail-provider IP ranges used by the dos_exempt_nets set. The daemon calls this before Apply() and on each provider refresh. Nil is valid when the daemon has no provider data yet; createSets() will produce an empty set in that case.

func (*Engine) SetDryRunEnabledFunc

func (e *Engine) SetDryRunEnabledFunc(fn func() bool)

SetDryRunEnabledFunc installs the callback BlockIP uses to decide whether auto_response.dry_run should intercept an automatic block. Nil means live.

func (*Engine) SetDryRunRecorder

func (e *Engine) SetDryRunRecorder(fn func(ip, reason string, timeout time.Duration))

SetDryRunRecorder installs a callback that is invoked by BlockIP whenever auto_response.dry_run is active. The daemon calls this after construction to wire in store.RecordDryRunBlock without creating an import cycle between internal/firewall and internal/store.

func (*Engine) SetShutdownContext

func (e *Engine) SetShutdownContext(ctx context.Context)

SetShutdownContext installs a context whose cancellation aborts any in-flight verdict callback. The daemon ties this to its stopCh so a graceful shutdown does not have to wait for an unresponsive panel callback to return.

func (*Engine) SetSoftAllowChecker

func (e *Engine) SetSoftAllowChecker(fn func(ip string) bool)

SetSoftAllowChecker installs the callback the auto-block path consults to decide whether an IP belongs to a verified-bot range. Nil disables the verified-bot side of the soft-allow gate (operator allowed_ips is still honoured). The daemon wires this to threatintel so the firewall package stays free of that import.

func (*Engine) SetVerdictAsker

func (e *Engine) SetVerdictAsker(fn func(ctx context.Context, ip, reason string) (string, string, string, error))

SetVerdictAsker installs the verdict callback the daemon constructs at startup. Nil disables the verdict callback (the gate skips entirely).

func (*Engine) Status

func (e *Engine) Status() map[string]interface{}

Status returns current firewall statistics.

Takes e.mu so the cached state can be read coherently. Before the cache existed loadStateFile was lock-free because every call did its own ReadFile + Unmarshal; now that loadStateFile mutates the shared cache + index, the lock is required.

func (*Engine) TempAllowIP

func (e *Engine) TempAllowIP(ip string, reason string, timeout time.Duration) error

TempAllowIP adds a temporary allow with expiry. Uses the same allowed set but tracks expiry in state - CleanExpiredAllows removes them periodically.

func (*Engine) UnblockIP

func (e *Engine) UnblockIP(ip string) error

UnblockIP removes an IP from the blocked set and state.

func (*Engine) UnblockSubnet

func (e *Engine) UnblockSubnet(cidr string) error

UnblockSubnet removes a CIDR range from the blocked subnets set (IPv4 or IPv6).

func (*Engine) UpdateCloudflareSet

func (e *Engine) UpdateCloudflareSet(ipv4, ipv6 []string) error

UpdateCloudflareSet flushes and repopulates the Cloudflare nftables sets.

func (*Engine) UpdateInfraResolved

func (e *Engine) UpdateInfraResolved(host string, ips []string)

UpdateInfraResolved records the IP set last resolved for an infra hostname. Replaces any previous entry for that host so the resolver's per-tick refresh leaves no stale ghost IPs. Pass an empty ips slice to remove the host entirely (e.g. when DNS stopped resolving).

type FirewallConfig

type FirewallConfig struct {
	Enabled bool `yaml:"enabled"`

	// Open ports (IPv4)
	TCPIn  []int `yaml:"tcp_in"`
	TCPOut []int `yaml:"tcp_out"`
	UDPIn  []int `yaml:"udp_in"`
	UDPOut []int `yaml:"udp_out"`

	// IPv6 - enable dual-stack filtering
	IPv6    bool  `yaml:"ipv6"`
	TCP6In  []int `yaml:"tcp6_in"`  // if empty, uses tcp_in
	TCP6Out []int `yaml:"tcp6_out"` // if empty, uses tcp_out
	UDP6In  []int `yaml:"udp6_in"`  // if empty, uses udp_in
	UDP6Out []int `yaml:"udp6_out"` // if empty, uses udp_out

	// Ports restricted to infra IPs only
	RestrictedTCP []int `yaml:"restricted_tcp"`

	// Passive FTP range
	PassiveFTPStart int `yaml:"passive_ftp_start"`
	PassiveFTPEnd   int `yaml:"passive_ftp_end"`

	// Infra IPs (CIDR notation)
	InfraIPs []string `yaml:"infra_ips"`

	// Rate limiting (per-source nftables meters). SYN/conn-rate/UDP are
	// dual-stack (IPv6 keyed per /64); ConnLimit is IPv4-only (per-source
	// ct count uses nf_conncount, whose GC has a kernel UAF on the el8 kernel).
	ConnRateLimit      int  `yaml:"conn_rate_limit"` // new connections per minute per source (IPv6 per /64)
	SYNFloodProtection bool `yaml:"syn_flood_protection"`
	ConnLimit          int  `yaml:"conn_limit"` // max concurrent connections per IPv4 source, IPv4 only (0 = disabled)

	// Per-port flood protection - per-source rate limit per port and IP family.
	PortFlood []PortFloodRule `yaml:"port_flood"`

	// UDP flood protection - per-source rate limit on UDP packets (IPv6 per /64)
	UDPFlood      bool `yaml:"udp_flood"`
	UDPFloodRate  int  `yaml:"udp_flood_rate"`  // packets per second
	UDPFloodBurst int  `yaml:"udp_flood_burst"` // burst allowance

	// Country blocking
	CountryBlock  []string `yaml:"country_block"` // ISO country codes
	CountryDBPath string   `yaml:"country_db_path"`

	// Ports to drop silently without logging (reduces log noise from scanners)
	DropNoLog []int `yaml:"drop_nolog"`

	// Max blocked IPs (prevents memory exhaustion, 0 = unlimited)
	DenyIPLimit     int `yaml:"deny_ip_limit"`
	DenyTempIPLimit int `yaml:"deny_temp_ip_limit"`

	// Outbound SMTP restriction - block outgoing mail except from allowed users
	SMTPBlock      bool     `yaml:"smtp_block"`
	SMTPAllowUsers []string `yaml:"smtp_allow_users"` // usernames allowed to send
	SMTPPorts      []int    `yaml:"smtp_ports"`

	// Dynamic DNS - resolve hostnames to IPs, update allowed set periodically
	DynDNSHosts []string `yaml:"dyndns_hosts"`

	// Logging
	LogDropped bool `yaml:"log_dropped"`
	LogRate    int  `yaml:"log_rate"` // log entries per minute

	// DoS-exempt ranges - source CIDRs excluded from connection/mail-port
	// meters and auto-subnet escalation. Intended for shared-source ranges
	// such as carrier CGNAT blocks and well-known mail-provider egress.
	DOSExemptRanges             []string `yaml:"dos_exempt_ranges"`
	DOSExemptKnownMailProviders *bool    `yaml:"dos_exempt_known_mail_providers"`
}

FirewallConfig defines the nftables firewall configuration.

func DefaultConfig

func DefaultConfig() *FirewallConfig

DefaultConfig returns a sensible default firewall configuration matching a typical cPanel server.

func (*FirewallConfig) ExemptKnownMailProviders

func (c *FirewallConfig) ExemptKnownMailProviders() bool

ExemptKnownMailProviders reports whether bundled mail-provider ranges are included in the DoS-exempt set. Defaults to true when unset.

type FirewallState

type FirewallState struct {
	Blocked     []BlockedEntry   `json:"blocked"`
	BlockedNet  []SubnetEntry    `json:"blocked_nets"`
	Allowed     []AllowedEntry   `json:"allowed"`
	PortAllowed []PortAllowEntry `json:"port_allowed"`
}

FirewallState is persisted to disk for restore on restart.

func LoadState

func LoadState(statePath string) (*FirewallState, error)

LoadState reads the authoritative firewall state file directly without requiring a running engine. A missing state file is a valid fresh-host state and returns an empty FirewallState.

type PortAllowEntry

type PortAllowEntry struct {
	IP     string `json:"ip"`
	Port   int    `json:"port"`
	Proto  string `json:"proto"` // "tcp" or "udp"
	Reason string `json:"reason"`
	Source string `json:"source,omitempty"`
}

PortAllowEntry represents a port-specific IP allow (e.g. tcp|in|d=PORT|s=IP).

type PortFloodRule

type PortFloodRule struct {
	Port    int    `yaml:"port"`
	Proto   string `yaml:"proto"`   // "tcp" or "udp"
	Hits    int    `yaml:"hits"`    // max new connections
	Seconds int    `yaml:"seconds"` // time window in seconds
}

PortFloodRule defines per-port connection rate limiting.

type RuleCounts

type RuleCounts struct {
	Blocked     int
	Allowed     int
	Subnets     int
	PortAllowed int
}

RuleCounts holds firewall rule cardinalities sourced from the engine state file, which is the authoritative store. The parallel bbolt fw:* buckets are written only during migration, so anything counting live rules must read the engine, not the store. Expired temp bans are excluded.

func (RuleCounts) Total

func (c RuleCounts) Total() int

Total returns the sum across all rule categories.

type SubnetEntry

type SubnetEntry struct {
	CIDR      string    `json:"cidr"`
	Reason    string    `json:"reason"`
	Source    string    `json:"source,omitempty"`
	BlockedAt time.Time `json:"blocked_at"`
	ExpiresAt time.Time `json:"expires_at,omitempty"`
}

SubnetEntry represents a blocked CIDR range.

Directories

Path Synopsis
Package rollback implements the firewall settings tentative-apply workflow: a save with a deadline that auto-reverts unless the operator confirms before the timer expires.
Package rollback implements the firewall settings tentative-apply workflow: a save with a deadline that auto-reverts unless the operator confirms before the timer expires.

Jump to

Keyboard shortcuts

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