ssh

package module
v0.2.4 Latest Latest
Warning

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

Go to latest
Published: Sep 10, 2026 License: BSD-3-Clause Imports: 30 Imported by: 0

README

SSH Server SDK for Go

This Go package wraps the crypto/ssh package with a higher-level API for building SSH servers. The goal of the API was to make it as simple as using net/http, so the API is very similar:

 package main

 import (
     "context"
     "io"
     "log"

     "github.com/engity-com/ssh-server-go"
 )

 func main() {
     ssh.Handle(func(s ssh.Session) error {
         _, err := io.WriteString(s, "Hello world\n")
         return err
     })

     log.Fatal(ssh.ListenAndServe(context.Background(), "127.0.0.1:2222", nil))
 }

[!NOTE] The minimal example uses anonymous authentication and an automatically generated, process-local host key. These defaults are intended for local development only. Production servers should configure PasswordHandler, PublicKeyHandler, or a custom authentication policy, add a persistent host key, set RequireHostSigners: true and RequireClientAuth: true, and expose only explicitly intended interfaces and forwarding destinations. Agent forwarding is denied unless AgentForwardingCallback explicitly allows it.

The module requires Go 1.27 or newer. The core package is portable across the platforms supported by Go and its dependencies; some examples require Unix facilities or programs.

Getting into this SDK

Acknowledgements

This project was originally forked from gliderlabs/ssh. Special thanks to its maintainers and contributors for laying the foundation for this work.

Documentation

Overview

Package ssh wraps the crypto/ssh package with a higher-level API for building SSH servers. The goal of the API was to make it as simple as using net/http, so the API is very similar.

You should be able to build any SSH server using only this package, which wraps relevant types and some functions from crypto/ssh. However, you still need to use crypto/ssh for building SSH clients.

ListenAndServe starts an SSH server with a given address, handler, and options. The handler is usually nil, which means to use DefaultHandler. Handle sets DefaultHandler:

ctx := context.Background()

ssh.Handle(func(s ssh.Session) error {
    _, err := io.WriteString(s, "Hello world\n")
    return err
})

log.Fatal(ssh.ListenAndServe(ctx, ":2222", nil))

If you don't specify a host key, the Server generates one on first use and reuses it for its lifetime. This development convenience does not provide a stable identity across processes. Production servers should configure a persistent signer and set Server.RequireHostSigners. It's a better idea to generate or point to an existing key on your system:

log.Fatal(ssh.ListenAndServe(ctx, ":2222", nil, ssh.HostKeyFile("/Users/progrium/.ssh/id_rsa")))

Although all options have functional option helpers, another way to control the server's behavior is by creating a custom Server:

s := &ssh.Server{
    Addr:             ":2222",
    Handler:          sessionHandler,
    PublicKeyHandler: authHandler,
}
s.AddHostKey(hostKeySigner)

log.Fatal(s.ListenAndServe(ctx))

See Server for lifecycle rules, ErrorHandler for operational errors, and SessionExitError for controlled session failures.

This package handles basic SSH requests such as environment variables, PTYs, window changes, signals, and breaks. Relevant state and delivery hooks are exposed through Session.

The module requires the Go version declared in go.mod. The core package supports the operating systems supported by its dependencies; examples that launch Unix programs or use Unix sockets have additional platform requirements.

Index

Examples

Constants

View Source
const (
	DefaultHandshakeTimeout                = 2 * time.Minute
	DefaultIdleTimeout                     = time.Duration(0)
	DefaultMaxTimeout                      = time.Duration(0)
	DefaultSessionRequestTimeout           = 30 * time.Second
	DefaultMaxStartupsStart                = 10
	DefaultMaxStartupsRate                 = 30
	DefaultMaxStartupsFull                 = 100
	DefaultMaxSessionsPerConnection        = 10
	DefaultMaxChannelsPerConnection        = 64
	DefaultMaxReverseForwardsPerConnection = 16
	DefaultMaxConnections                  = 256
	DefaultMaxChannels                     = 64
	DefaultMaxReverseForwards              = 256
)

Variables

View Source
var (

	// ContextKeyUser is a context key for use with Contexts in this package.
	// The associated value will be of type string.
	ContextKeyUser = &contextKey{"user"}

	// ContextKeySessionID is a context key for use with Contexts in this package.
	// The associated value will be of type string.
	ContextKeySessionID = &contextKey{"session-id"}

	// ContextKeyPermissions is a context key for use with Contexts in this package.
	// The associated value will be of type *Permissions.
	ContextKeyPermissions = &contextKey{"permissions"}

	// ContextKeyClientVersion is a context key for use with Contexts in this package.
	// The associated value will be of type string.
	ContextKeyClientVersion = &contextKey{"client-version"}

	// ContextKeyServerVersion is a context key for use with Contexts in this package.
	// The associated value will be of type string.
	ContextKeyServerVersion = &contextKey{"server-version"}

	// ContextKeyLocalAddr is a context key for use with Contexts in this package.
	// The associated value will be of type net.Addr.
	ContextKeyLocalAddr = &contextKey{"local-addr"}

	// ContextKeyRemoteAddr is a context key for use with Contexts in this package.
	// The associated value will be of type net.Addr.
	ContextKeyRemoteAddr = &contextKey{"remote-addr"}

	// ContextKeyServer is a context key for use with Contexts in this package.
	// The associated value will be of type *Server.
	ContextKeyServer = &contextKey{"ssh-server"}

	// ContextKeyConn is a context key for use with Contexts in this package.
	// The associated value will be of type *gossh.ServerConn after the SSH
	// handshake has completed.
	ContextKeyConn = &contextKey{"ssh-conn"}

	// ContextKeyPublicKey is a context key for use with Contexts in this package.
	// The associated value will be of type PublicKey.
	ContextKeyPublicKey = &contextKey{"public-key"}
)
View Source
var (
	// ErrErrorResponseUnsupported is returned when an error cannot be reported
	// to the client at the point where it occurred.
	ErrErrorResponseUnsupported = errors.New("ssh: error response is not supported")
	// ErrErrorResponseAlreadySent is returned when an ErrorResponder is called
	// again after a response attempt. The first response may already have changed
	// the SSH protocol state even if it returned an error.
	ErrErrorResponseAlreadySent = errors.New("ssh: error response was already sent")
	// ErrErrorResponseExpired is returned when an ErrorResponder is called after
	// its ErrorHandler has returned.
	ErrErrorResponseExpired = errors.New("ssh: error responder has expired")
	// ErrNextErrorHandlerAlreadyCalled is returned when next is called more than
	// once by the same ErrorHandler invocation.
	ErrNextErrorHandlerAlreadyCalled = errors.New("ssh: next error handler was already called")
	// ErrNextErrorHandlerExpired is returned when next is called after its
	// ErrorHandler has returned.
	ErrNextErrorHandlerExpired = errors.New("ssh: next error handler has expired")
	// ErrNextErrorHandlerIncomplete indicates that next did not complete before
	// its ErrorHandler returned.
	ErrNextErrorHandlerIncomplete = errors.New("ssh: next error handler did not complete synchronously")
)
View Source
var (
	ErrRequestResponseAlreadySent = errors.New("ssh: request response was already sent")
	ErrRequestResponseExpired     = errors.New("ssh: request response is no longer valid")
	ErrRequestResponseIncomplete  = errors.New("ssh: request response did not complete synchronously")
	ErrRequestResponseNotSent     = errors.New("ssh: request handler returned without a response")
)
View Source
var (
	// ErrGracefulShutdownTimeout is joined into the returned error when a
	// context-triggered graceful shutdown exceeds its configured period.
	ErrGracefulShutdownTimeout = errors.New("ssh: graceful shutdown timeout")

	ErrServerPermissionDenied     = errors.New("permission denied")
	ErrServerHostSignerRequired   = errors.New("ssh: at least one persistent host signer is required")
	ErrServerClientAuthRequired   = errors.New("ssh: at least one client authentication method is required")
	ErrServerAuthCallbackConflict = errors.New("ssh: conflicting authentication callbacks")
	ErrChannelResponseAlreadySent = errors.New("ssh: channel response was already sent")
	ErrChannelResponseNotSent     = errors.New("ssh: channel handler returned without accepting or rejecting the channel")
)
View Source
var DefaultChannelHandlers = map[string]ChannelHandler{
	"session": DefaultSessionHandler,
}

DefaultChannelHandlers is used by servers with nil ChannelHandlers. It must not be mutated while any such server is serving connections.

View Source
var DefaultRequestHandlers = map[string]RequestHandler{}

DefaultRequestHandlers is used by servers with nil RequestHandlers. It must not be mutated while any such server is serving connections.

View Source
var DefaultSubsystemHandlers = map[string]SubsystemHandler{}

DefaultSubsystemHandlers is used by servers with nil SubsystemHandlers. It must not be mutated while any such server is serving connections.

Functions

func AgentRequested

func AgentRequested(sess Session) bool

AgentRequested returns true if the client requested agent forwarding.

func DefaultSessionHandler

func DefaultSessionHandler(srv *Server, conn *gossh.ServerConn, newChan gossh.NewChannel, ctx Context) error

func DirectStreamLocalHandler added in v0.2.0

func DirectStreamLocalHandler(srv *Server, sshConn *gossh.ServerConn, newChan gossh.NewChannel, ctx Context) error

DirectStreamLocalHandler handles client-to-server Unix socket forwarding. It can be enabled under the direct-streamlocal@openssh.com channel type. The configured LocalUnixForwardingCallback owns path authorization and dialing.

func DirectTCPIPHandler

func DirectTCPIPHandler(srv *Server, sshConn *gossh.ServerConn, newChan gossh.NewChannel, ctx Context) error

DirectTCPIPHandler can be enabled by adding it to the server's ChannelHandlers under direct-tcpip.

func ForwardAgentConnections

func ForwardAgentConnections(ln net.Listener, logger log.Logger, sess Session)

ForwardAgentConnections takes connections from a listener to proxy into the session on the OpenSSH channel for agent connections. It blocks and services connections until the listener stop accepting.

func FullDuplexCopy

func FullDuplexCopy(ctx context.Context, left io.ReadWriteCloser, right io.ReadWriteCloser, opts *FullDuplexCopyOpts) (rErr error)

FullDuplexCopy copies data in both directions until both streams finish. It half-closes completed streams and closes both sides on cancellation or error. To guarantee cancellation, Close on each side must unblock concurrent Read and Write calls; implementations that do not honor that contract can prevent this function from returning. A nil context is treated as context.Background.

func Handle

func Handle(handler Handler)

Handle registers the handler as the DefaultHandler.

func KeysEqual

func KeysEqual(ak, bk PublicKey) bool

KeysEqual is constant time compare of the keys to avoid timing attacks.

func ListenAndServe

func ListenAndServe(ctx context.Context, addr string, handler Handler, options ...Option) error

ListenAndServe listens on the TCP network address addr and then calls Serve with handler to handle sessions on incoming connections. Handler is typically nil, in which case the DefaultHandler is used. Canceling ctx stops the server. The returned error contains the context cause and may contain cleanup errors or ErrGracefulShutdownTimeout; callers should inspect it with errors.Is.

Example
package main

import (
	"context"
	"io"
	"log"

	"github.com/engity-com/ssh-server-go"
)

func main() {
	log.Fatal(ssh.ListenAndServe(context.Background(), ":2222", func(s ssh.Session) error {
		_, err := io.WriteString(s, "Hello world\n")
		return err
	}))
}

func NewAgentListener

func NewAgentListener() (net.Listener, error)

NewAgentListener sets up a temporary Unix socket that can be communicated to the session environment and used for forwarding connections.

func Serve

func Serve(ctx context.Context, l net.Listener, handler Handler, options ...Option) error

Serve accepts incoming SSH connections on the listener l, creating a new connection goroutine for each. The connection goroutines read requests and then calls handler to handle sessions. Handler is typically nil, in which case the DefaultHandler is used. Canceling ctx stops this Serve scope. The returned error contains the context cause and may contain cleanup errors or ErrGracefulShutdownTimeout; callers should inspect it with errors.Is.

func SetAgentRequested

func SetAgentRequested(ctx Context)

SetAgentRequested sets up the session context so that AgentRequested returns true.

Types

type AgentForwardingCallback

type AgentForwardingCallback func(ctx Context, sess Session) (bool, error)

AgentForwardingCallback is a hook for allowing agent forwarding per session. A nil callback or false, nil denies agent forwarding. A non-nil error is passed to ErrorHandler and ends the session by default.

type BannerHandler

type BannerHandler func(ctx Context, conn gossh.ConnMetadata) (string, error)

BannerHandler resolves the server banner for a connection. A returned error aborts the SSH handshake.

type ChannelHandler

type ChannelHandler func(srv *Server, conn *gossh.ServerConn, newChan gossh.NewChannel, ctx Context) error

ChannelHandler handles one channel synchronously. It should not return until ownership of the channel and its associated resources has ended. Returning an error reports it to ErrorHandler.

type Cipher

type Cipher uint8
const (
	CipherAes128Cbc Cipher = iota
	Cipher3desCbc
	CipherArcfour
	CipherArcfour128
	CipherArcfour256
	CipherChacha20Poly1305
	CipherAes128Ctr
	CipherAes192Ctr
	CipherAes256Ctr
	CipherAes128Gcm
	CipherAes256Gcm
)

func (Cipher) IsEqualTo

func (c Cipher) IsEqualTo(other any) bool

func (Cipher) IsZero

func (c Cipher) IsZero() bool

func (Cipher) MarshalText

func (c Cipher) MarshalText() (text []byte, err error)

func (*Cipher) Set

func (c *Cipher) Set(text string) error

func (Cipher) String

func (c Cipher) String() string

func (*Cipher) UnmarshalText

func (c *Cipher) UnmarshalText(text []byte) error

func (Cipher) Validate

func (c Cipher) Validate() error

type Ciphers

type Ciphers []Cipher

func (Ciphers) Contains

func (c Ciphers) Contains(v Cipher) bool

func (Ciphers) IsCumulative

func (c Ciphers) IsCumulative() bool

func (Ciphers) IsEmpty

func (c Ciphers) IsEmpty() bool

func (Ciphers) IsEqualTo

func (c Ciphers) IsEqualTo(other any) bool

func (Ciphers) IsZero

func (c Ciphers) IsZero() bool

func (Ciphers) MarshalText

func (c Ciphers) MarshalText() (text []byte, err error)

func (Ciphers) MarshalTexts

func (c Ciphers) MarshalTexts() (texts [][]byte, err error)

func (*Ciphers) Set

func (c *Ciphers) Set(text string) error

func (Ciphers) String

func (c Ciphers) String() string

func (*Ciphers) UnmarshalText

func (c *Ciphers) UnmarshalText(text []byte) error

func (Ciphers) Validate

func (c Ciphers) Validate() error

type ConnCallback

type ConnCallback func(ctx Context, conn net.Conn) (net.Conn, error)

ConnCallback is a hook for new connections before handling. It allows wrapping for timeouts and limiting by returning the net.Conn that will be used as the underlying connection. Implementations must return promptly and honor Context cancellation; network deadlines cannot forcibly stop callback code that blocks without doing I/O. Returning nil, nil drops the connection without reporting an operational error. A non-nil error aborts the SSH handshake. A returned connection must own the input connection and close it when the returned connection is closed.

type ConnectionFailedCallback

type ConnectionFailedCallback func(ctx Context, conn net.Conn, err error) error

ConnectionFailedCallback is a hook for reporting failed connections. The net.Conn is likely to be closed at this point. A returned error is joined with the connection failure before it is passed to ErrorHandler.

type Context

type Context interface {
	context.Context
	sync.Locker

	// User returns the current username, or "" before metadata is available.
	User() string

	// SessionID returns the hex-encoded session hash, or "" before it is available.
	SessionID() string

	// ClientVersion returns the version reported by the client, or "" before it is available.
	ClientVersion() string

	// ServerVersion returns the version reported by the server, or "" before it is available.
	ServerVersion() string

	// RemoteAddr returns the remote address, or nil before metadata is available.
	RemoteAddr() net.Addr

	// LocalAddr returns the local address, or nil before metadata is available.
	LocalAddr() net.Addr

	// Permissions returns the current authentication permissions. Server-created
	// contexts initialize an empty value before authentication starts.
	Permissions() *Permissions

	// SetValue allows you to easily write new values into the underlying context.
	SetValue(key, value any)
}

Context is a package-specific context interface. It exposes connection metadata and allows new values to be written to it. Metadata getters return zero values until metadata is available during the SSH handshake. Context is used in authentication handlers and callbacks and exposed by Session.Context. A connection-scoped lock is embedded for coordinating application state.

type DisconnectCallback added in v0.2.0

type DisconnectCallback func(ctx Context, conn net.Conn) error

DisconnectCallback is called exactly once after a successfully established SSH connection ends. The Context is canceled, the connection is closed, and connection workers have stopped before the callback runs. Implementations must return promptly. A returned error is passed to ErrorHandler. Panics from the callback are not recovered.

type ErrorHandler added in v0.2.0

type ErrorHandler func(
	ctx context.Context,
	scope ErrorScope,
	operation ErrorOperation,
	err error,
	respond ErrorResponder,
	next ErrorHandler,
) (canContinue bool, filteredErr error)

ErrorHandler handles operational errors. scope and operation identify their source, respond sends a client-safe response, and next applies the default handling.

respond and next are valid only during the call and may each be used once. A non-nil filteredErr stops processing; otherwise canContinue permits, but does not guarantee, continued processing. Calls may run concurrently and must return promptly.

type ErrorOperation added in v0.2.0

type ErrorOperation uint8

ErrorOperation identifies the operation which failed within an ErrorScope.

const (
	ErrorOperationAccept ErrorOperation = iota
	ErrorOperationParse
	ErrorOperationHandle
	ErrorOperationReply
	ErrorOperationHandshake
	ErrorOperationDial
	ErrorOperationListen
	ErrorOperationOpenChannel
	ErrorOperationForward
)

func (ErrorOperation) MarshalText added in v0.2.0

func (v ErrorOperation) MarshalText() ([]byte, error)

func (*ErrorOperation) Set added in v0.2.0

func (v *ErrorOperation) Set(text string) error

func (ErrorOperation) String added in v0.2.0

func (v ErrorOperation) String() string

func (*ErrorOperation) UnmarshalText added in v0.2.0

func (v *ErrorOperation) UnmarshalText(text []byte) error

func (ErrorOperation) Validate added in v0.2.0

func (v ErrorOperation) Validate() error

type ErrorResponder added in v0.2.0

type ErrorResponder func(message []byte, closeAfterResponse bool) error

ErrorResponder sends a scope-appropriate response to the client. It is valid only during its ErrorHandler call and may be used once. closeAfterResponse closes the associated SSH connection after the response attempt.

type ErrorScope added in v0.2.0

type ErrorScope uint8

ErrorScope identifies the protocol resource in which an error occurred.

const (
	ErrorScopeServer ErrorScope = iota
	ErrorScopeConnection
	ErrorScopeRequest
	ErrorScopeChannel
	ErrorScopeSession
	ErrorScopeForwarding
)

func (ErrorScope) MarshalText added in v0.2.0

func (v ErrorScope) MarshalText() ([]byte, error)

func (*ErrorScope) Set added in v0.2.0

func (v *ErrorScope) Set(text string) error

func (ErrorScope) String added in v0.2.0

func (v ErrorScope) String() string

func (*ErrorScope) UnmarshalText added in v0.2.0

func (v *ErrorScope) UnmarshalText(text []byte) error

func (ErrorScope) Validate added in v0.2.0

func (v ErrorScope) Validate() error

type ForwardedTCPHandler

type ForwardedTCPHandler struct {
	Logger log.Logger

	sync.Mutex
	// contains filtered or unexported fields
}

ForwardedTCPHandler can be enabled by creating a ForwardedTCPHandler and adding the HandleSSHRequest callback to the server's RequestHandlers under tcpip-forward and cancel-tcpip-forward.

func (*ForwardedTCPHandler) HandleSSHRequest

func (h *ForwardedTCPHandler) HandleSSHRequest(response RequestResponseWriter, request *Request) error

type ForwardedUnixHandler added in v0.2.0

type ForwardedUnixHandler struct {
	Logger log.Logger

	sync.Mutex
	// contains filtered or unexported fields
}

ForwardedUnixHandler handles server-to-client Unix socket forwarding. Add HandleSSHRequest under streamlocal-forward@openssh.com and cancel-streamlocal-forward@openssh.com. Forwarding remains disabled unless a ReverseUnixForwardingCallback is configured.

func (*ForwardedUnixHandler) HandleSSHRequest added in v0.2.0

func (h *ForwardedUnixHandler) HandleSSHRequest(response RequestResponseWriter, request *Request) error

type FullDuplexCopyOpts

type FullDuplexCopyOpts struct {
	OnStart       func()
	OnEnd         func(l2r, r2l int64, duration time.Duration, err error, wasInL2r *bool)
	OnStreamStart func(isL2r bool)
	OnStreamEnd   func(isL2r bool, err error)
}

FullDuplexCopyOpts defines optional callbacks for observing a bidirectional copy. Callbacks are invoked asynchronously in event order on one observer goroutine and cannot delay FullDuplexCopy. They must still return promptly to avoid retaining that observer goroutine.

type GracefulShutdownHandler added in v0.2.0

type GracefulShutdownHandler func(context.Context) (time.Duration, error)

GracefulShutdownHandler determines how long a context-triggered shutdown waits for connections to drain before closing them. A returned error forces immediate shutdown.

func NewGracefulShutdownTimeoutHandler added in v0.2.0

func NewGracefulShutdownTimeoutHandler(timeout time.Duration) GracefulShutdownHandler

NewGracefulShutdownTimeoutHandler returns a graceful shutdown handler that always uses timeout.

type Handler

type Handler func(Session) error

Handler handles an established SSH session. Returned errors are passed to ErrorHandler and end the session by default. Return SessionExitError for a controlled client message and exit status.

var DefaultHandler Handler

DefaultHandler is the default Handler used by Serve.

type KeyExchange

type KeyExchange uint8
const (
	KeyExchangeDh1Sha1 KeyExchange = iota
	KeyExchangeDh14Sha1
	KeyExchangeDh14Sha256
	KeyExchangeDh16Sha512
	KeyExchangeEcdh256
	KeyExchangeEcdh384
	KeyExchangeEcdh521
	KeyExchangeCurve25519Sha256LibSsh
	KeyExchangeCurve25519Sha256
	KeyExchangeDhgexSha1
	KeyExchangeDhgexSha256
	KeyExchangeMlkem768x25519xSha256
)

func (KeyExchange) IsEqualTo

func (ke KeyExchange) IsEqualTo(other any) bool

func (KeyExchange) IsZero

func (ke KeyExchange) IsZero() bool

func (KeyExchange) MarshalText

func (ke KeyExchange) MarshalText() (text []byte, err error)

func (*KeyExchange) Set

func (ke *KeyExchange) Set(text string) error

func (KeyExchange) String

func (ke KeyExchange) String() string

func (*KeyExchange) UnmarshalText

func (ke *KeyExchange) UnmarshalText(text []byte) error

func (KeyExchange) Validate

func (ke KeyExchange) Validate() error

type KeyExchanges

type KeyExchanges []KeyExchange

func (KeyExchanges) Contains

func (ke KeyExchanges) Contains(v KeyExchange) bool

func (KeyExchanges) IsCumulative

func (ke KeyExchanges) IsCumulative() bool

func (KeyExchanges) IsEmpty

func (ke KeyExchanges) IsEmpty() bool

func (KeyExchanges) IsEqualTo

func (ke KeyExchanges) IsEqualTo(other any) bool

func (KeyExchanges) IsZero

func (ke KeyExchanges) IsZero() bool

func (KeyExchanges) MarshalText

func (ke KeyExchanges) MarshalText() (text []byte, err error)

func (KeyExchanges) MarshalTexts

func (ke KeyExchanges) MarshalTexts() (texts [][]byte, err error)

func (*KeyExchanges) Set

func (ke *KeyExchanges) Set(text string) error

func (KeyExchanges) String

func (ke KeyExchanges) String() string

func (*KeyExchanges) UnmarshalText

func (ke *KeyExchanges) UnmarshalText(text []byte) error

func (KeyExchanges) Validate

func (ke KeyExchanges) Validate() error

type KeyboardInteractiveHandler

type KeyboardInteractiveHandler func(ctx Context, conn gossh.ConnMetadata, challenger gossh.KeyboardInteractiveChallenge) (bool, error)

KeyboardInteractiveHandler is a callback for performing keyboard-interactive authentication. Returning false, nil denies the authentication attempt. A non-nil error aborts the SSH handshake.

type LocalPortForwardingCallback

type LocalPortForwardingCallback func(ctx Context, conn gossh.ConnMetadata, destinationHost string, destinationPort uint32) (bool, error)

LocalPortForwardingCallback is a hook for allowing port forwarding. Returning false, nil denies the request; a non-nil error is passed to ErrorHandler.

type LocalUnixForwardingCallback added in v0.2.0

type LocalUnixForwardingCallback func(ctx Context, conn gossh.ConnMetadata, socketPath string) (net.Conn, error)

LocalUnixForwardingCallback handles a direct-streamlocal@openssh.com request. A successful callback transfers ownership of the returned connection to the server. Return ErrServerPermissionDenied to reject the request without exposing an operational error to the client. Implementations must honor context cancellation.

type MaxStartupsConfig

type MaxStartupsConfig struct {
	Start int // number of unauthenticated connections before random early drop begins
	Rate  int // initial drop probability in percent, clamped to 0..100
	Full  int // hard limit for unauthenticated connections; nonpositive disables the limit
}

MaxStartupsConfig limits concurrent unauthenticated connections using the OpenSSH start:rate:full random early-drop model.

type MessageAuthentication

type MessageAuthentication uint8
const (
	MessageAuthenticationHmacSha1 MessageAuthentication = iota
	MessageAuthenticationHmacSha1B96
	MessageAuthenticationHmacSha2B256
	MessageAuthenticationHmacSha2B512
	MessageAuthenticationHmacSha2B256Etm
	MessageAuthenticationHmacSha2B512Etm
)

func (MessageAuthentication) IsEqualTo

func (ma MessageAuthentication) IsEqualTo(other any) bool

func (MessageAuthentication) IsZero

func (ma MessageAuthentication) IsZero() bool

func (MessageAuthentication) MarshalText

func (ma MessageAuthentication) MarshalText() (text []byte, err error)

func (*MessageAuthentication) Set

func (ma *MessageAuthentication) Set(text string) error

func (MessageAuthentication) String

func (ma MessageAuthentication) String() string

func (*MessageAuthentication) UnmarshalText

func (ma *MessageAuthentication) UnmarshalText(text []byte) error

func (MessageAuthentication) Validate

func (ma MessageAuthentication) Validate() error

type MessageAuthentications

type MessageAuthentications []MessageAuthentication

func (MessageAuthentications) Contains

func (MessageAuthentications) IsCumulative

func (me MessageAuthentications) IsCumulative() bool

func (MessageAuthentications) IsEmpty

func (me MessageAuthentications) IsEmpty() bool

func (MessageAuthentications) IsEqualTo

func (me MessageAuthentications) IsEqualTo(other any) bool

func (MessageAuthentications) IsZero

func (me MessageAuthentications) IsZero() bool

func (MessageAuthentications) MarshalText

func (me MessageAuthentications) MarshalText() (text []byte, err error)

func (MessageAuthentications) MarshalTexts

func (me MessageAuthentications) MarshalTexts() (texts [][]byte, err error)

func (*MessageAuthentications) Set

func (me *MessageAuthentications) Set(text string) error

func (MessageAuthentications) String

func (me MessageAuthentications) String() string

func (*MessageAuthentications) UnmarshalText

func (me *MessageAuthentications) UnmarshalText(text []byte) error

func (MessageAuthentications) Validate

func (me MessageAuthentications) Validate() error

type Option

type Option func(*Server) error

Option is a functional option handler for Server.

func EnableProxyProtocol added in v0.2.0

func EnableProxyProtocol(config ...ProxyProtocolConfig) Option

EnableProxyProtocol returns a functional option that enables PROXY protocol processing. With no configuration, every connection must supply a PROXY header and the header is trusted from any peer. At most one configuration may be supplied.

func HostKeyFile

func HostKeyFile(filepath string) Option

HostKeyFile returns a functional option that adds HostSigners to the server from a PEM file at filepath.

Example
package main

import (
	"context"
	"log"

	"github.com/engity-com/ssh-server-go"
)

func main() {
	log.Fatal(ssh.ListenAndServe(context.Background(), ":2222", nil, ssh.HostKeyFile("/path/to/host/key")))
}

func HostKeyPEM

func HostKeyPEM(bytes []byte) Option

HostKeyPEM returns a functional option that adds HostSigners to the server from a PEM file as bytes.

func KeyboardInteractiveAuth

func KeyboardInteractiveAuth(fn KeyboardInteractiveHandler) Option

func NoPty

func NoPty() Option

NoPty returns a functional option that sets PtyCallback to return false, denying PTY requests.

Example
package main

import (
	"context"
	"log"

	"github.com/engity-com/ssh-server-go"
)

func main() {
	log.Fatal(ssh.ListenAndServe(context.Background(), ":2222", nil, ssh.NoPty()))
}

func PasswordAuth

func PasswordAuth(fn PasswordHandler) Option

PasswordAuth returns a functional option that sets PasswordHandler on the server.

Example
package main

import (
	"context"
	"log"

	"github.com/engity-com/ssh-server-go"
	gossh "golang.org/x/crypto/ssh"
)

func main() {
	log.Fatal(ssh.ListenAndServe(context.Background(), ":2222", nil,
		ssh.PasswordAuth(func(ctx ssh.Context, conn gossh.ConnMetadata, pass string) (bool, error) {
			return pass == "secret", nil
		}),
	))
}

func PublicKeyAuth

func PublicKeyAuth(fn PublicKeyHandler) Option

PublicKeyAuth returns a functional option that sets PublicKeyHandler on the server.

Example
package main

import (
	"context"
	"log"
	"os"

	"github.com/engity-com/ssh-server-go"
	gossh "golang.org/x/crypto/ssh"
)

func main() {
	log.Fatal(ssh.ListenAndServe(context.Background(), ":2222", nil,
		ssh.PublicKeyAuth(func(ctx ssh.Context, conn gossh.ConnMetadata, key ssh.PublicKey) (bool, error) {
			data, _ := os.ReadFile("/path/to/allowed/key.pub")
			allowed, _, _, _, _ := ssh.ParseAuthorizedKey(data)
			return ssh.KeysEqual(key, allowed), nil
		}),
	))
}

func WithErrorHandler added in v0.2.0

func WithErrorHandler(handler ErrorHandler) Option

WithErrorHandler returns a functional option that sets the central handler for operational server and handler errors.

func WithGracefulShutdownHandler added in v0.2.0

func WithGracefulShutdownHandler(handler GracefulShutdownHandler) Option

WithGracefulShutdownHandler returns a functional option that sets the graceful shutdown handler on the server.

func WithGracefulShutdownTimeout added in v0.2.0

func WithGracefulShutdownTimeout(timeout time.Duration) Option

WithGracefulShutdownTimeout returns a functional option that configures a fixed graceful shutdown timeout on the server.

func WrapConn

func WrapConn(fn ConnCallback) Option

WrapConn returns a functional option that sets ConnCallback on the server.

type PasswordHandler

type PasswordHandler func(ctx Context, conn gossh.ConnMetadata, password string) (bool, error)

PasswordHandler is a callback for performing password authentication. Returning false, nil denies the authentication attempt. A non-nil error aborts the SSH handshake.

type Permissions

type Permissions struct {
	*gossh.Permissions
}

The Permissions type holds fine-grained permissions that are specific to a user or a specific authentication method for a user. Permissions, except for "source-address", must be enforced in the server application layer, after successful authentication.

type ProxyProtocolConfig added in v0.2.0

type ProxyProtocolConfig struct {
	// ConnPolicy decides whether a connection may supply a PROXY header and how
	// that header is handled. It must return promptly.
	ConnPolicy proxyproto.ConnPolicyFunc
	// ValidateHeader performs application-specific validation after parsing. It
	// must return promptly.
	ValidateHeader proxyproto.Validator
	// ReadHeaderTimeout bounds PROXY header processing. Zero uses the
	// go-proxyproto default; a negative value disables its header timeout.
	ReadHeaderTimeout time.Duration
	// ReadBufferSize controls the per-connection header buffer. A nonpositive
	// value uses the go-proxyproto default. Values below 107 bytes break maximum
	// length version 1 headers.
	ReadBufferSize int
}

ProxyProtocolConfig configures PROXY protocol processing for accepted connections. A nil ConnPolicy requires a PROXY header but trusts the source information supplied by every peer. Listeners reachable by untrusted peers should configure a policy such as proxyproto.TrustProxyHeaderFrom or proxyproto.TrustProxyHeaderFromRanges. Connections are wrapped before ConnCallback, while the header is processed lazily on first I/O or address access. A callback can type-assert the connection to *proxyproto.Conn and use Raw to inspect the transport peer instead of the address supplied by the header. It must retain or wrap the supplied connection for PROXY processing to remain effective.

type Pty

type Pty struct {
	Term   string
	Window Window
	// TerminalModes contains the initial RFC 4254 terminal modes requested by
	// the client. Callers may modify the map; each API boundary returns a copy.
	TerminalModes gossh.TerminalModes
}

Pty represents the metadata in an accepted PTY request. This package exposes the requested values but does not allocate or configure an operating-system PTY and does not interpret or apply TerminalModes.

type PtyCallback

type PtyCallback func(ctx Context, sess Session, pty Pty) (bool, error)

PtyCallback is a hook for allowing PTY sessions. The Pty contains the client's requested metadata; this package does not allocate or configure an operating-system PTY. Returning false, nil denies the request. A non-nil error is passed to ErrorHandler and ends the session by default.

type PublicKey

type PublicKey interface {
	gossh.PublicKey
}

PublicKey is an abstraction of different types of public keys.

func ParseAuthorizedKey

func ParseAuthorizedKey(in []byte) (out PublicKey, comment string, options []string, rest []byte, err error)

ParseAuthorizedKey parses a public key from an authorized_keys file used in OpenSSH according to the sshd(8) manual page.

func ParsePublicKey

func ParsePublicKey(in []byte) (out PublicKey, err error)

ParsePublicKey parses an SSH public key formatted for use in the SSH wire protocol according to RFC 4253, section 6.6.

type PublicKeyHandler

type PublicKeyHandler func(ctx Context, conn gossh.ConnMetadata, key PublicKey) (bool, error)

PublicKeyHandler is a callback for performing public key authentication. Returning false, nil denies the authentication attempt. A non-nil error aborts the SSH handshake.

type Request added in v0.2.0

type Request struct {
	Type      string
	Payload   []byte
	WantReply bool
	// contains filtered or unexported fields
}

Request contains one connection-level SSH request. Payload remains valid for the duration of the RequestHandler call and must not be retained or modified.

func (*Request) Context added in v0.2.0

func (r *Request) Context() Context

Context returns the SSH connection context associated with this request.

func (*Request) Server added in v0.2.0

func (r *Request) Server() *Server

Server returns the server handling this request.

type RequestHandler

type RequestHandler func(response RequestResponseWriter, request *Request) error

RequestHandler handles one connection-level SSH request. A returned error is passed to ErrorHandler. A request that wants a reply must be completed through response before the handler returns.

type RequestResponseWriter added in v0.2.0

type RequestResponseWriter interface {
	Accept(payload []byte) error
	Reject(message []byte) error
}

RequestResponseWriter completes a request with either acceptance or rejection. Each writer is valid only during its RequestHandler call and may be completed at most once. When Request.WantReply is false, the decision is recorded without writing a protocol reply.

type ReversePortForwardingCallback

type ReversePortForwardingCallback func(ctx Context, conn gossh.ConnMetadata, bindHost string, bindPort uint32) (bool, error)

ReversePortForwardingCallback is a hook for allowing reverse port forwarding. Returning false, nil denies the request; a non-nil error is passed to ErrorHandler.

type ReverseUnixForwardingCallback added in v0.2.0

type ReverseUnixForwardingCallback func(ctx Context, conn gossh.ConnMetadata, socketPath string) (net.Listener, error)

ReverseUnixForwardingCallback handles a streamlocal-forward@openssh.com request. A successful callback transfers ownership of the returned listener to the server. Close must unblock Accept. Return ErrServerPermissionDenied to reject the request without exposing an operational error to the client. Path validation, socket creation, permissions, and stale-file handling are application policy. Callbacks returning a *net.UnixListener must also choose appropriate unlink-on-close behavior. Implementations must honor context cancellation.

type Server

type Server struct {
	Logger log.Logger

	Addr                   string                 // TCP address to listen on, ":22" if empty
	Handler                Handler                // handler to invoke, ssh.DefaultHandler if nil
	HostSigners            []Signer               // private keys for the host key, must have at least one
	RequireHostSigners     bool                   // reject startup without an explicitly configured host signer
	RequireClientAuth      bool                   // reject connections without an effective, non-anonymous client authentication method
	Version                string                 // server version to be sent before the initial handshake
	Ciphers                Ciphers                // allowed ciphers, DefaultCiphers if empty
	KeyExchanges           KeyExchanges           // allowed key exchanges, DefaultKeyExchanges if empty
	MessageAuthentications MessageAuthentications // allowed MACs, DefaultMessageAuthentications if empty

	BannerHandler                 BannerHandler                 // server banner handler
	KeyboardInteractiveHandler    KeyboardInteractiveHandler    // keyboard-interactive authentication handler
	PasswordHandler               PasswordHandler               // password authentication handler
	PublicKeyHandler              PublicKeyHandler              // public key authentication handler
	PtyCallback                   PtyCallback                   // callback for allowing PTY sessions, allows all if nil
	ConnCallback                  ConnCallback                  // optional callback for wrapping net.Conn before handling
	LocalPortForwardingCallback   LocalPortForwardingCallback   // callback for allowing local port forwarding, denies all if nil
	ReversePortForwardingCallback ReversePortForwardingCallback // callback for allowing reverse port forwarding, denies all if nil
	LocalUnixForwardingCallback   LocalUnixForwardingCallback   // callback for local Unix forwarding, denies all if nil
	ReverseUnixForwardingCallback ReverseUnixForwardingCallback // callback for reverse Unix forwarding, denies all if nil
	ServerConfigCallback          ServerConfigCallback          // callback for detailed SSH options; same-method auth conflicts are rejected
	SessionRequestCallback        SessionRequestCallback        // callback for allowing or denying SSH sessions
	AgentForwardingCallback       AgentForwardingCallback       // callback for allowing agent forwarding, denies all if nil

	ConnectionFailedCallback ConnectionFailedCallback // callback to report connection failures
	DisconnectCallback       DisconnectCallback       // callback after an established SSH connection ends
	ErrorHandler             ErrorHandler             // central callback for operational server and handler errors
	// GracefulShutdownHandler determines how long a context-triggered shutdown
	// waits for connections to drain before closing them. It is called once with
	// the original canceled context after the listener has been closed and must
	// return promptly. Nil or a nonpositive result disables graceful shutdown.
	GracefulShutdownHandler GracefulShutdownHandler

	// ProxyProtocol enables PROXY protocol processing when non-nil. Connection
	// wrapping occurs before ConnCallback and the SSH handshake. Do not pass
	// connections already wrapped by go-proxyproto when this is configured.
	ProxyProtocol *ProxyProtocolConfig

	// Timeout fields use their Default* value when nil. A configured duration
	// less than or equal to zero disables that timeout.
	HandshakeTimeout *time.Duration // timeout until successful authentication, default 2 minutes
	IdleTimeout      *time.Duration // timeout when no activity, disabled by default
	MaxTimeout       *time.Duration // absolute connection timeout, disabled by default
	// SessionRequestTimeout limits how long an accepted session channel may wait
	// for shell, exec, or subsystem. The default is 30 seconds.
	SessionRequestTimeout *time.Duration

	// Limit fields use their Default* value when nil. A configured value less
	// than or equal to zero disables that limit, meaning no limit is enforced.
	// MaxStartups is disabled when Full is less than or equal to zero.
	MaxStartups                     *MaxStartupsConfig
	MaxSessionsPerConnection        *int
	MaxChannelsPerConnection        *int
	MaxReverseForwardsPerConnection *int
	MaxConnections                  *int // authenticated connections per Serve or HandleConn call
	MaxChannels                     *int // active channels per Serve or HandleConn call
	MaxReverseForwards              *int // active reverse-forward listeners per Serve or HandleConn call

	// ChannelHandlers allow overriding the built-in session handlers or provide
	// extensions to the protocol, such as tcpip forwarding. By default, only the
	// "session" handler is enabled.
	ChannelHandlers map[string]ChannelHandler

	// RequestHandlers allow overriding the server-level request handlers or
	// provide extensions to the protocol, such as tcpip forwarding. By default,
	// no handlers are enabled.
	RequestHandlers map[string]RequestHandler

	// SubsystemHandlers are handlers which are similar to the usual SSH command
	// handlers, but handle named subsystems.
	SubsystemHandlers map[string]SubsystemHandler
	// contains filtered or unexported fields
}

Server configures an SSH server. Its zero value is valid and does not require client authentication.

Configure Server and its referenced values before the first Server.Serve or Server.HandleConn call. It must not be copied or modified after first use, but may be reused concurrently. Each call owns independent runtime state and limits.

func (*Server) AddHostKey

func (srv *Server) AddHostKey(key Signer)

AddHostKey adds a private key as a host key. If an existing host key exists with the same algorithm, it is overwritten. Each server config must have at least one host key. It must only be called before the server's first Serve or HandleConn call.

func (*Server) Handle

func (srv *Server) Handle(fn Handler)

Handle sets the Handler for the server. It must only be called before the server's first Serve or HandleConn call.

func (*Server) HandleConn

func (srv *Server) HandleConn(ctx context.Context, newConn net.Conn) error

HandleConn handles one connection until it finishes or ctx is canceled. It does not invoke ConnectionFailedCallback. Unhandled connection failures are returned directly; ErrorHandler can handle or transform them. On cancellation, the returned error contains the context cause and may contain cleanup errors or ErrGracefulShutdownTimeout. Limits and ErrorHandler concurrency belong to this HandleConn call and are not shared with Serve calls on the same Server.

func (*Server) ListenAndServe

func (srv *Server) ListenAndServe(ctx context.Context) error

ListenAndServe listens on the TCP network address srv.Addr and then calls Serve with ctx to handle incoming connections. If srv.Addr is blank, ":22" is used. Its return value has the same error semantics as Serve.

func (*Server) Serve

func (srv *Server) Serve(ctx context.Context, l net.Listener) error

Serve accepts incoming connections on the Listener l, creating a new connection goroutine for each. The connection goroutines read requests and then calls srv.Handler to handle sessions. Canceling ctx closes the listener and stops this Serve scope; other concurrent Serve calls on srv are unaffected. Serve returns the context cause after draining or forcibly closing the scope's connections. Limits and ErrorHandler concurrency are independent for each Serve call. A forced shutdown after a positive graceful period also returns ErrGracefulShutdownTimeout.

func (*Server) SetOption

func (srv *Server) SetOption(option Option) error

SetOption runs a functional option against the server. It must only be called before the server's first Serve or HandleConn call.

type ServerConfigCallback

type ServerConfigCallback func(ctx Context, conn net.Conn, config *gossh.ServerConfig) error

ServerConfigCallback customizes a fresh per-connection server config. A returned error aborts the SSH handshake. Public key multi-factor authentication must return PartialSuccessError from VerifiedPublicKeyCallback, after key ownership has been proven. Configuring a PasswordCallback, PublicKeyCallback, or KeyboardInteractiveCallback together with the corresponding high-level Server handler rejects that auth method with ErrServerAuthCallbackConflict rather than silently replacing either policy.

type Session

type Session interface {
	gossh.Channel

	// User returns the username used when establishing the SSH connection.
	User() string

	// RemoteAddr returns the net.Addr of the client side of the connection.
	RemoteAddr() net.Addr

	// LocalAddr returns the net.Addr of the server side of the connection.
	LocalAddr() net.Addr

	// Environ returns a copy of strings representing the environment set by the
	// user for this session, in the form "key=value".
	Environ() []string

	// Exit sends an exit status and then closes the session.
	Exit(code int) error

	// Command returns a shell parsed slice of arguments that were provided by the
	// user. Shell parsing splits the command string according to POSIX shell rules,
	// which considers quoting not just whitespace.
	Command() []string

	// RawCommand returns the exact command that was provided by the user.
	RawCommand() string

	// Subsystem returns the subsystem requested by the user.
	Subsystem() string

	// PublicKey returns the PublicKey used to authenticate. If a public key was not
	// used it will return nil.
	PublicKey() PublicKey

	// Context returns the connection's context. The returned context is always
	// non-nil and holds the same data as the Context passed into auth
	// handlers and callbacks.
	//
	// Values are inherited from the context passed to Serve or HandleConn. Its
	// cancellation and deadline are intentionally detached during graceful
	// shutdown. The connection context is canceled when the connection closes,
	// an I/O operation fails, or a hard shutdown begins.
	Context() Context

	// Permissions returns a copy of the Permissions object that was available for
	// setup in the auth handlers via the Context. Its map containers are copied;
	// arbitrary values stored in ExtraData are not recursively cloned.
	Permissions() Permissions

	// Pty returns the client's PTY request, a channel of effective window sizes,
	// and whether a PTY was accepted for this session. The channel initially
	// contains the size from the PTY request. If the receiver falls behind,
	// intermediate changes are coalesced and the latest size is retained. The
	// returned Pty and its TerminalModes map are snapshots and can be modified by
	// the caller.
	Pty() (Pty, <-chan Window, bool)

	// Signals registers a channel to receive signals sent from the client. The
	// channel must handle signal sends promptly. Blocked sends are canceled when
	// the channel is unregistered, the session exits, or its context is canceled.
	// Registering nil will unregister the channel from signal sends. During the
	// time no channel is registered signals are buffered up to a reasonable amount.
	// If there are buffered signals when a channel is registered, they will be
	// sent in order on the channel immediately after registering. The receiver
	// must unregister the channel before closing it.
	Signals(c chan<- Signal)

	// Break registers a channel to receive notifications of break requests sent
	// from the client. The channel must handle break requests, or it will block
	// the request handling loop. Registering nil will unregister the channel.
	// During the time that no channel is registered, breaks are ignored.
	// The receiver must unregister the channel before closing it.
	Break(c chan<- bool)
}

Session provides access to information about an SSH session and methods to read and write to the SSH channel with an embedded Channel interface from crypto/ssh.

When Command() returns an empty slice, the user requested a shell. Otherwise the user is performing an exec with those command arguments.

type SessionExitError added in v0.2.0

type SessionExitError struct {
	Code    int
	Message string
}

SessionExitError requests a public message and exit status from a Handler or SubsystemHandler. It bypasses ErrorHandler unless sending the response fails. Wrapping preserves this behavior; joining another error routes the result through ErrorHandler.

func NewSessionExitError added in v0.2.0

func NewSessionExitError(code int, message string) *SessionExitError

NewSessionExitError creates a controlled session exit.

func (*SessionExitError) Error added in v0.2.0

func (e *SessionExitError) Error() string

type SessionRequestCallback

type SessionRequestCallback func(sess Session, requestType string) (bool, error)

SessionRequestCallback is a callback for allowing or denying SSH sessions. Returning false, nil denies the request. A non-nil error is passed to ErrorHandler and ends the session by default.

type Signal

type Signal string
const (
	SIGABRT Signal = "ABRT"
	SIGALRM Signal = "ALRM"
	SIGFPE  Signal = "FPE"
	SIGHUP  Signal = "HUP"
	SIGILL  Signal = "ILL"
	SIGINT  Signal = "INT"
	SIGKILL Signal = "KILL"
	SIGPIPE Signal = "PIPE"
	SIGQUIT Signal = "QUIT"
	SIGSEGV Signal = "SEGV"
	SIGTERM Signal = "TERM"
	SIGUSR1 Signal = "USR1"
	SIGUSR2 Signal = "USR2"
)

POSIX signals as listed in RFC 4254 Section 6.10.

type Signer

type Signer interface {
	gossh.Signer
}

A Signer can create signatures that verify against a public key.

type SubsystemHandler

type SubsystemHandler func(s Session) error

SubsystemHandler handles a named SSH subsystem. Returned errors have the same ErrorHandler and exit-status semantics as Handler errors.

type Window

type Window struct {
	Width        int
	Height       int
	WidthPixels  int
	HeightPixels int
}

Window represents the informational dimensions of a PTY window. Width and Height are measured in characters; WidthPixels and HeightPixels describe the drawable area. A zero value in an initial PTY request means unspecified. A zero value in a later window change leaves that dimension unchanged.

Jump to

Keyboard shortcuts

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