tunnel

package
v0.0.0-...-404d010 Latest Latest
Warning

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

Go to latest
Published: Jan 12, 2022 License: GPL-3.0, BSD-3-Clause Imports: 22 Imported by: 0

README

Tunnel

Tunnel is a server/client package that enables to proxy public connections to your local machine over a tunnel connection from the local machine to the public server. What this means is, you can share your localhost even if it doesn't have a Public IP or if it's not reachable from outside.

It uses the excellent yamux package to multiplex connections between server and client.

The project is under active development, please vendor it if you want to use it.

Usage

The tunnel package consists of two parts. The server and the client.

Server is the public facing part. It's type that satisfies the http.Handler. So it's easily pluggable into existing servers.

Let assume that you setup your DNS service so all *.example.com domains route to your server at the public IP 203.0.113.0. Let us first create the server part:

package main

import (
	"net/http"

	"git.sequentialread.com/forest/threshold"
)

func main() {
	cfg := &tunnel.ServerConfig{}
	server, _ := tunnel.NewServer(cfg)
	server.AddHost("sub.example.com", "1234")
	http.ListenAndServe(":80", server)
}

Once you create the server, you just plug it into your server. The only detail here is to map a virtualhost to a secret token. The secret token is the only part that needs to be known for the client side.

Let us now create the client side part:

package main

import "git.sequentialread.com/forest/threshold"

func main() {
	cfg := &tunnel.ClientConfig{
		Identifier: "1234",
		ServerAddr: "203.0.113.0:80",
	}

	client, err := tunnel.NewClient(cfg)
	if err != nil {
		panic(err)
	}

	client.Start()
}

The Start() method is by default blocking. As you see you, we just passed the server address and the secret token.

Now whenever someone hit sub.example.com, the request will be proxied to the machine where client is running and hit the local server running 127.0.0.1:80 (assuming there is one). If someone hits sub.example.com:3000 (assume your server is running at this port), it'll be routed to 127.0.0.1:3000

That's it.

There are many options that can be changed, such as a static local address for your client. Have alook at the documentation

Protocol

The server/client protocol is written in the spec.md file. Please have a look for more detail.

License

The BSD 3-Clause License - see LICENSE for more details

Documentation

Overview

Package tunnel is a server/client package that enables to proxy public connections to your local machine over a tunnel connection from the local machine to the public server.

Index

Constants

View Source
const GLOB = "*"

The character which is treated like a glob

Variables

View Source
var ErrRedialAborted = errors.New("unable to restore the connection, aborting")

ErrRedialAborted is emitted on ClientClosed event, when backoff policy used by a client decided no more reconnection attempts must be made.

Functions

func Glob

func Glob(pattern, subj string) bool

Glob will test a string pattern, potentially containing globs, against a subject string. The result is a simple true/false, determining whether or not the glob pattern matched the subject text.

func Join

func Join(local, remote net.Conn, debugLog bool)

Join copies data between local and remote connections. It reads from one connection and writes to the other. It's a building block for ProxyFunc implementations.

func NewExponentialBackoff

func NewExponentialBackoff() *expBackoff

Types

type Backoff

type Backoff interface {
	// Next returns the duration to sleep before retrying reconnections.
	// If the returned value is negative, the retry is aborted.
	NextBackOff() time.Duration

	// Reset is used to signal a reconnection was successful and next
	// call to Next should return desired time duration for 1st reconnection
	// attempt.
	Reset()
}

Backoff defines behavior of staggering reconnection retries.

type BandwidthMetric

type BandwidthMetric struct {
	Bytes         int
	RemoteAddress net.Addr
	Inbound       bool
	Service       string
	ClientId      string
}

type Client

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

Client is responsible for creating a control connection to a tunnel server, creating new tunnels and proxy them to tunnel server.

func NewClient

func NewClient(cfg *ClientConfig) (*Client, error)

NewClient creates a new tunnel that is established between the serverAddr and localAddr. It exits if it can't create a new control connection to the server. If localAddr is empty client will always try to proxy to a local port.

func (*Client) Close

func (c *Client) Close() error

Close closes the client and shutdowns the connection to the tunnel server

func (*Client) HandleForwardProxy

func (c *Client) HandleForwardProxy(conn net.Conn) error

func (*Client) Start

func (c *Client) Start()

Start starts the client and connects to the server with the identifier. client.FetchIdentifier() will be used if it's not nil. It's supports reconnecting with exponential backoff intervals when the connection to the server disconnects. Call client.Close() to shutdown the client completely. A successful connection will cause StartNotify() to receive a value.

func (*Client) StartNotify

func (c *Client) StartNotify() <-chan bool

StartNotify returns a channel that receives a single value when the client established a successful connection to the server.

type ClientConfig

type ClientConfig struct {
	// Identifier is the secret token that needs to be passed to the server.
	// Required if FetchIdentifier is not set.
	Identifier string

	// FetchIdentifier can be used to fetch identifier. Required if Identifier
	// is not set.
	FetchIdentifier func() (string, error)

	// ServerAddr defines the TCP address of the tunnel server to be connected.
	// Required if FetchServerAddr is not set.
	ServerAddr string

	// FetchServerAddr can be used to fetch tunnel server address.
	// Required if ServerAddress is not set.
	FetchServerAddr func() (string, error)

	// a function that returns local address (ip and port) depending on service name
	FetchLocalAddr func(service string) (string, error)

	// Proxy defines custom proxing logic. This is optional extension point
	// where you can provide your local server selection or communication rules.
	Proxy ProxyFunc

	// Dial provides custom transport layer for communication between the threshold client and threshold server.
	//
	// If nil, default implementation is to return net.Dial("tcp", address).
	//
	// It can be used for connection monitoring, setting different timeouts or
	// securing the connection.
	Dial func(network, address string) (net.Conn, error)

	// StateChanges receives state transition details each time client
	// connection state changes. The channel is expected to be sufficiently
	// buffered to keep up with event pace.
	//
	// If nil, no information about state transitions are dispatched
	// by the library.
	StateChanges chan<- *ClientStateChange

	// Backoff is used to control behavior of staggering reconnection loop.
	//
	// If nil, default backoff policy is used which makes a client to never
	// give up on reconnection.
	//
	// If custom backoff is used, client will emit ErrRedialAborted set
	// with ClientClosed event when no more reconnection atttemps should
	// be made.
	Backoff Backoff

	// YamuxConfig defines the config which passed to every new yamux.Session. If nil
	// yamux.DefaultConfig() is used.
	YamuxConfig *yamux.Config

	// Debug enables debug mode, enable only if you want to debug the server.
	DebugLog bool
}

ClientConfig defines the configuration for the Client

type ClientState

type ClientState uint32

ClientState represents client connection state to tunnel server.

const (
	ClientUnknown ClientState = iota
	ClientStarted
	ClientConnecting
	ClientConnected
	ClientDisconnected
	ClientClosed // keep it always last
)

ClientState enumeration.

func (ClientState) String

func (i ClientState) String() string

type ClientStateChange

type ClientStateChange struct {
	Identifier string
	Previous   ClientState
	Current    ClientState
	Error      error
}

ClientStateChange represents single client state transition.

func (*ClientStateChange) String

func (cs *ClientStateChange) String() string

Strings implements the fmt.Stringer interface.

type ConnWithMetrics

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

func (ConnWithMetrics) Accumulate

func (conn ConnWithMetrics) Accumulate(inbound bool, n int)

func (ConnWithMetrics) Close

func (conn ConnWithMetrics) Close() error

func (ConnWithMetrics) LocalAddr

func (conn ConnWithMetrics) LocalAddr() net.Addr

func (ConnWithMetrics) PushMetric

func (conn ConnWithMetrics) PushMetric(inbound bool, n int)

func (ConnWithMetrics) Read

func (conn ConnWithMetrics) Read(b []byte) (n int, err error)

func (ConnWithMetrics) RemoteAddr

func (conn ConnWithMetrics) RemoteAddr() net.Addr

func (ConnWithMetrics) SetDeadline

func (conn ConnWithMetrics) SetDeadline(t time.Time) error

func (ConnWithMetrics) SetReadDeadline

func (conn ConnWithMetrics) SetReadDeadline(t time.Time) error

func (ConnWithMetrics) SetWriteDeadline

func (conn ConnWithMetrics) SetWriteDeadline(t time.Time) error

func (ConnWithMetrics) Write

func (conn ConnWithMetrics) Write(b []byte) (n int, err error)

type ListenerInfo

type ListenerInfo struct {
	//Send the HAProxy PROXY protocol v1 header to the proxy client before streaming TCP from the remote client.
	SendProxyProtocolv1 bool

	BackendService     string
	AssociatedClientId string
	HostnameGlob       string
}

type ProxyFunc

type ProxyFunc func(remote net.Conn, msg *proto.ControlMessage)

ProxyFunc is responsible for forwarding a remote connection to local server and writing the response back.

type Server

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

Server is responsible for proxying public connections to the client over a tunnel connection. It also listens to control messages from the client.

func NewServer

func NewServer(cfg *ServerConfig) (*Server, error)

NewServer creates a new Server. The defaults are used if config is nil.

func (*Server) AddAddr

func (s *Server) AddAddr(
	ip net.IP,
	port int,
	hostnameGlob string,
	identifier string,
	sendProxyProtocolv1 bool,
	service string,
) error

AddAddr starts accepting connections, routing every connection to a tunnel client given by the identifier.

When ip parameter is nil, all connections accepted from the listener are routed to the tunnel client specified by the identifier (port-based routing).

When ip parameter is non-nil, only those connections are routed whose local address matches the specified ip (ip-based routing).

If l listens on multiple interfaces it's desirable to call AddAddr multiple times with the same l value but different ip one.

func (*Server) DeleteAddr

func (s *Server) DeleteAddr(ip net.IP, port int, hostnameGlob string)

DeleteAddr stops listening for connections on the given listener.

Upon return no more connections will be tunneled, but as the method does not close the listener, so any ongoing connection won't get interrupted.

func (*Server) OnConnect

func (s *Server) OnConnect(identifier string, fn func() error)

OnConnect invokes a callback for client with given identifier, when it establishes a control session. After a client is connected, the associated function is also removed and needs to be added again.

func (*Server) OnDisconnect

func (s *Server) OnDisconnect(identifier string, fn func() error)

OnDisconnect calls the function when the client connected with the associated identifier disconnects from the server. After a client is disconnected, the associated function is also removed and needs to be added again.

func (*Server) ServeHTTP

func (s *Server) ServeHTTP(responseWriter http.ResponseWriter, request *http.Request)

ServeHTTP is a tunnel that creates an http/websocket tunnel between a public connection and the client connection.

type ServerConfig

type ServerConfig struct {
	// StateChanges receives state transition details each time client
	// connection state changes. The channel is expected to be sufficiently
	// buffered to keep up with event pace.
	//
	// If nil, no information about state transitions are dispatched
	// by the library.
	StateChanges chan<- *ClientStateChange

	DebugLog bool

	// the domain of the server, used for validating clientIds
	Domain string

	Bandwidth chan<- BandwidthMetric

	// function that analyzes the TLS client certificate of the request.
	// this is based on the CommonName attribute of the TLS certificate.
	// If we are in multi-tenant mode, it must be formatted like `<tenantId>.<nodeId>@<domain>`
	//                      otherwise, it must be formatted like         `<nodeId>@<domain>`
	// <domain> must match the configured Domain of this Threshold server
	// the identifier it returns will be `<tenantId>.<nodeId>` or `<nodeId>`.
	// the tenantId it returns will be `<tenantId>` or ""
	ValidateCertificate func(domain string, multiTenantMode bool, request *http.Request) (identifier string, tenantId string, err error)

	MultitenantMode bool

	// YamuxConfig defines the config which passed to every new yamux.Session. If nil
	// yamux.DefaultConfig() is used.
	YamuxConfig *yamux.Config
}

ServerConfig defines the configuration for the Server

type TCPProxy

type TCPProxy struct {

	// FetchLocalAddr is used for looking up TCP address of the services.
	FetchLocalAddr func(service string) (string, error)

	// Log is a custom logger that can be used for the proxy.
	// If not set a "tcp" logger is used.
	DebugLog bool
}

func (*TCPProxy) Proxy

func (p *TCPProxy) Proxy(remote net.Conn, msg *proto.ControlMessage)

Proxy is a ProxyFunc.

Directories

Path Synopsis
Package proto defines tunnel client server communication protocol.
Package proto defines tunnel client server communication protocol.

Jump to

Keyboard shortcuts

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