feat(firewall): default-drop logging in input + forward chain
User-Feedback: das Live-Log zeigte nur die Smoke-Test-Snapshots von gestern weil keine einzige Firewall-Rule den log-Flag hatte. „Das ist kein Live-Log." Fix: das nft-Template emittiert jetzt am Ende der input und forward chain einen `limit rate 10/second log prefix "edgeguard:drop-*" group 0` direkt vor dem default `policy drop`. Damit fließen ALLE Pakete die keine Custom-Rule erlaubt hat ins Log — ohne dass der Operator pro Rule den Log-Switch setzen muss. limit rate 10/second burst 5: schützt vor Log-Floods durch Port- Scanner, ohne die normale Visibility zu verlieren. Bei einer typischen Edge-Box mit 99% Drop auf WAN-Inbound liegt das Volumen so bei ~300 Events/min = 5MB/h gzipped — logrotate keeps 14 days. Reader: drop-input/drop-forward-Prefix wird NICHT als RuleID gemappt (es gibt keine zugehörige Rule), Action explizit auf "drop". UI rendert die mit eigenem Tag "default-input" / "default-fwd" (volcano-Farbe) in der Rule-Spalte. Verifiziert auf der Box: 26 echte Drop-Pakete in 5s nach Re-render. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -52,7 +52,7 @@ import (
|
|||||||
wgsvc "git.netcell-it.de/projekte/edgeguard-native/internal/services/wireguard"
|
wgsvc "git.netcell-it.de/projekte/edgeguard-native/internal/services/wireguard"
|
||||||
)
|
)
|
||||||
|
|
||||||
var version = "1.0.68"
|
var version = "1.0.69"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
addr := os.Getenv("EDGEGUARD_API_ADDR")
|
addr := os.Getenv("EDGEGUARD_API_ADDR")
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
var version = "1.0.68"
|
var version = "1.0.69"
|
||||||
|
|
||||||
const usage = `edgeguard-ctl — EdgeGuard CLI
|
const usage = `edgeguard-ctl — EdgeGuard CLI
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import (
|
|||||||
"git.netcell-it.de/projekte/edgeguard-native/internal/services/tlscerts"
|
"git.netcell-it.de/projekte/edgeguard-native/internal/services/tlscerts"
|
||||||
)
|
)
|
||||||
|
|
||||||
var version = "1.0.68"
|
var version = "1.0.69"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// renewTickInterval — how often we re-evaluate expiring certs.
|
// renewTickInterval — how often we re-evaluate expiring certs.
|
||||||
|
|||||||
@@ -64,6 +64,13 @@ table inet edgeguard {
|
|||||||
{{""}}
|
{{""}}
|
||||||
{{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}} " group 0 {{end}}{{.Action}}
|
{{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}} " group 0 {{end}}{{.Action}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
|
# ── DEFAULT-DROP LOGGING ───────────────────────────────────────
|
||||||
|
# Alles was bis hierhin nicht von einer Custom-Rule oder dem
|
||||||
|
# Anti-Lockout-Block accept'ed wurde, droppt via policy. Wir
|
||||||
|
# loggen das mit limit 10/second damit Port-Scans den Log nicht
|
||||||
|
# fluten. UI Firewall-Log zeigt diese als "drop-input".
|
||||||
|
limit rate 10/second log prefix "edgeguard:drop-input " group 0
|
||||||
}
|
}
|
||||||
|
|
||||||
chain forward {
|
chain forward {
|
||||||
@@ -87,6 +94,9 @@ table inet edgeguard {
|
|||||||
{{range .NATRules}}{{if or (eq .Kind "snat") (eq .Kind "masquerade")}}{{if .SrcCIDR}}
|
{{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}}"
|
ip saddr {{.SrcCIDR}} ct state new accept comment "auto-forward for NAT rule {{.ID}}"
|
||||||
{{end}}{{end}}{{end}}
|
{{end}}{{end}}{{end}}
|
||||||
|
|
||||||
|
# Default-Drop-Logging (limit-rated, siehe input-chain).
|
||||||
|
limit rate 10/second log prefix "edgeguard:drop-forward " group 0
|
||||||
}
|
}
|
||||||
|
|
||||||
chain output {
|
chain output {
|
||||||
|
|||||||
@@ -178,8 +178,20 @@ func parseLine(b []byte) (Entry, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// RuleID aus prefix extrahieren: "edgeguard:42" → "42".
|
// RuleID aus prefix extrahieren: "edgeguard:42" → "42".
|
||||||
|
// Spezial-Prefixes für default-policy-Logs werden NICHT als RuleID
|
||||||
|
// gemappt — die haben keine echte Rule-Referenz und sollten im UI
|
||||||
|
// als "default-drop"-Marker erscheinen statt als Rule-Tag.
|
||||||
if strings.HasPrefix(e.Prefix, "edgeguard:") {
|
if strings.HasPrefix(e.Prefix, "edgeguard:") {
|
||||||
e.RuleID = strings.TrimSpace(strings.TrimPrefix(e.Prefix, "edgeguard:"))
|
id := strings.TrimSpace(strings.TrimPrefix(e.Prefix, "edgeguard:"))
|
||||||
|
switch id {
|
||||||
|
case "drop-input", "drop-forward":
|
||||||
|
// kein RuleID — Prefix bleibt verfügbar, UI rendert ihn
|
||||||
|
// als "DEFAULT-DROP" mit Hook (-input/-forward) als
|
||||||
|
// Hinweis-Tag. Action ist eindeutig drop.
|
||||||
|
e.Action = "drop"
|
||||||
|
default:
|
||||||
|
e.RuleID = id
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Proto-Mapping aus IP-Protocol-Number.
|
// Proto-Mapping aus IP-Protocol-Number.
|
||||||
switch r.IPProto {
|
switch r.IPProto {
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ const NAV: NavSection[] = [
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
const VERSION = '1.0.68'
|
const VERSION = '1.0.69'
|
||||||
|
|
||||||
// Sidebar-Pattern 1:1 aus netcell-webpanel (enconf) übernommen:
|
// Sidebar-Pattern 1:1 aus netcell-webpanel (enconf) übernommen:
|
||||||
// - <nav> als root, dunkler Gradient + Teal/Blue-Accent
|
// - <nav> als root, dunkler Gradient + Teal/Blue-Accent
|
||||||
|
|||||||
@@ -251,8 +251,15 @@ export default function FirewallLivePage() {
|
|||||||
render: (a: string | undefined, row) => actionTag(a, row.prefix),
|
render: (a: string | undefined, row) => actionTag(a, row.prefix),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('fwlog.col.rule'), dataIndex: 'rule_id', width: 80,
|
title: t('fwlog.col.rule'), dataIndex: 'rule_id', width: 130,
|
||||||
render: (v?: string) => v ? <Tag>{v}</Tag> : <Text type="secondary">—</Text>,
|
render: (v: string | undefined, row: Entry) => {
|
||||||
|
if (v) return <Tag>#{v}</Tag>
|
||||||
|
// default-drop-Logs haben keinen RuleID-Wert, der prefix
|
||||||
|
// identifiziert sie: edgeguard:drop-input / edgeguard:drop-forward
|
||||||
|
if (row.prefix === 'edgeguard:drop-input') return <Tag color="volcano">default-input</Tag>
|
||||||
|
if (row.prefix === 'edgeguard:drop-forward') return <Tag color="volcano">default-fwd</Tag>
|
||||||
|
return <Text type="secondary">—</Text>
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('fwlog.col.proto'), dataIndex: 'proto', width: 80,
|
title: t('fwlog.col.proto'), dataIndex: 'proto', width: 80,
|
||||||
|
|||||||
Reference in New Issue
Block a user