Documentation
¶
Index ¶
- Constants
- Variables
- func AppendAudit(statePath, action, ip, reason, source string, duration time.Duration)
- func EffectiveDOSExemptNets(cfg *FirewallConfig, providerNets []*net.IPNet) (v4, v6 []*net.IPNet)
- func FetchCloudflareIPs() (ipv4, ipv6 []string, err error)
- func InferProvenance(action, reason string) string
- func LoadCFRefreshTime(statePath string) time.Time
- func LoadCFState(statePath string) (ipv4, ipv6 []string)
- func LookupIP(dbPath string, ip string) []string
- func SaveCFState(statePath string, ipv4, ipv6 []string, refreshed time.Time)
- func UpdateGeoIPDB(dbPath string, countryCodes []string) (int, error)
- type AllowedEntry
- type AuditEntry
- type BlockOutcome
- type BlockedEntry
- type DynDNSResolver
- func (d *DynDNSResolver) AddHost(host string)
- func (d *DynDNSResolver) RegisterInfraHost(host string)
- func (d *DynDNSResolver) Run(stopCh <-chan struct{})
- func (d *DynDNSResolver) SetFindingSink(sink func(host string))
- func (d *DynDNSResolver) SetInfraEngine(eng interface{ ... })
- func (d *DynDNSResolver) UnresolvableHosts() []string
- type Engine
- func (e *Engine) AllowIP(ip string, reason string) error
- func (e *Engine) AllowIPPort(ip string, port int, proto string, reason string) error
- func (e *Engine) Apply() error
- func (e *Engine) BlockIP(ip string, reason string, timeout time.Duration) error
- func (e *Engine) BlockIPForce(ip string, reason string, timeout time.Duration) error
- func (e *Engine) BlockIPOutcome(ip string, reason string, timeout time.Duration) (BlockOutcome, error)
- func (e *Engine) BlockSubnet(cidr string, reason string, timeout time.Duration) error
- func (e *Engine) BlockedCount() int
- func (e *Engine) BlockedSubnetCovering(ip string) (string, bool)
- func (e *Engine) BlockedSubnets() []SubnetEntry
- func (e *Engine) CleanExpiredAllows() int
- func (e *Engine) CleanExpiredSubnets() int
- func (e *Engine) CloudflareIPs() (ipv4, ipv6 []string)
- func (e *Engine) DropInfraResolved(host string)
- func (e *Engine) FlushBlocked() error
- func (e *Engine) IsAllowed(ip string) bool
- func (e *Engine) IsBlocked(ip string) bool
- func (e *Engine) IsBlockedLive(ip string) (bool, error)
- func (e *Engine) IsSubnetBlocked(cidr string) bool
- func (e *Engine) PromoteToPermanentBlock(ip, reason string) error
- func (e *Engine) RefreshDOSExemptSets(providerNets []*net.IPNet) error
- func (e *Engine) RemoveAllowIP(ip string) error
- func (e *Engine) RemoveAllowIPBySource(ip, source string) error
- func (e *Engine) RemoveAllowIPPort(ip string, port int, proto string) error
- func (e *Engine) RuleCounts() RuleCounts
- func (e *Engine) SetDOSExemptProviderNets(nets []*net.IPNet)
- func (e *Engine) SetDryRunEnabledFunc(fn func() bool)
- func (e *Engine) SetDryRunRecorder(fn func(ip, reason string, timeout time.Duration))
- func (e *Engine) SetShutdownContext(ctx context.Context)
- func (e *Engine) SetSoftAllowChecker(fn func(ip string) bool)
- func (e *Engine) SetVerdictAsker(...)
- func (e *Engine) Status() map[string]interface{}
- func (e *Engine) TempAllowIP(ip string, reason string, timeout time.Duration) error
- func (e *Engine) UnblockIP(ip string) error
- func (e *Engine) UnblockSubnet(cidr string) error
- func (e *Engine) UpdateCloudflareSet(ipv4, ipv6 []string) error
- func (e *Engine) UpdateInfraResolved(host string, ips []string)
- type FirewallConfig
- type FirewallState
- type PortAllowEntry
- type PortFloodRule
- type RuleCounts
- type SubnetEntry
Constants ¶
const ( SourceUnknown = "unknown" SourceWebUI = "web_ui" SourceCLI = "cli" SourceAutoResponse = "auto_response" SourceChallenge = "challenge" SourceWhitelist = "whitelist" SourceDynDNS = "dyndns" SourceSystem = "system" )
Variables ¶
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 ¶
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 ¶
FetchCloudflareIPs downloads the current Cloudflare IP ranges.
func InferProvenance ¶
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 ¶
LoadCFRefreshTime reads the last CF refresh time from state.
func LoadCFState ¶
LoadCFState reads the cached Cloudflare CIDRs.
func LookupIP ¶
LookupIP finds which country CIDR files contain the given IP. Returns matching country codes.
func SaveCFState ¶
SaveCFState persists the Cloudflare CIDRs for status display.
func UpdateGeoIPDB ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
BlockSubnet adds a CIDR range to the blocked subnets set (IPv4 or IPv6). timeout 0 = permanent block.
func (*Engine) BlockedCount ¶
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 ¶
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 ¶
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 ¶
CleanExpiredSubnets removes expired temporary subnet blocks from nftables and state.
func (*Engine) CloudflareIPs ¶
CloudflareIPs returns the currently configured Cloudflare CIDRs from the cached state.
func (*Engine) DropInfraResolved ¶
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 ¶
FlushBlocked removes all IPs from the blocked set and clears persisted state.
func (*Engine) IsAllowed ¶
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 ¶
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 ¶
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 ¶
IsSubnetBlocked returns true if the CIDR is present in the persisted subnet block state.
func (*Engine) PromoteToPermanentBlock ¶
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 ¶
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 ¶
RemoveAllowIP removes an IP from the allowed set and state.
func (*Engine) RemoveAllowIPBySource ¶
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 ¶
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 ¶
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 ¶
SetDryRunEnabledFunc installs the callback BlockIP uses to decide whether auto_response.dry_run should intercept an automatic block. Nil means live.
func (*Engine) SetDryRunRecorder ¶
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 ¶
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 ¶
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 ¶
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 ¶
TempAllowIP adds a temporary allow with expiry. Uses the same allowed set but tracks expiry in state - CleanExpiredAllows removes them periodically.
func (*Engine) UnblockSubnet ¶
UnblockSubnet removes a CIDR range from the blocked subnets set (IPv4 or IPv6).
func (*Engine) UpdateCloudflareSet ¶
UpdateCloudflareSet flushes and repopulates the Cloudflare nftables sets.
func (*Engine) UpdateInfraResolved ¶
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 ¶
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.
Source Files
¶
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. |