fix: Audit-Bugfixes (Auth/WAF/Firewall/Cluster/Renderer) — v1.2.94
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>
This commit is contained in:
@@ -4,8 +4,11 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"log/slog"
|
||||
"net"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
@@ -15,6 +18,10 @@ import (
|
||||
wafsvc "git.netcell-it.de/projekte/edgeguard-native/internal/services/waf"
|
||||
)
|
||||
|
||||
// wafRuleIDRe erlaubt nur einzelne CRS-Rule-IDs oder Ranges ("942100" /
|
||||
// "942100-942999") als Exclusion — verhindert SecLang-Direktiven-Injection.
|
||||
var wafRuleIDRe = regexp.MustCompile(`^[0-9]{1,9}(-[0-9]{1,9})?$`)
|
||||
|
||||
// WafHandler exposes the per-domain WAF configuration REST API:
|
||||
//
|
||||
// GET /waf/configs — list all configs (one per domain)
|
||||
@@ -110,6 +117,27 @@ func (h *WafHandler) Upsert(c *gin.Context) {
|
||||
if body.ExclusionNotes == nil {
|
||||
body.ExclusionNotes = map[string]string{}
|
||||
}
|
||||
// Exclusions müssen reine Rule-IDs/Ranges sein (sonst Direktiven-Injection
|
||||
// in die SecLang-Config via Newline).
|
||||
for _, ex := range body.RuleExclusions {
|
||||
if !wafRuleIDRe.MatchString(strings.TrimSpace(ex)) {
|
||||
response.BadRequest(c, errors.New("ungültige Rule-Exclusion (nur IDs/Ranges erlaubt): "+ex))
|
||||
return
|
||||
}
|
||||
}
|
||||
// Trusted-Proxies müssen gültige IPs/CIDRs sein.
|
||||
for _, p := range body.TrustedProxies {
|
||||
p = strings.TrimSpace(p)
|
||||
if p == "" {
|
||||
continue
|
||||
}
|
||||
if net.ParseIP(p) == nil {
|
||||
if _, _, err := net.ParseCIDR(p); err != nil {
|
||||
response.BadRequest(c, errors.New("ungültiger Trusted-Proxy (IP/CIDR): "+p))
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
cfg := models.WafConfig{
|
||||
DomainID: domainID,
|
||||
Enabled: body.Enabled,
|
||||
|
||||
Reference in New Issue
Block a user