Files
edgeguard-native/internal/firewall/ruleset.nft.tpl
Debian b1eb940d09 fix(firewall+wg): Operator-Rule landete als Comment + wg-quick las falsche Conf
Zwei show-stopper beim Cutover .101 → .6 entdeckt + behoben:

1. nft-Template-Bug: {{- if ...}}-Whitespace-Trimmer nach der
   '# rule N' Kommentarzeile schluckte den Newline → die ganze
   Operator-Rule landete als Teil des # Kommentars. nft akzeptierte
   die Datei (legaler Comment) und der Operator sah keine Wirkung.
   Fix: Body auf eigener Zeile via {{""}}-Padding, Trimmer raus.

2. wg-Renderer schrieb /etc/edgeguard/wireguard/<iface>.conf, aber
   wg-quick@<iface>.service liest /etc/wireguard/<iface>.conf
   (Distro-Default). Die zwei Files driftet auseinander — beim
   Restart sah wg-quick die alte AllowedIPs. Fix: Renderer legt
   einen Symlink /etc/wireguard/<iface>.conf → /etc/edgeguard/...
   beim Render an (idempotent, ersetzt vorhandene Real-Files).

Beide Fixes waren voraussetzung für den .101 → .6 Cutover, der
jetzt sauber läuft: VIP .100 lebt auf .6, Unify Home dial't durch
zu wg7 (handshake), 10.0.10.x via wg7-Tunnel reachable.

Version 1.0.18.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 23:43:39 +02:00

100 lines
4.7 KiB
Smarty

#!/usr/sbin/nft -f
# Generated by edgeguard-api — DO NOT EDIT.
# Source: internal/firewall/firewall.go.
# Re-generate via `edgeguard-ctl render-config` or via API mutations.
flush ruleset
table inet edgeguard {
set peer_ipv4 {
type ipv4_addr; flags interval
{{- if .PeerIPv4}}
elements = { {{range $i, $ip := .PeerIPv4}}{{if $i}}, {{end}}{{$ip}}{{end}} }
{{- end}}
}
set peer_ipv6 {
type ipv6_addr; flags interval
{{- if .PeerIPv6}}
elements = { {{range $i, $ip := .PeerIPv6}}{{if $i}}, {{end}}{{$ip}}{{end}} }
{{- end}}
}
chain input {
type filter hook input priority 0; policy drop;
# ANTI-LOCKOUT (immer aktiv, kann von keiner Custom-Rule overruled werden)
# nft input-chain wird top-down evaluiert; eine accept-Action terminiert.
# Diese Block kommt VOR den Custom-Rules d.h. selbst wenn ein
# Operator versehentlich drop alles" baut, bleibt SSH + Admin-UI
# erreichbar.
tcp dport 22 ct state new limit rate 10/minute accept comment "anti-lockout: SSH (rate-limited)"
tcp dport 443 accept comment "anti-lockout: HAProxy public HTTPS"
tcp dport 3443 accept comment "anti-lockout: Management-UI (HAProxy admin HTTPS)"
# Stateful baseline
ct state established,related accept
ct state invalid drop
iif lo accept
# ICMP — keep PMTUD and basic diagnostics
ip protocol icmp icmp type { echo-request, destination-unreachable, time-exceeded, parameter-problem } accept
ip6 nexthdr icmpv6 icmpv6 type { echo-request, destination-unreachable, packet-too-big, time-exceeded, parameter-problem, nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert } accept
# Public ingress: HAProxy serves :80 (ACME + redirect)
tcp dport 80 accept
# Cluster-internal: peers reach edgeguard-api over mTLS on :8443
tcp dport 8443 ip saddr @peer_ipv4 accept
tcp dport 8443 ip6 saddr @peer_ipv6 accept
# ── Operator-defined rules ──
{{range .Legs}}
# rule {{.RuleID}}{{if .Name}} ({{.Name}}){{end}}{{if .Comment}} — {{.Comment}}{{end}}
{{- /* Body MUSS auf EIGENER Zeile starten (nicht via {{- }} an
die Comment-Zeile angehängt — sonst frisst nft die rule
als Teil des # Kommentars). */ -}}
{{""}}
{{if .SrcIfaces}}iifname { {{join .SrcIfaces ", "}} } {{end}}{{if .DstIfaces}}oifname { {{join .DstIfaces ", "}} } {{end}}{{if .SrcAddrs}}ip saddr { {{join .SrcAddrs ", "}} } {{end}}{{if .DstAddrs}}ip daddr { {{join .DstAddrs ", "}} } {{end}}{{with .Service}}{{if and (or (eq .Proto "tcp") (eq .Proto "udp")) .PortStart}}{{.Proto}} dport {{.PortStart}}{{if and .PortEnd (ne .PortEnd .PortStart)}}-{{.PortEnd}}{{end}} {{else if eq .Proto "icmp"}}ip protocol icmp {{else if eq .Proto "icmpv6"}}ip6 nexthdr icmpv6 {{end}}{{end}}{{if .Log}}log prefix "edgeguard:{{.RuleID}} " {{end}}{{.Action}}
{{end}}
}
chain forward {
type filter hook forward priority 0; policy drop;
ct state established,related accept
ct state invalid drop
}
chain output {
type filter hook output priority 0; policy accept;
}
chain prerouting_nat {
type nat hook prerouting priority -100;
{{- range .NATRules}}{{if eq .Kind "dnat"}}
# NAT {{.ID}} (dnat{{if .Comment}} — {{.Comment}}{{end}})
{{- if .InIfaces}} iifname { {{join .InIfaces ", "}} }{{end}}
{{- if and .Proto (ne .Proto "any")}} {{.Proto}}{{else}} meta l4proto { tcp, udp }{{end}}
{{- if .SrcCIDR}} ip saddr {{.SrcCIDR}}{{end}}
{{- if .DstCIDR}} ip daddr {{.DstCIDR}}{{end}}
{{- if .DPortStart}} dport {{.DPortStart}}{{if and .DPortEnd (ne .DPortEnd .DPortStart)}}-{{.DPortEnd}}{{end}}{{end}}
{{- if .TargetAddr}} dnat to {{.TargetAddr}}{{if .TargetPortStart}}:{{.TargetPortStart}}{{if and .TargetPortEnd (ne .TargetPortEnd .TargetPortStart)}}-{{.TargetPortEnd}}{{end}}{{end}}{{end}}
{{- end}}{{end}}
}
chain postrouting_nat {
type nat hook postrouting priority 100;
{{- range .NATRules}}{{if eq .Kind "snat"}}
# NAT {{.ID}} (snat{{if .Comment}} — {{.Comment}}{{end}})
{{- if .OutIfaces}} oifname { {{join .OutIfaces ", "}} }{{end}}
{{- if .SrcCIDR}} ip saddr {{.SrcCIDR}}{{end}}
{{- if .TargetAddr}} snat to {{.TargetAddr}}{{end}}
{{- end}}{{if eq .Kind "masquerade"}}
# NAT {{.ID}} (masquerade{{if .Comment}} — {{.Comment}}{{end}})
{{- if .OutIfaces}} oifname { {{join .OutIfaces ", "}} }{{end}}
{{- if .SrcCIDR}} ip saddr {{.SrcCIDR}}{{end}} masquerade
{{- end}}{{end}}
}
}