{"record":{"id":"95d409b116a2f6e8","repo":"netbirdio/netbird","slug":"failed-to-check-rule-w","errorCode":null,"errorMessage":"failed to check rule: %w","messagePattern":"failed to check rule: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/firewall/iptables/acl_linux.go","lineNumber":152,"sourceCode":"\t\t\t\tlog.Debugf(\"flush ipset %s before use: %v\", ipsetName, err)\n\t\t\t} else {\n\t\t\t\tlog.Errorf(\"flush ipset %s before use: %v\", ipsetName, err)\n\t\t\t}\n\t\t}\n\t\tif err := m.createIPSet(ipsetName); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"create ipset: %w\", err)\n\t\t}\n\t\tif err := m.addToIPSet(ipsetName, ip); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"add IP to ipset: %w\", err)\n\t\t}\n\n\t\tipList := newIpList(ip.String())\n\t\tm.ipsetStore.addIpList(ipsetName, ipList)\n\t}\n\n\tok, err := m.iptablesClient.Exists(tableFilter, chain, specs...)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to check rule: %w\", err)\n\t}\n\tif ok {\n\t\treturn nil, fmt.Errorf(\"rule already exists\")\n\t}\n\n\t// Insert DROP rules at the beginning, append ACCEPT rules at the end\n\tif action == firewall.ActionDrop {\n\t\t// Insert at the beginning of the chain (position 1)\n\t\terr = m.iptablesClient.Insert(tableFilter, chain, 1, specs...)\n\t} else {\n\t\terr = m.iptablesClient.Append(tableFilter, chain, specs...)\n\t}\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif err := m.iptablesClient.Append(tableMangle, chainRTPRE, mangleSpecs...); err != nil {\n\t\tlog.Errorf(\"failed to add mangle rule: %v\", err)","sourceCodeStart":134,"sourceCodeEnd":170,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/client/firewall/iptables/acl_linux.go#L134-L170","documentation":"go-iptables' Exists runs `iptables -t filter -C NETBIRD-ACL-INPUT <specs>`; exit status 1 means 'no match' (returns false,nil), but any other failure is returned as this error. Non-1 failures include xtables lock contention, a missing chain or table, unparseable/unsupported match extensions in the spec (e.g. `-m set --match-set <name>` when the referenced set or the xt_set match module is missing), and running as non-root (iptables exit 4).","triggerScenarios":"AddPeerFiltering when the spec references an ipset that does not exist in the kernel (set destroyed between addToIPSet and the Exists check); xt_set/xt_comment module not loaded on modular kernels; another process holding /run/xtables.lock while the agent checks; agent invoked without root so iptables refuses to list rules.","commonSituations":"Minimal container images without match extensions; iptables-legacy vs iptables-nft mixing where the rule lives in the other backend; hosts running heavy parallel iptables scripts (Docker, firewalld) causing transient lock errors.","solutions":["Run the agent as root; exit code 4 from iptables means permission denied.","Ensure the kernel set referenced by --match-set exists before rule creation (it is created earlier in the same function, so an external destroyer is the usual culprit).","Load required modules: `modprobe xt_set ip_set ip_set_hash_net`.","For xtables lock contention, retry the AddPeerFiltering call once after a short backoff, or reduce concurrent iptables users on the host."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"func checkIptablesEnv() error {\n    if os.Geteuid() != 0 {\n        return errors.New(\"iptables requires root (exit status 4 otherwise)\")\n    }\n    for _, b := range []string{\"iptables\"} {\n        if _, err := exec.LookPath(b); err != nil {\n            return fmt.Errorf(\"%s not in PATH: %w\", b, err)\n        }\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"if err := mgr.AddPeerFiltering(...); err != nil {\n    var ee *iptables.Error\n    if errors.As(err, &iptables.Error{}) || errors.As(err, &ee) {\n        // classify: exit 4 = permission, exit 2/3 = table/chain or lock issues\n        log.Errorf(\"iptables check failed with exit %d\", ee.ExitStatus())\n    }\n    if strings.Contains(err.Error(), \"failed to check rule\") && isXtablesLockHeld() {\n        time.Sleep(250 * time.Millisecond) // then retry once\n        err = mgr.AddPeerFiltering(...)\n    }\n}","preventionTips":["Always run the agent as root; iptables returns exit 4 for permission errors on every subcommand including -C.","Load xt_set and ip_set modules on modular kernels so `-m set --match-set` specs are parseable.","Avoid running competing iptables batch jobs (Docker daemon restarts, firewalld reloads) while the agent applies ACLs.","Pin one iptables backend (legacy vs nft) system-wide so rules and checks see the same ruleset."],"tags":["go","linux","iptables","firewall","acl","permissions","netbird"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}