Skip to content

pacnpal/ssh-wrappers

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

16 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ssh-wrappers — small POSIX shell wrappers around ssh

ssh-wrappers

shellcheck license: MIT shell: POSIX platforms GitHub stars GitHub last commit views

Eleven small POSIX shell wrappers around ssh and ssh-copy-id that fix the most common day-to-day OpenSSH annoyances — Too many authentication failures from agents with too many keys, client_loop: send disconnect: Broken pipe on idle sessions, sudo: a terminal is required to read the password, ssh-copy-id exiting before the password prompt, host-key prompts on ephemeral cloud VMs, reconnect latency on bastion hops, and the rest of the well-known papercuts. Each wrapper is a one-line tweak of options, packaged behind a name short enough that you'll actually use it. Plus a built-in sshh to remind you which is which.

Works on macOS, Linux, and any POSIX shell — zsh, bash, ksh, with a fish snippet for copy-paste.

Homepage: https://pacnpal.github.io/ssh-wrappers/

The wrappers

Wrapper Purpose Mnemonic
sshp Force password authentication (disable pubkey for one connection) password
sshi Use only explicitly configured identities (IdentitiesOnly=yes) identities
ssha Forward your local ssh-agent to the remote host (-A) agent
sshcp Copy a key with ssh-copy-id without pubkey auth (skip the MaxAuthTries burn) copy
sshq Quiet/quick: skip host key prompts, don't pollute known_hosts quick
sshk Keepalive: don't drop on idle (ServerAlive*) keepalive
sshm Multiplex: instant subsequent connections (ControlMaster) multiplex
ssht Force a pseudo-terminal (-t) — for sudo, htop, vim over ssh tty
sshc Compression (-C) — wins on slow links and text-heavy streams compression
sshv Verbose debug (-vvv) — see exactly what ssh is trying verbose
sshh Help — list installed wrappers, what they do, how to use help

Why?

ssh is configurable to a fault. Most of its sharp edges have a fix that's a single -o option=value away — but it's the kind of fix you have to remember exists, type correctly, and know when to apply.

These wrappers turn each fix into a one-character mnemonic:

  • Agent has too many keys, server says Too many authentication failuressshi.
  • Need to type a password but ssh keeps offering keys instead → sshp.
  • Pushing your key to a fresh server but ssh-copy-id fails before the password prompt → sshcp.
  • Connection dropped while you got coffee → sshk.
  • Spinning up the same host's connection 50 times in a deploy script → sshm.
  • Run sudo over ssh, get sudo: a terminal is requiredssht.
  • Cloud VM with a fresh host key, don't want to litter known_hostssshq.
  • Need agent forwarding for git pull on a bastion → ssha.
  • Streaming logs over a slow link → sshc.
  • Connection failing for an unknown reason → sshv.
  • Forgot which wrapper does what → sshh.

Install

One-liner (everything)

curl -fsSL https://pacnpal.github.io/ssh-wrappers/install.sh | sh

Selective install

Pass wrapper names as positional arguments to install only those:

# Just the auth helpers
curl -fsSL https://pacnpal.github.io/ssh-wrappers/install.sh | sh -s -- sshp sshi ssha

# Just connection management
curl -fsSL https://pacnpal.github.io/ssh-wrappers/install.sh | sh -s -- sshk sshm

# Single wrapper
curl -fsSL https://pacnpal.github.io/ssh-wrappers/install.sh | sh -s -- ssht

List the available names with --list:

curl -fsSL https://pacnpal.github.io/ssh-wrappers/install.sh | sh -s -- --list

What the installer does

  • auto-detects your shell from $SHELL (zsh, bash, ksh)
  • writes the selected functions to the matching rc file (~/.zshrc, ~/.bash_profile on macOS bash, ~/.bashrc elsewhere, ~/.kshrc, …) inside a marked block
  • is idempotent — re-running does nothing if the managed block is already there
  • refuses to silently shadow wrappers you've already defined yourself (use --force to install anyway)
  • supports --uninstall to cleanly remove the managed block (and only the managed block)

Override the target file

SSH_WRAPPERS_RC=~/.zprofile sh install.sh

Replace an existing install with a different selection

curl -fsSL https://pacnpal.github.io/ssh-wrappers/install.sh | sh -s -- --force sshp sshi sshk sshm

--force removes the existing managed block and writes the new selection.

Uninstall

curl -fsSL https://pacnpal.github.io/ssh-wrappers/install.sh | sh -s -- --uninstall

Removes only the marked block; anything else in your rc file is untouched.

Fish

Not covered by the installer (different function syntax). Run the installer once to see the snippet you can paste into ~/.config/fish/config.fish, or copy the bodies straight from the per-wrapper docs in docs/.

Manual

Skip the installer entirely — three ways:

  • Copy the function definition you want from the per-wrapper doc in docs/ into your rc file (each docs/sshX.md shows the function at the top).
  • Source a standalone file directly: . wrappers/sshX.sh defines just that one wrapper in your current shell. Files under wrappers/ are regenerated copies of what install.sh would write — useful for cherry-picking into your dotfiles.
  • Open install.sh and read the emit_fn() block — it's the source of truth for every wrapper body.

Usage

Every wrapper accepts the same arguments as ssh:

sshp user@host                                  # password instead of keys
sshi -i ~/.ssh/work_ed25519 user@host           # only this key
ssha bastion                                    # forward agent
sshcp user@new-host                             # push key, skip pubkey auth
sshq ec2-user@10.0.0.42                         # ephemeral cloud VM
sshk prod-bastion 'tail -f /var/log/syslog'     # long idle session
sshm work-bastion                               # then re-run; instant
ssht user@host sudo systemctl restart nginx     # sudo over ssh
sshc remote-builder 'tail -f build.log'         # compression
sshv user@host                                  # debug auth failure
sshh                                            # show all wrappers + install status
sshh sshm                                       # detail for one wrapper

Read the per-wrapper docs for what each option actually does, security tradeoffs, and ~/.ssh/config equivalents:

Requirements

  • POSIX shell (/bin/sh) for the installer
  • OpenSSH client (any version from the past decade — every option used here has been stable for years)
  • Interactive shell of zsh, bash, or ksh for the wrappers (fish has its own snippet — see install)

No build step, no runtime dependencies beyond what comes with your OS.

Troubleshooting

Too many authentication failures — your agent has more keys than the server's MaxAuthTries (default 6). Use sshi to offer only one specific key, or sshp to skip pubkey entirely.

Permission denied (publickey) with sshp — the server has PasswordAuthentication no. No client-side wrapper can fix this; the server must allow password auth.

ssh-copy-id exits with Too many authentication failures before asking for a password — your agent's loaded keys are exhausting MaxAuthTries before the password prompt. Use sshcp instead of plain ssh-copy-id.

sudo: a terminal is required over ssh — pass the command through ssht instead of plain ssh.

client_loop: send disconnect: Broken pipe after idle — use sshk, or set ServerAliveInterval 30 for Host * in ~/.ssh/config.

sshp/sshi/etc. "command not found" after install — open a fresh shell, or source ~/.zshrc. Shell functions only exist in interactive shells that have sourced your rc file. Check type sshp in the new shell.

Function overridden by an alias or another script in PATH — shell functions take precedence over executables in interactive shells, but not in non-interactive scripts. type sshp reveals what's actually being invoked.

Wrapper conflicts with one I already defined — the installer refuses to silently shadow you. Either remove your existing definition, install only the wrappers that don't conflict, or pass --force to overwrite.

Pages URL returns 404 or stale content — the GitHub Pages CDN caches for ~10 min. Use the commit-pinned raw URL or https://cdn.jsdelivr.net/gh/pacnpal/ssh-wrappers/install.sh to bust the cache once.

Development

Lint the installer locally:

shellcheck --shell=sh install.sh scripts/sync-wrappers.sh

CI runs the same on every push to master — see the badge above.

After editing install.sh's emit_fn() block (function bodies live there), regenerate the standalone copies in wrappers/:

sh scripts/sync-wrappers.sh

See docs/CONTRIBUTING.md for the full checklist when adding a new wrapper — every file/count/metadata block that needs to stay in sync.

Project layout:

.
├── README.md                 # this file
├── LICENSE
├── CHANGELOG.md              # release history; edit the [Unreleased] section
├── install.sh                # the installer (source of truth for function bodies)
├── index.html                # GitHub Pages landing page
├── wrappers/                 # standalone copies of each wrapper, one .sh per
│   ├── README.md             # what this dir is and how it's regenerated
│   └── ssh{p,i,a,cp,q,k,m,t,c,v,h}.sh
├── docs/                     # per-wrapper docs + CONTRIBUTING
│   ├── CONTRIBUTING.md       # how to add a wrapper
│   └── ssh{p,i,a,cp,q,k,m,t,c,v,h}.md
├── scripts/
│   └── sync-wrappers.sh      # regenerate wrappers/ from install.sh
├── assets/
│   ├── logo.svg              # the mark
│   ├── logo.png              # rendered 512x512
│   ├── social-card.svg       # 1280x640 OG image
│   └── social-card.png       # rendered
└── .github/workflows/
    └── shellcheck.yml        # CI

Contributing

See docs/CONTRIBUTING.md for what to touch when adding a new wrapper. The short version: edit install.sh's emit_fn() plus _sshh_data, add docs/sshX.md, update the count in README.md / docs/sshh.md / index.html / assets/social-card.svg, re-render the social card PNG, and run sh scripts/sync-wrappers.sh.

License

MIT © pacnpal

About

Eleven tiny POSIX shell wrappers for ssh and ssh-copy-id — fix MaxAuthTries, force password auth, pin one key, agent-forward, keepalive, multiplex (ControlMaster), push keys, force a TTY, compress, debug -vvv. sshh lists what's installed. One-liner curl install for zsh/bash/ksh on macOS and Linux.

Topics

Resources

License

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors