use dedicated registration ID for auth flow (#2337)
This commit is contained in:
parent
97e5d95399
commit
4c8e847f47
26 changed files with 586 additions and 586 deletions
|
@ -11,8 +11,8 @@ import (
|
|||
"net"
|
||||
"net/http"
|
||||
"net/http/cookiejar"
|
||||
"net/http/httptest"
|
||||
"net/netip"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
@ -56,7 +56,7 @@ func TestOIDCAuthenticationPingAll(t *testing.T) {
|
|||
scenario := AuthOIDCScenario{
|
||||
Scenario: baseScenario,
|
||||
}
|
||||
// defer scenario.ShutdownAssertNoPanics(t)
|
||||
defer scenario.ShutdownAssertNoPanics(t)
|
||||
|
||||
// Logins to MockOIDC is served by a queue with a strict order,
|
||||
// if we use more than one node per user, the order of the logins
|
||||
|
@ -91,7 +91,6 @@ func TestOIDCAuthenticationPingAll(t *testing.T) {
|
|||
hsic.WithTestName("oidcauthping"),
|
||||
hsic.WithConfigEnv(oidcMap),
|
||||
hsic.WithTLS(),
|
||||
hsic.WithHostnameAsServerURL(),
|
||||
hsic.WithFileInContainer("/tmp/hs_client_oidc_secret", []byte(oidcConfig.ClientSecret)),
|
||||
)
|
||||
assertNoErrHeadscaleEnv(t, err)
|
||||
|
@ -206,7 +205,6 @@ func TestOIDCExpireNodesBasedOnTokenExpiry(t *testing.T) {
|
|||
spec,
|
||||
hsic.WithTestName("oidcexpirenodes"),
|
||||
hsic.WithConfigEnv(oidcMap),
|
||||
hsic.WithHostnameAsServerURL(),
|
||||
)
|
||||
assertNoErrHeadscaleEnv(t, err)
|
||||
|
||||
|
@ -497,7 +495,6 @@ func TestOIDC024UserCreation(t *testing.T) {
|
|||
hsic.WithTestName("oidcmigration"),
|
||||
hsic.WithConfigEnv(oidcMap),
|
||||
hsic.WithTLS(),
|
||||
hsic.WithHostnameAsServerURL(),
|
||||
hsic.WithFileInContainer("/tmp/hs_client_oidc_secret", []byte(oidcConfig.ClientSecret)),
|
||||
)
|
||||
assertNoErrHeadscaleEnv(t, err)
|
||||
|
@ -576,7 +573,6 @@ func TestOIDCAuthenticationWithPKCE(t *testing.T) {
|
|||
hsic.WithTestName("oidcauthpkce"),
|
||||
hsic.WithConfigEnv(oidcMap),
|
||||
hsic.WithTLS(),
|
||||
hsic.WithHostnameAsServerURL(),
|
||||
hsic.WithFileInContainer("/tmp/hs_client_oidc_secret", []byte(oidcConfig.ClientSecret)),
|
||||
)
|
||||
assertNoErrHeadscaleEnv(t, err)
|
||||
|
@ -770,11 +766,6 @@ func (t LoggingRoundTripper) RoundTrip(req *http.Request) (*http.Response, error
|
|||
func (s *AuthOIDCScenario) runTailscaleUp(
|
||||
userStr, loginServer string,
|
||||
) error {
|
||||
headscale, err := s.Headscale()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("running tailscale up for user %s", userStr)
|
||||
if user, ok := s.users[userStr]; ok {
|
||||
for _, client := range user.Clients {
|
||||
|
@ -785,59 +776,11 @@ func (s *AuthOIDCScenario) runTailscaleUp(
|
|||
log.Printf("%s failed to run tailscale up: %s", tsc.Hostname(), err)
|
||||
}
|
||||
|
||||
loginURL.Host = fmt.Sprintf("%s:8080", headscale.GetHostname())
|
||||
loginURL.Scheme = "http"
|
||||
|
||||
if len(headscale.GetCert()) > 0 {
|
||||
loginURL.Scheme = "https"
|
||||
}
|
||||
|
||||
httptest.NewRecorder()
|
||||
hc := &http.Client{
|
||||
Transport: LoggingRoundTripper{},
|
||||
}
|
||||
hc.Jar, err = cookiejar.New(nil)
|
||||
_, err = doLoginURL(tsc.Hostname(), loginURL)
|
||||
if err != nil {
|
||||
log.Printf("failed to create cookie jar: %s", err)
|
||||
}
|
||||
|
||||
log.Printf("%s login url: %s\n", tsc.Hostname(), loginURL.String())
|
||||
|
||||
log.Printf("%s logging in with url", tsc.Hostname())
|
||||
ctx := context.Background()
|
||||
req, _ := http.NewRequestWithContext(ctx, http.MethodGet, loginURL.String(), nil)
|
||||
resp, err := hc.Do(req)
|
||||
if err != nil {
|
||||
log.Printf(
|
||||
"%s failed to login using url %s: %s",
|
||||
tsc.Hostname(),
|
||||
loginURL,
|
||||
err,
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("cookies: %+v", hc.Jar.Cookies(loginURL))
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
log.Printf("%s response code of oidc login request was %s", tsc.Hostname(), resp.Status)
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
log.Printf("body: %s", body)
|
||||
|
||||
return errStatusCodeNotOK
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
_, err = io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Printf("%s failed to read response body: %s", tsc.Hostname(), err)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("Finished request for %s to join tailnet", tsc.Hostname())
|
||||
return nil
|
||||
})
|
||||
|
||||
|
@ -865,6 +808,49 @@ func (s *AuthOIDCScenario) runTailscaleUp(
|
|||
return fmt.Errorf("failed to up tailscale node: %w", errNoUserAvailable)
|
||||
}
|
||||
|
||||
// doLoginURL visits the given login URL and returns the body as a
|
||||
// string.
|
||||
func doLoginURL(hostname string, loginURL *url.URL) (string, error) {
|
||||
log.Printf("%s login url: %s\n", hostname, loginURL.String())
|
||||
|
||||
var err error
|
||||
hc := &http.Client{
|
||||
Transport: LoggingRoundTripper{},
|
||||
}
|
||||
hc.Jar, err = cookiejar.New(nil)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("%s failed to create cookiejar : %w", hostname, err)
|
||||
}
|
||||
|
||||
log.Printf("%s logging in with url", hostname)
|
||||
ctx := context.Background()
|
||||
req, _ := http.NewRequestWithContext(ctx, http.MethodGet, loginURL.String(), nil)
|
||||
resp, err := hc.Do(req)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("%s failed to send http request: %w", hostname, err)
|
||||
}
|
||||
|
||||
log.Printf("cookies: %+v", hc.Jar.Cookies(loginURL))
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
log.Printf("body: %s", body)
|
||||
|
||||
return "", fmt.Errorf("%s response code of login request was %w", hostname, err)
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Printf("%s failed to read response body: %s", hostname, err)
|
||||
|
||||
return "", fmt.Errorf("%s failed to read response body: %w", hostname, err)
|
||||
}
|
||||
|
||||
return string(body), nil
|
||||
}
|
||||
|
||||
func (s *AuthOIDCScenario) Shutdown() {
|
||||
err := s.pool.Purge(s.mockOIDC)
|
||||
if err != nil {
|
||||
|
|
|
@ -1,13 +1,9 @@
|
|||
package integration
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/netip"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
@ -47,7 +43,6 @@ func TestAuthWebFlowAuthenticationPingAll(t *testing.T) {
|
|||
hsic.WithTestName("webauthping"),
|
||||
hsic.WithEmbeddedDERPServerOnly(),
|
||||
hsic.WithTLS(),
|
||||
hsic.WithHostnameAsServerURL(),
|
||||
)
|
||||
assertNoErrHeadscaleEnv(t, err)
|
||||
|
||||
|
@ -87,7 +82,10 @@ func TestAuthWebFlowLogoutAndRelogin(t *testing.T) {
|
|||
"user2": len(MustTestVersions),
|
||||
}
|
||||
|
||||
err = scenario.CreateHeadscaleEnv(spec, hsic.WithTestName("weblogout"))
|
||||
err = scenario.CreateHeadscaleEnv(spec,
|
||||
hsic.WithTestName("weblogout"),
|
||||
hsic.WithTLS(),
|
||||
)
|
||||
assertNoErrHeadscaleEnv(t, err)
|
||||
|
||||
allClients, err := scenario.ListTailscaleClients()
|
||||
|
@ -135,7 +133,7 @@ func TestAuthWebFlowLogoutAndRelogin(t *testing.T) {
|
|||
for userName := range spec {
|
||||
err = scenario.runTailscaleUp(userName, headscale.GetEndpoint())
|
||||
if err != nil {
|
||||
t.Fatalf("failed to run tailscale up: %s", err)
|
||||
t.Fatalf("failed to run tailscale up (%q): %s", headscale.GetEndpoint(), err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -227,11 +225,12 @@ func (s *AuthWebFlowScenario) CreateHeadscaleEnv(
|
|||
func (s *AuthWebFlowScenario) runTailscaleUp(
|
||||
userStr, loginServer string,
|
||||
) error {
|
||||
log.Printf("running tailscale up for user %s", userStr)
|
||||
log.Printf("running tailscale up for user %q", userStr)
|
||||
if user, ok := s.users[userStr]; ok {
|
||||
for _, client := range user.Clients {
|
||||
c := client
|
||||
user.joinWaitGroup.Go(func() error {
|
||||
log.Printf("logging %q into %q", c.Hostname(), loginServer)
|
||||
loginURL, err := c.LoginWithURL(loginServer)
|
||||
if err != nil {
|
||||
log.Printf("failed to run tailscale up (%s): %s", c.Hostname(), err)
|
||||
|
@ -273,39 +272,11 @@ func (s *AuthWebFlowScenario) runTailscaleUp(
|
|||
}
|
||||
|
||||
func (s *AuthWebFlowScenario) runHeadscaleRegister(userStr string, loginURL *url.URL) error {
|
||||
headscale, err := s.Headscale()
|
||||
body, err := doLoginURL("web-auth-not-set", loginURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("loginURL: %s", loginURL)
|
||||
loginURL.Host = fmt.Sprintf("%s:8080", headscale.GetIP())
|
||||
loginURL.Scheme = "http"
|
||||
|
||||
if len(headscale.GetCert()) > 0 {
|
||||
loginURL.Scheme = "https"
|
||||
}
|
||||
|
||||
insecureTransport := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, // nolint
|
||||
}
|
||||
httpClient := &http.Client{
|
||||
Transport: insecureTransport,
|
||||
}
|
||||
ctx := context.Background()
|
||||
req, _ := http.NewRequestWithContext(ctx, http.MethodGet, loginURL.String(), nil)
|
||||
resp, err := httpClient.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
// see api.go HTML template
|
||||
codeSep := strings.Split(string(body), "</code>")
|
||||
if len(codeSep) != 2 {
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
|
||||
"github.com/juanfont/headscale/hscontrol/policy"
|
||||
"github.com/juanfont/headscale/hscontrol/types"
|
||||
"github.com/juanfont/headscale/integration/hsic"
|
||||
"github.com/juanfont/headscale/integration/tsic"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
@ -544,7 +545,6 @@ func TestPreAuthKeyCorrectUserLoggedInCommand(t *testing.T) {
|
|||
hsic.WithTestName("clipak"),
|
||||
hsic.WithEmbeddedDERPServerOnly(),
|
||||
hsic.WithTLS(),
|
||||
hsic.WithHostnameAsServerURL(),
|
||||
)
|
||||
assertNoErr(t, err)
|
||||
|
||||
|
@ -812,14 +812,14 @@ func TestNodeTagCommand(t *testing.T) {
|
|||
headscale, err := scenario.Headscale()
|
||||
assertNoErr(t, err)
|
||||
|
||||
machineKeys := []string{
|
||||
"mkey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe",
|
||||
"mkey:6abd00bb5fdda622db51387088c68e97e71ce58e7056aa54f592b6a8219d524c",
|
||||
regIDs := []string{
|
||||
types.MustRegistrationID().String(),
|
||||
types.MustRegistrationID().String(),
|
||||
}
|
||||
nodes := make([]*v1.Node, len(machineKeys))
|
||||
nodes := make([]*v1.Node, len(regIDs))
|
||||
assert.Nil(t, err)
|
||||
|
||||
for index, machineKey := range machineKeys {
|
||||
for index, regID := range regIDs {
|
||||
_, err := headscale.Execute(
|
||||
[]string{
|
||||
"headscale",
|
||||
|
@ -830,7 +830,7 @@ func TestNodeTagCommand(t *testing.T) {
|
|||
"--user",
|
||||
"user1",
|
||||
"--key",
|
||||
machineKey,
|
||||
regID,
|
||||
"--output",
|
||||
"json",
|
||||
},
|
||||
|
@ -847,7 +847,7 @@ func TestNodeTagCommand(t *testing.T) {
|
|||
"user1",
|
||||
"register",
|
||||
"--key",
|
||||
machineKey,
|
||||
regID,
|
||||
"--output",
|
||||
"json",
|
||||
},
|
||||
|
@ -857,7 +857,7 @@ func TestNodeTagCommand(t *testing.T) {
|
|||
|
||||
nodes[index] = &node
|
||||
}
|
||||
assert.Len(t, nodes, len(machineKeys))
|
||||
assert.Len(t, nodes, len(regIDs))
|
||||
|
||||
var node v1.Node
|
||||
err = executeAndUnmarshal(
|
||||
|
@ -889,7 +889,7 @@ func TestNodeTagCommand(t *testing.T) {
|
|||
assert.ErrorContains(t, err, "tag must start with the string 'tag:'")
|
||||
|
||||
// Test list all nodes after added seconds
|
||||
resultMachines := make([]*v1.Node, len(machineKeys))
|
||||
resultMachines := make([]*v1.Node, len(regIDs))
|
||||
err = executeAndUnmarshal(
|
||||
headscale,
|
||||
[]string{
|
||||
|
@ -1054,18 +1054,17 @@ func TestNodeCommand(t *testing.T) {
|
|||
headscale, err := scenario.Headscale()
|
||||
assertNoErr(t, err)
|
||||
|
||||
// Pregenerated machine keys
|
||||
machineKeys := []string{
|
||||
"mkey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe",
|
||||
"mkey:6abd00bb5fdda622db51387088c68e97e71ce58e7056aa54f592b6a8219d524c",
|
||||
"mkey:f08305b4ee4250b95a70f3b7504d048d75d899993c624a26d422c67af0422507",
|
||||
"mkey:8bc13285cee598acf76b1824a6f4490f7f2e3751b201e28aeb3b07fe81d5b4a1",
|
||||
"mkey:cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084",
|
||||
regIDs := []string{
|
||||
types.MustRegistrationID().String(),
|
||||
types.MustRegistrationID().String(),
|
||||
types.MustRegistrationID().String(),
|
||||
types.MustRegistrationID().String(),
|
||||
types.MustRegistrationID().String(),
|
||||
}
|
||||
nodes := make([]*v1.Node, len(machineKeys))
|
||||
nodes := make([]*v1.Node, len(regIDs))
|
||||
assert.Nil(t, err)
|
||||
|
||||
for index, machineKey := range machineKeys {
|
||||
for index, regID := range regIDs {
|
||||
_, err := headscale.Execute(
|
||||
[]string{
|
||||
"headscale",
|
||||
|
@ -1076,7 +1075,7 @@ func TestNodeCommand(t *testing.T) {
|
|||
"--user",
|
||||
"node-user",
|
||||
"--key",
|
||||
machineKey,
|
||||
regID,
|
||||
"--output",
|
||||
"json",
|
||||
},
|
||||
|
@ -1093,7 +1092,7 @@ func TestNodeCommand(t *testing.T) {
|
|||
"node-user",
|
||||
"register",
|
||||
"--key",
|
||||
machineKey,
|
||||
regID,
|
||||
"--output",
|
||||
"json",
|
||||
},
|
||||
|
@ -1104,7 +1103,7 @@ func TestNodeCommand(t *testing.T) {
|
|||
nodes[index] = &node
|
||||
}
|
||||
|
||||
assert.Len(t, nodes, len(machineKeys))
|
||||
assert.Len(t, nodes, len(regIDs))
|
||||
|
||||
// Test list all nodes after added seconds
|
||||
var listAll []v1.Node
|
||||
|
@ -1135,14 +1134,14 @@ func TestNodeCommand(t *testing.T) {
|
|||
assert.Equal(t, "node-4", listAll[3].GetName())
|
||||
assert.Equal(t, "node-5", listAll[4].GetName())
|
||||
|
||||
otherUserMachineKeys := []string{
|
||||
"mkey:b5b444774186d4217adcec407563a1223929465ee2c68a4da13af0d0185b4f8e",
|
||||
"mkey:dc721977ac7415aafa87f7d4574cbe07c6b171834a6d37375782bdc1fb6b3584",
|
||||
otherUserRegIDs := []string{
|
||||
types.MustRegistrationID().String(),
|
||||
types.MustRegistrationID().String(),
|
||||
}
|
||||
otherUserMachines := make([]*v1.Node, len(otherUserMachineKeys))
|
||||
otherUserMachines := make([]*v1.Node, len(otherUserRegIDs))
|
||||
assert.Nil(t, err)
|
||||
|
||||
for index, machineKey := range otherUserMachineKeys {
|
||||
for index, regID := range otherUserRegIDs {
|
||||
_, err := headscale.Execute(
|
||||
[]string{
|
||||
"headscale",
|
||||
|
@ -1153,7 +1152,7 @@ func TestNodeCommand(t *testing.T) {
|
|||
"--user",
|
||||
"other-user",
|
||||
"--key",
|
||||
machineKey,
|
||||
regID,
|
||||
"--output",
|
||||
"json",
|
||||
},
|
||||
|
@ -1170,7 +1169,7 @@ func TestNodeCommand(t *testing.T) {
|
|||
"other-user",
|
||||
"register",
|
||||
"--key",
|
||||
machineKey,
|
||||
regID,
|
||||
"--output",
|
||||
"json",
|
||||
},
|
||||
|
@ -1181,7 +1180,7 @@ func TestNodeCommand(t *testing.T) {
|
|||
otherUserMachines[index] = &node
|
||||
}
|
||||
|
||||
assert.Len(t, otherUserMachines, len(otherUserMachineKeys))
|
||||
assert.Len(t, otherUserMachines, len(otherUserRegIDs))
|
||||
|
||||
// Test list all nodes after added otherUser
|
||||
var listAllWithotherUser []v1.Node
|
||||
|
@ -1294,17 +1293,16 @@ func TestNodeExpireCommand(t *testing.T) {
|
|||
headscale, err := scenario.Headscale()
|
||||
assertNoErr(t, err)
|
||||
|
||||
// Pregenerated machine keys
|
||||
machineKeys := []string{
|
||||
"mkey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe",
|
||||
"mkey:6abd00bb5fdda622db51387088c68e97e71ce58e7056aa54f592b6a8219d524c",
|
||||
"mkey:f08305b4ee4250b95a70f3b7504d048d75d899993c624a26d422c67af0422507",
|
||||
"mkey:8bc13285cee598acf76b1824a6f4490f7f2e3751b201e28aeb3b07fe81d5b4a1",
|
||||
"mkey:cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084",
|
||||
regIDs := []string{
|
||||
types.MustRegistrationID().String(),
|
||||
types.MustRegistrationID().String(),
|
||||
types.MustRegistrationID().String(),
|
||||
types.MustRegistrationID().String(),
|
||||
types.MustRegistrationID().String(),
|
||||
}
|
||||
nodes := make([]*v1.Node, len(machineKeys))
|
||||
nodes := make([]*v1.Node, len(regIDs))
|
||||
|
||||
for index, machineKey := range machineKeys {
|
||||
for index, regID := range regIDs {
|
||||
_, err := headscale.Execute(
|
||||
[]string{
|
||||
"headscale",
|
||||
|
@ -1315,7 +1313,7 @@ func TestNodeExpireCommand(t *testing.T) {
|
|||
"--user",
|
||||
"node-expire-user",
|
||||
"--key",
|
||||
machineKey,
|
||||
regID,
|
||||
"--output",
|
||||
"json",
|
||||
},
|
||||
|
@ -1332,7 +1330,7 @@ func TestNodeExpireCommand(t *testing.T) {
|
|||
"node-expire-user",
|
||||
"register",
|
||||
"--key",
|
||||
machineKey,
|
||||
regID,
|
||||
"--output",
|
||||
"json",
|
||||
},
|
||||
|
@ -1343,7 +1341,7 @@ func TestNodeExpireCommand(t *testing.T) {
|
|||
nodes[index] = &node
|
||||
}
|
||||
|
||||
assert.Len(t, nodes, len(machineKeys))
|
||||
assert.Len(t, nodes, len(regIDs))
|
||||
|
||||
var listAll []v1.Node
|
||||
err = executeAndUnmarshal(
|
||||
|
@ -1421,18 +1419,17 @@ func TestNodeRenameCommand(t *testing.T) {
|
|||
headscale, err := scenario.Headscale()
|
||||
assertNoErr(t, err)
|
||||
|
||||
// Pregenerated machine keys
|
||||
machineKeys := []string{
|
||||
"mkey:cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084",
|
||||
"mkey:8bc13285cee598acf76b1824a6f4490f7f2e3751b201e28aeb3b07fe81d5b4a1",
|
||||
"mkey:f08305b4ee4250b95a70f3b7504d048d75d899993c624a26d422c67af0422507",
|
||||
"mkey:6abd00bb5fdda622db51387088c68e97e71ce58e7056aa54f592b6a8219d524c",
|
||||
"mkey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe",
|
||||
regIDs := []string{
|
||||
types.MustRegistrationID().String(),
|
||||
types.MustRegistrationID().String(),
|
||||
types.MustRegistrationID().String(),
|
||||
types.MustRegistrationID().String(),
|
||||
types.MustRegistrationID().String(),
|
||||
}
|
||||
nodes := make([]*v1.Node, len(machineKeys))
|
||||
nodes := make([]*v1.Node, len(regIDs))
|
||||
assert.Nil(t, err)
|
||||
|
||||
for index, machineKey := range machineKeys {
|
||||
for index, regID := range regIDs {
|
||||
_, err := headscale.Execute(
|
||||
[]string{
|
||||
"headscale",
|
||||
|
@ -1443,7 +1440,7 @@ func TestNodeRenameCommand(t *testing.T) {
|
|||
"--user",
|
||||
"node-rename-command",
|
||||
"--key",
|
||||
machineKey,
|
||||
regID,
|
||||
"--output",
|
||||
"json",
|
||||
},
|
||||
|
@ -1460,7 +1457,7 @@ func TestNodeRenameCommand(t *testing.T) {
|
|||
"node-rename-command",
|
||||
"register",
|
||||
"--key",
|
||||
machineKey,
|
||||
regID,
|
||||
"--output",
|
||||
"json",
|
||||
},
|
||||
|
@ -1471,7 +1468,7 @@ func TestNodeRenameCommand(t *testing.T) {
|
|||
nodes[index] = &node
|
||||
}
|
||||
|
||||
assert.Len(t, nodes, len(machineKeys))
|
||||
assert.Len(t, nodes, len(regIDs))
|
||||
|
||||
var listAll []v1.Node
|
||||
err = executeAndUnmarshal(
|
||||
|
@ -1589,7 +1586,7 @@ func TestNodeMoveCommand(t *testing.T) {
|
|||
assertNoErr(t, err)
|
||||
|
||||
// Randomly generated node key
|
||||
machineKey := "mkey:688411b767663479632d44140f08a9fde87383adc7cdeb518f62ce28a17ef0aa"
|
||||
regID := types.MustRegistrationID()
|
||||
|
||||
_, err = headscale.Execute(
|
||||
[]string{
|
||||
|
@ -1601,7 +1598,7 @@ func TestNodeMoveCommand(t *testing.T) {
|
|||
"--user",
|
||||
"old-user",
|
||||
"--key",
|
||||
machineKey,
|
||||
regID.String(),
|
||||
"--output",
|
||||
"json",
|
||||
},
|
||||
|
@ -1618,7 +1615,7 @@ func TestNodeMoveCommand(t *testing.T) {
|
|||
"old-user",
|
||||
"register",
|
||||
"--key",
|
||||
machineKey,
|
||||
regID.String(),
|
||||
"--output",
|
||||
"json",
|
||||
},
|
||||
|
|
|
@ -69,7 +69,6 @@ func TestDERPVerifyEndpoint(t *testing.T) {
|
|||
hsic.WithHostname(hostname),
|
||||
hsic.WithPort(headscalePort),
|
||||
hsic.WithCustomTLS(certHeadscale, keyHeadscale),
|
||||
hsic.WithHostnameAsServerURL(),
|
||||
hsic.WithDERPConfig(derpMap))
|
||||
assertNoErrHeadscaleEnv(t, err)
|
||||
|
||||
|
|
|
@ -123,7 +123,6 @@ func TestResolveMagicDNSExtraRecordsPath(t *testing.T) {
|
|||
hsic.WithFileInContainer(erPath, b),
|
||||
hsic.WithEmbeddedDERPServerOnly(),
|
||||
hsic.WithTLS(),
|
||||
hsic.WithHostnameAsServerURL(),
|
||||
)
|
||||
assertNoErrHeadscaleEnv(t, err)
|
||||
|
||||
|
|
|
@ -105,7 +105,6 @@ func derpServerScenario(
|
|||
hsic.WithEmbeddedDERPServerOnly(),
|
||||
hsic.WithPort(443),
|
||||
hsic.WithTLS(),
|
||||
hsic.WithHostnameAsServerURL(),
|
||||
hsic.WithConfigEnv(map[string]string{
|
||||
"HEADSCALE_DERP_AUTO_UPDATE_ENABLED": "true",
|
||||
"HEADSCALE_DERP_UPDATE_FREQUENCY": "10s",
|
||||
|
|
|
@ -44,7 +44,6 @@ func TestPingAllByIP(t *testing.T) {
|
|||
hsic.WithTestName("pingallbyip"),
|
||||
hsic.WithEmbeddedDERPServerOnly(),
|
||||
hsic.WithTLS(),
|
||||
hsic.WithHostnameAsServerURL(),
|
||||
hsic.WithIPAllocationStrategy(types.IPAllocationStrategyRandom),
|
||||
)
|
||||
assertNoErrHeadscaleEnv(t, err)
|
||||
|
@ -123,12 +122,9 @@ func TestAuthKeyLogoutAndRelogin(t *testing.T) {
|
|||
|
||||
opts := []hsic.Option{hsic.WithTestName("pingallbyip")}
|
||||
if https {
|
||||
opts = []hsic.Option{
|
||||
hsic.WithTestName("pingallbyip"),
|
||||
hsic.WithEmbeddedDERPServerOnly(),
|
||||
opts = append(opts, []hsic.Option{
|
||||
hsic.WithTLS(),
|
||||
hsic.WithHostnameAsServerURL(),
|
||||
}
|
||||
}...)
|
||||
}
|
||||
|
||||
err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, opts...)
|
||||
|
@ -172,7 +168,7 @@ func TestAuthKeyLogoutAndRelogin(t *testing.T) {
|
|||
// https://github.com/tailscale/tailscale/commit/1eaad7d3deb0815e8932e913ca1a862afa34db38
|
||||
// https://github.com/juanfont/headscale/issues/2164
|
||||
if !https {
|
||||
time.Sleep(3 * time.Minute)
|
||||
time.Sleep(5 * time.Minute)
|
||||
}
|
||||
|
||||
for userName := range spec {
|
||||
|
@ -1050,7 +1046,6 @@ func TestPingAllByIPManyUpDown(t *testing.T) {
|
|||
hsic.WithTestName("pingallbyipmany"),
|
||||
hsic.WithEmbeddedDERPServerOnly(),
|
||||
hsic.WithTLS(),
|
||||
hsic.WithHostnameAsServerURL(),
|
||||
)
|
||||
assertNoErrHeadscaleEnv(t, err)
|
||||
|
||||
|
@ -1133,7 +1128,6 @@ func Test2118DeletingOnlineNodePanics(t *testing.T) {
|
|||
hsic.WithTestName("deletenocrash"),
|
||||
hsic.WithEmbeddedDERPServerOnly(),
|
||||
hsic.WithTLS(),
|
||||
hsic.WithHostnameAsServerURL(),
|
||||
)
|
||||
assertNoErrHeadscaleEnv(t, err)
|
||||
|
||||
|
|
|
@ -26,9 +26,7 @@ func DefaultConfigEnv() map[string]string {
|
|||
"HEADSCALE_DNS_NAMESERVERS_GLOBAL": "127.0.0.11 1.1.1.1",
|
||||
"HEADSCALE_PRIVATE_KEY_PATH": "/tmp/private.key",
|
||||
"HEADSCALE_NOISE_PRIVATE_KEY_PATH": "/tmp/noise_private.key",
|
||||
"HEADSCALE_LISTEN_ADDR": "0.0.0.0:8080",
|
||||
"HEADSCALE_METRICS_LISTEN_ADDR": "0.0.0.0:9090",
|
||||
"HEADSCALE_SERVER_URL": "http://headscale:8080",
|
||||
"HEADSCALE_DERP_URLS": "https://controlplane.tailscale.com/derpmap/default",
|
||||
"HEADSCALE_DERP_AUTO_UPDATE_ENABLED": "false",
|
||||
"HEADSCALE_DERP_UPDATE_FREQUENCY": "1m",
|
||||
|
|
|
@ -7,9 +7,7 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
|
@ -166,17 +164,6 @@ func WithHostname(hostname string) Option {
|
|||
}
|
||||
}
|
||||
|
||||
// WithHostnameAsServerURL sets the Headscale ServerURL based on
|
||||
// the Hostname.
|
||||
func WithHostnameAsServerURL() Option {
|
||||
return func(hsic *HeadscaleInContainer) {
|
||||
hsic.env["HEADSCALE_SERVER_URL"] = fmt.Sprintf("http://%s",
|
||||
net.JoinHostPort(hsic.GetHostname(),
|
||||
fmt.Sprintf("%d", hsic.port)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// WithFileInContainer adds a file to the container at the given path.
|
||||
func WithFileInContainer(path string, contents []byte) Option {
|
||||
return func(hsic *HeadscaleInContainer) {
|
||||
|
@ -297,16 +284,6 @@ func New(
|
|||
|
||||
portProto := fmt.Sprintf("%d/tcp", hsic.port)
|
||||
|
||||
serverURL, err := url.Parse(hsic.env["HEADSCALE_SERVER_URL"])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(hsic.tlsCert) != 0 && len(hsic.tlsKey) != 0 {
|
||||
serverURL.Scheme = "https"
|
||||
hsic.env["HEADSCALE_SERVER_URL"] = serverURL.String()
|
||||
}
|
||||
|
||||
headscaleBuildOptions := &dockertest.BuildOptions{
|
||||
Dockerfile: IntegrationTestDockerFileName,
|
||||
ContextDir: dockerContextPath,
|
||||
|
@ -352,6 +329,12 @@ func New(
|
|||
hsic.env["HEADSCALE_TLS_CERT_PATH"] = tlsCertPath
|
||||
hsic.env["HEADSCALE_TLS_KEY_PATH"] = tlsKeyPath
|
||||
}
|
||||
|
||||
// Server URL and Listen Addr should not be overridable outside of
|
||||
// the configuration passed to docker.
|
||||
hsic.env["HEADSCALE_SERVER_URL"] = hsic.GetEndpoint()
|
||||
hsic.env["HEADSCALE_LISTEN_ADDR"] = fmt.Sprintf("0.0.0.0:%d", hsic.port)
|
||||
|
||||
for key, value := range hsic.env {
|
||||
env = append(env, fmt.Sprintf("%s=%s", key, value))
|
||||
}
|
||||
|
@ -649,7 +632,7 @@ func (t *HeadscaleInContainer) GetHealthEndpoint() string {
|
|||
// GetEndpoint returns the Headscale endpoint for the HeadscaleInContainer.
|
||||
func (t *HeadscaleInContainer) GetEndpoint() string {
|
||||
hostEndpoint := fmt.Sprintf("%s:%d",
|
||||
t.GetIP(),
|
||||
t.GetHostname(),
|
||||
t.port)
|
||||
|
||||
if t.hasTLS() {
|
||||
|
|
|
@ -347,6 +347,51 @@ func (s *Scenario) CreateUser(user string) error {
|
|||
|
||||
/// Client related stuff
|
||||
|
||||
func (s *Scenario) CreateTailscaleNode(
|
||||
version string,
|
||||
opts ...tsic.Option,
|
||||
) (TailscaleClient, error) {
|
||||
headscale, err := s.Headscale()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create tailscale node (version: %s): %w", version, err)
|
||||
}
|
||||
|
||||
cert := headscale.GetCert()
|
||||
hostname := headscale.GetHostname()
|
||||
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
opts = append(opts,
|
||||
tsic.WithCACert(cert),
|
||||
tsic.WithHeadscaleName(hostname),
|
||||
)
|
||||
|
||||
tsClient, err := tsic.New(
|
||||
s.pool,
|
||||
version,
|
||||
s.network,
|
||||
opts...,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(
|
||||
"failed to create tailscale (%s) node: %w",
|
||||
tsClient.Hostname(),
|
||||
err,
|
||||
)
|
||||
}
|
||||
|
||||
err = tsClient.WaitForNeedsLogin()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(
|
||||
"failed to wait for tailscaled (%s) to need login: %w",
|
||||
tsClient.Hostname(),
|
||||
err,
|
||||
)
|
||||
}
|
||||
|
||||
return tsClient, nil
|
||||
}
|
||||
|
||||
// CreateTailscaleNodesInUser creates and adds a new TailscaleClient to a
|
||||
// User in the Scenario.
|
||||
func (s *Scenario) CreateTailscaleNodesInUser(
|
||||
|
|
|
@ -466,7 +466,7 @@ func (t *TailscaleInContainer) Login(
|
|||
// This login mechanism uses web + command line flow for authentication.
|
||||
func (t *TailscaleInContainer) LoginWithURL(
|
||||
loginServer string,
|
||||
) (*url.URL, error) {
|
||||
) (loginURL *url.URL, err error) {
|
||||
command := []string{
|
||||
"tailscale",
|
||||
"up",
|
||||
|
@ -475,20 +475,27 @@ func (t *TailscaleInContainer) LoginWithURL(
|
|||
"--accept-routes=false",
|
||||
}
|
||||
|
||||
_, stderr, err := t.Execute(command)
|
||||
stdout, stderr, err := t.Execute(command)
|
||||
if errors.Is(err, errTailscaleNotLoggedIn) {
|
||||
return nil, errTailscaleCannotUpWithoutAuthkey
|
||||
}
|
||||
|
||||
urlStr := strings.ReplaceAll(stderr, "\nTo authenticate, visit:\n\n\t", "")
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.Printf("join command: %q", strings.Join(command, " "))
|
||||
}
|
||||
}()
|
||||
|
||||
urlStr := strings.ReplaceAll(stdout+stderr, "\nTo authenticate, visit:\n\n\t", "")
|
||||
urlStr = strings.TrimSpace(urlStr)
|
||||
|
||||
// parse URL
|
||||
loginURL, err := url.Parse(urlStr)
|
||||
if err != nil {
|
||||
log.Printf("Could not parse login URL: %s", err)
|
||||
log.Printf("Original join command result: %s", stderr)
|
||||
if urlStr == "" {
|
||||
return nil, fmt.Errorf("failed to get login URL: stdout: %s, stderr: %s", stdout, stderr)
|
||||
}
|
||||
|
||||
// parse URL
|
||||
loginURL, err = url.Parse(urlStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -497,12 +504,17 @@ func (t *TailscaleInContainer) LoginWithURL(
|
|||
|
||||
// Logout runs the logout routine on the given Tailscale instance.
|
||||
func (t *TailscaleInContainer) Logout() error {
|
||||
_, _, err := t.Execute([]string{"tailscale", "logout"})
|
||||
stdout, stderr, err := t.Execute([]string{"tailscale", "logout"})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
stdout, stderr, _ = t.Execute([]string{"tailscale", "status"})
|
||||
if !strings.Contains(stdout+stderr, "Logged out.") {
|
||||
return fmt.Errorf("failed to logout, stdout: %s, stderr: %s", stdout, stderr)
|
||||
}
|
||||
|
||||
return t.waitForBackendState("NeedsLogin")
|
||||
}
|
||||
|
||||
// Helper that runs `tailscale up` with no arguments.
|
||||
|
@ -826,28 +838,16 @@ func (t *TailscaleInContainer) FailingPeersAsString() (string, bool, error) {
|
|||
// WaitForNeedsLogin blocks until the Tailscale (tailscaled) instance has
|
||||
// started and needs to be logged into.
|
||||
func (t *TailscaleInContainer) WaitForNeedsLogin() error {
|
||||
return t.pool.Retry(func() error {
|
||||
status, err := t.Status()
|
||||
if err != nil {
|
||||
return errTailscaleStatus(t.hostname, err)
|
||||
}
|
||||
|
||||
// ipnstate.Status.CurrentTailnet was added in Tailscale 1.22.0
|
||||
// https://github.com/tailscale/tailscale/pull/3865
|
||||
//
|
||||
// Before that, we can check the BackendState to see if the
|
||||
// tailscaled daemon is connected to the control system.
|
||||
if status.BackendState == "NeedsLogin" {
|
||||
return nil
|
||||
}
|
||||
|
||||
return errTailscaledNotReadyForLogin
|
||||
})
|
||||
return t.waitForBackendState("NeedsLogin")
|
||||
}
|
||||
|
||||
// WaitForRunning blocks until the Tailscale (tailscaled) instance is logged in
|
||||
// and ready to be used.
|
||||
func (t *TailscaleInContainer) WaitForRunning() error {
|
||||
return t.waitForBackendState("Running")
|
||||
}
|
||||
|
||||
func (t *TailscaleInContainer) waitForBackendState(state string) error {
|
||||
return t.pool.Retry(func() error {
|
||||
status, err := t.Status()
|
||||
if err != nil {
|
||||
|
@ -859,7 +859,7 @@ func (t *TailscaleInContainer) WaitForRunning() error {
|
|||
//
|
||||
// Before that, we can check the BackendState to see if the
|
||||
// tailscaled daemon is connected to the control system.
|
||||
if status.BackendState == "Running" {
|
||||
if status.BackendState == state {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue