SSH: Secure Remote Login
SSH (Secure Shell) is the standard way to log into a remote Linux machine, run commands on it, and move files to and from it, all over an encrypted connection. Before SSH, tools like telnet, rsh, and ftp sent everything — including your password — as plain text over the network, so anyone watching the traffic could read it. SSH replaced all of that with a single, strongly encrypted protocol, and it is the backbone of remote Linux administration: every cloud server, every CI/CD deploy step, and every git push over SSH depends on it.
Overview: How SSH Works
SSH is a client-server protocol. The server side is a background process (a daemon) called sshd, which listens for incoming connections on TCP port 22 by default. The client side is the ssh command you run on your own machine. When you type ssh alice@203.0.113.10, three things happen in sequence:
1. Transport layer negotiation. The client opens a TCP connection to the server, and the two sides exchange version strings and cryptographic algorithms they support. They then perform a Diffie-Hellman key exchange, a mathematical handshake that lets both sides agree on a shared secret without ever transmitting that secret over the wire. That shared secret becomes a symmetric session key, and from this point on, every byte of the conversation — including a password, if you use one — is encrypted. This is why SSH is safe even for password authentication, unlike its plaintext predecessors.
2. Host authentication. Every SSH server has its own persistent host key pair, generated once when sshd is installed. The client uses the server’s public host key to verify it is really talking to the server it intended to reach, not an attacker intercepting the connection (a man-in-the-middle). The very first time you connect to a given host, your client can’t yet verify this, so it shows you the key’s fingerprint and asks you to confirm it manually. If you accept, the fingerprint is stored in ~/.ssh/known_hosts. On every future connection, the client compares the server’s key against that stored entry; if it ever changes unexpectedly, SSH refuses to connect and warns you loudly, because that’s exactly what a man-in-the-middle attack would look like.
3. User authentication. Once the encrypted channel and the server’s identity are established, you have to prove who you are. SSH supports several methods, but two matter most: password authentication, where you type your account’s password (simple, but guessable, phishable, and vulnerable to brute-force attempts) and public-key authentication, where you prove possession of a private key without ever sending that key anywhere. You generate a key pair once: a private key that stays on your machine and a public key that you copy to the server’s ~/.ssh/authorized_keys file. To log in, the server sends a challenge, your client signs it with the private key, and the server verifies that signature using the public key it already has. The private key never leaves your laptop, so even if the server is compromised, your key material is never at risk of leaking from it.
Once authenticated, SSH opens one or more channels multiplexed over that single encrypted connection: an interactive shell, a single remote command, file transfer (via scp or sftp, which are built on the SSH protocol), or port forwarding. When you run ssh host some-command, the remote shell executes some-command and its exit status is passed back through the SSH connection as ssh‘s own exit status — so $? after an ssh call tells you whether the remote command succeeded, not just whether the connection worked.
Syntax
ssh [options] user@host [command]
| Option | Meaning |
|---|---|
-p <port> |
Connect to a non-default port (server’s sshd port, not 22) |
-i <file> |
Use a specific private key file instead of the default ones |
-l <user> |
Login as this user (alternative to user@host) |
-A |
Forward your local ssh-agent so the remote host can use your keys to hop further |
-L <local:host:remote> |
Local port forwarding — tunnel a local port to a service on the far side |
-R <remote:host:local> |
Remote port forwarding — expose a local service through the remote host |
-N |
Don’t run a remote command, just set up forwarding (used with -L/-R) |
-J <jumphost> |
Connect through an intermediate “jump” host (bastion) |
-v |
Verbose/debug output — invaluable when a connection fails and you don’t know why |
-o <option> |
Set a config option on the command line, e.g. -o StrictHostKeyChecking=no |
Examples
Example 1: A basic interactive login
ssh alice@203.0.113.10
Output:
The authenticity of host '203.0.113.10 (203.0.113.10)' can't be established.
ED25519 key fingerprint is SHA256:7QvKk3n2h+examplefingerprintDoNotUseThisOne.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '203.0.113.10' (ED25519) to the list of known hosts.
alice@203.0.113.10's password:
Welcome to Ubuntu 22.04.4 LTS (GNU/Linux 5.15.0-105-generic x86_64)
alice@webserver:~$
The first line only appears on the very first connection to a new host — this is the host-key verification step described above. After typing yes, the fingerprint is trusted and saved; on every later login that warning disappears and you go straight to the password prompt (or straight into a shell, if key-based auth is set up).
Example 2: Setting up key-based login
ssh-keygen -t ed25519 -C "alice@laptop"
ssh-copy-id -i ~/.ssh/id_ed25519.pub alice@203.0.113.10
ssh alice@203.0.113.10
Output:
Generating public/private ed25519 key pair.
Enter file in which to save the key (/home/alice/.ssh/id_ed25519):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/alice/.ssh/id_ed25519
Your public key has been saved in /home/alice/.ssh/id_ed25519.pub
...
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s)...
Number of key(s) added: 1
Welcome to Ubuntu 22.04.4 LTS (GNU/Linux 5.15.0-105-generic x86_64)
alice@webserver:~$
ssh-keygen -t ed25519 generates a modern, fast, and secure key pair (Ed25519 is preferred over the older RSA for new keys). The -C flag just attaches a label/comment to the public key so you can tell keys apart later. ssh-copy-id then appends your public key to ~/.ssh/authorized_keys on the remote server — it asks for your password one last time to do so. After that, every future ssh alice@203.0.113.10 logs you in with no password prompt at all, because the server can verify your private key’s signature instead.
Example 3: Custom port, identity file, and a host alias
ssh -p 2222 -i ~/.ssh/id_ed25519 deploy@198.51.100.25
Typing that every time is tedious, so SSH lets you save connection details per host in a config file:
cat > ~/.ssh/config << 'EOF'
Host backup
HostName 198.51.100.25
User deploy
Port 2222
IdentityFile ~/.ssh/id_ed25519
EOF
ssh backup
Output:
Welcome to Ubuntu 22.04.4 LTS (GNU/Linux 5.15.0-105-generic x86_64)
deploy@backup-host:~$
The client reads ~/.ssh/config before connecting, matches the Host alias you typed, and fills in the real hostname, user, port, and key automatically. This is the standard way experienced Linux users manage dozens of servers without memorizing IPs, ports, and usernames for each one.
How It Works Step by Step
- You run
ssh(optionally with a config alias, a port, and/or an identity file). - The client opens a TCP connection to the target host on the given port (22 by default).
- Client and server negotiate encryption algorithms and perform a Diffie-Hellman key exchange to derive a shared session key — from here on, the whole session is encrypted.
- The client checks the server’s host key against
~/.ssh/known_hosts. New host → you’re prompted to confirm the fingerprint. Known, unchanged host → silent pass. Known, changed host → SSH refuses and warns of possible tampering. - User authentication runs: SSH tries public-key auth first (offering keys from
ssh-agentand default files like~/.ssh/id_ed25519), and falls back to password auth if no key works and the server allows it. - Once authenticated, a channel opens: an interactive shell, a single remote command, an SFTP session, or a port-forwarding tunnel, depending on how you invoked
ssh. - When the remote shell or command exits, its exit status flows back to the client as
ssh‘s own exit status, and the TCP connection closes.
Common Mistakes
Mistake 1: Private key permissions are too open
SSH refuses to use a private key file that other users on the system could read, because that would defeat the whole point of keeping it private.
chmod 644 ~/.ssh/id_ed25519
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: UNPROTECTED PRIVATE KEY FILE! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions 0644 for '/home/alice/.ssh/id_ed25519' are too open.
Load key "/home/alice/.ssh/id_ed25519": bad permissions
alice@203.0.113.10: Permission denied (publickey).
Fix it by restricting the key (and the containing directory) to the owner only:
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519
chmod 644 ~/.ssh/id_ed25519.pub
Mistake 2: Forgetting the port when it isn’t 22
ssh deploy@198.51.100.25
ssh: connect to host 198.51.100.25 port 22: Connection refused
If a server’s sshd has been configured to listen on a non-standard port (2222 in our earlier example), connecting without -p tries port 22 and fails. Always check which port the host actually uses, or save it in ~/.ssh/config so you never have to remember it:
ssh -p 2222 deploy@198.51.100.25
Mistake 3: Blindly clearing a “host key changed” warning
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
Host key verification failed.
Many people’s reflex is to just delete the old entry and reconnect:
ssh-keygen -R 198.51.100.25
Sometimes this is legitimate (the server was rebuilt or its host key was rotated on purpose), but running it without checking why the key changed throws away the one protection that detects a man-in-the-middle attack. Verify the new fingerprint out-of-band (ask the server admin, check your cloud provider’s console) before trusting it again:
ssh-keygen -R 198.51.100.25
ssh alice@198.51.100.25
# compare the fingerprint shown against the one confirmed with the server admin
Best Practices
- Prefer public-key authentication over passwords; it’s both more convenient and far more resistant to brute-force and credential-stuffing attacks.
- Use
ssh-agentso you type your key’s passphrase once per session instead of on every connection:eval "$(ssh-agent -s)"thenssh-add ~/.ssh/id_ed25519. - Keep
~/.sshat mode700and private keys at mode600— SSH enforces this and will refuse loose permissions. - Use
~/.ssh/confighost aliases instead of memorizing IPs, ports, users, and key paths. - On servers, set
PasswordAuthentication noandPermitRootLogin noin/etc/ssh/sshd_configonce key-based login works, so password-guessing and direct root login are no longer possible. - Never share a private key file; only the matching public key is meant to leave your machine.
- Moving
sshdto a non-standard port can reduce noise from automated scanners, but treat it as a minor convenience, not a real security control — it is not a substitute for key-based auth and a firewall. - Use
-J(jump host) instead of storing a private key on an intermediate bastion server.
Practice Exercises
- Generate a new Ed25519 key pair with a passphrase, copy the public key to a remote account with
ssh-copy-id, and confirm you can log in without typing the account password. (Hint: watch for a permissions error if you ever copy a key manually instead of usingssh-copy-id.) - Add a
Hostentry to~/.ssh/configfor a server you use often, giving it a short alias, the correct port, user, andIdentityFile. Confirmssh <alias>connects with no extra flags. - On a test server, edit
/etc/ssh/sshd_configto setPermitRootLogin noandPasswordAuthentication no, then runsudo systemctl restart ssh. Open a second terminal and confirm you can still log in with your key before closing your original session — this order matters, so you never lock yourself out.
sudo grep -E "^(PermitRootLogin|PasswordAuthentication)" /etc/ssh/sshd_config
sudo systemctl restart ssh
Summary
- SSH encrypts the entire session — connection setup, authentication, and everything you type or see — using a session key derived through a Diffie-Hellman handshake.
- Servers have a persistent host key; the client checks it against
~/.ssh/known_hostson every connection to detect man-in-the-middle attacks. - Public-key authentication is stronger than passwords because the private key never leaves your machine; only the public key is ever placed on a server.
~/.ssh/configlets you save per-host settings (hostname, user, port, identity file) under a short alias.- Private keys must be mode
600and~/.sshmust be mode700, or SSH will refuse to use them. - Never dismiss a “host key changed” warning without verifying why — it exists specifically to catch tampering.
