You deploy a fresh Ubuntu server, configure your application, open the necessary ports, and call it done. The machine is “in production.”
That machine is running with a security posture designed for convenience, not defense.
Default Ubuntu ships with services you don’t need, kernel parameters tuned for compatibility rather than security, and filesystem mount options that allow code execution in places it should never happen. CIS Benchmarks exist to fix this systematically. In this article, we’ll walk through hardening Ubuntu using CIS controls — automated with Ansible, verified with auditing tools.
CIS Benchmarks: What They Are and Why They Matter
The Center for Internet Security (CIS) publishes detailed hardening guides for operating systems, cloud platforms, databases, and network devices. The Ubuntu CIS Benchmark is a 300+ page document with specific, testable recommendations for locking down a system.
Each recommendation is categorized into two levels:
May reduce functionality or increase operational overhead
Restrict access to kernel logs, enforce detailed auditing
Level 1 is the baseline you should apply everywhere. Level 2 is for systems handling sensitive data — PCI-DSS, HIPAA, or anything with regulatory requirements.
ℹ️
CIS Benchmarks are consensus-driven. They represent the minimum hardening that the security community agrees on. If your system doesn’t pass Level 1, you have work to do.
Ansible Automation: The ansible-lockdown Project
Manually applying 200+ CIS controls across a fleet of servers is not realistic. The ansible-lockdown project maintains Ansible roles that implement CIS Benchmarks for major operating systems, including Ubuntu.
Getting Started
1
2
3
4
5
6
# Clone the Ubuntu 22.04 CIS rolegit clone https://github.com/ansible-lockdown/UBUNTU22-CIS.git
cd UBUNTU22-CIS
# Install dependenciesansible-galaxy install -r requirements.yml
Playbook Structure
Create a playbook that applies the role with your customizations:
# Dry run first — alwaysansible-playbook harden.yml --check --diff
# Apply for realansible-playbook harden.yml
⚠️
Never run CIS hardening blindly in production. Always use --check --diff first, review the changes, and test on a staging system. Some controls will break applications that depend on permissive defaults.
Removing and Disabling Unnecessary Services
Every running service is attack surface. A default Ubuntu installation includes services that have no business running on a production server.
Services to Disable
Service
What It Does
Why Disable It
avahi-daemon
mDNS/DNS-SD service discovery
Exposes the host on the local network, useless on servers
cups
Print server
You’re not printing from a production server
bluetooth
Bluetooth stack
No server needs Bluetooth
rpcbind
RPC port mapper for NFS
Unless you’re running NFS, this is an open door
whoopsie
Ubuntu error reporting
Sends crash data to Canonical — not appropriate for production
The masked: true parameter is critical — it prevents the service from being started even by dependency resolution. Merely disabling a service is not enough if another package pulls it back in.
Automatic Security Patching with unattended-upgrades
The time between a CVE being published and an exploit appearing in the wild is shrinking. In 2025, the median time-to-exploit for critical vulnerabilities dropped below 72 hours. Manual patching schedules cannot keep up.
Never enable automatic reboots on single-instance production servers without a load balancer. Use rolling updates: drain traffic from one node, patch and reboot it, verify it’s healthy, then move to the next.
Filesystem Hardening
Default mount options allow code execution from temporary directories — a common technique in post-exploitation. CIS Benchmark Section 1.1 addresses this directly.
sudo mount -o remount,noexec,nosuid,nodev /tmp
sudo mount -o remount,noexec,nosuid,nodev /dev/shm
Kernel Hardening via sysctl
The Linux kernel exposes tunable parameters through /proc/sys/ that control networking behavior, memory protections, and access restrictions. The defaults are permissive.
# --- Network hardening ---# Disable IP forwarding (this is a server, not a router)net.ipv4.ip_forward =0net.ipv6.conf.all.forwarding =0# Ignore ICMP redirects (prevents MITM via route injection)net.ipv4.conf.all.accept_redirects =0net.ipv4.conf.default.accept_redirects =0net.ipv6.conf.all.accept_redirects =0net.ipv6.conf.default.accept_redirects =0# Don't send ICMP redirectsnet.ipv4.conf.all.send_redirects =0net.ipv4.conf.default.send_redirects =0# Enable SYN cookies (mitigate SYN flood attacks)net.ipv4.tcp_syncookies =1# Log martian packets (packets with impossible source addresses)net.ipv4.conf.all.log_martians =1net.ipv4.conf.default.log_martians =1# Ignore ICMP broadcast requests (prevent Smurf attacks)net.ipv4.icmp_echo_ignore_broadcasts =1# Reject source-routed packetsnet.ipv4.conf.all.accept_source_route =0net.ipv4.conf.default.accept_source_route =0# Enable reverse path filtering (anti-spoofing)net.ipv4.conf.all.rp_filter =1net.ipv4.conf.default.rp_filter =1# --- Kernel hardening ---# Restrict access to dmesg (kernel ring buffer)kernel.dmesg_restrict =1# Restrict access to kernel pointers in /prockernel.kptr_restrict =2# Disable SysRq key (prevents local console attacks)kernel.sysrq =0# Enable ASLR (Address Space Layout Randomization) — full randomizationkernel.randomize_va_space =2# Restrict unprivileged access to eBPFkernel.unprivileged_bpf_disabled =1# Restrict ptrace to direct child processes onlykernel.yama.ptrace_scope =1# --- Filesystem hardening ---# Restrict creation of hard links and symlinksfs.protected_hardlinks =1fs.protected_symlinks =1
Apply immediately:
1
sudo sysctl --system
Why Each Setting Matters
Disabling IP forwarding prevents an attacker from turning your compromised server into a pivot point. Ignoring ICMP redirects blocks a classic man-in-the-middle vector. Restricting dmesg and kptr_restrict hides kernel memory layout information that aids exploit development — as noted in kernel security research from CIQ and ARMO, privilege escalation via kernel flaws remains a top threat vector in 2025-2026.
AppArmor Enforcement
AppArmor is Ubuntu’s mandatory access control system. It confines programs to a limited set of resources — files, network access, capabilities — based on per-application profiles.
Verify AppArmor Status
1
2
3
sudo apparmor_status
# Or:sudo aa-status
You should see profiles loaded and in enforce mode, not complain mode.
Switch Profiles from Complain to Enforce
1
2
3
4
5
# List profiles in complain modesudo aa-status | grep complain
# Enforce all profilessudo aa-enforce /etc/apparmor.d/*
AppArmor vs SELinux: Ubuntu ships with AppArmor by default. Both provide mandatory access control. AppArmor uses path-based rules (easier to write), SELinux uses label-based rules (more granular). Use whichever your distribution supports natively — the important thing is that MAC is active and enforcing.
User Account Hardening
Weak user account policies are one of the most exploited vectors. CIS Benchmark Section 5 covers this extensively.
Password Policies in /etc/login.defs
1
2
3
4
5
6
7
8
# /etc/login.defs — hardened valuesPASS_MAX_DAYS 90# Force password rotation every 90 daysPASS_MIN_DAYS 7# Prevent immediate re-change (stops cycling)PASS_WARN_AGE 14# Warn users 14 days before expiryPASS_MIN_LEN 14# Minimum password lengthLOGIN_RETRIES 3# Lock after 3 failed attemptsLOGIN_TIMEOUT 60# Timeout for login promptENCRYPT_METHOD SHA512 # Strong hashing for /etc/shadow
PAM Configuration for Password Quality
Install and configure pam_pwquality:
1
sudo apt install libpam-pwquality
Edit /etc/security/pwquality.conf:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Minimum password lengthminlen=14# Minimum character classes (uppercase, lowercase, digit, special)minclass=4# Maximum consecutive identical charactersmaxrepeat=3# Maximum consecutive characters from the same classmaxclassrepeat=4# Reject passwords containing the usernameusercheck=1# Enforce checks against dictionary wordsdictcheck=1# Number of changed characters from old passworddifok=8
Account Lockout via PAM
Configure /etc/pam.d/common-auth to lock accounts after failed attempts:
1
2
3
# Add before other auth linesauth required pam_faillock.so preauth silent audit deny=5unlock_time=900auth [default=die] pam_faillock.so authfail audit deny=5unlock_time=900
This locks an account for 15 minutes (900 seconds) after 5 failed login attempts.
Hardening without verification is just hope. Two tools are essential for validating that your controls are actually applied.
OpenSCAP
OpenSCAP is the reference implementation for SCAP (Security Content Automation Protocol). It can evaluate your system against CIS Benchmarks and produce detailed compliance reports.
The HTML report shows each CIS control, whether it passed or failed, and remediation instructions for failures.
Lynis
Lynis is a lightweight, agentless auditing tool that performs a comprehensive security scan:
1
2
3
4
5
# Install Lynissudo apt install lynis
# Run a full system auditsudo lynis audit system
Lynis produces a hardening index (0-100) and categorized findings:
Lynis security scan details:
Hardening index : 82 [################ ]
Tests performed : 268
Plugins enabled : 2
- Warnings (3):
* Found one or more vulnerable packages [PKGS-7392]
* No password set for single user mode [AUTH-9308]
* Nameserver 127.0.0.53 does not respond [NETW-2705]
- Suggestions (18):
* Consider hardening SSH configuration [SSH-7408]
* Install a file integrity tool [FINT-4350]
* Enable process accounting [ACCT-9622]
Here’s the order of operations for hardening a new Ubuntu server:
Apply OS updates — apt update && apt upgrade
Run the CIS Ansible role — with --check first, then for real
Disable unnecessary services — remove what you don’t need
Harden filesystem mounts — noexec, nosuid, nodev on temp directories
Apply sysctl hardening — network, kernel, and filesystem parameters
Enforce AppArmor profiles — switch from complain to enforce
Configure user account policies — PAM, password quality, account lockout
Enable unattended-upgrades — security-only, with controlled reboot strategy
Audit with OpenSCAP and Lynis — verify everything, schedule recurring scans
Conclusion
A default Ubuntu installation is optimized for ease of use, not security. CIS Benchmarks provide the prescriptive, auditable checklist to close that gap — and Ansible makes it repeatable across your infrastructure.
Hardening is not a one-time task. New CVEs appear daily (over 130 per day in 2025 according to OWASP), kernel vulnerabilities require sysctl updates, and configuration drift happens the moment humans touch a system. Automate the hardening, automate the patching, and automate the auditing. If you can’t prove your system is hardened, it isn’t.