Skip to main content

SSH Server Configuration on Ubuntu

Trademark Notice

OpenSSH® is a trademark of the OpenBSD project. All trademarks are used for representation purposes only. This content is for educational purposes only.

Complete guide to configuring and securing SSH server on Ubuntu.

Installation

# Update package list
sudo apt update

# Install OpenSSH Server
sudo apt install -y openssh-server

# Check status
sudo systemctl status sshd

# Enable at boot
sudo systemctl enable sshd

# Check version
ssh -V

Basic Configuration

Main Configuration File

# Backup original config
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.backup

# Edit SSH configuration
sudo nano /etc/ssh/sshd_config
# Port and Protocol
Port 22
Protocol 2

# Listen addresses
ListenAddress 0.0.0.0
ListenAddress ::

# Host keys
HostKey /etc/ssh/ssh_host_rsa_key
HostKey /etc/ssh/ssh_host_ecdsa_key
HostKey /etc/ssh/ssh_host_ed25519_key

# Logging
SyslogFacility AUTH
LogLevel INFO

# Authentication
LoginGraceTime 60
PermitRootLogin no
StrictModes yes
MaxAuthTries 3
MaxSessions 10

PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys

PasswordAuthentication yes
PermitEmptyPasswords no

ChallengeResponseAuthentication no

# Kerberos
KerberosAuthentication no

# GSSAPI
GSSAPIAuthentication no

# PAM
UsePAM yes

# X11 Forwarding
X11Forwarding no

# Misc
PrintMotd no
PrintLastLog yes
TCPKeepAlive yes
AcceptEnv LANG LC_*
Subsystem sftp /usr/lib/openssh/sftp-server

# Client timeout
ClientAliveInterval 300
ClientAliveCountMax 2
# Test configuration
sudo sshd -t

# Restart SSH
sudo systemctl restart sshd

SSH Key Authentication

Generate SSH Keys (Client Side)

# Generate Ed25519 key (recommended)
ssh-keygen -t ed25519 -C "your_email@example.com"

# Generate RSA key (4096-bit)
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"

# Specify custom filename
ssh-keygen -t ed25519 -f ~/.ssh/custom_key -C "custom_key"

# Generate key with passphrase
ssh-keygen -t ed25519 -C "your_email@example.com" -N "your_passphrase"

Copy Public Key to Server

# Method 1: Using ssh-copy-id
ssh-copy-id username@server_ip

# Method 2: Specify key file
ssh-copy-id -i ~/.ssh/custom_key.pub username@server_ip

# Method 3: Manual copy
cat ~/.ssh/id_ed25519.pub | ssh username@server_ip "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"

# Method 4: Manual setup on server
mkdir -p ~/.ssh
chmod 700 ~/.ssh
nano ~/.ssh/authorized_keys
# Paste public key
chmod 600 ~/.ssh/authorized_keys

SSH Config File (Client Side)

# Create/edit SSH config
nano ~/.ssh/config
# Default settings
Host *
ServerAliveInterval 60
ServerAliveCountMax 3

# Production server
Host prod
HostName 192.168.1.100
User admin
Port 22
IdentityFile ~/.ssh/prod_key
ForwardAgent yes

# Development server
Host dev
HostName 192.168.1.200
User developer
Port 2222
IdentityFile ~/.ssh/dev_key

# Jump host configuration
Host internal
HostName 10.0.0.50
User admin
ProxyJump bastion

Host bastion
HostName bastion.example.com
User admin
Port 22
# Set correct permissions
chmod 600 ~/.ssh/config

# Connect using alias
ssh prod
ssh dev

Security Hardening

Disable Password Authentication

# Edit SSH config
sudo nano /etc/ssh/sshd_config
PasswordAuthentication no
ChallengeResponseAuthentication no
UsePAM no

Change Default Port

Port 2222
# Update firewall
sudo ufw allow 2222/tcp
sudo ufw delete allow 22/tcp

# Restart SSH
sudo systemctl restart sshd

Limit Users and Groups

# Allow specific users only
AllowUsers admin user1 user2

# Allow specific groups
AllowGroups ssh-users administrators

# Deny specific users
DenyUsers baduser attacker

# Deny specific groups
DenyGroups banned
# Create SSH users group
sudo groupadd ssh-users
sudo usermod -aG ssh-users admin

IP Whitelisting

# Allow from specific IPs
Match Address 192.168.1.0/24
PasswordAuthentication yes

Match Address 10.0.0.0/8
PermitRootLogin yes

# Default deny
Match Address *
PermitRootLogin no
PasswordAuthentication no

Two-Factor Authentication (2FA)

# Install Google Authenticator
sudo apt install -y libpam-google-authenticator

# Configure for user
google-authenticator

# Follow prompts:
# - Do you want authentication tokens to be time-based: yes
# - Update .google_authenticator file: yes
# - Disallow multiple uses: yes
# - Increase time skew: no
# - Enable rate-limiting: yes

# Configure PAM
sudo nano /etc/pam.d/sshd

Add at the top:

auth required pam_google_authenticator.so
# Configure SSH
sudo nano /etc/ssh/sshd_config
ChallengeResponseAuthentication yes
AuthenticationMethods publickey,keyboard-interactive
# Restart SSH
sudo systemctl restart sshd

Fail2Ban Protection

# Install Fail2Ban
sudo apt install -y fail2ban

# Create local config
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
sudo nano /etc/fail2ban/jail.local
[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 3
destemail = admin@example.com
sendername = Fail2Ban
action = %(action_mwl)s

[sshd]
enabled = true
port = 22
logpath = /var/log/auth.log
maxretry = 3
bantime = 3600
# Start Fail2Ban
sudo systemctl start fail2ban
sudo systemctl enable fail2ban

# Check status
sudo fail2ban-client status
sudo fail2ban-client status sshd

# Unban IP
sudo fail2ban-client set sshd unbanip 192.168.1.100

Advanced Configuration

SSH Tunneling (Port Forwarding)

# Local port forwarding
ssh -L 8080:localhost:80 user@server

# Remote port forwarding
ssh -R 8080:localhost:3000 user@server

# Dynamic port forwarding (SOCKS proxy)
ssh -D 9090 user@server

# Keep tunnel alive
ssh -L 8080:localhost:80 -N -f user@server

SSH Jump Host (Bastion)

# Direct command
ssh -J bastion_user@bastion.example.com target_user@target_server

# Multiple hops
ssh -J user1@hop1,user2@hop2 user3@target

# Using ProxyJump in config
Host target
HostName 10.0.0.100
User admin
ProxyJump bastion.example.com

X11 Forwarding

# Enable on server
sudo nano /etc/ssh/sshd_config
X11Forwarding yes
X11DisplayOffset 10
X11UseLocalhost yes
# Connect with X11 forwarding
ssh -X user@server
ssh -Y user@server # Trusted forwarding

# Test
xclock
xeyes

SFTP Configuration

# Create SFTP-only group
sudo groupadd sftponly

# Edit SSH config
sudo nano /etc/ssh/sshd_config
# Override default sftp subsystem
Subsystem sftp internal-sftp

# SFTP-only users
Match Group sftponly
ChrootDirectory /home/%u
ForceCommand internal-sftp
AllowTcpForwarding no
X11Forwarding no
# Create SFTP user
sudo useradd -m -G sftponly -s /bin/false sftpuser
sudo passwd sftpuser

# Set up directory structure
sudo mkdir -p /home/sftpuser/uploads
sudo chown root:root /home/sftpuser
sudo chmod 755 /home/sftpuser
sudo chown sftpuser:sftpuser /home/sftpuser/uploads

# Restart SSH
sudo systemctl restart sshd

SSH Agent Forwarding

# On client
eval $(ssh-agent)
ssh-add ~/.ssh/id_ed25519

# Connect with agent forwarding
ssh -A user@server

# Or in config
Host server
ForwardAgent yes

Monitoring and Logging

View SSH Logs

# Real-time auth log
sudo tail -f /var/log/auth.log

# Failed login attempts
sudo grep "Failed password" /var/log/auth.log

# Successful logins
sudo grep "Accepted" /var/log/auth.log

# Active SSH sessions
who
w

# Show last logins
last
lastlog

# Failed login attempts
lastb

Active Connections

# Show active SSH connections
sudo netstat -tnpa | grep 'ESTABLISHED.*sshd'

# Or using ss
sudo ss -o state established '( dport = :22 or sport = :22 )'

# List connected users
who
users

SSH Logging with Auditd

# Install auditd
sudo apt install -y auditd

# Add SSH monitoring rules
sudo nano /etc/audit/rules.d/ssh.rules
-w /etc/ssh/sshd_config -p wa -k sshd_config
-w /var/log/auth.log -p wa -k auth_log
# Reload rules
sudo augenrules --load

# Search audit logs
sudo ausearch -k sshd_config
sudo ausearch -k auth_log

Backup and Migration

Backup SSH Configuration

# Backup SSH directory
sudo tar -czf ssh-backup-$(date +%Y%m%d).tar.gz /etc/ssh/

# Backup authorized_keys
tar -czf authorized-keys-backup.tar.gz ~/.ssh/authorized_keys

Restore Configuration

# Restore SSH config
sudo tar -xzf ssh-backup.tar.gz -C /

# Restart SSH
sudo systemctl restart sshd

Troubleshooting

Debug SSH Connection

# Verbose client output
ssh -v user@server
ssh -vv user@server
ssh -vvv user@server

# Test SSH daemon config
sudo sshd -t

# Run SSH in debug mode
sudo /usr/sbin/sshd -d

# Check SSH service
sudo systemctl status sshd
sudo journalctl -u sshd -f

Common Issues

Permission denied (publickey):

# Check key permissions
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
chmod 600 ~/.ssh/id_ed25519

# Verify key is added
ssh-add -l

# Check server logs
sudo tail -f /var/log/auth.log

Connection timeout:

# Check firewall
sudo ufw status
sudo iptables -L -n

# Check SSH is listening
sudo netstat -tlnp | grep :22
sudo ss -tlnp | grep :22

Too many authentication failures:

# Clear SSH agent
ssh-add -D

# Use specific key
ssh -i ~/.ssh/specific_key user@server

# Reduce identities in config
IdentitiesOnly yes

Best Practices

  1. Disable Root Login: Never allow root SSH access
  2. Key-Based Auth: Use SSH keys instead of passwords
  3. Change Port: Use non-standard port to reduce automated attacks
  4. 2FA: Enable two-factor authentication
  5. Fail2Ban: Install and configure Fail2Ban
  6. Regular Updates: Keep SSH and system updated
  7. Strong Keys: Use Ed25519 or 4096-bit RSA keys
  8. Limit Access: Use AllowUsers or AllowGroups
  9. Monitor Logs: Regularly review auth.log
  10. Firewall: Restrict SSH access by IP when possible

Resources