fix(api): Service-Mutationen rendern jetzt auch FW automatisch

Bug: Wenn der Operator eine NTP/DNS/Squid/WG-Mutation gemacht hat,
wurde nur der Service neu konfiguriert + reloadet — die Auto-FW-
Rules (udp/123, udp/tcp 53, tcp 3128, udp/<wg-port>) blieben aber
auf dem Stand des letzten firewall-renders. Operator musste manuell
'edgeguard-ctl render-config --only=nftables' fahren.

Fix: withFW-Wrapper in main.go der nach jedem Service-Reloader auch
den firewall-Renderer aufruft. Service-Reload-Errors propagieren
weiterhin (Aktion gilt als gescheitert), FW-Render-Errors werden
nur geloggt (DB-Row ist commited, FW kann nachgezogen werden).

Wirkt für: WG, Squid, DNS, NTP. (HAProxy nicht — Domains/Backends
generieren keine Auto-FW-Rules.)

Version 1.0.41.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Debian
2026-05-11 07:04:45 +02:00
parent e4d83d226e
commit f78ada7732
6 changed files with 36 additions and 15 deletions

View File

@@ -1 +1 @@
1.0.40 1.0.41

View File

@@ -45,7 +45,7 @@ import (
wgsvc "git.netcell-it.de/projekte/edgeguard-native/internal/services/wireguard" wgsvc "git.netcell-it.de/projekte/edgeguard-native/internal/services/wireguard"
) )
var version = "1.0.40" var version = "1.0.41"
func main() { func main() {
addr := os.Getenv("EDGEGUARD_API_ADDR") addr := os.Getenv("EDGEGUARD_API_ADDR")
@@ -184,33 +184,54 @@ func main() {
} }
handlers.NewFirewallHandler(fwZones, fwAddrObj, fwAddrGrp, fwSvc, fwSvcGrp, fwRules, fwNAT, auditRepo, nodeID, fwReloader, pool).Register(authed) handlers.NewFirewallHandler(fwZones, fwAddrObj, fwAddrGrp, fwSvc, fwSvcGrp, fwRules, fwNAT, auditRepo, nodeID, fwReloader, pool).Register(authed)
// withFW wraps a service-reloader so that AFTER the service is
// reloaded, the firewall is also re-rendered. Necessary for
// services whose state feeds the auto-FW-rule generator (DNS
// listen-IPs, Squid ACL count, WG listen-port, NTP serve-clients).
// Service-Reload-Errors propagieren; FW-Errors werden nur
// geloggt (DB-Row ist commited, FW kann nachgezogen werden).
withFW := func(svc func(context.Context) error) func(context.Context) error {
return func(ctx context.Context) error {
if err := svc(ctx); err != nil {
return err
}
if err := fwReloader(ctx); err != nil {
slog.Warn("firewall: re-render after service mutation failed", "error", err)
}
return nil
}
}
// WireGuard reload: re-render /etc/edgeguard/wireguard/*.conf // WireGuard reload: re-render /etc/edgeguard/wireguard/*.conf
// + restart wg-quick@<iface>. Same pattern as the haproxy + // + restart wg-quick@<iface>. Same pattern as the haproxy +
// firewall reloaders. // firewall reloaders. WG braucht FW-Trigger (server-mode
// listen-port wird Auto-Rule).
wgReloader := func(ctx context.Context) error { wgReloader := func(ctx context.Context) error {
return wgrender.New(pool, secretsBox).Render(ctx) return wgrender.New(pool, secretsBox).Render(ctx)
} }
handlers.NewWireguardHandler(wgIfaces, wgPeers, secretsBox, auditRepo, nodeID, wgReloader).Register(authed) handlers.NewWireguardHandler(wgIfaces, wgPeers, secretsBox, auditRepo, nodeID, withFW(wgReloader)).Register(authed)
// Squid forward-proxy reload — re-render squid.conf + reload // Squid forward-proxy reload — re-render squid.conf + reload
// squid.service. sudoers im postinst whitelistet das. // squid.service. sudoers im postinst whitelistet das. ACL-Count
// triggert Auto-FW-Rule für tcp/3128.
squidReloader := func(ctx context.Context) error { squidReloader := func(ctx context.Context) error {
return squidrender.New(pool).Render(ctx) return squidrender.New(pool).Render(ctx)
} }
handlers.NewForwardProxyHandler(fwdProxyRepo, auditRepo, nodeID, squidReloader).Register(authed) handlers.NewForwardProxyHandler(fwdProxyRepo, auditRepo, nodeID, withFW(squidReloader)).Register(authed)
// Unbound DNS reload — re-render edgeguard.conf + reload // Unbound DNS reload — re-render edgeguard.conf + restart
// unbound.service. // unbound. Listen-IPs triggern Auto-FW-Rule für udp/tcp 53.
unboundReloader := func(ctx context.Context) error { unboundReloader := func(ctx context.Context) error {
return unboundrender.New(pool).Render(ctx) return unboundrender.New(pool).Render(ctx)
} }
handlers.NewDNSHandler(dnsRepo, auditRepo, nodeID, unboundReloader).Register(authed) handlers.NewDNSHandler(dnsRepo, auditRepo, nodeID, withFW(unboundReloader)).Register(authed)
// Chrony NTP reload — re-render edgeguard.conf + reload chrony. // Chrony NTP reload — re-render edgeguard.conf + restart chrony.
// Listen-IPs + serve_clients triggern Auto-FW-Rule für udp/123.
chronyReloader := func(ctx context.Context) error { chronyReloader := func(ctx context.Context) error {
return chronyrender.New(pool).Render(ctx) return chronyrender.New(pool).Render(ctx)
} }
handlers.NewNTPHandler(ntpRepo, auditRepo, nodeID, chronyReloader).Register(authed) handlers.NewNTPHandler(ntpRepo, auditRepo, nodeID, withFW(chronyReloader)).Register(authed)
} }
mountUI(r) mountUI(r)

View File

@@ -9,7 +9,7 @@ import (
"os" "os"
) )
var version = "1.0.40" var version = "1.0.41"
const usage = `edgeguard-ctl — EdgeGuard CLI const usage = `edgeguard-ctl — EdgeGuard CLI

View File

@@ -21,7 +21,7 @@ import (
"git.netcell-it.de/projekte/edgeguard-native/internal/services/tlscerts" "git.netcell-it.de/projekte/edgeguard-native/internal/services/tlscerts"
) )
var version = "1.0.40" var version = "1.0.41"
const ( const (
// renewTickInterval — how often we re-evaluate expiring certs. // renewTickInterval — how often we re-evaluate expiring certs.

View File

@@ -1,7 +1,7 @@
{ {
"name": "edgeguard-management-ui", "name": "edgeguard-management-ui",
"private": true, "private": true,
"version": "1.0.40", "version": "1.0.41",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",

View File

@@ -75,7 +75,7 @@ const NAV: NavSection[] = [
}, },
] ]
const VERSION = '1.0.40' const VERSION = '1.0.41'
export default function Sidebar({ isOpen, onClose }: SidebarProps) { export default function Sidebar({ isOpen, onClose }: SidebarProps) {
const { t } = useTranslation() const { t } = useTranslation()