firewall: interface-coverage apply guard, target drift, non-destructive DHCP sync
Post-DHCP-incident hardening per HARDEN.md.
- apply guard: refuse (ConflictError, `force` overrides) when a
network-managed interface would end up in no zone; absent
`interfaces` key = hands-off, explicit `[]` = unassign-all
- surface `uncovered_interfaces` in firewall state (lo/wg* filtered)
+ advisory in /api/status/pending; zones.js banner + interfaces-picker
last-zone confirm
- target drift (Option A): absent or default-normalizing target is
unmanaged: not diffed, never re-set by apply; create_zone runs
--new-zone first and sets non-default targets only; importer omits
the target key for default zones
- FirewallToDhcpSync keeps stale DHCP ranges and flags them instead of
deleting; `dnsmasq` affected only on a real gateway mutation
- real pre-apply recovery snapshot in data/firewall/rules.json
({timestamp, default_zone, zones, config}); drop the empty post-apply
skeleton
- daemon shutdown: bounded grace for in-flight tasks + suppressed
teardown exception noise on SIGTERM
- also carries the firewall service-descriptions feature
(get_service_descriptions + service_descriptions state field + UI)
- tests + docs across firewall/status/state/sync/schema; ruff clean,
867 passing
This commit is contained in:
+7
-2
@@ -1962,7 +1962,7 @@ Aggregate pending changes across all subsystems. Useful for the dashboard to sho
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `firewall` | `object` | `{ needs_apply, change_count, changes: [{summary, detail}] }` |
|
||||
| `firewall` | `object` | `{ needs_apply, change_count, changes: [{summary, detail}], uncovered_interfaces: [string], coverage_warnings: [string] }`. `uncovered_interfaces` lists network-config interfaces (excluding `lo`/`wg*`) that are in no live firewalld zone, and `coverage_warnings` carries the matching advisory text. Both are advisory only — they are **not** counted in `needs_apply`, `change_count`, or `total_changes` |
|
||||
| `dnsmasq` / `nginx` / `wireguard` / `networkd` | `object` | `{ pending_changes, summary, changes: [{summary, detail}] }` |
|
||||
| `total_changes` | `number` | Total count of pending changes across all subsystems |
|
||||
|
||||
@@ -1981,7 +1981,12 @@ Apply pending changes for all subsystems in dependency order.
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `applied` | `[string, ...]` | List of subsystems that were applied |
|
||||
| `errors` | `[object, ...]` | Any errors encountered during apply |
|
||||
| `errors` | `object` | Map of subsystem label → error message |
|
||||
|
||||
The firewall apply runs with `force=false`, so if a firewall interface
|
||||
would be left without zone coverage (the coverage guard), a
|
||||
`ConflictError` surfaces in `errors` under `"Firewall"` while the other
|
||||
subsystems proceed — the desired no-silent-apply behavior.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user