SSH Basics
SSH (Secure Shell) is the most essential tool for managing Linux servers. This article covers OpenSSH installation and configuration, key management, and security best practices.
Installing OpenSSH
Installing the Server
# Install OpenSSH server
sudo apt update
sudo apt install openssh-server -y
# Start and enable on boot
sudo systemctl enable --now ssh
# Check running status
sudo systemctl status ssh
# Check SSH listening port
ss -tlnp | grep sshInstalling the Client
# Ubuntu comes with the client pre-installed. If not:
sudo apt install openssh-client -ySSH Key Management

ssh-keygen -t ed25519 -C "you@host" — keep the private key local, the public key is shareable.
ssh-copy-id user@server appends the public key to the remote ~/.ssh/authorized_keys.
ssh user@server negotiates with your private key; the whole session is encrypted. Disable password login afterwards.
Key-based authentication is more secure and convenient than password authentication.
Generating a Key Pair
# Generate an Ed25519 key (recommended)
ssh-keygen -t ed25519 -C "your_email@example.com"
# Generate an RSA 4096-bit key (better compatibility)
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
# Specify a filename
ssh-keygen -t ed25519 -f ~/.ssh/id_myserver -C "myserver key"After key generation:
- Private key:
~/.ssh/id_ed25519(keep strictly secret) - Public key:
~/.ssh/id_ed25519.pub(can be distributed)
Copying the Public Key to the Server
# Method 1: Use ssh-copy-id (recommended)
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server-ip
# Method 2: Manual copy
cat ~/.ssh/id_ed25519.pub | ssh user@server-ip "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
# Method 3: If you already have the public key content, add it directly on the server
echo "ssh-ed25519 AAAA... your_email@example.com" >> ~/.ssh/authorized_keysManaging the SSH Agent
# Start the SSH Agent
eval "$(ssh-agent -s)"
# Add a key to the Agent
ssh-add ~/.ssh/id_ed25519
# List loaded keys
ssh-add -l
# Remove all loaded keys
ssh-add -DSSH Client Configuration
Edit ~/.ssh/config to simplify connections:
# ~/.ssh/config example
Host myserver
HostName 192.168.1.100
User ubuntu
Port 22
IdentityFile ~/.ssh/id_myserver
Host production
HostName prod.example.com
User deploy
Port 2222
IdentityFile ~/.ssh/id_prod
ForwardAgent yes
Host jump
HostName jump.example.com
User admin
# Connect to an internal server via a jump host
Host internal
HostName 10.0.0.50
User admin
ProxyJump jump
# Default settings for all hosts
Host *
ServerAliveInterval 60
ServerAliveCountMax 3
AddKeysToAgent yes
Compression yesUsing the configured aliases:
# Connect directly using the alias
ssh myserver
# Equivalent to
ssh -i ~/.ssh/id_myserver -p 22 ubuntu@192.168.1.100SSH Server Configuration
The main configuration file is /etc/ssh/sshd_config.
Security Hardening Configuration
# Back up the original configuration
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak
# Edit the configuration
sudo nano /etc/ssh/sshd_configRecommended security settings:
# /etc/ssh/sshd_config
# Change the default port (reduces scanning risk)
Port 2222
# Listen only on a specific address
ListenAddress 0.0.0.0
# Disable remote root login
PermitRootLogin no
# Disable password authentication (ensure key-based login is configured first)
PasswordAuthentication no
# Disable empty passwords
PermitEmptyPasswords no
# Enable public key authentication
PubkeyAuthentication yes
# Limit maximum authentication attempts
MaxAuthTries 3
# Limit maximum concurrent unauthenticated connections
MaxStartups 10:30:60
# Set login timeout
LoginGraceTime 30
# Disable X11 forwarding (usually not needed on servers)
X11Forwarding no
# Disable insecure authentication methods
KbdInteractiveAuthentication no
# Display last login information
PrintLastLog yes
# Client keepalive detection
ClientAliveInterval 300
ClientAliveCountMax 2
# Allow only specific users to log in
AllowUsers ubuntu deploy
# Or allow only specific groups
# AllowGroups sshusers# Check configuration syntax
sudo sshd -t
# Reload configuration (does not disconnect existing sessions)
sudo systemctl reload sshUsing Drop-in Configuration
Ubuntu 26.04 supports adding standalone configuration files in /etc/ssh/sshd_config.d/:
# Create a custom configuration
sudo tee /etc/ssh/sshd_config.d/hardening.conf << 'EOF'
PermitRootLogin no
PasswordAuthentication no
MaxAuthTries 3
X11Forwarding no
EOF
sudo systemctl reload sshCommon SSH Operations
Basic Connections
# Basic connection
ssh user@server-ip
# Specify a port
ssh -p 2222 user@server-ip
# Specify a key
ssh -i ~/.ssh/id_myserver user@server-ip
# Execute a remote command
ssh user@server-ip "df -h && free -h"
# Connect in verbose mode (for debugging)
ssh -v user@server-ip
ssh -vvv user@server-ip # Even more verboseFile Transfer
# SCP: Copy a file to remote
scp localfile.txt user@server-ip:/remote/path/
# SCP: Copy a file from remote
scp user@server-ip:/remote/file.txt ./local/
# SCP: Copy a directory
scp -r ./local-dir user@server-ip:/remote/path/
# SFTP: Interactive file transfer
sftp user@server-ip
# rsync: Incremental sync (recommended for large numbers of files)
rsync -avz --progress ./local-dir/ user@server-ip:/remote/dir/SSH Port Forwarding
# Local port forwarding: Map a remote service to localhost
# Access local port 8080 to reach remote MySQL
ssh -L 8080:localhost:3306 user@server-ip
# Remote port forwarding: Expose a local service to the remote machine
# The remote machine's port 9090 will forward to local port 3000
ssh -R 9090:localhost:3000 user@server-ip
# Dynamic port forwarding (SOCKS proxy)
ssh -D 1080 user@server-ip
# Run port forwarding in the background
ssh -fNL 8080:localhost:3306 user@server-ipSSH Jump Hosts
# Connect to a target server via a jump host
ssh -J jump-user@jump-host target-user@target-host
# Multi-hop jumping
ssh -J user1@jump1,user2@jump2 user@targetKey File Permission Requirements
SSH has strict file permission requirements:
# Correct permission settings
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519 # Private key
chmod 644 ~/.ssh/id_ed25519.pub # Public key
chmod 600 ~/.ssh/authorized_keys # Authorized keys
chmod 600 ~/.ssh/config # Client configurationBrute-Force Protection
Using fail2ban
# Install fail2ban
sudo apt install fail2ban -y
# Create local configuration
sudo tee /etc/fail2ban/jail.local << 'EOF'
[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 3600
findtime = 600
EOF
# Start the service
sudo systemctl enable --now fail2ban
# Check ban status
sudo fail2ban-client status sshd
# Manually unban an IP
sudo fail2ban-client set sshd unbanip 1.2.3.4Using UFW to Restrict SSH Access
# Allow SSH only from a specific subnet
sudo ufw allow from 192.168.1.0/24 to any port 22
# Rate-limit connections (max 6 connections within 30 seconds)
sudo ufw limit sshTroubleshooting
# View SSH service logs
sudo journalctl -u ssh -f
# View authentication logs
sudo tail -f /var/log/auth.log
# Test connection (verbose mode)
ssh -vvv user@server-ip
# Check server configuration syntax
sudo sshd -t
# Check the authorized_keys file
cat ~/.ssh/authorized_keys
# Check whether SELinux/AppArmor is blocking connections
sudo aa-statusCommon Issues
“Permission denied (publickey)”
# Check that the client key is correct
ssh-add -l
# Check authorized_keys permissions on the server
ls -la ~/.ssh/
ls -la ~/.ssh/authorized_keys
# Confirm public key authentication is enabled in sshd_config
grep PubkeyAuthentication /etc/ssh/sshd_config“Connection refused”
# Check whether the SSH service is running
sudo systemctl status ssh
# Check the port
ss -tlnp | grep ssh
# Check the firewall
sudo ufw status“Host key verification failed”
# Remove the old host key
ssh-keygen -R server-ip
# Or edit the known_hosts file to delete the corresponding line
nano ~/.ssh/known_hosts