,

How Can I Harden My Ubuntu Server? The Complete Security Checklist (2026)

How to Harden an Ubuntu Server Security Checklist

The moment you spin up an Ubuntu cloud server or expose a bare-metal machine to the internet, automated botnets and malicious crawlers begin hammering your public IP address within seconds. From rapid-fire SSH brute-force attempts to automated vulnerability scanners probing for exposed administrative ports, an out-of-the-box Linux deployment is perpetually in the crosshairs. If you have been wondering, “How can I harden my Ubuntu server against modern threats?”, this production-grade checklist delivers an end-to-end, defense-in-depth blueprint.

Hardening is not about installing a single magic tool; it is a systematic engineering process of reducing attack surfaces, locking down identities, enforcing strict network filtering, tuning kernel parameters, and automating security patches. Whether you are running Ubuntu 24.04 LTS (Noble Numbat) or Ubuntu 22.04 LTS (Jammy Jellyfish), this guide walks you through every critical layer. To ensure you can apply every configuration seamlessly directly from the command line, every configuration step includes exact GNU Nano terminal editor instructions and keyboard shortcuts.

Sysadmin Quick Reference: The Essential GNU Nano Cheat Sheet

On headless Ubuntu server environments, GNU Nano is the default, ubiquitous, and most dependable text editor. If you are not accustomed to command-line editors, here is the exact keyboard reference you will use throughout every step of this guide:

ActionKeyboard ShortcutDescription & Workflow
Open / Edit Filesudo nano /path/to/fileOpens the target configuration file with elevated administrative permissions.
Save ChangesCtrl + OWriteOut: Writes changes to disk. Press Enter to confirm the filename when prompted.
Exit EditorCtrl + XExits Nano. If you have unsaved edits, type Y (Yes) and hit Enter to save, or N (No) to discard.
Search / FindCtrl + WWhere Is: Type the search phrase or setting name and press Enter. Press Alt + W to jump to the next match.
Search & ReplaceCtrl + \Prompts for the search term, then the replacement string, then allows interactive confirmation.
Cut / Paste LineCtrl + K / Ctrl + UCuts the current line into the buffer (Ctrl + K); pastes it back (Ctrl + U).
Line / Column InfoCtrl + CDisplays the exact line number, column, and total character position on the bottom bar.
Go to LineAlt + G (or Ctrl + _)Prompts for a target line number and jumps straight to it.
Undo / RedoAlt + U / Alt + EUndoes or redoes the previous keystroke or block modification.

Step 1: Eliminate Direct Root Access and Create a Sudo User

Logging into a server directly as root violates the principle of least privilege. Any typo can destroy the operating system, and attackers specifically target the root account because its username is identical across all Linux servers. Your first step is creating a dedicated administrative user with sudo capabilities.

First, update your package repository index:

sudo apt update && sudo apt upgrade -y

Create a dedicated administrative user (replace sysadmin_user with your preferred username) and add them to the sudo group:

# Add new system user
sudo adduser sysadmin_user
# Grant sudo administrative privileges
sudo usermod -aG sudo sysadmin_user

Verify that your new user can execute commands via sudo before proceeding:

su - sysadmin_user
sudo whoami
# Output should return: root
exit

Step 2: OpenSSH Lockdown & Cryptographic Key Authentication

OpenSSH is the primary gateway to your server. Leaving password authentication enabled exposes your system to relentless credential-stuffing attacks. We will configure high-security Ed25519 elliptic curve public key authentication and completely disable passwords and root logins.

1. Generate an Ed25519 Key Pair on Your Local Computer

On your local laptop or workstation (not the server), open a terminal and generate a cryptographic key pair:

# Run on your local machine
ssh-keygen -t ed25519 -C "admin@techloverhd"

Copy your public key to your new administrative account on the Ubuntu server:

# Run on your local machine
ssh-copy-id -i ~/.ssh/id_ed25519.pub sysadmin_user@YOUR_SERVER_IP

2. Configure OpenSSH Daemon with Nano

Now, log into your server and edit the SSH daemon drop-in configuration file using Nano. Modern Ubuntu versions (22.04 and 24.04) support modular configuration drop-ins under /etc/ssh/sshd_config.d/, keeping your custom hardening cleanly isolated from package updates:

# Open SSH hardening configuration in Nano
sudo nano /etc/ssh/sshd_config.d/99-hardened-ssh.conf

In the Nano editor window, enter the following production hardening directives:

# Disable direct root login
PermitRootLogin no
# Enforce public key authentication exclusively
PasswordAuthentication no
PubkeyAuthentication yes
KbdInteractiveAuthentication no
ChallengeResponseAuthentication no
# Restrict authentication attempts and login timeout
MaxAuthTries 3
LoginGraceTime 30
MaxSessions 4
# Disable unneeded legacy forwarding features
X11Forwarding no
AllowAgentForwarding no
AllowTcpForwarding no
# Optional: Restrict SSH access only to specific users
AllowUsers sysadmin_user

Nano Editing Shortcuts for this step:

  • To save your changes: Press Ctrl + O, then press Enter to confirm the filename.
  • To exit Nano: Press Ctrl + X.

Crucial Safety Check: Always test the SSH daemon configuration syntax for errors before restarting the service:

# Test configuration syntax (no output means success)
sudo sshd -t
# If syntax is valid, reload the SSH daemon
sudo systemctl restart ssh

Security Warning: Do NOT close your existing terminal session! Open a brand new terminal window on your local machine and verify that you can connect with your key: ssh -i ~/.ssh/id_ed25519 sysadmin_user@YOUR_SERVER_IP. Only close the original session once you confirm the new connection succeeds.

Step 3: Network Perimeter Lockdown with UFW

Ubuntu includes UFW (Uncomplicated Firewall), an intuitive frontend for Netfilter/iptables. A hardened firewall operates under a default-deny posture: all inbound traffic is rejected unless explicitly whitelisted.

# 1. Reset UFW to clean baseline (optional, if previously modified)
sudo ufw --force reset
# 2. Set default policies: block incoming, permit outgoing
sudo ufw default deny incoming
sudo ufw default allow outgoing
# 3. Allow SSH with rate limiting (drops connections if an IP attempts >6 connections in 30 seconds)
sudo ufw limit 22/tcp comment 'Rate-limited SSH'
# 4. Allow standard web ports if running a web server
sudo ufw allow 80/tcp comment 'HTTP'
sudo ufw allow 443/tcp comment 'HTTPS'
# 5. Enable the firewall
sudo ufw enable
# 6. Verify active rules and logging
sudo ufw status verbose

Step 4: Active Intrusion Defense with Fail2ban

While disabling password authentication stops unauthorized logins, automated botnets will still spam your SSH port thousands of times an hour, wasting server bandwidth and CPU cycles. Fail2ban inspects authentication logs in real-time and dynamically writes firewall rules to ban offending IP addresses.

Install Fail2ban:

sudo apt install fail2ban -y

Create a local configuration override file. Never edit jail.conf directly, as upstream updates will overwrite your changes. Instead, create jail.local:

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

In Nano, use Ctrl + W to search for the [DEFAULT] section. Configure the global ban parameters:

[DEFAULT]
# Ban time for offending IPs (1 day)
bantime = 1d
# Lookback window for counting failed attempts (10 minutes)
findtime = 10m
# Number of failures before ban is triggered
maxretry = 3
# Use UFW as the banning mechanism
banaction = ufw
# Use systemd backend for log monitoring on modern Ubuntu
backend = systemd

Next, press Ctrl + W inside Nano and search for [sshd]. Ensure the SSH jail is enabled:

[sshd]
enabled = true
port = ssh
maxretry = 3
findtime = 10m
bantime = 1d

Nano Editing Shortcuts for this step:

  • To search for sections: Press Ctrl + W, type [sshd], and hit Enter.
  • To save your changes: Press Ctrl + O, then press Enter.
  • To exit Nano: Press Ctrl + X.

Restart Fail2ban and verify that the SSH jail is operational:

sudo systemctl restart fail2ban
sudo systemctl enable fail2ban
# Check jail status and view blocked attacker IPs
sudo fail2ban-client status sshd

Step 5: Automated Security Patching with Unattended-Upgrades

Zero-day exploits and high-severity CVEs in packages like OpenSSL, systemd, or glibc require immediate patching. Manually logging into your server every morning to run apt upgrade is neither scalable nor foolproof. Configure Ubuntu’s native unattended-upgrades utility to automatically download and apply security patches.

sudo apt install unattended-upgrades update-notifier-common -y

Open the unattended-upgrades configuration file in Nano:

sudo nano /etc/apt/apt.conf.d/50unattended-upgrades

Verify that the security repository origins are active and enable automated cleanup of superseded kernel images:

Unattended-Upgrade::Allowed-Origins {
    "${distro_id}:${distro_codename}-security";
    // "${distro_id}:${distro_codename}-updates";
};
// Automatically fix interrupted package installations
Unattended-Upgrade::AutoFixInterruptedDpkg "true";
// Perform upgrades in minimal atomic chunks
Unattended-Upgrade::MinimalSteps "true";
// Automatically remove unused kernel packages to prevent /boot exhaustion
Unattended-Upgrade::Remove-Unused-Kernel-Packages "true";
Unattended-Upgrade::Remove-Unused-Dependencies "true";
// Automatic reboot control (default: false, or true with a scheduled window)
Unattended-Upgrade::Automatic-Reboot "false";
// Unattended-Upgrade::Automatic-Reboot-Time "04:00";

Save and close with Ctrl + O → Enter → Ctrl + X.

Next, configure the update frequency by editing the auto-upgrades periodic schedule in Nano:

sudo nano /etc/apt/apt.conf.d/20auto-upgrades

Replace or add the following lines:

APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Download-Upgradeable-Packages "1";
APT::Periodic::AutocleanInterval "7";
APT::Periodic::Unattended-Upgrade "1";

Save and exit (Ctrl + O → Enter → Ctrl + X). Finally, test your configuration with a dry run:

# Run a dry-run test to verify unattended upgrades execution
sudo unattended-upgrades --dry-run --debug

Step 6: Kernel & TCP/IP Network Stack Hardening via sysctl

The Linux kernel contains extensive networking and memory controls accessible via the sysctl interface. By default, many distributions maintain permissive settings for legacy compatibility. By creating a dedicated hardening profile in /etc/sysctl.d/, we can protect the server against SYN flood exhaustion, IP spoofing, packet redirection attacks, and symlink exploits.

Create a dedicated configuration file using Nano:

sudo nano /etc/sysctl.d/99-security-hardening.conf

Paste the following hardened parameters:

# ==============================================================================
# Linux Kernel & Network Security Hardening Configuration
# ==============================================================================
# 1. IP Spoofing Protection (Strict Reverse Path Filtering)
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
# 2. SYN Flood Protection & TCP Tuning
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 4096
net.ipv4.tcp_synack_retries = 2
# 3. Disable ICMP Packet Redirects (Prevent Man-In-The-Middle Attacks)
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
net.ipv6.conf.default.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
# 4. Ignore Source-Routed Packets
net.ipv4.conf.all.accept_source_route = 0
net.ipv6.conf.all.accept_source_route = 0
# 5. Ignore ICMP Broadcast Requests (Smurf Attack Mitigation)
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.icmp_ignore_bogus_error_responses = 1
# 6. Log Martian Packets (Packets with Impossible Source Addresses)
net.ipv4.conf.all.log_martians = 1
net.ipv4.conf.default.log_martians = 1
# 7. Restrict Kernel Pointer Leaks & dmesg Access
kernel.dmesg_restrict = 1
kernel.kptr_restrict = 2
# 8. Filesystem Link Hardening (Prevent Symlink and Hardlink Exploitation)
fs.protected_hardlinks = 1
fs.protected_symlinks = 1
fs.protected_fifos = 2
fs.protected_regular = 2

Nano Editing Shortcuts for this step:

  • To write out changes: Press Ctrl + O, then press Enter.
  • To exit: Press Ctrl + X.

Apply the parameters dynamically to the running kernel without rebooting:

sudo sysctl --system

Step 7: Shared Memory Hardening (Securing /dev/shm)

On Linux systems, /dev/shm provides a temporary shared memory filesystem. Because it is world-writable by design, attackers frequently use it as a staging directory to download, compile, and execute malicious binary payloads during privilege-escalation exploits. We can neutralize this entire attack vector by mounting /dev/shm with the noexec, nosuid, and nodev mount flags.

Open the filesystem table configuration in Nano:

sudo nano /etc/fstab

Inside Nano, move your cursor to the bottom of the file (or press Alt + / to jump to the end) and append the following line:

# Secure shared memory mount
tmpfs /dev/shm tmpfs defaults,noexec,nosuid,nodev 0 0

Nano Editing Shortcuts for this step:

  • Jump to the bottom of the file: Press Alt + / (or press the down arrow key).
  • Save the file: Press Ctrl + O, followed by Enter.
  • Exit Nano: Press Ctrl + X.

Remount /dev/shm immediately and verify the new mount options:

# Remount without rebooting
sudo mount -o remount /dev/shm
# Verify that noexec and nosuid flags are active
mount | grep /dev/shm

Output confirmation: tmpfs on /dev/shm type tmpfs (rw,nosuid,nodev,noexec,relatime). Any attempt to execute a binary file located inside /dev/shm will now be immediately blocked by the kernel with a Permission denied error.

Step 8: Baseline Auditing & Vulnerability Assessment with Lynis

Once you have implemented your security layers, how do you verify that you haven’t left any blind spots? Lynis is an industry-standard, battle-tested open-source security auditing tool designed specifically for Linux/UNIX systems. It scans system configuration, kernel settings, listening ports, user permissions, and package states, providing an objective Hardening Index score along with actionable recommendations.

Install Lynis directly from the Ubuntu repositories:

sudo apt install lynis -y

Execute a comprehensive, non-destructive system audit:

sudo lynis audit system

Lynis will iterate through dozens of audit categories (System tools, Boot and services, Kernel, Memory, Users and groups, SSH, Networking, Firewalls, Web servers). At the conclusion of the test, Lynis displays a summary score (typically 75–85+ after applying this guide) and saves detailed findings to /var/log/lynis.log and /var/log/lynis-report.dat.

To inspect specific security warnings and recommendations, open the log with Nano:

sudo nano /var/log/lynis.log

In Nano, press Ctrl + W, type WARNING or SUGGESTION, and hit Enter to inspect individual findings.

The Production Server Hardening Summary Checklist

Security DomainCore MechanismTarget Config FileNano Edit CommandVerification Command
User AccessDisable direct root, enforce sudo/etc/sudoerssudo nano /etc/sudoers.d/adminsudo -l
Remote IngressEd25519 Keys, No Passwords/etc/ssh/sshd_config.d/99-hardened.confsudo nano /etc/ssh/sshd_config.d/99-hardened.confsudo sshd -t
FirewallDefault-deny inbound, rate-limit SSH/etc/ufw/ufw.confsudo ufw status verbosesudo ufw status
Intrusion DefenseFail2ban jail with UFW banaction/etc/fail2ban/jail.localsudo nano /etc/fail2ban/jail.localsudo fail2ban-client status sshd
Patch ManagementAutomated security updates/etc/apt/apt.conf.d/50unattended-upgradessudo nano /etc/apt/apt.conf.d/50unattended-upgradessudo unattended-upgrades --dry-run
Kernel TuningSYN cookies, RP filtering, no redirects/etc/sysctl.d/99-security-hardening.confsudo nano /etc/sysctl.d/99-security-hardening.confsudo sysctl --system
FilesystemMount /dev/shm with noexec,nosuid/etc/fstabsudo nano /etc/fstabmount | grep /dev/shm
Vulnerability AuditContinuous Lynis system scans/var/log/lynis.logsudo nano /var/log/lynis.logsudo lynis audit system

Final Thoughts & Next Steps

Hardening an Ubuntu server is not a one-time setup that you forget; it is an ongoing security posture. With Ed25519 SSH keys, passwordless access, a default-deny UFW firewall, Fail2ban active brute-force bans, unattended security updates, and kernel-level sysctl controls, you have closed over 95% of common external attack vectors.

If you run Docker containers on your server, be sure to check our companion guide on solving the Docker UFW bypass and implementing Cloudflare Origin Isolation to ensure Docker does not unintentionally puncture holes through your newly hardened firewall.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.