ipn

package
v1.2.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 11, 2020 License: BSD-3-Clause Imports: 38 Imported by: 93

Documentation

Overview

Package ipn implements the interactions between the Tailscale cloud control plane and the local network stack.

IPN is the abbreviated name for a Tailscale network. What's less clear is what it's an abbreviation for: Identified Private Network? IP Network? Internet Private Network? I Privately Network?

Index

Constants

View Source
const (
	NoState = State(iota)
	InUseOtherUser
	NeedsLogin
	NeedsMachineAuth
	Stopped
	Starting
	Running
)
View Source
const (
	// MachineKeyStateKey is the key under which we store the machine key,
	// in its wgcfg.PrivateKey.MarshalText representation.
	MachineKeyStateKey = StateKey("_machinekey")

	// GlobalDaemonStateKey is the ipn.StateKey that tailscaled
	// loads on startup.
	//
	// We have to support multiple state keys for other OSes (Windows in
	// particular), but right now Unix daemons run with a single
	// node-global state. To keep open the option of having per-user state
	// later, the global state key doesn't look like a username.
	GlobalDaemonStateKey = StateKey("_daemon")

	// ServerModeStartKey's value, if non-empty, is the value of a
	// StateKey containing the prefs to start with which to start the
	// server.
	//
	// For example, the value might be "user-1234", meaning the
	// the server should start with the Prefs JSON loaded from
	// StateKey "user-1234".
	ServerModeStartKey = StateKey("server-mode-start-key")
)
View Source
const GoogleIDTokenType = "ts_android_google_login"

GoogleIDToken Type is the oauth2.Token.TokenType for the Google ID tokens used by the Android client.

View Source
const MaxMessageSize = 10 << 20

MaxMessageSize is the maximum message size, in bytes.

Variables

View Source
var ErrStateNotExist = errors.New("no state with given ID")

ErrStateNotExist is returned by StateStore.ReadState when the requested state ID doesn't exist.

Functions

func ReadMsg

func ReadMsg(r io.Reader) ([]byte, error)

TODO(apenwarr): incremental json decode?

That would let us avoid storing the whole byte array uselessly in RAM.

func SavePrefs

func SavePrefs(filename string, p *Prefs)

func WriteMsg

func WriteMsg(w io.Writer, b []byte) error

TODO(apenwarr): incremental json encode?

That would save RAM, at the expense of having to encode once so that
we can produce the initial byte count.

Types

type Backend

type Backend interface {
	// Start starts or restarts the backend, typically when a
	// frontend client connects.
	Start(Options) error
	// StartLoginInteractive requests to start a new interactive login
	// flow. This should trigger a new BrowseToURL notification
	// eventually.
	StartLoginInteractive()
	// Login logs in with an OAuth2 token.
	Login(token *oauth2.Token)
	// Logout terminates the current login session and stops the
	// wireguard engine.
	Logout()
	// SetPrefs installs a new set of user preferences, including
	// WantRunning. This may cause the wireguard engine to
	// reconfigure or stop.
	SetPrefs(*Prefs)
	// SetWantRunning is like SetPrefs but sets only the
	// WantRunning field.
	SetWantRunning(wantRunning bool)
	// RequestEngineStatus polls for an update from the wireguard
	// engine. Only needed if you want to display byte
	// counts. Connection events are emitted automatically without
	// polling.
	RequestEngineStatus()
	// RequestStatus requests that a full Status update
	// notification is sent.
	RequestStatus()
	// FakeExpireAfter pretends that the current key is going to
	// expire after duration x. This is useful for testing GUIs to
	// make sure they react properly with keys that are going to
	// expire.
	FakeExpireAfter(x time.Duration)
	// Ping attempts to start connecting to the given IP and sends a Notify
	// with its PingResult. If the host is down, there might never
	// be a PingResult sent. The cmd/tailscale CLI client adds a timeout.
	Ping(ip string)
}

Backend is the interface between Tailscale frontends (e.g. cmd/tailscale, iOS/MacOS/Windows GUIs) and the tailscale backend (e.g. cmd/tailscaled) running on the same machine. (It has nothing to do with the interface between the backends and the cloud control plane.)

type BackendClient

type BackendClient struct {

	// AllowVersionSkew controls whether to allow mismatched
	// frontend & backend versions.
	AllowVersionSkew bool
	// contains filtered or unexported fields
}

func NewBackendClient

func NewBackendClient(logf logger.Logf, sendCommandMsg func(jsonb []byte)) *BackendClient

func (*BackendClient) FakeExpireAfter

func (bc *BackendClient) FakeExpireAfter(x time.Duration)

func (*BackendClient) GotNotifyMsg

func (bc *BackendClient) GotNotifyMsg(b []byte)

func (*BackendClient) Login added in v1.0.0

func (bc *BackendClient) Login(token *oauth2.Token)

func (*BackendClient) Logout

func (bc *BackendClient) Logout()

func (*BackendClient) Ping added in v1.2.0

func (bc *BackendClient) Ping(ip string)

func (*BackendClient) Quit

func (bc *BackendClient) Quit() error

func (*BackendClient) RequestEngineStatus

func (bc *BackendClient) RequestEngineStatus()

func (*BackendClient) RequestStatus added in v0.98.0

func (bc *BackendClient) RequestStatus()

func (*BackendClient) SetNotifyCallback added in v0.98.0

func (bc *BackendClient) SetNotifyCallback(fn func(Notify))

func (*BackendClient) SetPrefs

func (bc *BackendClient) SetPrefs(new *Prefs)

func (*BackendClient) SetWantRunning added in v1.2.0

func (bc *BackendClient) SetWantRunning(v bool)

func (*BackendClient) Start

func (bc *BackendClient) Start(opts Options) error

func (*BackendClient) StartLoginInteractive

func (bc *BackendClient) StartLoginInteractive()

type BackendServer

type BackendServer struct {
	GotQuit bool // a Quit command was received
	// contains filtered or unexported fields
}

func NewBackendServer

func NewBackendServer(logf logger.Logf, b Backend, sendNotifyMsg func(b []byte)) *BackendServer

func (*BackendServer) GotCommand

func (bs *BackendServer) GotCommand(cmd *Command) error

func (*BackendServer) GotCommandMsg

func (bs *BackendServer) GotCommandMsg(b []byte) error

GotCommandMsg parses the incoming message b as a JSON Command and calls GotCommand with it.

func (*BackendServer) GotFakeCommand added in v0.98.1

func (bs *BackendServer) GotFakeCommand(cmd *Command) error

func (*BackendServer) Reset

func (bs *BackendServer) Reset() error

func (*BackendServer) SendErrorMessage added in v1.0.0

func (bs *BackendServer) SendErrorMessage(msg string)

func (*BackendServer) SendInUseOtherUserErrorMessage added in v1.2.1

func (bs *BackendServer) SendInUseOtherUserErrorMessage(msg string)

SendInUseOtherUserErrorMessage sends a Notify message to the client that both sets the state to 'InUseOtherUser' and sets the associated reason to msg.

type Command

type Command struct {

	// Version is the binary version of the frontend (the client).
	Version string

	// AllowVersionSkew controls whether it's permitted for the
	// client and server to have a different version. The default
	// (false) means to be strict.
	AllowVersionSkew bool

	// Exactly one of the following must be non-nil.
	Quit                  *NoArgs
	Start                 *StartArgs
	StartLoginInteractive *NoArgs
	Login                 *oauth2.Token
	Logout                *NoArgs
	SetPrefs              *SetPrefsArgs
	SetWantRunning        *bool
	RequestEngineStatus   *NoArgs
	RequestStatus         *NoArgs
	FakeExpireAfter       *FakeExpireAfterArgs
	Ping                  *PingArgs
	// contains filtered or unexported fields
}

Command is a command message that is JSON encoded and sent by a frontend to a backend.

type EngineStatus

type EngineStatus struct {
	RBytes, WBytes wgengine.ByteCount
	NumLive        int
	LiveDERPs      int // number of active DERP connections
	LivePeers      map[tailcfg.NodeKey]wgengine.PeerStatus
}

EngineStatus contains WireGuard engine stats.

type FakeExpireAfterArgs

type FakeExpireAfterArgs struct {
	Duration time.Duration
}

type FileStore

type FileStore struct {
	// contains filtered or unexported fields
}

FileStore is a StateStore that uses a JSON file for persistence.

func NewFileStore

func NewFileStore(path string) (*FileStore, error)

NewFileStore returns a new file store that persists to path.

func (*FileStore) ReadState

func (s *FileStore) ReadState(id StateKey) ([]byte, error)

ReadState implements the StateStore interface.

func (*FileStore) String added in v1.2.0

func (s *FileStore) String() string

func (*FileStore) WriteState

func (s *FileStore) WriteState(id StateKey, bs []byte) error

WriteState implements the StateStore interface.

type Handle

type Handle struct {
	// contains filtered or unexported fields
}

func NewHandle

func NewHandle(b Backend, logf logger.Logf, opts Options) (*Handle, error)

func (*Handle) AdminPageURL

func (h *Handle) AdminPageURL() string

func (*Handle) EngineStatus

func (h *Handle) EngineStatus() EngineStatus

func (*Handle) Expiry

func (h *Handle) Expiry() time.Time

func (*Handle) FakeExpireAfter

func (h *Handle) FakeExpireAfter(x time.Duration)

func (*Handle) LocalAddrs

func (h *Handle) LocalAddrs() []wgcfg.CIDR

func (*Handle) Login added in v1.0.0

func (h *Handle) Login(token *oauth2.Token)

func (*Handle) Logout

func (h *Handle) Logout()

func (*Handle) NetMap

func (h *Handle) NetMap() *controlclient.NetworkMap

func (*Handle) Prefs

func (h *Handle) Prefs() *Prefs

func (*Handle) RequestEngineStatus

func (h *Handle) RequestEngineStatus()

func (*Handle) RequestStatus added in v0.98.0

func (h *Handle) RequestStatus()

func (*Handle) Reset

func (h *Handle) Reset()

func (*Handle) Start

func (h *Handle) Start(opts Options) error

func (*Handle) StartLoginInteractive

func (h *Handle) StartLoginInteractive()

func (*Handle) State

func (h *Handle) State() State

func (*Handle) UpdatePrefs

func (h *Handle) UpdatePrefs(updateFn func(p *Prefs))

type LocalBackend

type LocalBackend struct {
	// contains filtered or unexported fields
}

LocalBackend is the glue between the major pieces of the Tailscale network software: the cloud control plane (via controlclient), the network data plane (via wgengine), and the user-facing UIs and CLIs (collectively called "frontends", via LocalBackend's implementation of the Backend interface).

LocalBackend implements the overall state machine for the Tailscale application. Frontends, controlclient and wgengine can feed events into LocalBackend to advance the state machine, and advancing the state machine generates events back out to zero or more components.

func NewLocalBackend

func NewLocalBackend(logf logger.Logf, logid string, store StateStore, e wgengine.Engine) (*LocalBackend, error)

NewLocalBackend returns a new LocalBackend that is ready to run, but is not actually running.

func (*LocalBackend) FakeExpireAfter

func (b *LocalBackend) FakeExpireAfter(x time.Duration)

FakeExpireAfter implements Backend.

func (*LocalBackend) InServerMode added in v1.2.1

func (b *LocalBackend) InServerMode() bool

func (*LocalBackend) Login added in v1.0.0

func (b *LocalBackend) Login(token *oauth2.Token)

Login implements Backend.

func (*LocalBackend) Logout

func (b *LocalBackend) Logout()

Logout tells the controlclient that we want to log out, and transitions the local engine to the logged-out state without waiting for controlclient to be in that state.

TODO(danderson): controlclient Logout does nothing useful, and we shouldn't be transitioning to a state based on what we believe controlclient may have done.

NOTE(apenwarr): No easy way to persist logged-out status.

Maybe that's for the better; if someone logs out accidentally,
rebooting will fix it.

func (*LocalBackend) NetMap

func (b *LocalBackend) NetMap() *controlclient.NetworkMap

NetMap returns the latest cached network map received from controlclient, or nil if no network map was received yet.

func (*LocalBackend) Ping added in v1.2.0

func (b *LocalBackend) Ping(ipStr string)

func (*LocalBackend) RequestEngineStatus

func (b *LocalBackend) RequestEngineStatus()

RequestEngineStatus implements Backend.

func (*LocalBackend) RequestStatus added in v0.98.0

func (b *LocalBackend) RequestStatus()

RequestStatus implements Backend.

func (*LocalBackend) SetCurrentUserID added in v1.2.0

func (b *LocalBackend) SetCurrentUserID(uid string)

func (*LocalBackend) SetDecompressor

func (b *LocalBackend) SetDecompressor(fn func() (controlclient.Decompressor, error))

SetDecompressor sets a decompression function, which must be a zstd reader.

This exists because the iOS/Mac NetworkExtension is very resource constrained, and the zstd package is too heavy to fit in the constrained RSS limit.

func (*LocalBackend) SetPrefs

func (b *LocalBackend) SetPrefs(newp *Prefs)

SetPrefs saves new user preferences and propagates them throughout the system. Implements Backend.

func (*LocalBackend) SetWantRunning added in v1.2.0

func (b *LocalBackend) SetWantRunning(wantRunning bool)

func (*LocalBackend) Shutdown

func (b *LocalBackend) Shutdown()

Shutdown halts the backend and all its sub-components. The backend can no longer be used after Shutdown returns.

func (*LocalBackend) Start

func (b *LocalBackend) Start(opts Options) error

Start applies the configuration specified in opts, and starts the state machine.

TODO(danderson): this function is trying to do too many things at once: it loads state, or imports it, or updates prefs sometimes, contains some settings that are one-shot things done by `tailscale up` because we had nowhere else to put them, and there's no clear guarantee that switching from one user's state to another is actually a supported operation (it should be, but it's very unclear from the following whether or not that is a safe transition).

func (*LocalBackend) StartLoginInteractive

func (b *LocalBackend) StartLoginInteractive()

StartLoginInteractive implements Backend. It requests a new interactive login from controlclient, unless such a flow is already in progress, in which case StartLoginInteractive attempts to pick up the in-progress flow where it left off.

func (*LocalBackend) State

func (b *LocalBackend) State() State

State returns the backend state machine's current state.

func (*LocalBackend) Status added in v0.98.0

func (b *LocalBackend) Status() *ipnstate.Status

Status returns the latest status of the backend and its sub-components.

func (*LocalBackend) TestOnlyPublicKeys added in v0.98.1

func (b *LocalBackend) TestOnlyPublicKeys() (machineKey tailcfg.MachineKey, nodeKey tailcfg.NodeKey)

TestOnlyPublicKeys returns the current machine and node public keys. Used in tests only to facilitate automated node authorization in the test harness.

func (*LocalBackend) UpdateStatus added in v0.98.0

func (b *LocalBackend) UpdateStatus(sb *ipnstate.StatusBuilder)

UpdateStatus implements ipnstate.StatusUpdater.

type MemoryStore

type MemoryStore struct {
	// contains filtered or unexported fields
}

MemoryStore is a store that keeps state in memory only.

func (*MemoryStore) ReadState

func (s *MemoryStore) ReadState(id StateKey) ([]byte, error)

ReadState implements the StateStore interface.

func (*MemoryStore) String added in v1.2.0

func (s *MemoryStore) String() string

func (*MemoryStore) WriteState

func (s *MemoryStore) WriteState(id StateKey, bs []byte) error

WriteState implements the StateStore interface.

type NoArgs

type NoArgs struct{}

type Notify

type Notify struct {
	Version       string                    // version number of IPN backend
	ErrMessage    *string                   // critical error message, if any; for InUseOtherUser, the details
	LoginFinished *empty.Message            // event: non-nil when login process succeeded
	State         *State                    // current IPN state has changed
	Prefs         *Prefs                    // preferences were changed
	NetMap        *controlclient.NetworkMap // new netmap received
	Engine        *EngineStatus             // wireguard engine stats
	Status        *ipnstate.Status          // full status
	BrowseToURL   *string                   // UI should open a browser right now
	BackendLogID  *string                   // public logtail id used by backend
	PingResult    *ipnstate.PingResult

	// LocalTCPPort, if non-nil, informs the UI frontend which
	// (non-zero) localhost TCP port it's listening on.
	// This is currently only used by Tailscale when run in the
	// macOS Network Extension.
	LocalTCPPort *uint16 `json:",omitempty"`
	// contains filtered or unexported fields
}

Notify is a communication from a backend (e.g. tailscaled) to a frontend (cmd/tailscale, iOS, macOS, Win Tasktray). In any given notification, any or all of these may be nil, meaning that they have not changed. They are JSON-encoded on the wire, despite the lack of struct tags.

type Options

type Options struct {
	// FrontendLogID is the public logtail id used by the frontend.
	FrontendLogID string
	// StateKey and Prefs together define the state the backend should
	// use:
	//  - StateKey=="" && Prefs!=nil: use Prefs for internal state,
	//    don't persist changes in the backend, except for the machine key
	//    for migration purposes.
	//  - StateKey!="" && Prefs==nil: load the given backend-side
	//    state and use/update that.
	//  - StateKey!="" && Prefs!=nil: like the previous case, but do
	//    an initial overwrite of backend state with Prefs.
	StateKey StateKey
	Prefs    *Prefs
	// AuthKey is an optional node auth key used to authorize a
	// new node key without user interaction.
	AuthKey string
	// LegacyConfigPath optionally specifies the old-style relaynode
	// relay.conf location. If both LegacyConfigPath and StateKey are
	// specified and the requested state doesn't exist in the backend
	// store, the backend migrates the config from LegacyConfigPath.
	//
	// TODO(danderson): remove some time after the transition to
	// tailscaled is done.
	LegacyConfigPath string
	// Notify is called when backend events happen.
	Notify func(Notify) `json:"-"`
	// HTTPTestClient is an optional HTTP client to pass to controlclient
	// (for tests only).
	HTTPTestClient *http.Client
}

type PingArgs added in v1.2.0

type PingArgs struct {
	IP string
}

type Prefs

type Prefs struct {
	// ControlURL is the URL of the control server to use.
	ControlURL string

	// RouteAll specifies whether to accept subnet and default routes
	// advertised by other nodes on the Tailscale network.
	RouteAll bool

	// AllowSingleHosts specifies whether to install routes for each
	// node IP on the tailscale network, in addition to a route for
	// the whole network.
	// This corresponds to the "tailscale up --host-routes" value,
	// which defaults to true.
	//
	// TODO(danderson): why do we have this? It dumps a lot of stuff
	// into the routing table, and a single network route _should_ be
	// all that we need. But when I turn this off in my tailscaled,
	// packets stop flowing. What's up with that?
	AllowSingleHosts bool

	// CorpDNS specifies whether to install the Tailscale network's
	// DNS configuration, if it exists.
	CorpDNS bool

	// WantRunning indicates whether networking should be active on
	// this node.
	WantRunning bool

	// ShieldsUp indicates whether to block all incoming connections,
	// regardless of the control-provided packet filter. If false, we
	// use the packet filter as provided. If true, we block incoming
	// connections.
	ShieldsUp bool

	// AdvertiseTags specifies groups that this node wants to join, for
	// purposes of ACL enforcement. These can be referenced from the ACL
	// security policy. Note that advertising a tag doesn't guarantee that
	// the control server will allow you to take on the rights for that
	// tag.
	AdvertiseTags []string

	// Hostname is the hostname to use for identifying the node. If
	// not set, os.Hostname is used.
	Hostname string

	// OSVersion overrides tailcfg.Hostinfo's OSVersion.
	OSVersion string

	// DeviceModel overrides tailcfg.Hostinfo's DeviceModel.
	DeviceModel string

	// NotepadURLs is a debugging setting that opens OAuth URLs in
	// notepad.exe on Windows, rather than loading them in a browser.
	//
	// apenwarr 2020-04-29: Unfortunately this is still needed sometimes.
	// Windows' default browser setting is sometimes screwy and this helps
	// users narrow it down a bit.
	NotepadURLs bool

	// ForceDaemon specifies whether a platform that normally
	// operates in "client mode" (that is, requires an active user
	// logged in with the GUI app running) should keep running after the
	// GUI ends and/or the user logs out.
	//
	// The only current applicable platform is Windows. This
	// forced Windows to go into "server mode" where Tailscale is
	// running even with no users logged in. This might also be
	// used for macOS in the future. This setting has no effect
	// for Linux/etc, which always operate in daemon mode.
	ForceDaemon bool `json:"ForceDaemon,omitempty"`

	// AdvertiseRoutes specifies CIDR prefixes to advertise into the
	// Tailscale network as reachable through the current
	// node.
	AdvertiseRoutes []wgcfg.CIDR

	// NoSNAT specifies whether to source NAT traffic going to
	// destinations in AdvertiseRoutes. The default is to apply source
	// NAT, which makes the traffic appear to come from the router
	// machine rather than the peer's Tailscale IP.
	//
	// Disabling SNAT requires additional manual configuration in your
	// network to route Tailscale traffic back to the subnet relay
	// machine.
	//
	// Linux-only.
	NoSNAT bool

	// NetfilterMode specifies how much to manage netfilter rules for
	// Tailscale, if at all.
	NetfilterMode router.NetfilterMode

	// The Persist field is named 'Config' in the file for backward
	// compatibility with earlier versions.
	// TODO(apenwarr): We should move this out of here, it's not a pref.
	//  We can maybe do that once we're sure which module should persist
	//  it (backend or frontend?)
	Persist *controlclient.Persist `json:"Config"`
}

Prefs are the user modifiable settings of the Tailscale node agent.

func LoadPrefs

func LoadPrefs(filename string) (*Prefs, error)

LoadPrefs loads a legacy relaynode config file into Prefs with sensible migration defaults set.

func NewPrefs

func NewPrefs() *Prefs

func PrefsFromBytes

func PrefsFromBytes(b []byte, enforceDefaults bool) (*Prefs, error)

PrefsFromBytes deserializes Prefs from a JSON blob. If enforceDefaults is true, Prefs.RouteAll and Prefs.AllowSingleHosts are forced on.

func (*Prefs) Clone

func (src *Prefs) Clone() *Prefs

Clone makes a deep copy of Prefs. The result aliases no memory with the original.

func (*Prefs) Equals

func (p *Prefs) Equals(p2 *Prefs) bool

func (*Prefs) IsEmpty

func (p *Prefs) IsEmpty() bool

IsEmpty reports whether p is nil or pointing to a Prefs zero value.

func (*Prefs) Pretty

func (p *Prefs) Pretty() string

func (*Prefs) ToBytes

func (p *Prefs) ToBytes() []byte

type SetPrefsArgs

type SetPrefsArgs struct {
	New *Prefs
}

type StartArgs

type StartArgs struct {
	Opts Options
}

type State

type State int

func (State) String

func (s State) String() string

type StateKey

type StateKey string

StateKey is an opaque identifier for a set of LocalBackend state (preferences, private keys, etc.).

The reason we need this is that the Tailscale agent may be running on a multi-user machine, in a context where a single daemon is shared by several consecutive users. Ideally we would just use the username of the connected frontend as the StateKey.

Various platforms currently set StateKey in different ways:

  • the macOS/iOS GUI apps set it to "ipn-go-bridge"
  • the Android app sets it to "ipn-android"
  • on Windows, it's the empty string (in client mode) or, via LocalBackend.userID, a string like "user-$USER_ID" (used in server mode).
  • on Linux/etc, it's always "_daemon" (ipn.GlobalDaemonStateKey)

type StateStore

type StateStore interface {
	// ReadState returns the bytes associated with ID. Returns (nil,
	// ErrStateNotExist) if the ID doesn't have associated state.
	ReadState(id StateKey) ([]byte, error)
	// WriteState saves bs as the state associated with ID.
	WriteState(id StateKey, bs []byte) error
}

StateStore persists state, and produces it back on request.

Directories

Path Synopsis
Package ipnstate captures the entire state of the Tailscale network.
Package ipnstate captures the entire state of the Tailscale network.
Package policy contains various policy decisions that need to be shared between the node client & control server.
Package policy contains various policy decisions that need to be shared between the node client & control server.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL