mirror of
https://github.com/juanfont/headscale.git
synced 2026-08-21 14:43:48 +09:00
6275e3a356
Re-authenticating a tagged node with --advertise-tags checked the tag-owned node, not the authenticating user, so every tag was rejected. Fixes #3374
133 lines
5.5 KiB
Go
133 lines
5.5 KiB
Go
package policy
|
|
|
|
import (
|
|
"net/netip"
|
|
"time"
|
|
|
|
"github.com/juanfont/headscale/hscontrol/policy/matcher"
|
|
policyv2 "github.com/juanfont/headscale/hscontrol/policy/v2"
|
|
"github.com/juanfont/headscale/hscontrol/types"
|
|
"tailscale.com/tailcfg"
|
|
"tailscale.com/types/views"
|
|
)
|
|
|
|
type PolicyManager interface {
|
|
// Filter returns the current filter rules for the entire tailnet and the associated matchers.
|
|
Filter() ([]tailcfg.FilterRule, []matcher.Match)
|
|
// FilterForNode returns filter rules for a specific node, handling autogroup:self
|
|
FilterForNode(node types.NodeView) ([]tailcfg.FilterRule, error)
|
|
// MatchersForNode returns matchers for peer relationship determination (unreduced)
|
|
MatchersForNode(node types.NodeView) ([]matcher.Match, error)
|
|
// BuildPeerMap constructs peer relationship maps for the given nodes
|
|
BuildPeerMap(nodes views.Slice[types.NodeView]) map[types.NodeID][]types.NodeView
|
|
SSHPolicy(baseURL string, node types.NodeView) (*tailcfg.SSHPolicy, error)
|
|
// SSHCheckParams resolves the SSH check period for a (src, dst) pair
|
|
// from the current policy, avoiding trust of client-provided URL params.
|
|
SSHCheckParams(srcNodeID, dstNodeID types.NodeID) (time.Duration, bool)
|
|
SetPolicy(pol []byte) (bool, error)
|
|
SetUsers(users []types.User) (bool, error)
|
|
SetNodes(nodes views.Slice[types.NodeView]) (bool, error)
|
|
// 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
|
|
|
|
// TagOwnedByTags reports whether a credential holding ownerTags may apply
|
|
// tag: true if tag is one of ownerTags, or tag's tag-to-tag ownership chain
|
|
// transitively includes one of ownerTags. Authorises the tags an OAuth
|
|
// access token may set on the auth keys it mints.
|
|
TagOwnedByTags(tag string, ownerTags []string) bool
|
|
|
|
// NodeCanApproveRoute reports whether the given node can approve the given route.
|
|
NodeCanApproveRoute(node types.NodeView, route netip.Prefix) bool
|
|
|
|
// NodeNeedsPeerRecompute reports whether peers must recompute their
|
|
// netmap when the node's online state changes. True for subnet
|
|
// routers, relay targets (tailscale.com/cap/relay), and via targets;
|
|
// false for ordinary nodes, which only need a lightweight online or
|
|
// offline peer patch. [State.Connect] and [State.Disconnect] use it to
|
|
// avoid a tailnet-wide recompute on every ordinary reconnect.
|
|
NodeNeedsPeerRecompute(node types.NodeView) bool
|
|
|
|
// ViaRoutesForPeer computes via grant effects for a viewer-peer pair.
|
|
// It returns which routes should be included (peer is via-designated for viewer)
|
|
// and excluded (steered to a different peer). When no via grants apply,
|
|
// both fields are empty and the caller falls back to existing behavior.
|
|
ViaRoutesForPeer(viewer, peer types.NodeView) types.ViaRouteResult
|
|
|
|
// NodeCapMap returns the policy-derived CapMap for the given node,
|
|
// or nil when no nodeAttrs entry targets it. The returned map is
|
|
// owned by the manager; treat it as read-only and copy before
|
|
// merging into a [tailcfg.Node]. It describes the node's own
|
|
// capabilities, not a per-viewer view.
|
|
NodeCapMap(id types.NodeID) tailcfg.NodeCapMap
|
|
|
|
// NodeCapMaps returns a snapshot of the per-node policy CapMap so
|
|
// callers can amortise lock acquisitions over a peer loop. The
|
|
// outer map is a fresh container; the inner [tailcfg.NodeCapMap]
|
|
// values are shared with the manager and read-only.
|
|
NodeCapMaps() map[types.NodeID]tailcfg.NodeCapMap
|
|
|
|
// NodesWithChangedCapMap returns the IDs of nodes whose nodeAttrs
|
|
// CapMap shifted during recent updateLocked calls. The buffer
|
|
// drains on read; callers consume it once per update cycle to
|
|
// decide which nodes need a self-targeted MapResponse.
|
|
// refreshNodeAttrsLocked appends to the buffer rather than
|
|
// overwriting, so a SetUsers/SetNodes between SetPolicy and the
|
|
// drain cannot lose the policy-reload diff.
|
|
NodesWithChangedCapMap() []types.NodeID
|
|
|
|
Version() int
|
|
DebugString() string
|
|
}
|
|
|
|
// NewPolicyManager returns a new [PolicyManager].
|
|
func NewPolicyManager(pol []byte, users []types.User, nodes views.Slice[types.NodeView]) (PolicyManager, error) {
|
|
var (
|
|
polMan PolicyManager
|
|
err error
|
|
)
|
|
|
|
polMan, err = policyv2.NewPolicyManager(pol, users, nodes)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return polMan, err
|
|
}
|
|
|
|
// PolicyManagersForTest returns all available [PolicyManager] implementations to
|
|
// be used in tests to validate them in tests that try to determine that they
|
|
// behave the same.
|
|
func PolicyManagersForTest(pol []byte, users []types.User, nodes views.Slice[types.NodeView]) ([]PolicyManager, error) {
|
|
var polMans []PolicyManager
|
|
|
|
for _, pmf := range PolicyManagerFuncsForTest(pol) {
|
|
pm, err := pmf(users, nodes)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
polMans = append(polMans, pm)
|
|
}
|
|
|
|
return polMans, nil
|
|
}
|
|
|
|
func PolicyManagerFuncsForTest(pol []byte) []func([]types.User, views.Slice[types.NodeView]) (PolicyManager, error) {
|
|
polmanFuncs := make([]func([]types.User, views.Slice[types.NodeView]) (PolicyManager, error), 0, 1)
|
|
|
|
polmanFuncs = append(polmanFuncs, func(u []types.User, n views.Slice[types.NodeView]) (PolicyManager, error) {
|
|
return policyv2.NewPolicyManager(pol, u, n)
|
|
})
|
|
|
|
return polmanFuncs
|
|
}
|