policy: reduce routes sent to peers based on packetfilter (#2561)
* notifier: use convenience funcs Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> * policy: reduce routes based on policy Fixes #2365 Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> * hsic: more helper methods Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> * policy: more test cases Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> * integration: add route with filter acl integration test Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> * integration: correct route reduce test, now failing Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> * mapper: compare peer routes against node Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> * hs: more output to debug strings Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> * types/node: slice.ContainsFunc Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> * policy: more reduce route test Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> * changelog: add entry for route filter Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> --------- Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
This commit is contained in:
parent
b9868f6516
commit
45e38cb080
16 changed files with 903 additions and 47 deletions
|
@ -5,6 +5,7 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"net/netip"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
|
@ -308,9 +309,15 @@ func (m *Mapper) PeerChangedResponse(
|
|||
resp.PeersChangedPatch = patches
|
||||
}
|
||||
|
||||
_, matchers := m.polMan.Filter()
|
||||
// Add the node itself, it might have changed, and particularly
|
||||
// if there are no patches or changes, this is a self update.
|
||||
tailnode, err := tailNode(node, mapRequest.Version, m.polMan, m.primary, m.cfg)
|
||||
tailnode, err := tailNode(
|
||||
node, mapRequest.Version, m.polMan,
|
||||
func(id types.NodeID) []netip.Prefix {
|
||||
return policy.ReduceRoutes(node, m.primary.PrimaryRoutes(id), matchers)
|
||||
},
|
||||
m.cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -347,7 +354,7 @@ func (m *Mapper) marshalMapResponse(
|
|||
}
|
||||
|
||||
if debugDumpMapResponsePath != "" {
|
||||
data := map[string]interface{}{
|
||||
data := map[string]any{
|
||||
"Messages": messages,
|
||||
"MapRequest": mapRequest,
|
||||
"MapResponse": resp,
|
||||
|
@ -457,7 +464,13 @@ func (m *Mapper) baseWithConfigMapResponse(
|
|||
) (*tailcfg.MapResponse, error) {
|
||||
resp := m.baseMapResponse()
|
||||
|
||||
tailnode, err := tailNode(node, capVer, m.polMan, m.primary, m.cfg)
|
||||
_, matchers := m.polMan.Filter()
|
||||
tailnode, err := tailNode(
|
||||
node, capVer, m.polMan,
|
||||
func(id types.NodeID) []netip.Prefix {
|
||||
return policy.ReduceRoutes(node, m.primary.PrimaryRoutes(id), matchers)
|
||||
},
|
||||
m.cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -513,15 +526,10 @@ func (m *Mapper) ListNodes(nodeIDs ...types.NodeID) (types.Nodes, error) {
|
|||
return nodes, nil
|
||||
}
|
||||
|
||||
func nodeMapToList(nodes map[uint64]*types.Node) types.Nodes {
|
||||
ret := make(types.Nodes, 0)
|
||||
|
||||
for _, node := range nodes {
|
||||
ret = append(ret, node)
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
// routeFilterFunc is a function that takes a node ID and returns a list of
|
||||
// netip.Prefixes that are allowed for that node. It is used to filter routes
|
||||
// from the primary route manager to the node.
|
||||
type routeFilterFunc func(id types.NodeID) []netip.Prefix
|
||||
|
||||
// appendPeerChanges mutates a tailcfg.MapResponse with all the
|
||||
// necessary changes when peers have changed.
|
||||
|
@ -546,14 +554,19 @@ func appendPeerChanges(
|
|||
// If there are filter rules present, see if there are any nodes that cannot
|
||||
// access each-other at all and remove them from the peers.
|
||||
if len(filter) > 0 {
|
||||
changed = policy.FilterNodesByACL(node, changed, matchers)
|
||||
changed = policy.ReduceNodes(node, changed, matchers)
|
||||
}
|
||||
|
||||
profiles := generateUserProfiles(node, changed)
|
||||
|
||||
dnsConfig := generateDNSConfig(cfg, node)
|
||||
|
||||
tailPeers, err := tailNodes(changed, capVer, polMan, primary, cfg)
|
||||
tailPeers, err := tailNodes(
|
||||
changed, capVer, polMan,
|
||||
func(id types.NodeID) []netip.Prefix {
|
||||
return policy.ReduceRoutes(node, primary.PrimaryRoutes(id), matchers)
|
||||
},
|
||||
cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -348,6 +348,11 @@ func Test_fullMapResponse(t *testing.T) {
|
|||
"src": ["100.64.0.2"],
|
||||
"dst": ["user1@:*"],
|
||||
},
|
||||
{
|
||||
"action": "accept",
|
||||
"src": ["100.64.0.1"],
|
||||
"dst": ["192.168.0.0/24:*"],
|
||||
},
|
||||
],
|
||||
}
|
||||
`),
|
||||
|
@ -380,6 +385,10 @@ func Test_fullMapResponse(t *testing.T) {
|
|||
{IP: "100.64.0.1/32", Ports: tailcfg.PortRangeAny},
|
||||
},
|
||||
},
|
||||
{
|
||||
SrcIPs: []string{"100.64.0.1/32"},
|
||||
DstPorts: []tailcfg.NetPortRange{{IP: "192.168.0.0/24", Ports: tailcfg.PortRangeAny}},
|
||||
},
|
||||
},
|
||||
},
|
||||
SSHPolicy: nil,
|
||||
|
|
|
@ -5,7 +5,6 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/juanfont/headscale/hscontrol/policy"
|
||||
"github.com/juanfont/headscale/hscontrol/routes"
|
||||
"github.com/juanfont/headscale/hscontrol/types"
|
||||
"github.com/samber/lo"
|
||||
"tailscale.com/net/tsaddr"
|
||||
|
@ -16,7 +15,7 @@ func tailNodes(
|
|||
nodes types.Nodes,
|
||||
capVer tailcfg.CapabilityVersion,
|
||||
polMan policy.PolicyManager,
|
||||
primary *routes.PrimaryRoutes,
|
||||
primaryRouteFunc routeFilterFunc,
|
||||
cfg *types.Config,
|
||||
) ([]*tailcfg.Node, error) {
|
||||
tNodes := make([]*tailcfg.Node, len(nodes))
|
||||
|
@ -26,7 +25,7 @@ func tailNodes(
|
|||
node,
|
||||
capVer,
|
||||
polMan,
|
||||
primary,
|
||||
primaryRouteFunc,
|
||||
cfg,
|
||||
)
|
||||
if err != nil {
|
||||
|
@ -44,7 +43,7 @@ func tailNode(
|
|||
node *types.Node,
|
||||
capVer tailcfg.CapabilityVersion,
|
||||
polMan policy.PolicyManager,
|
||||
primary *routes.PrimaryRoutes,
|
||||
primaryRouteFunc routeFilterFunc,
|
||||
cfg *types.Config,
|
||||
) (*tailcfg.Node, error) {
|
||||
addrs := node.Prefixes()
|
||||
|
@ -81,7 +80,8 @@ func tailNode(
|
|||
}
|
||||
tags = lo.Uniq(append(tags, node.ForcedTags...))
|
||||
|
||||
allowed := append(node.Prefixes(), primary.PrimaryRoutes(node.ID)...)
|
||||
routes := primaryRouteFunc(node.ID)
|
||||
allowed := append(node.Prefixes(), routes...)
|
||||
allowed = append(allowed, node.ExitRoutes()...)
|
||||
tsaddr.SortPrefixes(allowed)
|
||||
|
||||
|
@ -99,7 +99,7 @@ func tailNode(
|
|||
Machine: node.MachineKey,
|
||||
DiscoKey: node.DiscoKey,
|
||||
Addresses: addrs,
|
||||
PrimaryRoutes: primary.PrimaryRoutes(node.ID),
|
||||
PrimaryRoutes: routes,
|
||||
AllowedIPs: allowed,
|
||||
Endpoints: node.Endpoints,
|
||||
HomeDERP: derp,
|
||||
|
|
|
@ -219,7 +219,9 @@ func TestTailNode(t *testing.T) {
|
|||
tt.node,
|
||||
0,
|
||||
polMan,
|
||||
primary,
|
||||
func(id types.NodeID) []netip.Prefix {
|
||||
return primary.PrimaryRoutes(id)
|
||||
},
|
||||
cfg,
|
||||
)
|
||||
|
||||
|
@ -266,14 +268,20 @@ func TestNodeExpiry(t *testing.T) {
|
|||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
node := &types.Node{
|
||||
ID: 0,
|
||||
GivenName: "test",
|
||||
Expiry: tt.exp,
|
||||
}
|
||||
polMan, err := policy.NewPolicyManager(nil, nil, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
tn, err := tailNode(
|
||||
node,
|
||||
0,
|
||||
nil, // TODO(kradalby): removed in merge but error?
|
||||
nil,
|
||||
polMan,
|
||||
func(id types.NodeID) []netip.Prefix {
|
||||
return []netip.Prefix{}
|
||||
},
|
||||
&types.Config{},
|
||||
)
|
||||
if err != nil {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue