tailscale / tailscale/tailscale

Unreachable ControlURL does not result in error

Open
#16,201 0 comments 0 reactions 0 assignees View on GitHub
bug tsnet
Dominant language
Go
Stars
36.5k
Forks
3.2k
Avg merge
2d 3h
Merged PRs (30d)
123

Description

### What is the issue?

When using tsnet, setting the ControlURL to a URL that does not exist or is not a tailscale-compatible backplane results in no error. Instead the output indicates that the state is "NeedsLogin" which makes the user believe that there are no issues with the ControlURL.

It seems that at some point the tsnet code must get a timeout or other error on its initial request to the ControlURL. It seems this error should at least make it into the logs, and it should be possible for the code that creates and Starts the tsnet server to "catch" it somehow.

### Steps to reproduce

To reproduce, simply run the [tshello](https://tailscale.com/kb/1521/hello-tsnet) example program from the tsilscale docs with one line modified to specify a non-existent tailscale backplane:

```
// This program demonstrates how to use tsnet as a library.
package main

import (
"crypto/tls"
"flag"
"fmt"
"html"
"log"
"net/http"
"strings"

"tailscale.com/tsnet"
)

var (
addr = flag.String("addr", ":80", "address to listen on")
)

func main() {
flag.Parse()
srv := new(tsnet.Server)
srv.ControlURL = "adfljh.asdflkjsfa" // <-- add this line -------------------------
defer srv.Close()
ln, err := srv.Listen("tcp", *addr)
if err != nil {
log.Fatal(err)
}
defer ln.Close()

lc, err := srv.LocalClient()
if err != nil {
log.Fatal(err)
}

if *addr == ":443" {
ln = tls.NewListener(ln, &tls.Config{
GetCertificate: lc.GetCertificate,
})
}

log.Fatal(http.Serve(ln, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
who, err := lc.WhoIs(r.Context(), r.RemoteAddr)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
fmt.Fprintf(w, "

Hello, world!

\n")
fmt.Fprintf(w, "

You are %s from %s (%s)

",
html.EscapeString(who.UserProfile.LoginName),
html.EscapeString(firstLabel(who.Node.ComputedName)),
r.RemoteAddr)
})))
}

func firstLabel(s string) string {
s, _, _ = strings.Cut(s, ".")
return s
}
```

The output:

```
2025/06/05 10:36:14 tsnet running state path /Users/[...]/Library/Application Support/tsnet-tsbadurl/tailscaled.state
2025/06/05 10:36:14 tsnet starting with hostname "tsbadurl", varRoot "/Users/[...]/Library/Application Support/tsnet-tsbadurl"
2025/06/05 10:36:14 LocalBackend state is NeedsLogin; running StartLoginInteractive...
```

It then stops producing any output. I have left it running for several minutes waiting for something to time out but no such luck.

### Are there any recent changes that introduced the issue?

_No response_

### OS

macOS

### OS version

MacOS 13.7.5

### Tailscale version

1.84.1

### Other software

_No response_

### Bug report

BUG-NO-LOGS-NO-SUPPORT-this-node-has-had-its-logging-disabled

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.