mirror of
https://github.com/juanfont/headscale.git
synced 2025-08-01 13:46:49 +02:00
split out exit nodes from primary manager
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
This commit is contained in:
parent
8e3201b78d
commit
c047278109
@ -165,9 +165,10 @@ func Test_fullMapResponse(t *testing.T) {
|
||||
),
|
||||
Addresses: []netip.Prefix{netip.MustParsePrefix("100.64.0.1/32")},
|
||||
AllowedIPs: []netip.Prefix{
|
||||
netip.MustParsePrefix("100.64.0.1/32"),
|
||||
tsaddr.AllIPv4(),
|
||||
netip.MustParsePrefix("192.168.0.0/24"),
|
||||
netip.MustParsePrefix("100.64.0.1/32"),
|
||||
tsaddr.AllIPv6(),
|
||||
},
|
||||
PrimaryRoutes: []netip.Prefix{
|
||||
netip.MustParsePrefix("192.168.0.0/24"),
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
"github.com/juanfont/headscale/hscontrol/routes"
|
||||
"github.com/juanfont/headscale/hscontrol/types"
|
||||
"github.com/samber/lo"
|
||||
"tailscale.com/net/tsaddr"
|
||||
"tailscale.com/tailcfg"
|
||||
)
|
||||
|
||||
@ -80,6 +81,10 @@ func tailNode(
|
||||
}
|
||||
tags = lo.Uniq(append(tags, node.ForcedTags...))
|
||||
|
||||
allowed := append(node.Prefixes(), primary.PrimaryRoutes(node.ID)...)
|
||||
allowed = append(allowed, node.ExitRoutes()...)
|
||||
tsaddr.SortPrefixes(allowed)
|
||||
|
||||
tNode := tailcfg.Node{
|
||||
ID: tailcfg.NodeID(node.ID), // this is the actual ID
|
||||
StableID: node.ID.StableID(),
|
||||
|
@ -137,9 +137,10 @@ func TestTailNode(t *testing.T) {
|
||||
),
|
||||
Addresses: []netip.Prefix{netip.MustParsePrefix("100.64.0.1/32")},
|
||||
AllowedIPs: []netip.Prefix{
|
||||
netip.MustParsePrefix("100.64.0.1/32"),
|
||||
tsaddr.AllIPv4(),
|
||||
netip.MustParsePrefix("192.168.0.0/24"),
|
||||
netip.MustParsePrefix("100.64.0.1/32"),
|
||||
tsaddr.AllIPv6(),
|
||||
},
|
||||
PrimaryRoutes: []netip.Prefix{
|
||||
netip.MustParsePrefix("192.168.0.0/24"),
|
||||
|
@ -102,12 +102,16 @@ func (pr *PrimaryRoutes) updatePrimaryLocked() bool {
|
||||
return changed
|
||||
}
|
||||
|
||||
func (pr *PrimaryRoutes) SetRoutes(node types.NodeID, prefix ...netip.Prefix) bool {
|
||||
// SetRoutes sets the routes for a given Node ID and recalculates the primary routes
|
||||
// of the headscale.
|
||||
// It returns true if there was a change in primary routes.
|
||||
// All exit routes are ignored as they are not used in primary route context.
|
||||
func (pr *PrimaryRoutes) SetRoutes(node types.NodeID, prefixes ...netip.Prefix) bool {
|
||||
pr.mu.Lock()
|
||||
defer pr.mu.Unlock()
|
||||
|
||||
// If no routes are being set, remove the node from the routes map.
|
||||
if len(prefix) == 0 {
|
||||
if len(prefixes) == 0 {
|
||||
if _, ok := pr.routes[node]; ok {
|
||||
delete(pr.routes, node)
|
||||
return pr.updatePrimaryLocked()
|
||||
|
@ -366,7 +366,6 @@ func TestPrimaryRoutes(t *testing.T) {
|
||||
},
|
||||
expectedPrimaries: map[netip.Prefix]types.NodeID{
|
||||
mp("192.168.1.0/24"): 1,
|
||||
mp("0.0.0.0/0"): 1,
|
||||
},
|
||||
expectedIsPrimary: map[types.NodeID]bool{
|
||||
1: true,
|
||||
@ -389,6 +388,26 @@ func TestPrimaryRoutes(t *testing.T) {
|
||||
expectedRoutes: nil,
|
||||
expectedChange: false,
|
||||
},
|
||||
{
|
||||
name: "exit-nodes",
|
||||
operations: func(pr *PrimaryRoutes) bool {
|
||||
pr.SetRoutes(1, mp("10.0.0.0/16"), mp("0.0.0.0/0"), mp("::/0"))
|
||||
pr.SetRoutes(3, mp("0.0.0.0/0"), mp("::/0"))
|
||||
return pr.SetRoutes(2, mp("0.0.0.0/0"), mp("::/0"))
|
||||
},
|
||||
expectedRoutes: map[types.NodeID]set.Set[netip.Prefix]{
|
||||
1: {
|
||||
mp("10.0.0.0/16"): {},
|
||||
},
|
||||
},
|
||||
expectedPrimaries: map[netip.Prefix]types.NodeID{
|
||||
mp("10.0.0.0/16"): 1,
|
||||
},
|
||||
expectedIsPrimary: map[types.NodeID]bool{
|
||||
1: true,
|
||||
},
|
||||
expectedChange: false,
|
||||
},
|
||||
{
|
||||
name: "concurrent-access",
|
||||
operations: func(pr *PrimaryRoutes) bool {
|
||||
|
@ -14,6 +14,7 @@ import (
|
||||
"github.com/juanfont/headscale/hscontrol/util"
|
||||
"go4.org/netipx"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
"tailscale.com/net/tsaddr"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/types/key"
|
||||
)
|
||||
@ -222,6 +223,19 @@ func (node *Node) Prefixes() []netip.Prefix {
|
||||
return addrs
|
||||
}
|
||||
|
||||
// ExitRoutes returns a list of both exit routes if the
|
||||
// node has any exit routes enabled.
|
||||
// If none are enabled, it will return nil.
|
||||
func (node *Node) ExitRoutes() []netip.Prefix {
|
||||
for _, route := range node.SubnetRoutes() {
|
||||
if tsaddr.IsExitRoute(route) {
|
||||
return tsaddr.ExitRoutes()
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (node *Node) IPsAsString() []string {
|
||||
var ret []string
|
||||
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
|
||||
policyv1 "github.com/juanfont/headscale/hscontrol/policy/v1"
|
||||
"github.com/juanfont/headscale/hscontrol/util"
|
||||
@ -19,6 +20,7 @@ import (
|
||||
"tailscale.com/net/tsaddr"
|
||||
"tailscale.com/types/ipproto"
|
||||
"tailscale.com/types/views"
|
||||
"tailscale.com/util/slicesx"
|
||||
"tailscale.com/wgengine/filter"
|
||||
)
|
||||
|
||||
@ -125,7 +127,7 @@ func TestEnablingRoutes(t *testing.T) {
|
||||
for _, peerKey := range status.Peers() {
|
||||
peerStatus := status.Peer[peerKey]
|
||||
|
||||
assert.Nil(t, peerStatus.PrimaryRoutes)
|
||||
assert.NotNil(t, peerStatus.PrimaryRoutes)
|
||||
|
||||
assert.Len(t, peerStatus.AllowedIPs.AsSlice(), 3)
|
||||
|
||||
@ -189,7 +191,6 @@ func TestEnablingRoutes(t *testing.T) {
|
||||
for _, peerKey := range status.Peers() {
|
||||
peerStatus := status.Peer[peerKey]
|
||||
|
||||
assert.Nil(t, peerStatus.PrimaryRoutes)
|
||||
if peerStatus.ID == "1" {
|
||||
requirePeerSubnetRoutes(t, peerStatus, nil)
|
||||
} else if peerStatus.ID == "2" {
|
||||
@ -1378,6 +1379,32 @@ func TestSubnetRouterMultiNetwork(t *testing.T) {
|
||||
peerStatus := status.Peer[peerKey]
|
||||
|
||||
assert.Contains(t, peerStatus.PrimaryRoutes.AsSlice(), *pref)
|
||||
requirePeerSubnetRoutes(t, peerStatus, []netip.Prefix{*pref})
|
||||
}
|
||||
|
||||
usernet1, err := scenario.Network("usernet1")
|
||||
require.NoError(t, err)
|
||||
|
||||
services, err := scenario.Services("usernet1")
|
||||
require.NoError(t, err)
|
||||
require.Len(t, services, 1)
|
||||
|
||||
web := services[0]
|
||||
webip := netip.MustParseAddr(web.GetIPInNetwork(usernet1))
|
||||
|
||||
url := fmt.Sprintf("http://%s/etc/hostname", webip)
|
||||
t.Logf("url from %s to %s", user2c.Hostname(), url)
|
||||
|
||||
result, err := user2c.Curl(url)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, result, 13)
|
||||
|
||||
tr, err := user2c.Traceroute(webip)
|
||||
require.NoError(t, err)
|
||||
assertTracerouteViaIP(t, tr, user1c.MustIPv4())
|
||||
}
|
||||
|
||||
// TestSubnetRouterMultiNetworkExitNode
|
||||
func TestSubnetRouterMultiNetworkExitNode(t *testing.T) {
|
||||
IntegrationSkip(t)
|
||||
t.Parallel()
|
||||
@ -1510,32 +1537,6 @@ func TestSubnetRouterMultiNetworkExitNode(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
assert.Nil(t, peerStatus.PrimaryRoutes)
|
||||
requirePeerSubnetRoutes(t, peerStatus, []netip.Prefix{*pref})
|
||||
}
|
||||
|
||||
usernet1, err := scenario.Network("usernet1")
|
||||
require.NoError(t, err)
|
||||
|
||||
services, err := scenario.Services("usernet1")
|
||||
require.NoError(t, err)
|
||||
require.Len(t, services, 1)
|
||||
|
||||
web := services[0]
|
||||
webip := netip.MustParseAddr(web.GetIPInNetwork(usernet1))
|
||||
|
||||
url := fmt.Sprintf("http://%s/etc/hostname", webip)
|
||||
t.Logf("url from %s to %s", user2c.Hostname(), url)
|
||||
|
||||
result, err := user2c.Curl(url)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, result, 13)
|
||||
|
||||
tr, err := user2c.Traceroute(webip)
|
||||
require.NoError(t, err)
|
||||
assertTracerouteViaIP(t, tr, user1c.MustIPv4())
|
||||
}
|
||||
|
||||
func assertTracerouteViaIP(t *testing.T, tr util.Traceroute, ip netip.Addr) {
|
||||
t.Helper()
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user