Files
wireguard-ui-multi/internal/firewall/nftables.go
T
sysopsandClaude Sonnet 5 3b3ffd8ebf Add wireguard-ui-multi core: multi-server DB, WireGuard manager, REST API, UI, installers
Implements the from-scratch multi-server WireGuard management fork per
CLAUDE.md spec: sqlite schema (servers/peers/audit_log/users), Curve25519
key generation, per-interface config rendering + wg-quick/systemd control,
nftables hook scaffolding, session+CSRF-protected REST API with QR code
and config download endpoints, a minimal vanilla-JS web UI, legacy
wg0.conf migration, and both a native installer and a Proxmox LXC
provisioning script (with auto-detected latest Debian template).

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

76 lines
2.2 KiB
Go

package firewall
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"gitea.perlbach24.de/scripte/wireguard-ui-multi/internal/server"
)
// HooksDir holds optional user-defined shell scripts run around lifecycle events.
var HooksDir = "/etc/wireguard-manager/hooks"
// HookEvent names the lifecycle points a hook script may exist for.
type HookEvent string
const (
HookServerStart HookEvent = "server-start"
HookServerStop HookEvent = "server-stop"
HookPeerAdd HookEvent = "peer-add"
HookPeerRemove HookEvent = "peer-remove"
)
// RunHook executes /etc/wireguard-manager/hooks/<event> if present and executable,
// passing iface (and optionally peer pubkey) as arguments. Missing hook is not an error.
func RunHook(event HookEvent, args ...string) error {
path := filepath.Join(HooksDir, string(event))
if _, err := os.Stat(path); err != nil {
return nil // hook not installed, skip silently
}
cmd := exec.Command(path, args...)
if out, err := cmd.CombinedOutput(); err != nil {
return fmt.Errorf("hook %s: %w: %s", event, err, out)
}
return nil
}
// NFTRuleset renders a suggested nftables ruleset snippet for a server, allowing
// its UDP listen port in and forwarding traffic between the tunnel and lanIface.
func NFTRuleset(srv *server.Server, lanIface string) string {
return fmt.Sprintf(`table inet wireguard_%s {
chain input {
type filter hook input priority 0; policy accept;
udp dport %d accept
}
chain forward {
type filter hook forward priority 0; policy accept;
iifname "%s" oifname "%s" accept
iifname "%s" oifname "%s" accept
}
}
`, srv.InterfaceName, srv.ListenPort, srv.InterfaceName, lanIface, lanIface, srv.InterfaceName)
}
// ApplyRuleset writes the ruleset to a temp file and loads it with `nft -f`.
func ApplyRuleset(srv *server.Server, lanIface string) error {
tmp, err := os.CreateTemp("", "wgm-nft-*.conf")
if err != nil {
return err
}
defer os.Remove(tmp.Name())
if _, err := tmp.WriteString(NFTRuleset(srv, lanIface)); err != nil {
tmp.Close()
return err
}
tmp.Close()
cmd := exec.Command("nft", "-f", tmp.Name())
if out, err := cmd.CombinedOutput(); err != nil {
return fmt.Errorf("nft -f: %w: %s", err, out)
}
return nil
}