ssh-config

Installation
SKILL.md

SSH Config

SSH key management, configuration, tunneling, and security hardening.

Key Generation

# Ed25519 (recommended — shorter, faster, more secure)
ssh-keygen -t ed25519 -C "your@email.com"
ssh-keygen -t ed25519 -f ~/.ssh/github_key -C "github"       # custom filename
ssh-keygen -t ed25519 -f ~/.ssh/deploy_key -C "deploy" -N "" # no passphrase

# RSA 4096 (for legacy systems that don't support Ed25519)
ssh-keygen -t rsa -b 4096 -C "your@email.com"

# Change passphrase on existing key
ssh-keygen -p -f ~/.ssh/id_ed25519

# Show fingerprint / update comment
ssh-keygen -lf ~/.ssh/id_ed25519.pub
ssh-keygen -c -f ~/.ssh/id_ed25519 -C "alice@work-laptop-2024"

Copying Public Keys

# ssh-copy-id (simplest)
ssh-copy-id user@host
ssh-copy-id -i ~/.ssh/mykey.pub user@host
ssh-copy-id -p 2222 user@host

# Manual method (when ssh-copy-id isn't available)
cat ~/.ssh/id_ed25519.pub | ssh user@host 'mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys'

# Copy to clipboard (macOS) for GitHub/GitLab web UI
pbcopy < ~/.ssh/id_ed25519.pub

SSH Agent

eval "$(ssh-agent -s)"                          # start agent
ssh-add ~/.ssh/id_ed25519                       # add key
ssh-add -t 43200 ~/.ssh/id_ed25519              # add with 12h timeout
ssh-add -l                                       # list loaded keys
ssh-add -d ~/.ssh/id_ed25519                    # remove specific key
ssh-add -D                                       # remove all keys

macOS Keychain Integration

Host *
    AddKeysToAgent yes
    UseKeychain yes
    IdentityFile ~/.ssh/id_ed25519
ssh-add --apple-use-keychain ~/.ssh/id_ed25519  # persist across reboots

Agent Forwarding

ssh -A user@server       # forward local agent to remote host
Host myserver
    ForwardAgent yes

Only forward agent to trusted hosts. A compromised host can use your agent to authenticate as you.

SSH Config File (~/.ssh/config)

Basic Host Entries

Host myserver
    HostName 192.168.1.100
    User admin
    Port 22
    IdentityFile ~/.ssh/id_ed25519

Host prod
    HostName prod.example.com
    User deploy
    IdentityFile ~/.ssh/deploy_key
    IdentitiesOnly yes

IdentitiesOnly yes prevents SSH from trying every key in the agent.

Wildcards and Defaults

Host *
    ServerAliveInterval 60
    ServerAliveCountMax 3
    AddKeysToAgent yes

Host *.example.com
    User deploy
    IdentityFile ~/.ssh/deploy_key

Host 192.168.1.*
    User admin
    StrictHostKeyChecking no
    UserKnownHostsFile /dev/null

Multiple GitHub/GitLab Accounts

Host github.com-personal
    HostName github.com
    User git
    IdentityFile ~/.ssh/github_personal
    IdentitiesOnly yes

Host github.com-work
    HostName github.com
    User git
    IdentityFile ~/.ssh/github_work
    IdentitiesOnly yes
git clone git@github.com-personal:myuser/repo.git
git clone git@github.com-work:company/repo.git
git remote set-url origin git@github.com-work:company/repo.git

Jump Hosts / Bastion Hosts

ProxyJump (OpenSSH 7.3+)

Host bastion
    HostName bastion.example.com
    User jump

Host internal
    HostName 10.0.0.5
    User admin
    ProxyJump bastion

# Chain: bastion -> middleware -> deep-internal
Host middleware
    HostName 10.0.0.10
    User app
    ProxyJump bastion

Host deep-internal
    HostName 172.16.0.5
    User admin
    ProxyJump middleware
ssh -J jump@bastion.example.com admin@10.0.0.5            # command line
ssh -J jump@bastion1,jump@bastion2 admin@10.0.0.5         # chain multiple

ProxyCommand (Legacy)

Host internal-legacy
    HostName 10.0.0.5
    User admin
    ProxyCommand ssh -W %h:%p bastion

Port Forwarding

Local Forward (-L)

ssh -L 8080:localhost:80 user@server                       # local:8080 -> remote:80
ssh -L 5432:localhost:5432 user@server                     # remote PostgreSQL
ssh -L 8080:internal-db.example.com:5432 user@bastion      # through bastion to third host
ssh -L 0.0.0.0:8080:localhost:80 user@server               # bind all interfaces

Remote Forward (-R)

ssh -R 8080:localhost:3000 user@server     # expose local:3000 on remote:8080

Dynamic Forward / SOCKS Proxy (-D)

ssh -D 1080 user@server                   # SOCKS5 proxy on localhost:1080
ssh -D 1080 -f -N user@server             # background SOCKS proxy

Port Forwarding in Config

Host tunnel-db
    HostName server.example.com
    User admin
    LocalForward 5432 localhost:5432
    LocalForward 6379 localhost:6379

Host tunnel-web
    HostName server.example.com
    User admin
    LocalForward 8080 localhost:80
    DynamicForward 1080

SSH Tunnels

# Background tunnel (-f: background, -N: no remote command)
ssh -f -N -L 8080:localhost:80 user@server
pkill -f "ssh -f -N -L 8080"              # kill it

# Persistent tunnel with autossh (auto-reconnects on drop)
autossh -M 0 -f -N -L 8080:localhost:80 user@server   # brew/apt install autossh

# Database access through bastion
ssh -f -N -L 5432:db.internal:5432 user@bastion && psql -h localhost -p 5432 -U dbuser mydb
ssh -f -N -L 6379:redis.internal:6379 user@bastion && redis-cli -h localhost

SCP and rsync Over SSH

# SCP
scp file.txt user@host:/path/                              # upload
scp user@host:/path/file.txt ./                             # download
scp -r folder/ user@host:/path/                             # recursive

# rsync (preferred — delta transfer, resume, compression)
rsync -avz folder/ user@host:/path/
rsync -avz --progress folder/ user@host:/path/              # with progress
rsync -avz --delete folder/ user@host:/path/                # mirror (delete extra)
rsync -avzn folder/ user@host:/path/                        # dry run
rsync -avz -e "ssh -J jump@bastion" folder/ admin@internal:/path/  # via jump host

Multiplexing (Connection Reuse)

Host *
    ControlMaster auto
    ControlPath ~/.ssh/sockets/%r@%h-%p
    ControlPersist 600
mkdir -p ~/.ssh/sockets     # create socket directory

# First ssh opens master socket; subsequent connections reuse it (instant, no re-auth)
ssh -O check myserver       # check master status
ssh -O exit myserver        # close master connection

ControlPersist 600 keeps the socket alive 600s after the last session closes.

SSH Escape Sequences

Press Enter then ~ during an SSH session:

~.      Disconnect (kill hung session)    ~?      List all escapes
~C      Open command line (add -L/-R/-D mid-session)
~#      List forwarded connections        ~~      Send literal ~

Debugging Connection Issues

ssh -v user@host                        # basic debug
ssh -vvv user@host                      # maximum verbosity
ssh -T git@github.com                   # test GitHub connection
ssh -G myserver | grep -i hostname      # show resolved config

# Remove stale host key after server reinstall
ssh-keygen -R hostname
ssh-keygen -R "[hostname]:port"

Common errors:

  • "Permission denied (publickey)" -- wrong key, key not in agent, or not in authorized_keys
  • "Host key verification failed" -- host key changed (server reinstall or MITM)
  • "Connection refused" -- sshd not running or wrong port
  • "Connection timed out" -- firewall, wrong IP, or host down

SSH Security Hardening

File Permissions

chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519 ~/.ssh/config ~/.ssh/authorized_keys
chmod 644 ~/.ssh/id_ed25519.pub

Server Configuration (/etc/ssh/sshd_config)

PasswordAuthentication no
PubkeyAuthentication yes
PermitRootLogin no
Port 2222
AllowUsers deploy admin
PermitEmptyPasswords no
MaxAuthTries 3
X11Forwarding no
AllowAgentForwarding no
sudo sshd -t                            # validate config
sudo systemctl restart sshd

fail2ban (Brute-Force Protection)

sudo apt install fail2ban
# /etc/fail2ban/jail.local: [sshd] enabled=true, maxretry=3, bantime=3600, findtime=600
sudo systemctl enable --now fail2ban
sudo fail2ban-client status sshd        # check banned IPs

SSH Certificates vs Keys

ssh-keygen -t ed25519 -f ca_key -C "SSH CA"                              # create CA
ssh-keygen -s ca_key -I "alice-cert" -n alice -V +52w id_ed25519.pub     # sign user key
ssh-keygen -s ca_key -I "web-server" -h -n web.example.com host_key.pub  # sign host key
ssh-keygen -Lf id_ed25519-cert.pub                                        # view cert details

Server trusts CA: add TrustedUserCAKeys /etc/ssh/ca_key.pub to sshd_config. Client trusts host CA: add @cert-authority *.example.com <ca_key.pub contents> to known_hosts.

Certificates eliminate distributing authorized_keys to every server -- sign once, access everywhere the CA is trusted.

Related skills

More from 1mangesh1/dev-skills-collection

Installs
6
GitHub Stars
3
First Seen
Feb 21, 2026