fix: Move VPN masquerade rules from iptables PostUp to nftables

iptables-nft rules added by WireGuard's PostUp were being flushed by
Docker on startup, breaking VPN LAN routing until the next reboot.
The nftables inet wild-central table survives Docker restarts.
This commit is contained in:
2026-10-23 13:32:41 +00:00
parent 1176c7715a
commit d35d0854cb
4 changed files with 76 additions and 20 deletions

View File

@@ -50,6 +50,20 @@ func (api *API) vpnAutoUDPPorts() []int {
return []int{cfg.ListenPort}
}
// vpnCIDR returns the VPN subnet CIDR (e.g. "10.8.0.0/24") when VPN is enabled
// with LAN routing configured. Used to generate nftables masquerade rules.
func (api *API) vpnCIDR() string {
cfg, err := api.vpn.GetConfig()
if err != nil || !cfg.Enabled || cfg.LanCIDR == "" {
return ""
}
_, vpnNet, err := net.ParseCIDR(cfg.Address)
if err != nil {
return ""
}
return vpnNet.String()
}
// syncNftablesOnly regenerates and applies nftables rules from the current global config
// without touching HAProxy. Called when firewall settings change independently.
// Runs errors are logged only — the caller does not wait for completion.
@@ -76,7 +90,8 @@ func (api *API) syncNftablesOnly(globalCfg *config.State) {
extraTCP, extraUDP := config.SplitExtraPorts(nftCfg.ExtraPorts)
extraUDP = append(extraUDP, api.vpnAutoUDPPorts()...)
ports := api.haproxy.GetListenPorts(customRoutes)
nftContent := api.nftables.Generate(ports, extraTCP, extraUDP, nftCfg.WANInterface)
vpnCIDR := api.vpnCIDR()
nftContent := api.nftables.Generate(ports, extraTCP, extraUDP, nftCfg.WANInterface, vpnCIDR)
if err := api.nftables.WriteRules(nftContent); err != nil {
slog.Error("failed to write nftables rules", "component", "nftables-sync", "error", err)
return