mirror of
https://github.com/tailscale/tailscale.git
synced 2025-04-07 16:54:37 +00:00
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:
parent
ac6f671c54
commit
af32d1c120
@ -1119,6 +1119,9 @@ func (b *LocalBackend) SetControlClientStatus(c controlclient.Client, st control
|
|||||||
if setExitNodeID(prefs, st.NetMap) {
|
if setExitNodeID(prefs, st.NetMap) {
|
||||||
prefsChanged = true
|
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,
|
// 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.
|
// and backfill it on incoming status update.
|
||||||
@ -1227,6 +1230,35 @@ func (b *LocalBackend) SetControlClientStatus(c controlclient.Client, st control
|
|||||||
b.authReconfig()
|
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)
|
var _ controlclient.NetmapDeltaUpdater = (*LocalBackend)(nil)
|
||||||
|
|
||||||
// UpdateNetmapDelta implements controlclient.NetmapDeltaUpdater.
|
// UpdateNetmapDelta implements controlclient.NetmapDeltaUpdater.
|
||||||
@ -3051,10 +3083,12 @@ func (b *LocalBackend) setPrefsLockedOnEntry(caller string, newp *ipn.Prefs) ipn
|
|||||||
if oldp.Valid() {
|
if oldp.Valid() {
|
||||||
newp.Persist = oldp.Persist().AsStruct() // caller isn't allowed to override this
|
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
|
// everything in this function treats b.prefs as completely new
|
||||||
// anyway. No-op if no exit node resolution is needed.
|
// anyway. No-op if no exit node resolution is needed.
|
||||||
setExitNodeID(newp, netMap)
|
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.
|
// We do this to avoid holding the lock while doing everything else.
|
||||||
|
|
||||||
oldHi := b.hostinfo
|
oldHi := b.hostinfo
|
||||||
|
@ -1334,31 +1334,40 @@ func (rc *routeCollector) AdvertiseRoute(pfx netip.Prefix) error {
|
|||||||
|
|
||||||
type mockSyspolicyHandler struct {
|
type mockSyspolicyHandler struct {
|
||||||
t *testing.T
|
t *testing.T
|
||||||
key syspolicy.Key
|
// stringPolicies is the collection of policies that we expect to see
|
||||||
s string
|
// queried by the current test. If the policy is expected but unset, then
|
||||||
exitNodeIDKey bool
|
// use nil, otherwise use a string equal to the policy's desired value.
|
||||||
exitNodeIPKey bool
|
stringPolicies map[syspolicy.Key]*string
|
||||||
exitNodeID string
|
// failUnknownPolicies is set if policies other than those in stringPolicies
|
||||||
exitNodeIP string
|
// (uint64 or bool policies are not supported by mockSyspolicyHandler yet)
|
||||||
|
// should be considered a test failure if they are queried.
|
||||||
|
failUnknownPolicies bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *mockSyspolicyHandler) ReadString(key string) (string, error) {
|
func (h *mockSyspolicyHandler) ReadString(key string) (string, error) {
|
||||||
if h.exitNodeIDKey && key == string(syspolicy.ExitNodeID) {
|
if s, ok := h.stringPolicies[syspolicy.Key(key)]; ok {
|
||||||
return h.exitNodeID, nil
|
if s == nil {
|
||||||
|
return "", syspolicy.ErrNoSuchKey
|
||||||
}
|
}
|
||||||
if h.exitNodeIPKey && key == string(syspolicy.ExitNodeIP) {
|
return *s, nil
|
||||||
return h.exitNodeIP, nil
|
}
|
||||||
|
if h.failUnknownPolicies {
|
||||||
|
h.t.Errorf("ReadString(%q) unexpectedly called", key)
|
||||||
}
|
}
|
||||||
return "", syspolicy.ErrNoSuchKey
|
return "", syspolicy.ErrNoSuchKey
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *mockSyspolicyHandler) ReadUInt64(key string) (uint64, error) {
|
func (h *mockSyspolicyHandler) ReadUInt64(key string) (uint64, error) {
|
||||||
|
if h.failUnknownPolicies {
|
||||||
h.t.Errorf("ReadUInt64(%q) unexpectedly called", key)
|
h.t.Errorf("ReadUInt64(%q) unexpectedly called", key)
|
||||||
|
}
|
||||||
return 0, syspolicy.ErrNoSuchKey
|
return 0, syspolicy.ErrNoSuchKey
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *mockSyspolicyHandler) ReadBoolean(key string) (bool, error) {
|
func (h *mockSyspolicyHandler) ReadBoolean(key string) (bool, error) {
|
||||||
|
if h.failUnknownPolicies {
|
||||||
h.t.Errorf("ReadBoolean(%q) unexpectedly called", key)
|
h.t.Errorf("ReadBoolean(%q) unexpectedly called", key)
|
||||||
|
}
|
||||||
return false, syspolicy.ErrNoSuchKey
|
return false, syspolicy.ErrNoSuchKey
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1558,13 +1567,20 @@ func TestSetExitNodeIDPolicy(t *testing.T) {
|
|||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
b := newTestBackend(t)
|
b := newTestBackend(t)
|
||||||
syspolicy.SetHandlerForTest(t, &mockSyspolicyHandler{
|
msh := &mockSyspolicyHandler{
|
||||||
t: t,
|
t: t,
|
||||||
exitNodeID: test.exitNodeID,
|
stringPolicies: map[syspolicy.Key]*string{
|
||||||
exitNodeIP: test.exitNodeIP,
|
syspolicy.ExitNodeID: nil,
|
||||||
exitNodeIDKey: test.exitNodeIDKey,
|
syspolicy.ExitNodeIP: nil,
|
||||||
exitNodeIPKey: test.exitNodeIPKey,
|
},
|
||||||
})
|
}
|
||||||
|
if test.exitNodeIDKey {
|
||||||
|
msh.stringPolicies[syspolicy.ExitNodeID] = &test.exitNodeID
|
||||||
|
}
|
||||||
|
if test.exitNodeIPKey {
|
||||||
|
msh.stringPolicies[syspolicy.ExitNodeIP] = &test.exitNodeIP
|
||||||
|
}
|
||||||
|
syspolicy.SetHandlerForTest(t, msh)
|
||||||
if test.nm == nil {
|
if test.nm == nil {
|
||||||
test.nm = new(netmap.NetworkMap)
|
test.nm = new(netmap.NetworkMap)
|
||||||
}
|
}
|
||||||
@ -1577,22 +1593,16 @@ func TestSetExitNodeIDPolicy(t *testing.T) {
|
|||||||
b.pm = pm
|
b.pm = pm
|
||||||
changed := setExitNodeID(b.pm.prefs.AsStruct(), test.nm)
|
changed := setExitNodeID(b.pm.prefs.AsStruct(), test.nm)
|
||||||
b.SetPrefs(pm.CurrentPrefs().AsStruct())
|
b.SetPrefs(pm.CurrentPrefs().AsStruct())
|
||||||
if test.exitNodeIDKey {
|
if got := b.pm.prefs.ExitNodeID(); got != tailcfg.StableNodeID(test.exitNodeIDWant) {
|
||||||
got := b.pm.prefs.ExitNodeID()
|
|
||||||
if got != tailcfg.StableNodeID(test.exitNodeIDWant) {
|
|
||||||
t.Errorf("got %v want %v", got, test.exitNodeIDWant)
|
t.Errorf("got %v want %v", got, test.exitNodeIDWant)
|
||||||
}
|
}
|
||||||
}
|
if got := b.pm.prefs.ExitNodeIP(); test.exitNodeIPWant == "" {
|
||||||
if test.exitNodeIPKey {
|
|
||||||
got := b.pm.prefs.ExitNodeIP()
|
|
||||||
if test.exitNodeIPWant == "" {
|
|
||||||
if got.String() != "invalid IP" {
|
if got.String() != "invalid IP" {
|
||||||
t.Errorf("got %v want invalid IP", got)
|
t.Errorf("got %v want invalid IP", got)
|
||||||
}
|
}
|
||||||
} else if got.String() != test.exitNodeIPWant {
|
} else if got.String() != test.exitNodeIPWant {
|
||||||
t.Errorf("got %v want %v", got, test.exitNodeIPWant)
|
t.Errorf("got %v want %v", got, test.exitNodeIPWant)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if changed != test.prefsChanged {
|
if changed != test.prefsChanged {
|
||||||
t.Errorf("wanted prefs changed %v, got prefs changed %v", test.prefsChanged, changed)
|
t.Errorf("wanted prefs changed %v, got prefs changed %v", test.prefsChanged, changed)
|
||||||
@ -1600,3 +1610,244 @@ func TestSetExitNodeIDPolicy(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestApplySysPolicy(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
prefs ipn.Prefs
|
||||||
|
wantPrefs ipn.Prefs
|
||||||
|
wantAnyChange bool
|
||||||
|
stringPolicies map[syspolicy.Key]string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "empty prefs without policies",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "prefs set without policies",
|
||||||
|
prefs: ipn.Prefs{
|
||||||
|
ControlURL: "1",
|
||||||
|
ShieldsUp: true,
|
||||||
|
ForceDaemon: true,
|
||||||
|
},
|
||||||
|
wantPrefs: ipn.Prefs{
|
||||||
|
ControlURL: "1",
|
||||||
|
ShieldsUp: true,
|
||||||
|
ForceDaemon: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty prefs with policies",
|
||||||
|
wantPrefs: ipn.Prefs{
|
||||||
|
ControlURL: "1",
|
||||||
|
ShieldsUp: true,
|
||||||
|
ForceDaemon: true,
|
||||||
|
},
|
||||||
|
wantAnyChange: true,
|
||||||
|
stringPolicies: map[syspolicy.Key]string{
|
||||||
|
syspolicy.ControlURL: "1",
|
||||||
|
syspolicy.EnableIncomingConnections: "never",
|
||||||
|
syspolicy.EnableServerMode: "always",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "prefs set with matching policies",
|
||||||
|
prefs: ipn.Prefs{
|
||||||
|
ControlURL: "1",
|
||||||
|
ShieldsUp: true,
|
||||||
|
ForceDaemon: true,
|
||||||
|
},
|
||||||
|
wantPrefs: ipn.Prefs{
|
||||||
|
ControlURL: "1",
|
||||||
|
ShieldsUp: true,
|
||||||
|
ForceDaemon: true,
|
||||||
|
},
|
||||||
|
stringPolicies: map[syspolicy.Key]string{
|
||||||
|
syspolicy.ControlURL: "1",
|
||||||
|
syspolicy.EnableIncomingConnections: "never",
|
||||||
|
syspolicy.EnableServerMode: "always",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "prefs set with conflicting policies",
|
||||||
|
prefs: ipn.Prefs{
|
||||||
|
ControlURL: "1",
|
||||||
|
ShieldsUp: true,
|
||||||
|
ForceDaemon: true,
|
||||||
|
},
|
||||||
|
wantPrefs: ipn.Prefs{
|
||||||
|
ControlURL: "2",
|
||||||
|
ShieldsUp: false,
|
||||||
|
ForceDaemon: false,
|
||||||
|
},
|
||||||
|
wantAnyChange: true,
|
||||||
|
stringPolicies: map[syspolicy.Key]string{
|
||||||
|
syspolicy.ControlURL: "2",
|
||||||
|
syspolicy.EnableIncomingConnections: "always",
|
||||||
|
syspolicy.EnableServerMode: "never",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "prefs set with neutral policies",
|
||||||
|
prefs: ipn.Prefs{
|
||||||
|
ControlURL: "1",
|
||||||
|
ShieldsUp: true,
|
||||||
|
ForceDaemon: true,
|
||||||
|
},
|
||||||
|
wantPrefs: ipn.Prefs{
|
||||||
|
ControlURL: "1",
|
||||||
|
ShieldsUp: true,
|
||||||
|
ForceDaemon: true,
|
||||||
|
},
|
||||||
|
stringPolicies: map[syspolicy.Key]string{
|
||||||
|
syspolicy.EnableIncomingConnections: "user-decides",
|
||||||
|
syspolicy.EnableServerMode: "user-decides",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ControlURL",
|
||||||
|
wantPrefs: ipn.Prefs{
|
||||||
|
ControlURL: "set",
|
||||||
|
},
|
||||||
|
wantAnyChange: true,
|
||||||
|
stringPolicies: map[syspolicy.Key]string{
|
||||||
|
syspolicy.ControlURL: "set",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "always incoming connections",
|
||||||
|
prefs: ipn.Prefs{
|
||||||
|
ShieldsUp: true,
|
||||||
|
},
|
||||||
|
wantPrefs: ipn.Prefs{
|
||||||
|
ShieldsUp: false,
|
||||||
|
},
|
||||||
|
wantAnyChange: true,
|
||||||
|
stringPolicies: map[syspolicy.Key]string{
|
||||||
|
syspolicy.EnableIncomingConnections: "always",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "never incoming connections",
|
||||||
|
prefs: ipn.Prefs{
|
||||||
|
ShieldsUp: false,
|
||||||
|
},
|
||||||
|
wantPrefs: ipn.Prefs{
|
||||||
|
ShieldsUp: true,
|
||||||
|
},
|
||||||
|
wantAnyChange: true,
|
||||||
|
stringPolicies: map[syspolicy.Key]string{
|
||||||
|
syspolicy.EnableIncomingConnections: "never",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "always server mode",
|
||||||
|
prefs: ipn.Prefs{
|
||||||
|
ForceDaemon: false,
|
||||||
|
},
|
||||||
|
wantPrefs: ipn.Prefs{
|
||||||
|
ForceDaemon: true,
|
||||||
|
},
|
||||||
|
wantAnyChange: true,
|
||||||
|
stringPolicies: map[syspolicy.Key]string{
|
||||||
|
syspolicy.EnableServerMode: "always",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "never server mode",
|
||||||
|
prefs: ipn.Prefs{
|
||||||
|
ForceDaemon: true,
|
||||||
|
},
|
||||||
|
wantPrefs: ipn.Prefs{
|
||||||
|
ForceDaemon: false,
|
||||||
|
},
|
||||||
|
wantAnyChange: true,
|
||||||
|
stringPolicies: map[syspolicy.Key]string{
|
||||||
|
syspolicy.EnableServerMode: "never",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Run("unit", func(t *testing.T) {
|
||||||
|
// Be strict when testing the func directly.
|
||||||
|
msh := &mockSyspolicyHandler{
|
||||||
|
t: t,
|
||||||
|
stringPolicies: map[syspolicy.Key]*string{
|
||||||
|
syspolicy.ControlURL: nil,
|
||||||
|
syspolicy.EnableIncomingConnections: nil,
|
||||||
|
syspolicy.EnableServerMode: nil,
|
||||||
|
},
|
||||||
|
failUnknownPolicies: true,
|
||||||
|
}
|
||||||
|
for p, v := range tt.stringPolicies {
|
||||||
|
v := v // construct a unique pointer for each policy value
|
||||||
|
msh.stringPolicies[p] = &v
|
||||||
|
}
|
||||||
|
syspolicy.SetHandlerForTest(t, msh)
|
||||||
|
|
||||||
|
prefs := tt.prefs.Clone()
|
||||||
|
|
||||||
|
gotAnyChange := applySysPolicy(prefs)
|
||||||
|
|
||||||
|
if gotAnyChange && prefs.Equals(&tt.prefs) {
|
||||||
|
t.Errorf("anyChange but prefs is unchanged: %v", prefs.Pretty())
|
||||||
|
}
|
||||||
|
if !gotAnyChange && !prefs.Equals(&tt.prefs) {
|
||||||
|
t.Errorf("!anyChange but prefs changed from %v to %v", tt.prefs.Pretty(), prefs.Pretty())
|
||||||
|
}
|
||||||
|
if gotAnyChange != tt.wantAnyChange {
|
||||||
|
t.Errorf("anyChange=%v, want %v", gotAnyChange, tt.wantAnyChange)
|
||||||
|
}
|
||||||
|
if !prefs.Equals(&tt.wantPrefs) {
|
||||||
|
t.Errorf("prefs=%v, want %v", prefs.Pretty(), tt.wantPrefs.Pretty())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// Create a more relaxed syspolicy for integration tests.
|
||||||
|
msh := &mockSyspolicyHandler{
|
||||||
|
t: t,
|
||||||
|
stringPolicies: make(map[syspolicy.Key]*string, len(tt.stringPolicies)),
|
||||||
|
}
|
||||||
|
for p, v := range tt.stringPolicies {
|
||||||
|
v := v // construct a unique pointer for each policy value
|
||||||
|
msh.stringPolicies[p] = &v
|
||||||
|
}
|
||||||
|
syspolicy.SetHandlerForTest(t, msh)
|
||||||
|
|
||||||
|
t.Run("set prefs", func(t *testing.T) {
|
||||||
|
|
||||||
|
b := newTestBackend(t)
|
||||||
|
b.SetPrefs(tt.prefs.Clone())
|
||||||
|
if !b.Prefs().Equals(tt.wantPrefs.View()) {
|
||||||
|
t.Errorf("prefs=%v, want %v", b.Prefs().Pretty(), tt.wantPrefs.Pretty())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("status update", func(t *testing.T) {
|
||||||
|
// Profile manager fills in blank ControlURL but it's not set
|
||||||
|
// in most test cases to avoid cluttering them, so adjust for
|
||||||
|
// that.
|
||||||
|
usePrefs := tt.prefs.Clone()
|
||||||
|
if usePrefs.ControlURL == "" {
|
||||||
|
usePrefs.ControlURL = ipn.DefaultControlURL
|
||||||
|
}
|
||||||
|
wantPrefs := tt.wantPrefs.Clone()
|
||||||
|
if wantPrefs.ControlURL == "" {
|
||||||
|
wantPrefs.ControlURL = ipn.DefaultControlURL
|
||||||
|
}
|
||||||
|
|
||||||
|
pm := must.Get(newProfileManager(new(mem.Store), t.Logf))
|
||||||
|
pm.prefs = usePrefs.View()
|
||||||
|
|
||||||
|
b := newTestBackend(t)
|
||||||
|
b.mu.Lock()
|
||||||
|
b.pm = pm
|
||||||
|
b.mu.Unlock()
|
||||||
|
|
||||||
|
b.SetControlClientStatus(b.cc, controlclient.Status{})
|
||||||
|
if !b.Prefs().Equals(wantPrefs.View()) {
|
||||||
|
t.Errorf("prefs=%v, want %v", b.Prefs().Pretty(), wantPrefs.Pretty())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -8,7 +8,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net/netip"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
@ -19,7 +18,6 @@ import (
|
|||||||
"tailscale.com/types/logger"
|
"tailscale.com/types/logger"
|
||||||
"tailscale.com/util/clientmetric"
|
"tailscale.com/util/clientmetric"
|
||||||
"tailscale.com/util/cmpx"
|
"tailscale.com/util/cmpx"
|
||||||
"tailscale.com/util/winutil"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var errAlreadyMigrated = errors.New("profile migration already completed")
|
var errAlreadyMigrated = errors.New("profile migration already completed")
|
||||||
@ -443,37 +441,18 @@ func (pm *profileManager) NewProfile() {
|
|||||||
pm.currentProfile = &ipn.LoginProfile{}
|
pm.currentProfile = &ipn.LoginProfile{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// defaultPrefs is the default prefs for a new profile.
|
// defaultPrefs is the default prefs for a new profile. This initializes before
|
||||||
|
// even this package's init() so do not rely on other parts of the system being
|
||||||
|
// fully initialized here (for example, syspolicy will not be available on
|
||||||
|
// Apple platforms).
|
||||||
var defaultPrefs = func() ipn.PrefsView {
|
var defaultPrefs = func() ipn.PrefsView {
|
||||||
prefs := ipn.NewPrefs()
|
prefs := ipn.NewPrefs()
|
||||||
prefs.LoggedOut = true
|
prefs.LoggedOut = true
|
||||||
prefs.WantRunning = false
|
prefs.WantRunning = false
|
||||||
|
|
||||||
controlURL, _ := winutil.GetPolicyString("LoginURL")
|
|
||||||
prefs.ControlURL = controlURL
|
|
||||||
|
|
||||||
prefs.ExitNodeIP = resolveExitNodeIP(netip.Addr{})
|
|
||||||
|
|
||||||
// Allow Incoming (used by the UI) is the negation of ShieldsUp (used by the
|
|
||||||
// backend), so this has to convert between the two conventions.
|
|
||||||
shieldsUp, _ := winutil.GetPolicyString("AllowIncomingConnections")
|
|
||||||
prefs.ShieldsUp = shieldsUp == "never"
|
|
||||||
forceDaemon, _ := winutil.GetPolicyString("UnattendedMode")
|
|
||||||
prefs.ForceDaemon = forceDaemon == "always"
|
|
||||||
|
|
||||||
return prefs.View()
|
return prefs.View()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
func resolveExitNodeIP(defIP netip.Addr) (ret netip.Addr) {
|
|
||||||
ret = defIP
|
|
||||||
if exitNode, _ := winutil.GetPolicyString("ExitNodeIP"); exitNode != "" {
|
|
||||||
if ip, err := netip.ParseAddr(exitNode); err == nil {
|
|
||||||
ret = ip
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store returns the StateStore used by the ProfileManager.
|
// Store returns the StateStore used by the ProfileManager.
|
||||||
func (pm *profileManager) Store() ipn.StateStore {
|
func (pm *profileManager) Store() ipn.StateStore {
|
||||||
return pm.store
|
return pm.store
|
||||||
|
@ -67,9 +67,6 @@ func (pm *profileManager) loadLegacyPrefs() (string, ipn.PrefsView, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
prefs.ControlURL = policy.SelectControlURL(defaultPrefs.ControlURL(), prefs.ControlURL)
|
prefs.ControlURL = policy.SelectControlURL(defaultPrefs.ControlURL(), prefs.ControlURL)
|
||||||
prefs.ExitNodeIP = resolveExitNodeIP(prefs.ExitNodeIP)
|
|
||||||
prefs.ShieldsUp = resolveShieldsUp(prefs.ShieldsUp)
|
|
||||||
prefs.ForceDaemon = resolveForceDaemon(prefs.ForceDaemon)
|
|
||||||
|
|
||||||
pm.logf("migrating Windows profile to new format")
|
pm.logf("migrating Windows profile to new format")
|
||||||
return migrationSentinel, prefs.View(), nil
|
return migrationSentinel, prefs.View(), nil
|
||||||
@ -78,13 +75,3 @@ func (pm *profileManager) loadLegacyPrefs() (string, ipn.PrefsView, error) {
|
|||||||
func (pm *profileManager) completeMigration(migrationSentinel string) {
|
func (pm *profileManager) completeMigration(migrationSentinel string) {
|
||||||
atomicfile.WriteFile(migrationSentinel, []byte{}, 0600)
|
atomicfile.WriteFile(migrationSentinel, []byte{}, 0600)
|
||||||
}
|
}
|
||||||
|
|
||||||
func resolveShieldsUp(defval bool) bool {
|
|
||||||
pol := policy.GetPreferenceOptionPolicy("AllowIncomingConnections")
|
|
||||||
return !pol.ShouldEnable(!defval)
|
|
||||||
}
|
|
||||||
|
|
||||||
func resolveForceDaemon(defval bool) bool {
|
|
||||||
pol := policy.GetPreferenceOptionPolicy("UnattendedMode")
|
|
||||||
return pol.ShouldEnable(defval)
|
|
||||||
}
|
|
||||||
|
@ -17,7 +17,8 @@ const (
|
|||||||
ExitNodeIP Key = "ExitNodeIP" // default ""; if blank, no exit node is forced. Value is exit node IP.
|
ExitNodeIP Key = "ExitNodeIP" // default ""; if blank, no exit node is forced. Value is exit node IP.
|
||||||
|
|
||||||
// Keys with a string value that specifies an option: "always", "never", "user-decides".
|
// Keys with a string value that specifies an option: "always", "never", "user-decides".
|
||||||
// The default is "user-decides" unless otherwise stated.
|
// The default is "user-decides" unless otherwise stated. Enforcement of
|
||||||
|
// these policies is typically performed in ipnlocal.applySysPolicy().
|
||||||
EnableIncomingConnections Key = "AllowIncomingConnections"
|
EnableIncomingConnections Key = "AllowIncomingConnections"
|
||||||
EnableServerMode Key = "UnattendedMode"
|
EnableServerMode Key = "UnattendedMode"
|
||||||
ExitNodeAllowLANAccess Key = "ExitNodeAllowLANAccess"
|
ExitNodeAllowLANAccess Key = "ExitNodeAllowLANAccess"
|
||||||
@ -25,7 +26,9 @@ const (
|
|||||||
EnableTailscaleSubnets Key = "UseTailscaleSubnets"
|
EnableTailscaleSubnets Key = "UseTailscaleSubnets"
|
||||||
|
|
||||||
// Keys with a string value that controls visibility: "show", "hide".
|
// Keys with a string value that controls visibility: "show", "hide".
|
||||||
// The default is "show" unless otherwise stated.
|
// The default is "show" unless otherwise stated. Enforcement of these
|
||||||
|
// policies is typically performed by the UI code for the relevant operating
|
||||||
|
// system.
|
||||||
AdminConsoleVisibility Key = "AdminConsole"
|
AdminConsoleVisibility Key = "AdminConsole"
|
||||||
NetworkDevicesVisibility Key = "NetworkDevices"
|
NetworkDevicesVisibility Key = "NetworkDevices"
|
||||||
TestMenuVisibility Key = "TestMenu"
|
TestMenuVisibility Key = "TestMenu"
|
||||||
|
@ -68,6 +68,12 @@ func (p PreferenceOption) ShouldEnable(userChoice bool) bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WillOverride checks if the choice administered by the policy is different
|
||||||
|
// from the user's choice.
|
||||||
|
func (p PreferenceOption) WillOverride(userChoice bool) bool {
|
||||||
|
return p.ShouldEnable(userChoice) != userChoice
|
||||||
|
}
|
||||||
|
|
||||||
// GetPreferenceOption loads a policy from the registry that can be
|
// GetPreferenceOption loads a policy from the registry that can be
|
||||||
// managed by an enterprise policy management system and allows administrative
|
// managed by an enterprise policy management system and allows administrative
|
||||||
// overrides of users' choices in a way that we do not want tailcontrol to have
|
// overrides of users' choices in a way that we do not want tailcontrol to have
|
||||||
|
Loading…
x
Reference in New Issue
Block a user