{"record":{"id":"669cf415c7c4ff1a","repo":"slackhq/nebula","slug":"errnomatchingrule","errorCode":"ErrNoMatchingRule","errorMessage":"no matching rule in firewall table","messagePattern":"no matching rule in firewall table","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"firewall.go","lineNumber":422,"sourceCode":"\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\n\t\tif h.vpnAddrs[0] != fp.RemoteAddr {\n\t\t\tf.metrics(incoming).droppedRemoteAddr.Inc(1)\n\t\t\treturn ErrInvalidRemoteIP\n\t\t}\n\t} else {\n\t\tnwType, ok := h.networks.Lookup(fp.RemoteAddr)\n\t\tif !ok {\n\t\t\tf.metrics(incoming).droppedRemoteAddr.Inc(1)\n\t\t\treturn ErrInvalidRemoteIP\n\t\t}\n\t\tswitch nwType {","sourceCodeStart":404,"sourceCodeEnd":440,"githubUrl":"https://github.com/slackhq/nebula/blob/dd8f660c0ac37903ec4080ca4d3c861ba9342ceb/firewall.go#L404-L440","documentation":"ErrNoMatchingRule is the sentinel error declared at firewall.go:422 with message \"no matching rule in firewall table\". Firewall.Drop returns it when the packet passed all address/certificate checks but no rule in the firewall rule table matches (firewall.go:473: table.match returns false). It is the library's way of saying 'default deny': nebula's firewall is allow-list only, so any flow not explicitly permitted is dropped with this error.","triggerScenarios":"Firewall.Drop reaches the final rule-matching stage (firewall.go:473) and table.match(fp, incoming, peerCert, caPool) finds no matching rule for the packet's protocol/port/groups. Returned by Drop for both inbound and outbound packets; exercised by tests like TestFirewall_Drop, TestFirewall_Drop2/3, TestFirewall_DropV6, TestFirewall_DropConntrackReload.","commonSituations":"Missing firewall rule for the intended service (e.g. no inbound rule for the port a peer connects on); direction mismatch — rule defined for outbound but traffic is inbound (see firewall_test.go:215 where outbound drops with ErrNoMatchingRule while a conntrack entry allows inbound); rule's group/port/proto constraints don't match the peer certificate's groups; rules reloaded while an existing conntrack entry was reset (TestFirewall_DropConntrackReload); IPv6 rules missing when traffic is v6.","solutions":["Add an explicit firewall rule allowing the flow (from group/CIDR, port, protocol) in the nebula config and reload","Check rule direction: inbound traffic needs a rule whose 'port'/'from' side matches the sender's cert groups and the local listening port","Verify the peer certificate's groups match the groups named in the rule","For established flows failing after a rule reload, ensure conntrack cache is handled/reset consistently","For v6 traffic, add the corresponding IPv6 rules — v4 rules do not match v6 packets"],"exampleFix":"// before: no inbound SSH rule -> Drop returns ErrNoMatchingRule\nfirewall:\n  inbound:\n    - port: 80\n      proto: tcp\n      group: app\n// after\nfirewall:\n  inbound:\n    - port: 80\n      proto: tcp\n      group: app\n    - port: 22\n      proto: tcp\n      group: admin","handlingStrategy":"fallback","validationCode":"// dry-check the rule set before deploying: ensure the intended flow matches at least one rule\nif !fw.RuleMatchesExactlyOnce(flow) && !conntrackHas(flow) {\n    // Drop will return ErrNoMatchingRule; add an allow rule first\n}","typeGuard":null,"tryCatchPattern":"if err := fw.Drop(pkt, incoming, host, caPool, cache); err != nil {\n    if errors.Is(err, firewall.ErrNoMatchingRule) {\n        // default-deny hit: log flow (proto, port, groups, direction) for rule authoring\n        // do not blanket-allow; add a targeted rule instead\n    }\n}","preventionTips":["Author inbound AND outbound rules for every service, remembering nebula is allow-list only","Match rule group names exactly to certificate groups; check direction when a test shows outbound drop but inbound allow","Reload firewall rules atomically and account for conntrack entries (see TestFirewall_DropConntrackReload)","Mirror rules for IPv6 where dual-stack traffic is expected"],"tags":["firewall","access-control","default-deny","rule-mismatch"],"backgroundTag":"no-matching-firewall-rule","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"}