slackhq/nebula · error

ErrInvalidRemoteIP

ErrInvalidRemoteIP

Error message

remote address is not in remote certificate networks

What it means

ErrInvalidRemoteIP is a sentinel error declared at firewall.go:420 with message "remote address is not in remote certificate networks". Firewall.Drop returns it during the remote-address sanity check: for hosts with a single VPN network, if the packet's remote address does not exactly equal the host's expected vpnAddrs[0], or (otherwise) if the remote address is not contained in any of the host's certificate networks, the packet is treated as spoofed and dropped. It is the IP-spoofing guard tied to the remote peer's certificate.

Source

Thrown at firewall.go:420

			l.Warn("firewall rule sanity check",
				"table", table,
				"rule", i,
				"warning", warning,
			)
		}

		err = fw.AddRule(inbound, proto, startPort, endPort, r.Groups, r.Host, r.Cidr, r.LocalCidr, r.CAName, r.CASha)
		if err != nil {
			return fmt.Errorf("%s rule #%v; `%s`", table, i, err)
		}
	}

	return nil
}

var ErrUnknownNetworkType = errors.New("unknown network type")
var ErrPeerRejected = errors.New("remote address is not within a network that we handle")
var ErrInvalidRemoteIP = errors.New("remote address is not in remote certificate networks")
var ErrInvalidLocalIP = errors.New("local address is not in list of handled local addresses")
var ErrNoMatchingRule = errors.New("no matching rule in firewall table")

// Drop returns an error if the packet should be dropped, explaining why. It
// returns nil if the packet should not be dropped.
func (f *Firewall) Drop(fp firewall.Packet, incoming bool, h *HostInfo, caPool *cert.CAPool, localCache firewall.ConntrackCache) error {
	// Make sure remote address matches nebula certificate, and determine how to treat it
	if h.networks == nil {
		// Simple case: Certificate has one address and no unsafe networks
		if h.vpnAddrs[0] != fp.RemoteAddr {
			f.metrics(incoming).droppedRemoteAddr.Inc(1)
			return ErrInvalidRemoteIP
		}
	} else {
		nwType, ok := h.networks.Lookup(fp.RemoteAddr)
		if !ok {
			f.metrics(incoming).droppedRemoteAddr.Inc(1)
			return ErrInvalidRemoteIP

View on GitHub (pinned to dd8f660c0a)

Solutions

  1. Verify the packet's source address matches the address in the remote peer's certificate; fix NAT/addressing so the certified address is the actual packet source
  2. If the peer legitimately has multiple addresses, ensure they are listed in the certificate's networks so the prefix lookup at firewall.go:438 succeeds
  3. Regenerate/reissue the remote peer's certificate with the correct networks entry for its current address
  4. Check for load-balancer or NAT44 devices rewriting source IPs on the tunnel and add NAT exceptions
  5. Confirm the HostInfo used in Drop() corresponds to the actual sender, not a cached entry for another peer

Example fix

// before: certificate issued without the NATed address
//   nebula-cert sign -name hostA -ip 10.0.0.5
//   packets arrive from 203.0.113.9 -> Drop returns ErrInvalidRemoteIP
// after: include the real address in the cert networks
//   nebula-cert sign -name hostA -ip 10.0.0.5 -subnets 203.0.113.9/32
Defensive patterns

Strategy: try-catch

Validate before calling

// confirm the packet source matches the HostInfo's certified address before Drop
if len(h.vpnAddrs) == 1 && fp.RemoteAddr != h.vpnAddrs[0] {
    // Drop WILL return ErrInvalidRemoteIP; fix the addressing/NAT first
}

Try / catch

if err := fw.Drop(pkt, incoming, host, caPool, cache); err != nil {
    if errors.Is(err, firewall.ErrInvalidRemoteIP) {
        // treat as possible spoofing or NAT mismatch: log remote addr vs cert networks
        // and do NOT retry the same packet
    }
}

Prevention

When it happens

Trigger: 1) firewall.go:432 — host has one VPN network and fp.RemoteAddr != h.vpnAddrs[0]. 2) firewall.go:438 — the remote address fails a prefix lookup in the host's networks table. Any Drop() call where the source IP of the packet does not match the address/networks bound to the remote peer's certificate triggers it.

Common situations: NAT or encapsulation rewriting the outer source address so it no longer matches the certified tunnel address; a host behind NAT sending packets from a different source IP than its certificate lists; misconfigured static_host_map / advertised networks; buggy code reusing a HostInfo for the wrong remote; testing with fabricated packet source addresses.

Understand the failure class

Related errors


AI-assisted analysis of slackhq/nebula@dd8f660c0a (2026-09-03). Data as JSON: /api/errors/df08df63b8516e53. Report an issue: GitHub.