39ede9130b
Build flock Image / build (push) Has been cancelled
New pkg/agent/netpol implementing standard networking.k8s.io/v1 NetworkPolicy. Pipeline: pods + policies + namespaces → Translate → Render → Apply Supports ingress + egress, all three peer types (podSelector, namespaceSelector, ipBlock with except), numeric ports + port ranges, default-deny semantics derived from PolicyTypes (or inferred from non-empty Spec.Egress when unset). Apply path is `nft -f -` shell-out — single transaction, atomic, kernel guarantees partial-failure rollback. Idempotent dedup via last-applied script. Reconcile triggers: informer events, 30s self-heal tick, every CNI ADD/DEL. Verified against the three live cluster NetPols (calico-apiserver, remote-proxies/lodge-home-assistant, storage/garage-admin-restrict). Fuzz target stitches Translate + Render with random selector and peer inputs; 21 unit tests cover the policy semantics. Named ports skip with a warn — deferred until kubelet exposes them in a form that doesn't require shadowing pod state. Dockerfile: + nftables. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
45 lines
2.1 KiB
Go
45 lines
2.1 KiB
Go
// Package netpol implements Kubernetes NetworkPolicy enforcement for flock.
|
|
//
|
|
// # Model
|
|
//
|
|
// NetworkPolicy is a Kubernetes-native API (`networking.k8s.io/v1`) that
|
|
// describes which pods may receive traffic (Ingress) and / or initiate
|
|
// traffic (Egress). The semantics are isolation by selection: a pod that is
|
|
// selected by *any* NetworkPolicy in a given direction becomes default-deny
|
|
// in that direction, plus the union of all "allow" rules from every policy
|
|
// that selects it. A pod selected by no policy is unrestricted.
|
|
//
|
|
// flock enforces these semantics with nftables. Each agent is responsible
|
|
// for the pods scheduled on its own node — peer addresses (from
|
|
// podSelector / namespaceSelector / ipBlock peers) come from a cluster-wide
|
|
// informer set so the agent can resolve peers that live elsewhere.
|
|
//
|
|
// # Pipeline
|
|
//
|
|
// The work is split into four stages with hard boundaries between them so
|
|
// each can be tested in isolation:
|
|
//
|
|
// 1. Informers (informers.go) — watch NetworkPolicies, Namespaces, and
|
|
// all Pods in the cluster. Maintain indices the translator can query.
|
|
//
|
|
// 2. Translator (translator.go) — pure function from
|
|
// (NetworkPolicy set, Namespace set, Pod set, local-node pod set) to
|
|
// []Rule. No I/O, no hidden state — straightforward to fuzz and unit
|
|
// test. Implements the default-deny semantics and the peer-resolution
|
|
// rules from the NetworkPolicy spec.
|
|
//
|
|
// 3. Renderer (render.go) — pure function from []Rule to an nft script
|
|
// (string). Output is deterministic so the apply stage can de-dupe.
|
|
//
|
|
// 4. Apply (apply_linux.go) — shell out to `nft -f -` for an atomic
|
|
// reconfiguration. nftables guarantees the whole script applies as a
|
|
// single transaction; partial failures roll back automatically.
|
|
//
|
|
// # Why nftables (and not eBPF)
|
|
//
|
|
// Atomic ruleset transactions, kernel-native, no userspace ebpf-loader to
|
|
// maintain, and behaviour an operator can read directly with
|
|
// `nft list ruleset`. The cost is that we walk per-pod chains in software,
|
|
// which is fine at the cluster sizes flock targets.
|
|
package netpol
|