ipn/ipnlocal: better enforce system policies

Previously, policies affected the default prefs for a new profile, but
that does not affect existing profiles. This change ensures that
policies are applied whenever preferences are loaded or changed, so a
CLI or GUI client that does not respect the policies will still be
overridden.

Exit node IP is dropped from this PR as it was implemented elsewhere
in #10172.

Fixes tailscale/corp#15585

Change-Id: Ide4c3a4b00a64e43f506fa1fab70ef591407663f
Signed-off-by: Adrian Dewhurst <adrian@tailscale.com>
This commit is contained in:
Adrian Dewhurst
2023-11-01 17:20:25 -04:00
committed by Adrian Dewhurst
parent ac6f671c54
commit af32d1c120
6 changed files with 334 additions and 74 deletions

View File

@@ -1119,6 +1119,9 @@ func (b *LocalBackend) SetControlClientStatus(c controlclient.Client, st control
if setExitNodeID(prefs, st.NetMap) {
prefsChanged = true
}
if applySysPolicy(prefs) {
prefsChanged = true
}
// Until recently, we did not store the account's tailnet name. So check if this is the case,
// and backfill it on incoming status update.
@@ -1227,6 +1230,35 @@ func (b *LocalBackend) SetControlClientStatus(c controlclient.Client, st control
b.authReconfig()
}
// applySysPolicy overwrites configured preferences with policies that may be
// configured by the system administrator in an OS-specific way.
func applySysPolicy(prefs *ipn.Prefs) (anyChange bool) {
if controlURL, err := syspolicy.GetString(syspolicy.ControlURL, prefs.ControlURL); err == nil && prefs.ControlURL != controlURL {
prefs.ControlURL = controlURL
anyChange = true
}
// Allow Incoming (used by the UI) is the negation of ShieldsUp (used by the
// backend), so this has to convert between the two conventions.
if shieldsUp, err := syspolicy.GetPreferenceOption(syspolicy.EnableIncomingConnections); err == nil {
newVal := !shieldsUp.ShouldEnable(!prefs.ShieldsUp)
if prefs.ShieldsUp != newVal {
prefs.ShieldsUp = newVal
anyChange = true
}
}
if forceDaemon, err := syspolicy.GetPreferenceOption(syspolicy.EnableServerMode); err == nil {
newVal := forceDaemon.ShouldEnable(prefs.ForceDaemon)
if prefs.ForceDaemon != newVal {
prefs.ForceDaemon = newVal
anyChange = true
}
}
return anyChange
}
var _ controlclient.NetmapDeltaUpdater = (*LocalBackend)(nil)
// UpdateNetmapDelta implements controlclient.NetmapDeltaUpdater.
@@ -3051,10 +3083,12 @@ func (b *LocalBackend) setPrefsLockedOnEntry(caller string, newp *ipn.Prefs) ipn
if oldp.Valid() {
newp.Persist = oldp.Persist().AsStruct() // caller isn't allowed to override this
}
// findExitNodeIDLocked returns whether it updated b.prefs, but
// setExitNodeID returns whether it updated b.prefs, but
// everything in this function treats b.prefs as completely new
// anyway. No-op if no exit node resolution is needed.
setExitNodeID(newp, netMap)
// applySysPolicy does likewise so we can also ignore its return value.
applySysPolicy(newp)
// We do this to avoid holding the lock while doing everything else.
oldHi := b.hostinfo