mirror of
https://github.com/juanfont/headscale.git
synced 2025-02-08 10:18:01 +09:00
231 lines
6.3 KiB
Go
231 lines
6.3 KiB
Go
|
package integration
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"net/netip"
|
||
|
"testing"
|
||
|
"time"
|
||
|
|
||
|
"github.com/juanfont/headscale/integration/hsic"
|
||
|
"github.com/juanfont/headscale/integration/tsic"
|
||
|
"github.com/samber/lo"
|
||
|
"github.com/stretchr/testify/assert"
|
||
|
"github.com/stretchr/testify/require"
|
||
|
)
|
||
|
|
||
|
func TestAuthKeyLogoutAndReloginSameUser(t *testing.T) {
|
||
|
IntegrationSkip(t)
|
||
|
t.Parallel()
|
||
|
|
||
|
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),
|
||
|
}
|
||
|
|
||
|
opts := []hsic.Option{hsic.WithTestName("pingallbyip")}
|
||
|
if https {
|
||
|
opts = append(opts, []hsic.Option{
|
||
|
hsic.WithTLS(),
|
||
|
}...)
|
||
|
}
|
||
|
|
||
|
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
|
||
|
}
|
||
|
|
||
|
headscale, err := scenario.Headscale()
|
||
|
assertNoErrGetHeadscale(t, err)
|
||
|
|
||
|
listNodes, err := headscale.ListNodes()
|
||
|
assert.Equal(t, len(listNodes), len(allClients))
|
||
|
nodeCountBeforeLogout := len(listNodes)
|
||
|
t.Logf("node count before logout: %d", nodeCountBeforeLogout)
|
||
|
|
||
|
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")
|
||
|
|
||
|
// 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(5 * 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)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
listNodes, err = headscale.ListNodes()
|
||
|
require.Equal(t, nodeCountBeforeLogout, len(listNodes))
|
||
|
|
||
|
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,
|
||
|
)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// This test will first log in two sets of nodes to two sets of users, then
|
||
|
// it will log out all users from user2 and log them in as user1.
|
||
|
// This should leave us with all nodes connected to user1, while user2
|
||
|
// still has nodes, but they are not connected.
|
||
|
func TestAuthKeyLogoutAndReloginNewUser(t *testing.T) {
|
||
|
IntegrationSkip(t)
|
||
|
t.Parallel()
|
||
|
|
||
|
scenario, err := NewScenario(dockertestMaxWait())
|
||
|
assertNoErr(t, err)
|
||
|
defer scenario.ShutdownAssertNoPanics(t)
|
||
|
|
||
|
spec := map[string]int{
|
||
|
"user1": len(MustTestVersions),
|
||
|
"user2": len(MustTestVersions),
|
||
|
}
|
||
|
|
||
|
err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{},
|
||
|
hsic.WithTestName("keyrelognewuser"),
|
||
|
hsic.WithTLS(),
|
||
|
)
|
||
|
assertNoErrHeadscaleEnv(t, err)
|
||
|
|
||
|
allClients, err := scenario.ListTailscaleClients()
|
||
|
assertNoErrListClients(t, err)
|
||
|
|
||
|
err = scenario.WaitForTailscaleSync()
|
||
|
assertNoErrSync(t, err)
|
||
|
|
||
|
// assertClientsState(t, allClients)
|
||
|
|
||
|
headscale, err := scenario.Headscale()
|
||
|
assertNoErrGetHeadscale(t, err)
|
||
|
|
||
|
listNodes, err := headscale.ListNodes()
|
||
|
assert.Equal(t, len(listNodes), len(allClients))
|
||
|
nodeCountBeforeLogout := len(listNodes)
|
||
|
t.Logf("node count before logout: %d", nodeCountBeforeLogout)
|
||
|
|
||
|
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")
|
||
|
|
||
|
// Create a new authkey for user1, to be used for all clients
|
||
|
key, err := scenario.CreatePreAuthKey("user1", true, false)
|
||
|
if err != nil {
|
||
|
t.Fatalf("failed to create pre-auth key for user1: %s", err)
|
||
|
}
|
||
|
|
||
|
// Log in all clients as user1, iterating over the spec only returns the
|
||
|
// clients, not the usernames.
|
||
|
for userName := range spec {
|
||
|
err = scenario.RunTailscaleUp(userName, headscale.GetEndpoint(), key.GetKey())
|
||
|
if err != nil {
|
||
|
t.Fatalf("failed to run tailscale up for user %s: %s", userName, err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
user1Nodes, err := headscale.ListNodes("user1")
|
||
|
assertNoErr(t, err)
|
||
|
assert.Len(t, user1Nodes, len(allClients))
|
||
|
|
||
|
// Validate that all the old nodes are still present with user2
|
||
|
user2Nodes, err := headscale.ListNodes("user2")
|
||
|
assertNoErr(t, err)
|
||
|
assert.Len(t, user2Nodes, len(allClients)/2)
|
||
|
|
||
|
for _, client := range allClients {
|
||
|
status, err := client.Status()
|
||
|
if err != nil {
|
||
|
t.Fatalf("failed to get status for client %s: %s", client.Hostname(), err)
|
||
|
}
|
||
|
|
||
|
assert.Equal(t, "user1@test.no", status.User[status.Self.UserID].LoginName)
|
||
|
}
|
||
|
}
|