firewall

package
v0.29.0 Latest Latest
Warning

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

Go to latest
Published: Aug 27, 2026 License: Apache-2.0 Imports: 19 Imported by: 0

Documentation

Overview

Package firewall implements the node-agnostic `solo-provisioner network firewall` scope: the `inet weaver-host-firewall` nftables table that protects the bare-metal host (SSH/mgmt allowlist, ICMP policy, in-cluster host-service ports).

It is a generic primitive — it knows nothing about block/consensus/mirror/ relay nodes. Orchestration (wiring create into `kube cluster install`, teardown into `kube cluster uninstall`) is owned by the host/cluster layer (#777 → #778/#791); this package only implements the verbs.

The `inet weaver-host-firewall` table is kept deliberately separate from `inet weaver-workload-policy` (the BN workload plane). The two tables have opposite lifecycles: `inet weaver-host-firewall` is set once and rarely changes, while `inet weaver-workload-policy` churns continuously as the daemon rewrites set elements.

Index

Constants

View Source
const (
	// TableName is the nftables table this package owns.
	TableName = "inet weaver-host-firewall"

	// HostNftPath is the on-disk artifact replayed at boot by the shared
	// solo-provisioner-network-nft.service oneshot (authored by #780). It lives
	// under /etc (host OS config on the root filesystem) — not /opt/solo/weaver,
	// which may be a late mount and would leave the firewall unloaded early at
	// boot.
	HostNftPath = "/etc/solo-provisioner/network-weaver-host-firewall.nft"

	// HostConfigPath is the declarative config this table is rendered from, and
	// the source of truth for every mutating verb. It sits beside the nft
	// artifact and holds exactly the schema `network firewall create --from-file`
	// accepts, so `show --output yaml` re-applied through --from-file is a no-op
	// by construction.
	//
	// A single file rather than one per rule: a change to the management
	// allowlist must be all-or-nothing, and a partial write across several files
	// could leave a host reachable by nobody.
	HostConfigPath = "/etc/solo-provisioner/network-weaver-host-firewall.yaml"

	// HostConfigPrevSuffix is appended to HostConfigPath to name the retained
	// previous generation of the config. Derived by suffix rather than declared
	// as an independent path so the two can never be pointed at different
	// directories.
	HostConfigPrevSuffix = ".prev"

	// HostConfigPrevPath is the generation of the config immediately before the
	// one currently applied, retained on every apply so a lost or truncated
	// state file has a recovery path that keeps named allow rules.
	//
	// Deliberately one generation deep: this is a recovery artifact, not a
	// version history. History belongs in the operator's own repository, holding
	// the output of `network firewall show --output yaml`.
	//
	// It is written only when the config it replaces parses, so the invariant is
	// "absent, or a loadable config exactly one generation back" — a retained
	// copy that is itself corrupt would be worthless for recovery, and worse than
	// worthless if an operator trusted it.
	HostConfigPrevPath = HostConfigPath + HostConfigPrevSuffix

	// WeaverNftPath is the inet weaver-workload-policy artifact, owned by `block node install`
	// (TS_2 #743). This package never writes it; it only checks for its presence
	// to decide whether the shared oneshot may be disabled (teardown is #791).
	WeaverNftPath = "/etc/solo-provisioner/network-weaver-workload-policy.nft"

	// NetworkNftService is the oneshot unit that loads network-weaver-host-firewall.nft at boot
	// and is restarted on every live mutation so the kernel and the on-disk file
	// are always in sync. This package authors, installs, and enables the unit;
	// it never disables it — that is orchestrated by `kube cluster uninstall`
	// (#791). The unit is extended by #780 to also load network-weaver-workload-policy.nft.
	NetworkNftService = "solo-provisioner-network-nft.service"

	// NetworkNftServiceUnitPath is the absolute path where the unit file is
	// installed so systemd can discover it.
	NetworkNftServiceUnitPath = "/usr/lib/systemd/system/" + NetworkNftService

	// LockDir holds the cross-command apply lock. It lives on tmpfs (/run) so it
	// is auto-cleared on reboot and leaves nothing behind on uninstall.
	LockDir = "/run/solo-provisioner/network"

	// LockPath is the flock acquired (LOCK_EX) for the duration of any mutating
	// verb, so a hand-run operator command and the daemon poll loop (#754) can
	// never interleave nft transactions.
	LockPath = "/run/solo-provisioner/network/.applying"
)
View Source
const (
	// RuleMgmt is the management allowlist: the only source of host-local
	// administrative access under the input chain's default drop.
	RuleMgmt = "mgmt"
	// RuleBlocked is the operator-curated deny list. It renders on prerouting,
	// input and output, so it is not expressible as an allow rule.
	RuleBlocked = "blocked"
	// RuleInCluster is the pod-CIDR-to-host-service allowance. Its address list
	// is auto-detected from the node's .spec.podCIDR when the operator omits it.
	RuleInCluster = "in_cluster"
)

Reserved rule names. These three are first-class rather than operator-authored because weaver derives or defaults their content and omitting them is dangerous: an empty mgmt list locks the operator out, an absent in-cluster list breaks the cluster, and the block list renders on three hooks rather than one. Everything else is an ordinary named allow rule.

View Source
const ConfigVersion = 1

ConfigVersion is the schema version this build writes. A file may omit `version` (treated as the current schema) but may not declare a newer one: silently ignoring a field a future weaver understands could leave a host with a firewall narrower — or wider — than the file says.

View Source
const (
	DefaultSSHPort = 22
)

Default flag values.

Variables

View Source
var DefaultInClusterPorts = []int{6443, 4244, 7472, 10250}

DefaultInClusterPorts is the "stack set" of host-service ports opened to the in-cluster (pod) CIDR by default: the kube-apiserver (6443), the Cilium cluster-mesh / health port (4244), the kubelet read-only/metrics port (10250), and the MetalLB metrics/memberlist port (7472). Operators override with --in-cluster-ports.

ReservedNames are the rule names an `allow` entry may not take, in render order.

Functions

func EnsureNetworkNftUnit added in v0.23.0

func EnsureNetworkNftUnit(ctx context.Context) error

EnsureNetworkNftUnit writes the embedded service unit file to NetworkNftServiceUnitPath, then daemon-reloads and enables the unit for boot.

The on-disk unit is compared against the embedded copy, not merely stat-ed, so an already-provisioned host converges on the current unit the next time a mutation runs. Stat-and-skip would have stranded every existing host on the unit that shipped when it was first provisioned — including the missing StartLimitIntervalSec=0 that lets a run of failed applies wedge every later command behind systemd's start limit (#1002). An unchanged unit is still a fast no-op: no write, no daemon-reload.

func IsReserved added in v0.28.0

func IsReserved(name string) bool

IsReserved reports whether name is one of the three reserved blocks.

func PortStrings added in v0.28.0

func PortStrings(ports []int) []string

PortStrings converts an int port list to the string port specs a Rule holds. It is the boundary conversion for callers whose own schema is still int-typed — the --in-cluster-ports / --mgmt-ports flags and models.HostConfig — so the int-vs-range mismatch is resolved in exactly one place.

Types

type Block added in v0.28.0

type Block struct {
	CIDRs []string `yaml:"cidrs"`
	Ports []string `yaml:"ports"`
}

Block is a reserved section of the config file: the subset of Rule an operator may set on mgmt, blocked or in_cluster. It deliberately has no `name` (the section key is the name), no `proto` and no `icmp_echo` — the reserved blocks either fix those or have no use for them, and accepting the fields only to reject them in validation would suggest they mean something.

Neither field carries `omitempty`: an empty list must survive a write as `cidrs: []`, because collapsing it to an absent key would turn "render no rule" back into "derive the default" on the next load.

func (*Block) MarshalYAML added in v0.28.0

func (b *Block) MarshalYAML() (any, error)

Blocked's port list is never populated: the block list drops every port, and Rule.Validate rejects a port on it. The field exists on Block only because one type serves all three reserved sections; writing it out for `blocked` would invite an operator to fill it in.

type Config

type Config struct {
	Runner          Runner
	NftPath         string
	ConfigPath      string
	LockPath        string
	ApplyViaService func(ctx context.Context) error
}

Config customises a Manager. The zero value is not useful; prefer NewManager. Tests inject a fake Runner, temp paths, and a no-op service func so the package builds and runs on any platform.

type FileConfig added in v0.28.0

type FileConfig struct {
	Version   int    `yaml:"version"`
	Mgmt      *Block `yaml:"mgmt,omitempty"`
	Blocked   *Block `yaml:"blocked,omitempty"`
	InCluster *Block `yaml:"in_cluster,omitempty"`
	Allow     []Rule `yaml:"allow,omitempty"`
}

FileConfig is the declarative form of a Table: the schema of the `network firewall create --from-file` input, of `network firewall show --output yaml`, and of the persisted config the mutating verbs load. Those three being one type is what makes the round-trip exact — the output of `show --output yaml` re-applied via `--from-file` is a no-op by construction, not by coincidence.

The reserved blocks are pointers so an absent section is distinguishable from an empty one. A file must state all three (see requireReservedBlocks): the file is the whole table, and a block left out would silently fall back to a compiled-in default — for `mgmt` that default is an empty allowlist under a default-drop policy, i.e. a lockout the operator never wrote down. A block present with an empty list renders no rule, which is how one is disabled.

func FileConfigFromTable added in v0.28.0

func FileConfigFromTable(t *Table) *FileConfig

FileConfigFromTable is the inverse of Table: the declarative view of a table, with every reserved block written out explicitly so a subsequent load resolves to the same table without consulting a default or the cluster.

func LoadConfigFile added in v0.28.0

func LoadConfigFile(path string) (*FileConfig, error)

LoadConfigFile reads and validates a declarative firewall config. Decoding is strict: an unrecognised key is an error rather than a silent no-op, since a typo in a firewall config would otherwise present as a rule that quietly never took effect.

func ParseConfig added in v0.28.0

func ParseConfig(data []byte) (*FileConfig, error)

ParseConfig decodes and validates a declarative firewall config from YAML.

func (*FileConfig) InClusterCIDRsUnset added in v0.28.0

func (c *FileConfig) InClusterCIDRsUnset() bool

InClusterCIDRsUnset reports whether the config left the in-cluster address list unspecified, so the caller knows to auto-detect the node's pod CIDR. An explicitly empty list (`in_cluster: {cidrs: []}`) is *specified* — it means "render no in-cluster rule" — and must not trigger detection.

A parsed config always has the block itself (requireReservedBlocks), so in practice this reports on the `cidrs` field alone; the nil-block arm covers a config assembled in Go rather than decoded.

func (*FileConfig) Marshal added in v0.28.0

func (c *FileConfig) Marshal() ([]byte, error)

Marshal renders the config as YAML, for `show --output yaml` and for the persisted state file.

func (*FileConfig) Table added in v0.28.0

func (c *FileConfig) Table() (*Table, error)

Table builds the Table this config describes, applying the defaults for any omitted reserved field. The in-cluster address list is the one value it cannot resolve on its own — see InClusterCIDRsUnset.

type Manager

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

Manager implements the `network firewall` verbs against the `inet weaver-host-firewall` table. Every mutating verb takes the shared apply lock, atomically rewrites the on-disk artifact, and then restarts the systemd service via DBus so the kernel is updated in one consistent operation — no separate nft apply exec.

func NewManager

func NewManager() *Manager

NewManager returns a Manager wired to the live kernel and the production paths.

func NewManagerWithConfig

func NewManagerWithConfig(cfg Config) *Manager

NewManagerWithConfig returns a Manager, filling any unset Config field with its production default.

func (*Manager) Add added in v0.28.0

func (m *Manager) Add(ctx context.Context, name string, cidrs, ports []string) error

Add adds CIDRs and/or port specs to the named rule and re-renders. Adding is idempotent: an entry already present is left alone. Growing a rule cannot empty the management allowlist, so unlike Remove/Set it takes no force.

func (*Manager) Apply added in v0.28.0

func (m *Manager) Apply(ctx context.Context, t *Table) error

Apply replaces the whole table from a declarative config and re-renders, regardless of whether one already exists: unlike Create it is not create-if-missing. It has no production caller today (`create --from-file` goes through Create) and is retained as the exported "replace outright" entry point, exercised by the package tests.

func (*Manager) Config added in v0.28.0

func (m *Manager) Config(ctx context.Context) (*FileConfig, error)

Config returns the declarative config of the currently-configured table, for `show --output yaml`. Unlike Show it reads the persisted config rather than the kernel, so its output is the same shape that produced the ruleset — a kernel dump has already lost the distinction between an authored rule and a default, and auto-merge may have rewritten the port sets.

func (*Manager) Create

func (m *Manager) Create(ctx context.Context, t *Table, force bool) (bool, error)

Create is create-if-missing: when the table already exists and force is false, it makes no changes and returns (false, nil). force re-renders the table from the supplied flags and returns (true, nil).

func (*Manager) CreateRule added in v0.28.0

func (m *Manager) CreateRule(ctx context.Context, r Rule, force bool) (bool, error)

CreateRule declares a named allow rule, so a rule can be brought into existence without a config file. It is create-if-missing like Create: a name that already exists is left alone and reported as unchanged unless force is set, in which case the rule is replaced outright — the declaration states the whole rule, so every field not supplied on the redeclare returns to its default, membership and matching alike.

The rule may be declared with no members; the element verbs populate it afterwards. It renders nothing until it has both sources and a destination, so running the declare and the populate as separate commands never opens access early.

Deliberately not built on mutate: mutate always re-applies, and the already-exists path has nothing to apply — re-rendering an identical document would restart the nft unit for no reason.

func (*Manager) Delete

func (m *Manager) Delete(ctx context.Context) error

Delete removes the inet weaver-host-firewall table and its on-disk artifact. It is idempotent. It deliberately does NOT disable the shared solo-provisioner-network-nft.service (shared with inet weaver-workload-policy) — that is orchestrated by `kube cluster uninstall` (#791).

func (*Manager) DeleteRule added in v0.28.0

func (m *Manager) DeleteRule(ctx context.Context, name string) error

DeleteRule removes one named allow rule and re-renders. The reserved blocks cannot be deleted; see Table.DeleteRule.

func (*Manager) IsActive added in v0.27.0

func (m *Manager) IsActive(ctx context.Context) (bool, error)

IsActive reports whether the inet weaver-host-firewall table is currently present in the kernel. It is a read-only probe (no lock, no mutation) used by callers that need the firewall's current on-host state, rather than a recorded decision whose absence cannot be distinguished from "disabled": common.ResolveFirewallSeed seeds the reconfigure enable/disable choice from it so an active firewall is never torn down by default, and NetworkFirewallCreate uses it to scope its rollback to a table that step actually introduced.

func (*Manager) Reapply added in v0.29.0

func (m *Manager) Reapply(ctx context.Context) error

Reapply re-renders and re-applies the persisted config without changing it. It is what re-asserts the weaver-managed table on demand: the operator states no intent, so there is nothing to supply and nothing to override.

Implemented as a no-op mutation because that is exactly the semantics wanted — load the persisted table, dry-run it, rewrite the artifacts, restart the unit. The rendered document carries the scoped-replace prefix, so this flushes and reloads `inet weaver-host-firewall` alone and leaves any third-party table on the host untouched.

No separate "nothing is persisted" guard: load() already fails with a pointer to `create` when neither the config nor the nft artifact exists, and never falls back to a default table — applying a default-drop policy with an empty management allowlist would be a lock-out. Validation is likewise load()'s, which parses through ParseConfig and so runs Table.Validate.

func (*Manager) Remove added in v0.28.0

func (m *Manager) Remove(ctx context.Context, name string, cidrs, ports []string, force bool) error

Remove drops CIDRs and/or port specs from the named rule and re-renders. Removing an absent entry is a no-op. force authorises removing the last address (or the last port) from the management allowlist; without it that one case is refused — see mutate.

func (*Manager) Set

func (m *Manager) Set(ctx context.Context, name string, cidrs, ports []string, force bool) error

Set atomically replaces the named rule's address list and/or port list. force authorises replacing the management allowlist (or its port list) with an empty one; without it that case is refused — see mutate.

func (*Manager) SetMany added in v0.28.0

func (m *Manager) SetMany(ctx context.Context, updates []Update, force bool) error

SetMany applies several rules' replacement membership in a single re-render, so a `set` naming more than one block lands as one nft transaction rather than several — a half-applied management allowlist is exactly the state worth avoiding here.

func (*Manager) Show

func (m *Manager) Show(ctx context.Context) (string, error)

Show returns the live inet weaver-host-firewall table. If the table is not active it returns a human-readable message (not an error) so the caller can print it cleanly.

func (*Manager) Table added in v0.28.0

func (m *Manager) Table(_ context.Context) (*Table, error)

Table returns the currently-configured table. Read-only: no lock is taken, because a torn read cannot happen — the config is replaced by rename.

type Proto added in v0.28.0

type Proto string

Proto is the L4 protocol a rule matches. A rule names exactly one: nft has no combined tcp/udp dport match, so a service reachable over both families of protocol is two rules.

const (
	// ProtoTCP matches TCP destination ports.
	ProtoTCP Proto = "tcp"
	// ProtoUDP matches UDP destination ports.
	ProtoUDP Proto = "udp"
)

type Rule added in v0.28.0

type Rule struct {
	Name  string   `yaml:"name" json:"name"`
	CIDRs []string `yaml:"cidrs,omitempty" json:"cidrs,omitempty"`
	Ports []string `yaml:"ports,omitempty" json:"ports,omitempty"`
	// Proto defaults to ProtoTCP when empty. Meaningless on mgmt (which renders
	// a fixed TCP accept plus its own ICMP type list) and on blocked (which
	// drops every protocol).
	Proto Proto `yaml:"proto,omitempty" json:"proto,omitempty"`
	// ICMPEcho grants this rule's sources unmetered echo-request, rendered into
	// the per-family ICMP chains above the rate meter. Meaningless on mgmt,
	// which already carries a broader ICMP type list, and on blocked.
	ICMPEcho bool `yaml:"icmp_echo,omitempty" json:"icmp_echo,omitempty"`
}

Rule is one named record in the host firewall: a source address list, a destination port list, and the protocol they apply to. The three reserved names render into fixed positions and ignore some fields (see Validate); an allow rule renders uniformly as `<family> saddr @<name> <proto> dport @<name>_ports accept`.

Ports are strings, not ints, so an inclusive range ("2379-2380") is expressible without a mixed int/string list. CIDRs may mix address families; the renderer routes each to the matching per-family set.

func (*Rule) AddCIDRs added in v0.28.0

func (r *Rule) AddCIDRs(cidrs []string) error

AddCIDRs adds CIDRs to the rule, ignoring ones already present. The list is kept sorted so a render is stable regardless of the order entries arrived in.

func (*Rule) AddPorts added in v0.28.0

func (r *Rule) AddPorts(ports []string) error

AddPorts adds port specs to the rule, ignoring ones already present.

func (*Rule) RemoveCIDRs added in v0.28.0

func (r *Rule) RemoveCIDRs(cidrs []string)

RemoveCIDRs drops CIDRs from the rule. Removing an absent entry is a no-op.

func (*Rule) RemovePorts added in v0.28.0

func (r *Rule) RemovePorts(ports []string)

RemovePorts drops port specs from the rule. Removal is by exact spec, so removing "2379" from a rule holding "2379-2380" is a no-op rather than a partial range split — nft ranges are single set elements and splitting one silently would be a surprising way to change a firewall.

func (*Rule) SetCIDRs added in v0.28.0

func (r *Rule) SetCIDRs(cidrs []string) error

SetCIDRs atomically replaces the rule's full address list. An empty (non-nil) slice clears it.

func (*Rule) SetPorts added in v0.28.0

func (r *Rule) SetPorts(ports []string) error

SetPorts atomically replaces the rule's full port list. An empty (non-nil) slice clears it.

func (*Rule) Validate added in v0.28.0

func (r *Rule) Validate() error

Validate rejects any field that would be unsafe or nonsensical to render. Every untrusted token goes through pkg/sanity, so a malformed value can never break the atomic nft transaction or smuggle in nft syntax. flagFor names the CLI flag in the error so an operator sees the input they supplied rather than an internal field name.

type Runner

type Runner interface {
	// List returns the rendered ruleset for the inet weaver-host-firewall table
	// (`nft list table inet weaver-host-firewall`).
	List(ctx context.Context) (string, error)
	// Check dry-runs a rendered ruleset file (`nft -c -f <path>`) without
	// committing it, so a document the kernel would reject is caught before it
	// becomes the persisted boot artifact.
	Check(ctx context.Context, path string) error
	// Delete removes the inet weaver-host-firewall table (`nft delete table inet weaver-host-firewall`).
	Delete(ctx context.Context) error
	// Exists reports whether the inet weaver-host-firewall table is present in the kernel.
	Exists(ctx context.Context) (bool, error)
}

Runner is the seam over the system `nft` binary for read, check and delete operations. Live rule application is done by writing the on-disk artifact and restarting the systemd service via DBus — so Apply is not part of this interface. Tests substitute a fake so the package builds and unit-tests on any platform (including macOS) without touching the kernel.

func NewExecRunner

func NewExecRunner() Runner

NewExecRunner resolves the nft binary path and returns a Runner that applies changes to the live kernel.

type Table

type Table struct {
	// Mgmt is the management allowlist (sets @mgmt_addrs / @mgmt_addrs6) and the
	// ports reachable from it (@mgmt_ports). Under the input chain's default
	// drop this is the only path to host-local administrative access, so an
	// empty address list locks the operator out of new connections.
	Mgmt Rule
	// Blocked is the operator-curated deny list (sets @blocked_addrs /
	// @blocked_addrs6). It is purely operator-managed for its whole lifecycle —
	// nothing in this package or the daemon ever writes to it. This is
	// deliberately distinct from the BN workload plane's `bn-restricted` set
	// (`inet weaver-workload-policy`), which the traffic-shaper daemon
	// reconciles from the block node's statusz "restricted" category; an
	// operator block list needs a home the daemon never overwrites.
	//
	// A blocked CIDR means "blocked on this node", not "blocked from the host's
	// own services": it is dropped on prerouting (which covers pod-bound
	// forwarded traffic and runs ahead of conntrack), again on input, and as a
	// destination on output — because blocking a peer inbound does not stop this
	// host from dialing it, and the replies to a host-initiated connection are
	// admitted by the input chain's established accept.
	Blocked Rule
	// InCluster admits host-service ports (@in_cluster_ports) from the pod CIDR
	// (@in_cluster_addrs / @in_cluster_addrs6). Per design there is deliberately
	// no rule here for block-node service ports: that traffic is forwarded
	// rather than delivered locally, so an input rule for it would never match.
	// It lives in `network policy --ports` instead.
	//
	// An empty address list renders no in-cluster rule at all, which is how an
	// operator disables the block without deleting it.
	InCluster Rule
	// Allow holds the operator-authored rules, each a source list x port list x
	// protocol accept. Order within the input chains is by name, so a render is
	// stable across CLI invocations; evaluation order does not matter because
	// every entry is an accept and none overlap a drop.
	Allow []Rule
}

Table is the in-memory model of the `inet weaver-host-firewall` nftables table. It is the single source of truth that the kernel apply (via `nft -f`), the on-disk nft artifact, and the persisted YAML config are all rendered from, so no two of them can diverge.

The three reserved blocks are separate fields rather than entries in Allow because each renders into a position an allow rule cannot reach: Mgmt also feeds the ICMP chains, Blocked renders as a drop on three hooks, and InCluster's address list is auto-detected rather than authored. Every other rule is uniform, so the CLI addresses all four kinds by name through Table.Rule.

func NewTable

func NewTable() *Table

NewTable returns a Table populated with the design defaults. Callers override fields from CLI flags or a config file before rendering.

func Parse

func Parse(content string) (*Table, error)

Parse recovers the three reserved blocks of a Table from a rendered network-weaver-host-firewall.nft artifact. It understands only the exact formats this package renders — it is not a general nft parser.

It is the fallback path, not the normal one: the persisted YAML config is the source of truth for the mutating verbs (see Manager.load). Parse exists so a host provisioned before named allow rules existed — or one whose config file was lost — still yields its management allowlist rather than an error that leaves the operator with no way to add their address back. Named allow rules are deliberately NOT recovered here: reverse-engineering arbitrary named rules out of nft syntax would be fragile in exactly the situation where being wrong costs the most. Recovering management access is the goal; the allow rules are not recovered at all — neither their existence nor their membership — so they must be re-declared with `network firewall create-allow-rule` and then re-populated with `network firewall add`.

Both the current and the pre-allow-rules renderings are accepted, since an upgraded host still has the old artifact on disk until its first mutation.

func (*Table) DeleteRule added in v0.28.0

func (t *Table) DeleteRule(name string) error

DeleteRule removes an allow rule. The reserved blocks cannot be deleted — they are structural, and deleting mgmt in particular would render a default-drop input chain with no way in. Emptying a block's address list is the supported way to disable it.

func (*Table) IncompleteAllowRules added in v0.28.0

func (t *Table) IncompleteAllowRules() []string

IncompleteAllowRules returns the names of allow rules that are declared but do not yet render anything, in table order. Declaring a rule before populating it is supported (`network firewall create-allow-rule`), so this is a warning the manager surfaces on apply rather than a validation failure.

func (*Table) Names added in v0.28.0

func (t *Table) Names() []string

Names returns every rule name in the table, reserved blocks first and allow rules sorted, for error messages that list the valid --name values.

func (*Table) Render

func (t *Table) Render() (string, error)

Render produces the full `inet weaver-host-firewall` nft document for this table. The same output feeds both the kernel apply (`nft -f`) and the on-disk artifact, so the live table and the persisted file can never diverge.

func (*Table) Rule added in v0.28.0

func (t *Table) Rule(name string) (*Rule, bool)

Rule returns a pointer to the named rule so a caller can mutate it in place. It resolves the three reserved names and any allow rule through one lookup, which is what lets `network firewall add --name <x>` treat them uniformly.

func (*Table) UpsertAllow added in v0.28.0

func (t *Table) UpsertAllow(r Rule) error

UpsertAllow adds or replaces an allow rule, rejecting the reserved names. The list is kept sorted by name so the rendered document is independent of the order rules were authored in.

func (*Table) Validate

func (t *Table) Validate() error

Validate rejects any table that would be unsafe to render. It is the last gate before the renderer: it validates every rule, holds the reserved names against the allow list, and checks that no two rules derive the same nft set name.

type Update added in v0.28.0

type Update struct {
	Name     string
	CIDRs    []string
	Ports    []string
	Proto    *Proto
	ICMPEcho *bool
}

Update is one rule's replacement membership for SetMany. A nil slice leaves that dimension unchanged; an empty (non-nil) slice clears it. Proto and ICMPEcho follow the same convention with pointers, since their zero values ("" and false) are both meaningful settings rather than "not supplied".

Jump to

Keyboard shortcuts

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