firewall

package
v0.19.0 Latest Latest
Warning

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

Go to latest
Published: Sep 1, 2026 License: Apache-2.0 Imports: 11 Imported by: 0

Documentation

Overview

Package firewall renders cargoship's backend-neutral firewall configuration onto whichever host firewall a node runs.

A Plan describes what the cluster needs open on one node: the addresses of its peers, the pod and service CIDRs, the ports from the inventory's `.host.ports`, and the rules from the inventory's `.host.firewall.rules`. A Backend translates that Plan into one firewall's own dialect and applies it. Backends are matched to a node by Detect, so a single inventory can target a mix of firewalld, ufw, and nftables hosts.

Index

Constants

View Source
const (
	// FirewalldService is the name of the firewalld system service.
	FirewalldService = "firewalld"
)
View Source
const (
	// NftablesService is the name of the nftables system service.
	NftablesService = "nftables"
)
View Source
const (
	// UFWService is the name of the ufw system service.
	UFWService = "ufw"
)

Variables

This section is empty.

Functions

This section is empty.

Types

type Backend

type Backend interface {
	// Name is the backend identifier, e.g. "firewalld" or "ufw".
	Name() string
	// Detect is true when this backend manages the firewall on h. It runs commands on the
	// host, so callers should call it once per host and reuse the result.
	Detect(h *cluster.ZarfHost) bool
	// Apply makes the node's firewall match p, then reloads the firewall. It is
	// idempotent: applying the same plan twice leaves the node in the same state, and
	// rules cargoship applied on an earlier run that p no longer contains are removed.
	Apply(ctx context.Context, h *cluster.ZarfHost, p Plan) error
}

Backend applies a Plan to a node using one host firewall implementation.

func Backends

func Backends() []Backend

Backends returns the registered backends, in match order.

func For

func For(h *cluster.ZarfHost) Backend

For returns the backend that manages h's firewall, or nil when the node runs no firewall cargoship knows how to configure.

type Firewalld

type Firewalld struct{}

Firewalld applies a Plan to a node running firewalld.

func (*Firewalld) Apply

func (f *Firewalld) Apply(ctx context.Context, h *cluster.ZarfHost, p Plan) error

Apply writes the ipsets, service, and policy files for p, enables them, and restarts firewalld.

func (*Firewalld) Detect

func (f *Firewalld) Detect(h *cluster.ZarfHost) bool

Detect is true when firewalld is running on h.

func (*Firewalld) Name

func (f *Firewalld) Name() string

Name is the backend identifier.

type Nftables

type Nftables struct{}

Nftables applies a Plan to a node that manages its firewall with nftables directly, rather than through firewalld or ufw. It is the backend for hosts that never had either -- Debian and Arch nodes configured by hand, and the minimal or immutable images (CoreOS, Flatcar) that ship no firewall front end at all.

Everything cargoship writes lives in one table, `inet cargoship`, which is replaced whole in a single nft transaction on every apply. Cargoship never flushes the ruleset and never touches another table, because kube-proxy and every CNI keep their own service and policy rules in this same subsystem; a global flush would break cluster networking until they resynced.

Two things differ from the other backends. Every base chain cargoship writes has an accept policy: a default-drop policy applied from a remote phase would cut the connection cargoship is running over, the same reason cargoship never runs `ufw enable`. And an accept here ends only cargoship's chain, not the hook, so a rule in an operator's own table can still drop traffic cargoship allowed -- unlike firewalld's trusted zone, cargoship's trust is not the last word on a packet.

func (*Nftables) Apply

func (n *Nftables) Apply(ctx context.Context, h *cluster.ZarfHost, p Plan) error

Apply renders p as a complete ruleset, checks it, and loads it in one transaction. The table is replaced rather than edited, so rules cargoship applied on an earlier run that p no longer contains are gone once the transaction commits.

func (*Nftables) Detect

func (n *Nftables) Detect(h *cluster.ZarfHost) bool

Detect is true when the node persists an nftables ruleset of its own, meaning the nftables service is running or one of the distro ruleset files exists.

A node whose only nftables content comes from kube-proxy and the CNI is deliberately not a match. Every node in a running cluster has a non-empty ruleset, so matching on that would claim hosts whose operator never configured a firewall at all.

func (*Nftables) Name

func (n *Nftables) Name() string

Name is the backend identifier.

type Plan

type Plan struct {
	// NodeAddresses lists the private addresses of every node in the cluster. A node trusts
	// all traffic from these addresses.
	NodeAddresses []string
	// ClusterCIDRs lists the pod and service CIDR blocks the distro engine uses. A node
	// trusts all traffic from these blocks.
	ClusterCIDRs []string
	// Ports lists the ports the node exposes publicly, from the inventory's `.host.ports`.
	Ports []cluster.ZarfHostPort
	// Rules lists the backend-neutral rules from the inventory's `.host.firewall.rules`.
	Rules []cluster.ZarfFirewallRule
	// Policies holds the legacy firewalld-only policies from the inventory's `.host.policy`.
	// Backends other than firewalld ignore it.
	Policies map[string]cluster.ZarfFirewallPolicyConfig
}

Plan is the desired firewall state for a single node, expressed without reference to any particular firewall implementation.

func (Plan) IsEmpty

func (p Plan) IsEmpty() bool

IsEmpty is true when the plan would not change anything on a node.

type UFW

type UFW struct{}

UFW applies a Plan to a node running ufw.

Cargoship never enables or disables ufw. A node is only configured when ufw is already active, because enabling a default-deny firewall from a remote phase would cut the connection cargoship is running over.

Two parts of the neutral model mean something slightly different here than they do on firewalld: a forward rule's ingress and egress name interfaces rather than zones, since ufw has no zones, and a rule with no address match becomes a rule from any address.

func (*UFW) Apply

func (u *UFW) Apply(ctx context.Context, h *cluster.ZarfHost, p Plan) error

Apply reconciles the node's ufw rules with p and reloads ufw. Rules cargoship applied on an earlier run that p no longer contains are deleted.

func (*UFW) Detect

func (u *UFW) Detect(h *cluster.ZarfHost) bool

Detect is true when ufw is installed on h and reports itself active.

func (*UFW) Name

func (u *UFW) Name() string

Name is the backend identifier.

Jump to

Keyboard shortcuts

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