feat(fw): Renderer-Rewrite + auto-apply + Anti-Lockout
internal/firewall/firewall.go komplett neu: joint zone-iface-mapping
(network_interfaces.role), address objects + groups (members
expandiert), services + groups, rules, nat-rules. Output: einheitliche
View mit Legs (rule × service cross-product) damit das Template kein
sub-template/dict braucht.
Template:
* Anti-Lockout-Block am input-chain-Top (SSH+443 immer erlaubt,
KANN nicht von Custom-Rules overruled werden — User-Wunsch).
* Rules: pro Leg eine nft-Zeile mit iif/oif sets, ip saddr/daddr,
proto+dport, optional log-prefix.
* prerouting_nat: iteriert dnat-Rules.
* postrouting_nat: snat + masquerade.
Auto-apply: FirewallHandler bekommt einen Reloader-Hook der nach
jedem POST/PUT/DELETE aufgerufen wird. main.go injected
firewall.New(pool).Render — schreibt + sudo nft -f.
Sudoers (/etc/sudoers.d/edgeguard): NOPASSWD für 'nft -f
/etc/edgeguard/nftables.d/ruleset.nft'. configgen.ReloadService
nutzt jetzt sudo (haproxy reload klappte vorher nicht aus dem
edgeguard-User).
Frontend (Sweep): style={{ marginBottom: 16 }} → className="mb-16"
in allen 7 Firewall-Tabs — User-Feedback "globales CSS statt inline".
Live auf 89.163.205.6: nft list table inet edgeguard zeigt
Anti-Lockout + Baseline + Cluster-Peer-Set + (jetzt noch leere)
Custom-Rules-Sektion. render-config postinst-mäßig sauber.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
#!/usr/sbin/nft -f
|
||||
# Generated by edgeguard-api — DO NOT EDIT.
|
||||
# Source: internal/firewall/firewall.go (template: ruleset.nft.tpl).
|
||||
# Re-generate via `edgeguard-ctl render-config`.
|
||||
# Source: internal/firewall/firewall.go.
|
||||
# Re-generate via `edgeguard-ctl render-config` or via API mutations.
|
||||
|
||||
flush ruleset
|
||||
|
||||
@@ -22,6 +22,14 @@ table inet edgeguard {
|
||||
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: Management-UI (HAProxy/HTTPS)"
|
||||
|
||||
# Stateful baseline
|
||||
ct state established,related accept
|
||||
ct state invalid drop
|
||||
@@ -31,39 +39,66 @@ table inet edgeguard {
|
||||
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
|
||||
|
||||
# SSH — rate-limit to keep brute-force out of the auth log
|
||||
tcp dport 22 ct state new limit rate 10/minute accept
|
||||
tcp dport 22 drop
|
||||
|
||||
# Public ingress: HAProxy terminates TLS on :443 and serves :80
|
||||
tcp dport { 80, 443 } 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
|
||||
|
||||
{{- range .CustomRulesInput}}
|
||||
# {{.Comment}}
|
||||
{{.MatchExpr}} {{.Action}}
|
||||
{{- end}}
|
||||
# ── Operator-defined rules ──
|
||||
{{- range .Legs}}
|
||||
# rule {{.RuleID}}{{if .Name}} ({{.Name}}){{end}}{{if .Comment}} — {{.Comment}}{{end}}
|
||||
{{- 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
|
||||
|
||||
{{- range .CustomRulesForward}}
|
||||
# {{.Comment}}
|
||||
{{.MatchExpr}} {{.Action}}
|
||||
{{- end}}
|
||||
}
|
||||
|
||||
chain output {
|
||||
type filter hook output priority 0; policy accept;
|
||||
{{- range .CustomRulesOutput}}
|
||||
# {{.Comment}}
|
||||
{{.MatchExpr}} {{.Action}}
|
||||
{{- end}}
|
||||
}
|
||||
|
||||
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}}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user