ssh

package
v0.0.0-...-5d47f50 Latest Latest
Warning

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

Go to latest
Published: Jun 22, 2025 License: Apache-2.0 Imports: 28 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var StartWithSize func(cmd *exec.Cmd, w, h, x, y uint16) (*os.File, error)

StartWithSize starts the command with a pty - normally creack/pty, but avoiding the dep in this package.

Functions

func DirectTCPIPHandler

func DirectTCPIPHandler(ctx context.Context, mux *SSHSMux, srv *SSHMesh, newChan ssh.NewChannel)

DirectTCPIPHandler is for 'direct-tcpip' channel type, to support client-originated reverseForwards. It runs on the server side.

See RFC 4254 7.2 RFC 4254 7.2" aria-label="Go to See RFC 4254 7.2">¶

- When client starts with a -L CPORT:host:port, and connects to CPORT - Also when client uses socks for dynamic reverseForwards (-D) - jump (-J)

If the destination port is 22/15022 - existing connections can be used to avoid an extra TCP connection.

Laddr is typically 127.0.0.1 (unless ssh has an open socks, and other machines use it)

func KeysEqual

func KeysEqual(ak, bk ssh.PublicKey) bool

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

Types

type Command

type Command struct {
	Run func(env map[string]string, args []string, in io.Reader, out io.WriteCloser, err io.WriteCloser)
}

type ContextDialer

type ContextDialer interface {
	// Dial with a context based on tls package - 'once successfully
	// connected, any expiration of the context will not affect the
	// connection'.
	DialContext(ctx context.Context, net, addr string) (net.Conn, error)
}

type Exec

type Exec struct {
	// Host of VM where this command will be executed. It will create a
	// SSH or https connection or reuse existing client mux for remote
	// connections, or use a command to enter the VM.
	Host string

	Args []string
	Env  map[string]string
	WD   string

	In  io.Reader
	Out io.WriteCloser // will be a ssh.Channel for ssh

}

Exec can be used with streams - SSH, H2 or other tunnels.

type KV

type KV struct {
	Key, Value string
}

type Pty

type Pty struct {
	Term   string
	Window Window
}

Pty represents a PTY request and configuration.

type RemoteExec

type RemoteExec struct {
	ssh.Channel
	// contains filtered or unexported fields
}

RemoteExec is a "session" channel.

type SSHCMux

type SSHCMux struct {
	// LastSeen    time.Time
	ConnectTime time.Time `json:"-"`

	User string `json:"user,omitempty"`

	// network stream
	// May be an original con with net.Conn with remote/local addr
	NetConn io.ReadWriteCloser `json:"-"`

	// Dependency to the transport (doesn't have to be listenining)
	*SSHMesh `json:"-"`

	// If set, a persistent connection will be maintained and
	// - mux reverseForwards registered for 22, 80, 443
	// - accept streams and trust auth
	Waypoint bool

	Address string `json:"address,omitempty"`

	// TODO: CIDR/Networks
	ReverseForwards map[string]string

	LastConnected time.Time `json:"-"`

	// The SSH Conn, client and internal objects
	SSHClient *ssh.Client `json:"-"`
	SSHConn   ssh.Conn    `json:"-"`

	// Last received remote key (should be a Certificate)
	RemoteKey ssh.PublicKey `json:"-"`
	// contains filtered or unexported fields
}

SSHCMux is a multiplexed client connection to a single destination. That corresponds to a H2 connection - it is possible to have multiple SSHCMux connections to the same destination at the same time.

func (*SSHCMux) ClientSession

func (sshc *SSHCMux) ClientSession()

func (*SSHCMux) Dial

func (sc *SSHCMux) Dial(ctx context.Context, addr string) error

Dial opens one TCP or H2 connection to addr, and starts SSH handshake. It blocks until the SSH handshake is done.

addr can be a https:// address or a hostname.

func (*SSHCMux) DialConn

func (sc *SSHCMux) DialConn(ctx context.Context, tcon net.Conn, addr string) error

func (*SSHCMux) Exec

func (ssht *SSHCMux) Exec(cmd string, env map[string]string) (*RemoteExec, error)

Exec opens a client session channel for a command.

func (*SSHCMux) Init

func (sc *SSHCMux) Init(ctx context.Context)

func (*SSHCMux) ListenTCP

func (c *SSHCMux) ListenTCP(domain string, port uint32) (uint32, error)

ListenTCP requests the remote peer open a listening socket on port.

Regular SSH servers don't multiplex on port. RFC4254: "" means that connections are to be accepted on all protocol

   families supported by the SSH implementation.

o  "0.0.0.0" means to listen on all IPv4 addresses.

o  "::" means to listen on all IPv6 addresses.

o  "localhost" means to listen on all protocol families supported by
   the SSH implementation on loopback addresses only ([RFC3330] and
   [RFC3513]).

o  "127.0.0.1" and "::1" indicate listening on the loopback
   interfaces for IPv4 and IPv6, respectively.

Port 0 is usually supported.

func (*SSHCMux) OpenStream

func (c *SSHCMux) OpenStream(n string, data []byte) (*Stream, error)

OpenStream creates a new stream. This uses the same channel in both directions.

func (*SSHCMux) Proxy

func (sc *SSHCMux) Proxy(ch io.ReadWriteCloser, dstp string, s string)

Proxy an incoming stream to a destination, for remotely accepted steams (-R) TODO: optimize.

func (*SSHCMux) StayConnected

func (sshc *SSHCMux) StayConnected()

StayConnected will maintain an active connection, typically with a jump host.

'addr' is the IP:port to connect to - not the 'canonical' service.

type SSHMesh

type SSHMesh struct {
	// Address to listen on as SSH. Will default to 14022 for regular nodes and
	// 15022 for gateways.
	Address string `json:"addr,omitempty"`

	KeepAlive map[string]*SSHCMux `json:"connect,omitempty"`

	CertClient string `json:"id_ecdsa_cert.pub,omitempty"`
	CertHost   string `json:"cert_host.pub,omitempty"`

	// Primary key - in PEM format (also used for mTLS)
	Key string `json:"tls.key,omitempty"`

	// AuthorizedKeys is the same as authorized_keys file.
	// The keys are used as 'trusted sources' for authentication. Any user key can be used for shell/debug access.
	// The CA keys are allowed to connect - but can get a shell only if 'role=admin' is present in the cert.
	//
	// If empty, the SSH_AUTHORIZED_KEYS env is used, falling back to authorized_keys in $HOME/.ssh (if FromEnv is called)
	AuthorizedKeys string `json:"authorized_keys,omitempty"`

	// User is in email format, as expected by the SSH client certificates.
	//
	User   string `json:"id,omitempty"`
	Domain string `json:"namespace,omitempty"`

	// Can be set to a custom dialer, for example for mesh protocol tunneling.
	Dialer ContextDialer `json:"-"`

	// Can be set to a custom dialer, for example for mesh protocol tunneling.
	H2Dialer ContextDialer `json:"-"`

	Listener net.Listener `json:"-"`

	CertChecker *ssh.CertChecker `json:"-"`

	ConnectErrors atomic.Int64 `json:"-"`

	// Map of public key to user ID.
	// Key is the marshalled public key (from authorized_keys), value is the user ID (comment)
	UsersKeys map[string]string `json:"-"`

	// Signer is the 'workload identity' signer. It is using the main workload
	// private key (workload key), but with a host certificate.
	Signer ssh.Signer `json:"-"`

	// SignerClient is a client workload identity - using the SA cert when
	// a CA is used. Otherwise, same as Signer.
	SignerClient ssh.Signer `json:"-"`
	SignerHost   ssh.Signer `json:"-"`

	// Forward is a function that will proxy a stream to a destination.
	// If missing, it will be dialed.
	// Used on a server for all client forwarding - except locally connected clients.
	Forward func(context.Context, string, io.ReadWriteCloser) `json:"-"`

	// WIP: Internally defined commands.
	InternalCommands map[string]*Command `json:"-"`

	// Root CA keys - will be authorized to connect and create tunnels, not get shell.
	AuthorizedCA []ssh.PublicKey `json:"-"`

	// WIP: Custom channel handlers.
	ChannelHandlers map[string]func(ctx context.Context, sconn *SSHSMux, newChannel ssh.NewChannel) `json:"-"`

	// TokenChecker will verify the password field - as a JWT or other forms.
	TokenChecker TokenChecker `json:"-"`
	// TokenSource will provide passwords or tokens.
	// Normally SSH is cert based - but in some cases all we get is a token. For example K8S, Cloudrun, etc.
	TokenSource TokenSource `json:"-"`

	sync.Mutex `json:"-"`

	Logger *slog.Logger
	// contains filtered or unexported fields
}

SSHMesh is a minimal L4S (ambient) mesh implementation based on SSH, and compatible with standard SSH clients and servers.

func New

func New() *SSHMesh

func (*SSHMesh) AddAuthorizedFile

func (sshMesh *SSHMesh) AddAuthorizedFile(auth []byte)

AddAuthorizedFile will load the ssh "authorized_files" content.

All CAs are added separately, and will also be used for host authorization. The 'comment' field is saved - and will be used as 'user' when public key auth is using that key.

func (*SSHMesh) CertAuthority

func (sshMesh *SSHMesh) CertAuthority() string

func (*SSHMesh) Client

func (sshMesh *SSHMesh) Client(ctx context.Context, dst string) (*SSHCMux, error)

Client returns a SSH client for a destination. It may be disconnected - first call is always disconnected.

func (*SSHMesh) DialHTTP

func (sshMesh *SSHMesh) DialHTTP(ctx context.Context, host string, orig string) (io.ReadWriteCloser, error)

DialHTTP will forward an HTTP connection to a client that opened a -R connection. The host must match the 'canonical' hostname of the client.

func (*SSHMesh) FromEnv

func (sshMesh *SSHMesh) FromEnv()

FromEnv is called to load private key and configs from file or env variables. Not useful for a library - only as a main app.

It will use the same key for clients and servers. Existing $HOME/.ssh is used unless SSHM_CFGDIR is set.

Configs: - id_ecdsa and id_ecdsa_{host,cert}.pub (optional) - authorzied_keys

func (*SSHMesh) HandleAccepted

func (sshMesh *SSHMesh) HandleAccepted(nc net.Conn) error

func (*SSHMesh) HandleServerConn

func (sshMesh *SSHMesh) HandleServerConn(acceptedSSHMux *SSHSMux)

Handles a connection as SSH server, using a net.Conn - which might be tunneled over other transports. SSH handles multiplexing and packets.

func (*SSHMesh) ListenAndStart

func (sshMesh *SSHMesh) ListenAndStart() (net.Listener, error)

func (*SSHMesh) NodeCertAuthority

func (sshMesh *SSHMesh) NodeCertAuthority() string

func (*SSHMesh) Provision

func (sshMesh *SSHMesh) Provision(ctx context.Context) error

func (*SSHMesh) PubString

func (sshMesh *SSHMesh) PubString()

func (*SSHMesh) ServeHTTP

func (sshMesh *SSHMesh) ServeHTTP(writer http.ResponseWriter, request *http.Request)

ServeHTTP is the main function implemented by SSH for HTTP purpose. It will take the H2 request and treat it as a TCP connection. HandleAccepted is handling accepted TCP connections.

func (*SSHMesh) SetCertClient

func (sshMesh *SSHMesh) SetCertClient(key string) error

func (*SSHMesh) SetCertHost

func (sshMesh *SSHMesh) SetCertHost(key string) error

func (*SSHMesh) SetKeyCrypto

func (sshMesh *SSHMesh) SetKeyCrypto(cpk crypto.PrivateKey)

Cert.PrivateKey can be used as a source, if one is loaded.

func (*SSHMesh) SetKeySSH

func (sshMesh *SSHMesh) SetKeySSH(sshk string) error

func (*SSHMesh) Sign

func (sshMesh *SSHMesh) Sign(pub ssh.PublicKey, certType uint32, names []string) ([]byte, *ssh.Certificate, error)

Sign will sign a client or server certificate.

Each host can sign - the resulting cert should be under the host trust.

func (*SSHMesh) SignCert

func (sshMesh *SSHMesh) SignCert(cert *ssh.Certificate) ([]byte, error)

SignCert signs an arbitrary certificate. The cert is expected to include Key, Valid*, CertType, ValidPrincipals.

Optional: Serial, KeyId, Permissions ( a map of claims and the critical like "force-command", "source-address" )

The cert signing will initialize Nonce, Signature.

The result is in 'authorized keys' format.

func (*SSHMesh) Start

func (sshMesh *SSHMesh) Start(ctx context.Context) error

type SSHSMux

type SSHSMux struct {
	// Includes the private key of this node
	SSHServer *SSHMesh `json:"-"`

	// LastSeen    time.Time
	ConnectTime time.Time

	// network stream
	// May be an original con with net.Conn with remote/local addr
	NetConn net.Conn `json:"-"`

	// ServerConn - also has Permission
	ServerConn *ssh.ServerConn `json:"-"`

	RemoteKey ssh.PublicKey `json:"-"`
	//RemoteHostname string
	//RemoteAddr     net.Addr
	FQDN string
}

SSHSMux is a server ssh connection - a long lived, accepted connection.

func (*SSHSMux) OpenStream

func (c *SSHSMux) OpenStream(n string, data []byte) (*Stream, error)

OpenStream creates a new stream. This uses the same channel in both directions.

type SSHSession

type SSHSession struct {
	Channel ssh.Channel

	// Window keeps getting update during execution.
	PTY *Pty

	Env []*KV

	Cmd *exec.Cmd
	// contains filtered or unexported fields
}

SSHSession is a net connection (H2 stream equivalent), but with extra packets for executing commands including SFTP and an extra stderr stream.

This is closer to a WebSocket in binary mode: packets, can multiplex sub-channels and commands - flow control is like H2, both on stream and mux.

The actual execution is in the exec.go -

func (*SSHSession) Close

func (sess *SSHSession) Close() error

func (*SSHSession) Handle

func (sess *SSHSession) Handle(ctx context.Context, newChannel ssh.NewChannel)

WIP: The SSH 'gateway' will not have a real shell / sftp session (except for debug). Instead, the session is used as a general purpose communication.

Notes on sessions: -N - do not start a session at all -T - do not get a pty If ssh is piped, a terminal will not be allocated - but no flushing seems to happen.

Inside a session - ~. close, ~B break, ~C CLI, ~# connections, ~? help

func (*SSHSession) SFTPHandler

func (s *SSHSession) SFTPHandler(ctx context.Context, req *ssh.Request)

type Stream

type Stream struct {
	ssh.Channel
	// contains filtered or unexported fields
}

Stream is a client or server stream - 'Channel' in SSH terms.

Also implements gossh.Channel - add SendRequest and Stderr, as well as CloseWrite

type TokenChecker

type TokenChecker interface {
	Check(token string) (claims map[string]string, e error)
}

type TokenSource

type TokenSource interface {
	// GetToken for a given audience.
	GetToken(context.Context, string) (string, error)
}

TokenSource is a common interface for anything returning Bearer or other kind of tokens.

type Window

type Window struct {
	Width  int
	Height int
}

Window represents the size of a PTY window.

Jump to

Keyboard shortcuts

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