mirror of
https://github.com/juanfont/headscale.git
synced 2025-11-09 05:34:51 -05:00
state: use AllApprovedRoutes instead of SubnetRoutes
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
This commit is contained in:
committed by
Kristoffer Dalby
parent
1c0bb0338d
commit
d7a43a7cf1
@@ -18,6 +18,7 @@ import (
|
|||||||
"github.com/juanfont/headscale/hscontrol/util"
|
"github.com/juanfont/headscale/hscontrol/util"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
"tailscale.com/net/tsaddr"
|
||||||
"tailscale.com/types/key"
|
"tailscale.com/types/key"
|
||||||
"tailscale.com/types/ptr"
|
"tailscale.com/types/ptr"
|
||||||
)
|
)
|
||||||
@@ -232,6 +233,17 @@ func SetApprovedRoutes(
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// When approving exit routes, ensure both IPv4 and IPv6 are included
|
||||||
|
// If either 0.0.0.0/0 or ::/0 is being approved, both should be approved
|
||||||
|
hasIPv4Exit := slices.Contains(routes, tsaddr.AllIPv4())
|
||||||
|
hasIPv6Exit := slices.Contains(routes, tsaddr.AllIPv6())
|
||||||
|
|
||||||
|
if hasIPv4Exit && !hasIPv6Exit {
|
||||||
|
routes = append(routes, tsaddr.AllIPv6())
|
||||||
|
} else if hasIPv6Exit && !hasIPv4Exit {
|
||||||
|
routes = append(routes, tsaddr.AllIPv4())
|
||||||
|
}
|
||||||
|
|
||||||
b, err := json.Marshal(routes)
|
b, err := json.Marshal(routes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -476,7 +476,7 @@ func TestAutoApproveRoutes(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
newRoutes2, changed2 := policy.ApproveRoutesWithPolicy(pm, nodeTagged.View(), node.ApprovedRoutes, tt.routes)
|
newRoutes2, changed2 := policy.ApproveRoutesWithPolicy(pm, nodeTagged.View(), nodeTagged.ApprovedRoutes, tt.routes)
|
||||||
if changed2 {
|
if changed2 {
|
||||||
err = SetApprovedRoutes(adb.DB, nodeTagged.ID, newRoutes2)
|
err = SetApprovedRoutes(adb.DB, nodeTagged.ID, newRoutes2)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -490,7 +490,7 @@ func TestAutoApproveRoutes(t *testing.T) {
|
|||||||
if len(expectedRoutes1) == 0 {
|
if len(expectedRoutes1) == 0 {
|
||||||
expectedRoutes1 = nil
|
expectedRoutes1 = nil
|
||||||
}
|
}
|
||||||
if diff := cmp.Diff(expectedRoutes1, node1ByID.SubnetRoutes(), util.Comparers...); diff != "" {
|
if diff := cmp.Diff(expectedRoutes1, node1ByID.AllApprovedRoutes(), util.Comparers...); diff != "" {
|
||||||
t.Errorf("unexpected enabled routes (-want +got):\n%s", diff)
|
t.Errorf("unexpected enabled routes (-want +got):\n%s", diff)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -501,7 +501,7 @@ func TestAutoApproveRoutes(t *testing.T) {
|
|||||||
if len(expectedRoutes2) == 0 {
|
if len(expectedRoutes2) == 0 {
|
||||||
expectedRoutes2 = nil
|
expectedRoutes2 = nil
|
||||||
}
|
}
|
||||||
if diff := cmp.Diff(expectedRoutes2, node2ByID.SubnetRoutes(), util.Comparers...); diff != "" {
|
if diff := cmp.Diff(expectedRoutes2, node2ByID.AllApprovedRoutes(), util.Comparers...); diff != "" {
|
||||||
t.Errorf("unexpected enabled routes (-want +got):\n%s", diff)
|
t.Errorf("unexpected enabled routes (-want +got):\n%s", diff)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -108,11 +108,12 @@ func TestTailNode(t *testing.T) {
|
|||||||
Hostinfo: &tailcfg.Hostinfo{
|
Hostinfo: &tailcfg.Hostinfo{
|
||||||
RoutableIPs: []netip.Prefix{
|
RoutableIPs: []netip.Prefix{
|
||||||
tsaddr.AllIPv4(),
|
tsaddr.AllIPv4(),
|
||||||
|
tsaddr.AllIPv6(),
|
||||||
netip.MustParsePrefix("192.168.0.0/24"),
|
netip.MustParsePrefix("192.168.0.0/24"),
|
||||||
netip.MustParsePrefix("172.0.0.0/10"),
|
netip.MustParsePrefix("172.0.0.0/10"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ApprovedRoutes: []netip.Prefix{tsaddr.AllIPv4(), netip.MustParsePrefix("192.168.0.0/24")},
|
ApprovedRoutes: []netip.Prefix{tsaddr.AllIPv4(), tsaddr.AllIPv6(), netip.MustParsePrefix("192.168.0.0/24")},
|
||||||
CreatedAt: created,
|
CreatedAt: created,
|
||||||
},
|
},
|
||||||
dnsConfig: &tailcfg.DNSConfig{},
|
dnsConfig: &tailcfg.DNSConfig{},
|
||||||
@@ -150,6 +151,7 @@ func TestTailNode(t *testing.T) {
|
|||||||
Hostinfo: hiview(tailcfg.Hostinfo{
|
Hostinfo: hiview(tailcfg.Hostinfo{
|
||||||
RoutableIPs: []netip.Prefix{
|
RoutableIPs: []netip.Prefix{
|
||||||
tsaddr.AllIPv4(),
|
tsaddr.AllIPv4(),
|
||||||
|
tsaddr.AllIPv6(),
|
||||||
netip.MustParsePrefix("192.168.0.0/24"),
|
netip.MustParsePrefix("192.168.0.0/24"),
|
||||||
netip.MustParsePrefix("172.0.0.0/10"),
|
netip.MustParsePrefix("172.0.0.0/10"),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -456,9 +456,9 @@ func (s *State) Connect(id types.NodeID) []change.ChangeSet {
|
|||||||
log.Info().Uint64("node.id", id.Uint64()).Str("node.name", node.Hostname()).Msg("Node connected")
|
log.Info().Uint64("node.id", id.Uint64()).Str("node.name", node.Hostname()).Msg("Node connected")
|
||||||
|
|
||||||
// Use the node's current routes for primary route update
|
// Use the node's current routes for primary route update
|
||||||
// SubnetRoutes() returns only the intersection of announced AND approved routes
|
// AllApprovedRoutes() returns only the intersection of announced AND approved routes
|
||||||
// We MUST use SubnetRoutes() to maintain the security model
|
// We MUST use AllApprovedRoutes() to maintain the security model
|
||||||
routeChange := s.primaryRoutes.SetRoutes(id, node.SubnetRoutes()...)
|
routeChange := s.primaryRoutes.SetRoutes(id, node.AllApprovedRoutes()...)
|
||||||
|
|
||||||
if routeChange {
|
if routeChange {
|
||||||
c = append(c, change.NodeAdded(id))
|
c = append(c, change.NodeAdded(id))
|
||||||
@@ -656,7 +656,7 @@ func (s *State) SetApprovedRoutes(nodeID types.NodeID, routes []netip.Prefix) (t
|
|||||||
// Update primary routes table based on SubnetRoutes (intersection of announced and approved).
|
// Update primary routes table based on SubnetRoutes (intersection of announced and approved).
|
||||||
// The primary routes table is what the mapper uses to generate network maps, so updating it
|
// The primary routes table is what the mapper uses to generate network maps, so updating it
|
||||||
// here ensures that route changes are distributed to peers.
|
// here ensures that route changes are distributed to peers.
|
||||||
routeChange := s.primaryRoutes.SetRoutes(nodeID, nodeView.SubnetRoutes()...)
|
routeChange := s.primaryRoutes.SetRoutes(nodeID, nodeView.AllApprovedRoutes()...)
|
||||||
|
|
||||||
// If routes changed or the changeset isn't already a full update, trigger a policy change
|
// If routes changed or the changeset isn't already a full update, trigger a policy change
|
||||||
// to ensure all nodes get updated network maps
|
// to ensure all nodes get updated network maps
|
||||||
@@ -1711,7 +1711,7 @@ func (s *State) UpdateNodeFromMapRequest(id types.NodeID, req tailcfg.MapRequest
|
|||||||
}
|
}
|
||||||
|
|
||||||
if needsRouteUpdate {
|
if needsRouteUpdate {
|
||||||
// SetNodeRoutes sets the active/distributed routes, so we must use SubnetRoutes()
|
// SetNodeRoutes sets the active/distributed routes, so we must use AllApprovedRoutes()
|
||||||
// which returns only the intersection of announced AND approved routes.
|
// which returns only the intersection of announced AND approved routes.
|
||||||
// Using AnnouncedRoutes() would bypass the security model and auto-approve everything.
|
// Using AnnouncedRoutes() would bypass the security model and auto-approve everything.
|
||||||
log.Debug().
|
log.Debug().
|
||||||
@@ -1719,9 +1719,9 @@ func (s *State) UpdateNodeFromMapRequest(id types.NodeID, req tailcfg.MapRequest
|
|||||||
Uint64("node.id", id.Uint64()).
|
Uint64("node.id", id.Uint64()).
|
||||||
Strs("announcedRoutes", util.PrefixesToString(updatedNode.AnnouncedRoutes())).
|
Strs("announcedRoutes", util.PrefixesToString(updatedNode.AnnouncedRoutes())).
|
||||||
Strs("approvedRoutes", util.PrefixesToString(updatedNode.ApprovedRoutes().AsSlice())).
|
Strs("approvedRoutes", util.PrefixesToString(updatedNode.ApprovedRoutes().AsSlice())).
|
||||||
Strs("subnetRoutes", util.PrefixesToString(updatedNode.SubnetRoutes())).
|
Strs("allApprovedRoutes", util.PrefixesToString(updatedNode.AllApprovedRoutes())).
|
||||||
Msg("updating node routes for distribution")
|
Msg("updating node routes for distribution")
|
||||||
nodeRouteChange = s.SetNodeRoutes(id, updatedNode.SubnetRoutes()...)
|
nodeRouteChange = s.SetNodeRoutes(id, updatedNode.AllApprovedRoutes()...)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, policyChange, err := s.persistNodeToDB(updatedNode)
|
_, policyChange, err := s.persistNodeToDB(updatedNode)
|
||||||
|
|||||||
Reference in New Issue
Block a user