slackhq/nebula · error

entry %v.route in tun.routes is not contained within the con

Error message

entry %v.route in tun.routes is not contained within the configured vpn networks; route: %v, networks: %v

What it means

The parsed route CIDR is valid but not contained within any of the configured VPN networks for this host. parseRoutes checks each route against the host's allowed networks (network.Contains(cidr.Addr()) && cidr.Bits() >= network.Bits()) and rejects routes that would reach outside the VPN's address space, listing the route and the allowed networks.

Source

Thrown at overlay/route.go:135

			Install: true,
			MTU:     mtu,
		}

		r.Cidr, err = netip.ParsePrefix(fmt.Sprintf("%v", rRoute))
		if err != nil {
			return nil, fmt.Errorf("entry %v.route in tun.routes failed to parse: %v", i+1, err)
		}

		found := false
		for _, network := range networks {
			if network.Contains(r.Cidr.Addr()) && r.Cidr.Bits() >= network.Bits() {
				found = true
				break
			}
		}

		if !found {
			return nil, fmt.Errorf(
				"entry %v.route in tun.routes is not contained within the configured vpn networks; route: %v, networks: %v",
				i+1,
				r.Cidr.String(),
				networks,
			)
		}

		routes[i] = r
	}

	return routes, nil
}

func parseUnsafeRoutes(c *config.C, networks []netip.Prefix) ([]Route, error) {
	var err error

	r := c.Get("tun.unsafe_routes")
	if r == nil {

View on GitHub (pinned to dd8f660c0a)

Solutions

  1. Add the desired network to the host's configured vpn networks (its allowed networks in the CA/certificate or networks list) so the route is contained
  2. Narrow the route to fit within an already-configured network (e.g. 10.0.1.0/24 instead of 10.0.0.0/8)
  3. Compare the printed route against the printed networks list to see the containment mismatch
  4. If routing beyond the VPN is genuinely needed, update the certificate/host network configuration first — never try to bypass this check

Example fix

// before: host networks = 10.0.0.0/24, route outside it
routes:
  - mtu: 1300
    route: 192.168.1.0/24
// after: either change route to fit, or extend host networks
tun:
  routes:
    - mtu: 1300
      route: 10.0.0.0/24
Defensive patterns

Strategy: validation

Validate before calling

cidr := netip.MustParsePrefix(fmt.Sprintf("%v", m["route"]))
contained := false
for _, n := range networks {
    if n.Contains(cidr.Addr()) && cidr.Bits() >= n.Bits() {
        contained = true
        break
    }
}
if !contained {
    return fmt.Errorf("route %s not within configured vpn networks %v", cidr, networks)
}

Prevention

When it happens

Trigger: getAllRoutesFromConfig reads a tun.routes entry whose CIDR falls outside (or is broader than) the networks configured for the host/certificate, e.g. route 192.168.1.0/24 when the VPN only covers 10.0.0.0/24, or route 10.0.0.0/8 when the host network is 10.0.1.0/24 (route broader than the allowed network).

Common situations: Operator wants to route additional corporate subnets but forgot to add them to the host's allowed networks; copy-pasting routes from another nebula/overlay deployment; prefix-length mistakes making the route broader than the network; intentional route escalation that the config correctly rejects as a security guard.

Related errors


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