Knowledge Base
Infrastructure
2026-01-19
16 min

Hardening Ubuntu Server 24.04 LTS for Production Use

Comprehensive hardening guide for Ubuntu Server 24.04 LTS. Covers system updates, SSH hardening, UFW firewall, service reduction, Ubuntu-specific configurations (snapd, cloud-init, systemd-resolved, AppArmor), kernel hardening, intrusion prevention, and audit logging.

Ubuntu Server
Linux Hardening
Security
SSH
UFW
Fail2Ban
AppArmor
auditd
systemd
Infrastructure
Production

This entry documents a comprehensive, server-grade hardening process for Ubuntu Server 24.04 LTS. The goal is to transform a default installation into a minimal, secure, and production-ready Linux server suitable for infrastructure workloads.

The steps below are structured as incremental phases, each tightening the system posture while maintaining clarity and maintainability. This guide builds on similar hardening principles but addresses Ubuntu-specific services, configurations, and behaviors.

🧭Phase 0: Baseline & Assumptions

  • Fresh installation of Ubuntu Server 24.04 LTS (Noble Numbat)
  • 64-bit (x86_64 or aarch64) architecture
  • Static IP configured (or reserved DHCP lease)
  • SSH access available using an existing ed25519 keypair
  • No desktop environment; server installation only
  • Initial user account created during installation with sudo privileges

âŦ†ī¸Phase 1: System Update & Preparation

The first step on any new Ubuntu Server installation is to ensure the system is fully patched and up to date. This includes security patches, kernel updates, and any post-installation fixes released by Canonical.

system-update.sh
# Refresh package metadata from Ubuntu repositories
sudo apt update

# Perform a full system upgrade
sudo apt full-upgrade -y

# Remove any orphaned packages
sudo apt autoremove --purge -y

# Reboot to apply new kernel if updated
sudo reboot

After rebooting, verify the system state:

verify-system.sh
# Check kernel version
uname -a

# Verify Ubuntu release
lsb_release -a

# Check for any remaining upgrades
apt list --upgradable
â„šī¸
Ubuntu Server 24.04 LTS is supported until April 2029 (standard support) and April 2034 with Extended Security Maintenance (ESM).

At this stage, the system is baseline-clean: fully patched, rebooted, and ready for security hardening.

🔐Phase 2: SSH Hardening

SSH is the primary remote access method for headless servers. Hardening SSH eliminates entire classes of attacks including brute-force attempts, credential stuffing, and protocol exploits.

The hardening principles applied:

  • Key-based authentication only — passwords disabled entirely.
  • Non-standard SSH port — reduces automated scanning and log noise.
  • No root login — privilege escalation must be explicit via sudo.
  • Minimal protocol features — forwarding and tunneling disabled by default.

Edit /etc/ssh/sshd_config to apply the following configuration:

sshd_config
# Custom SSH port
Port 9110
Protocol 2

# Authentication hardening
PermitRootLogin no
PasswordAuthentication no
ChallengeResponseAuthentication no
PubkeyAuthentication yes
AuthenticationMethods publickey

# Reduce attack surface
X11Forwarding no
AllowTcpForwarding no
AllowAgentForwarding no
PermitTunnel no

# Connection limits
MaxAuthTries 3
LoginGraceTime 30
ClientAliveInterval 300
ClientAliveCountMax 2

# Use strong cryptography
KexAlgorithms curve25519-sha256,[email protected],diffie-hellman-group-exchange-sha256
Ciphers [email protected],[email protected],[email protected],aes256-ctr,aes192-ctr,aes128-ctr
MACs [email protected],[email protected],hmac-sha2-512,hmac-sha2-256

After updating the configuration, reload SSH without disconnecting active sessions:

reload-ssh.sh
sudo systemctl reload ssh
âš ī¸
Always keep an existing SSH session open while testing SSH changes. Only close the session once successful login on the new port has been verified from a separate terminal.

In some cases, a simple reload may not apply all configuration changes (particularly port changes). If the new configuration doesn't take effect, restart the SSH service:

restart-ssh.sh
# Restart SSH service (may drop current session)
sudo systemctl restart ssh

# Verify the new port is listening
sudo ss -tlnp | grep :9110

Test the new configuration thoroughly from a separate terminal before closing your original session. Attempt to SSH to the new port and verify successful authentication.

âš ī¸
Reboot as a last resort only. If you reboot with a misconfigured SSH setup, you may lock yourself out of the system entirely. Only reboot after confirming you can successfully connect via the new SSH configuration using ssh -p 9110 user@host from another terminal. If physical or console access is unavailable, a failed SSH config after reboot means permanent lockout.

SSH is now restricted to key-only authentication on a custom port with strong cryptography and minimal protocol features enabled.

đŸ”ĨPhase 3: Firewall (UFW)

A host-based firewall enforces a strict network boundary. Ubuntu Server ships with Uncomplicated Firewall (UFW), which provides a simple interface to iptables/nftables.

The firewall strategy follows a deny-by-default model:

  • All inbound traffic is blocked unless explicitly allowed.
  • Outbound traffic is permitted (package updates, DNS, NTP).
  • Only the hardened SSH port is exposed.
ufw-setup.sh
# UFW is pre-installed on Ubuntu Server
# Reset to a clean state
sudo ufw reset

# Default policies
sudo ufw default deny incoming
sudo ufw default allow outgoing

# Allow SSH on the hardened port
sudo ufw allow 9110/tcp comment 'SSH'

# Enable the firewall
sudo ufw enable

Verify the firewall status:

ufw-status.sh
sudo ufw status verbose
â„šī¸
The system now exposes a single inbound TCP port (SSH on 9110). All other inbound connections are silently dropped.

🧹Phase 4: Service Surface Reduction

Ubuntu Server includes several services for convenience and broad compatibility. For a hardened server, unnecessary services should be disabled or removed to minimize attack surface.

Common services to evaluate:

  • cloud-init — cloud provisioning framework (disable for bare metal).
  • snapd — Snap package manager (optional; increases attack surface).
  • Avahi (mDNS) — local network service discovery.
  • ModemManager — mobile broadband device management (unnecessary for servers).

Disabling cloud-init (for bare metal installations):

disable-cloud-init.sh
# Create cloud-init disable marker
sudo touch /etc/cloud/cloud-init.disabled

# Disable cloud-init services
sudo systemctl disable cloud-init.service
sudo systemctl disable cloud-init-local.service
sudo systemctl disable cloud-config.service
sudo systemctl disable cloud-final.service

Disabling snapd (optional):

âš ī¸
Removing snapd will break any snap-installed packages. Verify no critical applications depend on it before proceeding.
disable-snapd.sh
# List installed snaps
snap list

# Remove all snaps (if none are needed)
sudo snap remove --purge lxd
sudo snap remove --purge core20
sudo snap remove --purge snapd

# Stop and disable snapd
sudo systemctl disable --now snapd.service
sudo systemctl disable --now snapd.socket

# Remove snapd package
sudo apt purge -y snapd

# Remove snap directories
sudo rm -rf /snap /var/snap /var/lib/snapd

Disabling Avahi and ModemManager:

disable-services.sh
# Disable Avahi (if installed)
sudo systemctl disable --now avahi-daemon.service
sudo systemctl disable --now avahi-daemon.socket

# Disable ModemManager
sudo systemctl disable --now ModemManager.service

# Remove if not needed
sudo apt purge -y avahi-daemon modemmanager

# Clean up orphaned dependencies
sudo apt autoremove --purge -y
â„šī¸
After service reduction, the system runs only services explicitly required for its role. Fewer services mean fewer attack vectors and lower resource consumption.

âš™ī¸Phase 5: Ubuntu-Specific Hardening

Ubuntu Server includes several Ubuntu-specific services and configurations that should be hardened or configured for production use.

systemd-resolved DNS Configuration:

Ubuntu uses systemd-resolved as a local DNS stub resolver. For production servers, it's often preferable to use static DNS servers directly.

configure-dns.sh
# Option 1: Configure systemd-resolved with specific DNS servers
sudo mkdir -p /etc/systemd/resolved.conf.d
sudo tee /etc/systemd/resolved.conf.d/dns_servers.conf << 'EOF'
[Resolve]
DNS=1.1.1.1 1.0.0.1
FallbackDNS=8.8.8.8 8.8.4.4
DNSStubListener=no
EOF

sudo systemctl restart systemd-resolved

# Update /etc/resolv.conf to point to real servers instead of 127.0.0.53
sudo ln -sf /run/systemd/resolve/resolv.conf /etc/resolv.conf

AppArmor Verification:

Ubuntu enables AppArmor by default. Verify it's active and profiles are loaded:

verify-apparmor.sh
# Check AppArmor status
sudo aa-status

# Verify AppArmor is enabled
sudo systemctl status apparmor

# Ensure AppArmor loads at boot
sudo systemctl enable apparmor

Unattended Upgrades Configuration:

Ubuntu's unattended-upgrades package can automatically install security updates. Configure it for security-only updates:

configure-unattended-upgrades.sh
# Install unattended-upgrades if not present
sudo apt install -y unattended-upgrades

# Configure to only install security updates
sudo tee /etc/apt/apt.conf.d/50unattended-upgrades << 'EOF'
Unattended-Upgrade::Allowed-Origins {
    "${distro_id}:${distro_codename}-security";
};

Unattended-Upgrade::AutoFixInterruptedDpkg "true";
Unattended-Upgrade::MinimalSteps "true";
Unattended-Upgrade::Remove-Unused-Kernel-Packages "true";
Unattended-Upgrade::Remove-Unused-Dependencies "true";
Unattended-Upgrade::Automatic-Reboot "false";
Unattended-Upgrade::Automatic-Reboot-Time "03:00";
EOF

# Enable automatic updates
sudo dpkg-reconfigure -plow unattended-upgrades
â„šī¸
Setting Automatic-Reboot to false prevents unexpected reboots. Security updates will be installed, but kernel updates requiring reboot will wait for manual intervention.

Netplan Static IP Configuration (if needed):

Ubuntu Server uses Netplan for network configuration. Here's an example static IP configuration:

/etc/netplan/50-static.yaml
network:
  version: 2
  renderer: networkd
  ethernets:
    eth0:  # Replace with your interface name
      dhcp4: no
      addresses:
        - 192.168.1.100/24
      routes:
        - to: default
          via: 192.168.1.1
      nameservers:
        addresses:
          - 1.1.1.1
          - 1.0.0.1
apply-netplan.sh
# Test the configuration first
sudo netplan try

# If successful, apply permanently
sudo netplan apply

đŸ§ŦPhase 6: Kernel & Network Hardening

Linux kernel parameters can be tuned to improve security and resilience against network-based attacks. These settings are applied via sysctl and persist across reboots.

/etc/sysctl.d/99-security-hardening.conf
# IP Forwarding (disable unless this is a router)
net.ipv4.ip_forward = 0
net.ipv6.conf.all.forwarding = 0

# Enable reverse path filtering (anti-spoofing)
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1

# Disable ICMP redirects (prevent MITM 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

# Disable sending of redirects
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0

# Disable source routing
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0
net.ipv6.conf.all.accept_source_route = 0
net.ipv6.conf.default.accept_source_route = 0

# Ignore ICMP broadcast requests
net.ipv4.icmp_echo_ignore_broadcasts = 1

# Ignore bogus ICMP error responses
net.ipv4.icmp_ignore_bogus_error_responses = 1

# Log suspicious packets (Martians)
net.ipv4.conf.all.log_martians = 1
net.ipv4.conf.default.log_martians = 1

# Enable SYN cookies (DoS protection)
net.ipv4.tcp_syncookies = 1

# Disable IPv6 if not used
# net.ipv6.conf.all.disable_ipv6 = 1
# net.ipv6.conf.default.disable_ipv6 = 1

# TCP hardening
net.ipv4.tcp_timestamps = 0
net.ipv4.tcp_max_syn_backlog = 2048
net.ipv4.tcp_synack_retries = 2
net.ipv4.tcp_syn_retries = 5

# Protection against TCP time-wait assassination
net.ipv4.tcp_rfc1337 = 1

# Kernel hardening
kernel.dmesg_restrict = 1
kernel.kptr_restrict = 2
kernel.yama.ptrace_scope = 1
fs.suid_dumpable = 0
apply-sysctl.sh
# Apply the configuration immediately
sudo sysctl --system

# Verify specific settings
sudo sysctl net.ipv4.tcp_syncookies
sudo sysctl net.ipv4.conf.all.rp_filter
sudo sysctl kernel.dmesg_restrict
â„šī¸
These kernel-level hardening parameters work silently in the background, providing defense-in-depth against network attacks and information disclosure vulnerabilities.

đŸ›Ąī¸Phase 7: Intrusion Prevention (Fail2Ban)

Even with SSH hardened and firewalled, automated brute-force attempts can still occur. fail2ban provides adaptive defense by monitoring logs and temporarily banning malicious IP addresses.

install-fail2ban.sh
# Install fail2ban
sudo apt install -y fail2ban

# Enable and start the service
sudo systemctl enable fail2ban
sudo systemctl start fail2ban

Configure fail2ban to monitor the custom SSH port:

/etc/fail2ban/jail.d/sshd.local
[DEFAULT]
# Ban for 1 hour
bantime = 3600
# Check for attempts in a 10-minute window
findtime = 600
# Allow 5 attempts before banning
maxretry = 5
# Use UFW for banning
banaction = ufw

[sshd]
enabled = true
port = 9110
logpath = /var/log/auth.log
maxretry = 5
reload-fail2ban.sh
# Reload fail2ban configuration
sudo systemctl reload fail2ban

# Check SSH jail status
sudo fail2ban-client status sshd
â„šī¸
fail2ban integrates seamlessly with UFW, automatically adding and removing firewall rules as IPs are banned and unbanned.

📝Phase 8: Audit Logging Configuration

Audit logging provides a detailed record of security-relevant events on the system. Ubuntu Server can leverage auditd for comprehensive system call auditing.

install-auditd.sh
# Install auditd
sudo apt install -y auditd audispd-plugins

# Enable and start auditd
sudo systemctl enable auditd
sudo systemctl start auditd

Configure basic audit rules to monitor critical system changes:

/etc/audit/rules.d/hardening.rules
# Monitor changes to system time
-a always,exit -F arch=b64 -S adjtimex,settimeofday -k time-change
-a always,exit -F arch=b32 -S adjtimex,settimeofday,stime -k time-change

# Monitor user and group changes
-w /etc/group -p wa -k identity
-w /etc/passwd -p wa -k identity
-w /etc/gshadow -p wa -k identity
-w /etc/shadow -p wa -k identity
-w /etc/security/opasswd -p wa -k identity

# Monitor network configuration changes
-a always,exit -F arch=b64 -S sethostname,setdomainname -k system-locale
-w /etc/issue -p wa -k system-locale
-w /etc/issue.net -p wa -k system-locale
-w /etc/hosts -p wa -k system-locale
-w /etc/network -p wa -k system-locale

# Monitor login/logout events
-w /var/log/lastlog -p wa -k logins
-w /var/run/faillock -p wa -k logins

# Monitor SSH configuration
-w /etc/ssh/sshd_config -p wa -k sshd_config

# Monitor sudo usage
-w /etc/sudoers -p wa -k sudoers
-w /etc/sudoers.d/ -p wa -k sudoers

# Monitor kernel module loading
-w /sbin/insmod -p x -k modules
-w /sbin/rmmod -p x -k modules
-w /sbin/modprobe -p x -k modules

# Make configuration immutable (requires reboot to change)
-e 2
apply-audit-rules.sh
# Load audit rules
sudo augenrules --load

# Restart auditd to apply configuration
sudo systemctl restart auditd

# Check audit status
sudo auditctl -s

# View current rules
sudo auditctl -l
âš ī¸
The -e 2 flag makes audit configuration immutable until reboot. Remove this line if you need to modify audit rules dynamically.

🔍Phase 9: Verification & Validation

Verification ensures all hardening steps produced the intended result and no unintended services remain exposed.

Network Listener Verification:

verify-listeners.sh
# Check for listening TCP/UDP sockets
sudo ss -tulpn

# Should only show SSH on port 9110 for TCP
# UDP sockets may include systemd-resolved (127.0.0.53:53) if not disabled

Firewall Verification:

verify-firewall.sh
# Check UFW status and rules
sudo ufw status verbose

# Verify iptables rules
sudo iptables -L -n -v
sudo ip6tables -L -n -v

Service Status Check:

verify-services.sh
# List all running services
systemctl list-units --type=service --state=running

# Check for unwanted services
systemctl is-active snapd && echo "WARNING: snapd is running"
systemctl is-active avahi-daemon && echo "WARNING: avahi is running"
systemctl is-active ModemManager && echo "WARNING: ModemManager is running"

# Verify critical services
systemctl is-active ssh && echo "✓ SSH is running"
systemctl is-active ufw && echo "✓ UFW is running"
systemctl is-active fail2ban && echo "✓ Fail2Ban is running"
systemctl is-active auditd && echo "✓ auditd is running"

Security Settings Verification:

verify-security.sh
# Verify AppArmor status
sudo aa-status | head -n 5

# Check kernel hardening parameters
sudo sysctl net.ipv4.tcp_syncookies
sudo sysctl net.ipv4.conf.all.rp_filter
sudo sysctl kernel.dmesg_restrict

# Verify fail2ban jails
sudo fail2ban-client status

# Check audit daemon status
sudo auditctl -s
â„šī¸
At this stage, the system should expose exactly one inbound service: SSH on port 9110, protected by key-based authentication, firewall rules, and intrusion prevention.

Document this baseline state. Re-running these verification commands after changes helps detect configuration drift or accidental exposure.

✅Final State & Best Practices

At the conclusion of this hardening process, the Ubuntu Server 24.04 LTS installation is transformed into a production-ready, minimal, and security-focused server node.

The resulting system state:

  • Fully updated Ubuntu Server 24.04 LTS with latest security patches.
  • SSH hardened with key-only authentication, non-standard port, and strong cryptography.
  • Host-based firewall (UFW) enforcing deny-by-default inbound policy.
  • Unnecessary services removed (cloud-init, snapd, Avahi, ModemManager).
  • Ubuntu-specific services configured (systemd-resolved, AppArmor, unattended-upgrades).
  • Kernel and network hardening applied via persistent sysctl configuration.
  • Intrusion prevention active via Fail2Ban with UFW integration.
  • Comprehensive audit logging configured for security-relevant events.
  • Verification baseline established for future drift detection.

Ongoing Maintenance:

  • Monitor /var/log/auth.log for authentication attempts.
  • Review fail2ban status regularly: sudo fail2ban-client status sshd
  • Check audit logs: sudo ausearch -k identity
  • Periodically re-run verification commands to detect drift.
  • Keep the system updated: sudo apt update && sudo apt full-upgrade
â„šī¸
Security hardening is not a one-time task. Regular verification, monitoring, and updates are essential to maintain a secure posture as the system and threat landscape evolve.

This methodology can be applied consistently across Ubuntu Server deployments to establish a predictable, auditable security baseline before deploying applications, containers, or orchestration platforms.

Changelog

2026-01-19v1.0

Initial release documenting comprehensive hardening process for Ubuntu Server 24.04 LTS.

Filed under: Ubuntu Server, Linux Hardening, Security, SSH, UFW, Fail2Ban, AppArmor, auditd, systemd, Infrastructure

Last updated: 2026-01-19