transport

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Sep 1, 2026 License: BSD-3-Clause Imports: 23 Imported by: 0

README

transport

Pure-Go (CGO=0) remote command execution — local, SSH, and WinRM connections, plus sudo/su/doas privilege escalation — shared across orchestration tools (consumed by go-ansible and go-puppet-bolt).

CI Go Reference License

Documentation

Overview

Package transport implements remote command execution — the low-level primitives an orchestration tool's task/module logic runs commands and moves files through, whether the target is the control node itself (Local), a Unix-like host over SSH (SSH), or a Windows host over WinRM (WinRM) — plus privilege escalation (Become: sudo/su/doas) as a decorator over any of them.

It is deliberately scoped to these primitives and nothing tool-specific: no playbooks, no catalogs, no task manifests. Ansible's modules and Puppet Bolt's tasks both reduce, in the end, to "run this command" and "put this file there" — this package is that reduction, shared instead of reimplemented per tool.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type BecomeConfig

type BecomeConfig struct {
	Method   BecomeMethod // default BecomeSudo
	User     string       // default "root"
	Password string       // optional; empty assumes passwordless (NOPASSWD sudoers, or doas persist)
}

BecomeConfig configures privilege escalation, Ansible's `become:`.

type BecomeMethod

type BecomeMethod string

BecomeMethod names a privilege-escalation program.

const (
	BecomeSudo BecomeMethod = "sudo"
	BecomeSu   BecomeMethod = "su"
	BecomeDoas BecomeMethod = "doas"
)

type Connection

type Connection interface {
	// Exec runs cmd through the target's shell (one command line, not an
	// argv — matching how both Ansible and Bolt build task invocations)
	// and returns its captured output and exit code. A non-zero exit is
	// not itself an error; err is only for a failure to run the command
	// at all. stdin, if non-nil, is streamed to the command's standard
	// input — how a become/run-as password reaches sudo/su without ever
	// appearing in argv or an environment variable.
	Exec(ctx context.Context, cmd string, stdin io.Reader) (Result, error)

	// Put copies the local file at localPath to remotePath on the
	// target.
	Put(ctx context.Context, localPath, remotePath string, opts PutOptions) error

	// Fetch copies the file at remotePath on the target to the local
	// file at localPath.
	Fetch(ctx context.Context, remotePath, localPath string) error

	// Remove deletes remotePath on the target. Removing a path that
	// does not exist is not an error.
	Remove(ctx context.Context, remotePath string) error

	// TempPath returns a fresh path under the target's temp directory
	// ending in base, using the target's own path syntax (forward
	// slashes for Local/SSH, backslashes under WinRM).
	TempPath(base string) string

	// Close releases any resources (an SSH/WinRM connection, an open
	// shell).
	Close() error
}

Connection is how a caller reaches its target: run a shell command, move files in either direction, and clean up after itself. Every task/module operation is expressed in terms of these primitives so the same higher-level logic runs unchanged whether the connection is local, SSH, or WinRM.

func Become

func Become(conn Connection, cfg BecomeConfig) Connection

Become wraps conn so every Exec runs as BecomeConfig.User via the configured escalation method. Put/Fetch/Close pass through unchanged (Ansible's own become plumbing only affects command execution, not file transfer, since the transferred file is written by the connection's own user and then chmod/chowned by an escalated task).

type Local

type Local struct {
	// Shell is the interpreter commands are run through. Defaults to
	// /bin/sh.
	Shell string
	// TempDir is where TempPath builds paths under. Defaults to
	// os.TempDir().
	TempDir string
}

Local is the "local" connection: the target IS the control node. Running the command through a real shell (rather than an argv exec.Command) is not a shortcut — it's what a local task/module runner needs, since a command's args are shell syntax (pipes, redirects, globs) that only a shell interprets.

func NewLocal

func NewLocal() *Local

NewLocal returns a Local connection using /bin/sh.

func (*Local) Close

func (l *Local) Close() error

func (*Local) Exec

func (l *Local) Exec(ctx context.Context, cmd string, stdin io.Reader) (Result, error)

func (*Local) Fetch

func (l *Local) Fetch(ctx context.Context, remotePath, localPath string) error

func (*Local) Put

func (l *Local) Put(ctx context.Context, localPath, remotePath string, opts PutOptions) error

func (*Local) Remove

func (l *Local) Remove(ctx context.Context, remotePath string) error

func (*Local) TempPath

func (l *Local) TempPath(base string) string

type PutOptions

type PutOptions struct {
	// Executable chmods the remote file +x after writing (POSIX targets
	// only; WinRM implementations ignore it — Windows has no exec bit).
	Executable bool
	// MkdirParents creates the destination's parent directory first.
	MkdirParents bool
}

PutOptions configures a file upload.

type Result

type Result struct {
	Stdout string
	Stderr string
	RC     int
}

Result is the outcome of running one command: its captured output and exit code.

type SSH

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

SSH is a live SSH connection to one target host. Every Exec opens its own session, matching how SSH sessions work (one command per session).

func DialSSH

func DialSSH(ctx context.Context, cfg SSHConfig) (*SSH, error)

DialSSH connects and authenticates to cfg.Host, trying (in order) explicit password, private key, and ssh-agent — whichever of those cfg populates.

func (*SSH) Close

func (s *SSH) Close() error

func (*SSH) Exec

func (s *SSH) Exec(ctx context.Context, cmd string, stdin io.Reader) (Result, error)

func (*SSH) Fetch

func (s *SSH) Fetch(ctx context.Context, remotePath, localPath string) error

func (*SSH) Put

func (s *SSH) Put(ctx context.Context, localPath, remotePath string, opts PutOptions) error

Put streams localPath's contents to remotePath over a plain `cat >` session. This avoids an SFTP dependency; it needs only /bin/sh, cat, mkdir and chmod on the target — universally present.

func (*SSH) Remove

func (s *SSH) Remove(ctx context.Context, remotePath string) error

func (*SSH) TempPath

func (s *SSH) TempPath(base string) string

type SSHConfig

type SSHConfig struct {
	Host string
	Port int // default 22
	User string

	Password             string // optional
	PrivateKeyFile       string // optional, e.g. ~/.ssh/id_ed25519
	PrivateKeyBytes      []byte // optional, an already-loaded key (e.g. from inventory)
	PrivateKeyPassphrase string
	UseAgent             bool // authenticate via SSH_AUTH_SOCK

	// HostKeyCheck: when true (the default), the remote host key is
	// verified against KnownHostsFile (default ~/.ssh/known_hosts) and
	// the connection is refused on any mismatch or unknown host.
	HostKeyCheck   bool
	KnownHostsFile string

	// TempDir is the remote directory TempPath builds paths under.
	// Defaults to /tmp.
	TempDir string

	Timeout time.Duration // default 30s

	// Dialer establishes the transport-level TCP connection. Defaults
	// to net.Dialer honoring Timeout. Tests inject a dialer that reaches
	// an in-process server.
	Dialer func(ctx context.Context, network, addr string) (net.Conn, error)
}

SSHConfig configures an SSH connection to a Unix-like remote host.

type WinRM

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

WinRM is a live WinRM connection to one target host: one WS-Man shell, opened at Dial time and reused across every Exec/Put/Fetch/Remove until Close.

func DialWinRM

func DialWinRM(ctx context.Context, cfg WinRMConfig) (*WinRM, error)

DialWinRM opens a WS-Man shell on cfg.Host.

func (*WinRM) Close

func (w *WinRM) Close() error

func (*WinRM) Exec

func (w *WinRM) Exec(ctx context.Context, cmd string, stdin io.Reader) (Result, error)

Exec runs cmd through cmd.exe /c on the target's open shell.

func (*WinRM) Fetch

func (w *WinRM) Fetch(ctx context.Context, remotePath, localPath string) error

Fetch reads remotePath by streaming its base64 encoding to stdout via PowerShell.

func (*WinRM) Put

func (w *WinRM) Put(ctx context.Context, localPath, remotePath string, opts PutOptions) error

Put writes content to dst by streaming base64 to a PowerShell decoder (WinRM has no plain file-transfer primitive). opts.Executable is ignored: Windows has no exec bit.

func (*WinRM) Remove

func (w *WinRM) Remove(ctx context.Context, remotePath string) error

func (*WinRM) TempPath

func (w *WinRM) TempPath(base string) string

type WinRMConfig

type WinRMConfig struct {
	Host string
	Port int // default 5986 (SSL) or 5985
	User string

	Password string

	SSL       bool   // default true
	SSLVerify bool   // default true
	CACert    string // path to a CA certificate PEM (custom trust root)

	// Transport selects the auth scheme: "negotiate" (default), "basic",
	// or "ssl" (TLS client certificate — set ClientCert/ClientKey).
	Transport  string
	ClientCert string
	ClientKey  string

	ConnectTimeout time.Duration // default 60s
	TempDir        string        // default `C:\Windows\Temp`
	Path           string        // WS-Man endpoint path, default "/wsman"

	// NewDoer builds the HTTP client used to reach the target. Defaults
	// to buildWinRMClient(cfg). Tests inject a doer that reaches an
	// in-process WS-Man server.
	NewDoer func(WinRMConfig) (httpDoer, error)
}

WinRMConfig configures a WinRM connection to a Windows remote host. It speaks WS-Management (SOAP 1.2 with WS-Addressing over HTTP/HTTPS) driving the MS-WSMV shell protocol: Create shell -> Command -> Send (stdin) -> Receive (stdout/stderr/CommandState/ExitCode) -> Signal (terminate) -> Delete shell.

Authentication is selected by Transport: "basic" sends HTTP Basic credentials, "negotiate" (the default) performs NTLM via the pure-Go github.com/Azure/go-ntlmssp round-tripper, and "ssl" uses TLS client-certificate authentication. Kerberos is not implemented.

Jump to

Keyboard shortcuts

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