Cheatsheets:LinuxCLI/ssh

From Wikibase
Revision as of 08:02, 30 September 2026 by Rongzhou (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

Languages: English · français · Esperanto

Quick reference for smart people — part of our Linux cheatsheets collection.

Common OpenSSH client ops: log into a remote machine, run one-off commands, copy files, and tunnel ports over an encrypted channel. Everything here is typed on your local Linux machine; the remote side only needs a running sshd.

New to SSH? Every command has a built-in manual — try man ssh, man ssh_config or tldr ssh for short examples.

Set up key login

ssh user@host                            # first connection: type the password, or for most cloud providers, via the web console shell
ssh-keygen -t ed25519 -C "user@laptop"   # create ~/.ssh/id_ed25519 and its .pub
ssh-copy-id user@host                    # install the public half on the server
ssh user@host                            # no password from now on

user: your username on the remote machine host: IP address or URL of the remote machine

The private key never leaves your machine; the server only needs to stores the public key, one key per line, in ~/.ssh/authorized_keys.

Connecting

ssh user@host                         # log in as user on host
ssh -p 2222 user@host                 # non-standard port
ssh -i ~/.ssh/my_key user@host        # use my_key as private key
ssh user@host command                 # run one command instead of spawning a shell
ssh user@host -N                      # no remote command: forward ports only
ssh user@host 'command 1; command 2'  # several commands: quote and separate commands by ;
ssh -t user@host tmux attach          # -t: force a TTY, for interactive programs. Opposite is -T
ssh -J user1@proxy:2222 user2@host    # -J*: proxy jump, connects to host as user2, via the intermediate (proxy) machine on port 2222
ssh -o StrictHostKeyChecking=accept-new user@host # accept an unknown host key once
ssh -G host                           # print effective configurations for a given host
ssh -v user@host                      # debug the connection; -vv and -vvv go further
  • This may be useful when you need to connect to a remote DB, etc. behind a firewall that is only accessible via machines on the local network. You first connect to a proxy machine on that network which accepts ssh from other networks, then ssh to the final destination via the proxy.

Keys

ssh-keygen -t ed25519                        # new key in ~/.ssh/id_ed25519 (+ .pub)
ssh-keygen -t ed25519 -f ~/.ssh/id_work -C "deploy@laptop"   # a named key, with a comment
ssh-keygen -t rsa -b 4096                    # use RSA instead of ed25519 for legacy compatibility
ssh-keygen -p -f ~/.ssh/id_ed25519           # change the passphrase
ssh-keygen -y -f ~/.ssh/id_ed25519           # print the public half of a private key
ssh-keygen -l -f ~/.ssh/id_ed25519.pub       # fingerprint of a key
ssh-keygen -R host                           # forget a host's key in known_hosts
ssh-copy-id user@host                        # install the default key on a server
ssh-copy-id -i ~/.ssh/id_work.pub user@host  # install one specific key

ssh enforces key permissions: It will not accept a private key where any other user has read permission. Keep ~/.ssh at 700 and private keys at 600 (chmod 600 ~/.ssh/id_ed25519).

Good security practice: Always protect your private keys with a passphrase.

~/.ssh/config

For frequently accessed machines, set up a connection profile in `~/.ssh/config` on your local machine.

Host mySSHRemote 
    HostName 203.0.113.10
    User deploy
    Port 2222
    IdentityFile ~/.ssh/id_work

Host *.internal.example.com
    ProxyJump mySSHRemote

Host myLegacyRemote
    HostKeyAlgorithms +ssh-rsa          # for old servers
    PubkeyAcceptedAlgorithms +ssh-rsa

Host *                         # global defaults: keep this block LAST
    ServerAliveInterval 30     # ping every 30 s, so idle NAT and firewall sessions survive
    ServerAliveCountMax 3
    AddKeysToAgent yes         # load a key into the agent on first use
    ControlMaster auto         # reuse one TCP connection for every later session to the 
    ControlPath ~/.ssh/cm-%r@%h:%p # save the TCP connections to the specified path, with automatic naming. %r: remote user, %h: host name %p: port
    ControlPersist 10m         # cache TCP connection for 10 min after disconnection
mkdir -p ~/.ssh/cm              # ControlPath does not create the directory
ssh -F ~/.ssh/otherconf host    # use an alternative config file
ssh -O check host               # is a shared connection open?
ssh -O exit host                # close shared connection, and every session using it

For each option, the first value found wins. Hence the catch-all Host * should always be at the bottom.

Encrypted ssh keys: ssh-agent

If you use encrypted ssh keys, ssh-agent allow you to cache those keys unencrypted in RAM over one login session on local machine, so you do not have to provide the passphrase on each use.

eval "$(ssh-agent -s)"            # start an agent in this shell
ssh-add ~/.ssh/id_ed25519         # load a key. If key encryped, asks for the passphrase once
ssh-add -t 8h ~/.ssh/id_ed25519   # -t: automatically drop the key after 8 hours
ssh-add -l                        # print fingerprints of the loaded keys
ssh-add -L                        # print full public keys of the loaded keys
ssh-add -d ~/.ssh/id_ed25519      # remove one key
ssh-add -D                        # remove every key
ssh-agent -k                      # kill this agent

AddKeysToAgent yes in ~/.ssh/config loads keys into the agent on first use.

Copying files

scp file1.txt user@host:/tmp/          # upload
scp -P 2222 file1.txt user@host:/tmp/  # note: scp's port flag is -P, NOT -p
scp user@host:~/file1.txt /tmp/        # download a file
scp 'user@host:/var/log/*.log' .       # download multiple files matching the glob: remember to quote remote path when * is used
scp -r dir/ user@host:~/folder1        # download a whole directory
rsync -avz --progress dir/ user@host:/tmp/   # sync changes
sftp user@host                         # interactive: ls, get, put, bye

Note rsync must be installed both on your local machine and the remote server to work.

Prefer rsync for large or repeated transfers, as it transfers only the differences and can resume when transfer is interrupted.

Port forwarding

ssh -L 8080:localhost:80 user@host        # local 8080 forwards to host port 80
ssh -L 5432:db.internal:5432 user@host    # local 5432 forwards to host, which then forwards to db.internal 5432 (proxy connect)
ssh -R 9000:localhost:3000 user@host      # reverse forward: the server's 9000 forwards to your local 3000
ssh -D 1080 user@host                     # make host accessible as a SOCKS5 proxy on localhost:1080
ssh -N -f -L 8080:localhost:80 user@host  # background tunnel only, no interactive shell
ssh -O exit user@host                     # close the backgrounded tunnel

The same forwardings in config, for a tunnel you start by name:

Host dbtunnel
    HostName db.example.com
    LocalForward 5432 localhost:5432   # -L 8080:localhost:80 by default for `ssh dbtunnel`
    SessionType none                   # background tunnel only

Escape sequences

Escape Action
~. disconnect
~^Z suspend ssh, back to the local shell
~# list the forwarded connections
~C open ssh command line: add -L, -R or -D to the live session
~? list all escapes

Inside an interactive session, escape sequences are recognised only at the start of a line.

~C is disabled by default. To enable, add to `~/.ssh/config`:

Host *
  EnableEscapeCommandline yes

Troubleshooting

Symptom Fix
Permission denied (publickey) Incorrect key or key-host/user mismatch: run ssh -vvv user@host for more details
Too many authentication failures the agent offers more keys than the server allows attempts: add IdentitiesOnly yes to that host's block to only use keys explicitely configured for that host
REMOTE HOST IDENTIFICATION HAS CHANGED! the server's key changed: this could either be a server-side reset or a man-in-the-middle attack: confirm with the server's admin whether a legitimate reset took place before running ssh-keygen -R host:port to accept the change
Connection refused wrong port, sshd down, or a firewall: test with nc -vz host 22
Connection timed out nothing answers at all: a firewall is dropping the packets, or the host is unreachable
the session freezes after some idle consider adding ServerAliveInterval 60 in the config file to keep the session alive by sending keep-alive signals every 60s

For more

  • ssh(1) — every client flag, escape sequence and environment variable
  • ssh_config(5) — every client option, including the forwarding and multiplexing ones
  • ssh-keygen(1) — certificates, FIDO keys, key format conversion
  • sshd_config(5) — the server side: authorized_keys, PermitRootLogin, PasswordAuthentication
  • OpenSSH — release notes and security advisories
  • explainshell.com — annotate any command line