Problem
Both nft templates use the scoped add table / delete table / add table idiom rather than flush ruleset, so weaver never destroys another subsystem's rules. But nothing recovers weaver's own state when a third party wipes it after boot, and every such loss is silent.
nftables side:
- Stock Debian/Ubuntu
/etc/nftables.conf begins with flush ruleset. If nftables.service is enabled or restarted at any point, both inet weaver-host-firewall and inet weaver-workload-policy are destroyed.
- The same applies to a
firewalld reload, or any operator running nft -f with a flush at the top.
tc side:
netplan apply can recreate the egress device; a driver reload, an ip link replace, or a stray tc qdisc del dev <nic> root removes the HTB hierarchy.
Neither is repaired. The daemon's hourly force-resync goes through policy.Manager.ApplySets, which reconciles set membership and assumes the table exists; the TrafficShaperMonitor covers only the per-pod $VETH hierarchy. There is no code path that re-asserts table presence or the $EGRESS root qdisc.
The impact is invisible from the outside:
- Host firewall gone → the node's INPUT filter disappears and it is wide open.
- Workload policy gone → no
meta priority stamping, so every block-node flow falls into the HTB default class. Traffic keeps flowing at wire speed; nothing errors, no counter moves, and the shaping guarantee is quietly void.
$EGRESS HTB gone → no egress shaping at all, same silence.
Proposed fix
Add a cheap presence check to the daemon on the existing force-resync cadence:
nft list table inet weaver-host-firewall / inet weaver-workload-policy — on absence, restart solo-provisioner-network-nft.service (which re-applies the persisted .nft files) and then let the normal statusz reconcile rehydrate set membership.
- The
$EGRESS root qdisc handle — on absence, restart solo-provisioner-bandwidth-shaper.service.
Every re-assertion should log at WARN and be reflected in the daemon's GET /status so the event is attributable rather than inferred.
Acceptance
Problem
Both nft templates use the scoped
add table/delete table/add tableidiom rather thanflush ruleset, so weaver never destroys another subsystem's rules. But nothing recovers weaver's own state when a third party wipes it after boot, and every such loss is silent.nftables side:
/etc/nftables.confbegins withflush ruleset. Ifnftables.serviceis enabled or restarted at any point, bothinet weaver-host-firewallandinet weaver-workload-policyare destroyed.firewalldreload, or any operator runningnft -fwith a flush at the top.tc side:
netplan applycan recreate the egress device; a driver reload, anip linkreplace, or a straytc qdisc del dev <nic> rootremoves the HTB hierarchy.Neither is repaired. The daemon's hourly force-resync goes through
policy.Manager.ApplySets, which reconciles set membership and assumes the table exists; theTrafficShaperMonitorcovers only the per-pod$VETHhierarchy. There is no code path that re-asserts table presence or the$EGRESSroot qdisc.The impact is invisible from the outside:
meta prioritystamping, so every block-node flow falls into the HTB default class. Traffic keeps flowing at wire speed; nothing errors, no counter moves, and the shaping guarantee is quietly void.$EGRESSHTB gone → no egress shaping at all, same silence.Proposed fix
Add a cheap presence check to the daemon on the existing force-resync cadence:
nft list table inet weaver-host-firewall/inet weaver-workload-policy— on absence, restartsolo-provisioner-network-nft.service(which re-applies the persisted.nftfiles) and then let the normal statusz reconcile rehydrate set membership.$EGRESSroot qdisc handle — on absence, restartsolo-provisioner-bandwidth-shaper.service.Every re-assertion should log at WARN and be reflected in the daemon's
GET /statusso the event is attributable rather than inferred.Acceptance
nft flush ruleseton a provisioned node: both weaver tables are restored within one resync interval, with set membership rehydrated.tc qdisc del dev $EGRESS rooton a provisioned node: the HTB hierarchy is restored within one resync interval.GET /statusoverdaemon.sock.