SSH (Secure Shell) is the de-facto standard cryptographic network protocol used to securely access, manage, and transfer data between remote Linux machines over untrusted networks. Unlike insecure legacy protocols like Telnet and FTP which transmit passwords in clear text, SSH encrypts all terminal keystrokes, sessions, and files using robust symmetric encryption and asymmetric public-key cryptography.
This tutorial provides complete hands-on mastery: connecting to remote hosts, generating modern Ed25519 SSH key pairs, configuring passwordless authentication with ssh-copy-id, hardening /etc/ssh/sshd_config against brute-force attacks, and transferring files with scp and rsync.
The basic syntax to establish a secure remote shell session is:
# Standard SSH connection on default port 22
$ ssh username@192.168.1.100
# Connect to a server listening on a custom port (e.g., 2222)
$ ssh -p 2222 username@server.veewom.com
# Execute a single command remotely and return output without opening an interactive shell
$ ssh username@192.168.1.100 "df -h && uptime"
Password authentication is inherently susceptible to dictionary attacks, keyloggers, and brute-force scanners. Public-Key Authentication uses a mathematical pair of cryptographic keys:
1. Private Key (e.g., id_ed25519): Kept strictly confidential on your client computer with chmod 600 permissions. Never share this file!
2. Public Key (e.g., id_ed25519.pub): Uploaded to the remote server and appended to ~/.ssh/authorized_keys.
# RECOMMENDED: Generate modern high-security Ed25519 key pair with comment
$ ssh-keygen -t ed25519 -C "admin@veewom.com"
# Generating legacy RSA 4096-bit key pair (if connecting to older legacy systems):
$ ssh-keygen -t rsa -b 4096 -C "admin@veewom.com"
# Default save location: ~/.ssh/id_ed25519 (Private) and ~/.ssh/id_ed25519.pub (Public)
# Always set a strong passphrase to encrypt your private key at rest!
# Automatically uploads public key and sets correct permissions on remote ~/.ssh/authorized_keys
$ ssh-copy-id -i ~/.ssh/id_ed25519.pub username@192.168.1.100
# Now login without typing user password!
$ ssh username@192.168.1.100
To protect your public-facing production servers from automated botnet attacks, edit the OpenSSH daemon configuration file /etc/ssh/sshd_config with these enterprise hardening rules:
# Open configuration file as superuser
$ sudo nano /etc/ssh/sshd_config
# ----------------- CRITICAL HARDENING DIRECTIVES -----------------
# 1. Change default port to deter automated script kiddie scans
Port 2222
# 2. Prevent direct root login (force admins to login as regular user and sudo)
PermitRootLogin no
# 3. Disable password authentication entirely (forces SSH key pair usage!)
PasswordAuthentication no
PermitEmptyPasswords no
# 4. Limit authentication attempts before disconnection
MaxAuthTries 3
# 5. Disable unused legacy authentication mechanisms
PubkeyAuthentication yes
X11Forwarding no
# 6. Restrict SSH access exclusively to specific usernames or IP subnets
AllowUsers devops admin_alex
# -----------------------------------------------------------------
# Test configuration syntax before restarting sshd!
$ sudo sshd -t
# Apply changes via systemd
$ sudo systemctl restart sshd || sudo systemctl restart ssh
SSH provides built-in mechanisms for secure remote file copying:
# Copy local file to remote server
$ scp -P 2222 database_backup.sql username@192.168.1.100:/var/backups/
# Copy directory recursively from remote server to local machine
$ scp -P 2222 -r username@192.168.1.100:/var/log/nginx/ ./remote_logs/
rsync is vastly superior to scp because it uses the delta-transfer algorithm, copying only modified chunks of files, resuming interrupted downloads, and preserving Linux permissions and timestamps:
# Sync local website directory to remote server with compression (-z), archive mode (-a), and verbose progress (-vP)
$ rsync -avzP -e 'ssh -p 2222' /var/www/html/ username@192.168.1.100:/var/www/html/
# Mirror directory exactly (delete remote files that no longer exist locally)
$ rsync -avzP --delete -e 'ssh -p 2222' ./dist/ username@192.168.1.100:/opt/app/
Instead of typing lengthy IP addresses, ports, and usernames, configure client aliases in ~/.ssh/config:
Host prod-web
HostName 203.0.113.50
User ubuntu
Port 2222
IdentityFile ~/.ssh/id_ed25519
Host db-backup
HostName 192.168.1.150
User dbadmin
IdentityFile ~/.ssh/id_rsa
Now you can effortlessly connect with a single word:
$ ssh prod-web
# Task 1: Check OpenSSH client and server versions
$ ssh -V
$ sudo systemctl status sshd || sudo systemctl status ssh
# Task 2: Verify strictly required directory and key permissions
$ chmod 700 ~/.ssh
$ chmod 600 ~/.ssh/id_ed25519
$ chmod 644 ~/.ssh/id_ed25519.pub
$ chmod 600 ~/.ssh/authorized_keys
# Task 3: View authorized public keys allowed to log into your account
$ cat ~/.ssh/authorized_keys
# Task 4: View active SSH sessions and logged-in IP addresses
$ who
$ sudo ss -tulnp | grep -E ':22|:2222'
# Task 5: Use SSH Agent to store decrypted keys in memory
$ eval "$(ssh-agent -s)"
$ ssh-add ~/.ssh/id_ed25519
This error occurs when the remote server's cryptographic host key does not match the fingerprint previously stored in your local ~/.ssh/known_hosts file. This happens if the remote server was reinstalled, had its OS wiped, or if a Man-in-the-Middle (MITM) attack is occurring. If you legitimately reinstalled the server, clear the old key with: ssh-keygen -R <hostname_or_ip>.
Add keep-alive packets to your local ~/.ssh/config:
ServerAliveInterval 60
ServerAliveCountMax 3
This sends a null packet every 60 seconds to prevent firewalls and NAT routers from dropping idle TCP connections.
Yes! Use the ssh-agent background tool. Run eval "$(ssh-agent -s)" and ssh-add ~/.ssh/id_ed25519. You enter your passphrase once, and the agent holds the decrypted key in secure memory for your active terminal session.
Automate repetitive administrative tasks and scheduled backups in Class 51: Linux Cron Jobs & Task Automation.
Continue to Class 51: Cron Jobs & Automation → Previous: ← Class 49: Systemd & Services | Full Syllabus