tstest/integration: test tailscale up when device approval is required

This patch extends the integration tests for `tailscale up` to include tailnets
where new devices need to be approved. It doesn't change the CLI, because it's
mostly working correctly already -- these tests are just to prevent future
regressions.

I've added support for `MachineAuthorized` to mock control, and I've refactored
`TestOneNodeUpAuth` to be more flexible. It now takes a sequence of steps to
run and asserts whether we got a login URL and/or machine approval URL after
each step.

Updates tailscale/corp#31476
Updates #17361

Signed-off-by: Alex Chan <alexc@tailscale.com>
This commit is contained in:
Alex Chan
2025-10-06 17:17:52 +01:00
committed by Alex Chan
parent 4543ea5c8a
commit 06f12186d9
3 changed files with 222 additions and 101 deletions

View File

@@ -50,14 +50,15 @@ const msgLimit = 1 << 20 // encrypted message length limit
// Server is a control plane server. Its zero value is ready for use.
// Everything is stored in-memory in one tailnet.
type Server struct {
Logf logger.Logf // nil means to use the log package
DERPMap *tailcfg.DERPMap // nil means to use prod DERP map
RequireAuth bool
RequireAuthKey string // required authkey for all nodes
Verbose bool
DNSConfig *tailcfg.DNSConfig // nil means no DNS config
MagicDNSDomain string
C2NResponses syncs.Map[string, func(*http.Response)] // token => onResponse func
Logf logger.Logf // nil means to use the log package
DERPMap *tailcfg.DERPMap // nil means to use prod DERP map
RequireAuth bool
RequireAuthKey string // required authkey for all nodes
RequireMachineAuth bool
Verbose bool
DNSConfig *tailcfg.DNSConfig // nil means no DNS config
MagicDNSDomain string
C2NResponses syncs.Map[string, func(*http.Response)] // token => onResponse func
// PeerRelayGrants, if true, inserts relay capabilities into the wildcard
// grants rules.
@@ -686,6 +687,21 @@ func (s *Server) CompleteAuth(authPathOrURL string) bool {
return true
}
func (s *Server) CompleteDeviceApproval(nodeKey *key.NodePublic) bool {
s.mu.Lock()
defer s.mu.Unlock()
node, ok := s.nodes[*nodeKey]
if !ok {
return false
}
sendUpdate(s.updates[node.ID], updateSelfChanged)
node.MachineAuthorized = true
return true
}
func (s *Server) serveRegister(w http.ResponseWriter, r *http.Request, mkey key.MachinePublic) {
msg, err := io.ReadAll(io.LimitReader(r.Body, msgLimit))
r.Body.Close()
@@ -761,7 +777,7 @@ func (s *Server) serveRegister(w http.ResponseWriter, r *http.Request, mkey key.
s.nodes = map[key.NodePublic]*tailcfg.Node{}
}
_, ok := s.nodes[nk]
machineAuthorized := true // TODO: add Server.RequireMachineAuth
machineAuthorized := !s.RequireMachineAuth
if !ok {
nodeID := len(s.nodes) + 1