SHA256SUMS 里是裸文件名,在仓库根目录执行 sha256sum -c dist/SHA256SUMS 会去找 ./golink_darwin_amd64 从而全部 FAILED。改成 ( cd dist && sha256sum -c SHA256SUMS )。
sshtools
Non-interactive SSH command-line tools for scripts: golink replaces PuTTY's
plink, goscp replaces the upload half of pscp.
Plain Go, CGO_ENABLED=0, one static binary per platform. The SSH stack and all
cryptography (RSA, ECDSA, Ed25519, AES-GCM, ChaCha20-Poly1305, SHA-2) are Go's
own implementation compiled into the binary -- nothing links against the system
OpenSSL and nothing has to be installed alongside it.
English | 简体中文
Install
Download the binary for your platform from the
latest release, then chmod +x it.
| Platform | golink | goscp |
|---|---|---|
| Windows x86-64 | golink_windows_amd64.exe |
goscp_windows_amd64.exe |
| Windows ARM64 | golink_windows_arm64.exe |
goscp_windows_arm64.exe |
| Linux x86-64 | golink_linux_amd64 |
goscp_linux_amd64 |
| Linux ARM64 | golink_linux_arm64 |
goscp_linux_arm64 |
| macOS Intel | golink_darwin_amd64 |
goscp_darwin_amd64 |
| macOS Apple Silicon | golink_darwin_arm64 |
goscp_darwin_arm64 |
SHA256SUMS is attached to every release.
On macOS a downloaded binary carries the quarantine attribute; clear it once with
xattr -d com.apple.quarantine ./golink.
The contract both tools share
- they never prompt: not for a password, not for a host key, not for anything;
- they never allocate a terminal or open an interactive shell;
- stdout carries only the remote program's stdout, diagnostics go to stderr;
- exit codes are meaningful (see each tool below);
- every credential is supplied up front, so they are safe inside cron jobs and CI.
golink
Connect, run one command, and return that command's exit status.
golink user@host -p "password" -port 22 -command "command"
Go's flag package stops parsing at the first positional argument, so golink
reorders argv before parsing: the target may come first, last, or in between,
and flags may be written in any order.
Examples
# the common case
golink root@10.0.0.5 -p 'secret' -port 22 -command "uptime && df -h"
# password from the environment, so it stays out of ps and shell history
GOLINK_PASSWORD='secret' golink deploy@web1 -command "systemctl restart nginx"
# the remote exit status is passed through, so this works
if golink ci@build1 -p "$PASS" -command "make test"; then echo passed; else echo "failed: $?"; fi
# feed local stdin to the remote command
tar czf - ./src | golink root@backup -p "$PASS" -command "cat > /srv/src.tgz"
# pipe remote output into a local filter
golink db@10.0.0.9 -p "$PASS" -command "cat /var/log/app.log" | grep ERROR
# key auth (OpenSSH private key, no passphrase)
golink ops@bastion -i ~/.ssh/id_ed25519 -command hostname
# pin the host key fingerprint
golink ops@10.0.0.7 -p "$PASS" -hostkey "$(ssh-keyscan -p 22 10.0.0.7 2>/dev/null | ssh-keygen -lf - | awk '{print $2}')" -command true
# the command may also be trailing positional arguments, plink style
golink ops@web1 -p "$PASS" systemctl status nginx
Flags
| Flag | Default | Meaning |
|---|---|---|
-p / -password / -pw |
empty | SSH password; GOLINK_PASSWORD works too |
-port / -P |
22 | SSH port; host:2222 in the target also works |
-l |
empty | login user, alternative to user@host |
-command |
empty | command to run; trailing positional arguments are appended |
-i |
empty | OpenSSH private key file (no passphrase, see below) |
-knownhosts |
empty | known_hosts file; unknown or changed keys fail, never prompt |
-hostkey |
empty | required host key fingerprint, SHA256:... or MD5 |
-timeout |
15s | covers connect, handshake and authentication only, never the command |
-v |
off | connection diagnostics on stderr |
-version / -h |
version / help |
Targets accept user@host, user@host:2222 and [::1]:2222.
Exit status
| Code | Meaning |
|---|---|
| 0-255 | the remote command's status, passed through (a killed command reports 128+signal, so SIGTERM gives 143) |
| 2 | usage error: bad flag, missing user, missing command, no credentials |
| 255 | connection, host key or authentication failure (same convention as OpenSSH) |
goscp
Copy local files and directories to a remote host with the SCP protocol. The
remote side runs scp -t, so no SFTP subsystem is required.
goscp [flags] [user@]host[:port] -files "dir or file" [-files ...] [-dest /remote/dir]
One flag handles both files and directories: -files inspects the path and
either sends a single entry or walks the tree. There is no -r switch and no
separate mode.
Examples
# one file, into the remote user's home directory
goscp user@host -p "$PASS" -port 22 -files "build/output.bin"
# a whole directory, into a specific place (created when missing)
goscp user@host -p "$PASS" -files "build" -dest /home/user/app
# several files, either repeated or trailing
goscp user@host -p "$PASS" -files a.txt -files b.txt -dest /srv/in
goscp user@host -p "$PASS" -files a.txt b.txt -dest /srv/in
# password from the environment
GOSCP_PASSWORD='secret' goscp user@host -files ./dist/
Flags
| Flag | Default | Meaning |
|---|---|---|
-files |
empty | local file or directory, detected automatically; repeatable, trailing arguments count too |
-dest |
remote home | remote target directory, created with mkdir -p when missing |
-p / -password / -pw |
empty | SSH password; GOSCP_PASSWORD or GOLINK_PASSWORD |
-port / -P |
22 | SSH port |
-l |
empty | login user |
-i |
empty | OpenSSH private key |
-knownhosts / -hostkey |
empty | host key policy |
-timeout |
15s | connect and authentication, not the transfer |
-v |
off | list every entry as it is sent |
-q |
off | no summary line on stderr |
Exit status
| Code | Meaning |
|---|---|
| 0 | everything was copied |
| 1 | transfer failed: missing local path, remote scp error, permission denied |
| 2 | usage error |
| 255 | connection, host key or authentication failure |
Behaviour
- directories keep their name and relative structure:
-files build -dest /srvgives/srv/build/... - permissions: regular files 0644 (0755 when the local file is executable), directories 0755
- symlinks named on the command line are followed; symlinks met inside a tree are skipped with a warning
- file names may contain spaces; newlines are rejected
- the transfer is streamed and verified byte for byte in the test suite
Host key policy
Host keys are not verified by default. A script cannot answer "do you trust
this host", so the tools behave like plink -batch: they accept the key and
continue. The trade-off is a theoretical man-in-the-middle.
Pin it when it matters:
-hostkey SHA256:...-- fail unless the fingerprint matches;-knownhosts ~/.ssh/known_hosts-- unknown or changed keys fail, never prompt.
Security notes
A password on the command line is visible in ps and the shell history, exactly
like plink -pw. Prefer GOLINK_PASSWORD / GOSCP_PASSWORD or -i.
Passphrase-protected keys are rejected with an explanatory error: these tools
never prompt, so decrypt the key once with ssh-keygen -p instead.
Building
./build.sh # every program for every platform, into dist/
./build.sh local # only the host platform
VERSION=1.2.3 ./build.sh
The only dependency is golang.org/x/crypto, so an offline build works as long
as the module cache is populated. build.sh points GOCACHE at the in-repo
.gocache/ directory, because some sandboxes mount ~/.cache/go-build read-only.
Pushing a v* tag makes GitHub Actions run both test suites, cross-compile
everything and attach the binaries to the GitHub release. Gitea also discovers
.github/workflows, so the jobs carry an explicit
if: github.server_url == 'https://github.com' guard and stay idle there.
Testing
./smoketest/run.sh # golink, 38 checks
./smoketest/run-goscp.sh # goscp, 19 checks
Both suites are fully local: they build a miniature sshd (smoketest/sshd,
supporting password, keyboard-interactive and public-key auth) and drive it over
loopback. Every case runs with stdin closed and a watchdog, so any attempt to
wait for interactive input shows up as a failure rather than a hang.
Layout
cmd/golink/ golink entry point: flags, exit status mapping
cmd/goscp/ goscp entry point
internal/cli/ argv reordering so flags work anywhere
internal/sshx/ dial, authentication, host key policy
internal/scp/ SCP wire protocol, client side
smoketest/sshd/ miniature SSH server used by the tests
smoketest/run.sh golink end-to-end suite
smoketest/run-goscp.sh
build.sh cross-compilation for six targets
Not implemented
- download direction for goscp (remote to local)
- resume, parallel transfers, progress
- an SFTP backend (would add a dependency; SCP keeps the binary self-contained)
- jump hosts (
-J), port forwarding, interactive shells / pty