{"record":{"id":"05e3a95b644dd060","repo":"netbirdio/netbird","slug":"apply-firewall-rule-w","errorCode":null,"errorMessage":"apply firewall rule: %w","messagePattern":"apply firewall rule: %w","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/internal/acl/manager.go","lineNumber":180,"sourceCode":"\tipsetByRuleSelectors := make(map[string]string)\n\n\t// TODO: deny rules should be fatal: if a deny rule fails to apply, we must\n\t// roll back all allow rules to avoid a fail-open where allowed traffic bypasses\n\t// the missing deny. Currently we accumulate errors and continue.\n\tvar merr *multierror.Error\n\tfor _, r := range rules {\n\t\t// if this rule is member of rule selection with more than DefaultIPsCountForSet\n\t\t// it's IP address can be used in the ipset for firewall manager which supports it\n\t\tselector := d.getRuleGroupingSelector(r)\n\t\tipsetName, ok := ipsetByRuleSelectors[selector]\n\t\tif !ok {\n\t\t\td.ipsetCounter++\n\t\t\tipsetName = fmt.Sprintf(\"nb%07d\", d.ipsetCounter)\n\t\t\tipsetByRuleSelectors[selector] = ipsetName\n\t\t}\n\t\tpairID, rulePair, err := d.protoRuleToFirewallRule(r, ipsetName)\n\t\tif err != nil {\n\t\t\tmerr = multierror.Append(merr, fmt.Errorf(\"apply firewall rule: %w\", err))\n\t\t\tcontinue\n\t\t}\n\t\tif len(rulePair) > 0 {\n\t\t\td.peerRulesPairs[pairID] = rulePair\n\t\t\tnewRulePairs[pairID] = rulePair\n\t\t}\n\t}\n\n\tif merr != nil {\n\t\tlog.Errorf(\"failed to apply %d peer ACL rule(s): %v\", merr.Len(), nberrors.FormatErrorOrNil(merr))\n\t}\n\n\tfor pairID, rules := range d.peerRulesPairs {\n\t\tif _, ok := newRulePairs[pairID]; !ok {\n\t\t\tfor _, rule := range rules {\n\t\t\t\tif err := d.firewall.DeletePeerRule(rule); err != nil {\n\t\t\t\t\tlog.Errorf(\"failed to delete peer firewall rule: %v\", err)\n\t\t\t\t\tcontinue","sourceCodeStart":162,"sourceCodeEnd":198,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/client/internal/acl/manager.go#L162-L198","documentation":"Appended per-rule when DefaultManager's peer ACL application fails in protoRuleToFirewallRule or the AddPeerFiltering call beneath addInRules/addOutRules. Root causes visible through the %w chain: extractRuleIP failing to parse the rule's CIDR, `invalid port` when an old management sends the legacy single-port string and strconv.Atoi fails, `skipping firewall rule` for unknown protocol/action (typically a newer management than the agent), or the firewall backend (nftables/iptables) rejecting the rule. Failures are accumulated and logged as 'failed to apply N peer ACL rule(s)'; the affected rules are simply absent, and since the manager's default is deny, that traffic is blocked.","triggerScenarios":"Management/agent version skew introducing protocol enum values or port formats the old agent cannot map; nftables missing or ruleset locked (nft -f failing); malformed CIDR in a distribution group's address; ipset creation failing when a rule group exceeds DefaultIPsCountForSet.","commonSituations":"Upgraded self-hosted management with stale agents; hardened hosts without nft or with firewalld holding transactions; policies edited via API with unvalidated CIDRs.","solutions":["Read the wrapped cause in the log line - each of the sub-errors has a distinct fix (parse vs backend)","Align versions: update the agent to the management's version so new protocol/action enum values map correctly","Verify the firewall backend: run `nft list ruleset` and check for NetBird chains; ensure the agent has NET_ADMIN","Inspect the policy's rules (network map in debug logs) for malformed CIDRs or legacy port strings and fix them in management"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// management-side: validate rules before dispatching the network map\nfunc validPeerRule(r *mgmProto.FirewallRule) bool {\n    if _, err := netip.ParseAddr(strings.Split(r.Source, \"/\")[0]); r.Source != \"\" && err != nil {\n        return false\n    }\n    switch r.Protocol {\n    case mgmProto.RuleProtocol_TCP, mgmProto.RuleProtocol_UDP,\n        mgmProto.RuleProtocol_ICMP, mgmProto.RuleProtocol_ALL:\n    default:\n        return false\n    }\n    return true\n}","typeGuard":null,"tryCatchPattern":"// the manager already aggregates; consume the multierror and alert per-rule\nif err := mgr.ApplyPeerACLs(rules); err != nil {\n    var merr *multierror.Error\n    if errors.As(err, &merr) {\n        for _, e := range merr.Errors {\n            if strings.Contains(e.Error(), \"invalid protocol\") || strings.Contains(e.Error(), \"skipping firewall rule\") {\n                log.Errorf(\"version skew: management sent unknown enum: %v\", e)\n            } else if strings.Contains(e.Error(), \"add firewall rule\") {\n                log.Errorf(\"firewall backend rejected rule (nftables/iptables): %v\", e)\n            }\n        }\n    }\n}","preventionTips":["Upgrade agents and management together; unknown protocol/action enums are the classic skew failure","Validate policies authored via REST API (CIDRs, ports, protocols) before saving","Keep the firewall backend healthy: no external edits to NetBird chains, NET_ADMIN present"],"tags":["go","netbird","acl","firewall","nftables","version-mismatch"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}