ssh

package
v1.34.0 Latest Latest
Warning

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

Go to latest
Published: Jul 16, 2026 License: Apache-2.0 Imports: 14 Imported by: 0

Documentation

Overview

Package ssh provides a fleet.Probe implementation that discovers running containers on a remote host via the system `ssh` binary and `docker ps`.

Security model:

  • Disabled by default; caller must pass WithEnabled(true).
  • Only one docker invocation is ever issued: `docker ps --no-trunc --format={{json .}}`.
  • Args are passed as slice elements to exec.CommandContext — no shell-string composition, no `sh -c`, no `bash -c`.
  • All args are validated against the allowlist before exec.
  • Filter.Service is validated BEFORE any remote call.
  • Stderr from ssh is discarded (never surfaces to callers).
  • Stdout is capped at 1 MiB (enforced streaming via cappedWriter).

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrSSHDisabled is returned when the driver has not been enabled.
	// Production wiring (P5) enables the driver only when
	// GOCODE_FLEET_SSH_ENABLE=true in env.
	ErrSSHDisabled = errors.New("fleet/ssh: driver disabled; set GOCODE_FLEET_SSH_ENABLE=true")

	// ErrAllowlistViolation is returned when the computed argv does not match
	// the fixed allowlist. This should never happen in normal usage because the
	// driver constructs the args slice itself; it protects against code-path bugs.
	ErrAllowlistViolation = errors.New("fleet/ssh: command not in allowlist")

	// ErrInvalidFilter is returned when Filter.Service contains characters
	// outside [a-zA-Z0-9._-]. Validated before any remote call.
	ErrInvalidFilter = errors.New("fleet/ssh: invalid filter")

	// ErrInvalidTarget is returned when Target.Scheme != "ssh" or Target.Host == "".
	ErrInvalidTarget = errors.New("fleet/ssh: invalid target")

	// ErrSSHError is returned when the exec.CommandContext call fails or
	// stdout exceeds the 1 MiB cap.
	ErrSSHError = errors.New("fleet/ssh: ssh execution error")

	// ErrParseError is returned when a JSON line cannot be decoded.
	// The driver uses best-effort parsing: a single bad line does not abort
	// the entire result; it is skipped silently.
	ErrParseError = errors.New("fleet/ssh: parse error")
)

Sentinel errors. Callers use errors.Is to distinguish error classes.

Functions

func ParseComposeService

func ParseComposeService(labels string) string

ParseComposeService extracts the com.docker.compose.service label value from a docker ps --format={{json .}} Labels string.

The Labels field is a comma-separated list of "key=value" pairs. Returns "" if the key is absent or Labels is empty. Exported for parse_test.go.

func ParseDockerPSLine

func ParseDockerPSLine(line []byte) (fleet.RuntimeImage, error)

ParseDockerPSLine parses a single JSON line from docker ps CLI output and returns a fleet.RuntimeImage.

On JSON decode error, an error is returned. On CreatedAt parse error, StartedAt is left as zero time.Time{} (no error).

This is exported for use in parse_test.go; the driver calls it internally.

func Validate

func Validate(args []string) error

Validate checks that args (the full argv that would be passed to ssh, including the host and any -p port prefix) match the allowlist.

Allowed shapes:

[host, "docker", "ps", "--no-trunc", "--format={{json .}}"]
["-p", "<port>", host, "docker", "ps", "--no-trunc", "--format={{json .}}"]

All other shapes -> wrapped ErrAllowlistViolation.

Metacharacter check runs first (before shape validation) so injection attempts in the host position are caught.

Types

type Driver

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

Driver is the ssh-shell-out fleet.Probe implementation. It delegates to the user's system `ssh` binary so that ~/.ssh/config (ProxyJump, agent, key passphrases, known_hosts) is the single source of truth — no parallel SSH stack to maintain.

func New

func New(opts ...Option) *Driver

New constructs a Driver with the given options. Default: disabled, binary="ssh", timeout=10s, no shadow-copy.

func (*Driver) List

List queries the remote host for running containers.

Flow:

  1. Gate on enabled.
  2. Validate target scheme and host.
  3. Validate Filter.Service before any remote call.
  4. Build argv (host string, optional -p port, fixed docker invocation).
  5. Run through allowlist.
  6. Call execer.Run.
  7. Check stdout size cap (belt-and-braces: also enforced streaming in realExecer).
  8. Parse JSON-per-line output.
  9. Post-fetch filter by Service.

func (*Driver) Scheme

func (d *Driver) Scheme() string

Scheme returns "ssh".

type Execer

type Execer interface {
	// Run executes `<binary> <host> <args...>`.
	// Returns stdout, stderr, error.
	// ctx deadline must be honoured.
	// Output is returned as-is without trailing newline trim — caller handles it.
	Run(ctx context.Context, binary, host string, args []string) (stdout []byte, stderr []byte, err error)
}

Execer is the testing seam.

Production: realExecer shells out to the system `ssh` binary. Tests: inject a fakeExecer to run in-process.

type Option

type Option func(*Driver)

Option configures a Driver.

func WithBinary

func WithBinary(path string) Option

WithBinary overrides the ssh binary path. Default "ssh" (resolved via PATH).

func WithEnabled

func WithEnabled(b bool) Option

WithEnabled gates the driver. Default false → List() returns ErrSSHDisabled.

func WithExecer

func WithExecer(e Execer) Option

WithExecer overrides the underlying exec mechanism. Tests inject fakes.

func WithSSHHome

func WithSSHHome(homeSrc, homeDst string) Option

WithSSHHome enables shadow-copy of the ssh source directory into a writable path before exec. Required when the source ~/.ssh is bind-mounted from a different uid — the OpenSSH client refuses such files via its built-in strict-mode ownership check.

homeSrc is the read-only source path (e.g. /root/.ssh bind-mounted from the host). homeDst is the writable copy parent (e.g. /tmp/fleet-ssh-home); the .ssh subdirectory is created inside it by ensureWritableSSHHome.

When either argument is empty, no shadow-copy happens and the subprocess inherits the parent's HOME unchanged — backward compatible for any local deploy where ~/.ssh is already root-owned.

func WithTimeout

func WithTimeout(d time.Duration) Option

WithTimeout sets the per-call ssh timeout. Default 10s.

Jump to

Keyboard shortcuts

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