netbirdio/netbird · error
add outbound masquerade rule: %v
Error message
add outbound masquerade rule: %v
What it means
First concrete failure inside addPostroutingRules(): appending the outbound masquerade rule (match on PreroutingFwmarkMasquerade, not lo, jump MASQUERADE) to the NETBIRD-RT-NAT chain in the nat table. It propagates through 'add static nat rules' from createContainers() and stops router setup, meaning marked outbound traffic will never be masqueraded.
Source
Thrown at client/firewall/iptables/router_linux.go:544
if err := r.iptablesClient.DeleteIfExists(tableMangle, chainPOSTROUTING, postRule...); err != nil {
merr = multierror.Append(merr, fmt.Errorf("remove mangle postrouting rule: %w", err))
} else {
delete(r.rules, markManglePost)
}
}
return nberrors.FormatErrorOrNil(merr)
}
func (r *router) addPostroutingRules() error {
// First rule for outbound masquerade
rule1 := []string{
"-m", "mark", "--mark", fmt.Sprintf("%#x", nbnet.PreroutingFwmarkMasquerade),
"!", "-o", "lo",
"-j", routingFinalNatJump,
}
if err := r.iptablesClient.Append(tableNat, chainRTNAT, rule1...); err != nil {
return fmt.Errorf("add outbound masquerade rule: %v", err)
}
r.rules["static-nat-outbound"] = rule1
// Second rule for return traffic masquerade
rule2 := []string{
"-m", "mark", "--mark", fmt.Sprintf("%#x", nbnet.PreroutingFwmarkMasqueradeReturn),
"-o", r.wgIface.Name(),
"-j", routingFinalNatJump,
}
if err := r.iptablesClient.Append(tableNat, chainRTNAT, rule2...); err != nil {
return fmt.Errorf("add return masquerade rule: %v", err)
}
r.rules["static-nat-return"] = rule2
return nil
}
// addMSSClampingRules adds MSS clamping rules to prevent fragmentation for forwarded traffic.View on GitHub (pinned to 93e97f4bf1)
Solutions
- Run the exact append by hand as root and read stderr to identify the missing match/target
- `modprobe iptable_nat xt_MASQUERADE xt_mark`
- Ensure `sudo iptables -t nat -N NETBIRD-RT-NAT` succeeds (chain creation happens earlier in createContainers; if it failed, read that error first)
- Confirm iptables and ip6tables binaries exist and are the same implementation family
- Retry `netbird up` after clearing xtables.lock contention
Example fix
// before
if err := r.iptablesClient.Append(tableNat, chainRTNAT, rule1...); err != nil {
return fmt.Errorf("add outbound masquerade rule: %v", err)
}
// after: verify chain presence, then append with positional insert so reruns converge
if ok, _ := r.iptablesClient.ChainExists(tableNat, chainRTNAT); !ok {
if err := r.iptablesClient.NewChain(tableNat, chainRTNAT); err != nil {
return fmt.Errorf("recreate %s: %w", chainRTNAT, err)
}
}
if err := r.iptablesClient.Insert(tableNat, chainRTNAT, 1, rule1...); err != nil {
return fmt.Errorf("add outbound masquerade rule: %w", err)
} Defensive patterns
Strategy: validation
Validate before calling
func masqueradeSupported(ipt *iptables.IPTables) error {
if err := ipt.NewChain("nat", "NB-PROBE"); err != nil {
return fmt.Errorf("create probe chain: %w", err)
}
defer ipt.ClearAndDeleteChain("nat", "NB-PROBE")
rule := []string{"-o", "lo", "-j", "MASQUERADE"}
return ipt.Append("nat", "NB-PROBE", rule...)
} Try / catch
Fail fast: return the error up through createContainers so the route manager reports setup failure; do not continue with jump rules pointing at an empty NAT chain.
Prevention
- modprobe iptable_nat xt_MASQUERADE xt_mark in the host baseline
- Include `iptables -t nat -A <chain> -j MASQUERADE` in container image CI smoke tests
- Never run the routed peer as an unprivileged user
When it happens
Trigger: `iptables -t nat -A NETBIRD-RT-NAT -m mark --mark 0x... ! -o lo -j MASQUERADE` failing: xt_mark match or MASQUERADE target unavailable, iptable_nat not loaded, NETBIRD-RT-NAT chain missing after a partially failed earlier cleanup, CAP_NET_ADMIN absent, or xtables lock held.
Common situations: Hosts where the nat table exists but masquerade support is missing (custom kernels); containers with NET_ADMIN but no iptable_nat module on the host; a previous unclean shutdown left the chain-deletion half done; Alpine images using iptables-legacy while the host uses nft.
Related errors
- add return masquerade rule: %v
- add nat rule: %w
- add inverse nat rule: %w
- add static nat rules: %w
- add nat postrouting jump rule: %v
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/f653ae9b8899854d.
Report an issue: GitHub.