extfw

package
v1.122.0 Latest Latest
Warning

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

Go to latest
Published: May 19, 2026 License: MPL-2.0 Imports: 3 Imported by: 0

Documentation

Overview

============================================================================= NFTBan v1.100 PR-P2-2 — External-Firewall Detection (Unified) ============================================================================= SPDX-License-Identifier: MPL-2.0 meta:name="installer-extfw-detect" meta:type="lib" meta:owner="Antonios Voulvoulis <contact@nftban.com>" meta:created_date="2026-04-20" meta:description="Single source of truth for external-firewall detection across install/update/uninstall" meta:inventory.files="internal/installer/extfw/detect.go" meta:inventory.binaries="" meta:inventory.env_vars="" meta:inventory.config_files="" meta:inventory.systemd_units="" meta:inventory.network="" meta:inventory.privileges="root" =============================================================================

Before PR-P2-2, external-firewall detection lived in two separate surfaces that disagreed on:

  • which systemd unit name to probe for UFW (bare vs .service)
  • whether to check /etc/csf/csf.conf (uninstall yes, install no)
  • whether ghost nft tables and iptables-save rules count
  • in what precedence order to report them

This divergence is exactly the class of drift that makes PR-23 (uninstall mutation) and PR-24 (restore enforcement) unsafe: two lifecycle modes could see different "ground truth" for the same host. PR-P2-2 unifies the detection layer so every caller gets the same answer from the same mocked or live host state.

Locked precedence (frozen 2026-04-20): ufw → firewalld → iptables → csf. This order is used ONLY when collapsing to a single Authoritative answer; when multiple firewalls are observed active simultaneously, the result is Ambiguous and the caller is responsible for handling that state explicitly. No silent collapse to "whoever wins by precedence" is ever performed for multi-active hosts.

Signal set (union of all previously-used signals — no new heuristics):

UFW:       ufw.service active
Firewalld: firewalld.service active, OR a ghost nft table whose
           name contains "firewalld"
Iptables:  iptables.service active OR iptables-save reports ≥3
           non-comment rule lines. A ghost nft table named
           filter/nat/mangle is RECORDED as an observation but
           does NOT classify iptables authority alone — it must
           be corroborated by service or live rules. See
           PR-P2-2A rationale in the iptables block below.
CSF:       csf.service active, OR lfd.service active, OR
           /etc/csf/csf.conf present on disk

No DNS checks, no process scraping, no heuristic expansion.

=============================================================================

Index

Constants

This section is empty.

Variables

Precedence is the frozen order used to pick a single authoritative external firewall when exactly ONE is observed active. When more than one is active, the detector returns Ambiguous and does NOT apply this precedence — the caller must handle ambiguity explicitly.

Contract lock (PR-P2-2, 2026-04-20): this order is authoritative and changes to it require a new Phase 2 PR with explicit contract update.

Functions

This section is empty.

Types

type DetectionResult

type DetectionResult struct {
	// Observations is every signal that fired during detection, in
	// detection order. Multiple observations for the same Name are
	// possible and expected (e.g. CSF service + config file).
	Observations []Observation `json:"observations,omitempty"`

	// Active is the deduplicated list of Names that were observed
	// active, in locked Precedence order.
	Active []Name `json:"active,omitempty"`

	// Authoritative is the single firewall considered to own external
	// authority. It is populated ONLY when exactly one Name appears in
	// Active. When zero or multiple are active, this is NameNone and
	// the caller must consult Ambiguous / Active directly.
	Authoritative Name `json:"authoritative,omitempty"`

	// Ambiguous is true when more than one distinct firewall was
	// observed active. The detector does NOT collapse this to the
	// precedence winner; callers must handle ambiguity explicitly per
	// the lifecycle-truth rule.
	Ambiguous bool `json:"ambiguous"`
}

DetectionResult is the full observation picture. Consumers that need a single authoritative answer use Authoritative; consumers that need the complete conflict list (e.g. DisableConflicts) use Observations.

func Detect

func Detect(exec executor.Executor, log *logging.Logger) *DetectionResult

Detect is the CANONICAL shared external-firewall detection function. Install-side authority classification, uninstall-side plan building, and any future consumer must call this function rather than re-implementing detection.

Read-only: no mutation, no writes, no service changes.

type Name

type Name string

Name identifies one of the recognized external firewalls. Values are deliberately lowercase so they can be used verbatim as JSON / plan-render strings; callers that need an operator-facing display name (e.g. "CSF" instead of "csf") use Observation.DisplayName().

const (
	NameNone      Name = ""
	NameUFW       Name = "ufw"
	NameFirewalld Name = "firewalld"
	NameIptables  Name = "iptables"
	NameCSF       Name = "csf"
)

type Observation

type Observation struct {
	Name   Name   `json:"name"`
	Source Source `json:"source"`
	// Unit is the systemd unit name for SourceService observations,
	// empty for other sources.
	Unit string `json:"unit,omitempty"`
	// Detail carries a short human string for logs/notes. NEVER depended
	// on for program behavior.
	Detail string `json:"detail,omitempty"`
}

Observation is one signal that a firewall was detected. A single firewall may produce multiple observations (e.g. CSF with both csf.service and /etc/csf/csf.conf). Callers that need the original unit name (e.g. DisableConflicts) read Observation.Unit.

func (Observation) DisplayName

func (o Observation) DisplayName() string

DisplayName returns the operator-facing name for an observation, preserving the legacy display semantics of install-side detection:

  • "UFW", "CSF" uppercase
  • "firewalld", "iptables" lowercase
  • ghost-nft-table iptables observations render as "iptables-nft" so existing consumers that distinguished that source continue to see the same string

type Source

type Source string

Source enumerates why a given firewall was observed. Kept as an explicit type (not free-form string) so downstream consumers can match on the source without string parsing.

const (
	SourceService       Source = "service"         // systemd unit is active
	SourceGhostTable    Source = "ghost_nft_table" // nft-owned table that is not an nftban-owned table
	SourceConfigFile    Source = "config_file"     // known config path is present
	SourceIptablesRules Source = "iptables_rules"  // iptables-save reports live rules
)

Jump to

Keyboard shortcuts

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