Skip to content

Commit

Permalink
General fixups discovered by checking errors
Browse files Browse the repository at this point in the history
There was a lot of tests that actually threw a lot of errors and that did
not pass all the way because we didnt check everything. This commit should
fix all of these cases.

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
  • Loading branch information
kradalby committed Sep 5, 2023
1 parent f8a58aa commit 1766e6b
Show file tree
Hide file tree
Showing 11 changed files with 134 additions and 64 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# DO NOT EDIT, generated with cmd/gh-action-integration-generator/main.go
# To regenerate, run "go generate" in cmd/gh-action-integration-generator/

name: Integration Test v2 - TestSSHOneUserAllToAll
name: Integration Test v2 - TestSSHOneUserToAll

on: [pull_request]

Expand All @@ -10,7 +10,7 @@ concurrency:
cancel-in-progress: true

jobs:
TestSSHOneUserAllToAll:
TestSSHOneUserToAll:
runs-on: ubuntu-latest

steps:
Expand All @@ -34,7 +34,7 @@ jobs:
integration_test/
config-example.yaml
- name: Run TestSSHOneUserAllToAll
- name: Run TestSSHOneUserToAll
if: steps.changed-files.outputs.any_changed == 'true'
run: |
nix develop --command -- docker run \
Expand All @@ -50,7 +50,7 @@ jobs:
-failfast \
-timeout 120m \
-parallel 1 \
-run "^TestSSHOneUserAllToAll$"
-run "^TestSSHOneUserToAll$"
- uses: actions/upload-artifact@v3
if: always() && steps.changed-files.outputs.any_changed == 'true'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# DO NOT EDIT, generated with cmd/gh-action-integration-generator/main.go
# To regenerate, run "go generate" in cmd/gh-action-integration-generator/

name: Integration Test v2 - TestSSUserOnlyIsolation
name: Integration Test v2 - TestSSHUserOnlyIsolation

on: [pull_request]

Expand All @@ -10,7 +10,7 @@ concurrency:
cancel-in-progress: true

jobs:
TestSSUserOnlyIsolation:
TestSSHUserOnlyIsolation:
runs-on: ubuntu-latest

steps:
Expand All @@ -34,7 +34,7 @@ jobs:
integration_test/
config-example.yaml
- name: Run TestSSUserOnlyIsolation
- name: Run TestSSHUserOnlyIsolation
if: steps.changed-files.outputs.any_changed == 'true'
run: |
nix develop --command -- docker run \
Expand All @@ -50,7 +50,7 @@ jobs:
-failfast \
-timeout 120m \
-parallel 1 \
-run "^TestSSUserOnlyIsolation$"
-run "^TestSSHUserOnlyIsolation$"
- uses: actions/upload-artifact@v3
if: always() && steps.changed-files.outputs.any_changed == 'true'
Expand Down
1 change: 1 addition & 0 deletions .golangci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ linters:
- exhaustruct
- nolintlint
- musttag # causes issues with imported libs
- depguard

# deprecated
- structcheck # replaced by unused
Expand Down
2 changes: 2 additions & 0 deletions hscontrol/types/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,8 @@ func GetDNSConfig() (*tailcfg.DNSConfig, string) {
baseDomain = "headscale.net" // does not really matter when MagicDNS is not enabled
}

log.Trace().Interface("dns_config", dnsConfig).Msg("DNS configuration loaded")

return dnsConfig, baseDomain
}

Expand Down
4 changes: 4 additions & 0 deletions integration/acl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ func aclScenario(

err = scenario.CreateHeadscaleEnv(spec,
[]tsic.Option{
// Alpine containers dont have ip6tables set up, which causes
// tailscaled to stop configuring the wgengine, causing it
// to not configure DNS.
tsic.WithNetfilter("off"),
tsic.WithDockerEntrypoint([]string{
"/bin/sh",
"-c",
Expand Down
74 changes: 53 additions & 21 deletions integration/auth_oidc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"github.com/ory/dockertest/v3"
"github.com/ory/dockertest/v3/docker"
"github.com/samber/lo"
"github.com/stretchr/testify/assert"
)

const (
Expand Down Expand Up @@ -90,6 +91,7 @@ func TestOIDCAuthenticationPingAll(t *testing.T) {
t.Logf("%d successful pings out of %d", success, len(allClients)*len(allIps))
}

// This test is really flaky.
func TestOIDCExpireNodesBasedOnTokenExpiry(t *testing.T) {
IntegrationSkip(t)
t.Parallel()
Expand All @@ -99,13 +101,15 @@ func TestOIDCExpireNodesBasedOnTokenExpiry(t *testing.T) {
baseScenario, err := NewScenario()
assertNoErr(t, err)

baseScenario.pool.MaxWait = 5 * time.Minute

scenario := AuthOIDCScenario{
Scenario: baseScenario,
}
defer scenario.Shutdown()

spec := map[string]int{
"user1": len(MustTestVersions),
"user1": 3,
}

oidcConfig, err := scenario.runMockOIDC(shortAccessTTL)
Expand Down Expand Up @@ -143,9 +147,13 @@ func TestOIDCExpireNodesBasedOnTokenExpiry(t *testing.T) {
success := pingAllHelper(t, allClients, allAddrs)
t.Logf("%d successful pings out of %d (before expiry)", success, len(allClients)*len(allIps))

// await all nodes being logged out after OIDC token expiry
err = scenario.WaitForTailscaleLogout()
assertNoErrLogout(t, err)
// This is not great, but this sadly is a time dependent test, so the
// safe thing to do is wait out the whole TTL time before checking if
// the clients have logged out. The Wait function cant do it itself
// as it has an upper bound of 1 min.
time.Sleep(shortAccessTTL)

assertTailscaleNodesLogout(t, allClients)
}

func (s *AuthOIDCScenario) CreateHeadscaleEnv(
Expand Down Expand Up @@ -296,27 +304,40 @@ func (s *AuthOIDCScenario) runTailscaleUp(

log.Printf("%s login url: %s\n", c.Hostname(), loginURL.String())

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 {
log.Printf(
"%s failed to get login url %s: %s",
c.Hostname(),
loginURL,
err,
)
if err := s.pool.Retry(func() error {
log.Printf("%s logging in with url", c.Hostname())
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 {
log.Printf(
"%s failed to login using url %s: %s",
c.Hostname(),
loginURL,
err,
)

return err
}
return err
}

defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
log.Printf("%s response code of oidc login request was %s", c.Hostname(), resp.Status)

_, err = io.ReadAll(resp.Body)
if err != nil {
log.Printf("%s failed to read response body: %s", c.Hostname(), err)
return errStatusCodeNotOK
}

defer resp.Body.Close()

_, err = io.ReadAll(resp.Body)
if err != nil {
log.Printf("%s failed to read response body: %s", c.Hostname(), err)

return err
}

return nil
}); err != nil {
return err
}

Expand Down Expand Up @@ -357,3 +378,14 @@ func (s *AuthOIDCScenario) Shutdown() {

s.Scenario.Shutdown()
}

func assertTailscaleNodesLogout(t *testing.T, clients []TailscaleClient) {
t.Helper()

for _, client := range clients {
status, err := client.Status()
assertNoErr(t, err)

assert.Equal(t, "NeedsLogin", status.BackendState)
}
}
8 changes: 4 additions & 4 deletions integration/scenario.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,14 +78,14 @@ var (
)

// MustTestVersions is the minimum set of versions we should test.
// At the moment, this is arbitrarily choosen as:
// At the moment, this is arbitrarily chosen as:
//
// - Two unstable (HEAD and unstable)
// - Two latest versions
// - Two oldest versions
// - Two oldest versions.
MustTestVersions = append(
tailscaleVersions2021[0:4],
tailscaleVersions2019[len(tailscaleVersions2019)-2:len(tailscaleVersions2019)]...,
tailscaleVersions2019[len(tailscaleVersions2019)-2:]...,
)
)

Expand Down Expand Up @@ -573,7 +573,7 @@ func (s *Scenario) WaitForTailscaleLogout() error {
for _, client := range user.Clients {
c := client
user.syncWaitGroup.Go(func() error {
return c.WaitForLogout()
return c.WaitForNeedsLogin()
})
}
if err := user.syncWaitGroup.Wait(); err != nil {
Expand Down
53 changes: 40 additions & 13 deletions integration/ssh_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package integration

import (
"fmt"
"log"
"strings"
"testing"
"time"
Expand Down Expand Up @@ -53,10 +54,16 @@ func sshScenario(t *testing.T, policy *policy.ACLPolicy, clientsPerUser int) *Sc

err = scenario.CreateHeadscaleEnv(spec,
[]tsic.Option{
tsic.WithSSH(),

// Alpine containers dont have ip6tables set up, which causes
// tailscaled to stop configuring the wgengine, causing it
// to not configure DNS.
tsic.WithNetfilter("off"),
tsic.WithDockerEntrypoint([]string{
"/bin/sh",
"-c",
"/bin/sleep 3 ; apk add openssh ; update-ca-certificates ; tailscaled --tun=tsdev",
"/bin/sleep 3 ; apk add openssh ; adduser ssh-it-user ; update-ca-certificates ; tailscaled --tun=tsdev",
}),
tsic.WithDockerWorkdir("/"),
},
Expand All @@ -77,7 +84,7 @@ func sshScenario(t *testing.T, policy *policy.ACLPolicy, clientsPerUser int) *Sc
return scenario
}

func TestSSHOneUserAllToAll(t *testing.T) {
func TestSSHOneUserToAll(t *testing.T) {
IntegrationSkip(t)
t.Parallel()

Expand All @@ -97,7 +104,7 @@ func TestSSHOneUserAllToAll(t *testing.T) {
{
Action: "accept",
Sources: []string{"group:integration-test"},
Destinations: []string{"group:integration-test"},
Destinations: []string{"*"},
Users: []string{"ssh-it-user"},
},
},
Expand All @@ -109,13 +116,19 @@ func TestSSHOneUserAllToAll(t *testing.T) {
allClients, err := scenario.ListTailscaleClients()
assertNoErrListClients(t, err)

user1Clients, err := scenario.ListTailscaleClients("user1")
assertNoErrListClients(t, err)

user2Clients, err := scenario.ListTailscaleClients("user2")
assertNoErrListClients(t, err)

err = scenario.WaitForTailscaleSync()
assertNoErrSync(t, err)

_, err = scenario.ListTailscaleClientsFQDNs()
assertNoErrListFQDN(t, err)

for _, client := range allClients {
for _, client := range user1Clients {
for _, peer := range allClients {
if client.Hostname() == peer.Hostname() {
continue
Expand All @@ -124,6 +137,16 @@ func TestSSHOneUserAllToAll(t *testing.T) {
assertSSHHostname(t, client, peer)
}
}

for _, client := range user2Clients {
for _, peer := range allClients {
if client.Hostname() == peer.Hostname() {
continue
}

assertSSHPermissionDenied(t, client, peer)
}
}
}

func TestSSHMultipleUsersAllToAll(t *testing.T) {
Expand Down Expand Up @@ -270,7 +293,7 @@ func TestSSHIsBlockedInACL(t *testing.T) {
}
}

func TestSSUserOnlyIsolation(t *testing.T) {
func TestSSHUserOnlyIsolation(t *testing.T) {
IntegrationSkip(t)
t.Parallel()

Expand Down Expand Up @@ -365,11 +388,14 @@ func doSSH(t *testing.T, client TailscaleClient, peer TailscaleClient) (string,
peerFQDN, _ := peer.FQDN()

command := []string{
"ssh", "-o StrictHostKeyChecking=no", "-o ConnectTimeout=1",
"/usr/bin/ssh", "-o StrictHostKeyChecking=no", "-o ConnectTimeout=1",
fmt.Sprintf("%s@%s", "ssh-it-user", peerFQDN),
"'hostname'",
}

log.Printf("Running from %s to %s", client.Hostname(), peer.Hostname())
log.Printf("Command: %s", strings.Join(command, " "))

return retry(10, 1*time.Second, func() (string, string, error) {
return client.Execute(command)
})
Expand All @@ -381,27 +407,28 @@ func assertSSHHostname(t *testing.T, client TailscaleClient, peer TailscaleClien
result, _, err := doSSH(t, client, peer)
assertNoErr(t, err)

assert.Contains(t, peer.ID(), strings.ReplaceAll(result, "\n", ""))
assertContains(t, peer.ID(), strings.ReplaceAll(result, "\n", ""))
}

func assertSSHPermissionDenied(t *testing.T, client TailscaleClient, peer TailscaleClient) {
t.Helper()

result, stderr, err := doSSH(t, client, peer)
assert.Error(t, err)
result, stderr, _ := doSSH(t, client, peer)

assert.Empty(t, result)

assert.Contains(t, stderr, "Permission denied (tailscale)")
assertContains(t, stderr, "Permission denied (tailscale)")
}

func assertSSHTimeout(t *testing.T, client TailscaleClient, peer TailscaleClient) {
t.Helper()

result, stderr, err := doSSH(t, client, peer)
assertNoErr(t, err)
result, stderr, _ := doSSH(t, client, peer)

assert.Empty(t, result)

assert.Contains(t, stderr, "Connection timed out")
if !strings.Contains(stderr, "Connection timed out") &&
!strings.Contains(stderr, "Operation timed out") {
t.Fatalf("connection did not time out")
}
}

0 comments on commit 1766e6b

Please sign in to comment.