* Migration 0012: firewall_zones (id, name UNIQUE, description, builtin),
Seed wan/lan/dmz/mgmt/cluster als builtin. CHECK-Constraints auf
network_interfaces.role + firewall_rules.{src,dst}_zone +
firewall_nat_rules.{in,out}_zone gedroppt — Validation lebt jetzt
app-side (Handler prüft Existenz in firewall_zones).
* Backend: firewall.ZonesRepo (CRUD + Exists + References-Lookup),
/api/v1/firewall/zones, builtin geschützt (Name nicht änderbar,
Delete blockiert), Rename eines Custom-Zone aktuell ohne Cascade
(Handler-Sorge bei Rules/NAT/Networks).
* Handler-Validation in CreateRule/UpdateRule/CreateNAT/UpdateNAT +
NetworksHandler: Zone-Existence-Check pro Mutation, 400 bei Tippfehler.
* Frontend: Firewall-Tab "Zonen" (CRUD mit builtin-Schutz). Networks-
Form lädt Rollen aus /firewall/zones (statt hardcoded Liste); Rules-
und NAT-Forms ziehen die Zone-Auswahl ebenfalls aus der API.
* Domain-Form bekommt Primary-Backend-Picker (Field war im Modell,
fehlte im UI). Backends-Tabelle zeigt umgekehrt welche Domains
darauf zeigen — bidirektionale Sicht ohne Schemaänderung.
* HAProxy-Renderer: safeID-FuncMap escaped Server-Namen mit Whitespace
("Control Master 1" → "Control_Master_1"). Vorher ist haproxy beim
Reload an Spaces im Backend-Namen kaputt gegangen.
* Version 1.0.3 → 1.0.6.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
49 lines
2.2 KiB
SQL
49 lines
2.2 KiB
SQL
-- +goose Up
|
|
-- +goose StatementBegin
|
|
|
|
-- firewall_zones promotes zones from a hard-coded enum
|
|
-- (wan/lan/dmz/mgmt/cluster) into a first-class entity. Operators
|
|
-- can add their own (e.g. iot, guest, voip) without a schema
|
|
-- change. Existing role/zone TEXT columns on network_interfaces,
|
|
-- firewall_rules and firewall_nat_rules continue to store the
|
|
-- zone NAME — referential integrity is enforced at the application
|
|
-- layer (handler validates name exists in firewall_zones), not by
|
|
-- a hard FK, so 'any' on rules and NULL on NAT keep working
|
|
-- without special-casing.
|
|
--
|
|
-- builtin = TRUE marks the seed zones; the API rejects DELETE on
|
|
-- those rows to prevent the operator from removing a zone the
|
|
-- renderer still expects.
|
|
CREATE TABLE IF NOT EXISTS firewall_zones (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
name TEXT NOT NULL UNIQUE,
|
|
description TEXT,
|
|
builtin BOOLEAN NOT NULL DEFAULT FALSE,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
CONSTRAINT firewall_zones_name_check CHECK (name ~ '^[a-z][a-z0-9_-]{0,31}$')
|
|
);
|
|
|
|
INSERT INTO firewall_zones (name, description, builtin) VALUES
|
|
('wan', 'Public-facing internet uplink', TRUE),
|
|
('lan', 'Internal trusted network', TRUE),
|
|
('dmz', 'Quarantined service network', TRUE),
|
|
('mgmt', 'Admin-only management network', TRUE),
|
|
('cluster', 'Inter-node cluster traffic (KeyDB / mTLS API)', TRUE)
|
|
ON CONFLICT (name) DO NOTHING;
|
|
|
|
-- Drop the hard-coded CHECK constraints so the operator can declare
|
|
-- new zones without the SQL layer rejecting them. App-side
|
|
-- validation in the handlers takes over.
|
|
ALTER TABLE network_interfaces DROP CONSTRAINT IF EXISTS network_interfaces_role_check;
|
|
ALTER TABLE firewall_rules DROP CONSTRAINT IF EXISTS firewall_rules_src_zone_check;
|
|
ALTER TABLE firewall_rules DROP CONSTRAINT IF EXISTS firewall_rules_dst_zone_check;
|
|
ALTER TABLE firewall_nat_rules DROP CONSTRAINT IF EXISTS firewall_nat_rules_zone_check;
|
|
|
|
-- +goose StatementEnd
|
|
|
|
-- +goose Down
|
|
-- +goose StatementBegin
|
|
DROP TABLE IF EXISTS firewall_zones;
|
|
-- +goose StatementEnd
|