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 ¶
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 ¶
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 ¶
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 ¶
New constructs a Driver with the given options. Default: disabled, binary="ssh", timeout=10s, no shadow-copy.
func (*Driver) List ¶
func (d *Driver) List(ctx context.Context, t fleet.Target, f fleet.Filter) ([]fleet.RuntimeImage, error)
List queries the remote host for running containers.
Flow:
- Gate on enabled.
- Validate target scheme and host.
- Validate Filter.Service before any remote call.
- Build argv (host string, optional -p port, fixed docker invocation).
- Run through allowlist.
- Call execer.Run.
- Check stdout size cap (belt-and-braces: also enforced streaming in realExecer).
- Parse JSON-per-line output.
- Post-fetch filter by Service.
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 ¶
WithBinary overrides the ssh binary path. Default "ssh" (resolved via PATH).
func WithEnabled ¶
WithEnabled gates the driver. Default false → List() returns ErrSSHDisabled.
func WithExecer ¶
WithExecer overrides the underlying exec mechanism. Tests inject fakes.
func WithSSHHome ¶
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 ¶
WithTimeout sets the per-call ssh timeout. Default 10s.