🔒 Ubuntu Server Hardening Guide

Complete Security Implementation for Non-Root Users

📋 Overview

This comprehensive guide covers essential server hardening techniques for Ubuntu Server, focusing on security measures that should be implemented as a non-root user. Server hardening is the process of securing a server by reducing its attack surface and implementing security best practices to protect against potential threats and unauthorized access.

What You'll Learn:

  • Understanding and using sudo for administrative tasks
  • Implementing SSH key authentication
  • Configuring SSH for secure remote access
  • Keeping your server updated
  • Implementing firewall policies
  • Installing and managing Fail2Ban for intrusion prevention

🛡️ Understanding Sudo: The Superuser Command

What is Sudo?

Sudo (Superuser Do) is a powerful command that allows permitted users to execute commands with elevated privileges without switching to the root user account. This approach provides an additional layer of security and accountability by making administrators more conscious of their actions and creating an audit trail of privileged operations.

💡 Key Principle:

The root user is all-powerful and any mistakes made as root can be devastating and often irreversible. A single command executed incorrectly as root can destroy an entire server. Sudo forces you to think before executing privileged commands.

How Sudo Works

User enters command with sudo prefix
System prompts for user's password
Password is cached for 5 minutes
Command executes with root privileges

Common Sudo Usage Examples

Example 1: Rebooting the Server

$ reboot Call to reboot failed: Interactive authentication required. $ sudo reboot [sudo] password for andrew: Connection closed by remote host.

Example 2: Editing Configuration Files

$ nano /etc/ssh/sshd_config [ Error writing /etc/ssh/sshd_config: Permission denied ] $ sudo nano /etc/ssh/sshd_config [sudo] password for andrew: [ File opens successfully for editing ]

Example 3: Clearing Cached Password

$ sudo -k [ Clears the cached sudo password ]

⚠️ Important:

When performing administrative tasks such as installing packages, updating the system, restarting services, or editing configuration files, you must always use sudo before the command.

When to Use Sudo

Task Type Requires Sudo? Example
Installing packages ✓ Yes sudo apt install nginx
Updating packages ✓ Yes sudo apt update
Restarting services ✓ Yes sudo systemctl restart ssh
Editing system config files ✓ Yes sudo nano /etc/ssh/sshd_config
Viewing directories ✗ No ls -la
Changing your own files ✗ No nano myfile.txt

🔑 SSH Key Authentication: Passwordless Security

Understanding SSH Key Authentication

SSH key authentication is a more secure alternative to password-based authentication. It uses a public-private key pair where the private key remains on your local machine (encrypted with a passphrase) and the public key is stored on the server. This method is significantly more secure because it eliminates the risk of password brute-force attacks and provides stronger cryptographic security.

SSH Key Architecture

Your Local PC/Mac
🔐 Private Key (Encrypted)
Never leaves your machine
Ubuntu Server
🔓 Public Key
Stored in ~/.ssh/authorized_keys

How SSH Key Authentication Works

Client initiates SSH connection
Server sends challenge encrypted with public key
Client decrypts challenge with private key
Client sends response to server
Server verifies response and grants access

⚠️ Critical Warning:

DO NOT log into your server to generate the SSH key pair! The key pair must be generated locally on your PC or Mac, not on the server. This is a fundamental security principle.

Step-by-Step Implementation Guide

Step 1: Verify You're on Your Local Machine

Before proceeding, ensure you're working on your local machine, not logged into the server. Check your terminal prompt.

$ hostname HP ← Local machine

If you see your server hostname (e.g., "2404"), you're logged into the server. Type 'exit' to log out.

Step 2: Generate the SSH Key Pair

Create a 4096-bit RSA key pair on your local machine. This command works identically on Linux, Mac, and Windows (Git Bash).

$ ssh-keygen -t rsa -b 4096 Generating public/private rsa key pair. Enter file in which to save the key (/home/andrew/.ssh/id_rsa): my_2404_server_keys ← Use underscores, no spaces Enter passphrase (empty for no passphrase): ******** ← Strong passphrase (NOT your server password) Enter same passphrase again: ******** Your identification has been saved in /home/andrew/.ssh/my_2404_server_keys Your public key has been saved in /home/andrew/.ssh/my_2404_server_keys.pub

Understanding the Passphrase:

The passphrase encrypts your private key on your local machine. It is NOT the password you use to log into your server. You'll need this passphrase each time you use the private key to connect to your server.

Step 3: Verify Key Generation

Check that both keys were created with correct permissions.

$ ls -l ~/.ssh/ -rw------- 1 andrew andrew 3389 Nov 05 10:30 my_2404_server_keys ← Private key (600) -rw-r--r-- 1 andrew andrew 742 Nov 05 10:30 my_2404_server_keys.pub ← Public key (644)

Step 4: Copy Public Key to Server

Use ssh-copy-id to securely transfer and configure your public key on the server.

$ ssh-copy-id -i ~/.ssh/my_2404_server_keys.pub [email protected] /usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s) [email protected]'s password: ← Enter your server password (one last time) Number of key(s) added: 1 Now try logging into the machine with: ssh '[email protected]' and check to make sure that only the key(s) you wanted were added.

What Just Happened:

The ssh-copy-id command connected to your server using SSH, created the necessary directories and files, added your public key to ~/.ssh/authorized_keys, and set the correct permissions automatically.

Step 5: Test SSH Key Authentication

Verify that you can log in using your private key instead of a password.

$ ssh -i ~/.ssh/my_2404_server_keys [email protected] Enter passphrase for key '/home/andrew/.ssh/my_2404_server_keys': ******** ← Enter your passphrase to unlock the private key Welcome to Ubuntu 24.04 LTS andrew@2404:~$ ← Successfully logged in!

Step 6: Disable Password Authentication

Now that SSH key authentication works, disable password-based login for enhanced security.

$ cd /etc/ssh/sshd_config.d/ $ sudo nano 50-cloud-init.conf [sudo] password for andrew:

Find and modify the following line:

# Find this line: PasswordAuthentication yes # Change it to: PasswordAuthentication no ← Change to 'no' # Save and exit: Ctrl+X, then Y, then Enter

Step 7: Restart SSH Service

Apply the configuration changes by restarting the SSH service.

$ sudo systemctl restart ssh [ SSH service restarted successfully ]

Step 8: Verify Password Authentication is Disabled

Log out and test that password authentication no longer works.

$ exit logout Connection to 192.168.1.100 closed. $ ssh -i ~/.ssh/my_2404_server_keys [email protected] Enter passphrase for key '/home/andrew/.ssh/my_2404_server_keys': ******** andrew@2404:~$ ← Only key authentication works now!

⚠️ Critical Backup Reminder:

  • DO NOT lose the private key file on your PC/Mac
  • Create a backup copy on a USB stick and store it securely
  • If you lose your private key and password authentication is disabled, you will be locked out of your server
  • Keep your passphrase in a secure password manager

⚡ Server Updates: Keeping Your System Secure

Regular system updates are crucial for maintaining server security. Updates include security patches, bug fixes, and performance improvements that protect your server from known vulnerabilities and exploits.

Understanding System Update Messages

Welcome to Ubuntu 24.04 LTS System restart required Pending kernel upgrade Running kernel version: 5.15.0-87-generic Expected kernel version: 5.15.0-91-generic

When Server Restart is Required

After creating a new server instance, many hosting providers (like Vultr, DigitalOcean, AWS) automatically run package updates. If kernel updates are applied, you'll see a "System restart required" message. Here's how to handle it:

$ sudo reboot [sudo] password for andrew: Connection to 192.168.1.100 closed by remote host. # Wait a minute or two for the server to reboot $ ssh -i ~/.ssh/my_2404_server_keys [email protected] ssh: connect to host 192.168.1.100 port 22: Connection refused # Server still rebooting, wait a bit longer... $ ssh -i ~/.ssh/my_2404_server_keys [email protected] Enter passphrase for key: Welcome to Ubuntu 24.04 LTS andrew@2404:~$ ← Restart complete, messages cleared

Regular Update Commands

Command Purpose When to Use
sudo apt update Updates package lists from repositories Before installing or upgrading packages
sudo apt upgrade Upgrades installed packages to newer versions Weekly or when security updates available
sudo apt dist-upgrade Upgrades packages and handles dependencies For major system upgrades
sudo apt autoremove Removes unnecessary packages After upgrades to clean up

Best Practice Update Routine

$ sudo apt update $ sudo apt upgrade -y $ sudo apt autoremove -y $ sudo reboot ← Only if kernel was updated

🔥 Firewall Configuration: Closing Unused Ports

Implementing a firewall policy is one of the most crucial steps in server hardening. A properly configured firewall acts as a barrier between your server and potential threats, allowing only necessary traffic while blocking everything else.

💡 Firewall Principle:

Deny all incoming traffic by default, then explicitly allow only the services and ports you need. This "default deny" approach minimizes your attack surface.

Common Ports and Services

Port Service Protocol Should Allow?
22 SSH TCP ✓ Yes (for remote access)
80 HTTP TCP ✓ If running web server
443 HTTPS TCP ✓ If running web server
3306 MySQL TCP ✗ Block (internal only)
5432 PostgreSQL TCP ✗ Block (internal only)

🛡️ Fail2Ban: Complete Installation and Management Guide

Fail2Ban is an intrusion prevention framework that protects your server from brute-force attacks by monitoring log files and automatically blocking IP addresses that show malicious behavior, such as too many failed login attempts.

How Fail2Ban Works

Fail2Ban monitors log files (e.g., /var/log/auth.log)
Detects failed login attempts or suspicious patterns
Counts failures from each IP address
If threshold exceeded, adds firewall rule to block IP
IP remains blocked for configured ban time
After ban expires, IP is automatically unblocked

Protection Benefits:

  • Automatically blocks brute-force SSH login attempts
  • Protects web applications from DDoS and authentication attacks
  • Reduces server load from malicious traffic
  • Creates detailed logs of attack attempts
  • Configurable rules for different services

Installing Fail2Ban

Step 1: Install Fail2Ban Package

$ sudo apt update $ sudo apt install fail2ban -y

Step 2: Verify Installation and Service Status

$ sudo systemctl status fail2ban ● fail2ban.service - Fail2Ban Service Loaded: loaded (/lib/systemd/system/fail2ban.service; enabled) Active: active (running) since...

Step 3: Enable Fail2Ban to Start on Boot

$ sudo systemctl enable fail2ban

Configuring Fail2Ban

Understanding Configuration Files

Fail2Ban uses two main configuration directories:

Best Practice:

Never edit jail.conf directly. Always create a jail.local file for your custom settings, as jail.conf will be overwritten during updates.

Step 4: Create Custom Configuration File

$ sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local $ sudo nano /etc/fail2ban/jail.local

Step 5: Configure SSH Protection (sshd jail)

Find the [sshd] section in jail.local and configure it:

[sshd] enabled = true port = ssh filter = sshd logpath = /var/log/auth.log maxretry = 5 findtime = 10m bantime = 1h

Configuration Explained:

  • enabled = true - Activates the SSH protection jail
  • port = ssh - Monitors SSH port (22 by default)
  • filter = sshd - Uses the sshd filter rules
  • logpath = /var/log/auth.log - Location of SSH authentication logs
  • maxretry = 5 - Block IP after 5 failed attempts
  • findtime = 10m - Count failures within 10 minute window
  • bantime = 1h - Ban IP for 1 hour (use bantime = -1 for permanent ban)

Step 6: Configure Global Default Settings

At the top of jail.local, find the [DEFAULT] section and adjust:

[DEFAULT] # Ban duration (can be overridden per jail) bantime = 1h # Time window to count failures findtime = 10m # Number of failures before ban maxretry = 5 # Destination email for notifications (optional) destemail = [email protected] # Action to take when banning action = %(action_mwl)s

Action Options:

  • %(action_)s - Ban only
  • %(action_mw)s - Ban and send email with whois report
  • %(action_mwl)s - Ban and send email with whois report and log lines

Save and exit: Ctrl+X, then Y, then Enter

Step 7: Restart Fail2Ban Service

$ sudo systemctl restart fail2ban

Step 8: Verify Fail2Ban is Running

$ sudo systemctl status fail2ban $ sudo fail2ban-client status Status |- Number of jails: 1 `- Jail list: sshd

Managing Fail2Ban

Check Status of Specific Jail

$ sudo fail2ban-client status sshd Status for the jail: sshd |- Filter | |- Currently failed: 2 | |- Total failed: 15 | `- File list: /var/log/auth.log `- Actions |- Currently banned: 1 |- Total banned: 3 `- Banned IP list: 203.0.113.42

View All Active Jails

$ sudo fail2ban-client status

Manually Ban an IP Address

$ sudo fail2ban-client set sshd banip 203.0.113.42

Manually Unban an IP Address

$ sudo fail2ban-client set sshd unbanip 203.0.113.42

View Banned IP Addresses

$ sudo fail2ban-client get sshd banned

Or using iptables:

$ sudo iptables -L -n | grep -i reject

Check Fail2Ban Logs

$ sudo tail -f /var/log/fail2ban.log 2026-03-25 10:30:15,123 fail2ban.actions [1234]: NOTICE [sshd] Ban 203.0.113.42 2026-03-25 11:30:15,456 fail2ban.actions [1234]: NOTICE [sshd] Unban 203.0.113.42

View Failed Login Attempts

$ sudo grep "Failed password" /var/log/auth.log | tail -20

Advanced Configuration

Protecting Web Services (Optional)

If you're running a web server, add protection for Nginx or Apache:

For Nginx:
$ sudo nano /etc/fail2ban/jail.local

Add these sections:

[nginx-http-auth] enabled = true port = http,https filter = nginx-http-auth logpath = /var/log/nginx/error.log maxretry = 5 findtime = 10m bantime = 1h [nginx-noscript] enabled = true port = http,https filter = nginx-noscript logpath = /var/log/nginx/access.log maxretry = 6 findtime = 10m bantime = 1h

Whitelist Your Own IP Address

To prevent accidentally banning yourself:

$ sudo nano /etc/fail2ban/jail.local

In the [DEFAULT] section, add:

[DEFAULT] ignoreip = 127.0.0.1/8 ::1 192.168.1.50

Replace 192.168.1.50 with your actual IP address.

Increase Ban Time for Repeat Offenders

Create a recidive jail that bans repeat offenders for longer:

[recidive] enabled = true filter = recidive logpath = /var/log/fail2ban.log action = %(action_mwl)s bantime = 1w findtime = 1d maxretry = 3

Testing Fail2Ban

Simulate Failed Login Attempts

From another machine (or use a VM), attempt multiple failed SSH logins:

$ ssh wronguser@your-server-ip # Enter wrong password 5+ times

Verify the Ban

On your server:

$ sudo fail2ban-client status sshd $ sudo tail -20 /var/log/fail2ban.log

You should see the attacker's IP banned.

Troubleshooting

Fail2Ban Not Starting

# Check for configuration errors $ sudo fail2ban-client -d # View detailed logs $ sudo journalctl -u fail2ban -n 50

Fail2Ban Not Banning

# Test the filter manually $ sudo fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf # Check if log file path is correct $ ls -la /var/log/auth.log

Accidentally Banned Your Own IP

From the server console (not SSH):

$ sudo fail2ban-client set sshd unbanip YOUR-IP-ADDRESS

Or disable Fail2Ban temporarily:

$ sudo systemctl stop fail2ban # Fix the issue, then restart $ sudo systemctl start fail2ban

Quick Reference Commands

# Installation $ sudo apt install fail2ban -y # Service management $ sudo systemctl start fail2ban $ sudo systemctl stop fail2ban $ sudo systemctl restart fail2ban $ sudo systemctl status fail2ban # Monitoring $ sudo fail2ban-client status $ sudo fail2ban-client status sshd $ sudo tail -f /var/log/fail2ban.log # Manual IP management $ sudo fail2ban-client set sshd banip IP_ADDRESS $ sudo fail2ban-client set sshd unbanip IP_ADDRESS # View banned IPs $ sudo fail2ban-client get sshd banned $ sudo iptables -L -n | grep REJECT # Configuration test $ sudo fail2ban-client -d $ sudo fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf

📝 Summary and Best Practices

Security Checklist

Security Measure Status Priority
Created non-root user with sudo privileges ✓ Complete Critical
Implemented SSH key authentication ✓ Complete Critical
Disabled password authentication ✓ Complete Critical
Applied system updates ✓ Complete High
Configure firewall (UFW) Pending Critical
Install and configure Fail2Ban ✓ Complete High

Key Takeaways

⚠️ Critical Reminders:

  • Never lose your SSH private key - store it securely with backups
  • Keep your passphrase in a password manager
  • Always verify you're on your local machine before generating keys
  • Test SSH key authentication before disabling password login
  • Document your security configurations for disaster recovery
  • Backup Fail2Ban configuration before making changes

Quick Reference Commands Summary

# Generate SSH key pair (LOCAL machine) $ ssh-keygen -t rsa -b 4096 # Copy public key to server (LOCAL machine) $ ssh-copy-id -i ~/.ssh/keyfile.pub user@server # Login with SSH key (LOCAL machine) $ ssh -i ~/.ssh/keyfile user@server # Update system (ON server) $ sudo apt update && sudo apt upgrade -y # Restart SSH service (ON server) $ sudo systemctl restart ssh # Clear cached sudo password (ON server) $ sudo -k # Install Fail2Ban $ sudo apt install fail2ban -y # Check Fail2Ban status $ sudo fail2ban-client status sshd

🎯 Next Steps

With SSH key authentication successfully implemented, password authentication disabled, and Fail2Ban configured, your server is significantly more secure. The next phase of hardening will focus on configuring the firewall (UFW) to create a comprehensive security posture for your Ubuntu server.

Remember:

Security is not a one-time setup but an ongoing process. Regularly review logs, update systems, and stay informed about security best practices and emerging threats.