util/syspolicy/policyclient: add policyclient.Client interface, start plumbing
This is step 2 of ~4, breaking up #14720 into reviewable chunks, with
the aim to make syspolicy be a build-time configurable feature.
Step 1 was #16984.
In this second step, the util/syspolicy/policyclient package is added
with the policyclient.Client interface. This is the interface that's
always present (regardless of build tags), and is what code around the
tree uses to ask syspolicy/MDM questions.
There are two implementations of policyclient.Client for now:
1) NoPolicyClient, which only returns default values.
2) the unexported, temporary 'globalSyspolicy', which is implemented
in terms of the global functions we wish to later eliminate.
This then starts to plumb around the policyclient.Client to most callers.
Future changes will plumb it more. When the last of the global func
callers are gone, then we can unexport the global functions and make a
proper policyclient.Client type and constructor in the syspolicy
package, removing the globalSyspolicy impl out of tsd.
The final change will sprinkle build tags in a few more places and
lock it in with dependency tests to make sure the dependencies don't
later creep back in.
Updates #16998
Updates #12614
Change-Id: Ib2c93d15c15c1f2b981464099177cd492d50391c
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2025-09-01 08:04:17 -07:00
|
|
|
// Copyright (c) Tailscale Inc & AUTHORS
|
|
|
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
|
|
|
|
|
|
// Package policyclient contains the minimal syspolicy interface as needed by
|
|
|
|
|
// client code using syspolicy. It's the part that's always linked in, even if the rest
|
|
|
|
|
// of syspolicy is omitted from the build.
|
|
|
|
|
package policyclient
|
|
|
|
|
|
2025-09-01 15:05:06 -07:00
|
|
|
import (
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
"tailscale.com/util/syspolicy/pkey"
|
|
|
|
|
"tailscale.com/util/syspolicy/ptype"
|
2025-09-02 21:41:06 -07:00
|
|
|
"tailscale.com/util/testenv"
|
2025-09-01 15:05:06 -07:00
|
|
|
)
|
util/syspolicy/policyclient: add policyclient.Client interface, start plumbing
This is step 2 of ~4, breaking up #14720 into reviewable chunks, with
the aim to make syspolicy be a build-time configurable feature.
Step 1 was #16984.
In this second step, the util/syspolicy/policyclient package is added
with the policyclient.Client interface. This is the interface that's
always present (regardless of build tags), and is what code around the
tree uses to ask syspolicy/MDM questions.
There are two implementations of policyclient.Client for now:
1) NoPolicyClient, which only returns default values.
2) the unexported, temporary 'globalSyspolicy', which is implemented
in terms of the global functions we wish to later eliminate.
This then starts to plumb around the policyclient.Client to most callers.
Future changes will plumb it more. When the last of the global func
callers are gone, then we can unexport the global functions and make a
proper policyclient.Client type and constructor in the syspolicy
package, removing the globalSyspolicy impl out of tsd.
The final change will sprinkle build tags in a few more places and
lock it in with dependency tests to make sure the dependencies don't
later creep back in.
Updates #16998
Updates #12614
Change-Id: Ib2c93d15c15c1f2b981464099177cd492d50391c
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2025-09-01 08:04:17 -07:00
|
|
|
|
|
|
|
|
// Client is the interface between code making questions about the system policy
|
|
|
|
|
// and the actual implementation.
|
|
|
|
|
type Client interface {
|
|
|
|
|
// GetString returns a string policy setting with the specified key,
|
|
|
|
|
// or defaultValue (and a nil error) if it does not exist.
|
|
|
|
|
GetString(key pkey.Key, defaultValue string) (string, error)
|
|
|
|
|
|
|
|
|
|
// GetStringArray returns a string array policy setting with the specified key,
|
|
|
|
|
// or defaultValue (and a nil error) if it does not exist.
|
|
|
|
|
GetStringArray(key pkey.Key, defaultValue []string) ([]string, error)
|
|
|
|
|
|
|
|
|
|
// GetBoolean returns a boolean policy setting with the specified key,
|
|
|
|
|
// or defaultValue (and a nil error) if it does not exist.
|
|
|
|
|
GetBoolean(key pkey.Key, defaultValue bool) (bool, error)
|
|
|
|
|
|
2025-09-01 15:05:06 -07:00
|
|
|
// GetUint64 returns a numeric policy setting with the specified key,
|
|
|
|
|
// or defaultValue (and a nil error) if it does not exist.
|
|
|
|
|
GetUint64(key pkey.Key, defaultValue uint64) (uint64, error)
|
|
|
|
|
|
|
|
|
|
// GetDuration loads a policy from the registry that can be managed by an
|
|
|
|
|
// enterprise policy management system and describes a duration for some
|
|
|
|
|
// action. The registry value should be a string that time.ParseDuration
|
|
|
|
|
// understands. If the registry value is "" or can not be processed,
|
|
|
|
|
// defaultValue (and a nil error) is returned instead.
|
|
|
|
|
GetDuration(key pkey.Key, defaultValue time.Duration) (time.Duration, error)
|
|
|
|
|
|
|
|
|
|
// GetPreferenceOption loads a policy from the registry that can be
|
|
|
|
|
// 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
|
|
|
|
|
// the authority to set. It describes user-decides/always/never options, where
|
|
|
|
|
// "always" and "never" remove the user's ability to make a selection. If not
|
2025-09-02 12:49:37 -07:00
|
|
|
// present or set to a different value, defaultValue (and a nil error) is returned.
|
|
|
|
|
GetPreferenceOption(key pkey.Key, defaultValue ptype.PreferenceOption) (ptype.PreferenceOption, error)
|
2025-09-01 15:05:06 -07:00
|
|
|
|
|
|
|
|
// GetVisibility returns whether a UI element should be visible based on
|
|
|
|
|
// the system's configuration.
|
|
|
|
|
// If unconfigured, implementations should return [ptype.VisibleByPolicy]
|
|
|
|
|
// and a nil error.
|
|
|
|
|
GetVisibility(key pkey.Key) (ptype.Visibility, error)
|
|
|
|
|
|
util/syspolicy/policyclient: add policyclient.Client interface, start plumbing
This is step 2 of ~4, breaking up #14720 into reviewable chunks, with
the aim to make syspolicy be a build-time configurable feature.
Step 1 was #16984.
In this second step, the util/syspolicy/policyclient package is added
with the policyclient.Client interface. This is the interface that's
always present (regardless of build tags), and is what code around the
tree uses to ask syspolicy/MDM questions.
There are two implementations of policyclient.Client for now:
1) NoPolicyClient, which only returns default values.
2) the unexported, temporary 'globalSyspolicy', which is implemented
in terms of the global functions we wish to later eliminate.
This then starts to plumb around the policyclient.Client to most callers.
Future changes will plumb it more. When the last of the global func
callers are gone, then we can unexport the global functions and make a
proper policyclient.Client type and constructor in the syspolicy
package, removing the globalSyspolicy impl out of tsd.
The final change will sprinkle build tags in a few more places and
lock it in with dependency tests to make sure the dependencies don't
later creep back in.
Updates #16998
Updates #12614
Change-Id: Ib2c93d15c15c1f2b981464099177cd492d50391c
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2025-09-01 08:04:17 -07:00
|
|
|
// SetDebugLoggingEnabled enables or disables debug logging for the policy client.
|
|
|
|
|
SetDebugLoggingEnabled(enabled bool)
|
|
|
|
|
|
2025-09-01 15:05:06 -07:00
|
|
|
// HasAnyOf returns whether at least one of the specified policy settings is
|
|
|
|
|
// configured, or an error if no keys are provided or the check fails.
|
|
|
|
|
HasAnyOf(keys ...pkey.Key) (bool, error)
|
|
|
|
|
|
util/syspolicy/policyclient: add policyclient.Client interface, start plumbing
This is step 2 of ~4, breaking up #14720 into reviewable chunks, with
the aim to make syspolicy be a build-time configurable feature.
Step 1 was #16984.
In this second step, the util/syspolicy/policyclient package is added
with the policyclient.Client interface. This is the interface that's
always present (regardless of build tags), and is what code around the
tree uses to ask syspolicy/MDM questions.
There are two implementations of policyclient.Client for now:
1) NoPolicyClient, which only returns default values.
2) the unexported, temporary 'globalSyspolicy', which is implemented
in terms of the global functions we wish to later eliminate.
This then starts to plumb around the policyclient.Client to most callers.
Future changes will plumb it more. When the last of the global func
callers are gone, then we can unexport the global functions and make a
proper policyclient.Client type and constructor in the syspolicy
package, removing the globalSyspolicy impl out of tsd.
The final change will sprinkle build tags in a few more places and
lock it in with dependency tests to make sure the dependencies don't
later creep back in.
Updates #16998
Updates #12614
Change-Id: Ib2c93d15c15c1f2b981464099177cd492d50391c
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2025-09-01 08:04:17 -07:00
|
|
|
// RegisterChangeCallback registers a callback function that will be called
|
|
|
|
|
// whenever a policy change is detected. It returns a function to unregister
|
|
|
|
|
// the callback and an error if the registration fails.
|
|
|
|
|
RegisterChangeCallback(cb func(PolicyChange)) (unregister func(), err error)
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-02 12:49:37 -07:00
|
|
|
// Get returns a non-nil [Client] implementation as a function of the
|
|
|
|
|
// build tags. It returns a no-op implementation if the full syspolicy
|
2025-09-02 21:41:06 -07:00
|
|
|
// package is omitted from the build, or in tests.
|
2025-09-02 12:49:37 -07:00
|
|
|
func Get() Client {
|
2025-09-02 21:41:06 -07:00
|
|
|
if testenv.InTest() {
|
|
|
|
|
// This is a little redundant (the Windows implementation at least
|
|
|
|
|
// already does this) but it's here for redundancy and clarity, that we
|
|
|
|
|
// don't want to accidentally use the real system policy when running
|
|
|
|
|
// tests.
|
|
|
|
|
return NoPolicyClient{}
|
|
|
|
|
}
|
2025-09-02 12:49:37 -07:00
|
|
|
return client
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// RegisterClientImpl registers a [Client] implementation to be returned by
|
|
|
|
|
// [Get].
|
|
|
|
|
func RegisterClientImpl(c Client) {
|
|
|
|
|
client = c
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var client Client = NoPolicyClient{}
|
|
|
|
|
|
util/syspolicy/policyclient: add policyclient.Client interface, start plumbing
This is step 2 of ~4, breaking up #14720 into reviewable chunks, with
the aim to make syspolicy be a build-time configurable feature.
Step 1 was #16984.
In this second step, the util/syspolicy/policyclient package is added
with the policyclient.Client interface. This is the interface that's
always present (regardless of build tags), and is what code around the
tree uses to ask syspolicy/MDM questions.
There are two implementations of policyclient.Client for now:
1) NoPolicyClient, which only returns default values.
2) the unexported, temporary 'globalSyspolicy', which is implemented
in terms of the global functions we wish to later eliminate.
This then starts to plumb around the policyclient.Client to most callers.
Future changes will plumb it more. When the last of the global func
callers are gone, then we can unexport the global functions and make a
proper policyclient.Client type and constructor in the syspolicy
package, removing the globalSyspolicy impl out of tsd.
The final change will sprinkle build tags in a few more places and
lock it in with dependency tests to make sure the dependencies don't
later creep back in.
Updates #16998
Updates #12614
Change-Id: Ib2c93d15c15c1f2b981464099177cd492d50391c
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2025-09-01 08:04:17 -07:00
|
|
|
// PolicyChange is the interface representing a change in policy settings.
|
|
|
|
|
type PolicyChange interface {
|
|
|
|
|
// HasChanged reports whether the policy setting identified by the given key
|
|
|
|
|
// has changed.
|
|
|
|
|
HasChanged(pkey.Key) bool
|
|
|
|
|
|
|
|
|
|
// HasChangedAnyOf reports whether any of the provided policy settings
|
|
|
|
|
// changed in this change.
|
|
|
|
|
HasChangedAnyOf(keys ...pkey.Key) bool
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NoPolicyClient is a no-op implementation of [Client] that only
|
|
|
|
|
// returns default values.
|
|
|
|
|
type NoPolicyClient struct{}
|
|
|
|
|
|
2025-09-02 12:49:37 -07:00
|
|
|
var _ Client = NoPolicyClient{}
|
|
|
|
|
|
util/syspolicy/policyclient: add policyclient.Client interface, start plumbing
This is step 2 of ~4, breaking up #14720 into reviewable chunks, with
the aim to make syspolicy be a build-time configurable feature.
Step 1 was #16984.
In this second step, the util/syspolicy/policyclient package is added
with the policyclient.Client interface. This is the interface that's
always present (regardless of build tags), and is what code around the
tree uses to ask syspolicy/MDM questions.
There are two implementations of policyclient.Client for now:
1) NoPolicyClient, which only returns default values.
2) the unexported, temporary 'globalSyspolicy', which is implemented
in terms of the global functions we wish to later eliminate.
This then starts to plumb around the policyclient.Client to most callers.
Future changes will plumb it more. When the last of the global func
callers are gone, then we can unexport the global functions and make a
proper policyclient.Client type and constructor in the syspolicy
package, removing the globalSyspolicy impl out of tsd.
The final change will sprinkle build tags in a few more places and
lock it in with dependency tests to make sure the dependencies don't
later creep back in.
Updates #16998
Updates #12614
Change-Id: Ib2c93d15c15c1f2b981464099177cd492d50391c
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2025-09-01 08:04:17 -07:00
|
|
|
func (NoPolicyClient) GetBoolean(key pkey.Key, defaultValue bool) (bool, error) {
|
|
|
|
|
return defaultValue, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (NoPolicyClient) GetString(key pkey.Key, defaultValue string) (string, error) {
|
|
|
|
|
return defaultValue, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (NoPolicyClient) GetStringArray(key pkey.Key, defaultValue []string) ([]string, error) {
|
|
|
|
|
return defaultValue, nil
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-01 15:05:06 -07:00
|
|
|
func (NoPolicyClient) GetUint64(key pkey.Key, defaultValue uint64) (uint64, error) {
|
|
|
|
|
return defaultValue, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (NoPolicyClient) GetDuration(name pkey.Key, defaultValue time.Duration) (time.Duration, error) {
|
|
|
|
|
return defaultValue, nil
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-02 12:49:37 -07:00
|
|
|
func (NoPolicyClient) GetPreferenceOption(name pkey.Key, defaultValue ptype.PreferenceOption) (ptype.PreferenceOption, error) {
|
|
|
|
|
return defaultValue, nil
|
2025-09-01 15:05:06 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (NoPolicyClient) GetVisibility(name pkey.Key) (ptype.Visibility, error) {
|
|
|
|
|
return ptype.VisibleByPolicy, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (NoPolicyClient) HasAnyOf(keys ...pkey.Key) (bool, error) {
|
|
|
|
|
return false, nil
|
|
|
|
|
}
|
|
|
|
|
|
util/syspolicy/policyclient: add policyclient.Client interface, start plumbing
This is step 2 of ~4, breaking up #14720 into reviewable chunks, with
the aim to make syspolicy be a build-time configurable feature.
Step 1 was #16984.
In this second step, the util/syspolicy/policyclient package is added
with the policyclient.Client interface. This is the interface that's
always present (regardless of build tags), and is what code around the
tree uses to ask syspolicy/MDM questions.
There are two implementations of policyclient.Client for now:
1) NoPolicyClient, which only returns default values.
2) the unexported, temporary 'globalSyspolicy', which is implemented
in terms of the global functions we wish to later eliminate.
This then starts to plumb around the policyclient.Client to most callers.
Future changes will plumb it more. When the last of the global func
callers are gone, then we can unexport the global functions and make a
proper policyclient.Client type and constructor in the syspolicy
package, removing the globalSyspolicy impl out of tsd.
The final change will sprinkle build tags in a few more places and
lock it in with dependency tests to make sure the dependencies don't
later creep back in.
Updates #16998
Updates #12614
Change-Id: Ib2c93d15c15c1f2b981464099177cd492d50391c
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2025-09-01 08:04:17 -07:00
|
|
|
func (NoPolicyClient) SetDebugLoggingEnabled(enabled bool) {}
|
|
|
|
|
|
|
|
|
|
func (NoPolicyClient) RegisterChangeCallback(cb func(PolicyChange)) (unregister func(), err error) {
|
|
|
|
|
return func() {}, nil
|
|
|
|
|
}
|