util/syspolicy/*: move syspolicy keys to new const leaf "pkey" package

This is step 1 of ~3, breaking up #14720 into reviewable chunks, with
the aim to make syspolicy be a build-time configurable feature.

In this first (very noisy) step, all the syspolicy string key
constants move to a new constant-only (code-free) package. This will
make future steps more reviewable, without this movement noise.

There are no code or behavior changes here.

The future steps of this series can be seen in #14720: removing global
funcs from syspolicy resolution and using an interface that's plumbed
around instead. Then adding build tags.

Updates #12614

Change-Id: If73bf2c28b9c9b1a408fe868b0b6a25b03eeabd1
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
Brad Fitzpatrick
2025-08-30 08:02:35 -07:00
committed by Brad Fitzpatrick
parent 6d45fcfc93
commit cc532efc20
48 changed files with 601 additions and 554 deletions
+27 -26
View File
@@ -108,6 +108,7 @@ import (
"tailscale.com/util/set"
"tailscale.com/util/slicesx"
"tailscale.com/util/syspolicy"
"tailscale.com/util/syspolicy/pkey"
"tailscale.com/util/syspolicy/rsop"
"tailscale.com/util/systemd"
"tailscale.com/util/testenv"
@@ -1762,51 +1763,51 @@ func (b *LocalBackend) SetControlClientStatus(c controlclient.Client, st control
}
type preferencePolicyInfo struct {
key syspolicy.Key
key pkey.Key
get func(ipn.PrefsView) bool
set func(*ipn.Prefs, bool)
}
var preferencePolicies = []preferencePolicyInfo{
{
key: syspolicy.EnableIncomingConnections,
key: pkey.EnableIncomingConnections,
// Allow Incoming (used by the UI) is the negation of ShieldsUp (used by the
// backend), so this has to convert between the two conventions.
get: func(p ipn.PrefsView) bool { return !p.ShieldsUp() },
set: func(p *ipn.Prefs, v bool) { p.ShieldsUp = !v },
},
{
key: syspolicy.EnableServerMode,
key: pkey.EnableServerMode,
get: func(p ipn.PrefsView) bool { return p.ForceDaemon() },
set: func(p *ipn.Prefs, v bool) { p.ForceDaemon = v },
},
{
key: syspolicy.ExitNodeAllowLANAccess,
key: pkey.ExitNodeAllowLANAccess,
get: func(p ipn.PrefsView) bool { return p.ExitNodeAllowLANAccess() },
set: func(p *ipn.Prefs, v bool) { p.ExitNodeAllowLANAccess = v },
},
{
key: syspolicy.EnableTailscaleDNS,
key: pkey.EnableTailscaleDNS,
get: func(p ipn.PrefsView) bool { return p.CorpDNS() },
set: func(p *ipn.Prefs, v bool) { p.CorpDNS = v },
},
{
key: syspolicy.EnableTailscaleSubnets,
key: pkey.EnableTailscaleSubnets,
get: func(p ipn.PrefsView) bool { return p.RouteAll() },
set: func(p *ipn.Prefs, v bool) { p.RouteAll = v },
},
{
key: syspolicy.CheckUpdates,
key: pkey.CheckUpdates,
get: func(p ipn.PrefsView) bool { return p.AutoUpdate().Check },
set: func(p *ipn.Prefs, v bool) { p.AutoUpdate.Check = v },
},
{
key: syspolicy.ApplyUpdates,
key: pkey.ApplyUpdates,
get: func(p ipn.PrefsView) bool { v, _ := p.AutoUpdate().Apply.Get(); return v },
set: func(p *ipn.Prefs, v bool) { p.AutoUpdate.Apply.Set(v) },
},
{
key: syspolicy.EnableRunExitNode,
key: pkey.EnableRunExitNode,
get: func(p ipn.PrefsView) bool { return p.AdvertisesExitNode() },
set: func(p *ipn.Prefs, v bool) { p.SetAdvertiseExitNode(v) },
},
@@ -1817,13 +1818,13 @@ var preferencePolicies = []preferencePolicyInfo{
//
// b.mu must be held.
func (b *LocalBackend) applySysPolicyLocked(prefs *ipn.Prefs) (anyChange bool) {
if controlURL, err := syspolicy.GetString(syspolicy.ControlURL, prefs.ControlURL); err == nil && prefs.ControlURL != controlURL {
if controlURL, err := syspolicy.GetString(pkey.ControlURL, prefs.ControlURL); err == nil && prefs.ControlURL != controlURL {
prefs.ControlURL = controlURL
anyChange = true
}
const sentinel = "HostnameDefaultValue"
hostnameFromPolicy, _ := syspolicy.GetString(syspolicy.Hostname, sentinel)
hostnameFromPolicy, _ := syspolicy.GetString(pkey.Hostname, sentinel)
switch hostnameFromPolicy {
case sentinel:
// An empty string for this policy value means that the admin wants to delete
@@ -1858,7 +1859,7 @@ func (b *LocalBackend) applySysPolicyLocked(prefs *ipn.Prefs) (anyChange bool) {
anyChange = true
}
if alwaysOn, _ := syspolicy.GetBoolean(syspolicy.AlwaysOn, false); alwaysOn && !b.overrideAlwaysOn && !prefs.WantRunning {
if alwaysOn, _ := syspolicy.GetBoolean(pkey.AlwaysOn, false); alwaysOn && !b.overrideAlwaysOn && !prefs.WantRunning {
prefs.WantRunning = true
anyChange = true
}
@@ -1882,7 +1883,7 @@ func (b *LocalBackend) applySysPolicyLocked(prefs *ipn.Prefs) (anyChange bool) {
//
// b.mu must be held.
func (b *LocalBackend) applyExitNodeSysPolicyLocked(prefs *ipn.Prefs) (anyChange bool) {
if exitNodeIDStr, _ := syspolicy.GetString(syspolicy.ExitNodeID, ""); exitNodeIDStr != "" {
if exitNodeIDStr, _ := syspolicy.GetString(pkey.ExitNodeID, ""); exitNodeIDStr != "" {
exitNodeID := tailcfg.StableNodeID(exitNodeIDStr)
// Try to parse the policy setting value as an "auto:"-prefixed [ipn.ExitNodeExpression],
@@ -1923,7 +1924,7 @@ func (b *LocalBackend) applyExitNodeSysPolicyLocked(prefs *ipn.Prefs) (anyChange
prefs.ExitNodeIP = netip.Addr{}
anyChange = true
}
} else if exitNodeIPStr, _ := syspolicy.GetString(syspolicy.ExitNodeIP, ""); exitNodeIPStr != "" {
} else if exitNodeIPStr, _ := syspolicy.GetString(pkey.ExitNodeIP, ""); exitNodeIPStr != "" {
if prefs.AutoExitNode != "" {
prefs.AutoExitNode = "" // mutually exclusive with ExitNodeIP
anyChange = true
@@ -1970,7 +1971,7 @@ func (b *LocalBackend) reconcilePrefs() (_ ipn.PrefsView, anyChange bool) {
// sysPolicyChanged is a callback triggered by syspolicy when it detects
// a change in one or more syspolicy settings.
func (b *LocalBackend) sysPolicyChanged(policy *rsop.PolicyChange) {
if policy.HasChangedAnyOf(syspolicy.AlwaysOn, syspolicy.AlwaysOnOverrideWithReason) {
if policy.HasChangedAnyOf(pkey.AlwaysOn, pkey.AlwaysOnOverrideWithReason) {
// If the AlwaysOn or the AlwaysOnOverrideWithReason policy has changed,
// we should reset the overrideAlwaysOn flag, as the override might
// no longer be valid.
@@ -1979,7 +1980,7 @@ func (b *LocalBackend) sysPolicyChanged(policy *rsop.PolicyChange) {
b.mu.Unlock()
}
if policy.HasChangedAnyOf(syspolicy.ExitNodeID, syspolicy.ExitNodeIP, syspolicy.AllowExitNodeOverride) {
if policy.HasChangedAnyOf(pkey.ExitNodeID, pkey.ExitNodeIP, pkey.AllowExitNodeOverride) {
// Reset the exit node override if a policy that enforces exit node usage
// or allows the user to override automatic exit node selection has changed.
b.mu.Lock()
@@ -1987,7 +1988,7 @@ func (b *LocalBackend) sysPolicyChanged(policy *rsop.PolicyChange) {
b.mu.Unlock()
}
if policy.HasChanged(syspolicy.AllowedSuggestedExitNodes) {
if policy.HasChanged(pkey.AllowedSuggestedExitNodes) {
b.refreshAllowedSuggestions()
// Re-evaluate exit node suggestion now that the policy setting has changed.
if _, err := b.SuggestExitNode(); err != nil && !errors.Is(err, ErrNoPreferredDERP) {
@@ -2348,7 +2349,7 @@ func (b *LocalBackend) Start(opts ipn.Options) error {
}
if b.state != ipn.Running && b.conf == nil && opts.AuthKey == "" {
sysak, _ := syspolicy.GetString(syspolicy.AuthKey, "")
sysak, _ := syspolicy.GetString(pkey.AuthKey, "")
if sysak != "" {
b.logf("Start: setting opts.AuthKey by syspolicy, len=%v", len(sysak))
opts.AuthKey = strings.TrimSpace(sysak)
@@ -4407,7 +4408,7 @@ func (b *LocalBackend) checkEditPrefsAccessLocked(actor ipnauth.Actor, prefs ipn
// Prevent users from changing exit node preferences
// when exit node usage is managed by policy.
if mp.ExitNodeIDSet || mp.ExitNodeIPSet || mp.AutoExitNodeSet {
isManaged, err := syspolicy.HasAnyOf(syspolicy.ExitNodeID, syspolicy.ExitNodeIP)
isManaged, err := syspolicy.HasAnyOf(pkey.ExitNodeID, pkey.ExitNodeIP)
if err != nil {
err = fmt.Errorf("policy check failed: %w", err)
} else if isManaged {
@@ -4415,7 +4416,7 @@ func (b *LocalBackend) checkEditPrefsAccessLocked(actor ipnauth.Actor, prefs ipn
// if permitted by [syspolicy.AllowExitNodeOverride].
//
// Disabling exit node usage entirely is not allowed.
allowExitNodeOverride, _ := syspolicy.GetBoolean(syspolicy.AllowExitNodeOverride, false)
allowExitNodeOverride, _ := syspolicy.GetBoolean(pkey.AllowExitNodeOverride, false)
if !allowExitNodeOverride || b.changeDisablesExitNodeLocked(prefs, mp) {
err = errManagedByPolicy
}
@@ -4519,7 +4520,7 @@ func (b *LocalBackend) onEditPrefsLocked(_ ipnauth.Actor, mp *ipn.MaskedPrefs, o
// mode on them until the policy changes, they switch to a different profile, etc.
b.overrideAlwaysOn = true
if reconnectAfter, _ := syspolicy.GetDuration(syspolicy.ReconnectAfter, 0); reconnectAfter > 0 {
if reconnectAfter, _ := syspolicy.GetDuration(pkey.ReconnectAfter, 0); reconnectAfter > 0 {
b.startReconnectTimerLocked(reconnectAfter)
}
}
@@ -4530,7 +4531,7 @@ func (b *LocalBackend) onEditPrefsLocked(_ ipnauth.Actor, mp *ipn.MaskedPrefs, o
b.overrideExitNodePolicy = false
}
if mp.AutoExitNodeSet || mp.ExitNodeIDSet || mp.ExitNodeIPSet {
if allowExitNodeOverride, _ := syspolicy.GetBoolean(syspolicy.AllowExitNodeOverride, false); allowExitNodeOverride {
if allowExitNodeOverride, _ := syspolicy.GetBoolean(pkey.AllowExitNodeOverride, false); allowExitNodeOverride {
// If applying exit node policy settings to the new prefs results in no change,
// the user is not overriding the policy. Otherwise, it is an override.
b.overrideExitNodePolicy = b.applyExitNodeSysPolicyLocked(newPrefs.AsStruct())
@@ -7807,9 +7808,9 @@ type selectRegionFunc func(views.Slice[int]) int
type selectNodeFunc func(nodes views.Slice[tailcfg.NodeView], last tailcfg.StableNodeID) tailcfg.NodeView
func fillAllowedSuggestions() set.Set[tailcfg.StableNodeID] {
nodes, err := syspolicy.GetStringArray(syspolicy.AllowedSuggestedExitNodes, nil)
nodes, err := syspolicy.GetStringArray(pkey.AllowedSuggestedExitNodes, nil)
if err != nil {
log.Printf("fillAllowedSuggestions: unable to look up %q policy: %v", syspolicy.AllowedSuggestedExitNodes, err)
log.Printf("fillAllowedSuggestions: unable to look up %q policy: %v", pkey.AllowedSuggestedExitNodes, err)
return nil
}
if nodes == nil {
@@ -8176,7 +8177,7 @@ func isAllowedAutoExitNodeID(exitNodeID tailcfg.StableNodeID) bool {
if exitNodeID == "" {
return false // an exit node is required
}
if nodes, _ := syspolicy.GetStringArray(syspolicy.AllowedSuggestedExitNodes, nil); nodes != nil {
if nodes, _ := syspolicy.GetStringArray(pkey.AllowedSuggestedExitNodes, nil); nodes != nil {
return slices.Contains(nodes, string(exitNodeID))
}
@@ -8339,7 +8340,7 @@ func (b *LocalBackend) stateEncrypted() opt.Bool {
// the Keychain. A future release will clean up the on-disk state
// files.
// TODO(#15830): always return true here once MacSys is fully migrated.
sp, _ := syspolicy.GetBoolean(syspolicy.EncryptState, false)
sp, _ := syspolicy.GetBoolean(pkey.EncryptState, false)
return opt.NewBool(sp)
default:
// Probably self-compiled tailscaled, we don't use the Keychain