{"record":{"id":"3afff82025225b82","repo":"netbirdio/netbird","slug":"add-nat-postrouting-jump-rule-v","errorCode":null,"errorMessage":"add nat postrouting jump rule: %v","messagePattern":"add nat postrouting jump rule: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/firewall/iptables/router_linux.go","lineNumber":612,"sourceCode":"func (r *router) insertEstablishedRule(chain string) error {\n\testablishedRule := getConntrackEstablished()\n\n\terr := r.iptablesClient.Insert(tableFilter, chain, 1, establishedRule...)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to insert established rule: %v\", err)\n\t}\n\n\truleKey := \"established-\" + chain\n\tr.rules[ruleKey] = establishedRule\n\n\treturn nil\n}\n\nfunc (r *router) addJumpRules() error {\n\t// Jump to nat chain\n\tnatRule := []string{\"-j\", chainRTNAT}\n\tif err := r.iptablesClient.Insert(tableNat, chainPOSTROUTING, 1, natRule...); err != nil {\n\t\treturn fmt.Errorf(\"add nat postrouting jump rule: %v\", err)\n\t}\n\tr.rules[jumpNatPost] = natRule\n\n\t// Jump to mangle prerouting chain\n\tpreRule := []string{\"-j\", chainRTPRE}\n\tif err := r.iptablesClient.Insert(tableMangle, chainPREROUTING, 1, preRule...); err != nil {\n\t\treturn fmt.Errorf(\"add mangle prerouting jump rule: %v\", err)\n\t}\n\tr.rules[jumpManglePre] = preRule\n\n\t// Jump to nat prerouting chain\n\trdrRule := []string{\"-j\", chainRTRDR}\n\tif err := r.iptablesClient.Insert(tableNat, chainPREROUTING, 1, rdrRule...); err != nil {\n\t\treturn fmt.Errorf(\"add nat prerouting jump rule: %v\", err)\n\t}\n\tr.rules[jumpNatPre] = rdrRule\n\n\treturn nil","sourceCodeStart":594,"sourceCodeEnd":630,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/client/firewall/iptables/router_linux.go#L594-L630","documentation":"First insert in addJumpRules(): puts `-j NETBIRD-RT-NAT` at position 1 of the built-in nat POSTROUTING chain so masqueraded traffic enters NetBird's chain. Wrapped as 'add jump rules' from createContainers(), failure aborts router setup and leaves the custom chains created but unreachable.","triggerScenarios":"`iptables -t nat -I POSTROUTING 1 -j NETBIRD-RT-NAT` failing when the nat table is missing (iptable_nat unloaded), CAP_NET_ADMIN is absent, the xtables lock is held, or another manager rewrote POSTROUTING between the agent's reads and writes.","commonSituations":"Same setup-time class as the other jump-rule errors: module-less kernels, unprivileged containers, docker/firewalld lock contention; also hosts where an admin replaced POSTROUTING wholesale with nft rules while the agent still speaks legacy iptables.","solutions":["Reproduce manually: `sudo iptables -t nat -I POSTROUTING 1 -j NETBIRD-RT-NAT`","`modprobe iptable_nat` (ip6table_nat for the v6 router)","Verify CAP_NET_ADMIN and that /run/xtables.lock is not held","Check `iptables --version` family consistency (iptables-legacy vs iptables-nft) across the system","Run `netbird down` then `netbird up` for a clean retry"],"exampleFix":"// before\nif err := r.iptablesClient.Insert(tableNat, chainPOSTROUTING, 1, natRule...); err != nil {\n    return fmt.Errorf(\"add nat postrouting jump rule: %v\", err)\n}\n\n// after: idempotent insert tolerant of an existing jump from a previous run\nexists, _ := r.iptablesClient.Exists(tableNat, chainPOSTROUTING, natRule...)\nif !exists {\n    if err := r.iptablesClient.Insert(tableNat, chainPOSTROUTING, 1, natRule...); err != nil {\n        return fmt.Errorf(\"add nat postrouting jump rule: %w\", err)\n    }\n}\nr.rules[jumpNatPost] = natRule","handlingStrategy":"validation","validationCode":"func builtinChainWritable(ipt *iptables.IPTables, table, chain string) error {\n    probe := []string{\"-m\", \"comment\", \"--comment\", \"nb-probe\", \"-j\", \"RETURN\"}\n    if err := ipt.Insert(table, chain, 1, probe...); err != nil {\n        return fmt.Errorf(\"%s/%s: %w\", table, chain, err)\n    }\n    return ipt.DeleteIfExists(table, chain, probe...)\n}","typeGuard":null,"tryCatchPattern":"Propagate as fatal for router init; ensure the previously created custom chains get cleaned by the existing cleanup path on the way out.","preventionTips":["Load iptable_nat before agent start","Avoid mid-start firewall backend switches (legacy to nft)","Keep CAP_NET_ADMIN on the daemon for the whole uptime, not just start"],"tags":["network","linux","iptables","nat","firewall"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}