Documentation
¶
Index ¶
- Constants
- Variables
- func ApplyPathBlocked(remotePath string) bool
- func CommandIsDestructive(command string) bool
- func CommandUsesSudo(command string) bool
- func GetSudoPassword(key string) (string, error)
- func InteractiveLoginSupported() bool
- func NormalizeApplySHA256(value string) (string, error)
- func RenderSFTPOutcome(out *SFTPOutcome) error
- func ResolveBind(bind, destHost string) (net.Addr, error)
- func SHA256Hex(data []byte) string
- func StdinIsTerminal() bool
- func ValidateApplyPath(remotePath string) error
- func ValidateCommand(command string) error
- type ApplyOutcome
- type ApplyRequest
- type AuthMethod
- type BoundaryError
- type CacheDialer
- type CommandBlockedError
- type Config
- type ExecResult
- type FileOutcome
- type HopError
- type HopIdentity
- type JumpCache
- type SFTPOutcome
- type SSHClient
- func (c *SSHClient) ApplyRegularFile(req ApplyRequest) (outcome *ApplyOutcome, err error)
- func (c *SSHClient) AuthMethodUsed() AuthMethod
- func (c *SSHClient) Close() error
- func (c *SSHClient) Connect() error
- func (c *SSHClient) ConnectDirect() error
- func (c *SSHClient) ExecuteCommandWithOutput() (string, error)
- func (c *SSHClient) ExecuteSftp() error
- func (c *SSHClient) ExecuteSftpResult() (out *SFTPOutcome, err error)
- func (c *SSHClient) ForceClose() error
- func (c *SSHClient) Hops() []HopIdentity
- func (c *SSHClient) HostKeyFingerprint() string
- func (c *SSHClient) Login() error
- func (c *SSHClient) PeerAddress() string
- func (c *SSHClient) ReadRemoteFile(remotePath string, limit int64, expectedUID string) ([]byte, error)
- func (c *SSHClient) RemoteHome() (string, error)
- func (c *SSHClient) RunCommand(capture bool) (ExecResult, error)
- func (c *SSHClient) RunCommandWithInput(command string, stdin []byte) (ExecResult, error)
- func (c *SSHClient) RunScript(payload []byte, useSudo bool) (ExecResult, error)
- func (c *SSHClient) RunScriptWithShell(payload []byte, shell string, useSudo bool) (ExecResult, error)
- func (c *SSHClient) TransferTo(dst *SSHClient, srcPath, dstPath string) error
- func (c *SSHClient) TransferToResult(dst *SSHClient, srcPath, dstPath string) (out *SFTPOutcome, err error)
- func (c *SSHClient) WriteRemoteFileAtomic(remotePath string, data []byte) error
Constants ¶
const ( DefaultSSHPort = "22" DefaultSSHUser = "master" DefaultSudoKey = "master" DefaultTimeout = 30 * time.Second SudoPrompt = "[sudo] password" PasswordPromptEnd = ": " )
const ErrorKindCancelled = "cancelled" //nolint:misspell // contract spelling
ErrorKindCancelled retains the released machine-readable spelling.
const KeyringServiceName = "sshx"
const ( // MaxApplyBytes bounds both the incoming payload and any existing remote // file that apply will read for hashing or backup. MaxApplyBytes = 10 << 20 )
const MaxCaptureBytes = 10 << 20
const PrivilegedLoginCommand = "sudo -S -p '' sh -c 'stty echo 2>/dev/null; exec sudo -i'"
PrivilegedLoginCommand is the remote program used by login --sudo. The sudo password is written to the session stdin ahead of the human TTY; it is never interpolated into argv. After authentication, echo is restored and a privileged login shell replaces the helper.
Variables ¶
var ( // ErrPrecondition indicates the remote file hash did not match --expect-sha256. ErrPrecondition = errors.New("apply precondition failed") // ErrApplyBlocked indicates the target path is refused by apply policy. ErrApplyBlocked = errors.New("apply target blocked") // ErrApplyVerification indicates publication was attempted but required // readback did not establish that the target matches the payload. ErrApplyVerification = errors.New("apply verification failed") )
var ( ErrCommandTimeout = errors.New("command execution timed out") ErrNoExitStatus = errors.New("remote command terminated without exit status") )
var ( // ErrLoginNotTTY is returned when login is requested without a local TTY. ErrLoginNotTTY = errors.New("login requires an interactive terminal (stdin is not a TTY)") // ErrLoginUnsupported is returned on platforms without a native login session. ErrLoginUnsupported = errors.New("interactive login is not supported on this platform") )
var ErrInvalidBind = errors.New("invalid bind")
ErrInvalidBind reports a bind value that cannot be resolved locally. Callers must treat this as a configuration error and must not dial.
Functions ¶
func ApplyPathBlocked ¶ added in v0.6.0
ApplyPathBlocked reports whether the path is a critical identity file that requires an explicit force + bypass-reason pair.
func CommandIsDestructive ¶ added in v0.15.0
CommandIsDestructive reuses the admission parser without conflating a direct-database-client routing restriction with a destructive operation.
func CommandUsesSudo ¶ added in v0.0.10
CommandUsesSudo reports whether sshx can safely treat the command as a sudo command for password auto-fill. Only a leading sudo command is supported, because that is the only form sudoStdinCommand can rewrite without guessing at shell syntax.
func GetSudoPassword ¶
GetSudoPassword reads a sudo password from the configured secret backend (OS keyring by default, or the explicit local vault).
func InteractiveLoginSupported ¶ added in v0.9.0
func InteractiveLoginSupported() bool
func NormalizeApplySHA256 ¶ added in v0.6.0
NormalizeApplySHA256 lowercases a hex digest and verifies it is SHA-256.
func RenderSFTPOutcome ¶ added in v0.15.0
func RenderSFTPOutcome(out *SFTPOutcome) error
RenderSFTPOutcome renders captured evidence without opening any transport.
func ResolveBind ¶ added in v0.11.0
ResolveBind turns a bind value (literal IP or interface name) into a local TCP address suitable for net.Dialer.LocalAddr. An empty bind is a no-op. destHost may be a hostname, IP, or host:port; hostnames do not trigger DNS.
func StdinIsTerminal ¶ added in v0.9.0
func StdinIsTerminal() bool
StdinIsTerminal reports whether stdin is an interactive terminal.
func ValidateApplyPath ¶ added in v0.6.0
ValidateApplyPath rejects anything that is not a clean POSIX absolute file path.
func ValidateCommand ¶
ValidateCommand performs a best-effort safety check against a small set of well-known destructive operations (for example "rm -rf /" or a fork bomb).
Matching happens on the token in *command position* after shell segmentation, not on the raw command string. That distinction matters: `last reboot -F`, `journalctl | grep -iE 'fail|halt'`, and `iptables-save | grep -F ...` are read-only and must not be blocked just because they contain a dangerous word.
It is a guardrail to catch accidental mistakes, NOT a security boundary: the matching is trivially bypassed (obfuscation, indirection, generated command strings), so it must never be relied upon to sandbox untrusted input.
Types ¶
type ApplyOutcome ¶ added in v0.6.0
type ApplyOutcome struct {
Changed bool
Created bool
BeforeSHA256 string
AfterSHA256 string
BackupPath string
Mode string
PayloadSHA256 string
ExpectSHA256 string
// PreconditionSHA256 is the latest guard observation; BeforeSHA256 remains
// the original snapshot used to verify the backup.
PreconditionSHA256 string
PreconditionStatus string
ChangeState string
// Executed describes target publication, not preparatory backup writes.
// Nil means publication may have occurred without acknowledgement.
Executed *bool
Verified bool
Verification string
BackupVerified bool
UID, GID *uint32
CleanupPending []string
ReplaceMethod string
}
ApplyOutcome is the observed result of one apply.
type ApplyRequest ¶ added in v0.6.0
type ApplyRequest struct {
RemotePath string
Payload []byte
ExpectSHA256 string
Backup bool
BackupDir string
Force bool
UseSudo bool
}
ApplyRequest is one guarded regular-file replacement.
type AuthMethod ¶ added in v0.0.10
type AuthMethod string
AuthMethod indicates which authentication mechanism was used for the SSH connection.
const ( AuthMethodUnknown AuthMethod = "unknown" AuthMethodKey AuthMethod = "key" AuthMethodPassword AuthMethod = "password" AuthMethodPasswordFallback AuthMethod = "password-fallback" )
type BoundaryError ¶ added in v0.15.0
BoundaryError preserves protocol errors while exposing a machine-readable kind without importing the execution package.
func (*BoundaryError) Error ¶ added in v0.15.0
func (e *BoundaryError) Error() string
func (*BoundaryError) ErrorKind ¶ added in v0.15.0
func (e *BoundaryError) ErrorKind() string
func (*BoundaryError) Unwrap ¶ added in v0.15.0
func (e *BoundaryError) Unwrap() error
type CacheDialer ¶ added in v0.15.0
CacheDialer reuses bastion sessions for one Execute() fan-out.
type CommandBlockedError ¶ added in v0.0.10
CommandBlockedError is returned by ValidateCommand when a command matches a known destructive pattern. Its message is unchanged from the previous plain error so existing output and substring checks keep working, while callers can now detect a safety block via errors.As.
func (*CommandBlockedError) Error ¶ added in v0.0.10
func (e *CommandBlockedError) Error() string
type Config ¶
type Config struct {
// Context owns the complete transport lifetime. Nil means Background.
Context context.Context
HostTimeout time.Duration
GlobalTimeout time.Duration
ExpectPlan string
PlanHash string
ExecutionID string
Risk string
MaxFailures int
AuditExecutionID string
// KnownHostsData, when nonnil, is the admitted immutable trust snapshot.
KnownHostsData []byte
ExpectedKeyFingerprint string
// PreparedPayload distinguishes an admitted empty payload from legacy I/O.
PreparedPayload []byte
TransferSource *Config
TransferDestination *Config
Host string
Port string
User string
Password string
SudoPassword string
KeyPath string
UseKeyAuth bool
SudoKey string
// SudoKeySet is true when -pk/--password-key/--sudo-password-key was
// present on the command line, including an explicit empty value.
// Host inventory must persist the key only when this is set; the
// runtime default "master" is an execution fallback, not inventory.
SudoKeySet bool
Command string
Mode string
DialTimeout time.Duration
// Timeout bounds the execution of a single remote command. Zero means no
// command timeout (the dial timeout still applies).
Timeout time.Duration
// JSONOutput emits a single structured JSON result instead of streaming
// human-readable output. It implies clean, separated stdout/stderr capture.
JSONOutput bool
// UsePTY requests a pseudo-terminal for command execution. It is off by
// default because a PTY merges stderr into stdout and injects terminal
// control characters; it is ignored in JSON/capture mode.
UsePTY bool
// DryRun emits a local execution plan without connecting, executing, reading
// keyring secrets, or mutating local/remote state.
DryRun bool
// AuditEnabled controls whether sshx writes a local structured audit event.
AuditEnabled bool
// AuditOutput overrides the directory where audit JSONL files are written.
AuditOutput string
SafetyCheck bool
Force bool
// AcceptUnknownHost controls whether sshx will automatically add
// previously unseen host keys to the user's known_hosts file.
AcceptUnknownHost bool
// AllowInsecureHostKey controls whether sshx may fall back to
// ssh.InsecureIgnoreHostKey (legacy behavior). Disabled by default.
AllowInsecureHostKey bool
// KnownHostsPath allows overriding the path to the known_hosts file.
KnownHostsPath string
SftpAction string
LocalPath string
RemotePath string
// Server-to-server transfer fields (Mode == "transfer").
TransferSrcHost string
TransferSrcPath string
TransferDstHost string
TransferDstPath string
PasswordAction string
PasswordKey string
PasswordValue string
// Host management fields
HostAction string
HostName string
HostDescription string
HostType string
// HostImportNames is a comma-separated list of ssh_config aliases to
// import non-interactively (HostAction == "import"). Empty means
// interactive selection.
HostImportNames string
// SSHConfigPath overrides the OpenSSH client config file read by
// --host-import (default ~/.ssh/config).
SSHConfigPath string
// Plugin lifecycle fields (Mode == "plugin").
PluginAction string
PluginID string
PluginRunner string
PluginPlatform string
PluginPrivilege string
PluginTemplate string
PluginFixture string
PluginReplace bool
// Agent skill lifecycle fields (Mode == "skill").
SkillAction string
SkillDir string
// Inspection fields (Mode == "inspect").
InspectCapability string
InspectCacheMode string
InspectRefresh bool
InspectMaxAge time.Duration
InspectAllowStale bool
InspectUseSudo bool
HostKeyFingerprint string
ArgumentError string
ReportedErrorKind string
ReportedError string
// Run-mode execution contract fields (Mode == "run").
RequestID string
RunTargets []string
RunGroups []string
RunTags map[string]string
RunAllHosts bool
RunAddress string
RunActionKind string
RunIntent string
RunUseSudo bool
RunConcurrency int
FailureMode string
BypassReason string
ScriptFile string
ScriptStdin bool
// ScriptShell overrides the interpreter used for --script-file /
// --script-stdin payloads. Empty means: follow the payload's shebang, or
// fall back to sh.
ScriptShell string
JSONLOutput bool
MaxOutputBytes int
MaxPayloadBytes int
SSHPasswordKey string
// Guarded SQL execution fields (Mode == "sql").
SQLStatement string
// SQLEngine names the database engine: "postgres" (default) or "sqlite".
SQLEngine string
SQLDatabase string
// SQLFile is the --db-file path for --engine=sqlite. Copied into
// SQLDatabase after validation so JSON/audit keep a single identity field.
SQLFile string
// SQLUser is the database role (-U), distinct from the SSH user.
SQLUser string
// SQLHost/SQLPort locate the database as seen from the remote host.
// SQLHost defaults to the local socket, or 127.0.0.1 when a password key
// is used (password auth implies TCP).
SQLHost string
SQLPort string
// SQLPasswordKey names the keyring entry holding the database password.
// The secret is delivered on the remote command's stdin, never in argv.
SQLPasswordKey string
// SQLRowThreshold switches from a row-level CSV snapshot to a full table
// dump when the EXPLAIN row estimate exceeds it (0 = package default).
SQLRowThreshold int64
// SQLAllowFullTable permits UPDATE/DELETE without a top-level WHERE.
SQLAllowFullTable bool
// SQLNoBackup skips pre-change backups; requires Force.
SQLNoBackup bool
// SQLExplainOnly stops after the remote EXPLAIN gate.
SQLExplainOnly bool
// SQLBackupDir overrides the remote backup directory.
SQLBackupDir string
// SQLDockerContainer runs psql inside this container via
// `docker exec -i` for databases deployed with Docker.
SQLDockerContainer string
// SQLCredFrom resolves database credentials on the remote host instead of
// the local keyring: "docker:<container>" or "env-file:<path>".
SQLCredFrom string
// SQLCredCacheTTL keeps remotely resolved credentials reusable in the OS
// keyring for this duration (0 = caching disabled).
SQLCredCacheTTL time.Duration
// SQLCredRefresh forces re-resolution, replacing any cached entry.
SQLCredRefresh bool
// SQLUseSudo runs the remote database client via sudo -S. Use it when the
// SSH user cannot read or write the database file (typical for service-
// owned SQLite). The sudo password is delivered on stdin ahead of SQL.
SQLUseSudo bool
// Audit query/export fields (Mode == "audit").
AuditAction string
AuditSince string
AuditUntil string
AuditFilterHost string
AuditFilterAct string
AuditRunID string
AuditErrorKind string
AuditBypassOnly bool
AuditExportPath string
// Guarded file apply fields (Mode == "apply").
ApplyExpectSHA256 string
ApplyNoBackup bool
ApplyBackupDir string
ApplyUseSudo bool
// Interactive login fields (Mode == "login").
LoginUseSudo bool
LoginLiteralHost bool
// Bind is a local source address: a literal IP or a network interface name.
// BindSet distinguishes "flag not provided" from an explicit empty --bind=
// that must clear a named host's persisted bind.
Bind string
BindSet bool
// Via is the named next hop. ViaSet distinguishes omit from --via=.
Via string
ViaSet bool
// HostAlias is the named-host identity used in hop audit/plan records.
HostAlias string
// JumpChain is the bastion path, outermost first. Each hop has no nested chain.
JumpChain []*Config
}
Config represents SSH configuration properties for connecting to remote hosts.
type ExecResult ¶ added in v0.0.10
type ExecResult struct {
ExitCode int
Stdout string
Stderr string
StdoutTruncated bool
StderrTruncated bool
Started bool
ExitObserved bool
// StartAttempted without Started means the exec request may have reached
// the peer but no positive acknowledgement was observed.
StartAttempted bool
}
ExecResult distinguishes a positive start acknowledgement from observed exit. A disconnect after Started does not prove remote termination or rollback.
type FileOutcome ¶ added in v0.15.0
type FileOutcome struct {
SourcePath string `json:"source_path,omitempty"`
Path string `json:"path"`
Type string `json:"type"`
Size int64 `json:"size"`
Mode string `json:"mode,omitempty"`
UID *uint32 `json:"uid,omitempty"`
GID *uint32 `json:"gid,omitempty"`
BytesTransferred int64 `json:"bytes_transferred"`
SHA256 string `json:"sha256,omitempty"`
SourceSHA256 string `json:"source_sha256,omitempty"`
Started bool `json:"started"`
Published bool `json:"published"`
Publication string `json:"publication,omitempty"`
ChangeState string `json:"change_state"`
Verified bool `json:"verified"`
Verification string `json:"verification"`
VerificationMethod string `json:"verification_method,omitempty"`
ModifiedAt string `json:"modified_at,omitempty"`
StagingPath string `json:"staging_path,omitempty"`
CleanupError string `json:"cleanup_error,omitempty"`
// contains filtered or unexported fields
}
FileOutcome describes one observed effect; digests are content evidence, whereas Size alone is never treated as content verification.
type HopError ¶ added in v0.15.0
HopError marks which hop failed without inventing a new error_kind.
type HopIdentity ¶ added in v0.15.0
type HopIdentity struct {
Role string
Alias string
Address string
Port string
User string
PeerAddress string
HostKeyFingerprint string
AuthMethod string
Closed bool
CloseError string
}
HopIdentity is the public, secret-free record of one SSH hop.
type JumpCache ¶ added in v0.15.0
type JumpCache struct {
// contains filtered or unexported fields
}
JumpCache holds process-scoped bastion sessions for one CLI invocation.
func NewJumpCache ¶ added in v0.15.0
func NewJumpCache() *JumpCache
NewJumpCache returns an empty cache. Close releases every hop.
type SFTPOutcome ¶ added in v0.15.0
type SFTPOutcome struct {
Action string `json:"action"`
SourcePath string `json:"source_path,omitempty"`
DestinationPath string `json:"destination_path,omitempty"`
Started bool `json:"started"`
Phase string `json:"phase"`
Completion string `json:"completion"`
// Executed describes acknowledged destination effects (or listing reads),
// not preparatory staging writes. Nil means an effect is unconfirmed.
Executed *bool `json:"executed"`
ChangeState string `json:"change_state"`
Verified bool `json:"verified"`
Verification string `json:"verification"`
VerificationMethod string `json:"verification_method"`
BytesTransferred int64 `json:"bytes_transferred"`
Partial bool `json:"partial"`
DirectoryAtomic bool `json:"directory_atomic"`
Entries []FileOutcome `json:"entries"`
// contains filtered or unexported fields
}
SFTPOutcome retains completed entries and partial-file evidence on failure. DirectoryAtomic is always false: a tree is a sequence of per-file operations.
type SSHClient ¶
type SSHClient struct {
// contains filtered or unexported fields
}
SSHClient wraps one ssh.Client with execution and SFTP helpers.
func (*SSHClient) ApplyRegularFile ¶ added in v0.6.0
func (c *SSHClient) ApplyRegularFile(req ApplyRequest) (outcome *ApplyOutcome, err error)
ApplyRegularFile replaces one remote regular file. The SFTP path is used unless UseSudo is set, in which case the payload is staged over SFTP and a privileged stdin script performs backup + atomic install.
func (*SSHClient) AuthMethodUsed ¶ added in v0.0.10
func (c *SSHClient) AuthMethodUsed() AuthMethod
AuthMethodUsed returns the authentication method used for the current connection.
func (*SSHClient) Connect ¶
Connect establishes any configured jump chain, then the target session. Jump sessions are owned by this client and closed with it.
func (*SSHClient) ConnectDirect ¶ added in v0.0.10
ConnectDirect uses one budget for TCP, SSH negotiation, and any allowed password fallback. The connection remains owned by Config.Context afterward.
func (*SSHClient) ExecuteCommandWithOutput ¶
ExecuteCommandWithOutput is the legacy combined-output adapter.
func (*SSHClient) ExecuteSftp ¶
ExecuteSftp is the human-output compatibility adapter.
func (*SSHClient) ExecuteSftpResult ¶ added in v0.15.0
func (c *SSHClient) ExecuteSftpResult() (out *SFTPOutcome, err error)
ExecuteSftpResult returns evidence without writing human output.
func (*SSHClient) ForceClose ¶
ForceClose forcefully closes the underlying SSH connection.
func (*SSHClient) Hops ¶ added in v0.15.0
func (c *SSHClient) Hops() []HopIdentity
Hops returns secret-free identities for bastion hops then the target.
func (*SSHClient) HostKeyFingerprint ¶ added in v0.15.0
HostKeyFingerprint identifies the verified key observed on the connection.
func (*SSHClient) Login ¶ added in v0.9.0
Login attaches the local TTY to a remote interactive session. Callers must already have connected the client.
func (*SSHClient) PeerAddress ¶ added in v0.15.0
PeerAddress is the actual connected TCP peer, not the configured DNS name.
func (*SSHClient) ReadRemoteFile ¶ added in v0.1.0
func (c *SSHClient) ReadRemoteFile(remotePath string, limit int64, expectedUID string) ([]byte, error)
ReadRemoteFile reads a restrictive, regular remote file with a hard size bound. Symlinks and group/world-accessible files fail closed.
func (*SSHClient) RemoteHome ¶ added in v0.1.0
RemoteHome resolves the authenticated user's home directory through SFTP.
func (*SSHClient) RunCommand ¶ added in v0.0.10
func (c *SSHClient) RunCommand(capture bool) (ExecResult, error)
RunCommand preserves nonzero remote exits as results, not transport errors.
func (*SSHClient) RunCommandWithInput ¶ added in v0.4.0
func (c *SSHClient) RunCommandWithInput(command string, stdin []byte) (ExecResult, error)
RunCommandWithInput carries caller-prepared data and secrets over stdin.
func (*SSHClient) RunScript ¶ added in v0.1.0
func (c *SSHClient) RunScript(payload []byte, useSudo bool) (ExecResult, error)
RunScript streams a collector without installing it on the remote host.
func (*SSHClient) RunScriptWithShell ¶ added in v0.12.0
func (*SSHClient) TransferTo ¶ added in v0.0.13
TransferTo preserves the error-only legacy API.
func (*SSHClient) TransferToResult ¶ added in v0.15.0
func (c *SSHClient) TransferToResult(dst *SSHClient, srcPath, dstPath string) (out *SFTPOutcome, err error)
TransferToResult relays through memory with per-file atomic publication where supported. Canceling either endpoint tears down both owned transports.