netbirdio/netbird · error

add static nat rules: %w

Error message

add static nat rules: %w

What it means

Returned by the NetBird iptables router manager when addPostroutingRules() fails while programming the NETBIRD-RT-NAT chain in the nat table. The agent (running as root) shells out to iptables via coreos/go-iptables to append two static masquerade rules, and any non-zero iptables exit is wrapped with this message inside createContainers(). It aborts router initialization, so network routes / exit-node forwarding cannot come up.

Source

Thrown at client/firewall/iptables/router_linux.go:471

			if err := r.iptablesClient.ClearAndDeleteChain(chainInfo.table, chainInfo.chain); err != nil {
				log.Warnf("clear stale chain %s in %s: %v", chainInfo.chain, chainInfo.table, err)
			}
		}
		if err := r.iptablesClient.NewChain(chainInfo.table, chainInfo.chain); err != nil {
			return fmt.Errorf("create chain %s in table %s: %w", chainInfo.chain, chainInfo.table, err)
		}
	}

	if err := r.insertEstablishedRule(chainRTFWDIN); err != nil {
		return fmt.Errorf("insert established rule: %w", err)
	}

	if err := r.insertEstablishedRule(chainRTFWDOUT); err != nil {
		return fmt.Errorf("insert established rule: %w", err)
	}

	if err := r.addPostroutingRules(); err != nil {
		return fmt.Errorf("add static nat rules: %w", err)
	}

	if err := r.addJumpRules(); err != nil {
		return fmt.Errorf("add jump rules: %w", err)
	}

	if err := r.addMSSClampingRules(); err != nil {
		log.Errorf("failed to add MSS clamping rules: %s", err)
	}

	return nil
}

// setupDataPlaneMark configures the fwmark for the data plane
func (r *router) setupDataPlaneMark() error {
	var merr *multierror.Error
	preRule := []string{
		"-i", r.wgIface.Name(),

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. Reproduce by hand as root: `iptables -t nat -A NETBIRD-RT-NAT -m mark --mark 0x1000 ! -o lo -j MASQUERADE` and read the stderr in the error chain
  2. Load the missing modules: `modprobe iptable_nat xt_MASQUERADE xt_mark` (and ip6table_nat for IPv6)
  3. Install an iptables implementation: `apt install iptables` (or iptables-nft) and confirm `iptables --version` works for both families
  4. Run the agent as root or grant CAP_NET_ADMIN to the container; verify with `iptables -L -n`
  5. Free the xtables lock: stop competing firewall tooling or wait and retry `netbird up`

Example fix

// before: agent fails at startup with only the wrapped error
if err := r.addPostroutingRules(); err != nil {
    return fmt.Errorf("add static nat rules: %w", err)
}

// after: pre-flight the nat table so the real cause surfaces early
if err := probeNatTable(r.iptablesClient); err != nil {
    return fmt.Errorf("nat table check: %w", err)
}
if err := r.addPostroutingRules(); err != nil {
    return fmt.Errorf("add static nat rules: %w", err)
}
Defensive patterns

Strategy: validation

Validate before calling

// Pre-flight before starting the router: prove the nat table accepts rules
func probeNatTable(ipt *iptables.IPTables) error {
    probe := []string{"-m", "comment", "--comment", "nb-nat-probe", "-j", "RETURN"}
    if err := ipt.Append("nat", "POSTROUTING", probe...); err != nil {
        return fmt.Errorf("nat table unusable: %w", err)
    }
    return ipt.DeleteIfExists("nat", "POSTROUTING", probe...)
}

Type guard

func isPermissionError(err error) bool {
    var ee *exec.ExitError
    return errors.As(err, &ee) && strings.Contains(err.Error(), "Permission denied")
}

Try / catch

Abort setup on first postrouting failure and run the router Reset path so half-created chains are removed before returning the error upward.

Prevention

When it happens

Trigger: Calling router.init() -> createContainers() -> addPostroutingRules() when `iptables -t nat -A NETBIRD-RT-NAT -m mark --mark 0x... ! -o lo -j MASQUERADE` fails: nat table missing (iptable_nat/nft module not loaded), MASQUERADE or mark match (xt_mark) unavailable, missing CAP_NET_ADMIN, no iptables binary, or /run/xtables.lock held by another process.

Common situations: Containers (Docker/LXC/OpenVZ) without CAP_NET_ADMIN or without iptables kernel modules; minimal Debian/Alpine images or nftables-only distros without iptables-nft installed; kernels shipped by some VPS providers without iptable_nat; concurrent firewall daemons (firewalld, ufw, docker) holding the xtables lock during `netbird up`.

Related errors


AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16). Data as JSON: /api/errors/c4cb1921822964ad. Report an issue: GitHub.