1
0
mirror of https://github.com/juanfont/headscale.git synced 2025-06-19 01:18:37 +02:00
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
This commit is contained in:
Kristoffer Dalby 2025-03-11 18:08:47 +01:00
parent 9968e5dcc9
commit 8a51bd3c64
No known key found for this signature in database
9 changed files with 48 additions and 34 deletions

View File

@ -71,7 +71,6 @@ jobs:
- TestSubnetRouteACL - TestSubnetRouteACL
- TestEnablingExitRoutes - TestEnablingExitRoutes
- TestHeadscale - TestHeadscale
- TestCreateTailscale
- TestTailscaleNodesJoiningHeadcale - TestTailscaleNodesJoiningHeadcale
- TestSSHOneUserToAll - TestSSHOneUserToAll
- TestSSHMultipleUsersAllToAll - TestSSHMultipleUsersAllToAll

View File

@ -71,7 +71,6 @@ jobs:
- TestSubnetRouteACL - TestSubnetRouteACL
- TestEnablingExitRoutes - TestEnablingExitRoutes
- TestHeadscale - TestHeadscale
- TestCreateTailscale
- TestTailscaleNodesJoiningHeadcale - TestTailscaleNodesJoiningHeadcale
- TestSSHOneUserToAll - TestSSHOneUserToAll
- TestSSHMultipleUsersAllToAll - TestSSHMultipleUsersAllToAll

View File

@ -931,6 +931,7 @@ func TestACLDevice1CanAccessDevice2(t *testing.T) {
for name, testCase := range tests { for name, testCase := range tests {
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
scenario := aclScenario(t, &testCase.policy, 1) scenario := aclScenario(t, &testCase.policy, 1)
defer scenario.ShutdownAssertNoPanics(t)
test1ip := netip.MustParseAddr("100.64.0.1") test1ip := netip.MustParseAddr("100.64.0.1")
test1ip6 := netip.MustParseAddr("fd7a:115c:a1e0::1") test1ip6 := netip.MustParseAddr("fd7a:115c:a1e0::1")

View File

@ -7,6 +7,8 @@ import (
"testing" "testing"
"time" "time"
"maps"
"github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts" "github.com/google/go-cmp/cmp/cmpopts"
v1 "github.com/juanfont/headscale/gen/go/headscale/v1" v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
@ -128,6 +130,7 @@ func TestOIDCExpireNodesBasedOnTokenExpiry(t *testing.T) {
oidcMockUser("user1", true), oidcMockUser("user1", true),
oidcMockUser("user2", false), oidcMockUser("user2", false),
}, },
OIDCAccessTTL: shortAccessTTL,
} }
scenario, err := NewScenario(spec) scenario, err := NewScenario(spec)
@ -295,24 +298,21 @@ func TestOIDC024UserCreation(t *testing.T) {
spec.Users = append(spec.Users, user) spec.Users = append(spec.Users, user)
} }
scenario, err := NewScenario(spec)
assertNoErr(t, err)
defer scenario.ShutdownAssertNoPanics(t)
for _, user := range tt.oidcUsers { for _, user := range tt.oidcUsers {
spec.OIDCUsers = append(spec.OIDCUsers, oidcMockUser(user, tt.emailVerified)) spec.OIDCUsers = append(spec.OIDCUsers, oidcMockUser(user, tt.emailVerified))
} }
scenario, err := NewScenario(spec)
assertNoErr(t, err)
defer scenario.ShutdownAssertNoPanics(t)
oidcMap := map[string]string{ oidcMap := map[string]string{
"HEADSCALE_OIDC_ISSUER": scenario.mockOIDC.Issuer(), "HEADSCALE_OIDC_ISSUER": scenario.mockOIDC.Issuer(),
"HEADSCALE_OIDC_CLIENT_ID": scenario.mockOIDC.ClientID(), "HEADSCALE_OIDC_CLIENT_ID": scenario.mockOIDC.ClientID(),
"CREDENTIALS_DIRECTORY_TEST": "/tmp", "CREDENTIALS_DIRECTORY_TEST": "/tmp",
"HEADSCALE_OIDC_CLIENT_SECRET_PATH": "${CREDENTIALS_DIRECTORY_TEST}/hs_client_oidc_secret", "HEADSCALE_OIDC_CLIENT_SECRET_PATH": "${CREDENTIALS_DIRECTORY_TEST}/hs_client_oidc_secret",
} }
maps.Copy(oidcMap, tt.config)
for k, v := range tt.config {
oidcMap[k] = v
}
err = scenario.CreateHeadscaleEnvWithLoginURL( err = scenario.CreateHeadscaleEnvWithLoginURL(
nil, nil,

View File

@ -528,7 +528,8 @@ func TestPreAuthKeyCorrectUserLoggedInCommand(t *testing.T) {
user2 := "user2" user2 := "user2"
spec := ScenarioSpec{ spec := ScenarioSpec{
Users: []string{user1, user2}, NodesPerUser: 1,
Users: []string{user1},
} }
scenario, err := NewScenario(spec) scenario, err := NewScenario(spec)
@ -546,6 +547,9 @@ func TestPreAuthKeyCorrectUserLoggedInCommand(t *testing.T) {
headscale, err := scenario.Headscale() headscale, err := scenario.Headscale()
assertNoErr(t, err) assertNoErr(t, err)
err = headscale.CreateUser(user2)
assertNoErr(t, err)
var user2Key v1.PreAuthKey var user2Key v1.PreAuthKey
err = executeAndUnmarshal( err = executeAndUnmarshal(
@ -568,10 +572,15 @@ func TestPreAuthKeyCorrectUserLoggedInCommand(t *testing.T) {
) )
assertNoErr(t, err) assertNoErr(t, err)
listNodes, err := headscale.ListNodes()
require.Nil(t, err)
require.Len(t, listNodes, 1)
assert.Equal(t, user1, listNodes[0].GetUser().GetName())
allClients, err := scenario.ListTailscaleClients() allClients, err := scenario.ListTailscaleClients()
assertNoErrListClients(t, err) assertNoErrListClients(t, err)
assert.Len(t, allClients, 1) require.Len(t, allClients, 1)
client := allClients[0] client := allClients[0]
@ -601,12 +610,11 @@ func TestPreAuthKeyCorrectUserLoggedInCommand(t *testing.T) {
t.Fatalf("expected node to be logged in as userid:2, got: %s", status.Self.UserID.String()) t.Fatalf("expected node to be logged in as userid:2, got: %s", status.Self.UserID.String())
} }
listNodes, err := headscale.ListNodes() listNodes, err = headscale.ListNodes()
assert.Nil(t, err) require.Nil(t, err)
assert.Len(t, listNodes, 2) require.Len(t, listNodes, 2)
assert.Equal(t, user1, listNodes[0].GetUser().GetName())
assert.Equal(t, "user1", listNodes[0].GetUser().GetName()) assert.Equal(t, user2, listNodes[1].GetUser().GetName())
assert.Equal(t, "user2", listNodes[1].GetUser().GetName())
} }
func TestApiKeyCommand(t *testing.T) { func TestApiKeyCommand(t *testing.T) {

View File

@ -138,7 +138,7 @@ func testEphemeralWithOptions(t *testing.T, opts ...hsic.Option) {
t.Fatalf("failed to create user %s: %s", userName, err) t.Fatalf("failed to create user %s: %s", userName, err)
} }
err = scenario.CreateTailscaleNodesInUser(userName, "all", spec.NodesPerUser, []tsic.Option{}...) err = scenario.CreateTailscaleNodesInUser(userName, "all", spec.NodesPerUser, tsic.WithNetwork(scenario.networks[TestDefaultNetwork]))
if err != nil { if err != nil {
t.Fatalf("failed to create tailscale nodes in user %s: %s", userName, err) t.Fatalf("failed to create tailscale nodes in user %s: %s", userName, err)
} }
@ -216,7 +216,7 @@ func TestEphemeral2006DeletedTooQuickly(t *testing.T) {
t.Fatalf("failed to create user %s: %s", userName, err) t.Fatalf("failed to create user %s: %s", userName, err)
} }
err = scenario.CreateTailscaleNodesInUser(userName, "all", spec.NodesPerUser, []tsic.Option{}...) err = scenario.CreateTailscaleNodesInUser(userName, "all", spec.NodesPerUser, tsic.WithNetwork(scenario.networks[TestDefaultNetwork]))
if err != nil { if err != nil {
t.Fatalf("failed to create tailscale nodes in user %s: %s", userName, err) t.Fatalf("failed to create tailscale nodes in user %s: %s", userName, err)
} }

View File

@ -130,7 +130,13 @@ type ScenarioSpec struct {
// OIDCUsers, if populated, will start a Mock OIDC server and populate // OIDCUsers, if populated, will start a Mock OIDC server and populate
// the user login stack with the given users. // the user login stack with the given users.
OIDCUsers []mockoidc.MockUser // If the NodesPerUser is set, it should align with this list to ensure
// the correct users are logged in.
// This is because the MockOIDC server can only serve login
// requests based on a queue it has been given on startup.
// We currently only populates it with one login request per user.
OIDCUsers []mockoidc.MockUser
OIDCAccessTTL time.Duration
MaxWait time.Duration MaxWait time.Duration
} }
@ -186,7 +192,11 @@ func NewScenario(spec ScenarioSpec) (*Scenario, error) {
s.userToNetwork = userToNetwork s.userToNetwork = userToNetwork
if spec.OIDCUsers != nil && len(spec.OIDCUsers) != 0 { if spec.OIDCUsers != nil && len(spec.OIDCUsers) != 0 {
err = s.runMockOIDC(defaultAccessTTL, spec.OIDCUsers) ttl := defaultAccessTTL
if spec.OIDCAccessTTL != 0 {
ttl = spec.OIDCAccessTTL
}
err = s.runMockOIDC(ttl, spec.OIDCUsers)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -436,7 +446,7 @@ func (s *Scenario) CreateTailscaleNodesInUser(
) error { ) error {
if user, ok := s.users[userStr]; ok { if user, ok := s.users[userStr]; ok {
var versions []string var versions []string
for i := 0; i < count; i++ { for i := range count {
version := requestedVersion version := requestedVersion
if requestedVersion == "all" { if requestedVersion == "all" {
version = MustTestVersions[i%len(MustTestVersions)] version = MustTestVersions[i%len(MustTestVersions)]
@ -468,8 +478,7 @@ func (s *Scenario) CreateTailscaleNodesInUser(
s.mu.Unlock() s.mu.Unlock()
if err != nil { if err != nil {
return fmt.Errorf( return fmt.Errorf(
"failed to create tailscale (%s) node: %w", "failed to create tailscale node: %w",
tsClient.Hostname(),
err, err,
) )
} }
@ -608,14 +617,6 @@ func (s *Scenario) createHeadscaleEnv(
return err return err
} }
if s.spec.OIDCUsers != nil && s.spec.NodesPerUser != 1 {
// OIDC scenario only supports one client per user.
// This is because the MockOIDC server can only serve login
// requests based on a queue it has been given on startup.
// We currently only populates it with one login request per user.
return fmt.Errorf("client count must be 1 for OIDC scenario.")
}
sort.Strings(s.spec.Users) sort.Strings(s.spec.Users)
for _, user := range s.spec.Users { for _, user := range s.spec.Users {
err = s.CreateUser(user) err = s.CreateUser(user)

View File

@ -4,6 +4,7 @@ import (
"testing" "testing"
"github.com/juanfont/headscale/integration/dockertestutil" "github.com/juanfont/headscale/integration/dockertestutil"
"github.com/juanfont/headscale/integration/tsic"
) )
// This file is intended to "test the test framework", by proxy it will also test // This file is intended to "test the test framework", by proxy it will also test
@ -110,7 +111,7 @@ func TestTailscaleNodesJoiningHeadcale(t *testing.T) {
}) })
t.Run("create-tailscale", func(t *testing.T) { t.Run("create-tailscale", func(t *testing.T) {
err := scenario.CreateTailscaleNodesInUser(user, "unstable", count) err := scenario.CreateTailscaleNodesInUser(user, "unstable", count, tsic.WithNetwork(scenario.networks[TestDefaultNetwork]))
if err != nil { if err != nil {
t.Fatalf("failed to add tailscale nodes: %s", err) t.Fatalf("failed to add tailscale nodes: %s", err)
} }

View File

@ -13,6 +13,7 @@ import (
"net/url" "net/url"
"os" "os"
"reflect" "reflect"
"runtime/debug"
"strconv" "strconv"
"strings" "strings"
"time" "time"
@ -226,6 +227,10 @@ func New(
opt(tsic) opt(tsic)
} }
if tsic.network == nil {
return nil, fmt.Errorf("no network set, called from: \n%s", string(debug.Stack()))
}
tailscaleOptions := &dockertest.RunOptions{ tailscaleOptions := &dockertest.RunOptions{
Name: hostname, Name: hostname,
Networks: []*dockertest.Network{tsic.network}, Networks: []*dockertest.Network{tsic.network},