{"record":{"id":"7792fbdcc04b885d","repo":"slackhq/nebula","slug":"s-rule-v-s-7792fb","errorCode":null,"errorMessage":"%s rule #%v; `%s`","messagePattern":"(.+?) rule #(.+?); `(.+?)`","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"firewall.go","lineNumber":411,"sourceCode":"\n\t\tif r.LocalCidr != \"\" && r.LocalCidr != \"any\" {\n\t\t\t_, err = netip.ParsePrefix(r.LocalCidr)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"%s rule #%v; local_cidr did not parse; %s\", table, i, err)\n\t\t\t}\n\t\t}\n\n\t\tif warning := r.sanity(); warning != nil {\n\t\t\tl.Warn(\"firewall rule sanity check\",\n\t\t\t\t\"table\", table,\n\t\t\t\t\"rule\", i,\n\t\t\t\t\"warning\", warning,\n\t\t\t)\n\t\t}\n\n\t\terr = fw.AddRule(inbound, proto, startPort, endPort, r.Groups, r.Host, r.Cidr, r.LocalCidr, r.CAName, r.CASha)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"%s rule #%v; `%s`\", table, i, err)\n\t\t}\n\t}\n\n\treturn nil\n}\n\nvar ErrUnknownNetworkType = errors.New(\"unknown network type\")\nvar ErrPeerRejected = errors.New(\"remote address is not within a network that we handle\")\nvar ErrInvalidRemoteIP = errors.New(\"remote address is not in remote certificate networks\")\nvar ErrInvalidLocalIP = errors.New(\"local address is not in list of handled local addresses\")\nvar ErrNoMatchingRule = errors.New(\"no matching rule in firewall table\")\n\n// Drop returns an error if the packet should be dropped, explaining why. It\n// returns nil if the packet should not be dropped.\nfunc (f *Firewall) Drop(fp firewall.Packet, incoming bool, h *HostInfo, caPool *cert.CAPool, localCache firewall.ConntrackCache) error {\n\t// Make sure remote address matches nebula certificate, and determine how to treat it\n\tif h.networks == nil {\n\t\t// Simple case: Certificate has one address and no unsafe networks","sourceCodeStart":393,"sourceCodeEnd":429,"githubUrl":"https://github.com/slackhq/nebula/blob/dd8f660c0ac37903ec4080ca4d3c861ba9342ceb/firewall.go#L393-L429","documentation":"This is a wrapper error from AddFirewallRulesFromConfig. When AddRule (backed by firewallPort.addRule) fails while installing a rule parsed from the YAML config, the error is re-wrapped as '<table> rule #<index>; `<original error>`' so the operator knows exactly which rule in which firewall table (inbound/outbound) is broken. The inner message (e.g. 'start port was lower than end port') is the actual cause.","triggerScenarios":"Calling NewFirewallFromConfig (or TestAddFirewallRulesFromConfig) with a firewall config where a rule passes parsing/validation (addFirewallRulesFromConfig) but fails at install time — most commonly a rule whose port range has start > end (e.g. port: '100-50'), since firewallPort.addRule rejects startPort > endPort.","commonSituations":"Hand-written YAML where the range was accidentally written high-to-low ('8080-80'); generated configs producing inverted ranges; port 'any' rules mixing with ranges; typos in range separators.","solutions":["Read the inner error after the backtick to get the real cause (e.g. inverted port range)","Fix the rule at the reported index in the firewall.inbound/outbound table so the port range is low-to-high or a single port","Validate all port ranges before loading the config"],"exampleFix":"// before (config)\nport: 8080-80\n// after\nport: 80-8080","handlingStrategy":"validation","validationCode":"for i, r := range cfg.Firewall.Inbound {\n    if p := strings.SplitN(r.Port, \"-\", 2); len(p) == 2 {\n        start, err1 := strconv.Atoi(strings.TrimSpace(p[0]))\n        end, err2 := strconv.Atoi(strings.TrimSpace(p[1]))\n        if err1 == nil && err2 == nil && start > end {\n            return fmt.Errorf(\"inbound rule #%d: port range %s is inverted\", i, r.Port)\n        }\n    }\n}","typeGuard":"func hasValidPortRange(port string) bool {\n    if !strings.Contains(port, \"-\") { return true }\n    parts := strings.SplitN(port, \"-\", 2)\n    s, err1 := strconv.Atoi(strings.TrimSpace(parts[0]))\n    e, err2 := strconv.Atoi(strings.TrimSpace(parts[1]))\n    return err1 == nil && err2 == nil && s <= e\n}","tryCatchPattern":"if err := fw.AddFirewallRulesFromConfig(l, inbound, outbound, config); err != nil {\n    var wrapped string = err.Error()\n    // inner cause is after the backtick: `table rule #N; <cause>`\n    return fmt.Errorf(\"firewall config load failed: %w\", err)\n}","preventionTips":["Pre-write port ranges low-to-high in configs","Validate the full firewall config before passing it to NewFirewallFromConfig","Include the rule index/table in your own config linting","Add a CI check that loads the config via the library before deployment"],"tags":["go","firewall","config","port-range"],"backgroundTag":"firewall-rule-invalid","analyzedSha":"dd8f660c0ac37903ec4080ca4d3c861ba9342ceb","analyzedAt":"2026-09-03T11:13:55.444Z","contentChangedAt":"2026-09-03T11:13:55.444Z","schemaVersion":2},"datasetVersion":"2026-09-10T17:17:09.494Z"}