Skip to content

Commit

Permalink
add integration test with nodes
Browse files Browse the repository at this point in the history
Signed-off-by: Kristoffer Dalby <[email protected]>
  • Loading branch information
kradalby committed Jul 15, 2024
1 parent edd36f3 commit 37a9798
Show file tree
Hide file tree
Showing 3 changed files with 157 additions and 2 deletions.
2 changes: 1 addition & 1 deletion hscontrol/types/policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (

var (
ErrPolicyNotFound = errors.New("acl policy not found")
ErrPolicyUpdateIsDisabled = errors.New("update is disabled for modes other than 'db'")
ErrPolicyUpdateIsDisabled = errors.New("update is disabled for modes other than 'database'")
)

// Policy represents a policy in the database.
Expand Down
155 changes: 155 additions & 0 deletions integration/acl_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package integration

import (
"encoding/json"
"fmt"
"net/netip"
"strings"
"testing"

"github.com/google/go-cmp/cmp"
"github.com/juanfont/headscale/hscontrol/policy"
"github.com/juanfont/headscale/integration/hsic"
"github.com/juanfont/headscale/integration/tsic"
Expand Down Expand Up @@ -1012,3 +1014,156 @@ func TestACLDevice1CanAccessDevice2(t *testing.T) {
})
}
}

func TestPolicyUpdateWhileRunningWithCLIInDatabase(t *testing.T) {
IntegrationSkip(t)
t.Parallel()

scenario, err := NewScenario(dockertestMaxWait())
assertNoErr(t, err)
defer scenario.Shutdown()

spec := map[string]int{
"user1": 1,
"user2": 1,
}

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",
"/bin/sleep 3 ; apk add python3 curl ; update-ca-certificates ; python3 -m http.server --bind :: 80 & tailscaled --tun=tsdev",
}),
tsic.WithDockerWorkdir("/"),
},
hsic.WithTestName("policyreload"),
hsic.WithConfigEnv(map[string]string{
"HEADSCALE_POLICY_MODE": "database",
}),
)
assertNoErr(t, err)

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

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

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

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

all := append(user1Clients, user2Clients...)

// Initially all nodes can reach each other
for _, client := range all {
for _, peer := range all {
if client.ID() == peer.ID() {
continue
}

fqdn, err := peer.FQDN()
assertNoErr(t, err)

url := fmt.Sprintf("http://%s/etc/hostname", fqdn)
t.Logf("url from %s to %s", client.Hostname(), url)

result, err := client.Curl(url)
assert.Len(t, result, 13)
assertNoErr(t, err)
}
}

headscale, err := scenario.Headscale()
assertNoErr(t, err)

p := policy.ACLPolicy{
ACLs: []policy.ACL{
{
Action: "accept",
Sources: []string{"user1"},
Destinations: []string{"user2:*"},
},
},
Hosts: policy.Hosts{},
}

pBytes, _ := json.Marshal(p)

policyFilePath := "/etc/headscale/policy.json"

err = headscale.WriteFile(policyFilePath, pBytes)
assertNoErr(t, err)

// No policy is present at this time.
// Add a new policy from a file.
_, err = headscale.Execute(
[]string{
"headscale",
"policy",
"set",
"-f",
policyFilePath,
},
)
assertNoErr(t, err)

// Get the current policy and check
// if it is the same as the one we set.
var output *policy.ACLPolicy
err = executeAndUnmarshal(
headscale,
[]string{
"headscale",
"policy",
"get",
"--output",
"json",
},
&output,
)
assertNoErr(t, err)

assert.Len(t, output.ACLs, 1)

if diff := cmp.Diff(p, *output); diff != "" {
t.Errorf("unexpected policy(-want +got):\n%s", diff)
}

// Test that user1 can visit all user2
for _, client := range user1Clients {
for _, peer := range user2Clients {
fqdn, err := peer.FQDN()
assertNoErr(t, err)

url := fmt.Sprintf("http://%s/etc/hostname", fqdn)
t.Logf("url from %s to %s", client.Hostname(), url)

result, err := client.Curl(url)
assert.Len(t, result, 13)
assertNoErr(t, err)
}
}

// Test that user2 _cannot_ visit user1
for _, client := range user2Clients {
for _, peer := range user1Clients {
fqdn, err := peer.FQDN()
assertNoErr(t, err)

url := fmt.Sprintf("http://%s/etc/hostname", fqdn)
t.Logf("url from %s to %s", client.Hostname(), url)

result, err := client.Curl(url)
assert.Empty(t, result)
assert.Error(t, err)
}
}
}
2 changes: 1 addition & 1 deletion integration/hsic/hsic.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ type Option = func(c *HeadscaleInContainer)
func WithACLPolicy(acl *policy.ACLPolicy) Option {
return func(hsic *HeadscaleInContainer) {
// TODO(kradalby): Move somewhere appropriate
hsic.env["HEADSCALE_ACL_POLICY_PATH"] = aclPolicyPath
hsic.env["HEADSCALE_POLICY_PATH"] = aclPolicyPath

hsic.aclPolicy = acl
}
Expand Down

0 comments on commit 37a9798

Please sign in to comment.