SSH: Secure Remote Access
SSH is how you reach every server, container and cloud VM: one encrypted channel for a shell, file transfer and port forwarding. Learn to connect with ssh user@host, generate and install key pairs with ssh-keygen and ssh-copy-id, understand the permissions that keep authorized_keys safe, use scp and the SSH config file, and harden sshd_config so password logins and root logins are locked down.
What is this?
SSH (Secure Shell) is the encrypted protocol you use to get a command line on a remote machine, transfer files and forward ports. Instead of sending your password over the network in plain text like Telnet, SSH encrypts everything between your client and the server, and it can authenticate you with cryptographic keys instead of passwords entirely.
Why do you need it?
- SSH is the universal remote control for servers: clouds, VMs, containers, switches and appliances all expose SSH as the admin interface.
- Everything is encrypted — the session, the password prompt and file transfers — so credentials are never readable on the wire.
- Key-based authentication removes passwords from the equation: servers that disable passwords cannot be brute-forced with login guessing.
- One key pair works across all your machines: add the public key to a new server and you log in instantly, everywhere.
- Beyond shells, SSH powers git over SSH, Ansible connections, SCP/rsync file transfer and encrypted tunnels to internal services.
Mental model
A passport-controlled border crossing
SSH authentication
Your private key is the passport you never show — the border agent only ever sees the ID page (public key). You prove you hold the passport by signing a challenge the agent sends; the agent proves the country is real by showing its own credential (the host key). Your passport never leaves your pocket, and the whole exchange happens in an encrypted tunnel.
How it works
- The client connects to port 22 and the server sends its host key; the client verifies it against its known_hosts file (first contact is "trust on first use").
- Both sides agree on a session key using public-key cryptography (key exchange), so all further traffic is encrypted without ever sending secrets over the wire.
- For password login the password is sent inside the encrypted session. For key login the server sends a challenge the client must sign with your private key.
- The server checks the matching public key against ~/.ssh/authorized_keys of the user you are logging in as; matching the signature grants access.
- The server enforces file permissions: ~/.ssh must not be group/world-writable and authorized_keys must be private, or SSH refuses to trust the keys.
How an SSH key-authentication session is established.
Key commands
ssh deploy@myserverOpen an encrypted shell session as the deploy user on myserver.
Welcome to Ubuntu 24.04.1 LTS (GNU/Linux 6.8.0-45-generic x86_64)
ssh-keygen -t ed25519 -C "you@example.com"Generate an Ed25519 key pair — modern, fast and the recommended default.
Generating public/private ed25519 key pair. Your identification has been saved in /home/you/.ssh/id_ed25519 Your public key has been saved in /home/you/.ssh/id_ed25519.pub
ssh-copy-id deploy@myserverInstall your public key into the server's authorized_keys file.
Number of key(s) added: 1 Now try logging into the machine, with: "ssh deploy@myserver"
ls -la ~/.sshList your key pair and the client config file.
drwx------ 2 you you 4096 Aug 8 10:30 . -rw------- 1 you you 464 Aug 8 10:30 id_ed25519 -rw-r--r-- 1 you you 92 Aug 8 10:30 id_ed25519.pub
scp app.jar deploy@myserver:/opt/app/Securely copy a file over the SSH connection.
app.jar 100% 12MB 6.0MB/s 00:02
grep -E "^(PasswordAuthentication|PermitRootLogin)" /etc/ssh/sshd_configCheck the two hardening settings that matter most.
PasswordAuthentication no PermitRootLogin no
Examples
Go from password login to passwordless key login on a server
# On your laptop: generate a modern key pair (no passphrase)ssh-keygen -t ed25519 -C "you@example.com" -f ~/.ssh/id_ed25519 -N "" # Install your public key on the server (asks for the password once)ssh-copy-id -i ~/.ssh/id_ed25519.pub deploy@myserver # Now connect — no password prompt, straight into a shellssh deploy@myserver # Hardening: turn off password auth on the serversudo sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_configsudo systemctl restart sshssh-keygen creates a private key that stays on your laptop and a public key that is safe to share. ssh-copy-id appends that public key to ~/.ssh/authorized_keys on the server. From then on the server can prove you hold the private key by asking you to sign a challenge — no password needed — and disabling PasswordAuthentication removes the login-guessing attack surface entirely.
In production
- Server administration: every cloud VM, on-prem box and network appliance is managed through an SSH session.
- Automation transport: Ansible runs over SSH by default, and CI/CD pipelines push and run code via SSH to deploy targets.
- Secure file movement: scp and rsync over SSH replace FTP for transfers, and git pushes to GitHub/GitLab use SSH keys.
- Tunnels to private services: ssh -L 8080:db.internal:3306 server gives you an encrypted path to a database behind a firewall.
- Secrets at rest and in motion: keys in CI secret stores and agent-based key management keep credentials off disk and out of repositories.
Best practices
- Generate Ed25519 keys, never reuse a passphrase-locked key in scripts — use ssh-agent for interactive sessions and dedicated keys per service.
- Keep your private key private: 600 permissions on id_ed25519, never commit it, never copy it to another machine.
- Set strict permissions on the server: chmod 700 ~/.ssh and chmod 600 ~/.ssh/authorized_keys.
- Use one key pair per purpose (personal, deploy, CI) so you can revoke one without touching the others.
- Harden sshd_config: PasswordAuthentication no, PermitRootLogin no, and prefer key-only access on production.
- Put per-host options in ~/.ssh/config (user, port, key) instead of long command lines — and test config changes with ssh -G host.
Common mistakes
- Copying the private key (id_ed25519) instead of the public key (id_ed25519.pub) to a server.
- Leaving ~/.ssh or authorized_keys world-readable, which makes sshd silently ignore the keys.
- Forgetting the username: ssh server instead of ssh user@server, then being puzzled by a login as the wrong account.
- Editing sshd_config and not restarting sshd, so the hardening never takes effect.
- Adding a key without a passphrase to a shared or CI machine, then leaking the private key.
- Ignoring the host key warning after a server rebuild and blindly accepting a changed key without verifying.
Common issues
Permission denied (publickey)The server found no matching public key in authorized_keys, or it rejected the key because of wrong file permissions.
Re-run ssh-copy-id, confirm the key is in ~/.ssh/authorized_keys on the server, and verify ~/.ssh is 700 and authorized_keys is 600.
WARNING: REMOTE HOST IDENTIFICATION HAS CHANGEDThe server's host key differs from the one stored in ~/.ssh/known_hosts — usually a reinstall or IP reuse, possibly a man-in-the-middle.
Verify the host is legitimately yours (out-of-band), then remove the stale entry with ssh-keygen -R host and reconnect.
ssh: connect to host port 22: Connection refusedsshd is not installed or running, the service failed to start, or a firewall is dropping the port.
On the server run systemctl status ssh, check sshd -t for config errors, and confirm the port is open with ss -tlnp. Verify firewall rules allow inbound 22.
Bad owner or permissions on ~/.ssh/configThe SSH client refuses to use a config file it considers insecure.
chmod 600 ~/.ssh/config (and ensure ~/.ssh is 700).
Debugging tips
- Run ssh -v user@host (or -vvv) to see exactly which key is tried, which auth method fails and where the connection stalls.
- Check server logs for rejected attempts: journalctl -u ssh or tail -f /var/log/auth.log | grep sshd.
- Verify permissions with ls -ld ~/.ssh ~/.ssh/authorized_keys and stat -c "%a %n" on the server.
- Test each hop: ping the host, then nc -vz host 22, then ssh -v — isolate network vs authentication.
- Dry-run config parsing on the server with sshd -t before restarting sshd after any config edit.
Interview checkpoint
Hands-on lab
- Install and start an OpenSSH server in a container
- Generate an Ed25519 key pair and inspect ~/.ssh
- Install your public key with ssh-copy-id and log in without a password
- Copy files with scp and harden sshd_config to key-only access
Set up passwordless SSH to localhost and harden it
15 min · intermediate
Quick quiz
On the server, where must your public key be placed to allow key-based login?
Which permissions should the ~/.ssh directory have on the server?
Your private SSH key should never leave your machine.
Which command installs your public key on a remote server automatically?
Which sshd_config setting disables password-based logins?
You set up keys and disabled password auth. A new teammate reports they still get "Permission denied (publickey)". What is the most likely cause?
Cheat sheet
ssh user@hostConnect to a remote hostssh -p 2222 user@hostConnect on a non-default portssh user@host "cmd"Run one remote command and exitssh -i ~/.ssh/mykey user@hostUse a specific private keyexitClose the remote sessionssh-keygen -t ed25519 -C "you@example.com"Generate a modern key paircat ~/.ssh/id_ed25519.pubView your public keyssh-copy-id user@hostInstall your public key on the serverssh-add ~/.ssh/id_ed25519Load a key into the agentscp file.txt user@host:/tmp/Copy a file to the serverscp -r logs/ user@host:/var/logs/Copy a directory recursivelyscp user@host:/etc/app.conf .Copy a file from the serverscp -P 2222 file user@host:/tmpscp on a non-default port~/.ssh/authorized_keysPublic keys allowed to log inchmod 700 ~/.sshDirectory must be privatechmod 600 ~/.ssh/authorized_keysKey file must be private/etc/ssh/sshd_configServer settings (needs restart)ssh-keygen -R hostForget a host key after it changedPasswordAuthentication noDisable password loginsPermitRootLogin noDisable root logins entirelyPort 2222Move SSH off the default portPermitEmptyPasswords noNever allow empty passwordsservice ssh restartApply sshd_config changes