integration: standardize test infrastructure options

Make embedded DERP server and TLS the default configuration for all
integration tests, replacing the per-test opt-in model that led to
inconsistent and flaky test behavior.

Infrastructure changes:
- DefaultConfigEnv() includes embedded DERP server settings
- New() auto-generates a proper CA + server TLS certificate pair
- CA cert is installed into container trust stores and returned by
  GetCert() so clients and internal tools (curl) trust the server
- CreateCertificate() now returns (caCert, cert, key) instead of
  discarding the CA certificate
- Add WithPublicDERP() and WithoutTLS() opt-out options
- Remove WithTLS(), WithEmbeddedDERPServerOnly(), and WithDERPAsIP()
  since all their behavior is now the default or unnecessary

Test cleanup:
- Remove all redundant WithTLS/WithEmbeddedDERPServerOnly/WithDERPAsIP
  calls from test files
- Give every test a unique WithTestName by parameterizing aclScenario,
  sshScenario, and derpServerScenario helpers
- Add WithTestName to tests that were missing it
- Document all non-standard options with inline comments explaining
  why each is needed

Updates #3139
This commit is contained in:
Kristoffer Dalby
2026-03-16 09:15:46 +00:00
parent 87b8507ac9
commit e5ebe3205a
18 changed files with 209 additions and 236 deletions

View File

@@ -58,6 +58,7 @@ var veryLargeDestination = []policyv2.AliasWithPorts{
func aclScenario(
t *testing.T,
policy *policyv2.Policy,
testName string,
clientsPerUser int,
) *Scenario {
t.Helper()
@@ -81,9 +82,7 @@ func aclScenario(
tsic.WithDockerWorkdir("/"),
},
hsic.WithACLPolicy(policy),
hsic.WithTestName("acl"),
hsic.WithEmbeddedDERPServerOnly(),
hsic.WithTLS(),
hsic.WithTestName(testName),
)
require.NoError(t, err)
@@ -305,6 +304,7 @@ func TestACLHostsInNetMapTable(t *testing.T) {
err = scenario.CreateHeadscaleEnv(
[]tsic.Option{},
hsic.WithTestName("aclnetmap"),
hsic.WithACLPolicy(&testCase.policy),
)
@@ -351,6 +351,7 @@ func TestACLAllowUser80Dst(t *testing.T) {
},
},
},
"acl-allowuser80",
1,
)
defer scenario.ShutdownAssertNoPanics(t)
@@ -414,6 +415,7 @@ func TestACLDenyAllPort80(t *testing.T) {
},
},
},
"acl-denyport80",
4,
)
defer scenario.ShutdownAssertNoPanics(t)
@@ -462,6 +464,7 @@ func TestACLAllowUserDst(t *testing.T) {
},
},
},
"acl-allowuserdst",
2,
)
defer scenario.ShutdownAssertNoPanics(t)
@@ -524,6 +527,7 @@ func TestACLAllowStarDst(t *testing.T) {
},
},
},
"acl-allowstar",
2,
)
defer scenario.ShutdownAssertNoPanics(t)
@@ -591,6 +595,7 @@ func TestACLNamedHostsCanReachBySubnet(t *testing.T) {
},
},
},
"acl-namedsubnet",
3,
)
defer scenario.ShutdownAssertNoPanics(t)
@@ -743,6 +748,7 @@ func TestACLNamedHostsCanReach(t *testing.T) {
t.Run(name, func(t *testing.T) {
scenario := aclScenario(t,
&testCase.policy,
"acl-namedreach",
2,
)
defer scenario.ShutdownAssertNoPanics(t)
@@ -1044,7 +1050,7 @@ func TestACLDevice1CanAccessDevice2(t *testing.T) {
for name, testCase := range tests {
t.Run(name, func(t *testing.T) {
scenario := aclScenario(t, &testCase.policy, 1)
scenario := aclScenario(t, &testCase.policy, "acl-dev1dev2", 1)
defer scenario.ShutdownAssertNoPanics(t)
test1ip := netip.MustParseAddr("100.64.0.1")
@@ -1159,7 +1165,7 @@ func TestPolicyUpdateWhileRunningWithCLIInDatabase(t *testing.T) {
tsic.WithDockerWorkdir("/"),
},
hsic.WithTestName("policyreload"),
hsic.WithPolicyMode(types.PolicyModeDB),
hsic.WithPolicyMode(types.PolicyModeDB), // test updates policy at runtime via CLI
)
require.NoError(t, err)
@@ -1290,6 +1296,7 @@ func TestACLAutogroupMember(t *testing.T) {
},
},
},
"acl-agmember",
2,
)
defer scenario.ShutdownAssertNoPanics(t)
@@ -1383,8 +1390,6 @@ func TestACLAutogroupTagged(t *testing.T) {
headscale, err := scenario.Headscale(
hsic.WithACLPolicy(policy),
hsic.WithTestName("acl-autogroup-tagged"),
hsic.WithEmbeddedDERPServerOnly(),
hsic.WithTLS(),
)
require.NoError(t, err)
@@ -1430,7 +1435,10 @@ func TestACLAutogroupTagged(t *testing.T) {
network = networks[0]
}
// Create the tailscale node with appropriate options
// Create the tailscale node with appropriate options.
// CACert and HeadscaleName are passed explicitly because
// nodes created via CreateTailscaleNode are not part of
// the standard CreateHeadscaleEnv flow.
opts := []tsic.Option{
tsic.WithCACert(headscale.GetCert()),
tsic.WithHeadscaleName(headscale.GetHostname()),
@@ -1698,8 +1706,6 @@ func TestACLAutogroupSelf(t *testing.T) {
},
hsic.WithACLPolicy(policy),
hsic.WithTestName("acl-autogroup-self"),
hsic.WithEmbeddedDERPServerOnly(),
hsic.WithTLS(),
)
require.NoError(t, err)
@@ -1721,7 +1727,10 @@ func TestACLAutogroupSelf(t *testing.T) {
authKey, err := scenario.CreatePreAuthKeyWithTags(routerUser.GetId(), true, false, []string{"tag:router-node"})
require.NoError(t, err)
// Create router node (tags come from the PreAuthKey)
// Create router node (tags come from the PreAuthKey).
// CACert and HeadscaleName are passed explicitly because
// nodes created via tsic.New are not part of the standard
// CreateHeadscaleEnv flow.
routerClient, err := tsic.New(
scenario.Pool(),
"unstable",
@@ -1917,9 +1926,7 @@ func TestACLPolicyPropagationOverTime(t *testing.T) {
tsic.WithDockerWorkdir("/"),
},
hsic.WithTestName("aclpropagation"),
hsic.WithPolicyMode(types.PolicyModeDB),
hsic.WithEmbeddedDERPServerOnly(),
hsic.WithTLS(),
hsic.WithPolicyMode(types.PolicyModeDB), // test updates policy at runtime via CLI
)
require.NoError(t, err)
@@ -2738,8 +2745,6 @@ func TestACLTagPropagation(t *testing.T) {
},
hsic.WithACLPolicy(tt.policy),
hsic.WithTestName("acl-tag-"+tt.name),
hsic.WithEmbeddedDERPServerOnly(),
hsic.WithTLS(),
)
require.NoError(t, err)
@@ -2926,8 +2931,6 @@ func TestACLTagPropagationPortSpecific(t *testing.T) {
},
hsic.WithACLPolicy(policy),
hsic.WithTestName("acl-tag-port-specific"),
hsic.WithEmbeddedDERPServerOnly(),
hsic.WithTLS(),
)
require.NoError(t, err)
@@ -3090,8 +3093,6 @@ func TestACLGroupWithUnknownUser(t *testing.T) {
},
hsic.WithACLPolicy(policy),
hsic.WithTestName("acl-unknown-user"),
hsic.WithEmbeddedDERPServerOnly(),
hsic.WithTLS(),
)
require.NoError(t, err)
@@ -3192,8 +3193,6 @@ func TestACLGroupAfterUserDeletion(t *testing.T) {
},
hsic.WithACLPolicy(policy),
hsic.WithTestName("acl-deleted-user"),
hsic.WithEmbeddedDERPServerOnly(),
hsic.WithTLS(),
hsic.WithPolicyMode(types.PolicyModeDB), // Use DB mode so policy persists after user deletion
)
require.NoError(t, err)
@@ -3386,9 +3385,7 @@ func TestACLGroupDeletionExactReproduction(t *testing.T) {
},
hsic.WithACLPolicy(initialPolicy),
hsic.WithTestName("acl-exact-repro"),
hsic.WithEmbeddedDERPServerOnly(),
hsic.WithTLS(),
hsic.WithPolicyMode(types.PolicyModeDB),
hsic.WithPolicyMode(types.PolicyModeDB), // test updates policy at runtime via CLI
)
require.NoError(t, err)
@@ -3565,9 +3562,7 @@ func TestACLDynamicUnknownUserAddition(t *testing.T) {
},
hsic.WithACLPolicy(validPolicy),
hsic.WithTestName("acl-dynamic-unknown"),
hsic.WithEmbeddedDERPServerOnly(),
hsic.WithTLS(),
hsic.WithPolicyMode(types.PolicyModeDB),
hsic.WithPolicyMode(types.PolicyModeDB), // test updates policy at runtime via CLI
)
require.NoError(t, err)
@@ -3723,9 +3718,7 @@ func TestACLDynamicUnknownUserRemoval(t *testing.T) {
},
hsic.WithACLPolicy(policyWithUnknown),
hsic.WithTestName("acl-unknown-removal"),
hsic.WithEmbeddedDERPServerOnly(),
hsic.WithTLS(),
hsic.WithPolicyMode(types.PolicyModeDB),
hsic.WithPolicyMode(types.PolicyModeDB), // test updates policy at runtime via CLI
)
require.NoError(t, err)

View File

@@ -435,7 +435,6 @@ func TestGRPCAuthenticationBypass(t *testing.T) {
err = scenario.CreateHeadscaleEnv(
[]tsic.Option{},
hsic.WithTestName("grpcauthtest"),
hsic.WithTLS(),
hsic.WithConfigEnv(map[string]string{
// Enable gRPC on the standard port
"HEADSCALE_GRPC_LISTEN_ADDR": "0.0.0.0:50443",
@@ -560,7 +559,6 @@ func TestCLIWithConfigAuthenticationBypass(t *testing.T) {
err = scenario.CreateHeadscaleEnv(
[]tsic.Option{},
hsic.WithTestName("cliconfigauth"),
hsic.WithTLS(),
hsic.WithConfigEnv(map[string]string{
"HEADSCALE_GRPC_LISTEN_ADDR": "0.0.0.0:50443",
}),

View File

@@ -35,14 +35,7 @@ func TestAuthKeyLogoutAndReloginSameUser(t *testing.T) {
defer scenario.ShutdownAssertNoPanics(t)
opts := []hsic.Option{
hsic.WithTestName("pingallbyip"),
hsic.WithEmbeddedDERPServerOnly(),
hsic.WithDERPAsIP(),
}
if https {
opts = append(opts, []hsic.Option{
hsic.WithTLS(),
}...)
hsic.WithTestName("authkey-relogsame"),
}
err = scenario.CreateHeadscaleEnv([]tsic.Option{}, opts...)
@@ -241,8 +234,6 @@ func TestAuthKeyLogoutAndReloginNewUser(t *testing.T) {
err = scenario.CreateHeadscaleEnv([]tsic.Option{},
hsic.WithTestName("keyrelognewuser"),
hsic.WithTLS(),
hsic.WithDERPAsIP(),
)
requireNoErrHeadscaleEnv(t, err)
@@ -375,13 +366,7 @@ func TestAuthKeyLogoutAndReloginSameUserExpiredKey(t *testing.T) {
defer scenario.ShutdownAssertNoPanics(t)
opts := []hsic.Option{
hsic.WithTestName("pingallbyip"),
hsic.WithDERPAsIP(),
}
if https {
opts = append(opts, []hsic.Option{
hsic.WithTLS(),
}...)
hsic.WithTestName("authkey-rlogexpired"),
}
err = scenario.CreateHeadscaleEnv([]tsic.Option{}, opts...)
@@ -503,7 +488,7 @@ func TestAuthKeyDeleteKey(t *testing.T) {
require.NoError(t, err)
defer scenario.ShutdownAssertNoPanics(t)
err = scenario.CreateHeadscaleEnv([]tsic.Option{}, hsic.WithTestName("delkey"), hsic.WithTLS(), hsic.WithDERPAsIP())
err = scenario.CreateHeadscaleEnv([]tsic.Option{}, hsic.WithTestName("delkey"))
requireNoErrHeadscaleEnv(t, err)
headscale, err := scenario.Headscale()
@@ -621,7 +606,6 @@ func TestAuthKeyLogoutAndReloginRoutesPreserved(t *testing.T) {
tsic.WithExtraLoginArgs([]string{"--advertise-routes=" + advertiseRoute}),
},
hsic.WithTestName("routelogout"),
hsic.WithTLS(),
hsic.WithACLPolicy(
&policyv2.Policy{
ACLs: []policyv2.ACL{

View File

@@ -51,11 +51,13 @@ func TestOIDCAuthenticationPingAll(t *testing.T) {
"HEADSCALE_OIDC_CLIENT_SECRET_PATH": "${CREDENTIALS_DIRECTORY_TEST}/hs_client_oidc_secret",
}
// OIDC tests configure the mock OIDC provider via environment
// variables and inject the client secret as a file. This
// pattern is shared by all OIDC integration tests.
err = scenario.CreateHeadscaleEnvWithLoginURL(
nil,
hsic.WithTestName("oidcauthping"),
hsic.WithConfigEnv(oidcMap),
hsic.WithTLS(),
hsic.WithFileInContainer("/tmp/hs_client_oidc_secret", []byte(scenario.mockOIDC.ClientSecret())),
)
requireNoErrHeadscaleEnv(t, err)
@@ -374,7 +376,6 @@ func TestOIDC024UserCreation(t *testing.T) {
nil,
hsic.WithTestName("oidcmigration"),
hsic.WithConfigEnv(oidcMap),
hsic.WithTLS(),
hsic.WithFileInContainer("/tmp/hs_client_oidc_secret", []byte(scenario.mockOIDC.ClientSecret())),
)
requireNoErrHeadscaleEnv(t, err)
@@ -432,7 +433,6 @@ func TestOIDCAuthenticationWithPKCE(t *testing.T) {
nil,
hsic.WithTestName("oidcauthpkce"),
hsic.WithConfigEnv(oidcMap),
hsic.WithTLS(),
hsic.WithFileInContainer("/tmp/hs_client_oidc_secret", []byte(scenario.mockOIDC.ClientSecret())),
)
requireNoErrHeadscaleEnv(t, err)
@@ -487,12 +487,9 @@ func TestOIDCReloginSameNodeNewUser(t *testing.T) {
err = scenario.CreateHeadscaleEnvWithLoginURL(
nil,
hsic.WithTestName("oidcauthrelog"),
hsic.WithTestName("oidc-authrelog"),
hsic.WithConfigEnv(oidcMap),
hsic.WithTLS(),
hsic.WithFileInContainer("/tmp/hs_client_oidc_secret", []byte(scenario.mockOIDC.ClientSecret())),
hsic.WithEmbeddedDERPServerOnly(),
hsic.WithDERPAsIP(),
)
requireNoErrHeadscaleEnv(t, err)
@@ -899,11 +896,9 @@ func TestOIDCFollowUpUrl(t *testing.T) {
err = scenario.CreateHeadscaleEnvWithLoginURL(
nil,
hsic.WithTestName("oidcauthrelog"),
hsic.WithTestName("oidc-followup"),
hsic.WithConfigEnv(oidcMap),
hsic.WithTLS(),
hsic.WithFileInContainer("/tmp/hs_client_oidc_secret", []byte(scenario.mockOIDC.ClientSecret())),
hsic.WithEmbeddedDERPServerOnly(),
)
require.NoError(t, err)
@@ -1011,9 +1006,7 @@ func TestOIDCMultipleOpenedLoginUrls(t *testing.T) {
nil,
hsic.WithTestName("oidcauthrelog"),
hsic.WithConfigEnv(oidcMap),
hsic.WithTLS(),
hsic.WithFileInContainer("/tmp/hs_client_oidc_secret", []byte(scenario.mockOIDC.ClientSecret())),
hsic.WithEmbeddedDERPServerOnly(),
)
require.NoError(t, err)
@@ -1145,10 +1138,7 @@ func TestOIDCReloginSameNodeSameUser(t *testing.T) {
nil,
hsic.WithTestName("oidcsameuser"),
hsic.WithConfigEnv(oidcMap),
hsic.WithTLS(),
hsic.WithFileInContainer("/tmp/hs_client_oidc_secret", []byte(scenario.mockOIDC.ClientSecret())),
hsic.WithEmbeddedDERPServerOnly(),
hsic.WithDERPAsIP(),
)
requireNoErrHeadscaleEnv(t, err)
@@ -1373,10 +1363,7 @@ func TestOIDCExpiryAfterRestart(t *testing.T) {
nil,
hsic.WithTestName("oidcexpiry"),
hsic.WithConfigEnv(oidcMap),
hsic.WithTLS(),
hsic.WithFileInContainer("/tmp/hs_client_oidc_secret", []byte(scenario.mockOIDC.ClientSecret())),
hsic.WithEmbeddedDERPServerOnly(),
hsic.WithDERPAsIP(),
)
requireNoErrHeadscaleEnv(t, err)
@@ -1524,7 +1511,6 @@ func TestOIDCACLPolicyOnJoin(t *testing.T) {
},
hsic.WithTestName("oidcaclpolicy"),
hsic.WithConfigEnv(oidcMap),
hsic.WithTLS(),
hsic.WithFileInContainer("/tmp/hs_client_oidc_secret", []byte(scenario.mockOIDC.ClientSecret())),
hsic.WithACLPolicy(
&policyv2.Policy{
@@ -1801,10 +1787,7 @@ func TestOIDCReloginSameUserRoutesPreserved(t *testing.T) {
},
hsic.WithTestName("oidcrouterelogin"),
hsic.WithConfigEnv(oidcMap),
hsic.WithTLS(),
hsic.WithFileInContainer("/tmp/hs_client_oidc_secret", []byte(scenario.mockOIDC.ClientSecret())),
hsic.WithEmbeddedDERPServerOnly(),
hsic.WithDERPAsIP(),
hsic.WithACLPolicy(
&policyv2.Policy{
ACLs: []policyv2.ACL{

View File

@@ -32,9 +32,6 @@ func TestAuthWebFlowAuthenticationPingAll(t *testing.T) {
err = scenario.CreateHeadscaleEnvWithLoginURL(
nil,
hsic.WithTestName("webauthping"),
hsic.WithEmbeddedDERPServerOnly(),
hsic.WithDERPAsIP(),
hsic.WithTLS(),
)
requireNoErrHeadscaleEnv(t, err)
@@ -73,8 +70,6 @@ func TestAuthWebFlowLogoutAndReloginSameUser(t *testing.T) {
err = scenario.CreateHeadscaleEnvWithLoginURL(
nil,
hsic.WithTestName("weblogout"),
hsic.WithDERPAsIP(),
hsic.WithTLS(),
)
requireNoErrHeadscaleEnv(t, err)
@@ -239,8 +234,6 @@ func TestAuthWebFlowLogoutAndReloginNewUser(t *testing.T) {
err = scenario.CreateHeadscaleEnvWithLoginURL(
nil,
hsic.WithTestName("webflowrelnewuser"),
hsic.WithDERPAsIP(),
hsic.WithTLS(),
)
requireNoErrHeadscaleEnv(t, err)

View File

@@ -58,7 +58,7 @@ func TestUserCommand(t *testing.T) {
require.NoError(t, err)
defer scenario.ShutdownAssertNoPanics(t)
err = scenario.CreateHeadscaleEnv([]tsic.Option{}, hsic.WithTestName("clins"))
err = scenario.CreateHeadscaleEnv([]tsic.Option{}, hsic.WithTestName("cli-user"))
require.NoError(t, err)
headscale, err := scenario.Headscale()
@@ -588,9 +588,7 @@ func TestPreAuthKeyCorrectUserLoggedInCommand(t *testing.T) {
err = scenario.CreateHeadscaleEnv(
[]tsic.Option{},
hsic.WithTestName("clipak"),
hsic.WithEmbeddedDERPServerOnly(),
hsic.WithTLS(),
hsic.WithTestName("cli-paklogin"),
)
require.NoError(t, err)
@@ -699,8 +697,6 @@ func TestTaggedNodesCLIOutput(t *testing.T) {
err = scenario.CreateHeadscaleEnv(
[]tsic.Option{},
hsic.WithTestName("tagcli"),
hsic.WithEmbeddedDERPServerOnly(),
hsic.WithTLS(),
)
require.NoError(t, err)
@@ -811,7 +807,7 @@ func TestApiKeyCommand(t *testing.T) {
require.NoError(t, err)
defer scenario.ShutdownAssertNoPanics(t)
err = scenario.CreateHeadscaleEnv([]tsic.Option{}, hsic.WithTestName("clins"))
err = scenario.CreateHeadscaleEnv([]tsic.Option{}, hsic.WithTestName("cli-apikey"))
require.NoError(t, err)
headscale, err := scenario.Headscale()
@@ -1058,7 +1054,7 @@ func TestNodeCommand(t *testing.T) {
require.NoError(t, err)
defer scenario.ShutdownAssertNoPanics(t)
err = scenario.CreateHeadscaleEnv([]tsic.Option{}, hsic.WithTestName("clins"))
err = scenario.CreateHeadscaleEnv([]tsic.Option{}, hsic.WithTestName("cli-node"))
require.NoError(t, err)
headscale, err := scenario.Headscale()
@@ -1319,7 +1315,7 @@ func TestNodeExpireCommand(t *testing.T) {
require.NoError(t, err)
defer scenario.ShutdownAssertNoPanics(t)
err = scenario.CreateHeadscaleEnv([]tsic.Option{}, hsic.WithTestName("clins"))
err = scenario.CreateHeadscaleEnv([]tsic.Option{}, hsic.WithTestName("cli-nodeexpire"))
require.NoError(t, err)
headscale, err := scenario.Headscale()
@@ -1454,7 +1450,7 @@ func TestNodeRenameCommand(t *testing.T) {
require.NoError(t, err)
defer scenario.ShutdownAssertNoPanics(t)
err = scenario.CreateHeadscaleEnv([]tsic.Option{}, hsic.WithTestName("clins"))
err = scenario.CreateHeadscaleEnv([]tsic.Option{}, hsic.WithTestName("cli-noderename"))
require.NoError(t, err)
headscale, err := scenario.Headscale()
@@ -1634,9 +1630,9 @@ func TestPolicyCommand(t *testing.T) {
err = scenario.CreateHeadscaleEnv(
[]tsic.Option{},
hsic.WithTestName("clins"),
hsic.WithTestName("cli-policy"),
hsic.WithConfigEnv(map[string]string{
"HEADSCALE_POLICY_MODE": "database",
"HEADSCALE_POLICY_MODE": "database", // test sets/gets policy via CLI
}),
)
require.NoError(t, err)
@@ -1719,9 +1715,9 @@ func TestPolicyBrokenConfigCommand(t *testing.T) {
err = scenario.CreateHeadscaleEnv(
[]tsic.Option{},
hsic.WithTestName("clins"),
hsic.WithTestName("cli-policybad"),
hsic.WithConfigEnv(map[string]string{
"HEADSCALE_POLICY_MODE": "database",
"HEADSCALE_POLICY_MODE": "database", // test sets invalid policy via CLI
}),
)
require.NoError(t, err)

View File

@@ -32,7 +32,7 @@ func TestDERPVerifyEndpoint(t *testing.T) {
headscalePort := 8080
// Create cert for headscale
certHeadscale, keyHeadscale, err := integrationutil.CreateCertificate(hostname)
caHeadscale, certHeadscale, keyHeadscale, err := integrationutil.CreateCertificate(hostname)
require.NoError(t, err)
spec := ScenarioSpec{
@@ -46,7 +46,7 @@ func TestDERPVerifyEndpoint(t *testing.T) {
defer scenario.ShutdownAssertNoPanics(t)
derper, err := scenario.CreateDERPServer("head",
dsic.WithCACert(certHeadscale),
dsic.WithCACert(caHeadscale),
dsic.WithVerifyClientURL(fmt.Sprintf("https://%s/verify", net.JoinHostPort(hostname, strconv.Itoa(headscalePort)))),
)
require.NoError(t, err)
@@ -72,10 +72,18 @@ func TestDERPVerifyEndpoint(t *testing.T) {
},
}
// WithHostname is used instead of WithTestName because the hostname
// must match the pre-generated TLS certificate created above.
// The test name "derpverify" is embedded in the hostname variable.
//
// WithCACert passes the external DERP server's certificate so
// tailscale clients trust it. WithCustomTLS and WithDERPConfig
// configure headscale to use the external DERP server created
// above instead of the default embedded one.
err = scenario.CreateHeadscaleEnv([]tsic.Option{tsic.WithCACert(derper.GetCert())},
hsic.WithHostname(hostname),
hsic.WithPort(headscalePort),
hsic.WithCustomTLS(certHeadscale, keyHeadscale),
hsic.WithCustomTLS(caHeadscale, certHeadscale, keyHeadscale),
hsic.WithDERPConfig(derpMap))
requireNoErrHeadscaleEnv(t, err)

View File

@@ -104,8 +104,6 @@ func TestResolveMagicDNSExtraRecordsPath(t *testing.T) {
"HEADSCALE_DNS_EXTRA_RECORDS_PATH": erPath,
}),
hsic.WithFileInContainer(erPath, b),
hsic.WithEmbeddedDERPServerOnly(),
hsic.WithTLS(),
)
requireNoErrHeadscaleEnv(t, err)

View File

@@ -40,6 +40,7 @@ type DERPServerInContainer struct {
stunPort int
derpPort int
caCerts [][]byte
tlsCACert []byte
tlsCert []byte
tlsKey []byte
withExtraHosts []string
@@ -160,22 +161,27 @@ func New(
hostname = fmt.Sprintf("derp-%s-%s", strings.ReplaceAll(version, ".", "-"), hash)
}
tlsCert, tlsKey, err := integrationutil.CreateCertificate(hostname)
tlsCACert, tlsCert, tlsKey, err := integrationutil.CreateCertificate(hostname)
if err != nil {
return nil, fmt.Errorf("creating certificates for headscale test: %w", err)
return nil, fmt.Errorf("creating certificates for derp test: %w", err)
}
dsic := &DERPServerInContainer{
version: version,
hostname: hostname,
pool: pool,
networks: networks,
tlsCert: tlsCert,
tlsKey: tlsKey,
stunPort: 3478, //nolint
derpPort: 443, //nolint
version: version,
hostname: hostname,
pool: pool,
networks: networks,
tlsCACert: tlsCACert,
tlsCert: tlsCert,
tlsKey: tlsKey,
stunPort: 3478, //nolint
derpPort: 443, //nolint
}
// Install the CA cert so the DERP server trusts its own certificate
// and any headscale CA certs passed via WithCACert.
dsic.caCerts = append(dsic.caCerts, tlsCACert)
for _, opt := range opts {
opt(dsic)
}
@@ -297,9 +303,10 @@ func (t *DERPServerInContainer) Shutdown() error {
return t.pool.Purge(t.container)
}
// GetCert returns the TLS certificate of the DERPer instance.
// GetCert returns the CA certificate that clients should trust to
// verify this DERP server's TLS certificate.
func (t *DERPServerInContainer) GetCert() []byte {
return t.tlsCert
return t.tlsCACert
}
// Hostname returns the hostname of the DERPer instance.

View File

@@ -28,7 +28,7 @@ func TestDERPServerScenario(t *testing.T) {
},
}
derpServerScenario(t, spec, false, func(scenario *Scenario) {
derpServerScenario(t, spec, "derp-tcp", false, func(scenario *Scenario) {
allClients, err := scenario.ListTailscaleClients()
requireNoErrListClients(t, err)
t.Logf("checking %d clients for websocket connections", len(allClients))
@@ -78,7 +78,7 @@ func TestDERPServerWebsocketScenario(t *testing.T) {
},
}
derpServerScenario(t, spec, true, func(scenario *Scenario) {
derpServerScenario(t, spec, "derp-ws", true, func(scenario *Scenario) {
allClients, err := scenario.ListTailscaleClients()
requireNoErrListClients(t, err)
t.Logf("checking %d clients for websocket connections", len(allClients))
@@ -103,6 +103,7 @@ func TestDERPServerWebsocketScenario(t *testing.T) {
func derpServerScenario(
t *testing.T,
spec ScenarioSpec,
testName string,
websocket bool,
furtherAssertions ...func(*Scenario),
) {
@@ -117,11 +118,11 @@ func derpServerScenario(
[]tsic.Option{
tsic.WithWebsocketDERP(websocket),
},
hsic.WithTestName("derpserver"),
hsic.WithTestName(testName),
// Expose STUN port for DERP NAT traversal.
hsic.WithExtraPorts([]string{"3478/udp"}),
hsic.WithEmbeddedDERPServerOnly(),
// DERP clients expect the server on the standard HTTPS port.
hsic.WithPort(443),
hsic.WithTLS(),
hsic.WithConfigEnv(map[string]string{
"HEADSCALE_DERP_AUTO_UPDATE_ENABLED": "true",
"HEADSCALE_DERP_UPDATE_FREQUENCY": "10s",

View File

@@ -41,8 +41,9 @@ func TestPingAllByIP(t *testing.T) {
err = scenario.CreateHeadscaleEnv(
[]tsic.Option{},
hsic.WithTestName("pingallbyip"),
hsic.WithEmbeddedDERPServerOnly(),
hsic.WithTLS(),
// All other tests use the default sequential allocation.
// This test uses random allocation to ensure it does not
// break basic connectivity.
hsic.WithIPAllocationStrategy(types.IPAllocationStrategyRandom),
)
requireNoErrHeadscaleEnv(t, err)
@@ -102,6 +103,12 @@ func TestPingAllByIPPublicDERP(t *testing.T) {
err = scenario.CreateHeadscaleEnv(
[]tsic.Option{},
hsic.WithTestName("pingallbyippubderp"),
// Explicitly use public DERP relays instead of the embedded
// DERP server to verify connectivity through Tailscale's
// infrastructure. TLS is disabled because the headscale
// server does not need to terminate TLS for this test.
hsic.WithPublicDERP(),
hsic.WithoutTLS(),
)
requireNoErrHeadscaleEnv(t, err)
@@ -128,6 +135,8 @@ func TestEphemeral(t *testing.T) {
testEphemeralWithOptions(t, hsic.WithTestName("ephemeral"))
}
// TestEphemeralInAlternateTimezone verifies that ephemeral node
// expiry works correctly when the server runs in a non-UTC timezone.
func TestEphemeralInAlternateTimezone(t *testing.T) {
testEphemeralWithOptions(
t,
@@ -387,8 +396,6 @@ func TestTaildrop(t *testing.T) {
err = scenario.CreateHeadscaleEnv([]tsic.Option{},
hsic.WithTestName("taildrop"),
hsic.WithEmbeddedDERPServerOnly(),
hsic.WithTLS(),
)
requireNoErrHeadscaleEnv(t, err)
@@ -1403,9 +1410,6 @@ func TestPingAllByIPManyUpDown(t *testing.T) {
err = scenario.CreateHeadscaleEnv(
[]tsic.Option{},
hsic.WithTestName("pingallbyipmany"),
hsic.WithEmbeddedDERPServerOnly(),
hsic.WithDERPAsIP(),
hsic.WithTLS(),
)
requireNoErrHeadscaleEnv(t, err)
@@ -1512,8 +1516,6 @@ func Test2118DeletingOnlineNodePanics(t *testing.T) {
err = scenario.CreateHeadscaleEnv(
[]tsic.Option{},
hsic.WithTestName("deletenocrash"),
hsic.WithEmbeddedDERPServerOnly(),
hsic.WithTLS(),
)
requireNoErrHeadscaleEnv(t, err)

View File

@@ -28,11 +28,24 @@ func DefaultConfigEnv() map[string]string {
"HEADSCALE_PRIVATE_KEY_PATH": "/tmp/private.key",
"HEADSCALE_NOISE_PRIVATE_KEY_PATH": "/tmp/noise_private.key",
"HEADSCALE_METRICS_LISTEN_ADDR": "0.0.0.0:9090",
"HEADSCALE_DERP_URLS": "https://controlplane.tailscale.com/derpmap/default",
"HEADSCALE_DERP_AUTO_UPDATE_ENABLED": "false",
"HEADSCALE_DERP_UPDATE_FREQUENCY": "1m",
"HEADSCALE_DEBUG_PORT": "40000",
// Embedded DERP is the default for test isolation.
// Tests should not depend on external DERP infrastructure.
// Use WithPublicDERP() to opt out for tests that explicitly
// need public DERP relays.
"HEADSCALE_DERP_URLS": "",
"HEADSCALE_DERP_AUTO_UPDATE_ENABLED": "false",
"HEADSCALE_DERP_UPDATE_FREQUENCY": "1m",
"HEADSCALE_DERP_SERVER_ENABLED": "true",
"HEADSCALE_DERP_SERVER_REGION_ID": "999",
"HEADSCALE_DERP_SERVER_REGION_CODE": "headscale",
"HEADSCALE_DERP_SERVER_REGION_NAME": "Headscale Embedded DERP",
"HEADSCALE_DERP_SERVER_STUN_LISTEN_ADDR": "0.0.0.0:3478",
"HEADSCALE_DERP_SERVER_PRIVATE_KEY_PATH": "/tmp/derp.key",
"DERP_DEBUG_LOGS": "true",
"DERP_PROBER_DEBUG_LOGS": "true",
// a bunch of tests (ACL/Policy) rely on predictable IP alloc,
// so ensure the sequential alloc is used by default.
"HEADSCALE_PREFIXES_ALLOCATION": string(types.IPAllocationStrategySequential),

View File

@@ -82,8 +82,10 @@ type HeadscaleInContainer struct {
hostPortBindings map[string][]string
aclPolicy *policyv2.Policy
env map[string]string
tlsCACert []byte
tlsCert []byte
tlsKey []byte
noTLS bool
filesInContainer []fileInContainer
postgres bool
policyMode types.PolicyMode
@@ -115,24 +117,24 @@ func WithCACert(cert []byte) Option {
}
}
// WithTLS creates certificates and enables HTTPS.
func WithTLS() Option {
// WithoutTLS disables the default TLS configuration.
// Most tests should not need this. Use only for tests that
// explicitly need to test non-TLS behavior.
func WithoutTLS() Option {
return func(hsic *HeadscaleInContainer) {
cert, key, err := integrationutil.CreateCertificate(hsic.hostname)
if err != nil {
log.Fatalf("creating certificates for headscale test: %s", err)
}
hsic.tlsCert = cert
hsic.tlsKey = key
hsic.noTLS = true
}
}
// WithCustomTLS uses the given certificates for the Headscale instance.
func WithCustomTLS(cert, key []byte) Option {
// The caCert is installed into the container's trust store and returned
// by GetCert() so that clients can trust this server.
func WithCustomTLS(caCert, cert, key []byte) Option {
return func(hsic *HeadscaleInContainer) {
hsic.tlsCACert = caCert
hsic.tlsCert = cert
hsic.tlsKey = key
hsic.caCerts = append(hsic.caCerts, caCert)
}
}
@@ -216,25 +218,20 @@ func WithIPAllocationStrategy(strategy types.IPAllocationStrategy) Option {
}
}
// WithEmbeddedDERPServerOnly configures Headscale to start
// and only use the embedded DERP server.
// It requires WithTLS and WithHostnameAsServerURL to be
// set.
//
//nolint:goconst // env var values like "true" and "headscale" are clearer inline
func WithEmbeddedDERPServerOnly() Option {
// WithPublicDERP disables the embedded DERP server and restores
// the default public DERP relay configuration. Use this for tests
// that explicitly need to test public DERP behavior.
func WithPublicDERP() Option {
return func(hsic *HeadscaleInContainer) {
hsic.env["HEADSCALE_DERP_URLS"] = ""
hsic.env["HEADSCALE_DERP_SERVER_ENABLED"] = "true"
hsic.env["HEADSCALE_DERP_SERVER_REGION_ID"] = "999"
hsic.env["HEADSCALE_DERP_SERVER_REGION_CODE"] = "headscale"
hsic.env["HEADSCALE_DERP_SERVER_REGION_NAME"] = "Headscale Embedded DERP"
hsic.env["HEADSCALE_DERP_SERVER_STUN_LISTEN_ADDR"] = "0.0.0.0:3478"
hsic.env["HEADSCALE_DERP_SERVER_PRIVATE_KEY_PATH"] = "/tmp/derp.key"
// Envknob for enabling DERP debug logs
hsic.env["DERP_DEBUG_LOGS"] = "true"
hsic.env["DERP_PROBER_DEBUG_LOGS"] = "true"
hsic.env["HEADSCALE_DERP_URLS"] = "https://controlplane.tailscale.com/derpmap/default"
hsic.env["HEADSCALE_DERP_SERVER_ENABLED"] = "false"
delete(hsic.env, "HEADSCALE_DERP_SERVER_REGION_ID")
delete(hsic.env, "HEADSCALE_DERP_SERVER_REGION_CODE")
delete(hsic.env, "HEADSCALE_DERP_SERVER_REGION_NAME")
delete(hsic.env, "HEADSCALE_DERP_SERVER_STUN_LISTEN_ADDR")
delete(hsic.env, "HEADSCALE_DERP_SERVER_PRIVATE_KEY_PATH")
delete(hsic.env, "DERP_DEBUG_LOGS")
delete(hsic.env, "DERP_PROBER_DEBUG_LOGS")
}
}
@@ -282,14 +279,6 @@ func WithTimezone(timezone string) Option {
}
}
// WithDERPAsIP enables using IP address instead of hostname for DERP server.
// This is useful for integration tests where DNS resolution may be unreliable.
func WithDERPAsIP() Option {
return func(hsic *HeadscaleInContainer) {
hsic.env["HEADSCALE_DEBUG_DERP_USE_IP"] = "1"
}
}
// buildEntrypoint builds the container entrypoint command based on configuration.
// It constructs proper wait conditions instead of fixed sleeps:
// 1. Wait for network to be ready
@@ -367,6 +356,26 @@ func New(
opt(hsic)
}
// TLS is enabled by default for all integration tests.
// Generate a self-signed certificate if TLS was not explicitly
// disabled via WithoutTLS() and no custom cert was provided
// via WithCustomTLS().
if !hsic.noTLS && len(hsic.tlsCert) == 0 {
caCert, cert, key, err := integrationutil.CreateCertificate(hsic.hostname)
if err != nil {
return nil, fmt.Errorf("creating default TLS certificates: %w", err)
}
hsic.tlsCACert = caCert
hsic.tlsCert = cert
hsic.tlsKey = key
// Install the CA cert into the headscale container's trust
// store so that tools like curl trust the server's own
// certificate.
hsic.caCerts = append(hsic.caCerts, caCert)
}
log.Println("NAME: ", hsic.hostname)
portProto := fmt.Sprintf("%d/tcp", hsic.port)
@@ -1030,9 +1039,10 @@ func (t *HeadscaleInContainer) getEndpoint(useIP bool) string {
return "http://" + hostEndpoint
}
// GetCert returns the public certificate of the HeadscaleInContainer.
// GetCert returns the CA certificate that clients should trust to
// verify this server's TLS certificate.
func (t *HeadscaleInContainer) GetCert() []byte {
return t.tlsCert
return t.tlsCACert
}
// GetHostname returns the hostname of the HeadscaleInContainer.

View File

@@ -120,7 +120,11 @@ func FetchPathFromContainer(
}
// nolint
func CreateCertificate(hostname string) ([]byte, []byte, error) {
// CreateCertificate generates a CA certificate and a server certificate
// signed by that CA for the given hostname. It returns the CA certificate
// PEM (for trust stores), server certificate PEM, and server private key
// PEM.
func CreateCertificate(hostname string) (caCertPEM, certPEM, keyPEM []byte, err error) {
// From:
// https://shaneutt.com/blog/golang-ca-and-signed-cert-go/
@@ -144,7 +148,27 @@ func CreateCertificate(hostname string) ([]byte, []byte, error) {
caPrivKey, err := rsa.GenerateKey(rand.Reader, 4096)
if err != nil {
return nil, nil, err
return nil, nil, nil, err
}
caBytes, err := x509.CreateCertificate(
rand.Reader,
ca,
ca,
&caPrivKey.PublicKey,
caPrivKey,
)
if err != nil {
return nil, nil, nil, err
}
caPEM := new(bytes.Buffer)
err = pem.Encode(caPEM, &pem.Block{
Type: "CERTIFICATE",
Bytes: caBytes,
})
if err != nil {
return nil, nil, nil, err
}
cert := &x509.Certificate{
@@ -165,7 +189,7 @@ func CreateCertificate(hostname string) ([]byte, []byte, error) {
certPrivKey, err := rsa.GenerateKey(rand.Reader, 4096)
if err != nil {
return nil, nil, err
return nil, nil, nil, err
}
certBytes, err := x509.CreateCertificate(
@@ -176,30 +200,28 @@ func CreateCertificate(hostname string) ([]byte, []byte, error) {
caPrivKey,
)
if err != nil {
return nil, nil, err
return nil, nil, nil, err
}
certPEM := new(bytes.Buffer)
err = pem.Encode(certPEM, &pem.Block{
serverCertPEM := new(bytes.Buffer)
err = pem.Encode(serverCertPEM, &pem.Block{
Type: "CERTIFICATE",
Bytes: certBytes,
})
if err != nil {
return nil, nil, err
return nil, nil, nil, err
}
certPrivKeyPEM := new(bytes.Buffer)
err = pem.Encode(certPrivKeyPEM, &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(certPrivKey),
})
if err != nil {
return nil, nil, err
return nil, nil, nil, err
}
return certPEM.Bytes(), certPrivKeyPEM.Bytes(), nil
return caPEM.Bytes(), serverCertPEM.Bytes(), certPrivKeyPEM.Bytes(), nil
}
func BuildExpectedOnlineMap(all map[types.NodeID][]tailcfg.MapResponse) map[types.NodeID]map[types.NodeID]bool {

View File

@@ -55,7 +55,7 @@ func TestEnablingRoutes(t *testing.T) {
err = scenario.CreateHeadscaleEnv(
[]tsic.Option{tsic.WithAcceptRoutes()},
hsic.WithTestName("clienableroute"))
hsic.WithTestName("rt-enable"))
requireNoErrHeadscaleEnv(t, err)
allClients, err := scenario.ListTailscaleClients()
@@ -261,9 +261,7 @@ func TestHASubnetRouterFailover(t *testing.T) {
err = scenario.CreateHeadscaleEnv(
[]tsic.Option{tsic.WithAcceptRoutes()},
hsic.WithTestName("clienableroute"),
hsic.WithEmbeddedDERPServerOnly(),
hsic.WithTLS(),
hsic.WithTestName("rt-hafailover"),
)
requireNoErrHeadscaleEnv(t, err)
@@ -1363,7 +1361,7 @@ func TestSubnetRouteACL(t *testing.T) {
err = scenario.CreateHeadscaleEnv([]tsic.Option{
tsic.WithAcceptRoutes(),
}, hsic.WithTestName("clienableroute"), hsic.WithACLPolicy(
}, hsic.WithTestName("rt-subnetacl"), hsic.WithACLPolicy(
&policyv2.Policy{
Groups: policyv2.Groups{
policyv2.Group("group:admins"): []policyv2.Username{policyv2.Username(user + "@")},
@@ -1616,7 +1614,7 @@ func TestEnablingExitRoutes(t *testing.T) {
err = scenario.CreateHeadscaleEnv([]tsic.Option{
tsic.WithExtraLoginArgs([]string{"--advertise-exit-node"}),
}, hsic.WithTestName("clienableroute"))
}, hsic.WithTestName("rt-exitroute"))
requireNoErrHeadscaleEnv(t, err)
allClients, err := scenario.ListTailscaleClients()
@@ -1729,9 +1727,7 @@ func TestSubnetRouterMultiNetwork(t *testing.T) {
defer scenario.ShutdownAssertNoPanics(t)
err = scenario.CreateHeadscaleEnv([]tsic.Option{tsic.WithAcceptRoutes()},
hsic.WithTestName("clienableroute"),
hsic.WithEmbeddedDERPServerOnly(),
hsic.WithTLS(),
hsic.WithTestName("rt-multinet"),
)
requireNoErrHeadscaleEnv(t, err)
@@ -1884,9 +1880,7 @@ func TestSubnetRouterMultiNetworkExitNode(t *testing.T) {
defer scenario.ShutdownAssertNoPanics(t)
err = scenario.CreateHeadscaleEnv([]tsic.Option{},
hsic.WithTestName("clienableroute"),
hsic.WithEmbeddedDERPServerOnly(),
hsic.WithTLS(),
hsic.WithTestName("rt-multinetexit"),
)
requireNoErrHeadscaleEnv(t, err)
@@ -2306,10 +2300,8 @@ func TestAutoApproveMultiNetwork(t *testing.T) {
opts := []hsic.Option{
hsic.WithTestName("autoapprovemulti"),
hsic.WithEmbeddedDERPServerOnly(),
hsic.WithTLS(),
hsic.WithACLPolicy(pol),
hsic.WithPolicyMode(polMode),
hsic.WithPolicyMode(polMode), // test iterates over file and DB policy modes
}
tsOpts := []tsic.Option{
@@ -3026,7 +3018,7 @@ func TestSubnetRouteACLFiltering(t *testing.T) {
tsic.WithAcceptRoutes(),
}, hsic.WithTestName("routeaclfilter"),
hsic.WithACLPolicy(aclPolicy),
hsic.WithPolicyMode(types.PolicyModeDB),
hsic.WithPolicyMode(types.PolicyModeDB), // test updates policy at runtime via CLI
)
requireNoErrHeadscaleEnv(t, err)

View File

@@ -4,6 +4,7 @@ import (
"testing"
"github.com/juanfont/headscale/integration/dockertestutil"
"github.com/juanfont/headscale/integration/hsic"
"github.com/juanfont/headscale/integration/tsic"
"github.com/stretchr/testify/require"
)
@@ -40,7 +41,7 @@ func TestHeadscale(t *testing.T) {
defer scenario.ShutdownAssertNoPanics(t)
t.Run("start-headscale", func(t *testing.T) {
headscale, err := scenario.Headscale()
headscale, err := scenario.Headscale(hsic.WithTestName("scenariohs"))
if err != nil {
t.Fatalf("failed to create start headcale: %s", err)
}
@@ -89,7 +90,7 @@ func TestTailscaleNodesJoiningHeadcale(t *testing.T) {
defer scenario.ShutdownAssertNoPanics(t)
t.Run("start-headscale", func(t *testing.T) {
headscale, err := scenario.Headscale()
headscale, err := scenario.Headscale(hsic.WithTestName("scenariojoin"))
if err != nil {
t.Fatalf("failed to create start headcale: %s", err)
}

View File

@@ -27,7 +27,7 @@ func isSSHNoAccessStdError(stderr string) bool {
strings.Contains(stderr, "tailnet policy does not permit you to SSH")
}
func sshScenario(t *testing.T, policy *policyv2.Policy, clientsPerUser int) *Scenario {
func sshScenario(t *testing.T, policy *policyv2.Policy, testName string, clientsPerUser int) *Scenario {
t.Helper()
spec := ScenarioSpec{
@@ -50,7 +50,7 @@ func sshScenario(t *testing.T, policy *policyv2.Policy, clientsPerUser int) *Sce
tsic.WithDockerWorkdir("/"),
},
hsic.WithACLPolicy(policy),
hsic.WithTestName("ssh"),
hsic.WithTestName(testName),
)
require.NoError(t, err)
@@ -95,6 +95,7 @@ func TestSSHOneUserToAll(t *testing.T) {
},
},
},
"ssh-onetoall",
len(MustTestVersions),
)
defer scenario.ShutdownAssertNoPanics(t)
@@ -168,6 +169,7 @@ func TestSSHMultipleUsersAllToAll(t *testing.T) {
},
},
},
"ssh-multiall",
len(MustTestVersions),
)
defer scenario.ShutdownAssertNoPanics(t)
@@ -242,6 +244,7 @@ func TestSSHNoSSHConfigured(t *testing.T) {
},
SSHs: []policyv2.SSH{},
},
"ssh-nosshcfg",
len(MustTestVersions),
)
defer scenario.ShutdownAssertNoPanics(t)
@@ -293,6 +296,7 @@ func TestSSHIsBlockedInACL(t *testing.T) {
},
},
},
"ssh-blocked",
len(MustTestVersions),
)
defer scenario.ShutdownAssertNoPanics(t)
@@ -354,6 +358,7 @@ func TestSSHUserOnlyIsolation(t *testing.T) {
},
},
},
"ssh-isolation",
len(MustTestVersions),
)
defer scenario.ShutdownAssertNoPanics(t)
@@ -571,6 +576,7 @@ func TestSSHAutogroupSelf(t *testing.T) {
},
},
},
"ssh-agself",
2, // 2 clients per user
)
defer scenario.ShutdownAssertNoPanics(t)
@@ -811,7 +817,7 @@ func findNewSSHCheckAuthID(
func TestSSHOneUserToOneCheckModeCLI(t *testing.T) {
IntegrationSkip(t)
scenario := sshScenario(t, sshCheckPolicy(), 1)
scenario := sshScenario(t, sshCheckPolicy(), "ssh-checkcli", 1)
// defer scenario.ShutdownAssertNoPanics(t)
allClients, err := scenario.ListTailscaleClients()
@@ -920,7 +926,6 @@ func TestSSHOneUserToOneCheckModeOIDC(t *testing.T) {
hsic.WithACLPolicy(sshCheckPolicy()),
hsic.WithTestName("sshcheckoidc"),
hsic.WithConfigEnv(oidcMap),
hsic.WithTLS(),
hsic.WithFileInContainer(
"/tmp/hs_client_oidc_secret",
[]byte(scenario.mockOIDC.ClientSecret()),
@@ -1087,7 +1092,7 @@ func TestSSHCheckModeCheckPeriodCLI(t *testing.T) {
IntegrationSkip(t)
// 1 minute is the documented minimum checkPeriod
scenario := sshScenario(t, sshCheckPolicyWithPeriod(time.Minute), 1)
scenario := sshScenario(t, sshCheckPolicyWithPeriod(time.Minute), "ssh-checkperiod", 1)
defer scenario.ShutdownAssertNoPanics(t)
allClients, err := scenario.ListTailscaleClients()
@@ -1182,7 +1187,7 @@ func TestSSHCheckModeAutoApprove(t *testing.T) {
IntegrationSkip(t)
// 5 minute checkPeriod — long enough not to expire during test
scenario := sshScenario(t, sshCheckPolicyWithPeriod(5*time.Minute), 1)
scenario := sshScenario(t, sshCheckPolicyWithPeriod(5*time.Minute), "ssh-autoapprove", 1)
defer scenario.ShutdownAssertNoPanics(t)
allClients, err := scenario.ListTailscaleClients()
@@ -1247,7 +1252,7 @@ func TestSSHCheckModeAutoApprove(t *testing.T) {
func TestSSHCheckModeNegativeCLI(t *testing.T) {
IntegrationSkip(t)
scenario := sshScenario(t, sshCheckPolicy(), 1)
scenario := sshScenario(t, sshCheckPolicy(), "ssh-negcli", 1)
defer scenario.ShutdownAssertNoPanics(t)
allClients, err := scenario.ListTailscaleClients()
@@ -1509,7 +1514,6 @@ func TestSSHLocalpart(t *testing.T) {
hsic.WithTestName("sshlocalpart"),
hsic.WithACLPolicy(tt.policy),
hsic.WithConfigEnv(oidcMap),
hsic.WithTLS(),
hsic.WithFileInContainer("/tmp/hs_client_oidc_secret", []byte(scenario.mockOIDC.ClientSecret())),
)
requireNoErrHeadscaleEnv(t, err)

View File

@@ -138,7 +138,6 @@ func TestTagsAuthKeyWithTagRequestDifferentTag(t *testing.T) {
[]tsic.Option{},
hsic.WithACLPolicy(policy),
hsic.WithTestName("tags-authkey-diff"),
hsic.WithTLS(),
)
requireNoErrHeadscaleEnv(t, err)
@@ -213,7 +212,6 @@ func TestTagsAuthKeyWithTagNoAdvertiseFlag(t *testing.T) {
[]tsic.Option{},
hsic.WithACLPolicy(policy),
hsic.WithTestName("tags-authkey-inherit"),
hsic.WithTLS(),
)
requireNoErrHeadscaleEnv(t, err)
@@ -286,7 +284,6 @@ func TestTagsAuthKeyWithTagCannotAddViaCLI(t *testing.T) {
[]tsic.Option{},
hsic.WithACLPolicy(policy),
hsic.WithTestName("tags-authkey-noadd"),
hsic.WithTLS(),
)
requireNoErrHeadscaleEnv(t, err)
@@ -387,7 +384,6 @@ func TestTagsAuthKeyWithTagCannotChangeViaCLI(t *testing.T) {
[]tsic.Option{},
hsic.WithACLPolicy(policy),
hsic.WithTestName("tags-authkey-nochange"),
hsic.WithTLS(),
)
requireNoErrHeadscaleEnv(t, err)
@@ -484,7 +480,6 @@ func TestTagsAuthKeyWithTagAdminOverrideReauthPreserves(t *testing.T) {
[]tsic.Option{},
hsic.WithACLPolicy(policy),
hsic.WithTestName("tags-authkey-admin"),
hsic.WithTLS(),
)
requireNoErrHeadscaleEnv(t, err)
@@ -612,7 +607,6 @@ func TestTagsAuthKeyWithTagCLICannotModifyAdminTags(t *testing.T) {
[]tsic.Option{},
hsic.WithACLPolicy(policy),
hsic.WithTestName("tags-authkey-noadmin"),
hsic.WithTLS(),
)
requireNoErrHeadscaleEnv(t, err)
@@ -735,7 +729,6 @@ func TestTagsAuthKeyWithoutTagCannotRequestTags(t *testing.T) {
[]tsic.Option{},
hsic.WithACLPolicy(policy),
hsic.WithTestName("tags-nokey-req"),
hsic.WithTLS(),
)
requireNoErrHeadscaleEnv(t, err)
@@ -807,7 +800,6 @@ func TestTagsAuthKeyWithoutTagRegisterNoTags(t *testing.T) {
[]tsic.Option{},
hsic.WithACLPolicy(policy),
hsic.WithTestName("tags-nokey-noreg"),
hsic.WithTLS(),
)
requireNoErrHeadscaleEnv(t, err)
@@ -877,7 +869,6 @@ func TestTagsAuthKeyWithoutTagCannotAddViaCLI(t *testing.T) {
[]tsic.Option{},
hsic.WithACLPolicy(policy),
hsic.WithTestName("tags-nokey-noadd"),
hsic.WithTLS(),
)
requireNoErrHeadscaleEnv(t, err)
@@ -977,7 +968,6 @@ func TestTagsAuthKeyWithoutTagCLINoOpAfterAdminWithReset(t *testing.T) {
[]tsic.Option{},
hsic.WithACLPolicy(policy),
hsic.WithTestName("tags-nokey-reset"),
hsic.WithTLS(),
)
requireNoErrHeadscaleEnv(t, err)
@@ -1098,7 +1088,6 @@ func TestTagsAuthKeyWithoutTagCLINoOpAfterAdminWithEmptyAdvertise(t *testing.T)
[]tsic.Option{},
hsic.WithACLPolicy(policy),
hsic.WithTestName("tags-nokey-empty"),
hsic.WithTLS(),
)
requireNoErrHeadscaleEnv(t, err)
@@ -1218,7 +1207,6 @@ func TestTagsAuthKeyWithoutTagCLICannotReduceAdminMultiTag(t *testing.T) {
[]tsic.Option{},
hsic.WithACLPolicy(policy),
hsic.WithTestName("tags-nokey-reduce"),
hsic.WithTLS(),
)
requireNoErrHeadscaleEnv(t, err)
@@ -1340,7 +1328,6 @@ func TestTagsUserLoginOwnedTagAtRegistration(t *testing.T) {
},
hsic.WithACLPolicy(policy),
hsic.WithTestName("tags-webauth-owned"),
hsic.WithTLS(),
)
requireNoErrHeadscaleEnv(t, err)
@@ -1411,7 +1398,6 @@ func TestTagsUserLoginNonExistentTagAtRegistration(t *testing.T) {
[]tsic.Option{},
hsic.WithACLPolicy(policy),
hsic.WithTestName("tags-webauth-nonexist"),
hsic.WithTLS(),
)
requireNoErrHeadscaleEnv(t, err)
@@ -1484,7 +1470,6 @@ func TestTagsUserLoginUnownedTagAtRegistration(t *testing.T) {
[]tsic.Option{},
hsic.WithACLPolicy(policy),
hsic.WithTestName("tags-webauth-unowned"),
hsic.WithTLS(),
)
requireNoErrHeadscaleEnv(t, err)
@@ -1554,7 +1539,6 @@ func TestTagsUserLoginAddTagViaCLIReauth(t *testing.T) {
[]tsic.Option{},
hsic.WithACLPolicy(policy),
hsic.WithTestName("tags-webauth-addtag"),
hsic.WithTLS(),
)
requireNoErrHeadscaleEnv(t, err)
@@ -1646,7 +1630,6 @@ func TestTagsUserLoginRemoveTagViaCLIReauth(t *testing.T) {
[]tsic.Option{},
hsic.WithACLPolicy(policy),
hsic.WithTestName("tags-webauth-rmtag"),
hsic.WithTLS(),
)
requireNoErrHeadscaleEnv(t, err)
@@ -1738,7 +1721,6 @@ func TestTagsUserLoginCLINoOpAfterAdminAssignment(t *testing.T) {
[]tsic.Option{},
hsic.WithACLPolicy(policy),
hsic.WithTestName("tags-webauth-adminwin"),
hsic.WithTLS(),
)
requireNoErrHeadscaleEnv(t, err)
@@ -1856,7 +1838,6 @@ func TestTagsUserLoginCLICannotRemoveAdminTags(t *testing.T) {
[]tsic.Option{},
hsic.WithACLPolicy(policy),
hsic.WithTestName("tags-webauth-norem"),
hsic.WithTLS(),
)
requireNoErrHeadscaleEnv(t, err)
@@ -1974,7 +1955,6 @@ func TestTagsAuthKeyWithTagRequestNonExistentTag(t *testing.T) {
[]tsic.Option{},
hsic.WithACLPolicy(policy),
hsic.WithTestName("tags-authkey-nonexist"),
hsic.WithTLS(),
)
requireNoErrHeadscaleEnv(t, err)
@@ -2045,7 +2025,6 @@ func TestTagsAuthKeyWithTagRequestUnownedTag(t *testing.T) {
[]tsic.Option{},
hsic.WithACLPolicy(policy),
hsic.WithTestName("tags-authkey-unowned"),
hsic.WithTLS(),
)
requireNoErrHeadscaleEnv(t, err)
@@ -2120,7 +2099,6 @@ func TestTagsAuthKeyWithoutTagRequestNonExistentTag(t *testing.T) {
[]tsic.Option{},
hsic.WithACLPolicy(policy),
hsic.WithTestName("tags-nokey-nonexist"),
hsic.WithTLS(),
)
requireNoErrHeadscaleEnv(t, err)
@@ -2191,7 +2169,6 @@ func TestTagsAuthKeyWithoutTagRequestUnownedTag(t *testing.T) {
[]tsic.Option{},
hsic.WithACLPolicy(policy),
hsic.WithTestName("tags-nokey-unowned"),
hsic.WithTLS(),
)
requireNoErrHeadscaleEnv(t, err)
@@ -2266,7 +2243,6 @@ func TestTagsAdminAPICannotSetNonExistentTag(t *testing.T) {
[]tsic.Option{},
hsic.WithACLPolicy(policy),
hsic.WithTestName("tags-admin-nonexist"),
hsic.WithTLS(),
)
requireNoErrHeadscaleEnv(t, err)
@@ -2339,7 +2315,6 @@ func TestTagsAdminAPICanSetUnownedTag(t *testing.T) {
[]tsic.Option{},
hsic.WithACLPolicy(policy),
hsic.WithTestName("tags-admin-unowned"),
hsic.WithTLS(),
)
requireNoErrHeadscaleEnv(t, err)
@@ -2428,7 +2403,6 @@ func TestTagsAdminAPICannotRemoveAllTags(t *testing.T) {
[]tsic.Option{},
hsic.WithACLPolicy(policy),
hsic.WithTestName("tags-admin-empty"),
hsic.WithTLS(),
)
requireNoErrHeadscaleEnv(t, err)
@@ -2546,7 +2520,6 @@ func TestTagsIssue2978ReproTagReplacement(t *testing.T) {
},
hsic.WithACLPolicy(policy),
hsic.WithTestName("tags-issue-2978"),
hsic.WithTLS(),
)
requireNoErrHeadscaleEnv(t, err)
@@ -2752,7 +2725,6 @@ func TestTagsAdminAPICannotSetInvalidFormat(t *testing.T) {
[]tsic.Option{},
hsic.WithACLPolicy(policy),
hsic.WithTestName("tags-admin-invalid"),
hsic.WithTLS(),
)
requireNoErrHeadscaleEnv(t, err)
@@ -2853,7 +2825,6 @@ func TestTagsUserLoginReauthWithEmptyTagsRemovesAllTags(t *testing.T) {
[]tsic.Option{},
hsic.WithACLPolicy(policy),
hsic.WithTestName("tags-reauth-untag-2979-"+tc.testName),
hsic.WithTLS(),
)
requireNoErrHeadscaleEnv(t, err)
@@ -3008,7 +2979,6 @@ func TestTagsAuthKeyWithoutUserInheritsTags(t *testing.T) {
[]tsic.Option{},
hsic.WithACLPolicy(policy),
hsic.WithTestName("tags-authkey-no-user-inherit"),
hsic.WithTLS(),
)
requireNoErrHeadscaleEnv(t, err)
@@ -3080,7 +3050,6 @@ func TestTagsAuthKeyWithoutUserRejectsAdvertisedTags(t *testing.T) {
[]tsic.Option{},
hsic.WithACLPolicy(policy),
hsic.WithTestName("tags-authkey-no-user-reject-advertise"),
hsic.WithTLS(),
)
requireNoErrHeadscaleEnv(t, err)
@@ -3147,7 +3116,6 @@ func TestTagsAuthKeyConvertToUserViaCLIRegister(t *testing.T) {
[]tsic.Option{},
hsic.WithACLPolicy(policy),
hsic.WithTestName("tags-authkey-to-user-cli-3038"),
hsic.WithTLS(),
)
requireNoErrHeadscaleEnv(t, err)