Documentation
¶
Overview ¶
Package firewall manages host-level iptables state owned by kukeon — the FORWARD admission chain that admits *ingress* (and stateful return traffic) to/from kukeon bridges. It is distinct from internal/netpolicy, which installs the per-space chains that now own *egress* admission as well as egress filtering: this host-scope chain is set up once at `kuke init` (and re-asserted by the daemon), while each space's chain is applied per-space by the runner. Since #1076 egress is no longer admitted by a host-global blanket here — that fails open — so egress admission lives entirely in the per-space chains, which fail closed when missing.
Index ¶
Constants ¶
const BridgeIfaceMatch = "k-+"
BridgeIfaceMatch is the iptables -i / -o interface match that scopes the admission rules to kukeon-managed bridges. The interface name is derived in internal/cni.SafeBridgeName as "k-<8 hex>" so the "+" wildcard matches the hex suffix and admits any kukeon bridge regardless of which space hash it represents.
const ForwardChainName = "KUKEON-FORWARD"
ForwardChainName is the kukeon-owned FORWARD admission chain. It now carries only ingress admission (`! -i k-+ -o k-+ ACCEPT`, scoped to non-bridge sources) plus the stateful return-traffic ACCEPT; egress admission moved per-space in #1076.
There is no longer any ordering contract with KUKEON-EGRESS (netpolicy.MasterChainName). Egress is decided entirely within each space's own KUKEON-EGRESS chain, which terminates every packet itself (ACCEPT/DROP) rather than RETURNing to a blanket admission here — so this chain's position relative to KUKEON-EGRESS is immaterial to correctness. ensureForwardJump only guarantees the jump *exists* (re-inserting it after a reboot flush or Docker churn); it no longer reorders it.
The one rule here that *could* shadow an egress decision is the ingress `-o k-+ ACCEPT`: an inter-bridge packet (`-i k-A -o k-B`) is both ingress to B and egress from A, so a bare `-o k-+` would ACCEPT a deny-space A's egress to B before A's chain runs — fail-open whenever this chain sits ahead of KUKEON-EGRESS. The ingress rule is therefore scoped `! -i k-+`: it admits only traffic that did *not* originate on a kukeon bridge (true external ingress), so a space's egress (always bridge-sourced) can never match it and always falls through to KUKEON-EGRESS. That is what keeps the position genuinely immaterial — including for the inter-bridge case.
Variables ¶
This section is empty.
Functions ¶
func AdmissionRules ¶
func AdmissionRules() [][]string
AdmissionRules returns the ordered iptables rules that populate ForwardChainName. The generator is pure — no I/O, no iptables calls — so tests can verify rule order without fakes.
Each rule carries a -m comment --comment "kukeon-forward:<role>" tag so `iptables -S` is self-documenting and the Install migration path can distinguish kukeon-installed rules from any user rules that happen to share the same chain name.
Rule order:
- RELATED,ESTABLISHED ACCEPT — return-traffic for already-admitted flows so reply packets cannot be dropped by FORWARD's default policy.
- ! -i k-+ -o k-+ ACCEPT — admit *external* ingress destined to a kukeon bridge. The `! -i k-+` qualifier scopes this to traffic that did not originate on a kukeon bridge, so it admits the internet/host→bridge case without ever matching a space's egress.
Why the `! -i k-+` scope matters: an inter-bridge packet (`-i k-A -o k-B`) is simultaneously ingress to B and egress from A. A bare `-o k-+ ACCEPT` would admit it here before A's KUKEON-EGRESS chain ever runs — fail-open for cross-space egress whenever this chain sits ahead of KUKEON-EGRESS in FORWARD, which nothing orders post-#1076. Excluding bridge-sourced traffic (`! -i k-+`) sends every space's egress — internet-bound and inter-bridge alike — through KUKEON-EGRESS, so the deny-space decision is never shadowed.
Egress admission (`-i k-+ ACCEPT`) is deliberately absent since #1076: a host-global egress blanket fails *open* — a Default=deny space whose per-space KUKEON-EGRESS chain went missing (e.g. post-reboot, pre-reconcile) would have its traffic ACCEPTed here. Egress is now admitted per-space inside each space's own KUKEON-EGRESS chain (see internal/netpolicy.BuildRules), so a missing chain fails *closed*. pruneObsoleteEgressAdmission strips a leftover `:egress` rule from upgraded hosts.
func IsIptablesAvailable ¶ added in v0.6.0
func IsIptablesAvailable() bool
IsIptablesAvailable reports whether the iptables binary can be located on PATH. Callers should invoke this before Install on hosts that may not carry iptables (minimal containers, nftables-only distros without the iptables-nft compat shim): without the binary in place every runner call would fail and abort bring-up. The intended caller-side pattern is log WARN and continue, treating absence as "the host owner has opted out of kukeon-managed FORWARD admission".
Types ¶
type CommandRunner ¶
CommandRunner executes an iptables invocation and returns its combined stdout+stderr. Tests inject a fake to capture invocations and return canned output for read-only calls like "-C" or "-L". Mirrors netpolicy.CommandRunner.
type Installer ¶
type Installer struct {
// contains filtered or unexported fields
}
Installer applies and removes the KUKEON-FORWARD admission chain.
func NewInstaller ¶
NewInstaller returns an Installer that shells out to the iptables binary on PATH. Logger is required.
func NewInstallerWithRunner ¶
func NewInstallerWithRunner(logger *slog.Logger, runner CommandRunner) *Installer
NewInstallerWithRunner is the test-hook constructor.
func (*Installer) Install ¶
Install ensures KUKEON-FORWARD exists, contains the admission rules in the expected order, and is jumped to from FORWARD. Idempotent — re-running on a healthy host produces no rule churn (every install step does -C before -I/-A, mirroring the netpolicy pattern).
Upgrade-path migration: when the chain already exists with untagged rules from an older kukeon version, Install flushes it once before re-installing the tagged rules so the chain does not end up carrying both the bare and the tagged variants side by side. A host upgraded from a pre-#1076 layout also carries the retired `:egress` blanket rule; pruneObsoleteEgressAdmission strips it so the fail-open hole #1076 closes does not survive the upgrade. A host upgraded from an intermediate #1076 layout carries the *unscoped* ingress rule (`-o k-+ ...:ingress`, no `! -i k-+`); pruneUnscopedIngressAdmission strips that one so the inter-bridge fail-open it allowed does not survive either — leaving only the scoped `! -i k-+ -o k-+` ingress rule.
func (*Installer) Remove ¶
Remove deletes the FORWARD jump, flushes, and deletes KUKEON-FORWARD. Safe to call when the chain does not exist; missing-chain failures from flush/delete are demoted to debug logs so reset --purge-system on a host that never installed the chain (or already removed it) does not error.