policy,state: authorize reauth tags against the authenticating user

Re-authenticating a tagged node with --advertise-tags checked the tag-owned
node, not the authenticating user, so every tag was rejected.

Fixes #3374

(cherry picked from commit 6275e3a356)
This commit is contained in:
Kristoffer Dalby
2026-07-22 06:44:22 +00:00
committed by Kristoffer Dalby
parent 1fccdb18bd
commit 4a1e77359d
7 changed files with 800 additions and 4 deletions
+6
View File
@@ -30,6 +30,12 @@ type PolicyManager interface {
// NodeCanHaveTag reports whether the given node can have the given tag.
NodeCanHaveTag(node types.NodeView, tag string) bool
// UserCanHaveTag reports whether the given user owns the given tag, i.e.
// is listed (directly or via a group) in the tag's tagOwners. This is the
// user half of NodeCanHaveTag, used to authorise re-auth tag changes
// against the authenticating user rather than the node's stale ownership.
UserCanHaveTag(user types.UserView, tag string) bool
// TagExists reports whether the given tag is defined in the policy.
TagExists(tag string) bool
+31
View File
@@ -964,6 +964,37 @@ func (pm *PolicyManager) NodeCanHaveTag(node types.NodeView, tag string) bool {
return false
}
// UserCanHaveTag reports whether the given user is one of the tag's owners
// (directly or via a group). It is the user half of [PolicyManager.NodeCanHaveTag]:
// re-authentication authorises requested tags against the authenticating user,
// because a tag-owned node carries no user and its IP is not in any owner set,
// so only the user presenting the credential can prove ownership.
func (pm *PolicyManager) UserCanHaveTag(user types.UserView, tag string) bool {
if pm == nil || !user.Valid() {
return false
}
pm.mu.RLock()
defer pm.mu.RUnlock()
if pm.pol == nil {
return false
}
owners, exists := pm.pol.TagOwners[Tag(tag)]
if !exists {
return false
}
for _, owner := range owners {
if pm.userMatchesOwner(user, owner) {
return true
}
}
return false
}
// userMatchesOwner checks if a user matches a tag owner entry.
// This is used as a fallback when the node's IP is not in the [PolicyManager.tagOwnerMap].
func (pm *PolicyManager) userMatchesOwner(user types.UserView, owner Owner) bool {