firewall

package
v0.22.169 Latest Latest
Warning

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

Go to latest
Published: May 25, 2026 License: MIT Imports: 6 Imported by: 0

Documentation

Overview

internal/firewall/backend.go — runtime.GOOS dispatcher.

Each platform contributes a `newBackend` constructor in its build-tagged file. The dispatcher picks the right one at compile-time based on the build tags, then DefaultBackend returns the constructed instance.

A package-level override (SetDefaultBackend) lets tests inject a fake without touching runtime.GOOS — used by the serve hook integration test to assert the warning flow without spawning PowerShell.

Package firewall — cross-platform host-firewall configuration for clawtool's LAN peer discovery surface.

The Windows defender firewall blocks inbound UDP 5353 (mDNS) and inbound TCP on the daemon port by default; without rules, two clawtool daemons on the same LAN cannot find each other even though both are dutifully announcing on multicast. Asking users to copy-paste `New-NetFirewallRule` from a doc is a UX failure — the operator's directive ("kullanıcılar beceremezler") is that the first run of `clawtool serve --listen 0.0.0.0 --allow-lan` installs the rules itself, with a single UAC prompt, default-on.

This package is the platform-agnostic interface. The Windows backend (`windows.go`) composes a PowerShell batch and elevates it via `Start-Process -Verb RunAs`. The Linux backend (`linux.go`) wraps `ufw allow` when ufw is active and no-ops otherwise. The macOS backend (`darwin.go`) is a no-op because pf is default-allow for unprivileged inbound ports and the Application Firewall already prompts the user once on first bind.

Lifecycle: `DefaultBackend()` returns the runtime.GOOS-specific implementation. Callers (the serve hook + the `clawtool firewall` subcommands) work against the Backend interface.

internal/firewall/linux.go — Linux backend.

Targets `ufw` (Uncomplicated Firewall) — the de-facto desktop Linux firewall on Ubuntu / Debian / Pop!_OS. When ufw is installed AND active, we add `ufw allow` rules for UDP 5353 + the daemon's TCP port. When ufw is absent OR inactive, we no-op: the host has no firewall gating inbound traffic, so there's nothing to configure.

iptables direct rules are intentionally out of scope. They cover the ~5% of advanced Linux desktops that disable ufw in favour of nftables / iptables-restore configs, and adding a rule via `iptables -A INPUT` on those systems would conflict with the existing rule chains. Operators on that path know their setup well enough to add the rules themselves.

internal/firewall/runner.go — shared exec wrapper used by the per-platform backends. Lives in a non-build-tagged file so both linux.go and windows.go can reference commandRunner/defaultRunner without each redeclaring the same types.

Tests inject a fakeRunner via newLinuxBackendFor / newWindowsBackendFor; production callers use defaultRunner.

Index

Constants

This section is empty.

Variables

View Source
var ErrElevationDeclined = errors.New("firewall: elevation declined")

ErrElevationDeclined is returned by AddRules when the user declined the platform's elevation prompt (UAC "No" on Windows, `sudo -n` failure on Linux). Callers should treat this as a non-fatal degraded state: the daemon still starts, just without the LAN-reachable surface.

View Source
var ErrNotSupported = errors.New("firewall: backend not supported on this platform")

ErrNotSupported is returned by backends that have no implementation for the current platform (e.g. exotic Unix distros without ufw + without root). Callers should treat this as a non-fatal "no-op" — there's nothing to configure.

Functions

func SetDefaultBackend

func SetDefaultBackend(b Backend)

SetDefaultBackend installs a backend override. Tests use this to inject a fake; pass nil to restore the platform default.

Types

type AddOptions

type AddOptions struct {
	// Elevated, when true, asks the backend to perform the rule
	// install with administrative privileges (Windows: UAC prompt
	// via Start-Process -Verb RunAs; Linux: sudo). When false,
	// the backend may still elevate if the platform requires it
	// — the flag is a hint, not a guarantee.
	Elevated bool
}

AddOptions controls the AddRules call.

type AddResult

type AddResult struct {
	// AddedRules lists the Rule.Name values that were freshly
	// created. Already-present rules are skipped (idempotent
	// install) and do NOT appear here.
	AddedRules []string

	// SkippedRules lists rules that were already present at the
	// requested name. Reported separately so the CLI can show a
	// neutral "already configured" message instead of a misleading
	// "added" success.
	SkippedRules []string
}

AddResult reports what the backend did during AddRules.

type Backend

type Backend interface {
	// Detect probes the host firewall and reports whether
	// clawtool needs to install rules.
	Detect() (BackendState, error)

	// HasRule returns true when a rule with the given name is
	// currently installed on the host firewall.
	HasRule(name string) (bool, error)

	// AddRules installs every rule in `rules` that isn't already
	// present. Idempotent: already-present rules are reported in
	// SkippedRules, not re-added. Returns ErrElevationDeclined
	// when the platform required elevation and the user declined
	// (e.g. UAC "No" on Windows).
	AddRules(rules []Rule, opts AddOptions) (AddResult, error)

	// RemoveRules deletes the named rules. Missing rules are
	// silently skipped — RemoveRules is best-effort cleanup.
	RemoveRules(names []string) error

	// List returns every clawtool-* rule currently installed.
	// Used by `clawtool firewall status`. Returns an empty slice
	// (not nil) when no rules are present.
	List() ([]Rule, error)
}

Backend is the platform-specific implementation surface.

func DefaultBackend

func DefaultBackend() Backend

DefaultBackend returns the firewall backend for the current platform. Tests can override via SetDefaultBackend.

type BackendState

type BackendState struct {
	// Active is true when the platform's firewall is enabled
	// and would block unknown inbound traffic. False on macOS
	// (default-allow pf) and on Linux when ufw is inactive.
	Active bool

	// NeedsRules is true when Active is true AND at least one of
	// the expected clawtool rules is missing. The serve auto-hook
	// keys off this — false skips the UAC prompt entirely.
	NeedsRules bool

	// Reason is a short human-readable explanation of the state,
	// surfaced by `clawtool firewall status`.
	Reason string
}

BackendState is the result of Detect — describes whether the host firewall is actively gating inbound traffic and whether clawtool's rules are already present.

type Rule

type Rule struct {
	// Name is the canonical clawtool-* identifier the backend
	// uses to look the rule up later. Must be unique across
	// clawtool rules; the `clawtool firewall remove` path treats
	// every rule with the `clawtool-` prefix as in scope.
	Name string

	// Description is a human-readable string shown in the
	// platform's firewall UI. Optional.
	Description string

	// Protocol is "TCP" or "UDP". Other values are rejected at
	// AddRules time.
	Protocol string

	// LocalPort is the inbound port the rule allows. Always
	// >0; zero means "rule is malformed".
	LocalPort int

	// RemoteCIDR is the source CIDR allowed to reach LocalPort.
	// Empty string means "any source" (UDP 5353 mDNS). For the
	// daemon TCP rule the backend fills in the detected LAN
	// range (e.g. 192.168.1.0/24) so the listener isn't reachable
	// from arbitrary internet sources.
	RemoteCIDR string

	// Profile is the Windows firewall profile selector
	// ("Private", "Domain", or "Private,Domain"). Ignored on
	// non-Windows backends. Public is intentionally excluded by
	// default — clawtool's LAN surface shouldn't fire on a
	// coffee-shop Wi-Fi the operator marked Public.
	Profile string
}

Rule describes a single inbound firewall rule. Platforms map this to their native primitive (Windows: New-NetFirewallRule; Linux ufw: ufw allow; macOS: no-op).

func DefaultRules

func DefaultRules(port int, lanCIDR string) []Rule

DefaultRules returns the canonical rule set for a given daemon port + detected LAN CIDR. Both the serve auto-hook and the manual `clawtool firewall add` subcommand call this so the rule list stays consistent.

func GatewayRule

func GatewayRule(port int, lanCIDR string) Rule

GatewayRule returns the canonical clawtool HTTP gateway rule for the given port + LAN CIDR. Port is the daemon's bound TCP port; lanCIDR scopes the rule to the operator's local network (auto-detected by the Windows backend, manually overridable via the `--lan-cidr` CLI flag).

func MDNSRule

func MDNSRule() Rule

MDNSRule returns the canonical clawtool mDNS-inbound rule description. Centralised so the serve auto-hook + the manual `clawtool firewall add` path stay in sync.

Jump to

Keyboard shortcuts

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