From 95150401612ec5143f5d9d44bcfa8730bcf94bba Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Thu, 3 Oct 2024 12:01:48 +0200 Subject: [PATCH] make reauth test compat with tailscale head (#2167) * make reauth test compat with tailscale head tailscale/tailscale@1eaad7d broke our reauth test as it makes the client retry with https/443 if it reconnects within 2 minutes. This commit fixes this by running the test as a two part, - with https, to confirm instant reconnect works - with http, and a 3 min wait, to check that it work without. The change is not a general consern as headscale in prod is ran with https. Updates #2164 Signed-off-by: Kristoffer Dalby * sort test for stable order Signed-off-by: Kristoffer Dalby --------- Signed-off-by: Kristoffer Dalby --- hscontrol/db/preauth_keys_test.go | 5 +- integration/general_test.go | 219 +++++++++++++++++------------- 2 files changed, 125 insertions(+), 99 deletions(-) diff --git a/hscontrol/db/preauth_keys_test.go b/hscontrol/db/preauth_keys_test.go index 9dd5b199..ec3f6441 100644 --- a/hscontrol/db/preauth_keys_test.go +++ b/hscontrol/db/preauth_keys_test.go @@ -1,6 +1,7 @@ package db import ( + "sort" "time" "github.com/juanfont/headscale/hscontrol/types" @@ -169,5 +170,7 @@ func (*Suite) TestPreAuthKeyACLTags(c *check.C) { listedPaks, err := db.ListPreAuthKeys("test8") c.Assert(err, check.IsNil) - c.Assert(listedPaks[0].Proto().GetAclTags(), check.DeepEquals, tags) + gotTags := listedPaks[0].Proto().GetAclTags() + sort.Sort(sort.StringSlice(gotTags)) + c.Assert(gotTags, check.DeepEquals, tags) } diff --git a/integration/general_test.go b/integration/general_test.go index d63b83b3..085691fb 100644 --- a/integration/general_test.go +++ b/integration/general_test.go @@ -107,110 +107,133 @@ func TestAuthKeyLogoutAndRelogin(t *testing.T) { IntegrationSkip(t) t.Parallel() - scenario, err := NewScenario(dockertestMaxWait()) - assertNoErr(t, err) - defer scenario.ShutdownAssertNoPanics(t) + for _, https := range []bool{true, false} { + t.Run(fmt.Sprintf("with-https-%t", https), func(t *testing.T) { + scenario, err := NewScenario(dockertestMaxWait()) + assertNoErr(t, err) + defer scenario.ShutdownAssertNoPanics(t) - spec := map[string]int{ - "user1": len(MustTestVersions), - "user2": len(MustTestVersions), - } + spec := map[string]int{ + "user1": len(MustTestVersions), + "user2": len(MustTestVersions), + } - err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, hsic.WithTestName("pingallbyip")) - assertNoErrHeadscaleEnv(t, err) - - allClients, err := scenario.ListTailscaleClients() - assertNoErrListClients(t, err) - - err = scenario.WaitForTailscaleSync() - assertNoErrSync(t, err) - - // assertClientsState(t, allClients) - - clientIPs := make(map[TailscaleClient][]netip.Addr) - for _, client := range allClients { - ips, err := client.IPs() - if err != nil { - t.Fatalf("failed to get IPs for client %s: %s", client.Hostname(), err) - } - clientIPs[client] = ips - } - - for _, client := range allClients { - err := client.Logout() - if err != nil { - t.Fatalf("failed to logout client %s: %s", client.Hostname(), err) - } - } - - err = scenario.WaitForTailscaleLogout() - assertNoErrLogout(t, err) - - t.Logf("all clients logged out") - - headscale, err := scenario.Headscale() - assertNoErrGetHeadscale(t, err) - - for userName := range spec { - key, err := scenario.CreatePreAuthKey(userName, true, false) - if err != nil { - t.Fatalf("failed to create pre-auth key for user %s: %s", userName, err) - } - - err = scenario.RunTailscaleUp(userName, headscale.GetEndpoint(), key.GetKey()) - if err != nil { - t.Fatalf("failed to run tailscale up for user %s: %s", userName, err) - } - } - - err = scenario.WaitForTailscaleSync() - assertNoErrSync(t, err) - - // assertClientsState(t, allClients) - - allClients, err = scenario.ListTailscaleClients() - assertNoErrListClients(t, err) - - allIps, err := scenario.ListTailscaleClientsIPs() - assertNoErrListClientIPs(t, err) - - allAddrs := lo.Map(allIps, func(x netip.Addr, index int) string { - return x.String() - }) - - success := pingAllHelper(t, allClients, allAddrs) - t.Logf("%d successful pings out of %d", success, len(allClients)*len(allIps)) - - for _, client := range allClients { - ips, err := client.IPs() - if err != nil { - t.Fatalf("failed to get IPs for client %s: %s", client.Hostname(), err) - } - - // lets check if the IPs are the same - if len(ips) != len(clientIPs[client]) { - t.Fatalf("IPs changed for client %s", client.Hostname()) - } - - for _, ip := range ips { - found := false - for _, oldIP := range clientIPs[client] { - if ip == oldIP { - found = true - - break + opts := []hsic.Option{hsic.WithTestName("pingallbyip")} + if https { + opts = []hsic.Option{ + hsic.WithTestName("pingallbyip"), + hsic.WithEmbeddedDERPServerOnly(), + hsic.WithTLS(), + hsic.WithHostnameAsServerURL(), } } - if !found { - t.Fatalf( - "IPs changed for client %s. Used to be %v now %v", - client.Hostname(), - clientIPs[client], - ips, - ) + err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, opts...) + assertNoErrHeadscaleEnv(t, err) + + allClients, err := scenario.ListTailscaleClients() + assertNoErrListClients(t, err) + + err = scenario.WaitForTailscaleSync() + assertNoErrSync(t, err) + + // assertClientsState(t, allClients) + + clientIPs := make(map[TailscaleClient][]netip.Addr) + for _, client := range allClients { + ips, err := client.IPs() + if err != nil { + t.Fatalf("failed to get IPs for client %s: %s", client.Hostname(), err) + } + clientIPs[client] = ips } - } + + for _, client := range allClients { + err := client.Logout() + if err != nil { + t.Fatalf("failed to logout client %s: %s", client.Hostname(), err) + } + } + + err = scenario.WaitForTailscaleLogout() + assertNoErrLogout(t, err) + + t.Logf("all clients logged out") + + headscale, err := scenario.Headscale() + assertNoErrGetHeadscale(t, err) + + // if the server is not running with HTTPS, we have to wait a bit before + // reconnection as the newest Tailscale client has a measure that will only + // reconnect over HTTPS if they saw a noise connection previously. + // https://github.com/tailscale/tailscale/commit/1eaad7d3deb0815e8932e913ca1a862afa34db38 + // https://github.com/juanfont/headscale/issues/2164 + if !https { + time.Sleep(3 * time.Minute) + } + + for userName := range spec { + key, err := scenario.CreatePreAuthKey(userName, true, false) + if err != nil { + t.Fatalf("failed to create pre-auth key for user %s: %s", userName, err) + } + + err = scenario.RunTailscaleUp(userName, headscale.GetEndpoint(), key.GetKey()) + if err != nil { + t.Fatalf("failed to run tailscale up for user %s: %s", userName, err) + } + } + + err = scenario.WaitForTailscaleSync() + assertNoErrSync(t, err) + + // assertClientsState(t, allClients) + + allClients, err = scenario.ListTailscaleClients() + assertNoErrListClients(t, err) + + allIps, err := scenario.ListTailscaleClientsIPs() + assertNoErrListClientIPs(t, err) + + allAddrs := lo.Map(allIps, func(x netip.Addr, index int) string { + return x.String() + }) + + success := pingAllHelper(t, allClients, allAddrs) + t.Logf("%d successful pings out of %d", success, len(allClients)*len(allIps)) + + for _, client := range allClients { + ips, err := client.IPs() + if err != nil { + t.Fatalf("failed to get IPs for client %s: %s", client.Hostname(), err) + } + + // lets check if the IPs are the same + if len(ips) != len(clientIPs[client]) { + t.Fatalf("IPs changed for client %s", client.Hostname()) + } + + for _, ip := range ips { + found := false + for _, oldIP := range clientIPs[client] { + if ip == oldIP { + found = true + + break + } + } + + if !found { + t.Fatalf( + "IPs changed for client %s. Used to be %v now %v", + client.Hostname(), + clientIPs[client], + ips, + ) + } + } + } + }) } }