sshclient

package
v0.6.0 Latest Latest
Warning

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

Go to latest
Published: Aug 16, 2026 License: MIT Imports: 24 Imported by: 0

Documentation

Index

Constants

View Source
const (
	DefaultSSHPort    = "22"
	DefaultSSHUser    = "master"
	DefaultSudoKey    = "master"
	DefaultTimeout    = 30 * time.Second
	SudoPrompt        = "[sudo] password"
	PasswordPromptEnd = ": "
)
View Source
const KeyringServiceName = "sshx"
View Source
const (
	// MaxApplyBytes bounds both the incoming payload and any existing remote
	// file that apply will read for hashing or backup.
	MaxApplyBytes = 10 << 20
)
View Source
const MaxCaptureBytes = 10 << 20 // 10 MiB

MaxCaptureBytes bounds how much stdout/stderr is buffered in capture mode so a runaway command cannot exhaust memory.

Variables

View Source
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")
)
View Source
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")
)

Functions

func ApplyPathBlocked added in v0.6.0

func ApplyPathBlocked(remotePath string) bool

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

func CommandUsesSudo(command string) bool

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

func GetSudoPassword(key string) (string, error)

GetSudoPassword reads sudo password from system keyring (cross-platform support) macOS: Keychain, Linux: Secret Service (gnome-keyring/kwallet), Windows: Credential Manager

func NormalizeApplySHA256 added in v0.6.0

func NormalizeApplySHA256(value string) (string, error)

NormalizeApplySHA256 lowercases a hex digest and verifies it is SHA-256.

func SHA256Hex added in v0.6.0

func SHA256Hex(data []byte) string

SHA256Hex returns the lowercase hex SHA-256 of data.

func ValidateApplyPath added in v0.6.0

func ValidateApplyPath(remotePath string) error

ValidateApplyPath rejects anything that is not a clean POSIX absolute file path.

func ValidateCommand

func ValidateCommand(command string) error

ValidateCommand performs a best-effort safety check against a small set of well-known destructive commands (for example "rm -rf /" or a fork bomb).

It is a guardrail to catch accidental mistakes, NOT a security boundary: the substring/keyword matching is trivially bypassed (casing, quoting, shell variables, alternate paths), 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

type CommandBlockedError struct {
	Command string
	Reason  string
}

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
	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
	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

	// Guarded file apply fields (Mode == "apply").
	ApplyExpectSHA256 string
	ApplyNoBackup     bool
	ApplyBackupDir    string
	ApplyUseSudo      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 NewSSHClient

func NewSSHClient(config *Config) (*SSHClient, error)

NewSSHClient 创建SSH客户端

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) Close

func (c *SSHClient) Close() error

Close closes the SFTP and SSH connections.

func (*SSHClient) ConnectDirect added in v0.0.10

func (c *SSHClient) ConnectDirect() error

ConnectDirect establishes a direct SSH connection.

func (*SSHClient) ExecuteCommandWithOutput

func (c *SSHClient) ExecuteCommandWithOutput() (output string, err error)

ExecuteCommandWithOutput executes a command and returns the output

func (*SSHClient) ExecuteSftp

func (c *SSHClient) ExecuteSftp() (err error)

ExecuteSftp executes SFTP operations

func (*SSHClient) ForceClose

func (c *SSHClient) ForceClose() error

ForceClose forcefully closes the underlying SSH connection.

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

func (c *SSHClient) RemoteHome() (string, error)

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 executes the configured command and returns a structured result.

When capture is true, stdout and stderr are buffered separately (used for --json output). When capture is false they stream live to os.Stdout and os.Stderr on independent channels with no PTY, which keeps output clean and machine-parseable. A PTY is only requested when UsePTY is set and capture is false; note that a PTY merges stderr into stdout.

The returned error is non-nil only for sshx-level failures (validation, session setup, timeout, or an abnormal teardown). A remote command that exits non-zero is NOT an error here: the status is reported in ExecResult.ExitCode with a nil 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. 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 sh consumes the remaining bytes.

func (*SSHClient) TransferTo added in v0.0.13

func (c *SSHClient) TransferTo(dst *SSHClient, srcPath, dstPath string) (err error)

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.

func (*SSHClient) WriteRemoteFileAtomic added in v0.1.0

func (c *SSHClient) WriteRemoteFileAtomic(remotePath string, data []byte) error

WriteRemoteFileAtomic writes state through a unique 0600 file in the same directory, then atomically renames it over the destination.

Jump to

Keyboard shortcuts

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