fix(firewall+nat): NAT funktioniert end-to-end + Edge-Sysctl-Profil
Mehrere zusammenhängende Fehler beim Import der NAT-Rules von der
alten EdgeGuard-Box gefunden + behoben:
1. nft-Template: NAT-Rules landeten als Comment (gleicher
Whitespace-Trimmer-Bug wie bei den Operator-Rules vor zwei
commits). Fix: Body auf eigener Zeile via {{""}}-Padding.
2. nft-Syntax-Reihenfolge: emittierte 'tcp ip daddr X dport Y' →
parser-Fehler. Korrekt ist L3-match (ip saddr/daddr) zuerst,
dann L4 (tcp/udp dport). Reihenfolge in der dnat-Zeile
getauscht.
3. eth0 als Iface-Row hinzugefügt (Type ethernet, role wan) damit
der zone→iface-Lookup für 'wan' tatsächlich auf das Linux-Iface
trifft. Vorher war nur 'WAN'-bridge in der DB, das im Kernel
nicht existiert → iifname-match griff nicht.
4. forward-chain: ct status dnat accept (DNAT-Pakete dürfen
forwarden) + Auto-Forward pro SNAT/masquerade-Rule für die
Origin-Pakete (return geht via established,related).
5. postrouting_nat: ct status dnat masquerade als Hairpin-Catch-All
— sonst antwortet das DNAT-Ziel via seinem default-GW (oft
nicht zur EdgeGuard-Box) → SYN_SENT + UNREPLIED. Trade-off:
Backend sieht Box-IP statt client-IP.
6. Sysctl-Profil /etc/sysctl.d/99-edgeguard.conf bei jedem Install:
- Forwarding (ip_forward + ipv6 forwarding) — Voraussetzung für
ALLES NAT/DNAT/Masquerade.
- Conntrack-Buckets + max=524288 (Edge-Box trackt viele
parallele Sessions).
- HAProxy-Tuning (somaxconn 64k, rmem/wmem 16M, keepalive,
tcp_tw_reuse, ip_local_port_range).
- BBR + fq als modernes Congestion-Control + Queueing.
- Anti-DoS: tcp_syncookies, log_martians, kptr_restrict.
Verified end-to-end:
$ nc -v 89.163.205.100 2030
SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu13.16
Version 1.0.25.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -63,6 +63,22 @@ table inet edgeguard {
|
||||
|
||||
ct state established,related accept
|
||||
ct state invalid drop
|
||||
|
||||
# DNAT-rewrites aus prerouting_nat haben den ct.status DNAT-Bit
|
||||
# gesetzt — die müssen forward-passieren dürfen, sonst kommen
|
||||
# Port-Forwards (z.B. :2030 → 10.10.20.12:22) zwar durch das
|
||||
# NAT-Rewrite, scheitern aber an policy=drop. Equivalent zu
|
||||
# iptables -m conntrack --ctstate DNAT.
|
||||
ct status dnat accept
|
||||
|
||||
# Auto-Forward für SNAT/Masquerade-Origin-Pakete. Forward-chain
|
||||
# sieht das Paket VOR der postrouting-Translation; ct.status ist
|
||||
# also noch nicht "snat". Wir ziehen pro NAT-Rule das SrcCIDR
|
||||
# nach und erlauben new-state-Pakete von dort. Return-Pakete
|
||||
# gehen via ct state established schon durch.
|
||||
{{range .NATRules}}{{if or (eq .Kind "snat") (eq .Kind "masquerade")}}{{if .SrcCIDR}}
|
||||
ip saddr {{.SrcCIDR}} ct state new accept comment "auto-forward for NAT rule {{.ID}}"
|
||||
{{end}}{{end}}{{end}}
|
||||
}
|
||||
|
||||
chain output {
|
||||
@@ -71,29 +87,36 @@ table inet edgeguard {
|
||||
|
||||
chain prerouting_nat {
|
||||
type nat hook prerouting priority -100;
|
||||
{{- range .NATRules}}{{if eq .Kind "dnat"}}
|
||||
{{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}}
|
||||
{{""}}
|
||||
{{/* nft-Syntax: erst L3-match (ip saddr/daddr), DANN L4 (tcp/udp dport).
|
||||
Sonst quittiert der parser '... unexpected ip' an dieser Stelle. */}}
|
||||
{{if .InIfaces}}iifname { {{join .InIfaces ", "}} } {{end}}{{if .SrcCIDR}}ip saddr {{.SrcCIDR}} {{end}}{{if .DstCIDR}}ip daddr {{.DstCIDR}} {{end}}{{if and .Proto (ne .Proto "any")}}{{.Proto}} {{else}}meta l4proto { tcp, udp } {{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"}}
|
||||
|
||||
# Auto-Hairpin für DNAT-Pakete: alle in prerouting_nat
|
||||
# umgeschriebenen Pakete bekommen zusätzlich SNAT auf die
|
||||
# Box-IP des ausgehenden Iface (masquerade). Sonst antwortet
|
||||
# das DNAT-Ziel via seinem eigenen default-Gateway, das oft
|
||||
# nicht zur EdgeGuard-Box zeigt → SYN_SENT + UNREPLIED.
|
||||
# Trade-off: Backend sieht die Box-IP statt der echten
|
||||
# client-IP (für Logging / Geo-Block: später optional via
|
||||
# NAT-Rule-Flag preserve_client_ip).
|
||||
ct status dnat masquerade
|
||||
{{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"}}
|
||||
{{""}}
|
||||
{{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}}
|
||||
{{""}}
|
||||
{{if .OutIfaces}}oifname { {{join .OutIfaces ", "}} } {{end}}{{if .SrcCIDR}}ip saddr {{.SrcCIDR}} {{end}}masquerade
|
||||
{{end}}{{end}}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user