Kiet Nguyen logo
NotesNotesResumeResume
© 2026 Kiet Nguyen
← All categories

16

SSH & Remote Transfer

  • Key-based SSH
  • Copy file remote
  • rsync deploy directory
ssh — sessionssh-keygenssh-copy-idssh-addssh-keyscanscpsftprsyncsshfsssh — tunnelsssh configHost keys, sshd, systemdssh — debug

Must-know cold

  • ssh user@host · ssh -i KEY user@host · ssh -p PORT user@host
  • scp file user@host:/path/ · sftp user@host · rsync -avz src/ user@host:dest/
  • Keys: ssh-keygen -t ed25519 · ssh-copy-id · chmod 600 key · 700 .ssh
  • Jump: ssh -J bastion user@internal (put jump and dest in ~/.ssh/config)
  • Auth fail: ssh -vv -o IdentitiesOnly=yes -i KEY user@host
  • Hung session: newline then ~.

Toolchain map

Definition: OpenSSH client/server plus tools that speak SSH. Six jobs, not six daemons.

JobTools / files
1 Sessionssh, -t/-T, -N, -f
2 Identityssh-keygen, ssh-copy-id, ssh-add, ssh-keyscan, authorized_keys
3 Transferscp, sftp, rsync -e ssh, sshfs (optional)
4 Tunnelsssh -L -R -D -J
5 Config~/.ssh/config, known_hosts, sshd_config, systemctl ssh/sshd
6 Debugssh -v/-vvv, escape ~., ~?

Auth quick facts

Definition: Password vs key auth and common SSH denial causes.

ItemMeaning
Password authInteractive; often disabled in prod
Key authPublic key in ~/.ssh/authorized_keys on the server
Permission denied (publickey)Key, path, modes, user, or sshd config — not “the network is down”
Host key warningknown_hosts mismatch: MITM or rebuilt host — verify before deleting the line

1. Connection & session

ssh — session

Definition: Encrypted remote login and one-shot command execution (OpenSSH client).

OptionArgumentMeaningExample
(none)user@hostInteractive login shellssh alice@10.0.0.5
(none)user@host cmdRun remote command and exit (no TTY unless -t)ssh alice@host 'uptime'
-iKEYFILEIdentity (private key) filessh -i ~/.ssh/id_ed25519 alice@host
-pPORTSSH server port (default 22). Not scp -Pssh -p 2222 alice@host
-lUSERLogin name (alt to user@)ssh -l alice host
-t—Force TTY (sudo, htop, pagers)ssh -t host sudo -i
-T—Disable TTYScripts / git
-N—No remote command — tunnels onlyWith -L/-R/-D
-f—Background just before the remote command (after prompts; implies -n)ssh -fN -L …
-n—stdin from /dev/nullBackground / scripts
-q—QuietScripts
-4 / -6—Force IPv4/IPv6
-oOPT=VALOverride client configssh -o ConnectTimeout=5 host
-o BatchMode=yes—No prompts (password, passphrase, host-key)CI
-o ConnectTimeout=SECTCP connect timeout only — not authssh -o ConnectTimeout=5 host

Common -o options

OptionMeaning
IdentitiesOnly=yesDo not offer extra agent keys; still uses defaults plus -i / IdentityFile
PasswordAuthentication=noRefuse the password method only — keyboard-interactive can still ask
KbdInteractiveAuthentication=noRefuse keyboard-interactive; pair with the row above for “no password prompt”
StrictHostKeyChecking=yes / no / accept-newHost key policy (ask is the usual default). Prefer accept-new carefully
User= / Port= / ProxyJump=Same meaning as ~/.ssh/config

Flag combos

ComboMeaning
ssh -i key -p 2222 user@hostKey + non-22 port
ssh -t user@host 'sudo systemctl status nginx'One-shot that needs a TTY
ssh -o BatchMode=yes -o ConnectTimeout=5 user@host 'true'Scripted reachability

2. Identity & keys

ssh-keygen

Definition: Create and inspect key pairs. File modes are enforced by ssh / sshd, not by this tool.

OptionArgumentMeaningExample
-ted25519 / rsaType. Prefer ed25519ssh-keygen -t ed25519
-CCOMMENTLabel (user@laptop, not a secret)ssh-keygen -t ed25519 -C 'agent'
-fFILEPath (writes FILE + FILE.pub)ssh-keygen -t ed25519 -f ~/.ssh/agent
-N''Empty passphrase (automation; the private key is the secret)Scripts only
-l-f KEYFingerprint (public or private)ssh-keygen -l -f ~/.ssh/id_ed25519.pub

Authorize the server: append the .pub line to ~USER/.ssh/authorized_keys (one line, no wrap). ssh-copy-id does that over an existing login.

Modes (sshd StrictModes, client too):

  • chmod 700 ~/.ssh
  • chmod 600 ~/.ssh/authorized_keys
  • chmod 600 private key
  • chmod 600 ~/.ssh/config (ignored if group/other-writable)
  • chmod 644 *.pub is fine

ssh-copy-id

Definition: Append a local public key to remote authorized_keys using a login that already works (password or another key).

OptionArgumentMeaningExample
-iKEY.pubPublic key to installssh-copy-id -i ~/.ssh/id_ed25519.pub user@host
-pPORTRemote SSH port (ssh -p, not scp -P)ssh-copy-id -p 2222 user@host

ssh-add

Definition: Load private keys into ssh-agent so you type the passphrase once per agent lifetime.

OptionArgumentMeaningExample
(none)KEYAdd private keyssh-add ~/.ssh/id_ed25519
-l—List fingerprints in the agentssh-add -l
-L—List public keys in the agentssh-add -L
-dKEYRemove one identityssh-add -d ~/.ssh/id_ed25519
-D—Delete all identities from this agentssh-add -D

Needs a running agent (SSH_AUTH_SOCK). On a desktop session you usually already have one. ssh-add -l → “Could not open a connection to your authentication agent” means there is no agent, not a bad key.

ssh-keyscan

Definition: Fetch a server’s host keys (not user keys) for known_hosts. Does not log in.

OptionArgumentMeaningExample
(none)HOSTPrint host keysssh-keyscan 10.0.0.5
-pPORTNon-22ssh-keyscan -p 2222 host
-tTYPESKey typesssh-keyscan -t ed25519,rsa host
-H—Hash hostnames in the outputSafer known_hosts lines
-fFILEHosts listed in FILEBootstrap

Compare fingerprints with an out-of-band source before appending. Blind ssh-keyscan >> known_hosts trusts the network the first time, same as StrictHostKeyChecking=accept-new.

3. File transfer

scp

Definition: Copy files over SSH. OpenSSH ≥ 9.0 uses SFTP under the hood; -O is the legacy scp protocol if you need it.

OptionArgumentMeaningExample
(none)SRC DESTCopyscp f user@host:/tmp/
-rSRC DESTRecursive. Follows symlinksscp -r dir user@host:/opt/
-iKEYIdentityscp -i key f user@host:
-PPORTPort (capital P)scp -P 2222 f user@host:
-p—Preserve mtimes/modes (not owner)scp -p f user@host:
-C—Compression
-q / -v—Quiet / verbose
-oOPTPass-through to sshscp -o ProxyJump=bastion f user@int:
-3—Remote↔remote via the local host (default since OpenSSH 9.0)Older clients need this flag
-R—Direct remote↔remote (OpenSSH 9.0+; origin must auth to dest)Opposite of -3

Directions: scp local user@host:remote · scp user@host:remote local · scp user@h1:a user@h2:b

scp does not use rsync’s trailing-slash rule: scp -r dir/ still copies the directory.

sftp

Definition: Interactive file protocol over SSH (ls/put/get/cd). Same auth as ssh. Port flag is -P (like scp).

Command / optionMeaningExample
sftp user@hostOpen a session (home on remote)
-i / -P / -o / -JSame idea as ssh/scpsftp -P 2222 -i key user@host
ls / cd / pwdRemote listing (sftp’s own)
lls / lcd / lpwdLocal listing
put local [remote]Uploadput app.conf /etc/app/
get remote [local]Downloadget /var/log/app.log
mkdir / rm / rmdir / renameRemote names
-b FILEBatch commands from a file (no prompt)sftp -b cmds.txt user@host

Quit with bye / exit / quit. For trees and deltas, prefer rsync.

rsync

Definition: Delta copy, local or over SSH. Default remote shell is ssh when the dest is user@host:path.

OptionArgumentMeaningExample
(none)SRC DESTSync (trailing / matters)rsync -a src/ dest/
-a—Archive (-rlptgoD). Usual start — not rsync’s default
-v—Verbosersync -av src/ dest/
-z—Compress in transitrsync -avz src/ host:dest/
-n / --dry-run—Show what would happenAlways before --delete
-h—Human numbers
--delete—Delete extra files in dest dirs being syncedDangerous — mirror
--progress—Per-file progressrsync -av --progress src/ dest/
-P—--partial --progress (not port)rsync -avP src/ dest/
-essh cmdRemote shell (port/key live here)rsync -e 'ssh -i key -p 2222' src/ host:dest/
--excludePATSkiprsync -a --exclude '.git' src/ dest/
-x—Don’t cross filesystem boundaries
-u—Skip files newer on receiver

Trailing slash

FormMeaning
rsync -a src/ dest/Copy contents of src into dest
rsync -a src dest/Copy directory src into dest as dest/src

sshfs

Definition: Mount a remote directory on the local filesystem (FUSE + SFTP). Optional package (sshfs / fuse-sshfs), not part of OpenSSH itself.

CommandMeaningExample
sshfs user@host:remote localmntMountsshfs alice@host:/opt/app /mnt/app
sshfs -p PORT -o IdentityFile=KEY …Port is -p (like ssh, not scp -P)
fusermount -u / fusermount3 -uUnmount (Linux FUSE; name varies)fusermount3 -u /mnt/app
umount localmntWorks if the mount is registered and you own it

Needs FUSE, a mountpoint directory, and the same auth as ssh. A dropped SSH session leaves a stuck mount — unmount, then remount. For deploys, rsync is the usual tool; sshfs is for interactive browsing.

4. Tunnels, forwarding, jump hosts

ssh — tunnels

Definition: The SSH client can listen and proxy TCP (and a SOCKS listener) through the encrypted session. HOST in -L/-R is not “your laptop” by default.

OptionArgumentMeaningExample
-LLPORT:HOST:RPORTListen locally. HOST:RPORT is reached from the SSH server. 127.0.0.1 = the remotessh -N -L 8080:127.0.0.1:80 user@bastion
-RRPORT:HOST:PORTListen on the server. HOST:PORT is reached from the clientssh -N -R 9000:127.0.0.1:3000 user@host
-DPORTLocal SOCKS dynamic forwardssh -N -D 1080 user@host
-JjumpProxyJump — hop via bastionssh -J bastion user@internal
-N—No remote shell (keep the tunnel only)Pair with -L/-R/-D
-f—Background after authssh -fN -L …
-A—Agent forwarding — the remote can use your agent. Avoid unless required

-L use: you cannot reach bastion:80 from here, but bastion can. Local http://127.0.0.1:8080 is bastion’s port 80.

-R use: a process on the server connects to server:9000 (loopback by default) and SSH brings that back to your 127.0.0.1:3000. To listen on all remote interfaces you need both ssh -R *:9000:127.0.0.1:3000 (or 0.0.0.0:…) and GatewayPorts enabled on sshd. GatewayPorts alone does not change a loopback -R.

-D use: point a browser/tool at socks5://127.0.0.1:1080 (OpenSSH speaks SOCKS4 and SOCKS5). Traffic egresses from the SSH server. Not a VPN (no UDP, no TUN — that is ssh -w, out of scope here).

-J use: ssh -J alice@bastion bob@10.0.0.8. -i / most -o apply to the destination, not the jump. Put each hop in ~/.ssh/config.

5. Config & server

ssh config

Definition: Client defaults in ~/.ssh/config. For each keyword, the first obtained value is kept. Later Host/Match blocks may still fill keywords that earlier blocks left unset — put specific hosts above Host *.

KeywordArgumentExample line
Hostalias / patternHost agent1 · Host agent-*
HostNameDNS or IPHostName 10.0.0.5
UserloginUser jenkins
IdentityFileprivate keyIdentityFile ~/.ssh/id_ed25519
IdentitiesOnlyyesIdentitiesOnly yes
PortNPort 2222
ProxyJumpbastionProxyJump bastion
ForwardAgentno (prefer)
ServerAliveIntervalsecondsKeep NAT mappings
Host bastion
  HostName bastion.corp
  User alice
  IdentityFile ~/.ssh/id_ed25519
  IdentitiesOnly yes

Host agent-*
  User jenkins
  ProxyJump bastion
  IdentityFile ~/.ssh/agent_key
  IdentitiesOnly yes

Then: ssh bastion · ssh agent-01.

Host keys, sshd, systemd

Definition: Who you are talking to (known_hosts), who may log in (authorized_keys), how sshd listens (sshd_config).

PathSideRole
~/.ssh/configclientAliases, keys, jump, ports
~/.ssh/known_hostsclientHost key fingerprints (MITM check)
~/.ssh/id_ed25519 + .pubclientUser key pair
~/.ssh/authorized_keysserver (per user)Public keys allowed to log in as that user
/etc/ssh/ssh_configclient (system)Defaults for all users
/etc/ssh/sshd_configserverPort, PermitRootLogin, PasswordAuthentication, AllowUsers, AuthorizedKeysFile
/etc/ssh/ssh_host_*serverHost keys (what ssh-keyscan prints)

Debian/Ubuntu unit is often ssh.service (alias sshd). RHEL family is sshd.service.

CommandMeaning
sudo systemctl status ssh / sshdIs the daemon up?
sudo systemctl reload sshRe-read sshd_config (preferred over restart)
sudo sshd -tConfig syntax check before reload
sudo sshd -TDump effective config (-C user,host,addr if you use Match)

Reload, do not blindly restart, if you only have this SSH session.

6. Debug & audit

ssh — debug

Definition: Client verbosity, host-key policy, and in-session escape characters.

Option / keyMeaningExample
-vAuth and config outlinessh -v user@host
-vv / -vvvKEX, keys offered, which file matchedssh -vvv -i key user@host
-vv -o IdentitiesOnly=yes -i keyStop “Too many authentication failures” from extra agent keys
~.Disconnect (tilde at start of line, after Enter)Interactive pty sessions — not ssh -T / typical ssh -N / -f
~?List escape sequencesEnter, then ~?
~#List forwarded connections
~CEscape command line (add/cancel forwards)Off by default unless EnableEscapeCommandline yes

EscapeChar default is ~. It is only special at the beginning of a line and when a pty was allocated. Mid-line ~. is just characters. Background -f tunnels have no stdin for this — kill the client pid. man ssh “ESCAPE CHARACTERS”.

Server-side audit (this host as the SSH server): journalctl -u ssh / -u sshd, ss -tlnp | grep :22.

Flag combos

ComboMeaning
ssh -v -o IdentitiesOnly=yes -i key user@hostDebug this key only
ssh -vvv -p 2222 user@hostPort + protocol dump

Common recipes

GoalCommand
Loginssh user@host
Key loginssh -i ~/.ssh/id_ed25519 user@host
Remote commandssh user@host 'df -h; uptime'
Debug authssh -vv -o IdentitiesOnly=yes -i key user@host
Copy file upscp ./app.conf user@host:/etc/app/
Copy downscp user@host:/var/log/app.log .
Interactive filessftp user@host
Sync dirrsync -avz ./build/ user@host:/opt/app/
Dry-run syncrsync -avn ./build/ user@host:/opt/app/
Via bastionssh -J bastion user@internal
Local forwardssh -N -L 8080:127.0.0.1:80 user@bastion
SOCKSssh -N -D 1080 user@host
New keyssh-keygen -t ed25519 -C 'laptop' -f ~/.ssh/id_ed25519
Install pubkeyssh-copy-id -i ~/.ssh/id_ed25519.pub user@host
Agentssh-add -l · ssh-add ~/.ssh/id_ed25519
Hung sessionEnter, then ~.

Pitfalls

  • Port letters: ssh / ssh-copy-id / ssh-keyscan / sshfs use -p. scp / sftp use -P. scp -p is preserve. rsync -P is --partial --progress. Modern ssh -P tag is a config tag, not a port.
  • scp -r follows symlinks. rsync -a copies the link itself.
  • scp does not use rsync’s trailing-slash rule.
  • --delete can wipe remote — dry-run first.
  • Private key or ~/.ssh/config group/other-writable → client ignores them. authorized_keys too open → sshd ignores it (StrictModes).
  • PasswordAuthentication=no does not block keyboard-interactive prompts.
  • Remote sudo often needs ssh -t.
  • -i / most -o with -J apply to the destination, not the jump — use ~/.ssh/config per Host.
  • -L 127.0.0.1 is the SSH server’s loopback. -R 127.0.0.1 is your loopback.
  • Agent forwarding (-A) lets a compromised remote use your keys. Prefer ProxyJump.
  • Host key change: verify rebuild vs MITM before editing known_hosts.
  • ssh-keyscan >> known_hosts without checking the fingerprint is TOFU, not a second factor.
  • sshfs is not OpenSSH; a dead connection needs an unmount. Not a deploy tool.
  • Escape ~. only at the start of a line, and only if a pty was allocated. It will not save a ssh -fN tunnel. ~C needs EnableEscapeCommandline yes on current OpenSSH.
  • Debian systemctl restart ssh vs RHEL sshd — systemctl status 'ssh*'.

For more details, try man <command> in your terminal.

Previous15 Archives & CompressionNext17 Redirection & Shell Glue