auto-merge is a standalone set statement, not an nft "flags" value - "flags interval, auto-merge;" is a syntax error; fixed to "flags interval;" followed by "auto-merge" on its own line. Needed because large public blocklists (FireHOL, Spamhaus) contain overlapping CIDRs that nftables otherwise refuses as "conflicting intervals". Also add a hard guard: reject any "block" entry that overlaps a private/ reserved/bogon range (RFC1918, CGNAT, loopback, link-local, etc.) in both the single-entry and bulk-import paths. Public feeds like FireHOL level1 routinely include ranges like 10.0.0.0/8 and 172.16.0.0/12, meant for WAN-only edge firewalls - applied host-wide here (where WireGuard/LAN subnets legitimately live in that same private space), those entries would silently block a server's own internal/VPN traffic instead of actual bad actors. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
64 lines
2.3 KiB
Go
64 lines
2.3 KiB
Go
package firewall
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/ngoduykhanh/wireguard-ui/model"
|
|
)
|
|
|
|
// GlobalTableName is the dedicated nftables table for the host-wide
|
|
// allow/block lists, kept separate from every per-server table.
|
|
const GlobalTableName = "wireguard_ui_global"
|
|
|
|
// GenerateGlobalRuleset renders the host-wide allow/block list ruleset.
|
|
// It runs at priority -10, before any per-server table (priority 0), so it
|
|
// applies to ALL traffic on the host, not just WireGuard - this is meant to
|
|
// be the one central place for IP allow/block lists. Allow entries take
|
|
// precedence over block entries.
|
|
func GenerateGlobalRuleset(entries []model.IPListEntry) string {
|
|
var allow, block []string
|
|
for _, e := range entries {
|
|
if e.ListType == "allow" {
|
|
allow = append(allow, e.CIDR)
|
|
} else if e.ListType == "block" {
|
|
block = append(block, e.CIDR)
|
|
}
|
|
}
|
|
|
|
var b strings.Builder
|
|
fmt.Fprintf(&b, "# Host-wide allow/block list, generated by wireguard-ui-multi.\n")
|
|
fmt.Fprintf(&b, "# Applies to all traffic on this host (priority -10), before any\n")
|
|
fmt.Fprintf(&b, "# per-server WireGuard firewall table.\n\n")
|
|
|
|
fmt.Fprintf(&b, "table inet %s {\n", GlobalTableName)
|
|
|
|
fmt.Fprintf(&b, " set allowlist {\n type ipv4_addr; flags interval;\n auto-merge\n")
|
|
if len(allow) > 0 {
|
|
fmt.Fprintf(&b, " elements = { %s }\n", strings.Join(allow, ", "))
|
|
}
|
|
fmt.Fprintf(&b, " }\n\n")
|
|
|
|
fmt.Fprintf(&b, " set blocklist {\n type ipv4_addr; flags interval;\n auto-merge\n")
|
|
if len(block) > 0 {
|
|
fmt.Fprintf(&b, " elements = { %s }\n", strings.Join(block, ", "))
|
|
}
|
|
fmt.Fprintf(&b, " }\n\n")
|
|
|
|
fmt.Fprintf(&b, " chain input {\n")
|
|
fmt.Fprintf(&b, " type filter hook input priority -10; policy accept;\n")
|
|
fmt.Fprintf(&b, " ip saddr @allowlist accept comment \"wg-ui-multi: global allowlist\"\n")
|
|
fmt.Fprintf(&b, " ip saddr @blocklist drop comment \"wg-ui-multi: global blocklist\"\n")
|
|
fmt.Fprintf(&b, " }\n\n")
|
|
|
|
fmt.Fprintf(&b, " chain forward {\n")
|
|
fmt.Fprintf(&b, " type filter hook forward priority -10; policy accept;\n")
|
|
fmt.Fprintf(&b, " ip saddr @allowlist accept comment \"wg-ui-multi: global allowlist\"\n")
|
|
fmt.Fprintf(&b, " ip saddr @blocklist drop comment \"wg-ui-multi: global blocklist\"\n")
|
|
fmt.Fprintf(&b, " }\n")
|
|
|
|
fmt.Fprintf(&b, "}\n")
|
|
|
|
return b.String()
|
|
}
|