Verifizierte Bugs aus dem Code-Audit behoben (je mit Test/Build/nft -c geprüft):
- session: IssueWithRoleTTL mutierte geteiltes s.TTL (Data-Race + falsche TTL) → interne issue(); -race-Test.
- auth: Fallback/Federation leiteten role/TOTP nicht aus DB ab (2FA-Bypass auf Secondary, Rolle aus Remote) → viaDB-Flag + DB-Re-Lookup.
- waf: TrustedProxies waren No-op (bogus-Direktive) → XFF-Auflösung im SPOE-Agent (rightmostXFF/ipMatchesAny); RuleExclusions/TrustedProxies validiert (Direktiven-Injection); GetForHost via net.SplitHostPort.
- firewall: Auto-Rule mit IPv6-DstIP erzeugte 'ip daddr <v6>' → bricht ganzes nft-Ruleset; jetzt familienbewusst (ip/ip6, ungültige raus).
- kea: 'interfaces': null bei 0 Subnets → leeres Array.
- cluster_repair: nodeHasPublication schluckte DB-Fehler (Resync auf falschem Node) → (bool,error) fail-closed; IPv6-Primary-URL via net.JoinHostPort.
- cluster_replication: Replikations-Passwort via stdin statt psql -c (nicht mehr in argv/Logs).
- wireguard: Config (Private Key) jetzt configgen.AtomicWrite VOR Symlink/enable; SkipReload-Feld.
- render.go: --no-reload jetzt für alle Renderer (squid/unbound/chrony/wireguard).
- radius: leeres Secret/Passwort + Newlines abgelehnt; freeradius confEscape strippt CR/LF.
- configorch: continue-on-error + errors.Join statt Abbruch mitten in der Sequenz.
- i18n: fehlender Key common.status (de/en).
Verworfen als kein Bug: WAF detection-'blocked' (DetectionOnly liefert keine Interruption), render secrets.New('') (nutzt Default-Masterkey), FanOut-Sort (nur Kommentar), pg_hba (durch nft abgesichert).
Offen/bewusst zurückgestellt (low/risk): AlertWriter-Close (langlebiger Worker, vernachlässigbar), Rolling-Update-Kleinkram (sudoers-gebundener Script-Pfad / GET-State).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
65 lines
2.0 KiB
Go
65 lines
2.0 KiB
Go
package firewall
|
|
|
|
import (
|
|
"bytes"
|
|
"os"
|
|
"os/exec"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// TestTemplate_autoRuleIface prüft, dass eine Auto-Rule mit Iface als
|
|
// `iifname "<x>"`-gescopte Zeile rendert (DHCP udp/67 auf LAN) und dass
|
|
// DstIP-basierte Auto-Rules unverändert bleiben.
|
|
func TestTemplate_autoRuleIface(t *testing.T) {
|
|
view := &View{
|
|
AutoRules: []AutoFWRule{
|
|
{Proto: "udp", Port: 67, Iface: "eth1", Comment: "DHCP (Kea) auf eth1"},
|
|
{Proto: "udp", Port: 53, DstIP: "10.0.0.1", L3: "ip", Comment: "DNS"},
|
|
{Proto: "udp", Port: 53, DstIP: "2001:db8::1", L3: "ip6", Comment: "DNS v6"},
|
|
},
|
|
}
|
|
var buf bytes.Buffer
|
|
if err := tpl.Execute(&buf, view); err != nil {
|
|
t.Fatalf("template execute: %v", err)
|
|
}
|
|
out := buf.String()
|
|
|
|
if !strings.Contains(out, `iifname "eth1" udp dport 67 accept comment "auto: DHCP (Kea) auf eth1"`) {
|
|
t.Errorf("missing iface-scoped DHCP auto-rule\n----\n%s", out)
|
|
}
|
|
// v4-DstIP-Auto-Rule: ip daddr.
|
|
if !strings.Contains(out, `ip daddr 10.0.0.1 udp dport 53 accept`) {
|
|
t.Errorf("v4 DstIP auto-rule wrong\n----\n%s", out)
|
|
}
|
|
// Fix #5: v6-DstIP muss `ip6 daddr` ergeben (sonst bricht nft das Ruleset).
|
|
if !strings.Contains(out, `ip6 daddr 2001:db8::1 udp dport 53 accept`) {
|
|
t.Errorf("v6 DstIP auto-rule must use ip6 daddr\n----\n%s", out)
|
|
}
|
|
|
|
// Echte nft-Syntaxvalidierung (braucht root → via sudo, sonst skip).
|
|
nft, err := exec.LookPath("nft")
|
|
if err != nil {
|
|
t.Skip("nft not in PATH")
|
|
}
|
|
f, err := os.CreateTemp(t.TempDir(), "autorule-*.nft")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
_, _ = f.WriteString(out)
|
|
f.Close()
|
|
var cmd *exec.Cmd
|
|
if os.Geteuid() == 0 {
|
|
cmd = exec.Command(nft, "-c", "-f", f.Name())
|
|
} else {
|
|
cmd = exec.Command("sudo", "-n", nft, "-c", "-f", f.Name())
|
|
}
|
|
if combined, err := cmd.CombinedOutput(); err != nil {
|
|
msg := string(combined)
|
|
if strings.Contains(msg, "Operation not permitted") || strings.Contains(msg, "password is required") {
|
|
t.Skipf("nft -c needs root: %s", strings.TrimSpace(msg))
|
|
}
|
|
t.Fatalf("nft -c rejected ruleset: %v\n%s\n----\n%s", err, combined, out)
|
|
}
|
|
}
|