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.
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_64oraarch64) architecture - Static IP configured (or reserved DHCP lease)
- SSH access available using an existing
ed25519keypair - 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.
# 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 rebootAfter rebooting, verify the system state:
# Check kernel version
uname -a
# Verify Ubuntu release
lsb_release -a
# Check for any remaining upgrades
apt list --upgradableAt 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:
# 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-256After updating the configuration, reload SSH without disconnecting active sessions:
sudo systemctl reload sshIn 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 service (may drop current session)
sudo systemctl restart ssh
# Verify the new port is listening
sudo ss -tlnp | grep :9110Test the new configuration thoroughly from a separate terminal before closing your original session. Attempt to SSH to the new port and verify successful authentication.
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 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 enableVerify the firewall status:
sudo ufw status verbose9110). 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):
# 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.serviceDisabling snapd (optional):
# 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/snapdDisabling Avahi and ModemManager:
# 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âī¸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.
# 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.confAppArmor Verification:
Ubuntu enables AppArmor by default. Verify it's active and profiles are loaded:
# Check AppArmor status
sudo aa-status
# Verify AppArmor is enabled
sudo systemctl status apparmor
# Ensure AppArmor loads at boot
sudo systemctl enable apparmorUnattended Upgrades Configuration:
Ubuntu's unattended-upgrades package can automatically install security updates. Configure it for security-only updates:
# 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-upgradesAutomatic-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:
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# 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.
# 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 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đĄī¸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
sudo apt install -y fail2ban
# Enable and start the service
sudo systemctl enable fail2ban
sudo systemctl start fail2banConfigure fail2ban to monitor the custom SSH port:
[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 configuration
sudo systemctl reload fail2ban
# Check SSH jail status
sudo fail2ban-client status sshdfail2ban 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
sudo apt install -y auditd audispd-plugins
# Enable and start auditd
sudo systemctl enable auditd
sudo systemctl start auditdConfigure basic audit rules to monitor critical system changes:
# 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# 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-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:
# 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 disabledFirewall Verification:
# Check UFW status and rules
sudo ufw status verbose
# Verify iptables rules
sudo iptables -L -n -v
sudo ip6tables -L -n -vService Status Check:
# 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 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 -s9110, 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.logfor 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
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
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