A minimal bash script to manage and connect to SSH servers using fuzzy search via fzf.
- Fuzzy search across all your servers by name, host, user, or port
- Direct connect by server name:
connect SERVERNAME - Simple plain-text config file
- Auto-generates an example config on first run
fzf— install withbrew install fzf
sudo ln -sf /path/to/ssh-connect/connect /usr/local/bin/connectServers are defined in ~/.ssh/connect.conf:
# Format: name host [user] [port] [key]
# Fields are whitespace-separated. user, port and key are optional.
@key ~/.ssh/id_ed25519
work-server 192.168.1.10
dev-box 10.0.0.5 deploy 2222
staging staging.example.com ubuntu
backup 192.168.1.10 deploy 22 ~/.ssh/connect_backup
homelab homelab.example.com deploy 22 homelab
If the config file doesn't exist, connect will create an example one on first run.
- No
keycolumn:sshuses normal authentication — any running SSH agent (via$SSH_AUTH_SOCK, including the Bitwarden/Vaultwarden SSH agent) offering all its keys, and then a password prompt. keyis a path (contains/, e.g.~/.ssh/id_ed25519):connectadds-i <key> -o IdentitiesOnly=yes, forcing that on-disk identity file (~is expanded). A missing file warns and falls back to default auth.keyis a bare name (e.g.homelab): the name/comment of a key held by your SSH agent, as listed byssh-add -L.connectpins that single agent key — it writes only the public key to a temp file and uses-o IdentitiesOnly=yes, so the private key stays in the agent (e.g. Bitwarden) and only one identity is offered. Use this to fixToo many authentication failures.@key <path>directive: sets a config-wide fallback identity file used by any entry that does not name its ownkey.
Fuzzy search (interactive):
connectDirect connect by name:
connect work-serverName matching is case-insensitive.
Keys stored in Vaultwarden are served transparently through the Bitwarden
desktop SSH agent — connect needs no special configuration, just leave the
key column blank for those servers.
One-time setup:
- Vaultwarden server: enable the experimental flags
EXPERIMENTAL_CLIENT_FEATURE_FLAGS=ssh-key-vault-item,ssh-agent. - Bitwarden desktop client: enable the SSH agent in Settings.
- Point
sshat the agent — add to~/.zshrc:# .dmg build: export SSH_AUTH_SOCK="$HOME/.bitwarden-ssh-agent.sock" # App Store build: # export SSH_AUTH_SOCK="$HOME/Library/Containers/com.bitwarden.desktop/Data/.bitwarden-ssh-agent.sock"
connect calls plain ssh, which uses whatever $SSH_AUTH_SOCK points at — so the agent setup above is all it needs. Leaving the key column blank works until the agent holds many keys (see below), at which point you name the key in the key column.
Received disconnect from HOST port 22:2: Too many authentication failures
This is not a server fault. With no key column, ssh offers the agent's
keys one by one until one matches. Each counts as an attempt, and the server
cuts the connection once it exceeds MaxAuthTries (default 6). When the
Bitwarden/Vaultwarden agent holds more than ~6 keys, hosts whose key happens to
sit late in the list never get reached. ssh-add -l shows how many keys are
loaded.
Fix: put the key's name (its comment in ssh-add -L, i.e. the SSH-key
item name in Vaultwarden) in the key column. connect then pins that single
identity with -o IdentitiesOnly=yes, so ssh offers exactly one key. The
private key still lives in Bitwarden — only the public key is touched, written
to a temp file that is deleted when the session ends, so nothing for a device
compliance tool (e.g. Kolide) to flag.
# before — relies on the agent offering the right key in time
homelab homelab.example.com deploy 22
# after — pins the agent key named "homelab"
homelab homelab.example.com deploy 22 homelab
Some environments ship their own SSH agent (corporate device-trust tools,
1Password, gpg-agent, etc.) that owns $SSH_AUTH_SOCK. If that agent wins,
ssh/connect never reach Bitwarden and you get Permission denied (publickey) with no approval popup.
Diagnose by comparing the two agents:
ssh-add -l # the active agent
SSH_AUTH_SOCK="$HOME/.bitwarden-ssh-agent.sock" ssh-add -l # should list your keyFix it per-host with IdentityAgent in ~/.ssh/config, scoped to your own
hosts so the other agent stays the default for everything else (adjust the
patterns to match your servers):
Host 10.0.0.* *.example.internal
IdentityAgent ~/.bitwarden-ssh-agent.sock
IdentityAgent overrides $SSH_AUTH_SOCK for matching hosts, so both ssh
and connect use Bitwarden for them.
# 1. Generate the SSH key inside Vaultwarden (New item -> SSH key).
# 2. Export the public key locally (KEY_NAME = the key's name/comment in Vaultwarden).
# If `ssh-add -L` does not list it, copy the "Public key" field from the Bitwarden item instead.
ssh-add -L | grep 'KEY_NAME' > /tmp/bw.pub
# 3. Install it on the target server (prompts for the password one last time).
cat /tmp/bw.pub | ssh USER@HOST 'umask 077; mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys'
# 4. Clean up the temp file.
rm /tmp/bw.pubThen add the server to ~/.ssh/connect.conf (no key column) and connect:
connect SERVERNAME # Bitwarden pops up to approve the connectionOnce connect SERVERNAME logs in via the key without a password, disable
password authentication. Keep your current session open and run this in a
second terminal so you can roll back if anything goes wrong:
ssh -t USER@HOST '
printf "PasswordAuthentication no\nKbdInteractiveAuthentication no\n" \
| sudo tee /etc/ssh/sshd_config.d/00-disable-password.conf >/dev/null &&
sudo sshd -t &&
sudo systemctl reload ssh' # use "sshd" instead of "ssh" on non-Ubuntu distrosThe filename matters: drop-ins are read in lexical order and the first
value for a keyword wins, so the 00- prefix ensures this loads before
distro defaults like Ubuntu's 50-cloud-init.conf (which often sets
PasswordAuthentication yes). sshd -t validates the config before reload.
Verify it took effect, then confirm password login is actually refused:
ssh USER@HOST 'sudo sshd -T | grep -iE "passwordauthentication|kbdinteractive"' # expect: no
ssh -o PubkeyAuthentication=no -o PreferredAuthentications=password USER@HOST # expect: Permission denied (publickey)To roll back, remove the drop-in and reload:
ssh -t USER@HOST 'sudo rm /etc/ssh/sshd_config.d/00-disable-password.conf && sudo systemctl reload ssh'