mirror of
https://github.com/tailscale/tailscale.git
synced 2025-09-21 05:31:36 +02:00
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>
223 lines
6.7 KiB
Go
223 lines
6.7 KiB
Go
// Copyright (c) Tailscale Inc & AUTHORS
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
// Package tsd (short for "Tailscale Daemon") contains a System type that
|
|
// containing all the subsystems a Tailscale node (tailscaled or platform
|
|
// equivalent) uses.
|
|
//
|
|
// The goal of this package (as of 2023-05-03) is to eventually unify
|
|
// initialization across tailscaled, tailscaled as a Windows services, the mac
|
|
// GUI, tsnet, wasm, tests, and other places that wire up all the subsystems.
|
|
// And doing so without weird optional interface accessors on some subsystems
|
|
// that return other subsystems. It's all a work in progress.
|
|
//
|
|
// This package depends on nearly all parts of Tailscale, so it should not be
|
|
// imported by (or thus passed to) any package that does not want to depend on
|
|
// the world. In practice this means that only things like cmd/tailscaled,
|
|
// ipn/ipnlocal, and ipn/ipnserver should import this package.
|
|
package tsd
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
|
|
"tailscale.com/control/controlknobs"
|
|
"tailscale.com/drive"
|
|
"tailscale.com/health"
|
|
"tailscale.com/ipn"
|
|
"tailscale.com/ipn/conffile"
|
|
"tailscale.com/net/dns"
|
|
"tailscale.com/net/netmon"
|
|
"tailscale.com/net/tsdial"
|
|
"tailscale.com/net/tstun"
|
|
"tailscale.com/proxymap"
|
|
"tailscale.com/types/netmap"
|
|
"tailscale.com/util/eventbus"
|
|
"tailscale.com/util/syspolicy/policyclient"
|
|
"tailscale.com/util/usermetric"
|
|
"tailscale.com/wgengine"
|
|
"tailscale.com/wgengine/magicsock"
|
|
"tailscale.com/wgengine/router"
|
|
)
|
|
|
|
// System contains all the subsystems of a Tailscale node (tailscaled, etc.)
|
|
//
|
|
// A valid System value must always have a non-nil Bus populated. Callers must
|
|
// ensure this before using the value further. Call [NewSystem] to obtain a
|
|
// value ready to use.
|
|
type System struct {
|
|
Bus SubSystem[*eventbus.Bus]
|
|
Dialer SubSystem[*tsdial.Dialer]
|
|
DNSManager SubSystem[*dns.Manager] // can get its *resolver.Resolver from DNSManager.Resolver
|
|
Engine SubSystem[wgengine.Engine]
|
|
NetMon SubSystem[*netmon.Monitor]
|
|
MagicSock SubSystem[*magicsock.Conn]
|
|
NetstackRouter SubSystem[bool] // using Netstack at all (either entirely or at least for subnets)
|
|
Router SubSystem[router.Router]
|
|
Tun SubSystem[*tstun.Wrapper]
|
|
StateStore SubSystem[ipn.StateStore]
|
|
Netstack SubSystem[NetstackImpl] // actually a *netstack.Impl
|
|
DriveForLocal SubSystem[drive.FileSystemForLocal]
|
|
DriveForRemote SubSystem[drive.FileSystemForRemote]
|
|
|
|
// InitialConfig is initial server config, if any.
|
|
// It is nil if the node is not in declarative mode.
|
|
// This value is never updated after startup.
|
|
// LocalBackend tracks the current config after any reloads.
|
|
InitialConfig *conffile.Config
|
|
|
|
// onlyNetstack is whether the Tun value is a fake TUN device
|
|
// and we're using netstack for everything.
|
|
onlyNetstack bool
|
|
|
|
controlKnobs controlknobs.Knobs
|
|
proxyMap proxymap.Mapper
|
|
|
|
healthTracker health.Tracker
|
|
userMetricsRegistry usermetric.Registry
|
|
}
|
|
|
|
// NewSystem constructs a new otherwise-empty [System] with a
|
|
// freshly-constructed event bus populated.
|
|
func NewSystem() *System {
|
|
sys := new(System)
|
|
sys.Set(eventbus.New())
|
|
return sys
|
|
}
|
|
|
|
// NetstackImpl is the interface that *netstack.Impl implements.
|
|
// It's an interface for circular dependency reasons: netstack.Impl
|
|
// references LocalBackend, and LocalBackend has a tsd.System.
|
|
type NetstackImpl interface {
|
|
UpdateNetstackIPs(*netmap.NetworkMap)
|
|
}
|
|
|
|
// Set is a convenience method to set a subsystem value.
|
|
// It panics if the type is unknown or has that type
|
|
// has already been set.
|
|
func (s *System) Set(v any) {
|
|
switch v := v.(type) {
|
|
case *eventbus.Bus:
|
|
s.Bus.Set(v)
|
|
case *netmon.Monitor:
|
|
s.NetMon.Set(v)
|
|
case *dns.Manager:
|
|
s.DNSManager.Set(v)
|
|
case *tsdial.Dialer:
|
|
s.Dialer.Set(v)
|
|
case wgengine.Engine:
|
|
s.Engine.Set(v)
|
|
case router.Router:
|
|
s.Router.Set(v)
|
|
case *tstun.Wrapper:
|
|
type ft interface {
|
|
IsFakeTun() bool
|
|
}
|
|
if _, ok := v.Unwrap().(ft); ok {
|
|
s.onlyNetstack = true
|
|
}
|
|
s.Tun.Set(v)
|
|
case *magicsock.Conn:
|
|
s.MagicSock.Set(v)
|
|
case ipn.StateStore:
|
|
s.StateStore.Set(v)
|
|
case NetstackImpl:
|
|
s.Netstack.Set(v)
|
|
case drive.FileSystemForLocal:
|
|
s.DriveForLocal.Set(v)
|
|
case drive.FileSystemForRemote:
|
|
s.DriveForRemote.Set(v)
|
|
default:
|
|
panic(fmt.Sprintf("unknown type %T", v))
|
|
}
|
|
}
|
|
|
|
// IsNetstackRouter reports whether Tailscale is either fully netstack based
|
|
// (without TUN) or is at least using netstack for routing.
|
|
func (s *System) IsNetstackRouter() bool {
|
|
if v, ok := s.NetstackRouter.GetOK(); ok && v {
|
|
return true
|
|
}
|
|
return s.IsNetstack()
|
|
}
|
|
|
|
// IsNetstack reports whether Tailscale is running as a netstack-based TUN-free engine.
|
|
func (s *System) IsNetstack() bool {
|
|
return s.onlyNetstack
|
|
}
|
|
|
|
// ControlKnobs returns the control knobs for this node.
|
|
func (s *System) ControlKnobs() *controlknobs.Knobs {
|
|
return &s.controlKnobs
|
|
}
|
|
|
|
// ProxyMapper returns the ephemeral ip:port mapper.
|
|
func (s *System) ProxyMapper() *proxymap.Mapper {
|
|
return &s.proxyMap
|
|
}
|
|
|
|
// HealthTracker returns the system health tracker.
|
|
func (s *System) HealthTracker() *health.Tracker {
|
|
return &s.healthTracker
|
|
}
|
|
|
|
// UserMetricsRegistry returns the system usermetrics.
|
|
func (s *System) UserMetricsRegistry() *usermetric.Registry {
|
|
return &s.userMetricsRegistry
|
|
}
|
|
|
|
// PolicyClientOrDefault returns the policy client if set or a no-op default
|
|
// otherwise. It always returns a non-nil value.
|
|
func (s *System) PolicyClientOrDefault() policyclient.Client {
|
|
return getPolicyClient()
|
|
}
|
|
|
|
// SubSystem represents some subsystem of the Tailscale node daemon.
|
|
//
|
|
// A subsystem can be set to a value, and then later retrieved. A subsystem
|
|
// value tracks whether it's been set and, once set, doesn't allow the value to
|
|
// change.
|
|
type SubSystem[T any] struct {
|
|
set bool
|
|
v T
|
|
}
|
|
|
|
// Set sets p to v.
|
|
//
|
|
// It panics if p is already set to a different value.
|
|
//
|
|
// Set must not be called concurrently with other Sets or Gets.
|
|
func (p *SubSystem[T]) Set(v T) {
|
|
if p.set {
|
|
var oldVal any = p.v
|
|
var newVal any = v
|
|
if oldVal == newVal {
|
|
// Allow setting to the same value.
|
|
// Note we had to box them through "any" to force them to be comparable.
|
|
// We can't set the type constraint T to be "comparable" because the interfaces
|
|
// aren't comparable. (See https://github.com/golang/go/issues/52531 and
|
|
// https://github.com/golang/go/issues/52614 for some background)
|
|
return
|
|
}
|
|
|
|
var z *T
|
|
panic(fmt.Sprintf("%v is already set", reflect.TypeOf(z).Elem().String()))
|
|
}
|
|
p.v = v
|
|
p.set = true
|
|
}
|
|
|
|
// Get returns the value of p, panicking if it hasn't been set.
|
|
func (p *SubSystem[T]) Get() T {
|
|
if !p.set {
|
|
var z *T
|
|
panic(fmt.Sprintf("%v is not set", reflect.TypeOf(z).Elem().String()))
|
|
}
|
|
return p.v
|
|
}
|
|
|
|
// GetOK returns the value of p (if any) and whether it's been set.
|
|
func (p *SubSystem[T]) GetOK() (_ T, ok bool) {
|
|
return p.v, p.set
|
|
}
|