Repository navigation
feat(router): Apply traffic policy bindings on each node - #870
Conversation
|
Detail for reviewers, in suggested review order.
Checks run on 870e5a1 with base 5399e67: Gaps: no containerlab traffic yet (PR 8). After a restart, an invalid edit of an adopted same-UID policy invalidates rather than keeping the adopted snapshot, since adopted snapshots carry no rule names; this is fail-closed and documented in the notes. A pre-push review's request-changes findings (rebind leaving grants, cluster-wide status writes, partial startup) are fixed in this commit. |
|
Two notes from the conventions review. The new grants sit in the ClusterRole the router and the route reflector share, like the existing service binding grants, so the reflector holds read access and status patch it never uses because it refuses the feature at startup. #643 also edits the router setup, its RBAC and go.mod, so whichever of the two lands second rebases. |
galactic-router gains the node side of interface traffic policy, behind GALACTIC_ROUTER_TRAFFIC_POLICY (off by default, refused on a route reflector). For every VPCAttachment on its node it resolves the binding's ingress and egress references, compiles each policy, and publishes it into the uSID datapath; a direction with no reference gets the built-in default-deny-all. - Counter rows: each rule keeps its row while its name and action stay the same within one policy UID and interface token, so editing a match keeps its counts. Replaced rows are freed only after the outer-map update returns; a failed publish frees what it allocated. - Fail closed: a missing or reserved-conflict reference, and a rebind that cannot be installed, unbind the direction (policy_unavailable). A failed update or an invalid edit of the same policy keeps its last snapshot. - Restart: snapshots record the policy UID and generation beside the token. A restarted router adopts a snapshot only when it holds exactly what the policy compiles to today, so counts continue and an older compiler's output is republished. Startup reads every bound snapshot or nothing, and a snapshot nothing claims within a resync is unbound and its rows freed. - Status: this node's entry in the binding's status.nodes, applied with server-side apply, written only when it changes, after the datapath lock is released. - Metrics: galactic_cni_traffic_policy_* on the router's metrics port, labeled from what the router installed, plus a gauge of counter rows a scrape could not read. The design said galactic-cni would serve them; the router allocates the rows and knows their rule and interface, so it does, under the names the design fixed. docs/metrics.md documents them. - Diagnostics: `galactic-router policy explain` decides a packet with the Go evaluator and the compiled entries and fails if they disagree; `policy show` prints what the node holds for one attachment. RBAC gains read access to policies and bindings and patch on binding status. The root unit job gains envtest assets so the reconciler's envtest-and-BPF test runs in CI. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
870e5a1 to
8555e52
Compare
Summary
The datapath can enforce interface traffic policy, but nothing on a node yet turns a tenant's bindings into the rules it runs, so every interface could only hold the built-in deny.
The node router now compiles each attachment's bound policies, installs them per direction, reports what it applied in the binding's per-node status, and serves the policy counters to the existing metrics scrape.
A rebind that cannot be installed, a missing policy, or a reserved-name conflict leaves the direction denying rather than keeping old grants, and a restart keeps counts by adopting what is still installed.
The feature is off by default and does not enforce anything by itself, since whether the datapath enforces stays the CNI setting.
Test plan
Stacked on #869. Related to #691
🤖 Generated with Claude Code