Documentation
¶
Index ¶
- Constants
- Variables
- func ApplyPathBlocked(remotePath string) bool
- func CommandUsesSudo(command string) bool
- func GetSudoPassword(key string) (string, error)
- func InteractiveLoginSupported() bool
- func NormalizeApplySHA256(value string) (string, 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 CommandBlockedError
- type Config
- type ExecResult
- type SSHClient
- func (c *SSHClient) ApplyRegularFile(req ApplyRequest) (*ApplyOutcome, error)
- func (c *SSHClient) AuthMethodUsed() AuthMethod
- func (c *SSHClient) Close() error
- func (c *SSHClient) ConnectDirect() error
- func (c *SSHClient) ExecuteCommandWithOutput() (output string, err error)
- func (c *SSHClient) ExecuteSftp() (err error)
- func (c *SSHClient) ForceClose() error
- func (c *SSHClient) Login() error
- 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) (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 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 // 10 MiB
MaxCaptureBytes bounds how much stdout/stderr is buffered in capture mode so a runaway command cannot exhaust memory.
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") )
var ( // ErrCommandTimeout indicates the command exceeded the configured timeout. ErrCommandTimeout = errors.New("command execution timed out") // ErrNoExitStatus indicates the remote closed the session without reporting // an exit status (for example, the command was terminated by a signal). 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 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 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
}
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 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 {
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
}
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
}
ExecResult captures the outcome of running a remote command.
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) (*ApplyOutcome, 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) ConnectDirect ¶ added in v0.0.10
ConnectDirect establishes a direct SSH connection.
func (*SSHClient) ExecuteCommandWithOutput ¶
ExecuteCommandWithOutput executes a command and returns the output
func (*SSHClient) ExecuteSftp ¶
func (*SSHClient) ForceClose ¶
ForceClose forcefully closes the underlying SSH 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) 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)
func (*SSHClient) RunCommandWithInput ¶ added in v0.4.0
func (c *SSHClient) RunCommandWithInput(command string, stdin []byte) (ExecResult, error)
RunCommandWithInput runs a caller-assembled command on a fresh SSH session with the given bytes streamed to its stdin, capturing separated output. It is used by the sql mode, whose commands are built from validated templates and may carry a leading secret line on stdin (never in argv).
func (*SSHClient) RunScript ¶ added in v0.1.0
func (c *SSHClient) RunScript(payload []byte, useSudo bool) (ExecResult, error)
RunScript streams a trusted local collector to a fresh SSH session using the POSIX shell. The payload is never installed on the target.
func (*SSHClient) RunScriptWithShell ¶ added in v0.12.0
func (c *SSHClient) RunScriptWithShell(payload []byte, shell string, useSudo bool) (ExecResult, error)
RunScriptWithShell streams a trusted local script to a fresh SSH session and executes it with the named POSIX-shell-family interpreter. The payload is never installed on the target. When useSudo is true, the password and script share stdin in that order: sudo consumes one line and the shell consumes the remaining bytes.
func (*SSHClient) TransferTo ¶ added in v0.0.13
TransferTo streams files from this client's remote host directly to the destination client's remote host over SFTP, relaying the data through the local machine without writing it to local disk. It supports single files and recursive directory transfers.