fix(security-audit): HTTPS/Port-Checks erkennen nftables Set-Syntax nicht
strings.Contains(nftStr, "dport 443") fand den Substring nicht, wenn
nftables mehrere Ports als Set ausgibt ("tcp dport { 80, 443 } accept" statt
"tcp dport 443 accept") — das ist bei archivmail-Installationen der
Normalfall (80+443 stehen zusammen in einer Regel). Dashboard zeigte
dadurch fälschlich "Kein HTTPS — Verbindungen unverschlüsselt", obwohl
Port 443 korrekt offen war (bestätigt auf 131 und 132).
Neue Helper-Funktion nftHasPort() per Regex erkennt beide Formen
(Einzelport und Set). Betrifft die Checks für HTTPS/443, Port 3000 und
Port 8080.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
c274df5023
commit
04e5b0f74a
@@ -6,12 +6,39 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ── Security Audit ──────────────────────────────────────────────────────────
|
||||
|
||||
// nftDportRe matches "dport 443" as well as the set form nft uses for
|
||||
// multiple ports on one rule, e.g. "dport { 80, 443 }".
|
||||
var nftDportRe = regexp.MustCompile(`dport\s+(\{[^}]*\}|\d+)`)
|
||||
|
||||
// nftHasPort reports whether nft's "list ruleset" output opens the given
|
||||
// port, either as a standalone "dport <port>" rule or as a member of a
|
||||
// "dport { ... }" set. A plain strings.Contains(nftStr, "dport 443") misses
|
||||
// the set form because "{ 80, " sits between "dport" and "443", producing a
|
||||
// false "no HTTPS" warning even when port 443 is correctly whitelisted.
|
||||
func nftHasPort(nftStr, port string) bool {
|
||||
for _, m := range nftDportRe.FindAllStringSubmatch(nftStr, -1) {
|
||||
group := m[1]
|
||||
if group == port {
|
||||
return true
|
||||
}
|
||||
if strings.HasPrefix(group, "{") {
|
||||
for _, p := range strings.Split(strings.Trim(group, "{} "), ",") {
|
||||
if strings.TrimSpace(p) == port {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type securityCheck struct {
|
||||
Name string `json:"name"`
|
||||
Status string `json:"status"` // "ok" | "warning" | "error"
|
||||
@@ -49,7 +76,7 @@ func (s *Server) handleSecurityAudit(w http.ResponseWriter, r *http.Request) {
|
||||
// 2. Port 3000 (Next.js) extern erreichbar?
|
||||
if !firewallActive {
|
||||
checks = append(checks, securityCheck{Name: "Port 3000 (Next.js)", Status: "error", Message: "Keine Firewall aktiv — Port möglicherweise extern erreichbar"})
|
||||
} else if strings.Contains(nftStr, "dport 3000") {
|
||||
} else if nftHasPort(nftStr, "3000") {
|
||||
checks = append(checks, securityCheck{Name: "Port 3000 (Next.js)", Status: "warning", Message: "Port 3000 explizit in Firewall-Regeln — prüfen ob gewollt"})
|
||||
} else {
|
||||
checks = append(checks, securityCheck{Name: "Port 3000 (Next.js)", Status: "ok", Message: "Blockiert (nicht in Whitelist)"})
|
||||
@@ -58,14 +85,14 @@ func (s *Server) handleSecurityAudit(w http.ResponseWriter, r *http.Request) {
|
||||
// 3. Port 8080 (Go Backend) extern erreichbar?
|
||||
if !firewallActive {
|
||||
checks = append(checks, securityCheck{Name: "Port 8080 (Go Backend)", Status: "error", Message: "Keine Firewall aktiv — Port möglicherweise extern erreichbar"})
|
||||
} else if strings.Contains(nftStr, "dport 8080") {
|
||||
} else if nftHasPort(nftStr, "8080") {
|
||||
checks = append(checks, securityCheck{Name: "Port 8080 (Go Backend)", Status: "warning", Message: "Port 8080 explizit in Firewall-Regeln — prüfen ob gewollt"})
|
||||
} else {
|
||||
checks = append(checks, securityCheck{Name: "Port 8080 (Go Backend)", Status: "ok", Message: "Blockiert (nicht in Whitelist)"})
|
||||
}
|
||||
|
||||
// 4. HTTPS aktiv?
|
||||
if firewallActive && strings.Contains(nftStr, "dport 443") {
|
||||
if firewallActive && nftHasPort(nftStr, "443") {
|
||||
checks = append(checks, securityCheck{Name: "HTTPS (TLS)", Status: "ok", Message: "Port 443 in Firewall freigegeben"})
|
||||
} else {
|
||||
checks = append(checks, securityCheck{Name: "HTTPS (TLS)", Status: "warning", Message: "Kein HTTPS — Verbindungen unverschlüsselt (certbot empfohlen)"})
|
||||
|
||||
Reference in New Issue
Block a user