mirror of
https://github.com/juanfont/headscale.git
synced 2026-11-04 04:34:07 +09:00
policy/v2: make a user's own devices peers under any policy
As SaaS. Without the peer, sending a file to your own device failed with 404 under a restrictive policy. The packet filter is unchanged.
This commit is contained in:
1 parent
420293cecf
commit
490305417c
11 files changed
+127
-107
No files matched your search
@@ -2291,8 +2291,8 @@ func TestEvictedSessionLeavesPeerTrackingAlone(t *testing.T) {
|
||||
td, cleanup := setupBatcherWithTestData(
|
||||
t,
|
||||
NewBatcherAndMapper,
|
||||
1,
|
||||
2,
|
||||
1,
|
||||
normalBufferSize,
|
||||
)
|
||||
defer cleanup()
|
||||
@@ -2379,8 +2379,8 @@ func TestOlderInitialMapLeavesNewerStreamsPeers(t *testing.T) {
|
||||
td, cleanup := setupBatcherWithTestData(
|
||||
t,
|
||||
NewBatcherAndMapper,
|
||||
1,
|
||||
2,
|
||||
1,
|
||||
normalBufferSize,
|
||||
)
|
||||
defer cleanup()
|
||||
|
||||
@@ -385,7 +385,7 @@ func TestBatcherRetryUsesCurrentPolicyAfterReconnect(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBatcherUnreadyRetryRegeneratesRemovals(t *testing.T) {
|
||||
testData, cleanup := setupBatcherWithTestData(t, NewBatcherAndMapper, 1, 2, 1)
|
||||
testData, cleanup := setupBatcherWithTestData(t, NewBatcherAndMapper, 2, 1, 1)
|
||||
defer cleanup()
|
||||
|
||||
b := NewBatcher(time.Hour, 1, testData.Batcher.mapper)
|
||||
@@ -1767,7 +1767,7 @@ func TestMultiChannelSend_SSHPolicyPerConnection(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPartialPeerAdditionThenRevocation(t *testing.T) {
|
||||
td, cleanup := setupBatcherWithTestData(t, NewBatcherAndMapper, 1, 2, 1)
|
||||
td, cleanup := setupBatcherWithTestData(t, NewBatcherAndMapper, 2, 1, 1)
|
||||
defer cleanup()
|
||||
|
||||
_, err := td.State.SetPolicy([]byte(`{"acls":[]}`))
|
||||
|
||||
@@ -673,8 +673,8 @@ func TestBuildFromChangeVisibilityMatchesFullMap(t *testing.T) {
|
||||
// wantFull pins the actual peer-visibility semantics so the cross-path
|
||||
// check below cannot pass vacuously (e.g. if every path broke to zero
|
||||
// identically).
|
||||
// Note deny_all: an empty ACL set yields no peer adjacency, so nothing is
|
||||
// visible on any path. user_isolation and autogroup_self remain the
|
||||
// Note deny_all: an empty ACL set yields no peer adjacency beyond a user's
|
||||
// own devices, on any path. user_isolation and autogroup_self remain the
|
||||
// discriminating cases that prove filtering works.
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -690,7 +690,7 @@ func TestBuildFromChangeVisibilityMatchesFullMap(t *testing.T) {
|
||||
]}`,
|
||||
1,
|
||||
},
|
||||
{"deny_all", `{"acls":[]}`, 0},
|
||||
{"deny_all", `{"acls":[]}`, 1},
|
||||
{
|
||||
"autogroup_self",
|
||||
`{"acls":[{"action":"accept","src":["autogroup:member"],"dst":["autogroup:self:*"]}]}`,
|
||||
|
||||
@@ -197,7 +197,7 @@ func peerMapPrefixReference(
|
||||
canAccessPrefixReference(b, a, mi) ||
|
||||
canAccessPrefixReference(a, b, mj) ||
|
||||
canAccessPrefixReference(b, a, mj)
|
||||
if linkedByEitherPolicy {
|
||||
if linkedByEitherPolicy || sameUser(a.View(), b.View()) {
|
||||
peers[a.ID] = append(peers[a.ID], b.ID)
|
||||
peers[b.ID] = append(peers[b.ID], a.ID)
|
||||
}
|
||||
|
||||
@@ -44,8 +44,10 @@ func buildPeerMapPairScan(pm *PolicyManager, nodes views.Slice[types.NodeView])
|
||||
}
|
||||
|
||||
ri, rj := routeInfo[nodes.At(i).ID()], routeInfo[nodes.At(j).ID()]
|
||||
if nodes.At(i).CanAccessWithRoutes(pm.matchers, nodes.At(j), ri.subnet, rj.subnet, rj.isExit) ||
|
||||
nodes.At(j).CanAccessWithRoutes(pm.matchers, nodes.At(i), rj.subnet, ri.subnet, ri.isExit) {
|
||||
|
||||
linked := nodes.At(i).CanAccessWithRoutes(pm.matchers, nodes.At(j), ri.subnet, rj.subnet, rj.isExit) ||
|
||||
nodes.At(j).CanAccessWithRoutes(pm.matchers, nodes.At(i), rj.subnet, ri.subnet, ri.isExit)
|
||||
if linked || sameUser(nodes.At(i), nodes.At(j)) {
|
||||
ret[nodes.At(i).ID()] = append(ret[nodes.At(i).ID()], nodes.At(j).ID())
|
||||
ret[nodes.At(j).ID()] = append(ret[nodes.At(j).ID()], nodes.At(i).ID())
|
||||
}
|
||||
@@ -70,10 +72,13 @@ func buildPeerMapPairScan(pm *PolicyManager, nodes views.Slice[types.NodeView])
|
||||
matchersJ, hasFilterJ := nodeMatchers[nodeJ.ID()]
|
||||
riJ := routeInfo[nodeJ.ID()]
|
||||
|
||||
if hasFilterI && nodeI.CanAccessWithRoutes(matchersI, nodeJ, riI.subnet, riJ.subnet, riJ.isExit) ||
|
||||
hasFilterJ && nodeJ.CanAccessWithRoutes(matchersJ, nodeI, riJ.subnet, riI.subnet, riI.isExit) ||
|
||||
hasFilterI && nodeJ.CanAccessWithRoutes(matchersI, nodeI, riJ.subnet, riI.subnet, riI.isExit) ||
|
||||
hasFilterJ && nodeI.CanAccessWithRoutes(matchersJ, nodeJ, riI.subnet, riJ.subnet, riJ.isExit) {
|
||||
canIAccessJ := hasFilterI && nodeI.CanAccessWithRoutes(matchersI, nodeJ, riI.subnet, riJ.subnet, riJ.isExit)
|
||||
canJAccessI := hasFilterJ && nodeJ.CanAccessWithRoutes(matchersJ, nodeI, riJ.subnet, riI.subnet, riI.isExit)
|
||||
canJReachI := hasFilterI && nodeJ.CanAccessWithRoutes(matchersI, nodeI, riJ.subnet, riI.subnet, riI.isExit)
|
||||
canIReachJ := hasFilterJ && nodeI.CanAccessWithRoutes(matchersJ, nodeJ, riI.subnet, riJ.subnet, riJ.isExit)
|
||||
|
||||
linked := canIAccessJ || canJAccessI || canJReachI || canIReachJ
|
||||
if linked || sameUser(nodeI, nodeJ) {
|
||||
ret[nodeI.ID()] = append(ret[nodeI.ID()], nodeJ.ID())
|
||||
ret[nodeJ.ID()] = append(ret[nodeJ.ID()], nodeI.ID())
|
||||
}
|
||||
@@ -578,8 +583,8 @@ func buildPeerMapSortedSlice(
|
||||
continue
|
||||
}
|
||||
|
||||
if sortedMatcherSetsIntersect(srcOf[i], dstOf[j]) ||
|
||||
sortedMatcherSetsIntersect(srcOf[j], dstOf[i]) {
|
||||
linked := sortedMatcherSetsIntersect(srcOf[i], dstOf[j]) || sortedMatcherSetsIntersect(srcOf[j], dstOf[i])
|
||||
if linked || sameUser(nodes.At(i), nodes.At(j)) {
|
||||
ret[ids[i]] = append(ret[ids[i]], ids[j])
|
||||
ret[ids[j]] = append(ret[ids[j]], ids[i])
|
||||
}
|
||||
@@ -674,8 +679,8 @@ func buildPeerMapSliceMap(
|
||||
continue
|
||||
}
|
||||
|
||||
if sliceMapMatcherSetsIntersect(srcOf[i], dstOf[j]) ||
|
||||
sliceMapMatcherSetsIntersect(srcOf[j], dstOf[i]) {
|
||||
linked := sliceMapMatcherSetsIntersect(srcOf[i], dstOf[j]) || sliceMapMatcherSetsIntersect(srcOf[j], dstOf[i])
|
||||
if linked || sameUser(nodes.At(i), nodes.At(j)) {
|
||||
ret[ids[i]] = append(ret[ids[i]], ids[j])
|
||||
ret[ids[j]] = append(ret[ids[j]], ids[i])
|
||||
}
|
||||
|
||||
@@ -906,7 +906,8 @@ func (pm *PolicyManager) BuildPeerMap(nodes views.Slice[types.NodeView]) map[typ
|
||||
continue
|
||||
}
|
||||
|
||||
if canAccess(srcOf[i], dstOf[j]) || canAccess(srcOf[j], dstOf[i]) {
|
||||
linked := canAccess(srcOf[i], dstOf[j]) || canAccess(srcOf[j], dstOf[i])
|
||||
if linked || sameUser(nodes.At(i), nodes.At(j)) {
|
||||
ret[ids[i]] = append(ret[ids[i]], ids[j])
|
||||
ret[ids[j]] = append(ret[ids[j]], ids[i])
|
||||
}
|
||||
@@ -959,7 +960,8 @@ func (pm *PolicyManager) BuildPeerMap(nodes views.Slice[types.NodeView]) map[typ
|
||||
canJReachI := hasFilterI && nodeJ.CanAccessWithRoutes(matchersI, nodeI, riJ.subnet, riI.subnet, riI.isExit)
|
||||
canIReachJ := hasFilterJ && nodeI.CanAccessWithRoutes(matchersJ, nodeJ, riI.subnet, riJ.subnet, riJ.isExit)
|
||||
|
||||
if canIAccessJ || canJAccessI || canJReachI || canIReachJ {
|
||||
linked := canIAccessJ || canJAccessI || canJReachI || canIReachJ
|
||||
if linked || sameUser(nodeI, nodeJ) {
|
||||
ret[nodeI.ID()] = append(ret[nodeI.ID()], nodeJ.ID())
|
||||
ret[nodeJ.ID()] = append(ret[nodeJ.ID()], nodeI.ID())
|
||||
}
|
||||
@@ -983,6 +985,21 @@ func matcherReachesDestination(
|
||||
(isExit && m.DestsIsTheInternet())
|
||||
}
|
||||
|
||||
// sameUser reports whether two nodes belong to one user. A user's own devices
|
||||
// are always each other's peers, whatever the rules say, as on SaaS: clients
|
||||
// need the peer to send a file to their own device. It grants no access, so
|
||||
// the packet filter still decides what can connect.
|
||||
func sameUser(a, b types.NodeView) bool {
|
||||
if a.IsTagged() || b.IsTagged() {
|
||||
return false
|
||||
}
|
||||
|
||||
userA, okA := a.UserID().GetOk()
|
||||
userB, okB := b.UserID().GetOk()
|
||||
|
||||
return okA && okB && userA == userB
|
||||
}
|
||||
|
||||
// filterRulesForNodeLocked returns the unreduced compiled filter rules
|
||||
// for a node, combining pre-compiled global rules with per-node self
|
||||
// and via rules from the stored compiled grants.
|
||||
|
||||
@@ -189,15 +189,6 @@ var routesSkipReasons = map[string]string{
|
||||
"routes-m4-member-self/m-odin1/filter": filterAddressForm,
|
||||
"routes-m4-member-self/m-odin2/filter": filterAddressForm,
|
||||
"routes-m4-member-self/r-exit/filter": "FILTER_ADDRESS_FORM: same addresses, different wire form and rule order",
|
||||
|
||||
"routes-m1-wildcard-src-multi-range/m-odin1/peers": sameUserPeers,
|
||||
"routes-m1-wildcard-src-multi-range/m-odin2/peers": sameUserPeers,
|
||||
"routes-m2-member-partial-subnet/m-odin1/peers": sameUserPeers,
|
||||
"routes-m2-member-partial-subnet/m-odin2/peers": sameUserPeers,
|
||||
"routes-m3-subnet-route-as-src/m-odin1/peers": sameUserPeers,
|
||||
"routes-m3-subnet-route-as-src/m-odin2/peers": sameUserPeers,
|
||||
"routes-m6-subnet-to-subnet-multi-route/m-odin1/peers": sameUserPeers,
|
||||
"routes-m6-subnet-to-subnet-multi-route/m-odin2/peers": sameUserPeers,
|
||||
}
|
||||
|
||||
// routesNotCompared lists, by key prefix, checks left out: SaaS's answer
|
||||
@@ -276,8 +267,6 @@ func notCompared(prefixes []string, key string) bool {
|
||||
|
||||
const filterAddressForm = "FILTER_ADDRESS_FORM: same addresses, different wire form"
|
||||
|
||||
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"
|
||||
|
||||
// routesSetByAPI reports whether the capture's routes were approved through
|
||||
// the routes API rather than by the policy.
|
||||
func routesSetByAPI(c *testcapture.Capture) bool {
|
||||
|
||||
@@ -63,9 +63,6 @@ var mapStreamSkipReasons = map[string]string{
|
||||
"map-delivery/S2.5/laptop/endpoints-gap-bystander": restartEndpointsKept,
|
||||
"map-delivery/S2.5/laptop/endpoints-gap-svc": restartEndpointsKept,
|
||||
|
||||
"map-delivery/S3.6/bystander/peers": sameUserPeers,
|
||||
"map-delivery/S3.6/laptop/peers": sameUserPeers,
|
||||
|
||||
"hidden-peers/S2/witness/presence": policyResponseToEveryNode,
|
||||
"hidden-peers/S8/observer/presence": policyResponseToEveryNode,
|
||||
"hidden-peers/S9/observer/presence": policyResponseToEveryNode,
|
||||
@@ -81,19 +78,6 @@ var mapStreamSkipReasons = map[string]string{
|
||||
"route-announce/RA9/rtr/presence": policyResponseToEveryNode,
|
||||
"route-announce/RA9/srv/presence": policyResponseToEveryNode,
|
||||
"route-announce/RA9/wit/presence": policyResponseToEveryNode,
|
||||
|
||||
"policy-generation/G2/thor-1/peers": sameUserPeers,
|
||||
"policy-generation/G2/thor-2/peers": sameUserPeers,
|
||||
"policy-generation/G3/thor-1/peers": sameUserPeers,
|
||||
"policy-generation/G3/thor-2/peers": sameUserPeers,
|
||||
"policy-generation/G4/thor-1/peers": sameUserPeers,
|
||||
"policy-generation/G4/thor-2/peers": sameUserPeers,
|
||||
"policy-generation/G2/thor-1/peer-owners": sameUserPeers,
|
||||
"policy-generation/G2/thor-2/peer-owners": sameUserPeers,
|
||||
"policy-generation/G3/thor-1/peer-owners": sameUserPeers,
|
||||
"policy-generation/G3/thor-2/peer-owners": sameUserPeers,
|
||||
"policy-generation/G4/thor-1/peer-owners": sameUserPeers,
|
||||
"policy-generation/G4/thor-2/peer-owners": sameUserPeers,
|
||||
}
|
||||
|
||||
const (
|
||||
|
||||
@@ -30,7 +30,6 @@ type scaleCase struct {
|
||||
policy string
|
||||
taggedService bool
|
||||
ssh bool
|
||||
peerCount func(node, total int) int
|
||||
canSend func(source, destination int) bool
|
||||
}
|
||||
|
||||
@@ -47,10 +46,6 @@ func TestScale(t *testing.T) {
|
||||
require.GreaterOrEqual(t, nodes, 2, "need at least two connected nodes")
|
||||
}
|
||||
|
||||
personalPeers := func(node, total int) int {
|
||||
first := node / scaleDevicesPerUser * scaleDevicesPerUser
|
||||
return min(scaleDevicesPerUser, total-first) - 1
|
||||
}
|
||||
personalVisibility := func(node, peer int) bool {
|
||||
return node/scaleDevicesPerUser == peer/scaleDevicesPerUser
|
||||
}
|
||||
@@ -61,14 +56,12 @@ func TestScale(t *testing.T) {
|
||||
policy: `{
|
||||
"grants": [{"src": ["*"], "dst": ["*"], "ip": ["tcp:443"]}]
|
||||
}`,
|
||||
peerCount: func(_, total int) int { return total - 1 },
|
||||
canSend: func(_, _ int) bool { return true },
|
||||
canSend: func(_, _ int) bool { return true },
|
||||
},
|
||||
{
|
||||
name: "isolated",
|
||||
policy: `{"acls": []}`,
|
||||
peerCount: func(_, _ int) int { return 0 },
|
||||
canSend: func(_, _ int) bool { return false },
|
||||
name: "isolated",
|
||||
policy: `{"acls": []}`,
|
||||
canSend: func(_, _ int) bool { return false },
|
||||
},
|
||||
{
|
||||
name: "personal_devices",
|
||||
@@ -78,8 +71,7 @@ func TestScale(t *testing.T) {
|
||||
"ip": ["tcp:443"]
|
||||
}]
|
||||
}`,
|
||||
peerCount: personalPeers,
|
||||
canSend: personalVisibility,
|
||||
canSend: personalVisibility,
|
||||
},
|
||||
{
|
||||
name: "shared_service",
|
||||
@@ -91,14 +83,7 @@ func TestScale(t *testing.T) {
|
||||
}]
|
||||
}`,
|
||||
taggedService: true,
|
||||
peerCount: func(node, total int) int {
|
||||
if node == 0 {
|
||||
return total - 1
|
||||
}
|
||||
|
||||
return 1
|
||||
},
|
||||
canSend: func(source, destination int) bool { return source != 0 && destination == 0 },
|
||||
canSend: func(source, destination int) bool { return source != 0 && destination == 0 },
|
||||
},
|
||||
{
|
||||
name: "personal_ssh",
|
||||
@@ -112,9 +97,8 @@ func TestScale(t *testing.T) {
|
||||
"dst": ["autogroup:self"], "users": ["root"]
|
||||
}]
|
||||
}`,
|
||||
ssh: true,
|
||||
peerCount: personalPeers,
|
||||
canSend: personalVisibility,
|
||||
ssh: true,
|
||||
canSend: personalVisibility,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -220,10 +204,7 @@ func runScale(t *testing.T, nodes int, tc scaleCase) {
|
||||
deadline := time.Now().Add(convergenceTimeout)
|
||||
|
||||
for i, c := range clients {
|
||||
wantPeers := tc.peerCount(i, nodes)
|
||||
if revoked {
|
||||
wantPeers = 0
|
||||
}
|
||||
wantPeers := tc.peerCount(i, nodes, revoked)
|
||||
|
||||
c.WaitForCondition(
|
||||
t,
|
||||
@@ -244,10 +225,13 @@ func runScale(t *testing.T, nodes int, tc scaleCase) {
|
||||
return false
|
||||
}
|
||||
|
||||
return tc.peersMatch(nm, i, indices, osMarker)
|
||||
// The policy's attribute can reach the node a frame ahead of
|
||||
// its filter, and peers alone may not tell the two apart.
|
||||
return tc.peersMatch(nm, i, indices, osMarker) &&
|
||||
scalePacketFilterMismatch(tc, nm, selfNodes, i, revoked) == ""
|
||||
},
|
||||
)
|
||||
checkScalePacketFilter(t, tc, c.Netmap(), selfNodes, i, revoked)
|
||||
require.Empty(t, scalePacketFilterMismatch(tc, c.Netmap(), selfNodes, i, revoked))
|
||||
|
||||
if tc.ssh && !revoked {
|
||||
checkScaleSSH(t, c.Netmap().SSHPolicy, selfNodes, i)
|
||||
@@ -337,6 +321,40 @@ func runScale(t *testing.T, nodes int, tc scaleCase) {
|
||||
})
|
||||
}
|
||||
|
||||
// peerCount is how many peers viewer has: the nodes it can reach or be reached
|
||||
// from, and its user's other devices, which are peers under any policy.
|
||||
func (tc scaleCase) peerCount(viewer, total int, revoked bool) int {
|
||||
count := 0
|
||||
|
||||
for j := range total {
|
||||
if j == viewer {
|
||||
continue
|
||||
}
|
||||
|
||||
reachable := !revoked && tc.linked(viewer, j)
|
||||
if reachable || tc.sameUser(viewer, j) {
|
||||
count++
|
||||
}
|
||||
}
|
||||
|
||||
return count
|
||||
}
|
||||
|
||||
// sameUser reports whether two nodes are devices of one user. The tagged
|
||||
// service belongs to no user.
|
||||
func (tc scaleCase) sameUser(a, b int) bool {
|
||||
if tc.taggedService && (a == 0 || b == 0) {
|
||||
return false
|
||||
}
|
||||
|
||||
return a/scaleDevicesPerUser == b/scaleDevicesPerUser
|
||||
}
|
||||
|
||||
// linked reports whether the policy lets either node reach the other.
|
||||
func (tc scaleCase) linked(a, b int) bool {
|
||||
return tc.canSend(a, b) || tc.canSend(b, a)
|
||||
}
|
||||
|
||||
func (tc scaleCase) peersMatch(
|
||||
nm *netmap.NetworkMap,
|
||||
viewer int,
|
||||
@@ -347,8 +365,11 @@ func (tc scaleCase) peersMatch(
|
||||
|
||||
for _, peer := range nm.Peers {
|
||||
j, ok := indices[peer.ID()]
|
||||
if !ok || j == viewer || (!tc.canSend(viewer, j) && !tc.canSend(j, viewer)) ||
|
||||
peer.ID() <= previous {
|
||||
if !ok || j == viewer || peer.ID() <= previous {
|
||||
return false
|
||||
}
|
||||
|
||||
if !tc.linked(viewer, j) && !tc.sameUser(viewer, j) {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -405,24 +426,25 @@ func updateScaleClients(
|
||||
}
|
||||
}
|
||||
|
||||
// Probe a few sources per node instead of making the test itself all-pairs.
|
||||
func checkScalePacketFilter(
|
||||
t *testing.T,
|
||||
// scalePacketFilterMismatch names the first verdict nm's packet filter gets
|
||||
// wrong, or returns "". Probe a few sources per node instead of making the test
|
||||
// itself all-pairs.
|
||||
func scalePacketFilterMismatch(
|
||||
tc scaleCase,
|
||||
nm *netmap.NetworkMap,
|
||||
selfNodes []tailcfg.NodeView,
|
||||
viewer int,
|
||||
revoked bool,
|
||||
) {
|
||||
t.Helper()
|
||||
|
||||
) string {
|
||||
var localIPs netipx.IPSetBuilder
|
||||
for _, prefix := range nm.SelfNode.Addresses().All() {
|
||||
localIPs.AddPrefix(prefix)
|
||||
}
|
||||
|
||||
local, err := localIPs.IPSet()
|
||||
require.NoError(t, err)
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
|
||||
packetFilter := filter.New(nm.PacketFilter, nil, local, nil, nil, logger.Discard)
|
||||
|
||||
@@ -439,20 +461,30 @@ func checkScalePacketFilter(
|
||||
want = filter.Accept
|
||||
}
|
||||
|
||||
require.Equal(t, want, packetFilter.CheckTCP(src, dst.Addr(), 443),
|
||||
"%d -> %d TCP 443", selfNodes[source].ID(), selfNodes[viewer].ID())
|
||||
require.Equal(t, filter.Drop, packetFilter.CheckTCP(src, dst.Addr(), 80))
|
||||
require.Equal(
|
||||
t,
|
||||
filter.Drop,
|
||||
packetFilter.Check(src, dst.Addr(), 443, ipproto.UDP),
|
||||
)
|
||||
type verdict struct {
|
||||
name string
|
||||
want, got filter.Response
|
||||
}
|
||||
|
||||
checks := []verdict{
|
||||
{"TCP 443", want, packetFilter.CheckTCP(src, dst.Addr(), 443)},
|
||||
{"TCP 80", filter.Drop, packetFilter.CheckTCP(src, dst.Addr(), 80)},
|
||||
{"UDP 443", filter.Drop, packetFilter.Check(src, dst.Addr(), 443, ipproto.UDP)},
|
||||
}
|
||||
if tc.ssh {
|
||||
require.Equal(t, want, packetFilter.CheckTCP(src, dst.Addr(), 22))
|
||||
checks = append(checks, verdict{"TCP 22", want, packetFilter.CheckTCP(src, dst.Addr(), 22)})
|
||||
}
|
||||
|
||||
for _, check := range checks {
|
||||
if check.got != check.want {
|
||||
return fmt.Sprintf("%d -> %d %s: got %v, want %v",
|
||||
selfNodes[source].ID(), selfNodes[viewer].ID(), check.name, check.got, check.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func checkScaleSSH(
|
||||
|
||||
@@ -143,19 +143,8 @@ var aclMapCompatTests = []string{
|
||||
// same way. A listed check that matches fails, so a fix must delete its entry.
|
||||
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",
|
||||
|
||||
"routes-m1-wildcard-src-multi-range/m-odin1/peers": sameUserPeers,
|
||||
"routes-m1-wildcard-src-multi-range/m-odin2/peers": sameUserPeers,
|
||||
"routes-m2-member-partial-subnet/m-odin1/peers": sameUserPeers,
|
||||
"routes-m2-member-partial-subnet/m-odin2/peers": sameUserPeers,
|
||||
"routes-m3-subnet-route-as-src/m-odin1/peers": sameUserPeers,
|
||||
"routes-m3-subnet-route-as-src/m-odin2/peers": sameUserPeers,
|
||||
"routes-m6-subnet-to-subnet-multi-route/m-odin1/peers": sameUserPeers,
|
||||
"routes-m6-subnet-to-subnet-multi-route/m-odin2/peers": sameUserPeers,
|
||||
}
|
||||
|
||||
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"
|
||||
|
||||
// TestRoutesMapCompat replays routesMapCompatTests through
|
||||
// [runViaMapCompat].
|
||||
func TestRoutesMapCompat(t *testing.T) {
|
||||
|
||||
@@ -25,7 +25,11 @@ func TestReloadPolicyRebuildsPeerMapOnChangeOnly(t *testing.T) {
|
||||
database, err := db.NewHeadscaleDatabase(cfg)
|
||||
require.NoError(t, err)
|
||||
|
||||
nodes := database.CreateRegisteredNodesForTest(database.CreateUserForTest("reload-user"), 2, "reload-node")
|
||||
// Two users: a user's own devices stay peers under any policy.
|
||||
nodes := append(
|
||||
database.CreateRegisteredNodesForTest(database.CreateUserForTest("reload-user"), 1, "reload-node"),
|
||||
database.CreateRegisteredNodesForTest(database.CreateUserForTest("reload-other"), 1, "reload-other")...,
|
||||
)
|
||||
require.NoError(t, database.Close())
|
||||
|
||||
s, err := NewState(cfg)
|
||||
|
||||
Reference in new issue
Block a user