From ec2e76e7cd29b0e478f83d020ded8b88367234c6 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Sat, 31 Oct 2026 14:01:14 +0000 Subject: [PATCH] servertest: replay ACL captures with auto-approved routes Only the policy approves their routes, so broken approval fails the replay. --- hscontrol/servertest/via_compat_test.go | 49 +++++++++++++++++++++---- 1 file changed, 42 insertions(+), 7 deletions(-) diff --git a/hscontrol/servertest/via_compat_test.go b/hscontrol/servertest/via_compat_test.go index e4ce510df..d213d00f4 100644 --- a/hscontrol/servertest/via_compat_test.go +++ b/hscontrol/servertest/via_compat_test.go @@ -3,8 +3,8 @@ // grant steering with peer connectivity, cross-subnet forwarding and // exit nodes. // -// Test data source: ../policy/v2/testdata/{grant,routes}_results/, see -// viaCompatTests and routesMapCompatTests +// Test data source: ../policy/v2/testdata/{acl,grant,routes}_results/, see +// viaCompatTests, routesMapCompatTests and aclMapCompatTests // Source format: github.com/juanfont/headscale/hscontrol/types/testcapture package servertest_test @@ -107,6 +107,21 @@ var routesMapCompatTests = []struct { {"routes-exit-family-wildcard", "wildcard destinations offer both exit route families"}, } +// aclMapCompatTests lists ACL captures replayed through the mapper, with only +// the policy approving routes: a router advertises in each, so each holds +// auto-approval to SaaS. They are shared with TestACLCompat in policy/v2, which +// reads their filters; this replay reads the netmaps. In acl-ah01 and acl-h03 +// some nodes have peers only through the route the policy approves. +var aclMapCompatTests = []string{ + "acl-ah01", "acl-ah02", "acl-au01", "acl-au06", "acl-c09", + "acl-d03", "acl-d04", "acl-d08", "acl-e01", "acl-e03", + "acl-h02", "acl-h03", "acl-h06", "acl-k01", "acl-k03", + "acl-k05", "acl-m02", "acl-m03", "acl-m10", "acl-mr09", + "acl-mr20", "acl-mu02", "acl-mu07", "acl-o04", "acl-pf03", + "acl-r02", "acl-r08", "acl-rs01", "acl-rs02", "acl-rs03", + "acl-rs07", "acl-t10", "acl-u11", "acl-w02", +} + // viaCompatSkipReasons lists checks where headscale differs from SaaS, keyed // //, or by check alone where every capture differs the // same way. A listed check that matches fails, so a fix must delete its entry. @@ -146,6 +161,25 @@ func TestRoutesMapCompat(t *testing.T) { } } +// TestACLMapCompat replays aclMapCompatTests through [runViaMapCompat]. +func TestACLMapCompat(t *testing.T) { + t.Parallel() + + for _, id := range aclMapCompatTests { + t.Run(id, func(t *testing.T) { + t.Parallel() + + path := filepath.Join("..", "policy", "v2", "testdata", "acl_results", id+".hujson") + + c, err := testcapture.Read(path) + require.NoError(t, err, "failed to read %s", path) + + _, clients := startViaMapCompat(t, c, false) + compareViaMapCompat(t, c, clients) + }) + } +} + // TestViaGrantMapCompat loads golden captures from Tailscale SaaS and // compares headscale's [tailcfg.MapResponse] structure against the captured [netmap.NetworkMap]. // @@ -214,7 +248,7 @@ func TestViaGrantSourceLossCompat(t *testing.T) { before, after := read("via-grant-v66"), read("via-grant-v67") - srv, clients := startViaMapCompat(t, before) + srv, clients := startViaMapCompat(t, before, true) compareViaMapCompat(t, before, clients) user, err := srv.State().GetUserByName("thor") @@ -299,20 +333,21 @@ func passesOnce(check func(require.TestingT)) bool { func runViaMapCompat(t *testing.T, c *testcapture.Capture) { t.Helper() - _, clients := startViaMapCompat(t, c) + _, clients := startViaMapCompat(t, c, true) compareViaMapCompat(t, c, clients) } // startViaMapCompat starts a server holding the capture's policy and a -// client per captured node, with the capture's routes approved, and waits -// for every client's first netmap and the peers SaaS gave it. +// client per captured node, approving routes as [startCaptureClients] does, +// and waits for every client's first netmap and the peers SaaS gave it. func startViaMapCompat( t *testing.T, c *testcapture.Capture, + approve bool, ) (*servertest.TestServer, map[string]*servertest.TestClient) { t.Helper() - srv, clients := startCaptureClients(t, c, true) + srv, clients := startCaptureClients(t, c, approve) // Wait for peers based on golden [netmap.NetworkMap] expected counts. // A viewer whose peers are a listed difference never reaches them.