types: do not stamp is-admin on tagged nodes

A tagged node has no user to hold the role. SaaS leaves it out unless policy
grants it. Stop stripping the cap, so captures now compare it.
This commit is contained in:
Kristoffer Dalby committed 2026-11-03 17:05:00 +01:00
1 parent 581ec5b3a0
commit edad4ff324
2 files changed
+10 -14

No files matched your search

+4 -13
View File
@@ -66,19 +66,10 @@ func PeerCapMap(peer types.NodeView, peerSelfCaps tailcfg.NodeCapMap) tailcfg.No
var unmodelledTailnetStateCaps = []nodecap.Cap{
// --- 1. User-role gated ---
// [tailcfg.CapabilityAdmin]: the hosted control plane stamps this
// on nodes whose owning user has the admin role; tagged nodes
// inherit from a tagOwner with the role. Headscale has no
// user-role model — [types.Node.TailNode] emits it as part of
// the always-on baseline. Stripping on both sides keeps the diff
// from failing on every user-owned non-admin node in a capture.
// Long-term fix is autogroup:admin support.
nodecap.Admin,
// [tailcfg.CapabilityOwner]: same shape as is-admin, conditional
// on the "owner" role rather than admin. Headscale does not emit
// this cap at all. autogroup:owner support is tracked under
// NO_USER_ROLES — see the compat skip list.
// [tailcfg.CapabilityOwner]: conditional on the "owner" role, which
// headscale does not model, so it does not emit this cap at all.
// autogroup:owner support is tracked under NO_USER_ROLES — see the
// compat skip list.
nodecap.Owner,
// --- 2. Feature not implemented ---
+6 -1
View File
@@ -1429,11 +1429,16 @@ func (nv NodeView) TailNode(
// cfg.DNSConfig.Certificates.Enabled gates HTTPS, without which
// `tailscale serve` refuses to serve HTTPS.
capMap := tailcfg.NodeCapMap{
nodecap.Admin: []tailcfg.RawMessage{},
nodecap.SSH: []tailcfg.RawMessage{},
nodecap.SSHEnvironmentVariables: []tailcfg.RawMessage{},
}
// Headscale has no user roles, so every user counts as an admin. A tagged
// node has no user to hold the role, and SaaS does not stamp it either.
if !nv.IsTagged() {
capMap[nodecap.Admin] = []tailcfg.RawMessage{}
}
if cfg.Taildrop.Enabled {
capMap[nodecap.FileSharing] = []tailcfg.RawMessage{}
}