mirror of
https://github.com/juanfont/headscale.git
synced 2026-11-05 04:59:26 +09:00
mapper: send a router all of its own routes
Its AllowedIPs held only the primaries the policy let it reach. It now holds every approved route, and PrimaryRoutes the ones it serves. Fixes #3626
This commit is contained in:
1 parent
1cd93ec432
commit
505eb1c1ed
4 files changed
+19
-204
No files matched your search
@@ -8,7 +8,6 @@ import (
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
"github.com/juanfont/headscale/hscontrol/policy"
|
||||
policyv2 "github.com/juanfont/headscale/hscontrol/policy/v2"
|
||||
"github.com/juanfont/headscale/hscontrol/types"
|
||||
"github.com/juanfont/headscale/hscontrol/util/zlog/zf"
|
||||
@@ -88,15 +87,12 @@ func (b *MapResponseBuilder) WithSelfNode() *MapResponseBuilder {
|
||||
return b
|
||||
}
|
||||
|
||||
_, matchers := b.mapper.state.Filter()
|
||||
|
||||
tailnode, err := nv.TailNode(
|
||||
b.capVer,
|
||||
func(id types.NodeID) []netip.Prefix {
|
||||
// Self node: include own primaries + exit routes (no via steering for self).
|
||||
primaries := policy.ReduceRoutes(nv, b.mapper.state.GetNodePrimaryRoutes(id), matchers)
|
||||
|
||||
return slices.Concat(primaries, nv.ExitRoutes())
|
||||
func(types.NodeID) []netip.Prefix {
|
||||
// A router holds every route it is approved for, whatever
|
||||
// its own policy reaches.
|
||||
return nv.AllApprovedRoutes()
|
||||
},
|
||||
b.mapper.cfg,
|
||||
b.mapper.state.NodeCapMap(nv.ID()),
|
||||
@@ -106,6 +102,13 @@ func (b *MapResponseBuilder) WithSelfNode() *MapResponseBuilder {
|
||||
return b
|
||||
}
|
||||
|
||||
// PrimaryRoutes is what the router serves: the routes it is primary
|
||||
// for, under the rule its peers get them by.
|
||||
tailnode.PrimaryRoutes = slices.DeleteFunc(
|
||||
b.mapper.state.GetNodePrimaryRoutes(nv.ID()),
|
||||
func(p netip.Prefix) bool { return !b.mapper.cfg.ServesRoute(p) },
|
||||
)
|
||||
|
||||
b.resp.Node = tailnode
|
||||
|
||||
return b
|
||||
|
||||
@@ -114,19 +114,6 @@ var mapStreamSkipReasons = map[string]string{
|
||||
"route-announce/RA9/srv/presence": policyResponseToEveryNode,
|
||||
"route-announce/RA9/wit/presence": policyResponseToEveryNode,
|
||||
|
||||
"route-announce/RA0/rtr/self-routes": selfRoutesReduced,
|
||||
"route-announce/RA1/rtr/self-routes": selfRoutesReduced,
|
||||
"route-announce/RA2/rtr/self-routes": selfRoutesReduced,
|
||||
"route-announce/RA3/rtr/self-routes": selfRoutesReduced,
|
||||
"route-announce/RA4/rtr/self-routes": selfRoutesReduced,
|
||||
"route-announce/RA5/rtr/self-routes": selfRoutesReduced,
|
||||
"route-announce/RA6/rtr/self-routes": selfRoutesReduced,
|
||||
"route-announce/RA7/rtr/self-routes": selfRoutesReduced,
|
||||
"route-announce/RA8/rtr/self-routes": selfRoutesReduced,
|
||||
"route-announce/RA9/rtr/self-routes": selfRoutesReduced,
|
||||
"route-announce/RA10/rtr/self-routes": selfRoutesReduced,
|
||||
"route-announce/RA11/rtr/self-routes": selfRoutesReduced,
|
||||
|
||||
"policy-generation/G2/thor-1/peers": sameUserPeers,
|
||||
"policy-generation/G2/thor-2/peers": sameUserPeers,
|
||||
"policy-generation/G3/thor-1/peers": sameUserPeers,
|
||||
|
||||
@@ -373,50 +373,7 @@ var routeOverlapCompatTests = []string{
|
||||
// routeOverlapSkipReasons lists checks where headscale differs from SaaS,
|
||||
// keyed <test_id>/<node>/<check>. A listed check that matches fails, so a fix
|
||||
// must delete its entry.
|
||||
var routeOverlapSkipReasons = map[string]string{
|
||||
// Once a router is sent its own routes, its self-primary also needs the
|
||||
// approved singles in the tailnet range left unserved.
|
||||
"routes-t1-auto-v4-default/ovl-r-ctrl/self-allowed": selfRoutesReduced,
|
||||
"routes-t1-auto-v4-default/ovl-r-ctrl/self-primary": selfRoutesReduced,
|
||||
"routes-t1-auto-v4-default/ovl-r-host4/self-allowed": selfRoutesReduced,
|
||||
"routes-t1-auto-v4-default/ovl-r-narrow/self-allowed": selfRoutesReduced,
|
||||
"routes-t1-auto-v4-default/ovl-r-narrow/self-primary": selfRoutesReduced,
|
||||
"routes-t2-auto-v6-default/ovl-r-host6/self-allowed": selfRoutesReduced,
|
||||
"routes-t2-auto-v6-default/ovl-r-mapped/self-allowed": selfRoutesReduced,
|
||||
"routes-t2-auto-v6-default/ovl-r-mapped/self-primary": selfRoutesReduced,
|
||||
"routes-t2-auto-v6-default/ovl-r-narrow/self-allowed": selfRoutesReduced,
|
||||
"routes-t2-auto-v6-default/ovl-r-narrow/self-primary": selfRoutesReduced,
|
||||
"routes-t2-auto-v6-default/ovl-r-via/self-allowed": selfRoutesReduced,
|
||||
"routes-t2-auto-v6-default/ovl-r-via/self-primary": selfRoutesReduced,
|
||||
"routes-t3-auto-narrow-keys/ovl-r-ctrl/self-allowed": selfRoutesReduced,
|
||||
"routes-t3-auto-narrow-keys/ovl-r-ctrl/self-primary": selfRoutesReduced,
|
||||
"routes-t3-auto-narrow-keys/ovl-r-host4/self-allowed": selfRoutesReduced,
|
||||
"routes-t3-auto-narrow-keys/ovl-r-host6/self-allowed": selfRoutesReduced,
|
||||
"routes-t3-auto-narrow-keys/ovl-r-narrow/self-allowed": selfRoutesReduced,
|
||||
"routes-t3-auto-narrow-keys/ovl-r-narrow/self-primary": selfRoutesReduced,
|
||||
"routes-t3-auto-narrow-keys/ovl-r-via/self-allowed": selfRoutesReduced,
|
||||
"routes-t3-auto-narrow-keys/ovl-r-via/self-primary": selfRoutesReduced,
|
||||
"routes-t4-admin/ovl-r-ctrl/self-allowed": selfRoutesReduced,
|
||||
"routes-t4-admin/ovl-r-ctrl/self-primary": selfRoutesReduced,
|
||||
"routes-t4-admin/ovl-r-host4/self-allowed": selfRoutesReduced,
|
||||
"routes-t4-admin/ovl-r-host6/self-allowed": selfRoutesReduced,
|
||||
"routes-t4-admin/ovl-r-mapped/self-allowed": selfRoutesReduced,
|
||||
"routes-t4-admin/ovl-r-mapped/self-primary": selfRoutesReduced,
|
||||
"routes-t4-admin/ovl-r-narrow/self-allowed": selfRoutesReduced,
|
||||
"routes-t4-admin/ovl-r-narrow/self-primary": selfRoutesReduced,
|
||||
"routes-t4-admin/ovl-r-via/self-allowed": selfRoutesReduced,
|
||||
"routes-t4-admin/ovl-r-via/self-primary": selfRoutesReduced,
|
||||
"routes-t5-oauth-routes-scope/ovl-r-ctrl/self-allowed": selfRoutesReduced,
|
||||
"routes-t5-oauth-routes-scope/ovl-r-ctrl/self-primary": selfRoutesReduced,
|
||||
"routes-t5-oauth-routes-scope/ovl-r-host4/self-allowed": selfRoutesReduced,
|
||||
"routes-t5-oauth-routes-scope/ovl-r-host6/self-allowed": selfRoutesReduced,
|
||||
"routes-t5-oauth-routes-scope/ovl-r-mapped/self-allowed": selfRoutesReduced,
|
||||
"routes-t5-oauth-routes-scope/ovl-r-mapped/self-primary": selfRoutesReduced,
|
||||
"routes-t5-oauth-routes-scope/ovl-r-narrow/self-allowed": selfRoutesReduced,
|
||||
"routes-t5-oauth-routes-scope/ovl-r-narrow/self-primary": selfRoutesReduced,
|
||||
"routes-t5-oauth-routes-scope/ovl-r-via/self-allowed": selfRoutesReduced,
|
||||
"routes-t5-oauth-routes-scope/ovl-r-via/self-primary": selfRoutesReduced,
|
||||
}
|
||||
var routeOverlapSkipReasons = map[string]string{}
|
||||
|
||||
// routeOverlapNotCompared lists, by key prefix, checks left out: SaaS's
|
||||
// answer there is in doubt, so it is no reference.
|
||||
@@ -643,8 +600,9 @@ func sortedStrings(s []string) string {
|
||||
|
||||
// TestRoutesNodeRangeSingleNotServed checks that a router's approved single
|
||||
// addresses inside the node ranges, in either spelling, reach no viewer
|
||||
// through a plain grant or a via grant. They stay approved, the router's own
|
||||
// entry keeps them, and its wider route is still served.
|
||||
// through a plain grant or a via grant. They stay approved and in the
|
||||
// router's own AllowedIPs, never in its PrimaryRoutes, and its wider route
|
||||
// is still served.
|
||||
func TestRoutesNodeRangeSingleNotServed(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -704,9 +662,9 @@ func TestRoutesNodeRangeSingleNotServed(t *testing.T) {
|
||||
|
||||
return ok && views.SliceContains(p.PrimaryRoutes(), subnet)
|
||||
})
|
||||
router.WaitForCondition(t, "router holds its routes", 15*time.Second,
|
||||
router.WaitForCondition(t, "router serves its subnet", 15*time.Second,
|
||||
func(nm *netmap.NetworkMap) bool {
|
||||
return views.SliceContains(nm.SelfNode.AllowedIPs(), subnet)
|
||||
return views.SliceContains(nm.SelfNode.PrimaryRoutes(), subnet)
|
||||
})
|
||||
|
||||
peer, _ := viewer.PeerByName("router")
|
||||
@@ -719,6 +677,8 @@ func TestRoutesNodeRangeSingleNotServed(t *testing.T) {
|
||||
"%s served in PrimaryRoutes", single)
|
||||
assert.Truef(t, views.SliceContains(self.AllowedIPs(), single),
|
||||
"%s missing from the router's own AllowedIPs", single)
|
||||
assert.Falsef(t, views.SliceContains(self.PrimaryRoutes(), single),
|
||||
"%s in the router's own PrimaryRoutes", single)
|
||||
}
|
||||
|
||||
node, ok := srv.State().GetNodeByID(routerID)
|
||||
|
||||
@@ -107,139 +107,6 @@ var routesMapCompatTests = []struct {
|
||||
var viaCompatSkipReasons = map[string]string{
|
||||
"tagged-self": "TAGGED_SELF_PROFILE: SaaS gives a tagged node a profile of its own, headscale the shared tagged-devices one",
|
||||
|
||||
// SaaS sends a router every approved route in its own AllowedIPs and
|
||||
// every route it is primary for in PrimaryRoutes.
|
||||
"grant-v11/blastoise/self-routes": selfRoutesReduced,
|
||||
"grant-v11/squirtle/self-routes": selfRoutesReduced,
|
||||
"grant-v12/blastoise/self-routes": selfRoutesReduced,
|
||||
"grant-v12/squirtle/self-routes": selfRoutesReduced,
|
||||
"grant-v13/blastoise/self-routes": selfRoutesReduced,
|
||||
"grant-v13/squirtle/self-routes": selfRoutesReduced,
|
||||
|
||||
"routes-a1-wildcard-acl-includes-routes-in-srcips/butterfree/self-routes": selfRoutesReduced,
|
||||
"routes-a2-tag-based-acl-excludes-routes/butterfree/self-routes": selfRoutesReduced,
|
||||
"routes-a2-tag-based-acl-excludes-routes/caterpie/self-routes": selfRoutesReduced,
|
||||
"routes-a2-tag-based-acl-excludes-routes/metapod/self-routes": selfRoutesReduced,
|
||||
"routes-a2-tag-based-acl-excludes-routes/squirtle/self-routes": selfRoutesReduced,
|
||||
"routes-a2-tag-based-acl-excludes-routes/wartortle/self-routes": selfRoutesReduced,
|
||||
"routes-a3-explicit-subnet-filter-to-router/butterfree/self-routes": selfRoutesReduced,
|
||||
"routes-a3-explicit-subnet-filter-to-router/metapod/self-routes": selfRoutesReduced,
|
||||
"routes-a3-explicit-subnet-filter-to-router/wartortle/self-routes": selfRoutesReduced,
|
||||
"routes-a4-multiple-routes-same-router/butterfree/self-routes": selfRoutesReduced,
|
||||
"routes-a4-multiple-routes-same-router/caterpie/self-routes": selfRoutesReduced,
|
||||
"routes-a4-multiple-routes-same-router/metapod/self-routes": selfRoutesReduced,
|
||||
"routes-a4-multiple-routes-same-router/squirtle/self-routes": selfRoutesReduced,
|
||||
"routes-e1-ha-two-routers-same-subnet/butterfree/self-routes": selfRoutesReduced,
|
||||
"routes-e1-ha-two-routers-same-subnet/caterpie/self-routes": selfRoutesReduced,
|
||||
"routes-e1-ha-two-routers-same-subnet/squirtle/self-routes": selfRoutesReduced,
|
||||
"routes-e1-ha-two-routers-same-subnet/wartortle/self-routes": selfRoutesReduced,
|
||||
"routes-e2-ha-primary-in-allowedips/butterfree/self-routes": selfRoutesReduced,
|
||||
"routes-e2-ha-primary-in-allowedips/caterpie/self-routes": selfRoutesReduced,
|
||||
"routes-e2-ha-primary-in-allowedips/squirtle/self-routes": selfRoutesReduced,
|
||||
"routes-e2-ha-primary-in-allowedips/wartortle/self-routes": selfRoutesReduced,
|
||||
"routes-f2-subnet-as-acl-source/butterfree/self-routes": selfRoutesReduced,
|
||||
"routes-f2-subnet-as-acl-source/caterpie/self-routes": selfRoutesReduced,
|
||||
"routes-f2-subnet-as-acl-source/metapod/self-routes": selfRoutesReduced,
|
||||
"routes-f2-subnet-as-acl-source/squirtle/self-routes": selfRoutesReduced,
|
||||
"routes-f2-subnet-as-acl-source/wartortle/self-routes": selfRoutesReduced,
|
||||
"routes-f3-wildcard-src-specific-dst/butterfree/self-routes": selfRoutesReduced,
|
||||
"routes-f3-wildcard-src-specific-dst/metapod/self-routes": selfRoutesReduced,
|
||||
"routes-f3-wildcard-src-specific-dst/wartortle/self-routes": selfRoutesReduced,
|
||||
"routes-h1-wildcard-srcips-format/butterfree/self-routes": selfRoutesReduced,
|
||||
"routes-h1-wildcard-srcips-format/metapod/self-routes": selfRoutesReduced,
|
||||
"routes-pa1-partial-route-approval/butterfree/self-routes": selfRoutesReduced,
|
||||
"routes-m1-wildcard-src-multi-range/r-exit/self-routes": selfRoutesReduced,
|
||||
"routes-m1-wildcard-src-multi-range/r-multi/self-routes": selfRoutesReduced,
|
||||
"routes-m2-member-partial-subnet/r-exit/self-routes": selfRoutesReduced,
|
||||
"routes-m2-member-partial-subnet/r-multi/self-routes": selfRoutesReduced,
|
||||
"routes-m3-subnet-route-as-src/r-exit/self-routes": selfRoutesReduced,
|
||||
"routes-m3-subnet-route-as-src/r-multi/self-routes": selfRoutesReduced,
|
||||
"routes-m4-member-self/r-exit/self-routes": selfRoutesReduced,
|
||||
"routes-m4-member-self/r-multi/self-routes": selfRoutesReduced,
|
||||
"routes-m6-subnet-to-subnet-multi-route/r-exit/self-routes": selfRoutesReduced,
|
||||
"routes-m6-subnet-to-subnet-multi-route/r-multi/self-routes": selfRoutesReduced,
|
||||
|
||||
"via-grant-v14/blastoise/self-routes": selfRoutesReduced,
|
||||
"via-grant-v14/fearow/self-routes": selfRoutesReduced,
|
||||
"via-grant-v14/spearow/self-routes": selfRoutesReduced,
|
||||
"via-grant-v14/squirtle/self-routes": selfRoutesReduced,
|
||||
"via-grant-v15/blastoise/self-routes": selfRoutesReduced,
|
||||
"via-grant-v15/fearow/self-routes": selfRoutesReduced,
|
||||
"via-grant-v15/spearow/self-routes": selfRoutesReduced,
|
||||
"via-grant-v15/squirtle/self-routes": selfRoutesReduced,
|
||||
"via-grant-v16/blastoise/self-routes": selfRoutesReduced,
|
||||
"via-grant-v16/fearow/self-routes": selfRoutesReduced,
|
||||
"via-grant-v16/spearow/self-routes": selfRoutesReduced,
|
||||
"via-grant-v16/squirtle/self-routes": selfRoutesReduced,
|
||||
"via-grant-v26/blastoise/self-routes": selfRoutesReduced,
|
||||
"via-grant-v26/fearow/self-routes": selfRoutesReduced,
|
||||
"via-grant-v26/spearow/self-routes": selfRoutesReduced,
|
||||
"via-grant-v26/squirtle/self-routes": selfRoutesReduced,
|
||||
"via-grant-v27/blastoise/self-routes": selfRoutesReduced,
|
||||
"via-grant-v27/fearow/self-routes": selfRoutesReduced,
|
||||
"via-grant-v27/spearow/self-routes": selfRoutesReduced,
|
||||
"via-grant-v27/squirtle/self-routes": selfRoutesReduced,
|
||||
"via-grant-v28/blastoise/self-routes": selfRoutesReduced,
|
||||
"via-grant-v28/fearow/self-routes": selfRoutesReduced,
|
||||
"via-grant-v28/spearow/self-routes": selfRoutesReduced,
|
||||
"via-grant-v28/squirtle/self-routes": selfRoutesReduced,
|
||||
"via-grant-v29/blastoise/self-routes": selfRoutesReduced,
|
||||
"via-grant-v29/fearow/self-routes": selfRoutesReduced,
|
||||
"via-grant-v29/spearow/self-routes": selfRoutesReduced,
|
||||
"via-grant-v29/squirtle/self-routes": selfRoutesReduced,
|
||||
"via-grant-v30/blastoise/self-routes": selfRoutesReduced,
|
||||
"via-grant-v30/fearow/self-routes": selfRoutesReduced,
|
||||
"via-grant-v30/spearow/self-routes": selfRoutesReduced,
|
||||
"via-grant-v30/squirtle/self-routes": selfRoutesReduced,
|
||||
"via-grant-v31/blastoise/self-routes": selfRoutesReduced,
|
||||
"via-grant-v31/fearow/self-routes": selfRoutesReduced,
|
||||
"via-grant-v31/spearow/self-routes": selfRoutesReduced,
|
||||
"via-grant-v31/squirtle/self-routes": selfRoutesReduced,
|
||||
"via-grant-v32/blastoise/self-routes": selfRoutesReduced,
|
||||
"via-grant-v32/fearow/self-routes": selfRoutesReduced,
|
||||
"via-grant-v32/spearow/self-routes": selfRoutesReduced,
|
||||
"via-grant-v32/squirtle/self-routes": selfRoutesReduced,
|
||||
"via-grant-v33/blastoise/self-routes": selfRoutesReduced,
|
||||
"via-grant-v33/fearow/self-routes": selfRoutesReduced,
|
||||
"via-grant-v33/spearow/self-routes": selfRoutesReduced,
|
||||
"via-grant-v33/squirtle/self-routes": selfRoutesReduced,
|
||||
"via-grant-v34/blastoise/self-routes": selfRoutesReduced,
|
||||
"via-grant-v34/fearow/self-routes": selfRoutesReduced,
|
||||
"via-grant-v34/spearow/self-routes": selfRoutesReduced,
|
||||
"via-grant-v34/squirtle/self-routes": selfRoutesReduced,
|
||||
"via-grant-v35/blastoise/self-routes": selfRoutesReduced,
|
||||
"via-grant-v35/fearow/self-routes": selfRoutesReduced,
|
||||
"via-grant-v35/spearow/self-routes": selfRoutesReduced,
|
||||
"via-grant-v35/squirtle/self-routes": selfRoutesReduced,
|
||||
"via-grant-v36/blastoise/self-routes": selfRoutesReduced,
|
||||
"via-grant-v36/fearow/self-routes": selfRoutesReduced,
|
||||
"via-grant-v36/spearow/self-routes": selfRoutesReduced,
|
||||
"via-grant-v36/squirtle/self-routes": selfRoutesReduced,
|
||||
"via-grant-v47/blastoise/self-routes": selfRoutesReduced,
|
||||
"via-grant-v47/fearow/self-routes": selfRoutesReduced,
|
||||
"via-grant-v47/spearow/self-routes": selfRoutesReduced,
|
||||
"via-grant-v47/squirtle/self-routes": selfRoutesReduced,
|
||||
"via-grant-v48/blastoise/self-routes": selfRoutesReduced,
|
||||
"via-grant-v48/fearow/self-routes": selfRoutesReduced,
|
||||
"via-grant-v48/spearow/self-routes": selfRoutesReduced,
|
||||
"via-grant-v48/squirtle/self-routes": selfRoutesReduced,
|
||||
"via-grant-v49/blastoise/self-routes": selfRoutesReduced,
|
||||
"via-grant-v49/fearow/self-routes": selfRoutesReduced,
|
||||
"via-grant-v49/spearow/self-routes": selfRoutesReduced,
|
||||
"via-grant-v49/squirtle/self-routes": selfRoutesReduced,
|
||||
"via-grant-v50/blastoise/self-routes": selfRoutesReduced,
|
||||
"via-grant-v50/fearow/self-routes": selfRoutesReduced,
|
||||
"via-grant-v50/spearow/self-routes": selfRoutesReduced,
|
||||
"via-grant-v50/squirtle/self-routes": selfRoutesReduced,
|
||||
"via-grant-v63/blastoise/self-routes": selfRoutesReduced,
|
||||
"via-grant-v63/fearow/self-routes": selfRoutesReduced,
|
||||
"via-grant-v63/spearow/self-routes": selfRoutesReduced,
|
||||
"via-grant-v63/squirtle/self-routes": selfRoutesReduced,
|
||||
"via-grant-v66/blastoise/self-routes": selfRoutesReduced,
|
||||
"via-grant-v66/squirtle/self-routes": selfRoutesReduced,
|
||||
"via-grant-v67/blastoise/self-routes": selfRoutesReduced,
|
||||
"via-grant-v67/squirtle/self-routes": selfRoutesReduced,
|
||||
|
||||
"routes-m1-wildcard-src-multi-range/srv/peer-allowed-ips": exitRoutesOnOverlap,
|
||||
"routes-m6-subnet-to-subnet-multi-route/r-multi/peer-allowed-ips": exitRoutesOnOverlap,
|
||||
|
||||
@@ -257,8 +124,6 @@ const exitRoutesOnOverlap = "EXIT_ROUTES_ON_OVERLAP: headscale offers a peer's e
|
||||
|
||||
const sameUserPeers = "SAME_USER_PEERS: SaaS lists a user's own devices as each other's peers with no rule linking them; headscale peers only what the policy links"
|
||||
|
||||
const selfRoutesReduced = "SELF_ROUTES_REDUCED: headscale sends a router only the primary routes its policy lets it reach"
|
||||
|
||||
// TestRoutesMapCompat replays routesMapCompatTests through
|
||||
// [runViaMapCompat].
|
||||
func TestRoutesMapCompat(t *testing.T) {
|
||||
|
||||
Reference in new issue
Block a user