SSH — How It Works, Public-Key Authentication, and Essential Commands thumbnail

SSH — How It Works, Public-Key Authentication, and Essential Commands

⏱ approx. 13 min views 163 likes 0 LOG_DATE:2026-05-09
TOC

SSH (Secure Shell) is the protocol used to reach another computer safely over a network, perform remote operations, and transfer files. It swept away the eavesdropping, tampering, and impersonation issues that Telnet, rlogin, and other cleartext protocols had. This article walks through, in practical terms, the three-step connection, public-key authentication, file transfer, port forwarding, and the minimum settings worth getting right.

01

Where SSH fits #

SSH runs on TCP port 22 and is standardised in RFC 4253. It is essential infrastructure for modern development — server operations, pushing to GitHub, CI/CD deployments. OpenSSH (the OpenBSD-originated open-source implementation) ships with virtually every Linux distribution, macOS, and Windows 10 or later, and is the de facto standard.

▸ Why Telnet wasn't enough

Telnet sends usernames, passwords, and command output across the network in cleartext. Anyone running tcpdump on the same segment could see the credentials in plain sight. SSH solved this completely by integrating encryption + host authentication + user authentication into a single protocol.

02

The three-step connection #

The SSH connection sequence runs in this order. Internalise this skeleton and you've essentially understood how SSH works.

1. Host authentication
The client checks "is this really the server?". The server signs with its host key, and the fingerprint is saved into ~/.ssh/known_hosts.
2. User authentication
The server checks "is this really the right user?". Identity is proven with a password or a public key.
3. Encrypted communication
A shared key independently generated by both sides via Diffie-Hellman is established. From here on, traffic is encrypted with ChaCha20-Poly1305 or AES-GCM.

Try it yourself — step through from the TCP connection to key exchange, host auth, and user auth until the encrypted shell is established.

Host authentication — the TOFU model #

On a first-time connection the client is presented with a previously-unknown host-key fingerprint. The fingerprint is saved into known_hosts, and subsequent connections verify the match. This is the TOFU (Trust On First Use) model.

The host-key warning on first connection
The authenticity of host 'example.com' can't be established. ED25519 key fingerprint is SHA256:abcd1234... Are you sure you want to continue connecting (yes/no/[fingerprint])?
▸ The first time is the MITM window

TOFU's weak spot is "that very first connection". In strict environments, server administrators publish the fingerprint via a separate channel (internal wiki / an authenticator app) in advance, and the user visually confirms that the value shown on first connection matches.

User authentication — two methods #

Method How it works Recommendation
Password authentication Username + password are sent over the encrypted channel △ a brute-force magnet
Public-key authentication The client signs with its private key, the server verifies with the public key ◎ the standard for production

In production the standard practice is to disable password authentication and allow only public-key authentication.

03

How public-key authentication works #

For public-key authentication, a key pair is generated on the client side.

  • Private keynever leaves your hands
  • Public key — copied to the server in advance
Generate a key pair (Ed25519 recommended)
$ ssh-keygen -t ed25519 -C "your@email" Generating public/private ed25519 key pair. Enter file in which to save the key (/home/user/.ssh/id_ed25519): Enter passphrase (empty for no passphrase): Your identification has been saved in /home/user/.ssh/id_ed25519 Your public key has been saved in /home/user/.ssh/id_ed25519.pub # private key: ~/.ssh/id_ed25519 (never let this leave your machine) # public key: ~/.ssh/id_ed25519.pub (copy this to the server)
▸ Why Ed25519

Short keys, strong, and fast to generate and verify. Unless compatibility forces your hand, just pick Ed25519 today. Fall back to RSA-4096 only when you really do have to talk to OpenSSH older than 7.0.

Register the public key on the server #

Append to authorized_keys with ssh-copy-id
$ ssh-copy-id user@example.com /usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/user/.ssh/id_ed25519.pub" Number of key(s) added: 1 # gets appended to ~/.ssh/authorized_keys on the server (and permissions are set to 600)

The challenge-response flow #

1. Present the public key
The client announces "I want to log in with this public key".
2. Random challenge
The server generates a random number and sends it.
3. Sign with the private key
The client signs the random number with its private key and returns it.
4. Verify with the public key
The server verifies the signature against the public key in authorized_keys. If it matches, authentication succeeds.
▸ The private key never travels the network

The signing happens entirely on the client side. The private key itself never touches the wire. That's the fundamental reason it's overwhelmingly stronger than password authentication. The challenge is random every time, so it's also immune to replay attacks.

04

Basic commands #

Connection variants
# standard connection $ ssh user@example.com # specify port $ ssh -p 2222 user@example.com # specify key file $ ssh -i ~/.ssh/special_key user@host # single-command execution (no interactive shell) $ ssh user@host 'uptime' # verbose logging (useful when troubleshooting) $ ssh -vvv user@host
Key management commands
# generate a new key $ ssh-keygen -t ed25519 # change the passphrase $ ssh-keygen -p -f ~/.ssh/id_ed25519 # check the public key fingerprint $ ssh-keygen -l -f ~/.ssh/id_ed25519.pub # remove an old host key from known_hosts after a key rotation $ ssh-keygen -R example.com
▸ Use ssh-agent so you don't retype the passphrase

Typing the passphrase every time gets tedious. Start ssh-agent, run ssh-add ~/.ssh/id_ed25519 to decrypt the key once, and the rest of the session is passphrase-free. Never use a key without a passphrase — that's the iron rule.

05

File transfer #

There are three file-transfer methods that ride on top of the SSH encrypted channel. Pick by use case.

scp / sftp / rsync over SSH
# scp — one-shot copy (simple, good for small transfers) $ scp local.txt user@host:/path/ $ scp -r local-dir/ user@host:/path/ # sftp — interactive file operations (ls / cd / get / put) $ sftp user@host sftp> get remote.txt sftp> put local.txt # rsync over SSH — incremental transfer (ideal for lots of files / backups) $ rsync -avz -e ssh src/ user@host:/dst/
06

Port forwarding #

A feature that tunnels traffic from another application through the SSH encrypted channel. There are three patterns.

Pattern Flag Typical use
Local forward -L Reach a remote DB through a bastion as if it were local
Remote forward -R Temporarily expose a local app via an external server (ngrok-like)
Dynamic -D SOCKS proxy — route a browser's egress through SSH
Reach PostgreSQL through a bastion
# connect to bastion, forwarding local 5432 to whatever 5432 the bastion can see $ ssh -L 5432:localhost:5432 user@bastion.example.com # in another terminal, just run psql against localhost $ psql -h localhost psql (16.0) postgres=#

Multi-hop bastions — ProxyJump #

Chain bastions with -J
# one hop — internal via bastion $ ssh -J user@bastion user@internal-server # multi-hop — b1 → b2 → target $ ssh -J user@b1,user@b2 user@target # the old ProxyCommand syntax (now superseded by -J, no longer recommended)
07

The minimum server settings #

In /etc/ssh/sshd_config, make sure these three lines are in effect.

The sshd_config baseline
# /etc/ssh/sshd_config PermitRootLogin no # no direct root login PasswordAuthentication no # disable password auth (public key only) PubkeyAuthentication yes # enable public-key authentication # always syntax-check before applying $ sudo sshd -t $ sudo systemctl reload sshd
▸ How to not lock yourself out

After changing the config, do not close the current session. Open a new terminal, reconnect, confirm the new session works, and only then disconnect the old one. There are countless stories of someone setting PasswordAuthentication no, fat-fingering something else, and being unable to log back in.

08

Handy ~/.ssh/config tricks #

Give each destination an alias and you can just type ssh internal. Once your inventory grows beyond a few servers — whether for a team or personally — this becomes essential.

A typical ~/.ssh/config
# bastion Host bastion HostName bastion.example.com User admin Port 2222 IdentityFile ~/.ssh/id_ed25519_admin # internal server (reached via the bastion using ProxyJump) Host internal HostName 10.0.0.5 User deploy IdentityFile ~/.ssh/id_ed25519_deploy ProxyJump bastion # now ssh internal is enough to reach the internal server
09

References #

𝕏 Post B! Hatena