sshserver

package
v0.5.0 Latest Latest
Warning

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

Go to latest
Published: Jan 7, 2024 License: Apache-2.0 Imports: 30 Imported by: 0

README

ContainerSSH SSH Server Library

This library provides an overlay for the built-in go SSH server library that makes it easier to handle.

Using this library

This library provides a friendlier way to handle SSH requests than with the built-in SSH library. the primary method of using this library is via the Lifecycle objects from the service library:

// Create the server. See the description below for parameters.
server, err := sshserver.New(
    cfg,
    handler,
    logger,
)
if err != nil {
    // Handle configuration errors
    log.Fatalf("%v", err)
}
lifecycle := service.NewLifecycle(server)

defer func() {
    // The Run method will run the server and return when the server is shut down.
    // We are running this in a goroutine so the shutdown below can proceed after a minute.
    if err := lifecycle.Run(); err != nil {
        // Handle errors while running the server
    }
}()

time.Sleep(60 * time.Second)

// Shut down the server. Pass a context to indicate how long the server should wait
// for existing connections to finish. This function will return when the server
// has stopped. 
lifecycle.Stop(
    context.WithTimeout(
        context.Background(),
        30 * time.Second,
    ),
)

The cfg variable will be a Config structure as described in config.go.

The handler variable must be an implementation of the Handler interface described in handler.go.

The logger variable needs to be an instance of the Logger interface from github.com/containerssh/libcontainerssh/log.

Implementing a handler

The handler interface consists of multiple parts:

  • The Handler is the main handler for the application providing several hooks for events. On new connections the OnNetworkConnection method is called, which must return a NetworkConnectionHandler
  • The NetworkConnectionHandler is a handler for network connections before the SSH handshake is complete. It is called to perform authentication and return an SSHConnectionHandler when the authentication is successful.
  • The SSHConnectionHandler is responsible for handling an individual SSH connection. Most importantly, it is responsible for providing a SessionChannelHandler when a new session channel is requested by the client.
  • The SessionChannelHandler is responsible for an individual session channel (single program execution). It provides several hooks for setting up and running the program. Once the program execution is complete the channel is closed. You must, however, keep handling requests (e.g. window size change) during program execution.

A sample implementation can be found in the test code at the bottom of the file.

About the connectionID

The connectionID parameter in the OnNetworkConnection() is a hexadecimal string uniquely identifying a connection. This ID can be used to track connection-related information across multiple subsystems (e.g. logs, audit logs, authentication and configuration requests, etc.)

Testing a backend

This library contains a testing toolkit for running Linux commands against a backend. The primary resource for these tests will be the conformance tests. To use these you must implement a set of factories that fulfill the following signature: func(logger log.Logger) (sshserver.NetworkConnectionHandler, error).

These factories can then be used as follows:

func TestConformance(t *testing.T) {
		var factories = map[string]func() (
            sshserver.NetworkConnectionHandler,
            error,
        ) {
    		"some-method": func(
                logger log.Logger,
            ) (sshserver.NetworkConnectionHandler, error) {
    			
    		},
    		"some-other-method": func(
                logger log.Logger,
            ) (sshserver.NetworkConnectionHandler, error) {
    			
    		},
    	}
    
    	sshserver.RunConformanceTests(t, factories)
}

The conformance tests will then attempt to execute a series of Linux interactions against the network connection handler and report features that have failed.

Alternatively, you can also use the components that make up the conformance tests separately.

Creating a test user

The first step of using the test utilities is creating a test user. This can be done using the following calls:

user := sshserver.NewTestUser(
    "test",
)

This use can then be configured with a random password using RandomPassword(), or set credentials using SetPassword() or GenerateKey(). These test users can then be passed to the test server or test client.

Creating a test client

The test SSH client offers functionality over the Golang SSH client that helps with testing. The test client can be created as follows:

sshclient := NewTestClient(
    serverIPAndPort,
    serverHostPrivateKey,
    user *TestUser,
    logger log.Logger,
)

Note that you must pass the servers private key in PEM format which will be used to extract the public key for validation. This makes the client unsuitable for purposes other than testing.

The test client can then be used to interact with the server. The client is described in test_client.go and functions can be discovered using code completion in the IDE.

Creating a test server

We also provide a simplified test server that you can plug in any backend:

srv := NewTestServer(
    handler,
    logger,
)

You can then start the server in the background and subsequently stop it:

srv.Start()
defer srv.Stop(10 * time.Second)

Creating a simple authenticating handler

We also provide an authentication handler that can be used to authenticate using the test users:

handler := NewTestAuthenticationHandler(
    handler,
    user1,
    user2,
    user3,
)

Documentation

Index

Constants

View Source
const (
	ChannelTypeSession              string = "session"
	ChannelTypeDirectTCPIP          string = "direct-tcpip"
	ChannelTypeReverseForward       string = "forwarded-tcpip"
	ChannelTypeX11                  string = "x11"
	ChannelTypeDirectStreamLocal    string = "direct-streamlocal@openssh.com"
	ChannelTypeForwardedStreamLocal string = "forwarded-streamlocal@openssh.com"
)

Variables

View Source
var ErrAuthenticationFailed = errors.New("authentication failed")

ErrAuthenticationFailed is the error that is returned from TestClient.Connect when the authentication failed.

Functions

func GenerateConnectionID

func GenerateConnectionID() string

GenerateConnectionID generates a globally unique connection ID consisting of hexadecimal characters.

func RunConformanceTests

func RunConformanceTests(t *testing.T, backendFactories map[string]ConformanceTestBackendFactory)

RunConformanceTests runs a suite of conformance tests against the provided backends supporting a standard Linux shell.

Types

type AbstractHandler

type AbstractHandler struct {
}

AbstractHandler is the abstract implementation of the Handler interface that can be embedded to get a partial implementation.

func (*AbstractHandler) OnNetworkConnection

func (a *AbstractHandler) OnNetworkConnection(_ net.TCPAddr, _ string) (NetworkConnectionHandler, error)

OnNetworkConnection is called when a new network connection is opened. It must either return a NetworkConnectionHandler object or an error. In case of an error the network connection is closed.

The ip parameter provides the IP address of the connecting user. The connectionID parameter provides an opaque binary identifier for the connection that can be used to track the connection across multiple subsystems.

func (*AbstractHandler) OnReady

func (a *AbstractHandler) OnReady() error

OnReady is called when the server is ready to receive connections. It has an opportunity to return an error to

abort the startup.

func (*AbstractHandler) OnShutdown

func (a *AbstractHandler) OnShutdown(_ context.Context)

OnShutdown is called when a shutdown of the SSH server is desired. The shutdownContext is passed as a deadline

for the shutdown, after which the server should abort all running connections and return as fast as
possible.

type AbstractNetworkConnectionHandler

type AbstractNetworkConnectionHandler struct {
}

AbstractNetworkConnectionHandler is an empty implementation for the NetworkConnectionHandler interface.

func (*AbstractNetworkConnectionHandler) OnAuthGSSAPI

func (*AbstractNetworkConnectionHandler) OnAuthKeyboardInteractive

func (a *AbstractNetworkConnectionHandler) OnAuthKeyboardInteractive(
	pendingMeta metadata.ConnectionAuthPendingMetadata,
	_ func(
		instruction string,
		questions KeyboardInteractiveQuestions,
	) (answers KeyboardInteractiveAnswers, err error),
) (response AuthResponse, meta metadata.ConnectionAuthenticatedMetadata, reason error)

OnAuthKeyboardInteractive is a callback for interactive authentication. The implementer will be passed a callback function that can be used to issue challenges to the user. These challenges can, but do not have to contain questions.

func (*AbstractNetworkConnectionHandler) OnAuthPassword

OnAuthPassword is called when a user attempts a password authentication. The implementation must always supply

AuthResponse and may supply error as a reason description.

func (*AbstractNetworkConnectionHandler) OnAuthPubKey

OnAuthPubKey is called when a user attempts a pubkey authentication. The implementation must always supply

 AuthResponse and may supply error as a reason description. The pubKey parameter is an SSH key in
the form of "ssh-rsa KEY HERE".

func (*AbstractNetworkConnectionHandler) OnDisconnect

func (a *AbstractNetworkConnectionHandler) OnDisconnect()

OnDisconnect is called when the network connection is closed.

func (*AbstractNetworkConnectionHandler) OnHandshakeFailed

OnHandshakeFailed is called when the SSH handshake failed. This method is also called after an authentication

failure. After this method is the connection will be closed and the OnDisconnect method will be
called.

func (*AbstractNetworkConnectionHandler) OnHandshakeSuccess

OnHandshakeSuccess is called when the SSH handshake was successful. It returns connection to process

requests, or failureReason to indicate that a backend error has happened. In this case, the
connection will be closed and OnDisconnect will be called.

func (*AbstractNetworkConnectionHandler) OnShutdown

OnShutdown is called when a shutdown of the SSH server is desired. The shutdownContext is passed as a deadline

for the shutdown, after which the server should abort all running connections and return as fast as
possible.

type AbstractSSHConnectionHandler

type AbstractSSHConnectionHandler struct {
}

AbstractSSHConnectionHandler is an empty implementation of the SSHConnectionHandler providing default methods.

func (*AbstractSSHConnectionHandler) OnFailedDecodeGlobalRequest

func (s *AbstractSSHConnectionHandler) OnFailedDecodeGlobalRequest(_ uint64, _ string, _ []byte, _ error)

func (*AbstractSSHConnectionHandler) OnSessionChannel

func (a *AbstractSSHConnectionHandler) OnSessionChannel(_ metadata.ChannelMetadata, _ []byte, _ SessionChannel) (
	channel SessionChannelHandler, failureReason ChannelRejection,
)

OnSessionChannel is called when a channel of the session type is requested. The implementer must either return

the channel result if the channel was successful, or failureReason to state why the channel
should be rejected.

func (*AbstractSSHConnectionHandler) OnShutdown

func (a *AbstractSSHConnectionHandler) OnShutdown(_ context.Context)

OnShutdown is called when a shutdown of the SSH server is desired. The shutdownContext is passed as a deadline

for the shutdown, after which the server should abort all running connections and return as fast as
possible.

func (*AbstractSSHConnectionHandler) OnUnsupportedChannel

func (a *AbstractSSHConnectionHandler) OnUnsupportedChannel(_ uint64, _ string, _ []byte)

OnUnsupportedChannel is called when a new channel is requested of an unsupported type. This gives the implementer

the ability to log unsupported channel requests.

channelID is an ID uniquely identifying the channel within the connection. channelType is the type of channel requested by the client. We only support the "session" channel type extraData contains the binary extra data submitted by the client. This is usually empty.

func (*AbstractSSHConnectionHandler) OnUnsupportedGlobalRequest

func (a *AbstractSSHConnectionHandler) OnUnsupportedGlobalRequest(_ uint64, _ string, _ []byte)

OnUnsupportedGlobalRequest captures all global SSH requests and gives the implementation an opportunity to log

the request.

requestID is an ID uniquely identifying the request within the scope connection. The same ID may appear within

a channel.

type AbstractSessionChannelHandler

type AbstractSessionChannelHandler struct {
}

AbstractSessionChannelHandler is an abstract implementation of SessionChannelHandler providing default implementations.

func (*AbstractSessionChannelHandler) OnClose

func (a *AbstractSessionChannelHandler) OnClose()

OnClose is called when the channel is closed.

func (*AbstractSessionChannelHandler) OnEnvRequest

func (a *AbstractSessionChannelHandler) OnEnvRequest(
	_ uint64,
	_ string,
	_ string,
) error

OnEnvRequest is called when the client requests an environment variable to be set. The implementation can return

an error to reject the request.

func (*AbstractSessionChannelHandler) OnExecRequest

func (a *AbstractSessionChannelHandler) OnExecRequest(
	_ uint64,
	_ string,
) error

OnExecRequest is called when the client request a program to be executed. The implementation can return an error

to reject the request. This method MUST NOT block beyond initializing the program.

func (*AbstractSessionChannelHandler) OnFailedDecodeChannelRequest

func (a *AbstractSessionChannelHandler) OnFailedDecodeChannelRequest(
	_ uint64,
	_ string,
	_ []byte,
	_ error,
)

OnFailedDecodeChannelRequest is called when a supported channel request was received, but the payload could not

be decoded.

requestID is an incrementing number uniquely identifying this request within the channel. RequestType contains the SSH request type. payload is the binary payload. reason is the reason why the decoding failed.

func (*AbstractSessionChannelHandler) OnPtyRequest

func (a *AbstractSessionChannelHandler) OnPtyRequest(
	_ uint64,
	_ string,
	_ uint32,
	_ uint32,
	_ uint32,
	_ uint32,
	_ []byte,
) error

OnPtyRequest is called when the client requests an interactive terminal to be allocated. The implementation can

return an error to reject the request.

requestID is an incrementing number uniquely identifying this request within the channel. Term is the terminal Name. This is usually set in the TERM environment variable. Columns is the number of Columns in the terminal. Rows is the number of Rows in the terminal. Width is the Width of the terminal in pixels. Height is the Height of a terminal in pixels. ModeList are the encoded terminal modes the client desires. See RFC4254 section 8 and RFC8160 for details.

func (*AbstractSessionChannelHandler) OnShell

func (a *AbstractSessionChannelHandler) OnShell(
	_ uint64,
) error

OnShell is called when the client requests a shell to be started. The implementation can return an error to

reject the request. The implementation should send the IO handling into background. It should also
respect the shutdown context on the Handler. This method MUST NOT block beyond initializing the shell.

func (*AbstractSessionChannelHandler) OnShutdown

OnShutdown is called when a shutdown of the SSH server is desired. The shutdownContext is passed as a deadline

for the shutdown, after which the server should abort all running connections and return as fast as
possible.

func (*AbstractSessionChannelHandler) OnSignal

func (a *AbstractSessionChannelHandler) OnSignal(
	_ uint64,
	_ string,
) error

OnSignal is called when the client requests a Signal to be sent to the running process. The implementation can

return an error to reject the request.

func (*AbstractSessionChannelHandler) OnSubsystem

func (a *AbstractSessionChannelHandler) OnSubsystem(
	_ uint64,
	_ string,
) error

OnSubsystem is called when the client calls a well-known Subsystem (e.g. sftp). The implementation can return an

error to reject the request. The implementation should send the IO handling into background. It
should also respect the shutdown context on the Handler. This method MUST NOT block beyond
initializing the subsystem.

func (*AbstractSessionChannelHandler) OnUnsupportedChannelRequest

func (a *AbstractSessionChannelHandler) OnUnsupportedChannelRequest(
	_ uint64,
	_ string,
	_ []byte,
)

OnUnsupportedChannelRequest captures channel requests of unsupported types.

requestID is an incrementing number uniquely identifying this request within the channel. RequestType contains the SSH request type. payload is the binary payload.

func (*AbstractSessionChannelHandler) OnWindow

func (a *AbstractSessionChannelHandler) OnWindow(
	_ uint64,
	_ uint32,
	_ uint32,
	_ uint32,
	_ uint32,
) error

OnWindow is called when the client requests the window size to be changed. This method may be called

after a program is started. The implementation can return an error to reject the request.

requestID is an incrementing number uniquely identifying this request within the channel. Columns is the number of Columns in the terminal. Rows is the number of Rows in the terminal. Width is the Width of the terminal in pixels. Height is the Height of a terminal in pixels.

func (*AbstractSessionChannelHandler) OnX11Request

func (s *AbstractSessionChannelHandler) OnX11Request(

	requestID uint64,
	singleConnection bool,
	protocol string,
	cookie string,
	screen uint32,
	reverseHandler ReverseForward,
) error

OnX11Request is called when the client requests the forwarding of X11 connections from the container to the client. This method may be called after a program is started. The implementation can return an error to reject the request.

requestID is an incrementing number uniquely identifying the request within the channel. singleConnection is a flag determining whether only one or multiple connections should be forwarded protocol is the authentication protocol for the X11 connections cookie is the authentication cookie for the X11 connections screen is the X11 screen number reverseHandler is a callback interface to signal when new connections are made

type AuthResponse

type AuthResponse uint8

AuthResponse is the result of the authentication process.

const (
	// AuthResponseSuccess indicates that the authentication was successful.
	AuthResponseSuccess AuthResponse = 1

	// AuthResponseFailure indicates that the authentication failed for invalid credentials.
	AuthResponseFailure AuthResponse = 2

	// AuthResponseUnavailable indicates that the authentication could not be performed because a backend system failed
	//                         to respond.
	AuthResponseUnavailable AuthResponse = 3
)

type ChannelRejection

type ChannelRejection interface {
	message2.Message

	// Reason contains the SSH-specific reason for the rejection.
	Reason() ssh.RejectionReason
}

ChannelRejection is an error type that also contains a Message and a Reason

func NewChannelRejection

func NewChannelRejection(
	Reason ssh.RejectionReason,
	Code string,
	UserMessage string,
	Explanation string,
	Args ...interface{},
) ChannelRejection

NewChannelRejection constructs a Message that rejects a channel.

Reason is the SSH rejection reason.

Code is an error code allowing an administrator to identify the error that happened.

UserMessage is the message that can be printed to the user if needed.

Explanation is the explanation string to the system administrator. This is an fmt.Sprintf-compatible string

Args are the arguments to Explanation to create a formatted message. It is recommended that these arguments also be added as labels to allow system administrators to index the error properly.

type ConformanceTestBackendFactory

type ConformanceTestBackendFactory = func(t *testing.T, logger log.Logger) (NetworkConnectionHandler, error)

ConformanceTestBackendFactory is a method to creating a network connection conformanceTestHandler for testing purposes.

type ExitStatus

type ExitStatus uint32

ExitStatus contains the status code with which the program exited. See RFC 4254 section 6.10: Returning Exit Status for details. ( https://tools.ietf.org/html/rfc4254#section-6.10 )

type ForwardChannel

type ForwardChannel interface {
	Read([]byte) (int, error)

	Write([]byte) (int, error)

	Close() error
}

ForwardChannel represents a network forwarding channel

type Handler

type Handler interface {
	// OnReady is called when the server is ready to receive connections. It has an opportunity to return an error to
	//         abort the startup.
	OnReady() error

	// OnShutdown is called when a shutdown of the SSH server is desired. The shutdownContext is passed as a deadline
	//            for the shutdown, after which the server should abort all running connections and return as fast as
	//            possible.
	OnShutdown(shutdownContext context.Context)

	// OnNetworkConnection is called when a new network connection is opened. It must either return a
	// NetworkConnectionHandler object or an error. In case of an error the network connection is closed.
	OnNetworkConnection(metadata.ConnectionMetadata) (NetworkConnectionHandler, metadata.ConnectionMetadata, error)
}

Handler is the basic conformanceTestHandler for SSH connections. It contains several methods to handle startup and operations of the

server

func NewTestAuthenticationHandler

func NewTestAuthenticationHandler(
	backend Handler,
	users ...*TestUser,
) Handler

NewTestAuthenticationHandler creates a new backend that authenticates a user based on the users variable and passes all further calls to the backend.

func NewTestHandler

func NewTestHandler() Handler

NewTestHandler creates a conformanceTestHandler that can be used for testing purposes. It does not authenticate, that can be done using the NewTestAuthenticationHandler

type KeyboardInteractiveAnswers

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

KeyboardInteractiveAnswers is a set of answer to a keyboard-interactive challenge.

func (*KeyboardInteractiveAnswers) Get

Get returns the answer for a question, or an error if no answer is present.

func (*KeyboardInteractiveAnswers) GetByQuestionText

func (k *KeyboardInteractiveAnswers) GetByQuestionText(question string) (string, error)

GetByQuestionText returns the answer for a question text, or an error if no answer is present.

type KeyboardInteractiveQuestion

type KeyboardInteractiveQuestion struct {
	// ID is an optional opaque ID that can be used to identify a question in an answer. Can be left empty.
	ID string
	// Question is the question text sent to the user.
	Question string
	// EchoResponse should be set to true to show the typed response to the user.
	EchoResponse bool
}

KeyboardInteractiveQuestion contains a question issued to a user as part of the keyboard-interactive exchange.

type KeyboardInteractiveQuestions

type KeyboardInteractiveQuestions []KeyboardInteractiveQuestion

KeyboardInteractiveQuestions is a list of questions for keyboard-interactive authentication

func (*KeyboardInteractiveQuestions) Add

type NetworkConnectionHandler

type NetworkConnectionHandler interface {
	// OnAuthPassword is called when a user attempts a password authentication. The implementation must always supply
	// AuthResponse and may supply error as a reason description.
	OnAuthPassword(meta metadata.ConnectionAuthPendingMetadata, password []byte) (
		AuthResponse,
		metadata.ConnectionAuthenticatedMetadata,
		error,
	)

	// OnAuthPubKey is called when a user attempts a pubkey authentication. The implementation must always supply
	// AuthResponse and may supply error as a reason description. The pubKey parameter is an SSH key in
	// the form of "ssh-rsa KEY HERE".
	OnAuthPubKey(meta metadata.ConnectionAuthPendingMetadata, pubKey auth.PublicKey) (
		AuthResponse,
		metadata.ConnectionAuthenticatedMetadata,
		error,
	)

	// OnAuthKeyboardInteractive is a callback for interactive authentication. The implementer will be passed a callback
	// function that can be used to issue challenges to the user. These challenges can, but do not have to contain
	// questions.
	OnAuthKeyboardInteractive(
		meta metadata.ConnectionAuthPendingMetadata,
		challenge func(
			instruction string,
			questions KeyboardInteractiveQuestions,
		) (answers KeyboardInteractiveAnswers, err error),
	) (AuthResponse, metadata.ConnectionAuthenticatedMetadata, error)

	// OnAuthGSSAPI returns a GSSAPIServer which can perform a GSSAPI authentication.
	OnAuthGSSAPI(metadata metadata.ConnectionMetadata) auth2.GSSAPIServer

	// OnHandshakeFailed is called when the SSH handshake failed. This method is also called after an authentication
	// failure. After this method is the connection will be closed and the OnDisconnect method will be
	// called.
	OnHandshakeFailed(metadata metadata.ConnectionMetadata, reason error)

	// OnHandshakeSuccess is called when the SSH handshake was successful. It returns metadata to process
	// requests, or failureReason to indicate that a backend error has happened. In this case, the
	// metadata will be closed and OnDisconnect will be called.
	OnHandshakeSuccess(metadata.ConnectionAuthenticatedMetadata) (
		connection SSHConnectionHandler,
		meta metadata.ConnectionAuthenticatedMetadata,
		failureReason error,
	)

	// OnDisconnect is called when the network connection is closed.
	OnDisconnect()

	// OnShutdown is called when a shutdown of the SSH server is desired. The shutdownContext is passed as a deadline
	// for the shutdown, after which the server should abort all running connections and return as fast as
	// possible.
	OnShutdown(shutdownContext context.Context)
}

NetworkConnectionHandler is an object that is used to represent the underlying network connection and the SSH handshake.

type ReverseForward

type ReverseForward interface {
	// NewChannelTCP requests the opening of a reverse forwarding TCP channel
	//
	// connectedAddress is the address that was connected to
	// connectedPort is the port that was connected to
	// originatorAddress is the address of the initiator of the connection
	// originatorPort is the port of the initiator of the connection
	NewChannelTCP(connectedAddress string, connectedPort uint32, originatorAddress string, originatorPort uint32) (ForwardChannel, uint64, error)
	// NewChannelUnix requests the opening of a reverse forwarding unix socket channel
	//
	// path is the container-based path to the unix socket that is being forwarded
	NewChannelUnix(path string) (ForwardChannel, uint64, error)
	// NewChannelX11 requests the opening of an X11 channel
	//
	// originatorAddress is the address that initiated the X11 request
	// originatorPort is the port that originated the X11 request
	NewChannelX11(originatorAddress string, originatorPort uint32) (ForwardChannel, uint64, error)
}

ReverseForward contains a set of callbacks for backends to request the opening of a new channel

type ReverseForwardHandler

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

func (*ReverseForwardHandler) NewChannelTCP

func (r *ReverseForwardHandler) NewChannelTCP(
	connectedAddress string,
	connectedPort uint32,
	originatorAddress string,
	originatorPort uint32,
) (ForwardChannel, uint64, error)

func (*ReverseForwardHandler) NewChannelUnix

func (r *ReverseForwardHandler) NewChannelUnix(
	path string,
) (ForwardChannel, uint64, error)

func (*ReverseForwardHandler) NewChannelX11

func (r *ReverseForwardHandler) NewChannelX11(
	originatorAddress string,
	originatorPort uint32,
) (ForwardChannel, uint64, error)

type SSHConnectionHandler

type SSHConnectionHandler interface {
	// OnUnsupportedGlobalRequest captures all global SSH requests and gives the implementation an opportunity to log
	//                            the request.
	//
	// requestID is an ID uniquely identifying the request within the scope connection. The same ID may appear within
	//           a channel.
	OnUnsupportedGlobalRequest(requestID uint64, requestType string, payload []byte)

	// OnFailedDecodeGlobalRequest is called when a global request was received but the payload could not be decoded
	//
	// requestID is an ID uniquely identifying the request within the scope of the connection. The same ID may appear within a channel
	OnFailedDecodeGlobalRequest(requestID uint64, requestType string, payload []byte, reason error)

	// OnUnsupportedChannel is called when a new channel is requested of an unsupported type. This gives the implementer
	//                      the ability to log unsupported channel requests.
	//
	// channelID is an ID uniquely identifying the channel within the connection.
	// channelType is the type of channel requested by the client. We only support the "session" channel type
	// extraData contains the binary extra data submitted by the client. This is usually empty.
	OnUnsupportedChannel(channelID uint64, channelType string, extraData []byte)

	// OnSessionChannel is called when a channel of the session type is requested. The implementer must either return
	//                  the channel result if the channel was successful, or failureReason to state why the channel
	//                  should be rejected.
	//
	// channelMetadata contains the metadata for the channel.
	// extraData contains the binary extra data submitted by the client. This is usually empty.
	// session contains a set of calls that can be used to manipulate the SSH session.
	OnSessionChannel(
		channelMetadata metadata.ChannelMetadata,
		extraData []byte,
		session SessionChannel,
	) (channel SessionChannelHandler, failureReason ChannelRejection)

	// OnTCPForwardChannel is called when a channel of the direct-tcpip type is requested. The implementer must either return
	//                  the channel result if the channel was successful, or failureReason to state why the channel
	//                  should be rejected.
	//
	// channelID is an ID uniquely identifying the channel within then connection.
	// hostToConnect contains the IP address or hostname to connect to
	// portToConnect contains the port to connect to
	// originatorHost contains the IP address or hostname the connection originates from
	// originatorPort contains the port the connection originates from
	OnTCPForwardChannel(
		channelID uint64,
		hostToConnect string,
		portToConnect uint32,
		originatorHost string,
		originatorPort uint32,
	) (channel ForwardChannel, failureReason ChannelRejection)

	// OnRequestTCPReverseForward is called when a request is received to start listening on a tcp port and forward all connections from it. The implementer must listen on the host and port provided and signal new connections via the reverseHandler calling the appropriate function (NewChannelTCP)
	//
	// bindHost is the interface to listen on
	// bindPort is the port to listen on
	// reverseHandler is a set of callbacks to signal new connections
	OnRequestTCPReverseForward(bindHost string, bindPort uint32, reverseHandler ReverseForward) error

	// OnRequestCancelTCPReverseForward is called when a request to cancel an existing tcp port forwarding is received
	//
	// bindHost is the interface of the forwarding to be cancelled
	// bindPort is the port of the forwarding to be cancelled
	OnRequestCancelTCPReverseForward(bindHost string, bindPort uint32) error

	// OnDirectStreamLocal is called when a new forwarding channel is opened to connect and forward data to a unix socket within a container
	//
	// channelID is the channelID of the channel that was opened
	// path is the path to the unix socket to be used
	OnDirectStreamLocal(
		channelID uint64,
		path string,
	) (channel ForwardChannel, failureReason ChannelRejection)

	// OnRequestStreamLocal is called when unix socket forwarding from the container to the client is requested. The implementer must listen on socket path provided and signal new connections via the reverseHandler calling the appropriate function (NewChannelTCP)
	//
	// path is the path to the unix socket to be forwarded
	// reverseHandler is a set of callbacks to signal new connections
	OnRequestStreamLocal(
		path string,
		reverseHandler ReverseForward,
	) error

	// OnRequestCancelStreamLocal is called when a request to cancel an existing tcp port forwarding is received
	//
	// bindHost is the interface of the forwarding to be cancelled
	// bindPort is the port of the forwarding to be cancelled
	OnRequestCancelStreamLocal(
		path string,
	) error

	// OnShutdown is called when a shutdown of the SSH server is desired. The shutdownContext is passed as a deadline
	//            for the shutdown, after which the server should abort all running connections and return as fast as
	//            possible.
	OnShutdown(shutdownContext context.Context)
}

SSHConnectionHandler represents an established SSH connection that is ready to receive requests.

type Server

type Server interface {
	service.Service
}

Server is the main SSH server interface, compatible with the Service library. It should always be used in conjunction with the Lifecycle interface from the service library.

func New

func New(cfg config.SSHConfig, handler Handler, logger log.Logger) (Server, error)

New creates a new SSH server ready to be run. It may return an error if the configuration is invalid.

type SessionChannel

type SessionChannel interface {
	// Stdin returns the reader for the standard input.
	Stdin() io.Reader
	// Stdout returns the writer for the standard output.
	Stdout() io.Writer
	// Stderr returns the writer for the standard error.
	Stderr() io.Writer
	// ExitStatus sends the program exit status to the client.
	ExitStatus(code uint32)
	// ExitSignal sends a message to the client indicating that the program exited violently.
	ExitSignal(signal string, coreDumped bool, errorMessage string, languageTag string)
	// CloseWrite sends an EOF to the client indicating that no more data will be sent on stdout or stderr.
	CloseWrite() error
	// Close closes the channel for reading and writing.
	Close() error
}

SessionChannel contains a set of calls to manipulate the session channel.

type SessionChannelHandler

type SessionChannelHandler interface {

	// OnUnsupportedChannelRequest captures channel requests of unsupported types.
	//
	// requestID is an incrementing number uniquely identifying this request within the channel.
	// RequestType contains the SSH request type.
	// payload is the binary payload.
	OnUnsupportedChannelRequest(
		requestID uint64,
		requestType string,
		payload []byte,
	)

	// OnFailedDecodeChannelRequest is called when a supported channel request was received, but the payload could not
	//                              be decoded.
	//
	// requestID is an incrementing number uniquely identifying this request within the channel.
	// RequestType contains the SSH request type.
	// payload is the binary payload.
	// reason is the reason why the decoding failed.
	OnFailedDecodeChannelRequest(
		requestID uint64,
		requestType string,
		payload []byte,
		reason error,
	)

	// OnEnvRequest is called when the client requests an environment variable to be set. The implementation can return
	//              an error to reject the request.
	OnEnvRequest(
		requestID uint64,
		name string,
		value string,
	) error

	// OnPtyRequest is called when the client requests an interactive terminal to be allocated. The implementation can
	//              return an error to reject the request.
	//
	// requestID is an incrementing number uniquely identifying this request within the channel.
	// Term is the terminal Name. This is usually set in the TERM environment variable.
	// Columns is the number of Columns in the terminal.
	// Rows is the number of Rows in the terminal.
	// Width is the Width of the terminal in pixels.
	// Height is the Height of a terminal in pixels.
	// ModeList are the encoded terminal modes the client desires. See RFC4254 section 8 and RFC8160 for details.
	OnPtyRequest(
		requestID uint64,
		term string,
		columns uint32,
		rows uint32,
		width uint32,
		height uint32,
		modeList []byte,
	) error

	// OnX11Request is called when the client requests the forwarding of X11 connections from the container to the client.
	// This method may be called after a program is started. The implementation can return an error to reject the request.
	//
	// requestID is an incrementing number uniquely identifying the request within the channel.
	// singleConnection is a flag determining whether only one or multiple connections should be forwarded
	// protocol is the authentication protocol for the X11 connections
	// cookie is the authentication cookie for the X11 connections
	// screen is the X11 screen number
	// reverseHandler is a callback interface to signal when new connections are made
	OnX11Request(
		requestID uint64,
		singleConnection bool,
		protocol string,
		cookie string,
		screen uint32,
		reverseHandler ReverseForward,
	) error

	// OnExecRequest is called when the client request a program to be executed. The implementation can return an error
	//               to reject the request. This method MUST NOT block beyond initializing the program.
	//
	// requestID is an incrementing number uniquely identifying this request within the channel.
	// program is the Name of the program to be executed.
	OnExecRequest(
		requestID uint64,
		program string,
	) error

	// OnShell is called when the client requests a shell to be started. The implementation can return an error to
	//         reject the request. The implementation should send the IO handling into background. It should also
	//         respect the shutdown context on the Handler. This method MUST NOT block beyond initializing the shell.
	//
	// requestID is an incrementing number uniquely identifying this request within the channel.
	// stdin is a reader for the shell or program to read the stdin.
	// stdout is a writer for the shell or program standard output.
	// stderr is a writer for the shell or program standard error.
	// writeClose closes the stdout and stderr for writing.
	// onExit is a callback to send the exit status back to the client.
	OnShell(
		requestID uint64,
	) error

	// OnSubsystem is called when the client calls a well-known Subsystem (e.g. sftp). The implementation can return an
	//             error to reject the request. The implementation should send the IO handling into background. It
	//             should also respect the shutdown context on the Handler. This method MUST NOT block beyond
	//             initializing the subsystem.
	//
	// requestID is an incrementing number uniquely identifying this request within the channel.
	// subsystem is the name of the subsystem to be launched (e.g. sftp)
	OnSubsystem(
		requestID uint64,
		subsystem string,
	) error

	// OnSignal is called when the client requests a Signal to be sent to the running process. The implementation can
	//          return an error to reject the request.
	OnSignal(
		requestID uint64,
		signal string,
	) error

	// OnWindow is called when the client requests the window size to be changed. This method may be called
	//          after a program is started. The implementation can return an error to reject the request.
	//
	// requestID is an incrementing number uniquely identifying this request within the channel.
	// Columns is the number of Columns in the terminal.
	// Rows is the number of Rows in the terminal.
	// Width is the Width of the terminal in pixels.
	// Height is the Height of a terminal in pixels.
	OnWindow(
		requestID uint64,
		columns uint32,
		rows uint32,
		width uint32,
		height uint32,
	) error

	// OnClose is called when the channel is closed.
	OnClose()

	// OnShutdown is called when a shutdown of the SSH server is desired. The shutdownContext is passed as a deadline
	//            for the shutdown, after which the server should abort all running connections and return as fast as
	//            possible.
	OnShutdown(shutdownContext context.Context)
}

SessionChannelHandler is a channel of the "session" type used for interactive and non-interactive sessions

type TestClient

type TestClient interface {
	// Connect establishes a connection to the server.
	Connect() (TestClientConnection, error)
	// MustConnect is identical to Connect, but panics it if it cannot connect.
	MustConnect() TestClientConnection
}

TestClient is an SSH client intended solely for testing purposes.

func NewTestClient

func NewTestClient(
	server string,
	hostPrivateKey string,
	user *TestUser,
	logger log.Logger,
) TestClient

NewTestClient creates a new TestClient instance with the specified parameters

- server is the host and IP pair of the server. - hostPrivateKey is the PEM-encoded private host key. The public key and fingerprint are automatically extracted. - username is the username. - password is the password used for authentication.

type TestClientConnection

type TestClientConnection interface {
	// Session establishes a new session channel
	Session() (TestClientSession, error)

	//MustSession is identical to Session but panics if a session cannot be requested.
	MustSession() TestClientSession

	// Close closes the connection and all sessions in it.
	Close() error
}

TestClientConnection is an individual established connection to the server

type TestClientSession

type TestClientSession interface {
	// SetEnv sets an environment variable or returns with an error.
	SetEnv(name string, value string) error

	// MustSetEnv is identical to SetEnv, but panics if an error happens.
	MustSetEnv(name string, value string)

	// Window requests the terminal window to be resized to a certain size.
	Window(cols int, rows int) error

	// MustWindow is identical to Window, but panics if an error happens.
	MustWindow(cols int, rows int)

	// RequestPTY requests the server to open a PTY/TTY for this channel. Returns an error if the request failed.
	RequestPTY(term string, cols int, rows int) error

	// MustRequestPTY is identical to RequestPTY but panics if an error happens.
	MustRequestPTY(term string, cols int, rows int)

	// Signal sends a signal to the process
	Signal(signal string) error

	// MustSignal is equal to Signal but panics if an error happens.
	MustSignal(signal string)

	// Shell requests a shell to be opened. After this call returns I/O interactions are possible.
	Shell() error

	// MustShell is identical to Shell but panics if an error happens.
	MustShell()

	// Exec requests a specific program to be executed. After this call returns I/O interactions are possible.
	Exec(program string) error

	// MustExec is identical to Exec but panics if an error happens.
	MustExec(program string)

	// Subsystem requests a specific subsystem to be executed. After this call returns I/O interactions are possible.
	Subsystem(name string) error

	// MustSubsystem is identical to Subsystem but panics if an error happens.
	MustSubsystem(name string)

	// Write writes to the stdin of the session.
	Write(data []byte) (int, error)

	// Type writes to the stdin slowly with 50 ms delays
	Type(data []byte) error

	// Read reads from the stdout of the session.
	Read(data []byte) (int, error)

	// ReadRemaining reads the remaining bytes from stdout until EOF.
	ReadRemaining()

	// ReadRemainingStderr reads the remaining bytes from stderr until EOF.
	ReadRemainingStderr()

	// WaitForStdout waits for a specific byte sequence to appear on the stdout.
	WaitForStdout(ctx context.Context, data []byte) error

	// Stderr returns the reader for the stdout.
	Stderr() io.Reader

	// Wait waits for the session to terminate.
	Wait() error

	// ExitCode returns the exit code received from the session, or -1 if not received.
	ExitCode() int

	// Close closes the session.
	Close() error
}

TestClientSession is a representation of a session channel inside a test client connection.

type TestServer

type TestServer interface {
	// GetHostKey returns the hosts private key in PEM format. This can be used to extract the public key.
	GetHostKey() string
	// Start starts the server in the background.
	Start()
	// Stop stops the server running in the background.
	Stop(timeout time.Duration)

	// GetListen returns the listen IP and port
	GetListen() string
}

TestServer describes

func NewTestServer

func NewTestServer(t *testing.T, handler Handler, logger log.Logger, config *config2.SSHConfig) TestServer

NewTestServer is a simplified API to start and stop a test server.

type TestUser

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

TestUser is a container for a username, a password and public keys

func NewTestUser

func NewTestUser(username string) *TestUser

NewTestUser creates a user that can be used with NewTestHandler and NewTestClient.

func (*TestUser) AddKeyboardInteractiveChallengeResponse

func (u *TestUser) AddKeyboardInteractiveChallengeResponse(challenge string, expectedResponse string)

AddKeyboardInteractiveChallengeResponse adds a challenge with an expected response for keyboard-interactive authentication.

func (*TestUser) GenerateKey

func (u *TestUser) GenerateKey() (privateKeyPEM string, publicKeyAuthorizedKeys string)

GenerateKey generates a public and private key pair that can be used to authenticate with this user.

func (*TestUser) GetAuthMethods

func (u *TestUser) GetAuthMethods() []ssh.AuthMethod

func (*TestUser) GetAuthorizedKeys

func (u *TestUser) GetAuthorizedKeys() []string

GetAuthorizedKeys returns a slice of the authorized keys of this user.

func (*TestUser) KeyboardInteractiveChallengeResponse

func (u *TestUser) KeyboardInteractiveChallengeResponse() (questions KeyboardInteractiveQuestions)

KeyboardInteractiveChallengeResponse returns a construct of KeyboardInteractiveQuestions

func (*TestUser) Password

func (u *TestUser) Password() string

Password returns the current password for this user.

func (*TestUser) RandomPassword

func (u *TestUser) RandomPassword()

RandomPassword generates a random password for this user.

func (*TestUser) SetPassword

func (u *TestUser) SetPassword(password string)

SetPassword sets a specific password for this user.

func (*TestUser) Username

func (u *TestUser) Username() string

Username returns the username of this user.

Jump to

Keyboard shortcuts

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