sshpool

package
v1.32.0 Latest Latest
Warning

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

Go to latest
Published: Oct 13, 2026 License: GPL-2.0 Imports: 16 Imported by: 0

Documentation

Overview

Package sshpool dials an instance's SSH endpoint with its stored credentials and reports how the presented host key relates to the pinned one. Nothing here writes to the database: pinning is the caller's decision, taken on the report this package returns.

Index

Constants

View Source
const HardlinkExtension = "hardlink@openssh.com"

HardlinkExtension is the sftp extension a server advertises when it can make hard links.

Variables

View Source
var ErrConnect = errors.New("could not connect to the SSH host")

ErrConnect marks a failure to reach the host or complete the handshake, as opposed to a fault in what qui stored: the caller answers the two differently, and only the first earns an unreadable pin its precedence.

View Source
var ErrNotRemote = errors.New("instance is not in remote filesystem mode")

ErrNotRemote answers a dial for an instance whose row is not in remote filesystem mode. It is not memoised: the row changes without the host doing anything, and the next dial reads it again.

View Source
var ErrPinUnusable = errors.New("host key pin is unusable")

ErrPinUnusable marks the errors Connect raises before it dials, when the stored pin is missing or will not decrypt or parse. The pool dials only rows in remote mode, which carry a pin, so there it means a pin that will not decrypt or parse. That lasts until the pin changes, which is why the pool tells it apart from a host that is merely down.

View Source
var ErrPoolClosed = errors.New("ssh pool is closed")

ErrPoolClosed answers a caller that arrives after shutdown began: nothing may dial once Close has run.

Functions

This section is empty.

Types

type Capabilities

type Capabilities struct {
	SFTP        bool
	Statvfs     bool
	Hardlink    bool
	Limits      bool
	Exec        bool
	GNUUserland bool
}

Capabilities is what the server lets us do, probed read-only.

type Dialer

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

Dialer opens SSH connections: one-shot ones for Test and Confirm, and the long-lived ones Pool keeps through Connect.

func NewDialer

func NewDialer(creds credentialSource) *Dialer

func (*Dialer) Confirm

func (d *Dialer) Confirm(ctx context.Context, inst *models.Instance, hostKey []byte) error

Confirm dials the instance and succeeds only if the host presents hostKey, so the caller can pin the key the user was shown rather than whatever the host offers at pin time. hostKey is in SSH wire format, the form the pin column holds.

func (*Dialer) Connect added in v1.31.0

func (d *Dialer) Connect(ctx context.Context, inst *models.Instance) (*ssh.Client, error)

Connect opens a long-lived connection to a host whose key is already pinned. A background connection has no first contact and nobody to confirm one, so unpinned and unreadable both refuse rather than trust what the host presents.

func (*Dialer) Test

func (d *Dialer) Test(ctx context.Context, inst *models.Instance) (*Report, error)

Test dials the instance and reports the host key, its relation to the pin, and — unless the key mismatched or the pin is unreadable — what the server can do.

type MismatchError

type MismatchError struct {
	Presented ssh.PublicKey
	Pinned    ssh.PublicKey
}

MismatchError reports that the host presented a key other than the expected one. Error prints fingerprints only, so the message is safe to log; the fields carry the keys for callers that need them.

func (*MismatchError) Error

func (e *MismatchError) Error() string

type Pool added in v1.31.0

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

Pool keeps one SSH connection with one SFTP session per instance.

ponytail: one connection per instance, no bounded session pool; that arrives with the exec tier, which needs more than one channel at a time.

func NewPool added in v1.31.0

func NewPool(dialer *Dialer) *Pool

func (*Pool) Close added in v1.31.0

func (p *Pool) Close()

Close drops every connection and refuses every later caller. Called once, at shutdown; a caller mid-dial holds its entry until that dial ends, which the dial timeout bounds.

func (*Pool) Invalidate added in v1.31.0

func (p *Pool) Invalidate(instanceID int)

Invalidate ends the instance's connection and forgets its memo. The credential, pin and instance update routes call it after they write the row, so the next dial reads the new values. A mismatch is re-memoised by the next dial, so a refusal survives a spurious call at the cost of one dial. Safe on a nil pool.

func (*Pool) Remove added in v1.31.0

func (p *Pool) Remove(instanceID int)

Remove is Invalidate for an instance that no longer exists: the entry goes with the connection.

func (*Pool) SFTP added in v1.31.0

func (p *Pool) SFTP(ctx context.Context, inst *models.Instance) (*sftp.Client, error)

SFTP returns the instance's SFTP client, dialing if there is no live one. A failed dial is memoised, so a job touching hundreds of paths against a down host pays for one connect attempt, not hundreds.

type Report

type Report struct {
	Status       Status
	HostKey      ssh.PublicKey
	PinnedKey    ssh.PublicKey // mismatch only
	Capabilities *Capabilities // nil on mismatch and pin_unreadable: no session is opened
}

Report is the outcome of Test. A mismatch is a result, not a failure: the user has to see both keys to decide whether the host was re-keyed or replaced.

type Status

type Status string

Status is how the host key the server presented relates to the stored pin.

const (
	StatusUnpinned Status = "unpinned"
	StatusPinned   Status = "pinned"
	StatusMismatch Status = "mismatch"
	// StatusPinUnreadable: the stored pin does not decrypt. The presented key
	// is reported so the user can replace the pin through the same heavy
	// confirmation a mismatch gets; nothing is trusted and nothing is probed.
	StatusPinUnreadable Status = "pin_unreadable"
)

Jump to

Keyboard shortcuts

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