mirror of
https://github.com/juanfont/headscale.git
synced 2024-11-29 18:33:05 +00:00
Compare commits
12 commits
47eb7f644c
...
73e038fa5c
Author | SHA1 | Date | |
---|---|---|---|
|
73e038fa5c | ||
|
dfebd176df | ||
|
bb79471139 | ||
|
28a40c0019 | ||
|
b0f074d18d | ||
|
689c0983dc | ||
|
4f294c243c | ||
|
b3beb73f3f | ||
|
b6adc84dc6 | ||
|
c726224e53 | ||
|
a5f27b2265 | ||
|
13454a3f2d |
8 changed files with 93 additions and 99 deletions
2
.github/workflows/test-integration.yaml
vendored
2
.github/workflows/test-integration.yaml
vendored
|
@ -37,8 +37,8 @@ jobs:
|
|||
- TestNodeRenameCommand
|
||||
- TestNodeMoveCommand
|
||||
- TestPolicyCommand
|
||||
- TestDERPVerifyEndpoint
|
||||
- TestPolicyBrokenConfigCommand
|
||||
- TestDERPVerifyEndpoint
|
||||
- TestResolveMagicDNS
|
||||
- TestValidateResolvConf
|
||||
- TestDERPServerScenario
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# For testing purposes only
|
||||
|
||||
FROM golang:1.22-alpine AS build-env
|
||||
FROM golang:alpine AS build-env
|
||||
|
||||
WORKDIR /go/src
|
||||
|
||||
|
|
|
@ -64,52 +64,39 @@ func (h *Headscale) VerifyHandler(
|
|||
) {
|
||||
if req.Method != http.MethodPost {
|
||||
http.Error(writer, "Wrong method", http.StatusMethodNotAllowed)
|
||||
|
||||
return
|
||||
}
|
||||
log.Debug().
|
||||
Str("handler", "/verify").
|
||||
Msg("verify client")
|
||||
|
||||
body, err := io.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Str("handler", "/verify").
|
||||
Err(err).
|
||||
Msg("Cannot read request body")
|
||||
http.Error(writer, "Internal error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
var derpAdmitClientRequest tailcfg.DERPAdmitClientRequest
|
||||
if err := json.Unmarshal(body, &derpAdmitClientRequest); err != nil {
|
||||
log.Error().
|
||||
Caller().
|
||||
Err(err).
|
||||
Msg("Cannot parse derpAdmitClientRequest")
|
||||
http.Error(writer, "Internal error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
nodes, err := h.db.ListNodes()
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Caller().
|
||||
Err(err).
|
||||
Msg("Cannot list nodes")
|
||||
http.Error(writer, "Internal error", http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
for _, node := range nodes {
|
||||
log.Debug().Str("node", node.NodeKey.String()).Msg("Node")
|
||||
}
|
||||
|
||||
allow := false
|
||||
// Check if the node is in the list of nodes
|
||||
for _, node := range nodes {
|
||||
if node.NodeKey == derpAdmitClientRequest.NodePublic {
|
||||
allow = true
|
||||
break
|
||||
doVerify := func() (bool, error) {
|
||||
body, err := io.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("cannot read request body: %w", err)
|
||||
}
|
||||
|
||||
var derpAdmitClientRequest tailcfg.DERPAdmitClientRequest
|
||||
if err := json.Unmarshal(body, &derpAdmitClientRequest); err != nil {
|
||||
return false, fmt.Errorf("cannot parse derpAdmitClientRequest: %w", err)
|
||||
}
|
||||
|
||||
nodes, err := h.db.ListNodes()
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("cannot list nodes: %w", err)
|
||||
}
|
||||
|
||||
return nodes.ContainsNodeKey(derpAdmitClientRequest.NodePublic), nil
|
||||
}
|
||||
|
||||
allow, err := doVerify()
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Caller().
|
||||
Err(err).
|
||||
Msg("Failed to verify client")
|
||||
http.Error(writer, "Internal error", http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
resp := tailcfg.DERPAdmitClientResponse{
|
||||
|
|
|
@ -223,6 +223,16 @@ func (nodes Nodes) FilterByIP(ip netip.Addr) Nodes {
|
|||
return found
|
||||
}
|
||||
|
||||
func (nodes Nodes) ContainsNodeKey(nodeKey key.NodePublic) bool {
|
||||
for _, node := range nodes {
|
||||
if node.NodeKey == nodeKey {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (node *Node) Proto() *v1.Node {
|
||||
nodeProto := &v1.Node{
|
||||
Id: uint64(node.ID),
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
"github.com/juanfont/headscale/integration/hsic"
|
||||
"github.com/juanfont/headscale/integration/integrationutil"
|
||||
"github.com/juanfont/headscale/integration/tsic"
|
||||
"tailscale.com/tailcfg"
|
||||
)
|
||||
|
||||
func TestDERPVerifyEndpoint(t *testing.T) {
|
||||
|
@ -44,50 +45,34 @@ func TestDERPVerifyEndpoint(t *testing.T) {
|
|||
)
|
||||
assertNoErr(t, err)
|
||||
|
||||
derpConfig := "regions:\n"
|
||||
derpConfig += " 900:\n"
|
||||
derpConfig += " regionid: 900\n"
|
||||
derpConfig += " regioncode: test-derpverify\n"
|
||||
derpConfig += " regionname: TestDerpVerify\n"
|
||||
derpConfig += " nodes:\n"
|
||||
derpConfig += " - name: TestDerpVerify\n"
|
||||
derpConfig += " regionid: 900\n"
|
||||
derpConfig += " hostname: " + derper.GetHostname() + "\n"
|
||||
derpConfig += " stunport: " + derper.GetSTUNPort() + "\n"
|
||||
derpConfig += " stunonly: false\n"
|
||||
derpConfig += " derpport: " + derper.GetDERPPort() + "\n"
|
||||
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(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
headscale, err := scenario.Headscale(
|
||||
err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{tsic.WithCACert(derper.GetCert())},
|
||||
hsic.WithHostname(hostname),
|
||||
hsic.WithPort(headscalePort),
|
||||
hsic.WithCustomTLS(certHeadscale, keyHeadscale),
|
||||
hsic.WithHostnameAsServerURL(),
|
||||
hsic.WithCustomDERPServerOnly([]byte(derpConfig)),
|
||||
)
|
||||
hsic.WithDERPConfig(derpMap))
|
||||
assertNoErrHeadscaleEnv(t, err)
|
||||
|
||||
for userName, clientCount := range spec {
|
||||
err = scenario.CreateUser(userName)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create user %s: %s", userName, err)
|
||||
}
|
||||
|
||||
err = scenario.CreateTailscaleNodesInUser(userName, "all", clientCount, tsic.WithCACert(derper.GetCert()))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tailscale nodes in user %s: %s", userName, err)
|
||||
}
|
||||
|
||||
key, err := scenario.CreatePreAuthKey(userName, true, true)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create pre-auth key for user %s: %s", userName, err)
|
||||
}
|
||||
|
||||
err = scenario.RunTailscaleUp(userName, headscale.GetEndpoint(), key.GetKey())
|
||||
if err != nil {
|
||||
t.Fatalf("failed to run tailscale up for user %s: %s", userName, err)
|
||||
}
|
||||
}
|
||||
|
||||
allClients, err := scenario.ListTailscaleClients()
|
||||
assertNoErrListClients(t, err)
|
||||
|
||||
|
|
|
@ -72,7 +72,7 @@ func WithOrCreateNetwork(network *dockertest.Network) Option {
|
|||
|
||||
network, err := dockertestutil.GetFirstOrCreateNetwork(
|
||||
tsic.pool,
|
||||
fmt.Sprintf("%s-network", tsic.hostname),
|
||||
tsic.hostname+"-network",
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to create network: %s", err)
|
||||
|
@ -135,14 +135,15 @@ func New(
|
|||
opt(dsic)
|
||||
}
|
||||
|
||||
cmdArgs := "--hostname=" + hostname
|
||||
cmdArgs += " --certmode=manual"
|
||||
cmdArgs += " --certdir=" + DERPerCertRoot
|
||||
cmdArgs += " --a=:" + strconv.Itoa(dsic.derpPort)
|
||||
cmdArgs += " --stun=true"
|
||||
cmdArgs += " --stun-port=" + strconv.Itoa(dsic.stunPort)
|
||||
var cmdArgs strings.Builder
|
||||
fmt.Fprintf(&cmdArgs, "--hostname=%s", hostname)
|
||||
fmt.Fprintf(&cmdArgs, " --certmode=manual")
|
||||
fmt.Fprintf(&cmdArgs, " --certdir=%s", DERPerCertRoot)
|
||||
fmt.Fprintf(&cmdArgs, " --a=:%d", dsic.derpPort)
|
||||
fmt.Fprintf(&cmdArgs, " --stun=true")
|
||||
fmt.Fprintf(&cmdArgs, " --stun-port=%d", dsic.stunPort)
|
||||
if dsic.withVerifyClientURL != "" {
|
||||
cmdArgs += " --verify-client-url=" + dsic.withVerifyClientURL
|
||||
fmt.Fprintf(&cmdArgs, " --verify-client-url=%s", dsic.withVerifyClientURL)
|
||||
}
|
||||
|
||||
runOptions := &dockertest.RunOptions{
|
||||
|
@ -150,7 +151,7 @@ func New(
|
|||
Networks: []*dockertest.Network{dsic.network},
|
||||
ExtraHosts: dsic.withExtraHosts,
|
||||
// we currently need to give us some time to inject the certificate further down.
|
||||
Entrypoint: []string{"/bin/sh", "-c", "/bin/sleep 3 ; update-ca-certificates ; derper " + cmdArgs},
|
||||
Entrypoint: []string{"/bin/sh", "-c", "/bin/sleep 3 ; update-ca-certificates ; derper " + cmdArgs.String()},
|
||||
ExposedPorts: []string{
|
||||
"80/tcp",
|
||||
fmt.Sprintf("%d/tcp", dsic.derpPort),
|
||||
|
@ -225,6 +226,7 @@ func New(
|
|||
return nil, fmt.Errorf("failed to write TLS key to container: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return dsic, nil
|
||||
}
|
||||
|
||||
|
@ -238,6 +240,7 @@ func (t *DERPServerInContainer) Shutdown() error {
|
|||
fmt.Errorf("failed to save log: %w", err),
|
||||
)
|
||||
}
|
||||
|
||||
return t.pool.Purge(t.container)
|
||||
}
|
||||
|
||||
|
@ -267,18 +270,18 @@ func (t *DERPServerInContainer) GetHostname() string {
|
|||
}
|
||||
|
||||
// GetSTUNPort returns the STUN port of the DERPer instance.
|
||||
func (t *DERPServerInContainer) GetSTUNPort() string {
|
||||
return strconv.Itoa(t.stunPort)
|
||||
func (t *DERPServerInContainer) GetSTUNPort() int {
|
||||
return t.stunPort
|
||||
}
|
||||
|
||||
// GetDERPPort returns the DERP port of the DERPer instance.
|
||||
func (t *DERPServerInContainer) GetDERPPort() string {
|
||||
return strconv.Itoa(t.derpPort)
|
||||
func (t *DERPServerInContainer) GetDERPPort() int {
|
||||
return t.derpPort
|
||||
}
|
||||
|
||||
// WaitForRunning blocks until the DERPer instance is ready to be used.
|
||||
func (t *DERPServerInContainer) WaitForRunning() error {
|
||||
url := "https://" + net.JoinHostPort(t.GetHostname(), t.GetDERPPort()) + "/"
|
||||
url := "https://" + net.JoinHostPort(t.GetHostname(), strconv.Itoa(t.GetDERPPort())) + "/"
|
||||
log.Printf("waiting for DERPer to be ready at %s", url)
|
||||
|
||||
insecureTransport := http.DefaultTransport.(*http.Transport).Clone() //nolint
|
||||
|
|
|
@ -25,6 +25,7 @@ import (
|
|||
"github.com/juanfont/headscale/integration/integrationutil"
|
||||
"github.com/ory/dockertest/v3"
|
||||
"github.com/ory/dockertest/v3/docker"
|
||||
"tailscale.com/tailcfg"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -216,10 +217,17 @@ func WithEmbeddedDERPServerOnly() Option {
|
|||
}
|
||||
}
|
||||
|
||||
// WithCustomDERPServerOnly configures Headscale use a custom
|
||||
// WithDERPConfig configures Headscale use a custom
|
||||
// DERP server only.
|
||||
func WithCustomDERPServerOnly(contents []byte) Option {
|
||||
func WithDERPConfig(derpMap tailcfg.DERPMap) Option {
|
||||
return func(hsic *HeadscaleInContainer) {
|
||||
contents, err := json.Marshal(derpMap)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to marshal DERP map: %s", err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
hsic.env["HEADSCALE_DERP_PATHS"] = "/etc/headscale/derp.yml"
|
||||
hsic.filesInContainer = append(hsic.filesInContainer,
|
||||
fileInContainer{
|
||||
|
|
|
@ -675,7 +675,7 @@ func (t *TailscaleInContainer) watchIPN(ctx context.Context) (*ipn.Notify, error
|
|||
|
||||
func (t *TailscaleInContainer) DebugDERPRegion(region string) (*ipnstate.DebugDERPRegionReport, error) {
|
||||
if !util.TailscaleVersionNewerOrEqual("1.34", t.version) {
|
||||
panic(fmt.Sprintf("tsic.DebugDERPRegion() called with unsupported version: %s", t.version))
|
||||
panic("tsic.DebugDERPRegion() called with unsupported version: " + t.version)
|
||||
}
|
||||
|
||||
command := []string{
|
||||
|
@ -687,17 +687,18 @@ func (t *TailscaleInContainer) DebugDERPRegion(region string) (*ipnstate.DebugDE
|
|||
|
||||
result, stderr, err := t.Execute(command)
|
||||
if err != nil {
|
||||
fmt.Printf("stderr: %s\n", stderr)
|
||||
fmt.Printf("stderr: %s\n", stderr) // nolint
|
||||
|
||||
return nil, fmt.Errorf("failed to execute tailscale debug derp command: %w", err)
|
||||
}
|
||||
|
||||
var st ipnstate.DebugDERPRegionReport
|
||||
err = json.Unmarshal([]byte(result), &st)
|
||||
var report ipnstate.DebugDERPRegionReport
|
||||
err = json.Unmarshal([]byte(result), &report)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal tailscale derp region report: %w", err)
|
||||
}
|
||||
|
||||
return &st, err
|
||||
return &report, err
|
||||
}
|
||||
|
||||
// Netcheck returns the current Netcheck Report (netcheck.Report) of the Tailscale instance.
|
||||
|
|
Loading…
Reference in a new issue