ipnext
packageThis package is not in the latest version of its module.
Details
Validgo.mod file
The Go module system was introduced in Go 1.11 and is the official dependency management solution for Go.
Redistributable license
Redistributable licenses place minimal restrictions on how software can be used, modified, and redistributed.
Tagged version
Modules with tagged versions give importers more predictable builds.
Stable version
When a project reaches major version v1 it is considered stable.
- Learn more about best practices
Repository
Links
Documentation¶
Overview¶
Package ipnext defines types and interfaces used for extending the core LocalBackendfunctionality with additional features and services.
Index¶
- Variables
- func Extensions() iter.Seq[*Definition]
- func RegisterExtension(name string, newExt NewExtensionFn)
- type AuditLogProvider
- type Definition
- type Extension
- type ExtensionServices
- type Hooks
- type Host
- type NewControlClientCallback
- type NewExtensionFn
- type NodeBackend
- type ProfileResolver
- type ProfileServices
- type ProfileStateChangeCallback
- type ProfileStore
- type SafeBackend
Constants¶
This section is empty.
Variables¶
var SkipExtension =errors.New("skipping extension")SkipExtension is an error returned byNewExtensionFn to indicate that the extensionshould be skipped rather than prevent the LocalBackend from starting.
Skipping an extension should be reserved for cases where the extension is not supportedon the current platform or configuration, or depends on a feature that is not available,or otherwise should be disabled permanently rather than temporarily.
Specifically, it must not be returned if the extension is not required right nowbased on user preferences, policy settings, the current tailnet, or other factorsthat may change throughout the LocalBackend's lifetime.
Functions¶
funcExtensions¶
func Extensions()iter.Seq[*Definition]
Extensions iterates over the extensions in the order they were registeredviaRegisterExtension.
funcRegisterExtension¶
func RegisterExtension(namestring, newExtNewExtensionFn)
RegisterExtension registers a function that instantiates anExtension.The name must be the same as returned by the extension's [Extension.Name].
It must be called on the main goroutine before LocalBackend is created,such as from an init function of the package implementing the extension.
It panics if newExt is nil or if an extension with the same namehas already been registered.
Types¶
typeAuditLogProvider¶
type AuditLogProvider func()ipnauth.AuditLogFunc
AuditLogProvider is a function that returns anipnauth.AuditLogFunc forlogging auditable actions.
typeDefinition¶
type Definition struct {// contains filtered or unexported fields}Definition describes a registeredExtension.
funcDefinitionForTest¶
func DefinitionForTest(extExtension) *Definition
DefinitionForTest returns aDefinition for the specifiedExtension.It is primarily used for testing where the test code needs to instantiateand use an extension without registering it.
funcDefinitionWithErrForTest¶
func DefinitionWithErrForTest(namestring, errerror) *Definition
DefinitionWithErrForTest returns aDefinition with the specified extension namewhoseDefinition.MakeExtension method returns the specified error.It is used for testing.
func (*Definition)MakeExtension¶
func (d *Definition) MakeExtension(logflogger.Logf, sbSafeBackend) (Extension,error)
MakeExtension instantiates the extension.
typeExtension¶
type Extension interface {// Name is a unique name of the extension.// It must be the same as the name used to register the extension.Name()string// Init is called to initialize the extension when LocalBackend's// Start method is called. Extensions are created but not initialized// unless LocalBackend is started.//// If the extension cannot be initialized, it must return an error,// and its Shutdown method will not be called on the host's shutdown.// Returned errors are not fatal; they are used for logging.// A [SkipExtension] error indicates an intentional decision rather than a failure.Init(Host)error// Shutdown is called when LocalBackend is shutting down,// provided the extension was initialized. For multiple extensions,// Shutdown is called in the reverse order of Init.// Returned errors are not fatal; they are used for logging.// After a call to Shutdown, the extension will not be called again.Shutdown()error}Extension augments LocalBackend with additional functionality.
An extension uses the providedHost to register callbacksand interact with the backend in a controlled, well-definedand thread-safe manner.
Extensions are registered usingRegisterExtension.
They must be safe for concurrent use.
typeExtensionServices¶
type ExtensionServices interface {// FindExtensionByName returns an active extension with the given name,// or nil if no such extension exists.FindExtensionByName(namestring)any// FindMatchingExtension finds the first active extension that matches target,// and if one is found, sets target to that extension and returns true.// Otherwise, it returns false.//// It panics if target is not a non-nil pointer to either a type// that implements [ipnext.Extension], or to any interface type.FindMatchingExtension(targetany)bool}ExtensionServices provides access to theHost's extension management services,such as fetching active extensions.
typeHooks¶
type Hooks struct {// BackendStateChange is called when the backend state changes.BackendStateChangefeature.Hooks[func(ipn.State)]// ProfileStateChange contains callbacks that are invoked when the current login profile// or its [ipn.Prefs] change, after those changes have been made. The current login profile// may be changed either because of a profile switch, or because the profile information// was updated by [LocalBackend.SetControlClientStatus], including when the profile// is first populated and persisted.ProfileStateChangefeature.Hooks[ProfileStateChangeCallback]// BackgroundProfileResolvers are registered background profile resolvers.// They're used to determine the profile to use when no GUI/CLI client is connected.//// TODO(nickkhyl): allow specifying some kind of priority/altitude for the resolver.// TODO(nickkhyl): make it a "profile resolver" instead of a "background profile resolver".// The concepts of the "current user", "foreground profile" and "background profile"// only exist on Windows, and we're moving away from them anyway.BackgroundProfileResolversfeature.Hooks[ProfileResolver]// AuditLoggers are registered [AuditLogProvider]s.// Each provider is called to get an [ipnauth.AuditLogFunc] when an auditable action// is about to be performed. If an audit logger returns an error, the action is denied.AuditLoggersfeature.Hooks[AuditLogProvider]// NewControlClient are the functions to be called when a new control client// is created. It is called with the LocalBackend locked.NewControlClientfeature.Hooks[NewControlClientCallback]// OnSelfChange is called (with LocalBackend.mu held) when the self node// changes, including changing to nothing (an invalid view).OnSelfChangefeature.Hooks[func(tailcfg.NodeView)]// MutateNotifyLocked is called to optionally mutate the provided Notify// before sending it to the IPN bus. It is called with LocalBackend.mu held.MutateNotifyLockedfeature.Hooks[func(*ipn.Notify)]// SetPeerStatus is called to mutate PeerStatus.// Callers must only use NodeBackend to read data.SetPeerStatusfeature.Hooks[func(*ipnstate.PeerStatus,tailcfg.NodeView,NodeBackend)]// ShouldUploadServices reports whether this node should include services// in Hostinfo from the portlist extension.ShouldUploadServicesfeature.Hook[func()bool]}Hooks is a collection of hooks that extensions can add to (non-concurrently)during program initialization and can be called by LocalBackend and others atruntime.
Each hook has its own rules about when it's called and what environment ithas access to and what it's allowed to do.
typeHost¶
type Host interface {// Extensions returns the host's [ExtensionServices].Extensions()ExtensionServices// Profiles returns the host's [ProfileServices].Profiles()ProfileServices// AuditLogger returns a function that calls all currently registered audit loggers.// The function fails if any logger returns an error, indicating that the action// cannot be logged and must not be performed.//// The returned function captures the current state (e.g., the current profile) at// the time of the call and must not be persisted.AuditLogger()ipnauth.AuditLogFunc// Hooks returns a non-nil pointer to a [Hooks] struct.// Hooks must not be modified concurrently or after Tailscale has started.Hooks() *Hooks// SendNotifyAsync sends a notification to the IPN bus,// typically to the GUI client.SendNotifyAsync(ipn.Notify)// NodeBackend returns the [NodeBackend] for the currently active node// (which is approximately the same as the current profile).NodeBackend()NodeBackend}Host is the API surface used by [Extension]s to interact with LocalBackendin a controlled manner.
Extensions can register callbacks, request information, or perform actionsvia theHost interface.
Typically, the host invokes registered callbacks when one of the following occurs:
- LocalBackend notifies it of an event or state change that may beof interest to extensions, such as when switchingipn.LoginProfile.
- LocalBackend needs to consult extensions for information, for example,determining the most appropriate profile for the current state of the system.
- LocalBackend performs an extensible action, such as logging an auditable event,and delegates its execution to the extension.
The callbacks are invoked synchronously, and the LocalBackend's stateremains unchanged while callbacks execute.
In contrast, actions initiated by extensions are generally asynchronous,as indicated by the "Async" suffix in their names.Performing actions may result in callbacks being invoked as described above.
To prevent conflicts between extensions competing for shared state,such as the current profile or prefs, the host must not expose methodsthat directly modify that state. For example, instead of allowing extensionsto switch profiles at-will, the host'sProfileServices provides a methodto switch to the "best" profile. The host can then consult extensionsto determine the appropriate profile to use and resolve any conflictsin a controlled manner.
A host must be safe for concurrent use.
typeNewControlClientCallback¶
type NewControlClientCallback func(controlclient.Client,ipn.LoginProfileView) (cleanup func())
NewControlClientCallback is a function to be called when a newcontrolclient.Clientis created and before it is first used. The specified profile represents the nodefor which the cc is created and is always valid. Itsipn.LoginProfileView.IDreturns "" if it is a new node whose profile has never been persisted.
If thecontrolclient.Client is created due to a profile switch, any registered[ProfileStateChangeCallback]s are called first.
It returns a function to be called when the cc is being shut down,or nil if no cleanup is needed. That cleanup function should not callback into LocalBackend, which may be locked during shutdown.
typeNewExtensionFn¶
type NewExtensionFn func(logger.Logf,SafeBackend) (Extension,error)
NewExtensionFn is a function that instantiates anExtension.If a registered extension cannot be instantiated, the function must return an error.If the extension should be skipped at runtime, it must return eitherSkipExtensionor a wrappedSkipExtension. Any other error returned is fatal and will preventthe LocalBackend from starting.
typeNodeBackend¶
type NodeBackend interface {// AppendMatchingPeers appends all peers that match the predicate// to the base slice and returns it.AppendMatchingPeers(base []tailcfg.NodeView, pred func(tailcfg.NodeView)bool) []tailcfg.NodeView// PeerCaps returns the capabilities that src has to this node.PeerCaps(srcnetip.Addr)tailcfg.PeerCapMap// PeerHasCap reports whether the peer has the specified peer capability.PeerHasCap(peertailcfg.NodeView, captailcfg.PeerCapability)bool// PeerAPIBase returns the "http://ip:port" URL base to reach peer's// PeerAPI, or the empty string if the peer is invalid or doesn't support// PeerAPI.PeerAPIBase(tailcfg.NodeView)string// PeerHasPeerAPI whether the provided peer supports PeerAPI.//// It effectively just reports whether PeerAPIBase(node) is non-empty, but// potentially more efficiently.PeerHasPeerAPI(tailcfg.NodeView)bool// CollectServices reports whether the control plane is telling this// node that the portlist service collection is desirable, should it// choose to report them.CollectServices()bool}NodeBackend is an interface to query the current node and its peers.
It is not a snapshot in time but is locked to a particular node.
typeProfileResolver¶
type ProfileResolver func(ProfileStore)ipn.LoginProfileView
ProfileResolver is a function that returns a read-only view of a login profile.An invalid view indicates no profile. A valid profile view with an emptyipn.ProfileIDindicates that the profile is new and has not been persisted yet.The providedProfileStore can only be used for the duration of the callback.
typeProfileServices¶
type ProfileServices interface {// CurrentProfileState returns read-only views of the current profile// and its preferences. The returned views are always valid,// but the profile's [ipn.LoginProfileView.ID] returns ""// if the profile is new and has not been persisted yet.//// The returned views are immutable snapshots of the current profile// and prefs at the time of the call. The actual state is only guaranteed// to remain unchanged and match these views for the duration// of a callback invoked by the host, if used within that callback.//// Extensions that need the current profile or prefs at other times// should typically subscribe to [ProfileStateChangeCallback]// to be notified if the profile or prefs change after retrieval.// CurrentProfileState returns both the profile and prefs// to guarantee that they are consistent with each other.CurrentProfileState() (ipn.LoginProfileView,ipn.PrefsView)// CurrentPrefs is like [CurrentProfileState] but only returns prefs.CurrentPrefs()ipn.PrefsView// SwitchToBestProfileAsync asynchronously selects the best profile to use// and switches to it, unless it is already the current profile.//// If an extension needs to know when a profile switch occurs,// it must use [ProfileServices.RegisterProfileStateChangeCallback]// to register a [ProfileStateChangeCallback].//// The reason indicates why the profile is being switched, such as due// to a client connecting or disconnecting or a change in the desktop// session state. It is used for logging.SwitchToBestProfileAsync(reasonstring)}ProfileServices provides access to theHost's profile management services,such as switching profiles and registering profile change callbacks.
typeProfileStateChangeCallback¶
type ProfileStateChangeCallback func(_ipn.LoginProfileView, _ipn.PrefsView, sameNodebool)
ProfileStateChangeCallback is a function to be called when the current login profileor its preferences change.
The sameNode parameter indicates whether the profile represents the same node as before,which is true when:
- Only the profile'sipn.Prefs or metadata (e.g.,tailcfg.UserProfile) have changed,but the node ID andipn.ProfileID remain the same.
- The profile has been persisted and assigned anipn.ProfileID for the first time,so while its node ID andipn.ProfileID have changed, it is still the same profile.
It can be used to decide whether to reset state bound to the current profile or node identity.
The profile and prefs are always valid, but the profile'sipn.LoginProfileView.IDreturns "" if the profile is new and has not been persisted yet.
typeProfileStore¶
type ProfileStore interface {// CurrentUserID returns the current user ID. It is only non-empty on// Windows where we have a multi-user system.//// Deprecated: this method exists for compatibility with the current (as of 2024-08-27)// permission model and will be removed as we progress on tailscale/corp#18342.CurrentUserID()ipn.WindowsUserID// CurrentProfile returns a read-only [ipn.LoginProfileView] of the current profile.// The returned view is always valid, but the profile's [ipn.LoginProfileView.ID]// returns "" if the profile is new and has not been persisted yet.CurrentProfile()ipn.LoginProfileView// CurrentPrefs returns a read-only view of the current prefs.// The returned view is always valid.CurrentPrefs()ipn.PrefsView// DefaultUserProfile returns a read-only view of the default (last used) profile for the specified user.// It returns a read-only view of a new, non-persisted profile if the specified user does not have a default profile.DefaultUserProfile(uidipn.WindowsUserID)ipn.LoginProfileView}ProfileStore provides read-only access to available login profiles and their preferences.It is not safe for concurrent use and can only be used from the callback it is passed to.