util/syspolicy: finish plumbing policyclient, add feature/syspolicy, move global impl
This is step 4 of making syspolicy a build-time feature. This adds a policyclient.Get() accessor to return the correct implementation to use: either the real one, or the no-op one. (A third type, a static one for testing, also exists, so in general a policyclient.Client should be plumbed around and not always fetched via policyclient.Get whenever possible, especially if tests need to use alternate syspolicy) Updates #16998 Updates #12614 Change-Id: Iaf19670744a596d5918acfa744f5db4564272978 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
committed by
Brad Fitzpatrick
parent
9e9bf13063
commit
2b3e533048
@@ -18,8 +18,8 @@ import (
|
||||
"tailscale.com/ipn"
|
||||
"tailscale.com/ipn/ipnext"
|
||||
"tailscale.com/types/logger"
|
||||
"tailscale.com/util/syspolicy"
|
||||
"tailscale.com/util/syspolicy/pkey"
|
||||
"tailscale.com/util/syspolicy/policyclient"
|
||||
)
|
||||
|
||||
// featureName is the name of the feature implemented by this package.
|
||||
@@ -136,7 +136,7 @@ func (e *desktopSessionsExt) getBackgroundProfile(profiles ipnext.ProfileStore)
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
|
||||
if alwaysOn, _ := syspolicy.GetBoolean(pkey.AlwaysOn, false); !alwaysOn {
|
||||
if alwaysOn, _ := policyclient.Get().GetBoolean(pkey.AlwaysOn, false); !alwaysOn {
|
||||
// If the Always-On mode is disabled, there's no background profile
|
||||
// as far as the desktop session extension is concerned.
|
||||
return ipn.LoginProfileView{}
|
||||
|
||||
@@ -10,8 +10,8 @@ import (
|
||||
"tailscale.com/client/tailscale/apitype"
|
||||
"tailscale.com/ipn"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/util/syspolicy"
|
||||
"tailscale.com/util/syspolicy/pkey"
|
||||
"tailscale.com/util/syspolicy/policyclient"
|
||||
)
|
||||
|
||||
type actorWithPolicyChecks struct{ Actor }
|
||||
@@ -51,10 +51,10 @@ func (a actorWithPolicyChecks) CheckProfileAccess(profile ipn.LoginProfileView,
|
||||
// TODO(nickkhyl): unexport it when we move [ipn.Actor] implementations from [ipnserver]
|
||||
// and corp to this package.
|
||||
func CheckDisconnectPolicy(actor Actor, profile ipn.LoginProfileView, reason string, auditFn AuditLogFunc) error {
|
||||
if alwaysOn, _ := syspolicy.GetBoolean(pkey.AlwaysOn, false); !alwaysOn {
|
||||
if alwaysOn, _ := policyclient.Get().GetBoolean(pkey.AlwaysOn, false); !alwaysOn {
|
||||
return nil
|
||||
}
|
||||
if allowWithReason, _ := syspolicy.GetBoolean(pkey.AlwaysOnOverrideWithReason, false); !allowWithReason {
|
||||
if allowWithReason, _ := policyclient.Get().GetBoolean(pkey.AlwaysOnOverrideWithReason, false); !allowWithReason {
|
||||
return errors.New("disconnect not allowed: always-on mode is enabled")
|
||||
}
|
||||
if reason == "" {
|
||||
|
||||
+2
-1
@@ -30,6 +30,7 @@ import (
|
||||
"tailscale.com/util/goroutines"
|
||||
"tailscale.com/util/set"
|
||||
"tailscale.com/util/syspolicy/pkey"
|
||||
"tailscale.com/util/syspolicy/ptype"
|
||||
"tailscale.com/version"
|
||||
"tailscale.com/version/distro"
|
||||
)
|
||||
@@ -342,7 +343,7 @@ func handleC2NPostureIdentityGet(b *LocalBackend, w http.ResponseWriter, r *http
|
||||
// this will first check syspolicy, MDM settings like Registry
|
||||
// on Windows or defaults on macOS. If they are not set, it falls
|
||||
// back to the cli-flag, `--posture-checking`.
|
||||
choice, err := b.polc.GetPreferenceOption(pkey.PostureChecking)
|
||||
choice, err := b.polc.GetPreferenceOption(pkey.PostureChecking, ptype.ShowChoiceByPolicy)
|
||||
if err != nil {
|
||||
b.logf(
|
||||
"c2n: failed to read PostureChecking from syspolicy, returning default from CLI: %s; got error: %s",
|
||||
|
||||
+11
-10
@@ -109,6 +109,7 @@ import (
|
||||
"tailscale.com/util/slicesx"
|
||||
"tailscale.com/util/syspolicy/pkey"
|
||||
"tailscale.com/util/syspolicy/policyclient"
|
||||
"tailscale.com/util/syspolicy/ptype"
|
||||
"tailscale.com/util/systemd"
|
||||
"tailscale.com/util/testenv"
|
||||
"tailscale.com/util/usermetric"
|
||||
@@ -1610,7 +1611,7 @@ func (b *LocalBackend) SetControlClientStatus(c controlclient.Client, st control
|
||||
// future "tailscale up" to start checking for
|
||||
// implicit setting reverts, which it doesn't do when
|
||||
// ControlURL is blank.
|
||||
prefs.ControlURL = prefs.ControlURLOrDefault()
|
||||
prefs.ControlURL = prefs.ControlURLOrDefault(b.polc)
|
||||
prefsChanged = true
|
||||
}
|
||||
if st.Persist.Valid() {
|
||||
@@ -1870,7 +1871,7 @@ func (b *LocalBackend) applySysPolicyLocked(prefs *ipn.Prefs) (anyChange bool) {
|
||||
}
|
||||
|
||||
for _, opt := range preferencePolicies {
|
||||
if po, err := b.polc.GetPreferenceOption(opt.key); err == nil {
|
||||
if po, err := b.polc.GetPreferenceOption(opt.key, ptype.ShowChoiceByPolicy); err == nil {
|
||||
curVal := opt.get(prefs.View())
|
||||
newVal := po.ShouldEnable(curVal)
|
||||
if curVal != newVal {
|
||||
@@ -2425,7 +2426,7 @@ func (b *LocalBackend) Start(opts ipn.Options) error {
|
||||
|
||||
loggedOut := prefs.LoggedOut()
|
||||
|
||||
serverURL := prefs.ControlURLOrDefault()
|
||||
serverURL := prefs.ControlURLOrDefault(b.polc)
|
||||
if inServerMode := prefs.ForceDaemon(); inServerMode || runtime.GOOS == "windows" {
|
||||
b.logf("Start: serverMode=%v", inServerMode)
|
||||
}
|
||||
@@ -3498,7 +3499,7 @@ func (b *LocalBackend) validPopBrowserURLLocked(urlStr string) bool {
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
serverURL := b.sanitizedPrefsLocked().ControlURLOrDefault()
|
||||
serverURL := b.sanitizedPrefsLocked().ControlURLOrDefault(b.polc)
|
||||
if ipn.IsLoginServerSynonym(serverURL) {
|
||||
// When connected to the official Tailscale control plane, only allow
|
||||
// URLs from tailscale.com or its subdomains.
|
||||
@@ -4049,7 +4050,7 @@ func (b *LocalBackend) SwitchToBestProfile(reason string) {
|
||||
// but b.mu must held on entry. It is released on exit.
|
||||
func (b *LocalBackend) switchToBestProfileLockedOnEntry(reason string, unlock unlockOnce) {
|
||||
defer unlock()
|
||||
oldControlURL := b.pm.CurrentPrefs().ControlURLOrDefault()
|
||||
oldControlURL := b.pm.CurrentPrefs().ControlURLOrDefault(b.polc)
|
||||
profile, background := b.resolveBestProfileLocked()
|
||||
cp, switched, err := b.pm.SwitchToProfile(profile)
|
||||
switch {
|
||||
@@ -4076,7 +4077,7 @@ func (b *LocalBackend) switchToBestProfileLockedOnEntry(reason string, unlock un
|
||||
return
|
||||
}
|
||||
// As an optimization, only reset the dialPlan if the control URL changed.
|
||||
if newControlURL := b.pm.CurrentPrefs().ControlURLOrDefault(); oldControlURL != newControlURL {
|
||||
if newControlURL := b.pm.CurrentPrefs().ControlURLOrDefault(b.polc); oldControlURL != newControlURL {
|
||||
b.resetDialPlan()
|
||||
}
|
||||
if err := b.resetForProfileChangeLockedOnEntry(unlock); err != nil {
|
||||
@@ -4250,7 +4251,7 @@ func (b *LocalBackend) isDefaultServerLocked() bool {
|
||||
if !prefs.Valid() {
|
||||
return true // assume true until set otherwise
|
||||
}
|
||||
return prefs.ControlURLOrDefault() == ipn.DefaultControlURL
|
||||
return prefs.ControlURLOrDefault(b.polc) == ipn.DefaultControlURL
|
||||
}
|
||||
|
||||
var exitNodeMisconfigurationWarnable = health.Register(&health.Warnable{
|
||||
@@ -5687,7 +5688,7 @@ func (b *LocalBackend) enterStateLockedOnEntry(newState ipn.State, unlock unlock
|
||||
// Some temporary (2024-05-05) debugging code to help us catch
|
||||
// https://github.com/tailscale/tailscale/issues/11962 in the act.
|
||||
if prefs.WantRunning() &&
|
||||
prefs.ControlURLOrDefault() == ipn.DefaultControlURL &&
|
||||
prefs.ControlURLOrDefault(b.polc) == ipn.DefaultControlURL &&
|
||||
envknob.Bool("TS_PANIC_IF_HIT_MAIN_CONTROL") {
|
||||
panic("[unexpected] use of main control server in integration test")
|
||||
}
|
||||
@@ -7288,13 +7289,13 @@ func (b *LocalBackend) SwitchProfile(profile ipn.ProfileID) error {
|
||||
unlock := b.lockAndGetUnlock()
|
||||
defer unlock()
|
||||
|
||||
oldControlURL := b.pm.CurrentPrefs().ControlURLOrDefault()
|
||||
oldControlURL := b.pm.CurrentPrefs().ControlURLOrDefault(b.polc)
|
||||
if _, changed, err := b.pm.SwitchToProfileByID(profile); !changed || err != nil {
|
||||
return err // nil if we're already on the target profile
|
||||
}
|
||||
|
||||
// As an optimization, only reset the dialPlan if the control URL changed.
|
||||
if newControlURL := b.pm.CurrentPrefs().ControlURLOrDefault(); oldControlURL != newControlURL {
|
||||
if newControlURL := b.pm.CurrentPrefs().ControlURLOrDefault(b.polc); oldControlURL != newControlURL {
|
||||
b.resetDialPlan()
|
||||
}
|
||||
|
||||
|
||||
+8
-8
@@ -28,8 +28,8 @@ import (
|
||||
"tailscale.com/types/preftype"
|
||||
"tailscale.com/types/views"
|
||||
"tailscale.com/util/dnsname"
|
||||
"tailscale.com/util/syspolicy"
|
||||
"tailscale.com/util/syspolicy/pkey"
|
||||
"tailscale.com/util/syspolicy/policyclient"
|
||||
"tailscale.com/version"
|
||||
)
|
||||
|
||||
@@ -718,16 +718,16 @@ func NewPrefs() *Prefs {
|
||||
//
|
||||
// If not configured, or if the configured value is a legacy name equivalent to
|
||||
// the default, then DefaultControlURL is returned instead.
|
||||
func (p PrefsView) ControlURLOrDefault() string {
|
||||
return p.ж.ControlURLOrDefault()
|
||||
func (p PrefsView) ControlURLOrDefault(polc policyclient.Client) string {
|
||||
return p.ж.ControlURLOrDefault(polc)
|
||||
}
|
||||
|
||||
// ControlURLOrDefault returns the coordination server's URL base.
|
||||
//
|
||||
// If not configured, or if the configured value is a legacy name equivalent to
|
||||
// the default, then DefaultControlURL is returned instead.
|
||||
func (p *Prefs) ControlURLOrDefault() string {
|
||||
controlURL, err := syspolicy.GetString(pkey.ControlURL, p.ControlURL)
|
||||
func (p *Prefs) ControlURLOrDefault(polc policyclient.Client) string {
|
||||
controlURL, err := polc.GetString(pkey.ControlURL, p.ControlURL)
|
||||
if err != nil {
|
||||
controlURL = p.ControlURL
|
||||
}
|
||||
@@ -756,11 +756,11 @@ func (p *Prefs) DefaultRouteAll(goos string) bool {
|
||||
}
|
||||
|
||||
// AdminPageURL returns the admin web site URL for the current ControlURL.
|
||||
func (p PrefsView) AdminPageURL() string { return p.ж.AdminPageURL() }
|
||||
func (p PrefsView) AdminPageURL(polc policyclient.Client) string { return p.ж.AdminPageURL(polc) }
|
||||
|
||||
// AdminPageURL returns the admin web site URL for the current ControlURL.
|
||||
func (p *Prefs) AdminPageURL() string {
|
||||
url := p.ControlURLOrDefault()
|
||||
func (p *Prefs) AdminPageURL(polc policyclient.Client) string {
|
||||
url := p.ControlURLOrDefault(polc)
|
||||
if IsLoginServerSynonym(url) {
|
||||
// TODO(crawshaw): In future release, make this https://console.tailscale.com
|
||||
url = "https://login.tailscale.com"
|
||||
|
||||
+5
-3
@@ -23,6 +23,7 @@ import (
|
||||
"tailscale.com/types/opt"
|
||||
"tailscale.com/types/persist"
|
||||
"tailscale.com/types/preftype"
|
||||
"tailscale.com/util/syspolicy/policyclient"
|
||||
)
|
||||
|
||||
func fieldsOf(t reflect.Type) (fields []string) {
|
||||
@@ -1032,15 +1033,16 @@ func TestExitNodeIPOfArg(t *testing.T) {
|
||||
|
||||
func TestControlURLOrDefault(t *testing.T) {
|
||||
var p Prefs
|
||||
if got, want := p.ControlURLOrDefault(), DefaultControlURL; got != want {
|
||||
polc := policyclient.NoPolicyClient{}
|
||||
if got, want := p.ControlURLOrDefault(polc), DefaultControlURL; got != want {
|
||||
t.Errorf("got %q; want %q", got, want)
|
||||
}
|
||||
p.ControlURL = "http://foo.bar"
|
||||
if got, want := p.ControlURLOrDefault(), "http://foo.bar"; got != want {
|
||||
if got, want := p.ControlURLOrDefault(polc), "http://foo.bar"; got != want {
|
||||
t.Errorf("got %q; want %q", got, want)
|
||||
}
|
||||
p.ControlURL = "https://login.tailscale.com"
|
||||
if got, want := p.ControlURLOrDefault(), DefaultControlURL; got != want {
|
||||
if got, want := p.ControlURLOrDefault(polc), DefaultControlURL; got != want {
|
||||
t.Errorf("got %q; want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user