transport

package module
v1.24.8 Latest Latest
Warning

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

Go to latest
Published: Feb 19, 2024 License: Apache-2.0 Imports: 20 Imported by: 2

README

go.qbee.io/transport

Introduction

This repository contains the implementation of the qbee remote access transport protocol. It is used to establish a secure connection between a device and the qbee infrastructure.

Protocol

edge infrastructure.png

The protocol upgrades a standard HTTPS request to a smux session. This ensures that the connection is less likely to be blocked by firewalls and proxies, and allows for multiplexing of multiple streams over a single TCP connection. The brokering of smux streams is performed by the qbee edge service and is not part of the transport protocol itself (even though a mock edge service implementation is provided for testing purposes).

Each smux stream initiated by the client is verified and authorized by the edge service before passing to the device. The device cannot initiate smux streams.

Each stream begins with a message which determines the type of the stream and contains the necessary metadata. After the initial message, the stream becomes a plain duplex connection and the data exchange format is specific to the initial message type.

Authentication

Device authentication is based on mTLS and uses the same device key/certificate pair issued by the qbee platform during device bootstrap.

Client authentication is based on authorization header.

Authorization

Device and Client authorization is performed by the qbee edge service and is not part of the transport protocol.

Privacy and Integrity

Privacy and integrity of the transport protocol is ensured by TCP and TLS 1.3. Any communication between a client and a device uses a separate data stream and is encrypted using a separate TLS session.

Documentation

Index

Constants

View Source
const (
	// KB kilobyte
	KB = 1024

	// MB megabyte
	MB = 1024 * KB
)
View Source
const Protocol = "qbee-v1"

Protocol is the protocol to which the localListener is upgraded.

Variables

View Source
var DefaultSmuxConfig = &smux.Config{
	Version:           2,
	KeepAliveInterval: 45 * time.Second,
	KeepAliveTimeout:  120 * time.Second,
	MaxFrameSize:      32 * KB,
	MaxReceiveBuffer:  4 * MB,
	MaxStreamBuffer:   128 * KB,
}

DefaultSmuxConfig is the default smux configuration.

Functions

func ClientConnect

func ClientConnect(ctx context.Context, endpointURL, authHeader string, tlsConfig *tls.Config) (*smux.Session, error)

ClientConnect initiates smux session with the provided edge endpoint.

func ExpectOK added in v1.24.5

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

ExpectOK reads a message from the given reader and returns the payload if it is an OK message. Otherwise, an error is returned.

func HandleTCPTunnel

func HandleTCPTunnel(ctx context.Context, stream *smux.Stream, remoteAddr []byte) error

HandleTCPTunnel handles a TCP tunnel request as a device.

func HandleUDPTunnel

func HandleUDPTunnel(ctx context.Context, stream *smux.Stream, payload []byte) error

HandleUDPTunnel handles a UDP tunnel stream as a device.

func NewEdgeMock

func NewEdgeMock(t Tester) (*Client, *DeviceClient, *EdgeMock)

NewEdgeMock creates a new test server and returns it together with a client and a device client connected to it.

func Pipe

func Pipe(src io.ReadWriteCloser, dst io.ReadWriteCloser) (sent int64, received int64, err error)

Pipe copies data from src to dst and vice versa and returns first non-nil error.

func UpgradeHandler

func UpgradeHandler(w http.ResponseWriter, r *http.Request) (*smux.Session, error)

UpgradeHandler upgrades the server request localListener to the remote access protocol. For protocol errors, the handler will write an error response and return an error.

func WithIOWaitTimeout

func WithIOWaitTimeout(ctx context.Context, timeout time.Duration) context.Context

WithIOWaitTimeout returns a copy of the context with the specified timeout for I/O operations.

func WriteError added in v1.24.5

func WriteError(w io.Writer, err error) error

WriteError writes an error message to the given writer.

func WriteMessage

func WriteMessage(w io.Writer, messageType MessageType, payload []byte) error

WriteMessage writes a message to the given writer.

func WriteOK added in v1.24.5

func WriteOK(w io.Writer, payload []byte) error

WriteOK writes an OK message to the given writer.

Types

type Client

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

Client is the client for the remote access service.

func NewClient

func NewClient(ctx context.Context, endpointURL, authToken string, tlsConfig *tls.Config) (*Client, error)

NewClient creates a new remote access client instance connected to the given host.

func (*Client) Close

func (cli *Client) Close() error

Close closes the client.

func (*Client) OpenStream added in v1.24.5

func (cli *Client) OpenStream(ctx context.Context, msgType MessageType, payload []byte) (*smux.Stream, error)

OpenStream opens a new stream and sends the given message type and payload.

func (*Client) OpenTCPTunnel

func (cli *Client) OpenTCPTunnel(ctx context.Context, localHostPort, remoteHostPort string) (*net.TCPListener, error)

OpenTCPTunnel listens on the given local port and forwards all connections to the given remote host and port. The returned listener can be used to close the tunnel.

func (*Client) OpenUDPTunnel

func (cli *Client) OpenUDPTunnel(ctx context.Context, localHostPort, remoteHostPort string) (*UDPTunnel, error)

OpenUDPTunnel listens on the given local port and forwards all packets to the given remote host and port. Remember to close the tunnel when you're done with it using the Close method.

type DeviceClient

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

DeviceClient is the remote access client for devices.

func NewDeviceClient

func NewDeviceClient(endpoint string, tlsConfig *tls.Config) (*DeviceClient, error)

NewDeviceClient creates a new DeviceClient. endpoint is the address of the remote access device registration endpoint (e.g. "https://edge.example.com/device").

func (*DeviceClient) Close

func (cli *DeviceClient) Close() error

Close kills all streams and disconnects from the edge service.

func (*DeviceClient) Err

func (cli *DeviceClient) Err() error

Err returns the error that caused the device client to stop.

func (*DeviceClient) IsRunning

func (cli *DeviceClient) IsRunning() bool

IsRunning returns true if the device client is currently running.

func (*DeviceClient) Ready

func (cli *DeviceClient) Ready()

Ready returns when the device client is ready.

func (*DeviceClient) Start

func (cli *DeviceClient) Start(ctx context.Context)

Start starts the device client and processing loop. If connection to the edge service fails, the device client will retry to connect.

func (*DeviceClient) WithHandler

func (cli *DeviceClient) WithHandler(messageType MessageType, handler Handler) *DeviceClient

WithHandler sets the handler for the given message type.

func (*DeviceClient) WithSmuxConfig

func (cli *DeviceClient) WithSmuxConfig(smuxConfig *smux.Config) *DeviceClient

WithSmuxConfig sets the smux config for the device client.

type EdgeMock

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

EdgeMock provides a minimalistic edge server implementation to make testing of clients a bit easier.

func (*EdgeMock) Client

func (edge *EdgeMock) Client() *Client

Client returns a new client connected to the test server.

func (*EdgeMock) ClientTLS

func (edge *EdgeMock) ClientTLS() *tls.Config

ClientTLS returns the TLS configuration of the test server.

func (*EdgeMock) DeviceClient

func (edge *EdgeMock) DeviceClient() *DeviceClient

DeviceClient returns a new device client connected to the test server.

func (*EdgeMock) ServeHTTP

func (edge *EdgeMock) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP connects two clients together.

func (*EdgeMock) URL

func (edge *EdgeMock) URL() string

URL returns the URL of the test server.

type Handler

type Handler func(ctx context.Context, stream *smux.Stream, payload []byte) error

Handler is a function that handles a stream on the device client.

type MessageType

type MessageType uint8

MessageType defines the type of message sent over the wire.

const (
	// MessageTypeOK indicates that requested operation was successful.
	// The payload is empty.
	MessageTypeOK MessageType = 0x01

	// MessageTypeError indicates that requested operation failed.
	// The payload contains the error message.
	MessageTypeError MessageType = 0x02

	// MessageTypeGoAway indicates that client should reconnect to another server.
	MessageTypeGoAway MessageType = 0x03

	// MessageTypeTCPTunnel indicates that the message is a TCP tunnel request.
	// The payload contains the remote host and port.
	MessageTypeTCPTunnel MessageType = 0x10

	// MessageTypeUDPTunnel indicates that the message is a UDP tunnel request.
	// The payload contains the suggested listener port, remote host and port.
	MessageTypeUDPTunnel MessageType = 0x11

	// MessageTypePTY indicates that the message is a PTY request.
	// The payload contains initial JSON-encoded PTYCommand with PTYCommandTypeResize to set the initial window size.
	MessageTypePTY MessageType = 0x12

	// MessageTypePTYCommand indicates that the message is a PTY command request.
	// The payload contains JSON-encoded PTYCommand.
	MessageTypePTYCommand MessageType = 0x13

	// MessageTypeReload triggers a configuration reload.
	MessageTypeReload MessageType = 0x14
)

func ReadMessage

func ReadMessage(r io.Reader) (messageType MessageType, payload []byte, err error)

ReadMessage reads a message from the given reader.

type PTYCommand

type PTYCommand struct {
	// SessionID is the session ID (initiated with MessageTypePTY) to which the command applies.
	SessionID string `json:"sid"`

	// Type is the type of the command to be executed on the PTY.
	Type PTYCommandType `json:"type,omitempty"`

	// Cols and Rows are the new window size.
	// Those fields are only used when Type is PTYCommandTypeResize.
	Cols uint16 `json:"cols,omitempty"`
	Rows uint16 `json:"rows,omitempty"`
}

PTYCommand carries a command to be executed on the PTY stream.

type PTYCommandType

type PTYCommandType uint8

PTYCommandType is the type of PTY command.

const (
	// PTYCommandTypeResize indicates that the command is a resize request.
	PTYCommandTypeResize PTYCommandType = iota
)

type Tester added in v1.24.7

type Tester interface {
	Errorf(format string, args ...interface{})
	Fatalf(format string, args ...interface{})
	Cleanup(action func())
}

type UDPStream

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

UDPStream represents a stream of UDP datagrams between a local client and a remote server.

type UDPTunnel

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

UDPTunnel is a client implementation of a tunnel for UDP datagrams exchange. It listens on a local port and for each new client it receives a packet from, it creates a new smux stream to isolate and simplify the communication. When remote responds from a different port, it creates a new listener for that remote source port locally, so we can send the response to the client from a separate port as well.

func (*UDPTunnel) Close

func (t *UDPTunnel) Close() error

Close closes the tunnel.

func (*UDPTunnel) PrimaryAddr

func (t *UDPTunnel) PrimaryAddr() *net.UDPAddr

PrimaryAddr returns the primary address of the local listener.

Jump to

Keyboard shortcuts

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