netbirdio/netbird · error
apply firewall rule: %w
Error message
apply firewall rule: %w
What it means
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.
Source
Thrown at client/internal/acl/manager.go:180
ipsetByRuleSelectors := make(map[string]string)
// TODO: deny rules should be fatal: if a deny rule fails to apply, we must
// roll back all allow rules to avoid a fail-open where allowed traffic bypasses
// the missing deny. Currently we accumulate errors and continue.
var merr *multierror.Error
for _, r := range rules {
// if this rule is member of rule selection with more than DefaultIPsCountForSet
// it's IP address can be used in the ipset for firewall manager which supports it
selector := d.getRuleGroupingSelector(r)
ipsetName, ok := ipsetByRuleSelectors[selector]
if !ok {
d.ipsetCounter++
ipsetName = fmt.Sprintf("nb%07d", d.ipsetCounter)
ipsetByRuleSelectors[selector] = ipsetName
}
pairID, rulePair, err := d.protoRuleToFirewallRule(r, ipsetName)
if err != nil {
merr = multierror.Append(merr, fmt.Errorf("apply firewall rule: %w", err))
continue
}
if len(rulePair) > 0 {
d.peerRulesPairs[pairID] = rulePair
newRulePairs[pairID] = rulePair
}
}
if merr != nil {
log.Errorf("failed to apply %d peer ACL rule(s): %v", merr.Len(), nberrors.FormatErrorOrNil(merr))
}
for pairID, rules := range d.peerRulesPairs {
if _, ok := newRulePairs[pairID]; !ok {
for _, rule := range rules {
if err := d.firewall.DeletePeerRule(rule); err != nil {
log.Errorf("failed to delete peer firewall rule: %v", err)
continueView on GitHub (pinned to 93e97f4bf1)
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
Defensive patterns
Strategy: try-catch
Validate before calling
// management-side: validate rules before dispatching the network map
func validPeerRule(r *mgmProto.FirewallRule) bool {
if _, err := netip.ParseAddr(strings.Split(r.Source, "/")[0]); r.Source != "" && err != nil {
return false
}
switch r.Protocol {
case mgmProto.RuleProtocol_TCP, mgmProto.RuleProtocol_UDP,
mgmProto.RuleProtocol_ICMP, mgmProto.RuleProtocol_ALL:
default:
return false
}
return true
} Try / catch
// the manager already aggregates; consume the multierror and alert per-rule
if err := mgr.ApplyPeerACLs(rules); err != nil {
var merr *multierror.Error
if errors.As(err, &merr) {
for _, e := range merr.Errors {
if strings.Contains(e.Error(), "invalid protocol") || strings.Contains(e.Error(), "skipping firewall rule") {
log.Errorf("version skew: management sent unknown enum: %v", e)
} else if strings.Contains(e.Error(), "add firewall rule") {
log.Errorf("firewall backend rejected rule (nftables/iptables): %v", e)
}
}
}
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: Upgraded self-hosted management with stale agents; hardened hosts without nft or with firewalld holding transactions; policies edited via API with unvalidated CIDRs.
Related errors
- add IP to ipset: %w
- failed to check rule: %w
- rule already exists
- failed to delete rule: %s, %v: %w
- add peer filtering for %s: %w
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/05e3a95b644dd060.
Report an issue: GitHub.