reversetunnel

package
v11.3.3 Latest Latest
Warning

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

Go to latest
Published: Dec 13, 2022 License: Apache-2.0 Imports: 49 Imported by: 0

Documentation

Overview

Package reversetunnel sets up persistent reverse tunnel between remote site and teleport proxy, when site agents dial to teleport proxy's socket and teleport proxy can connect to any server through this tunnel.

Package reversetunnel provides interfaces for accessing remote clusters

via reverse tunnels and directly.

Reverse Tunnels

Proxy server                      Proxy agent
               Reverse tunnel
+----------+                      +---------+
|          <----------------------+         |
|          |                      |         |

+-----+----------+ +---------+-----+ | | | | | | | | +----------------+ +---------------+

Proxy Cluster "A"                      Proxy Cluster "B"

Reverse tunnel is established from a cluster "B" Proxy to the a cluster "A" proxy, and clients of the cluster "A" can access servers of the cluster "B" via reverse tunnel connection, even if the cluster "B" is behind the firewall.

Multiple Proxies and Revese Tunnels

With multiple proxies behind the load balancer, proxy agents will eventually discover and establish connections to all proxies in cluster.

* Initially Proxy Agent connects to Proxy 1. * Proxy 1 starts sending information about all available proxies to the the Proxy Agent . This process is called "sending discovery request".

+----------+ | <--------+ | | | +----------+ | +-----------+ +----------+

Proxy 1           +-------------------------------+          |
                        |           |             |          |
                        +-----------+             +----------+
                         Load Balancer             Proxy Agent

+----------+ | | | | +----------+

Proxy 2

* Agent will use the discovery request to establish new connections and check if it has connected and "discovered" all the proxies specified

in the discovery request.

* Assuming that load balancer uses fair load balancing algorithm, agent will eventually discover and connect back to all the proxies.

+----------+ | <--------+ | | | +----------+ | +-----------+ +----------+

Proxy 1           +-------------------------------+          |
                  |     |           |             |          |
                  |     +-----------+             +----------+
                  |      Load Balancer             Proxy Agent

+----------+ | | <--------+ | | +----------+

Proxy 2

Index

Constants

View Source
const (
	// LocalNode is a special non-resolvable address that indicates the request
	// wants to connect to a dialed back node.
	LocalNode = "@local-node"
	// RemoteAuthServer is a special non-resolvable address that indicates client
	// requests a connection to the remote auth server.
	RemoteAuthServer = "@remote-auth-server"
	// LocalKubernetes is a special non-resolvable address that indicates that clients
	// requests a connection to the kubernetes endpoint of the local proxy.
	// This has to be a valid domain name, so it lacks @
	LocalKubernetes = "remote.kube.proxy." + constants.APIDomain
	// LocalWindowsDesktop is a special non-resolvable address that indicates
	// that clients requests a connection to the windows service endpoint of
	// the local proxy.
	// This has to be a valid domain name, so it lacks @
	LocalWindowsDesktop = "remote.windows_desktop.proxy." + constants.APIDomain
)
View Source
const (
	// NoApplicationTunnel is the error message returned when application
	// reverse tunnel cannot be found.
	//
	// It usually happens when an app agent has shut down (or crashed) but
	// hasn't expired from the backend yet.
	NoApplicationTunnel = "could not find reverse tunnel, check that Application Service agent proxying this application is up and running"
	// NoDatabaseTunnel is the error message returned when database reverse
	// tunnel cannot be found.
	//
	// It usually happens when a database agent has shut down (or crashed) but
	// hasn't expired from the backend yet.
	NoDatabaseTunnel = "could not find reverse tunnel, check that Database Service agent proxying this database is up and running"
)

Variables

This section is empty.

Functions

func UseTunnel

func UseTunnel(logger *log.Entry, c *sshutils.ChConn) bool

UseTunnel makes a channel request asking for the type of connection. If the other side does not respond (older cluster) or takes to long to respond, be on the safe side and assume it's not a tunnel connection.

Types

type Agent

type Agent interface {
	// Start starts the agent in the background.
	Start(context.Context) error
	// Stop closes the agent and releases resources.
	Stop() error
	// GetState returns the current state of the agent.
	GetState() AgentState
	// GetProxyID returns the proxy id of the proxy the agent is connected to.
	GetProxyID() (string, bool)
}

Agent represents a reverse tunnel agent.

type AgentPool

type AgentPool struct {
	AgentPoolConfig
	// contains filtered or unexported fields
}

AgentPool manages a pool of reverse tunnel agents.

func NewAgentPool

func NewAgentPool(ctx context.Context, config AgentPoolConfig) (*AgentPool, error)

NewAgentPool returns new instance of the agent pool.

func (*AgentPool) Count

func (p *AgentPool) Count() int

Count is the number connected agents.

func (*AgentPool) GetConnectedProxyGetter

func (p *AgentPool) GetConnectedProxyGetter() *ConnectedProxyGetter

GetConnectedProxyGetter returns the ConnectedProxyGetter for this agent pool.

func (*AgentPool) Start

func (p *AgentPool) Start() error

Start starts the agent pool in the background.

func (*AgentPool) Stop

func (p *AgentPool) Stop()

Stop stops the pool and waits for all resources to be released.

func (*AgentPool) Wait

func (p *AgentPool) Wait()

Wait blocks until the pool context is stopped.

type AgentPoolConfig

type AgentPoolConfig struct {
	// Client is client to the auth server this agent connects to receive
	// a list of pools
	Client auth.ClientI
	// AccessPoint is a lightweight access point
	// that can optionally cache some values
	AccessPoint auth.AccessCache
	// HostSigner is a host signer this agent presents itself as
	HostSigner ssh.Signer
	// HostUUID is a unique ID of this host
	HostUUID string
	// LocalCluster is a cluster name this client is a member of.
	LocalCluster string
	// Clock is a clock used to get time, if not set,
	// system clock is used
	Clock clockwork.Clock
	// KubeDialAddr is an address of a kubernetes proxy
	KubeDialAddr utils.NetAddr
	// Server is either an SSH or application server. It can handle a connection
	// (perform handshake and handle request).
	Server ServerHandler
	// Component is the Teleport component this agent pool is running in. It can
	// either be proxy (trusted clusters) or node (dial back).
	Component string
	// ReverseTunnelServer holds all reverse tunnel connections.
	ReverseTunnelServer Server
	// Resolver retrieves the reverse tunnel address
	Resolver Resolver
	// Cluster is a cluster name of the proxy.
	Cluster string
	// FIPS indicates if Teleport was started in FIPS mode.
	FIPS bool
	// ProxiedServiceUpdater updates a proxied service with the proxies it is connected to.
	ConnectedProxyGetter *ConnectedProxyGetter
	// IsRemoteCluster indicates the agent pool is connecting to a remote cluster.
	// This means the tunnel strategy should be ignored and tls routing is determined
	// by the remote cluster.
	IsRemoteCluster bool
	// DisableCreateHostUser disables host user creation on a node.
	DisableCreateHostUser bool
	// LocalAuthAddresses is a list of auth servers to use when dialing back to
	// the local cluster.
	LocalAuthAddresses []string
}

AgentPoolConfig holds configuration parameters for the agent pool

func (*AgentPoolConfig) CheckAndSetDefaults

func (cfg *AgentPoolConfig) CheckAndSetDefaults() error

CheckAndSetDefaults checks and sets defaults.

type AgentState

type AgentState string
const (
	// AgentInitial is the state of an agent when first created.
	AgentInitial AgentState = "initial"
	// AgentConnecting is the state when an agent is starting but not yet connected.
	AgentConnecting AgentState = "connecting"
	// AgentConnected is the state of an agent when is successfully connects
	// to a server and sends its first heartbeat.
	AgentConnected AgentState = "connected"
	// AgentClosed is the state of an agent when the connection and all other
	// resources are cleaned up.
	AgentClosed AgentState = "closed"
)

type AgentStateCallback

type AgentStateCallback func(AgentState)

AgentStateCallback is called when an agent's state changes.

type ClusterGetter

type ClusterGetter interface {
	// GetRemoteCluster returns a remote cluster by name
	GetRemoteCluster(clusterName string) (types.RemoteCluster, error)
}

ClusterGetter is an interface that defines GetRemoteCluster method

type Config

type Config struct {
	// ID is the ID of this server proxy
	ID string
	// ClusterName is a name of this cluster
	ClusterName string
	// ClientTLS is a TLS config associated with this proxy
	// used to connect to remote auth servers on remote clusters
	ClientTLS *tls.Config
	// Listener is a listener address for reverse tunnel server
	Listener net.Listener
	// HostSigners is a list of host signers
	HostSigners []ssh.Signer
	// HostKeyCallback
	// Limiter is optional request limiter
	Limiter *limiter.Limiter
	// LocalAuthClient provides access to a full AuthClient for the local cluster.
	LocalAuthClient auth.ClientI
	// AccessPoint provides access to a subset of AuthClient of the cluster.
	// AccessPoint caches values and can still return results during connection
	// problems.
	LocalAccessPoint auth.ProxyAccessPoint
	// NewCachingAccessPoint returns new caching access points
	// per remote cluster
	NewCachingAccessPoint auth.NewRemoteProxyCachingAccessPoint
	// Context is a signaling context
	Context context.Context
	// Clock is a clock used in the server, set up to
	// wall clock if not set
	Clock clockwork.Clock

	// KeyGen is a process wide key generator. It is shared to speed up
	// generation of public/private keypairs.
	KeyGen sshca.Authority

	// Ciphers is a list of ciphers that the server supports. If omitted,
	// the defaults will be used.
	Ciphers []string

	// KEXAlgorithms is a list of key exchange (KEX) algorithms that the
	// server supports. If omitted, the defaults will be used.
	KEXAlgorithms []string

	// MACAlgorithms is a list of message authentication codes (MAC) that
	// the server supports. If omitted the defaults will be used.
	MACAlgorithms []string

	// DataDir is a local server data directory
	DataDir string

	// PollingPeriod specifies polling period for internal sync
	// goroutines, used to speed up sync-ups in tests.
	PollingPeriod time.Duration

	// Component is a component used in logs
	Component string

	// Log specifies the logger
	Log log.FieldLogger

	// FIPS means Teleport was started in a FedRAMP/FIPS 140-2 compliant
	// configuration.
	FIPS bool

	// Emitter is event emitter
	Emitter events.StreamEmitter

	// DELETE IN: 8.0.0
	//
	// NewCachingAccessPointOldProxy is an access point that can be configured
	// with the old access point policy until all clusters are migrated to 7.0.0
	// and above.
	NewCachingAccessPointOldProxy auth.NewRemoteProxyCachingAccessPoint

	// PeerClient is a client to peer proxy servers.
	PeerClient *peer.Client

	// LockWatcher is a lock watcher.
	LockWatcher *services.LockWatcher

	// NodeWatcher is a node watcher.
	NodeWatcher *services.NodeWatcher

	// CertAuthorityWatcher is a cert authority watcher.
	CertAuthorityWatcher *services.CertAuthorityWatcher

	// CircuitBreakerConfig configures the auth client circuit breaker
	CircuitBreakerConfig breaker.Config

	// LocalAuthAddresses is a list of auth servers to use when dialing back to
	// the local cluster.
	LocalAuthAddresses []string
}

Config is a reverse tunnel server configuration

func (*Config) CheckAndSetDefaults

func (cfg *Config) CheckAndSetDefaults() error

CheckAndSetDefaults checks parameters and sets default values

type ConnectedProxyGetter

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

ConnectedProxyGetter gets the proxy ids that the a reverse tunnel pool is connected to. This is used to communicate the connected proxies between reversetunnel.AgentPool and service implementations to include the connected proxy ids in the service's heartbeats.

func NewConnectedProxyGetter

func NewConnectedProxyGetter() *ConnectedProxyGetter

NewConnectedProxyGetter creates a new ConnectedProxyGetter instance.

func (*ConnectedProxyGetter) GetProxyIDs

func (g *ConnectedProxyGetter) GetProxyIDs() []string

GetProxyIDs gets the list of connected proxy ids.

type DialParams

type DialParams struct {
	// From is the source address.
	From net.Addr

	// To is the destination address.
	To net.Addr

	// GetUserAgent gets an SSH agent for use in connecting to the remote host. Used by the
	// forwarding proxy.
	GetUserAgent teleagent.Getter

	// Address is used by the forwarding proxy to generate a host certificate for
	// the target node. This is needed because while dialing occurs via IP
	// address, tsh thinks it's connecting via DNS name and that's how it
	// validates the host certificate.
	Address string

	// Principals are additional principals that need to be added to the host
	// certificate. Used by the recording proxy to correctly generate a host
	// certificate.
	Principals []string

	// ServerID the hostUUID.clusterName of a Teleport node. Used with nodes
	// that are connected over a reverse tunnel.
	ServerID string

	// ProxyIDs is a list of proxy ids the node is connected to.
	ProxyIDs []string

	// ConnType is the type of connection requested, either node or application.
	// Only used when connecting through a tunnel.
	ConnType types.TunnelType

	// FromPeerProxy indicates that the dial request is being tunneled from
	// a peer proxy.
	FromPeerProxy bool
}

DialParams is a list of parameters used to Dial to a node within a cluster.

func (DialParams) String

func (params DialParams) String() string

type FakeRemoteSite

type FakeRemoteSite struct {
	RemoteSite
	// Name is the remote site name.
	Name string
	// AccessPoint is the auth server client.
	AccessPoint auth.RemoteProxyAccessPoint
	// OfflineTunnels is a list of server IDs that will return connection error.
	OfflineTunnels map[string]struct{}
	// contains filtered or unexported fields
}

FakeRemoteSite is a fake reversetunnel.RemoteSite implementation used in tests.

func NewFakeRemoteSite

func NewFakeRemoteSite(clusterName string, accessPoint auth.RemoteProxyAccessPoint) *FakeRemoteSite

NewFakeRemoteSite is a FakeRemoteSite constructor.

func (*FakeRemoteSite) CachingAccessPoint

func (s *FakeRemoteSite) CachingAccessPoint() (auth.RemoteProxyAccessPoint, error)

CachingAccessPoint returns caching auth server client.

func (*FakeRemoteSite) Close

func (s *FakeRemoteSite) Close() error

func (*FakeRemoteSite) Dial

func (s *FakeRemoteSite) Dial(params DialParams) (net.Conn, error)

Dial returns the connection to the remote site.

func (*FakeRemoteSite) DialCount

func (s *FakeRemoteSite) DialCount() int64

func (*FakeRemoteSite) GetName

func (s *FakeRemoteSite) GetName() string

GetName returns the remote site name.

func (*FakeRemoteSite) ProxyConn

func (s *FakeRemoteSite) ProxyConn() <-chan net.Conn

ProxyConn returns proxy connection channel with incoming connections.

type FakeServer

type FakeServer struct {
	Server
	// Sites is a list of sites registered via this fake reverse tunnel.
	Sites []RemoteSite
}

FakeServer is a fake reversetunnel.Server implementation used in tests.

func (*FakeServer) GetSite

func (s *FakeServer) GetSite(name string) (RemoteSite, error)

GetSite returns the remote site by name.

func (*FakeServer) GetSites

func (s *FakeServer) GetSites() ([]RemoteSite, error)

GetSites returns all available remote sites.

type RemoteClusterTunnelManager

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

RemoteClusterTunnelManager manages AgentPools for trusted (remote) clusters. It polls the auth server for ReverseTunnel resources and spins AgentPools for each one as needed.

Note: ReverseTunnel resources on the auth server represent the desired tunnels, not actually active ones.

func NewRemoteClusterTunnelManager

func NewRemoteClusterTunnelManager(cfg RemoteClusterTunnelManagerConfig) (*RemoteClusterTunnelManager, error)

NewRemoteClusterTunnelManager creates a new stopped tunnel manager with the provided config. Call Run() to start the manager.

func (*RemoteClusterTunnelManager) Close

func (w *RemoteClusterTunnelManager) Close() error

Close cleans up all outbound tunnels and stops the manager.

func (*RemoteClusterTunnelManager) Counts

func (w *RemoteClusterTunnelManager) Counts() map[string]int

Counts returns the number of tunnels for each remote cluster.

func (*RemoteClusterTunnelManager) Run

Run runs the manager polling loop. Run is blocking, start it in a goroutine.

func (*RemoteClusterTunnelManager) Sync

Sync does a one-time sync of trusted clusters with running agent pools. Non-test code should use Run() instead.

type RemoteClusterTunnelManagerConfig

type RemoteClusterTunnelManagerConfig struct {
	// AuthClient is client to the auth server.
	AuthClient auth.ClientI
	// AccessPoint is a lightweight access point that can optionally cache some
	// values.
	AccessPoint auth.ProxyAccessPoint
	// HostSigners is a signer for the host private key.
	HostSigner ssh.Signer
	// HostUUID is a unique ID of this host
	HostUUID string
	// LocalCluster is a cluster name this client is a member of.
	LocalCluster string
	// Local ReverseTunnelServer to reach other cluster members connecting to
	// this proxy over a tunnel.
	ReverseTunnelServer Server
	// Clock is a mock-able clock.
	Clock clockwork.Clock
	// KubeDialAddr is an optional address of a local kubernetes proxy.
	KubeDialAddr utils.NetAddr
	// FIPS indicates if Teleport was started in FIPS mode.
	FIPS bool
	// Log is the logger
	Log logrus.FieldLogger
	// LocalAuthAddresses is a list of auth servers to use when dialing back to
	// the local cluster.
	LocalAuthAddresses []string
}

RemoteClusterTunnelManagerConfig is a bundle of parameters used by a RemoteClusterTunnelManager.

func (*RemoteClusterTunnelManagerConfig) CheckAndSetDefaults

func (c *RemoteClusterTunnelManagerConfig) CheckAndSetDefaults() error

type RemoteSite

type RemoteSite interface {
	// DialAuthServer returns a net.Conn to the Auth Server of a site.
	DialAuthServer() (net.Conn, error)
	// Dial dials any address within the site network, in terminating
	// mode it uses local instance of forwarding server to terminate
	// and record the connection
	Dial(DialParams) (net.Conn, error)
	// DialTCP dials any address within the site network,
	// ignores recording mode and always uses TCP dial, used
	// in components that need direct dialer.
	DialTCP(DialParams) (net.Conn, error)
	// GetLastConnected returns last time the remote site was seen connected
	GetLastConnected() time.Time
	// GetName returns site name (identified by authority domain's name)
	GetName() string
	// GetStatus returns status of this site (either offline or connected)
	GetStatus() string
	// GetClient returns client connected to remote auth server
	GetClient() (auth.ClientI, error)
	// CachingAccessPoint returns access point that is lightweight
	// but is resilient to auth server crashes
	CachingAccessPoint() (auth.RemoteProxyAccessPoint, error)
	// NodeWatcher returns the node watcher that maintains the node set for the site
	NodeWatcher() (*services.NodeWatcher, error)
	// GetTunnelsCount returns the amount of active inbound tunnels
	// from the remote cluster
	GetTunnelsCount() int
	// IsClosed reports whether this RemoteSite has been closed and should no
	// longer be used.
	IsClosed() bool
	// Closer allows the site to be closed
	io.Closer
}

RemoteSite represents remote teleport site that can be accessed via teleport tunnel or directly by proxy

There are two implementations of this interface: local and remote sites.

type Resolver

type Resolver func(ctx context.Context) (*utils.NetAddr, types.ProxyListenerMode, error)

Resolver looks up reverse tunnel addresses

func CachingResolver

func CachingResolver(ctx context.Context, resolver Resolver, clock clockwork.Clock) (Resolver, error)

CachingResolver wraps the provided Resolver with one that will cache the previous result for 3 seconds to reduce the number of resolutions in an effort to mitigate potentially overwhelming the Resolver source.

func StaticResolver

func StaticResolver(address string, mode types.ProxyListenerMode) Resolver

StaticResolver returns a Resolver which will always resolve to the provided address

func WebClientResolver

func WebClientResolver(addrs []utils.NetAddr, insecureTLS bool) Resolver

WebClientResolver returns a Resolver which uses the web proxy to discover where the SSH reverse tunnel server is running.

type SSHClient

type SSHClient interface {
	ssh.ConnMetadata
	io.Closer
	Wait() error
	OpenChannel(ctx context.Context, name string, data []byte) (*tracessh.Channel, <-chan *ssh.Request, error)
	SendRequest(ctx context.Context, name string, wantReply bool, payload []byte) (bool, []byte, error)
	Principals() []string
	GlobalRequests() <-chan *ssh.Request
	HandleChannelOpen(channelType string) <-chan ssh.NewChannel
	Reply(*ssh.Request, bool, []byte) error
}

SSHClient is a client for an ssh connection.

type Server

type Server interface {
	Tunnel
	// Start starts server
	Start() error
	// Close closes server's operations immediately
	Close() error
	// DrainConnections closes listeners and begins draining connections without
	// closing open connections.
	DrainConnections(context.Context) error
	// Shutdown performs graceful server shutdown closing open connections.
	Shutdown(context.Context) error
	// Wait waits for server to close all outstanding operations
	Wait()
	// GetProxyPeerClient returns the proxy peer client
	GetProxyPeerClient() *peer.Client
}

Server is a TCP/IP SSH server which listens on an SSH endpoint and remote/local sites connect and register with it.

func NewServer

func NewServer(cfg Config) (Server, error)

NewServer creates and returns a reverse tunnel server which is fully initialized but hasn't been started yet

type ServerHandler

type ServerHandler interface {
	// HandleConnection performs a handshake then process the connection.
	HandleConnection(conn net.Conn)
}

ServerHandler implements an interface which can handle a connection (perform a handshake then process). This is needed because importing lib/srv in lib/reversetunnel causes a circular import.

type ServerHandlerToListener

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

ServerHandlerToListener is an adapter from ServerHandler to net.Listener. It can be used as a Server field in AgentPoolConfig, while also being passed to http.Server.Serve (or any other func Serve(net.Listener)).

func NewServerHandlerToListener

func NewServerHandlerToListener(tunnelAddr string) ServerHandlerToListener

NewServerHandlerToListener creates a new ServerHandlerToListener adapter.

func (ServerHandlerToListener) Accept

func (l ServerHandlerToListener) Accept() (net.Conn, error)

func (ServerHandlerToListener) Addr

func (l ServerHandlerToListener) Addr() net.Addr

func (ServerHandlerToListener) Close

func (l ServerHandlerToListener) Close() error

func (ServerHandlerToListener) HandleConnection

func (l ServerHandlerToListener) HandleConnection(c net.Conn)

type Tunnel

type Tunnel interface {
	// GetSites returns a list of connected remote sites
	GetSites() ([]RemoteSite, error)
	// GetSite returns remote site this node belongs to
	GetSite(domainName string) (RemoteSite, error)
}

Tunnel provides access to connected local or remote clusters using unified interface.

type TunnelAuthDialer

type TunnelAuthDialer struct {
	// TunnelAuthDialerConfig is the TunnelAuthDialer configuration.
	TunnelAuthDialerConfig
}

TunnelAuthDialer connects to the Auth Server through the reverse tunnel.

func NewTunnelAuthDialer

func NewTunnelAuthDialer(config TunnelAuthDialerConfig) (*TunnelAuthDialer, error)

NewTunnelAuthDialer creates a new instance of TunnelAuthDialer

func (*TunnelAuthDialer) DialContext

func (t *TunnelAuthDialer) DialContext(ctx context.Context, _, _ string) (net.Conn, error)

DialContext dials auth server via SSH tunnel

type TunnelAuthDialerConfig

type TunnelAuthDialerConfig struct {
	// Resolver retrieves the address of the proxy
	Resolver Resolver
	// ClientConfig is SSH tunnel client config
	ClientConfig *ssh.ClientConfig
	// Log is used for logging.
	Log logrus.FieldLogger
	// InsecureSkipTLSVerify is whether to skip certificate validation.
	InsecureSkipTLSVerify bool
}

TunnelAuthDialerConfig specifies TunnelAuthDialer configuration.

func (*TunnelAuthDialerConfig) CheckAndSetDefaults

func (c *TunnelAuthDialerConfig) CheckAndSetDefaults() error

type TunnelWithRoles

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

TunnelWithRoles authorizes requests

func NewTunnelWithRoles

func NewTunnelWithRoles(tunnel Tunnel, accessChecker services.AccessChecker, access ClusterGetter) *TunnelWithRoles

NewTunnelWithRoles returns new authorizing tunnel

func (*TunnelWithRoles) GetSite

func (t *TunnelWithRoles) GetSite(clusterName string) (RemoteSite, error)

GetSite returns remote site this node belongs to

func (*TunnelWithRoles) GetSites

func (t *TunnelWithRoles) GetSites() ([]RemoteSite, error)

GetSites returns a list of connected remote sites

Directories

Path Synopsis
Package track provides a simple interface for tracking known proxies by endpoint/name and correctly handling expiration and exclusivity.
Package track provides a simple interface for tracking known proxies by endpoint/name and correctly handling expiration and exclusivity.

Jump to

Keyboard shortcuts

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