Files
wireguard-ui-multi/firewall/apply.go
T
sysopsandClaude Sonnet 5 eb1913d400 Add central host-wide firewall allow/block lists
New model.IPListEntry + jsondb CRUD, independent of any single WireGuard
server. firewall.GenerateGlobalRuleset builds an nftables table
(wireguard_ui_global) with allow/block sets evaluated at priority -10 -
before every per-server table - so it applies to all traffic on the host,
not just WireGuard. Allow entries always win over block entries.
firewall.ApplyGlobal loads it live via `nft -f`, scoped to that one table.

New "Global Firewall Lists" page (nav entry under Settings): add/delete
entries, ruleset preview, "Apply now (live)" with an explicit confirm()
warning since this affects the whole host's firewall, not just one server.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-12 17:45:53 +02:00

55 lines
1.7 KiB
Go

package firewall
import (
"context"
"fmt"
"os"
"os/exec"
"time"
)
// applyTable writes ruleset to a temp file and loads it with `nft -f`,
// after first deleting the given table (ignoring the error - the table may
// not exist yet on first apply). Only ever touches that single table,
// never any other nftables state.
func applyTable(tableName, ruleset string) (string, error) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
// best-effort: drop the previous version of this table so reapplying
// is idempotent. Error ignored - table may not exist yet.
_ = exec.CommandContext(ctx, "nft", "delete", "table", "inet", tableName).Run()
tmpFile, err := os.CreateTemp("", "wg-ui-multi-fw-*.nft")
if err != nil {
return "", fmt.Errorf("cannot create temp ruleset file: %w", err)
}
defer os.Remove(tmpFile.Name())
if _, err := tmpFile.WriteString(ruleset); err != nil {
tmpFile.Close()
return "", fmt.Errorf("cannot write temp ruleset file: %w", err)
}
if err := tmpFile.Close(); err != nil {
return "", fmt.Errorf("cannot close temp ruleset file: %w", err)
}
cmd := exec.CommandContext(ctx, "nft", "-f", tmpFile.Name())
out, err := cmd.CombinedOutput()
if err != nil {
return string(out), fmt.Errorf("nft -f failed: %w", err)
}
return string(out), nil
}
// Apply loads a single server's ruleset live, scoped to TableName(serverID).
func Apply(serverID, ruleset string) (string, error) {
return applyTable(TableName(serverID), ruleset)
}
// ApplyGlobal loads the host-wide allow/block list ruleset live, scoped to
// GlobalTableName.
func ApplyGlobal(ruleset string) (string, error) {
return applyTable(GlobalTableName, ruleset)
}