mirror of
https://github.com/juanfont/headscale.git
synced 2025-11-10 05:59:38 -05:00
feat: add verify client config for embedded DERP (#2260)
* feat: add verify client config for embedded DERP * refactor: embedded DERP no longer verify clients via HTTP - register the `headscale://` protocol in `http.DefaultTransport` to intercept network requests - update configuration to use a single boolean option `verify_clients` * refactor: use `http.HandlerFunc` for type definition * refactor: some renaming and restructuring * chore: some renaming and fix lint * test: fix TestDERPVerifyEndpoint - `tailscale debug derp` use random node private key * test: add verify clients integration test for embedded DERP server * fix: apply code review suggestions * chore: merge upstream changes * fix: apply code review suggestions --------- Co-authored-by: Kristoffer Dalby <kristoffer@dalby.cc>
This commit is contained in:
@@ -1,11 +1,10 @@
|
||||
package integration
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/juanfont/headscale/hscontrol/util"
|
||||
@@ -13,7 +12,11 @@ import (
|
||||
"github.com/juanfont/headscale/integration/hsic"
|
||||
"github.com/juanfont/headscale/integration/integrationutil"
|
||||
"github.com/juanfont/headscale/integration/tsic"
|
||||
"tailscale.com/derp"
|
||||
"tailscale.com/derp/derphttp"
|
||||
"tailscale.com/net/netmon"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/types/key"
|
||||
)
|
||||
|
||||
func TestDERPVerifyEndpoint(t *testing.T) {
|
||||
@@ -46,23 +49,24 @@ func TestDERPVerifyEndpoint(t *testing.T) {
|
||||
)
|
||||
assertNoErr(t, err)
|
||||
|
||||
derpRegion := tailcfg.DERPRegion{
|
||||
RegionCode: "test-derpverify",
|
||||
RegionName: "TestDerpVerify",
|
||||
Nodes: []*tailcfg.DERPNode{
|
||||
{
|
||||
Name: "TestDerpVerify",
|
||||
RegionID: 900,
|
||||
HostName: derper.GetHostname(),
|
||||
STUNPort: derper.GetSTUNPort(),
|
||||
STUNOnly: false,
|
||||
DERPPort: derper.GetDERPPort(),
|
||||
InsecureForTests: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
derpMap := tailcfg.DERPMap{
|
||||
Regions: map[int]*tailcfg.DERPRegion{
|
||||
900: {
|
||||
RegionID: 900,
|
||||
RegionCode: "test-derpverify",
|
||||
RegionName: "TestDerpVerify",
|
||||
Nodes: []*tailcfg.DERPNode{
|
||||
{
|
||||
Name: "TestDerpVerify",
|
||||
RegionID: 900,
|
||||
HostName: derper.GetHostname(),
|
||||
STUNPort: derper.GetSTUNPort(),
|
||||
STUNOnly: false,
|
||||
DERPPort: derper.GetDERPPort(),
|
||||
},
|
||||
},
|
||||
},
|
||||
900: &derpRegion,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -76,21 +80,42 @@ func TestDERPVerifyEndpoint(t *testing.T) {
|
||||
allClients, err := scenario.ListTailscaleClients()
|
||||
assertNoErrListClients(t, err)
|
||||
|
||||
for _, client := range allClients {
|
||||
report, err := client.DebugDERPRegion("test-derpverify")
|
||||
assertNoErr(t, err)
|
||||
successful := false
|
||||
for _, line := range report.Info {
|
||||
if strings.Contains(line, "Successfully established a DERP connection with node") {
|
||||
successful = true
|
||||
fakeKey := key.NewNode()
|
||||
DERPVerify(t, fakeKey, derpRegion, false)
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
if !successful {
|
||||
stJSON, err := json.Marshal(report)
|
||||
assertNoErr(t, err)
|
||||
t.Errorf("Client %s could not establish a DERP connection: %s", client.Hostname(), string(stJSON))
|
||||
}
|
||||
for _, client := range allClients {
|
||||
nodeKey, err := client.GetNodePrivateKey()
|
||||
assertNoErr(t, err)
|
||||
DERPVerify(t, *nodeKey, derpRegion, true)
|
||||
}
|
||||
}
|
||||
|
||||
func DERPVerify(
|
||||
t *testing.T,
|
||||
nodeKey key.NodePrivate,
|
||||
region tailcfg.DERPRegion,
|
||||
expectSuccess bool,
|
||||
) {
|
||||
t.Helper()
|
||||
|
||||
c := derphttp.NewRegionClient(nodeKey, t.Logf, netmon.NewStatic(), func() *tailcfg.DERPRegion {
|
||||
return ®ion
|
||||
})
|
||||
defer c.Close()
|
||||
|
||||
var result error
|
||||
if err := c.Connect(context.Background()); err != nil {
|
||||
result = fmt.Errorf("client Connect: %w", err)
|
||||
}
|
||||
if m, err := c.Recv(); err != nil {
|
||||
result = fmt.Errorf("client first Recv: %w", err)
|
||||
} else if v, ok := m.(derp.ServerInfoMessage); !ok {
|
||||
result = fmt.Errorf("client first Recv was unexpected type %T", v)
|
||||
}
|
||||
|
||||
if expectSuccess && result != nil {
|
||||
t.Fatalf("DERP verify failed unexpectedly for client %s. Expected success but got error: %v", nodeKey.Public(), result)
|
||||
} else if !expectSuccess && result == nil {
|
||||
t.Fatalf("DERP verify succeeded unexpectedly for client %s. Expected failure but it succeeded.", nodeKey.Public())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user