Add per-server NAT egress, ip_forward auto-enable, OPNsense import review checklist

- ServerSetting gains WanInterface/EgressSNATIP for optional per-server
  masquerade/SNAT of client traffic, isolated in each server's own
  nftables table
- wireguard.Start/Restart now ensure net.ipv4.ip_forward and
  net.ipv6.conf.all.forwarding are enabled before bringing an interface up
- OPNsense config.xml import now parses staticroutes/filter/nat rules and
  surfaces them as a manual-review checklist in the preview UI (never
  auto-applied)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ATVUwTa4Pqwq26orW5BcDW
This commit is contained in:
sysops
2026-07-29 13:08:52 +02:00
co-authored by Claude Sonnet 5
parent cf9c136874
commit 83b1da291f
7 changed files with 1132 additions and 5 deletions
+24
View File
@@ -3,7 +3,9 @@ package wireguard
import (
"context"
"fmt"
"os"
"os/exec"
"strings"
"github.com/ngoduykhanh/wireguard-ui/util"
)
@@ -17,6 +19,7 @@ func UnitName(iface string) string {
// Start brings up the given WireGuard interface via
// `systemctl start wg-quick@<iface>.service`.
func Start(ctx context.Context, iface string) error {
EnsureIPForwarding()
return runSystemctl(ctx, "start", iface)
}
@@ -29,9 +32,30 @@ func Stop(ctx context.Context, iface string) error {
// Restart restarts the given WireGuard interface via
// `systemctl restart wg-quick@<iface>.service`.
func Restart(ctx context.Context, iface string) error {
EnsureIPForwarding()
return runSystemctl(ctx, "restart", iface)
}
// EnsureIPForwarding turns on IPv4/IPv6 forwarding for the running kernel
// (equivalent to `sysctl -w net.ipv4.ip_forward=1`), best-effort. Without
// this, any server relying on FORWARD rules or NAT egress silently drops
// all forwarded traffic. Persistence across reboots (e.g.
// /etc/sysctl.d/*.conf) is left to install-time setup, not this runtime
// call - this only guarantees the currently running kernel is correct
// whenever a server is (re)started.
func EnsureIPForwarding() {
setSysctl("/proc/sys/net/ipv4/ip_forward")
setSysctl("/proc/sys/net/ipv6/conf/all/forwarding")
}
func setSysctl(path string) {
data, err := os.ReadFile(path)
if err == nil && strings.TrimSpace(string(data)) == "1" {
return
}
_ = os.WriteFile(path, []byte("1"), 0644)
}
func runSystemctl(ctx context.Context, action, iface string) error {
if !util.ValidateInterfaceName(iface) {
return fmt.Errorf("invalid interface name: %q", iface)