tailnet

package
v2.10.2 Latest Latest
Warning

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

Go to latest
Published: Apr 22, 2024 License: AGPL-3.0 Imports: 64 Imported by: 0

Documentation

Index

Constants

View Source
const (
	WorkspaceAgentSSHPort             = 1
	WorkspaceAgentReconnectingPTYPort = 2
	WorkspaceAgentSpeedtestPort       = 3
)
View Source
const (
	// WriteTimeout is the amount of time we wait to write a node update to a connection before we
	// declare it hung. It is exported so that tests can use it.
	WriteTimeout = time.Second * 5
	// ResponseBufferSize is the max number of responses to buffer per connection before we start
	// dropping updates
	ResponseBufferSize = 512
	// RequestBufferSize is the max number of requests to buffer per connection
	RequestBufferSize = 32
)
View Source
const DisableSTUN = "disable"
View Source
const EnvMagicsockDebugLogging = "CODER_MAGICSOCK_DEBUG_LOGGING"

EnvMagicsockDebugLogging enables super-verbose logging for the magicsock internals. A logger must be supplied to the connection with the debug level enabled.

With this disabled, you still get a lot of output if you have a valid logger with the debug level enabled.

View Source
const LoggerName = "coord"

Variables

View Source
var (
	ErrClosed         = xerrors.New("coordinator is closed")
	ErrWouldBlock     = xerrors.New("would block")
	ErrAlreadyRemoved = xerrors.New("already removed")
)
View Source
var ErrConnClosed = xerrors.New("connection closed")
View Source
var ErrMultiAgentClosed = xerrors.New("multiagent is closed")

Functions

func CompareDERPMaps

func CompareDERPMaps(a *tailcfg.DERPMap, b *tailcfg.DERPMap) bool

CompareDERPMaps returns true if the given DERPMaps are equivalent. Ordering of slices is ignored.

If the first map is nil, the second map must also be nil for them to be considered equivalent. If the second map is nil, the first map can be any value and the function will return true.

func DERPMapFromProto added in v2.6.0

func DERPMapFromProto(derpMap *proto.DERPMap) *tailcfg.DERPMap

func DERPMapToProto added in v2.6.0

func DERPMapToProto(derpMap *tailcfg.DERPMap) *proto.DERPMap

func DERPNodeFromProto added in v2.6.0

func DERPNodeFromProto(node *proto.DERPMap_Region_Node) *tailcfg.DERPNode

func DERPNodeToProto added in v2.6.0

func DERPNodeToProto(node *tailcfg.DERPNode) *proto.DERPMap_Region_Node

func DERPRegionFromProto added in v2.6.0

func DERPRegionFromProto(region *proto.DERPMap_Region) *tailcfg.DERPRegion

func DERPRegionToProto added in v2.6.0

func DERPRegionToProto(region *tailcfg.DERPRegion) *proto.DERPMap_Region

func IP

func IP() netip.Addr

IP generates a random IP with a static service prefix.

func IPFromUUID

func IPFromUUID(uid uuid.UUID) netip.Addr

IP generates a new IP from a UUID.

func Logger

func Logger(logger slog.Logger) tslogger.Logf

Logger converts the Tailscale logging function to use slog.

func NewDERPMap

func NewDERPMap(ctx context.Context, region *tailcfg.DERPRegion, stunAddrs []string, remoteURL, localPath string, disableSTUN bool) (*tailcfg.DERPMap, error)

NewDERPMap constructs a DERPMap from a set of STUN addresses and optionally a remote URL to fetch a mapping from e.g. https://controlplane.tailscale.com/derpmap/default.

func NewDRPCClient added in v2.6.0

func NewDRPCClient(conn net.Conn, logger slog.Logger) (proto.DRPCTailnetClient, error)

func NodeID

func NodeID(uid uuid.UUID) tailcfg.NodeID

NodeID creates a Tailscale NodeID from the last 8 bytes of a UUID. It ensures the returned NodeID is always positive.

func NodeToProto added in v2.4.0

func NodeToProto(n *Node) (*proto.Node, error)

func RecvCtx added in v2.5.0

func RecvCtx[A any](ctx context.Context, c <-chan A) (a A, err error)

func STUNRegions

func STUNRegions(baseRegionID int, stunAddrs []string) ([]*tailcfg.DERPRegion, error)

func SendCtx added in v2.5.0

func SendCtx[A any](ctx context.Context, c chan<- A, a A) (err error)

func ServeAgentV1 added in v2.5.0

func ServeAgentV1(ctx context.Context, logger slog.Logger, c CoordinatorV2, conn net.Conn, id uuid.UUID, name string) error

func ServeClientV1 added in v2.5.0

func ServeClientV1(ctx context.Context, logger slog.Logger, c CoordinatorV2, conn net.Conn, id uuid.UUID, agent uuid.UUID) error

ServeClientV1 adapts a v1 Client to a v2 Coordinator

func ServeCoordinator

func ServeCoordinator(conn net.Conn, updateNodes func(node []*Node) error) (func(node *Node), <-chan error)

ServeCoordinator matches the RW structure of a coordinator to exchange node messages.

func SingleNodeUpdate added in v2.4.0

func SingleNodeUpdate(id uuid.UUID, node *Node, reason string) (*proto.CoordinateResponse, error)

func UUIDToByteSlice added in v2.4.0

func UUIDToByteSlice(u uuid.UUID) []byte

func WithStreamID added in v2.6.0

func WithStreamID(ctx context.Context, streamID StreamID) context.Context

func WithWebsocketSupport

func WithWebsocketSupport(s *derp.Server, base http.Handler) (http.Handler, func())

WithWebsocketSupport returns an http.Handler that upgrades connections to the "derp" subprotocol to WebSockets and passes them to the DERP server. Taken from: https://github.com/tailscale/tailscale/blob/e3211ff88ba85435f70984cf67d9b353f3d650d8/cmd/derper/websocket.go#L21

Types

type AgentCoordinateeAuth added in v2.9.0

type AgentCoordinateeAuth struct {
	ID uuid.UUID
}

AgentCoordinateeAuth disallows all tunnels, since agents are not allowed to initiate their own tunnels

func (AgentCoordinateeAuth) Authorize added in v2.9.0

type ClientCoordinateeAuth added in v2.9.0

type ClientCoordinateeAuth struct {
	AgentID uuid.UUID
}

ClientCoordinateeAuth allows connecting to a single, given agent

func (ClientCoordinateeAuth) Authorize added in v2.9.0

type ClientService added in v2.6.0

type ClientService struct {
	Logger   slog.Logger
	CoordPtr *atomic.Pointer[Coordinator]
	// contains filtered or unexported fields
}

ClientService is a tailnet coordination service that accepts a connection and version from a tailnet client, and support versions 1.0 and 2.x of the Tailnet API protocol.

func NewClientService added in v2.6.0

func NewClientService(
	logger slog.Logger,
	coordPtr *atomic.Pointer[Coordinator],
	derpMapUpdateFrequency time.Duration,
	derpMapFn func() *tailcfg.DERPMap,
) (
	*ClientService, error,
)

NewClientService returns a ClientService based on the given Coordinator pointer. The pointer is loaded on each processed connection.

func (*ClientService) ServeClient added in v2.6.0

func (s *ClientService) ServeClient(ctx context.Context, version string, conn net.Conn, id uuid.UUID, agent uuid.UUID) error

func (ClientService) ServeConnV2 added in v2.7.0

func (s ClientService) ServeConnV2(ctx context.Context, conn net.Conn, streamID StreamID) error

type Conn

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

Conn is an actively listening Wireguard connection.

func NewConn

func NewConn(options *Options) (conn *Conn, err error)

NewConn constructs a new Wireguard server that will accept connections from the addresses provided.

func (*Conn) AwaitReachable

func (c *Conn) AwaitReachable(ctx context.Context, ip netip.Addr) bool

AwaitReachable pings the provided IP continually until the address is reachable. It's the callers responsibility to provide a timeout, otherwise this function will block forever.

func (*Conn) Close

func (c *Conn) Close() error

Close shuts down the Wireguard connection.

func (*Conn) Closed

func (c *Conn) Closed() <-chan struct{}

Closed is a channel that ends when the connection has been closed.

func (*Conn) DERPMap

func (c *Conn) DERPMap() *tailcfg.DERPMap

DERPMap returns the currently set DERP mapping.

func (*Conn) DialContextTCP

func (c *Conn) DialContextTCP(ctx context.Context, ipp netip.AddrPort) (*gonet.TCPConn, error)

func (*Conn) DialContextUDP

func (c *Conn) DialContextUDP(ctx context.Context, ipp netip.AddrPort) (*gonet.UDPConn, error)

func (*Conn) GetBlockEndpoints added in v2.9.0

func (c *Conn) GetBlockEndpoints() bool

func (*Conn) GetPeerDiagnostics added in v2.9.0

func (c *Conn) GetPeerDiagnostics(peerID uuid.UUID) PeerDiagnostics

func (*Conn) InstallCaptureHook added in v2.8.0

func (c *Conn) InstallCaptureHook(f capture.Callback)

func (*Conn) Listen

func (c *Conn) Listen(network, addr string) (net.Listener, error)

Listen listens for connections only on the Tailscale network.

func (*Conn) MagicsockServeHTTPDebug

func (c *Conn) MagicsockServeHTTPDebug(w http.ResponseWriter, r *http.Request)

func (*Conn) MagicsockSetDebugLoggingEnabled

func (c *Conn) MagicsockSetDebugLoggingEnabled(enabled bool)

func (*Conn) Node

func (c *Conn) Node() *Node

Node returns the last node that was sent to the node callback.

func (*Conn) NodeAddresses

func (c *Conn) NodeAddresses(publicKey key.NodePublic) ([]netip.Prefix, bool)

NodeAddresses returns the addresses of a node from the NetworkMap.

func (*Conn) Ping

Ping sends a ping to the Wireguard engine. The bool returned is true if the ping was performed P2P.

func (*Conn) SetAddresses

func (c *Conn) SetAddresses(ips []netip.Prefix) error

func (*Conn) SetAllPeersLost added in v2.7.1

func (c *Conn) SetAllPeersLost()

SetAllPeersLost marks all peers lost; typically used when we disconnect from a coordinator.

func (*Conn) SetBlockEndpoints

func (c *Conn) SetBlockEndpoints(blockEndpoints bool)

SetBlockEndpoints sets whether to block P2P endpoints. This setting will only apply to new peers.

func (*Conn) SetConnStatsCallback

func (c *Conn) SetConnStatsCallback(maxPeriod time.Duration, maxConns int, dump func(start, end time.Time, virtual, physical map[netlogtype.Connection]netlogtype.Counts))

SetConnStatsCallback sets a callback to be called after maxPeriod or maxConns, whichever comes first. Multiple calls overwrites the callback.

func (*Conn) SetDERPForceWebSockets added in v2.1.3

func (c *Conn) SetDERPForceWebSockets(v bool)

func (*Conn) SetDERPMap

func (c *Conn) SetDERPMap(derpMap *tailcfg.DERPMap)

SetDERPMap updates the DERPMap of a connection.

func (*Conn) SetDERPRegionDialer

func (c *Conn) SetDERPRegionDialer(dialer func(ctx context.Context, region *tailcfg.DERPRegion) net.Conn)

SetDERPRegionDialer updates the dialer to use for connecting to DERP regions.

func (*Conn) SetNodeCallback

func (c *Conn) SetNodeCallback(callback func(node *Node))

func (*Conn) Status

func (c *Conn) Status() *ipnstate.Status

Status returns the current ipnstate of a connection.

func (*Conn) UpdatePeers added in v2.7.1

func (c *Conn) UpdatePeers(updates []*proto.CoordinateResponse_PeerUpdate) error

UpdatePeers connects with a set of peers. This can be constantly updated, and peers will continually be reconnected as necessary.

type Coordinatee added in v2.7.1

type Coordinatee interface {
	UpdatePeers([]*proto.CoordinateResponse_PeerUpdate) error
	SetAllPeersLost()
	SetNodeCallback(func(*Node))
}

Coordinatee is something that can be coordinated over the Coordinate protocol. Usually this is a Conn.

type CoordinateeAuth added in v2.9.0

type CoordinateeAuth interface {
	Authorize(req *proto.CoordinateRequest) error
}

type Coordination added in v2.7.1

type Coordination interface {
	io.Closer
	Error() <-chan error
}

func NewInMemoryCoordination added in v2.7.1

func NewInMemoryCoordination(
	ctx context.Context, logger slog.Logger,
	clientID, agentID uuid.UUID,
	coordinator Coordinator, coordinatee Coordinatee,
) Coordination

NewInMemoryCoordination connects a Coordinatee (usually Conn) to an in memory Coordinator, for testing or local clients. Set ClientID to uuid.Nil for an agent.

func NewRemoteCoordination added in v2.7.1

func NewRemoteCoordination(logger slog.Logger,
	protocol proto.DRPCTailnet_CoordinateClient, coordinatee Coordinatee,
	tunnelTarget uuid.UUID,
) Coordination

NewRemoteCoordination uses the provided protocol to coordinate the provided coordinatee (usually a Conn). If the tunnelTarget is not uuid.Nil, then we add a tunnel to the peer (i.e. we are acting as a client---agents should NOT set this!).

type Coordinator

type Coordinator interface {
	CoordinatorV1
	CoordinatorV2
}

Coordinator exchanges nodes with agents to establish connections. ┌──────────────────┐ ┌────────────────────┐ ┌───────────────────┐ ┌──────────────────┐ │tailnet.Coordinate├──►│tailnet.AcceptClient│◄─►│tailnet.AcceptAgent│◄──┤tailnet.Coordinate│ └──────────────────┘ └────────────────────┘ └───────────────────┘ └──────────────────┘ Coordinators have different guarantees for HA support.

func NewCoordinator

func NewCoordinator(logger slog.Logger) Coordinator

NewCoordinator constructs a new in-memory connection coordinator. This coordinator is incompatible with multiple Coder replicas as all node data is in-memory.

type CoordinatorV1 added in v2.6.0

type CoordinatorV1 interface {
	// ServeHTTPDebug serves a debug webpage that shows the internal state of
	// the coordinator.
	ServeHTTPDebug(w http.ResponseWriter, r *http.Request)
	// Node returns an in-memory node by ID.
	Node(id uuid.UUID) *Node
	// ServeClient accepts a WebSocket connection that wants to connect to an agent
	// with the specified ID.
	ServeClient(conn net.Conn, id uuid.UUID, agent uuid.UUID) error
	// ServeAgent accepts a WebSocket connection to an agent that listens to
	// incoming connections and publishes node updates.
	// Name is just used for debug information. It can be left blank.
	ServeAgent(conn net.Conn, id uuid.UUID, name string) error
	// Close closes the coordinator.
	Close() error

	ServeMultiAgent(id uuid.UUID) MultiAgentConn
}

type CoordinatorV2 added in v2.5.0

type CoordinatorV2 interface {
	// ServeHTTPDebug serves a debug webpage that shows the internal state of
	// the coordinator.
	ServeHTTPDebug(w http.ResponseWriter, r *http.Request)
	// Node returns a node by peer ID, if known to the coordinator.  Returns nil if unknown.
	Node(id uuid.UUID) *Node
	Close() error
	Coordinate(ctx context.Context, id uuid.UUID, name string, a CoordinateeAuth) (chan<- *proto.CoordinateRequest, <-chan *proto.CoordinateResponse)
}

CoordinatorV2 is the interface for interacting with the coordinator via the 2.0 tailnet API.

type DRPCService added in v2.6.0

type DRPCService struct {
	CoordPtr               *atomic.Pointer[Coordinator]
	Logger                 slog.Logger
	DerpMapUpdateFrequency time.Duration
	DerpMapFn              func() *tailcfg.DERPMap
}

DRPCService is the dRPC-based, version 2.x of the tailnet API and implements proto.DRPCClientServer

func (*DRPCService) Coordinate added in v2.7.0

func (s *DRPCService) Coordinate(stream proto.DRPCTailnet_CoordinateStream) error

func (*DRPCService) StreamDERPMaps added in v2.6.0

type HTMLDebug

type HTMLDebug struct {
	Peers   []HTMLPeer
	Tunnels []HTMLTunnel
}

type HTMLPeer added in v2.5.0

type HTMLPeer struct {
	ID           uuid.UUID
	Name         string
	CreatedAge   time.Duration
	LastWriteAge time.Duration
	Overwrites   int
	Node         string
}

type HTMLTunnel added in v2.5.0

type HTMLTunnel struct {
	Src, Dst uuid.UUID
}

type MultiAgent

type MultiAgent struct {
	ID uuid.UUID

	OnSubscribe   func(enq Queue, agent uuid.UUID) error
	OnUnsubscribe func(enq Queue, agent uuid.UUID) error
	OnNodeUpdate  func(id uuid.UUID, node *proto.Node) error
	OnRemove      func(enq Queue)
	// contains filtered or unexported fields
}

func (*MultiAgent) Close

func (m *MultiAgent) Close() error

func (*MultiAgent) CoordinatorClose

func (m *MultiAgent) CoordinatorClose() error

func (*MultiAgent) Done added in v2.2.0

func (m *MultiAgent) Done() <-chan struct{}

func (*MultiAgent) Enqueue

func (m *MultiAgent) Enqueue(resp *proto.CoordinateResponse) error

func (*MultiAgent) Init

func (m *MultiAgent) Init() *MultiAgent

func (*MultiAgent) IsClosed

func (m *MultiAgent) IsClosed() bool

func (*MultiAgent) Kind added in v2.2.0

func (*MultiAgent) Kind() QueueKind

func (*MultiAgent) Name

func (m *MultiAgent) Name() string

func (*MultiAgent) NextUpdate

func (m *MultiAgent) NextUpdate(ctx context.Context) (*proto.CoordinateResponse, bool)

func (*MultiAgent) Overwrites

func (m *MultiAgent) Overwrites() int64

func (*MultiAgent) Stats

func (m *MultiAgent) Stats() (start int64, lastWrite int64)

func (*MultiAgent) SubscribeAgent

func (m *MultiAgent) SubscribeAgent(agentID uuid.UUID) error

func (*MultiAgent) UniqueID

func (m *MultiAgent) UniqueID() uuid.UUID

func (*MultiAgent) UnsubscribeAgent

func (m *MultiAgent) UnsubscribeAgent(agentID uuid.UUID) error

func (*MultiAgent) UpdateSelf

func (m *MultiAgent) UpdateSelf(node *proto.Node) error

type MultiAgentConn

type MultiAgentConn interface {
	UpdateSelf(node *proto.Node) error
	SubscribeAgent(agentID uuid.UUID) error
	UnsubscribeAgent(agentID uuid.UUID) error
	NextUpdate(ctx context.Context) (*proto.CoordinateResponse, bool)
	Close() error
	IsClosed() bool
}

func ServeMultiAgent added in v2.5.0

func ServeMultiAgent(c CoordinatorV2, logger slog.Logger, id uuid.UUID) MultiAgentConn

type Node

type Node struct {
	// ID is used to identify the connection.
	ID tailcfg.NodeID `json:"id"`
	// AsOf is the time the node was created.
	AsOf time.Time `json:"as_of"`
	// Key is the Wireguard public key of the node.
	Key key.NodePublic `json:"key"`
	// DiscoKey is used for discovery messages over DERP to establish
	// peer-to-peer connections.
	DiscoKey key.DiscoPublic `json:"disco"`
	// PreferredDERP is the DERP server that peered connections should meet at
	// to establish.
	PreferredDERP int `json:"preferred_derp"`
	// DERPLatency is the latency in seconds to each DERP server.
	DERPLatency map[string]float64 `json:"derp_latency"`
	// DERPForcedWebsocket contains a mapping of DERP regions to
	// error messages that caused the connection to be forced to
	// use WebSockets. We don't use WebSockets by default because
	// they are less performant.
	DERPForcedWebsocket map[int]string `json:"derp_forced_websockets"`
	// Addresses are the IP address ranges this connection exposes.
	Addresses []netip.Prefix `json:"addresses"`
	// AllowedIPs specify what addresses can dial the connection. We allow all
	// by default.
	AllowedIPs []netip.Prefix `json:"allowed_ips"`
	// Endpoints are ip:port combinations that can be used to establish
	// peer-to-peer connections.
	Endpoints []string `json:"endpoints"`
}

Node represents a node in the network.

func OnlyNodeUpdates added in v2.4.0

func OnlyNodeUpdates(resp *proto.CoordinateResponse) ([]*Node, error)

func ProtoToNode added in v2.4.0

func ProtoToNode(p *proto.Node) (*Node, error)

type Options

type Options struct {
	ID         uuid.UUID
	Addresses  []netip.Prefix
	DERPMap    *tailcfg.DERPMap
	DERPHeader *http.Header
	// DERPForceWebSockets determines whether websockets is always used for DERP
	// connections, rather than trying `Upgrade: derp` first and potentially
	// falling back. This is useful for misbehaving proxies that prevent
	// fallback due to odd behavior, like Azure App Proxy.
	DERPForceWebSockets bool

	// BlockEndpoints specifies whether P2P endpoints are blocked.
	// If so, only DERPs can establish connections.
	BlockEndpoints bool
	Logger         slog.Logger
	ListenPort     uint16
}

type PeerDiagnostics added in v2.9.0

type PeerDiagnostics struct {
	// PreferredDERP is 0 if we are not connected to a DERP region. If non-zero, we are connected to
	// the given region as our home or "preferred" DERP.
	PreferredDERP   int
	DERPRegionNames map[int]string
	// SentNode is true if we have successfully transmitted our local Node via the most recently set
	// NodeCallback.
	SentNode bool
	// ReceivedNode is the last Node we received for the peer, or nil if we haven't received the node.
	ReceivedNode *tailcfg.Node
	// LastWireguardHandshake is the last time we completed a wireguard handshake
	LastWireguardHandshake time.Time
}

PeerDiagnostics is a checklist of human-readable conditions necessary to establish an encrypted tunnel to a peer via a Conn

type Queue

type Queue interface {
	UniqueID() uuid.UUID
	Kind() QueueKind
	Enqueue(resp *proto.CoordinateResponse) error
	Name() string
	Stats() (start, lastWrite int64)
	Overwrites() int64
	// CoordinatorClose is used by the coordinator when closing a Queue. It
	// should skip removing itself from the coordinator.
	CoordinatorClose() error
	Done() <-chan struct{}
	Close() error
}

type QueueKind added in v2.2.0

type QueueKind int
const (
	QueueKindClient QueueKind = 1 + iota
	QueueKindAgent
)

type SingleTailnetCoordinateeAuth added in v2.9.0

type SingleTailnetCoordinateeAuth struct{}

SingleTailnetCoordinateeAuth allows all tunnels, since Coderd and wsproxy are allowed to initiate a tunnel to any agent

func (SingleTailnetCoordinateeAuth) Authorize added in v2.9.0

type StreamID added in v2.6.0

type StreamID struct {
	Name string
	ID   uuid.UUID
	Auth CoordinateeAuth
}

StreamID identifies the caller of the CoordinateTailnet RPC. We store this on the context, since the information is extracted at the HTTP layer for remote clients of the API, or set outside tailnet for local clients (e.g. Coderd's single_tailnet)

type TrackedConn

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

func NewTrackedConn

func NewTrackedConn(ctx context.Context, cancel func(),
	conn net.Conn,
	id uuid.UUID,
	logger slog.Logger,
	name string,
	overwrites int64,
	kind QueueKind,
) *TrackedConn

func (*TrackedConn) Close

func (t *TrackedConn) Close() error

Close the connection and cancel the context for reading node updates from the queue

func (*TrackedConn) CoordinatorClose

func (t *TrackedConn) CoordinatorClose() error

func (*TrackedConn) Done added in v2.2.0

func (t *TrackedConn) Done() <-chan struct{}

func (*TrackedConn) Enqueue

func (t *TrackedConn) Enqueue(resp *proto.CoordinateResponse) (err error)

func (*TrackedConn) Kind added in v2.2.0

func (t *TrackedConn) Kind() QueueKind

func (*TrackedConn) Name

func (t *TrackedConn) Name() string

func (*TrackedConn) Overwrites

func (t *TrackedConn) Overwrites() int64

func (*TrackedConn) SendUpdates

func (t *TrackedConn) SendUpdates()

SendUpdates reads node updates and writes them to the connection. Ends when writes hit an error or context is canceled.

func (*TrackedConn) Stats

func (t *TrackedConn) Stats() (start, lastWrite int64)

func (*TrackedConn) UniqueID

func (t *TrackedConn) UniqueID() uuid.UUID

Directories

Path Synopsis
Package tailnettest is a generated GoMock package.
Package tailnettest is a generated GoMock package.

Jump to

Keyboard shortcuts

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