CIS Level 1 Ubuntu Hardening: A Field-Tested Production Guide
CIS Level 1 Ubuntu hardening guide covering filesystem, SSH, sysctl, and audit logging — with real production pitfalls, configs, and a compliance checklist. Tested in enterprise environments.
Why Most Ubuntu Servers Are Not Hardened Out of the Box
You just deployed an Ubuntu server — cloud image, AWS AMI, or a fresh ISO. It works. But by default it is also broadcasting mDNS on your production VLAN, accepting SSH password authentication, logging nothing useful for forensics, and loaded with kernel parameters that make MITM routing attacks easier.
These are not edge cases. They are the default. And they are exactly what attackers look for when they scan your infrastructure.
The CIS Ubuntu Linux Benchmark Level 1 is the industry-standard baseline that fixes this. It is what SOC 2 auditors check, what compliance frameworks reference, and what separates a production-hardened server from a target.
The pitfalls are real: I have seen CIS hardening cause SSH lockouts, Docker failures, and broken package installs in production — all because controls were applied without understanding the dependencies. This guide covers what actually matters, what breaks, and how to apply CIS Level 1 Ubuntu hardening safely across a real fleet.
Table of Contents
- Quick Summary
- What Is CIS Level 1?
- Level 1 vs Level 2: When to Choose Each
- Why Default Ubuntu Installs Fail
- Audit First
- Key Hardening Areas
- Common Failures and Fixes
- Tools
- Real-World Lessons
- Implementation Checklist
- Best Practices
- FAQ
- Conclusion
Quick Summary: What Is CIS Level 1 Ubuntu?
CIS Level 1 is the baseline security profile from the Center for Internet Security Ubuntu Linux Benchmark.
It defines configuration controls that:
- Apply to virtually every Ubuntu server
- Have minimal operational impact on legitimate workloads
- Are recognized by SOC 2, PCI-DSS, ISO 27001, and HIPAA as evidence of hardening
Controls cover five areas: filesystem restrictions, service reduction, network kernel parameters, SSH configuration, and audit logging. Each maps to documented attack techniques. A default Ubuntu install fails 30–50% of these controls. Applying them correctly substantially reduces your attack surface without breaking legitimate workloads — provided you test each change before production.
What Is CIS Level 1? (Full Explanation)
The CIS (Center for Internet Security) Benchmark is maintained by a nonprofit with input from security engineers, government agencies, and enterprise vendors. Each major Linux distribution has its own benchmark, updated with each major release.
Level 1 is the broadly applicable baseline:
- Applies to virtually every Ubuntu server regardless of workload
- Minimal operational impact on legitimate applications
- Provides significant, measurable security improvement
- Recognized by major compliance frameworks as a hardening baseline
Level 2 is the high-security profile:
- Stricter controls for sensitive or regulated environments
- May require significant application changes
- Appropriate for systems handling highly sensitive data or in air-gapped environments
For most production Ubuntu systems running web services, APIs, or databases, Level 1 is the correct target.
CIS Level 1 vs Level 2: When to Choose Each
| Criteria | Level 1 | Level 2 |
|---|---|---|
| Target environment | General production servers | High-security / regulated |
| Operational impact | Minimal | Significant — may break apps |
| Compliance coverage | SOC 2, PCI-DSS, HIPAA, ISO 27001 | NIST 800-53 High, FedRAMP |
| Application changes required | Rare | Common |
| Docker / container compatibility | Mostly compatible (with exceptions) | Often incompatible |
| Audit burden | Moderate | High |
| Right for most teams? | ✅ Yes | Only if required by regulation |
Choose Level 1 if you need compliance coverage for SOC 2 or PCI-DSS, are hardening general-purpose Ubuntu servers, or want a baseline that survives contact with real production workloads.
Choose Level 2 if you are operating in a regulated environment with explicit high-security requirements, have dedicated security engineering capacity to manage exceptions, and have tested compatibility with your full application stack.
Starting with Level 1 and selectively adding Level 2 controls is a valid and common approach in enterprise environments.
Why Default Ubuntu Installs Fail in Production Security
A fresh Ubuntu install is optimized for usability, not security. Out of the box:
| Issue | Security Risk |
|---|---|
| SSH password auth enabled | Brute-force entry point — bots scan 24/7 |
| Avahi/mDNS broadcasting | Leaks network topology to local attackers |
| No audit logging configured | Zero forensic capability after a breach |
| ICMP redirects accepted | Enables MITM routing attacks |
No /tmp mount restrictions |
Attacker can drop and execute payloads directly |
| Unused kernel modules loaded | Expanded kernel attack surface |
rpcbind often running |
Exposes NFS enumeration endpoints |
CIS Level 1 Ubuntu hardening closes these gaps systematically. None of these controls are theoretical — they appear directly in penetration test findings and breach reports.
Before You Apply Anything: Audit First
Never apply CIS controls without auditing your current state first.
You need to know what will break before it breaks in production.
# Lynis — fastest baseline audit, no changes made
apt install lynis -y
lynis audit system 2>/dev/null | grep -E "Hardening index|WARNING|SUGGESTION" | head -40
A default Ubuntu install typically scores 55–65. After correctly applying Level 1 controls, expect 75–85+.
# Ubuntu Security Guide — Canonical's official CIS tool (Ubuntu 22.04+)
apt install ubuntu-security-guide -y
usg audit cis_level1_server # read-only — lists all failing controls before touching anything
Run the audit. Read every failing control. Identify which ones will conflict with your workload. Then apply incrementally — never all at once.
Key Areas of CIS Level 1 Hardening on Ubuntu
1. Filesystem Hardening
Why it matters: Attackers drop payloads into world-writable directories like /tmp. Without mount restrictions, they execute code directly from there. Filesystem hardening removes this option.
Required separate partitions:
/tmp — temporary files, world-writable
/var — variable data
/var/tmp — persistent temp files
/var/log — system logs
/var/log/audit — audit logs
/home — user home directories
Set this up at provisioning time. Retrofitting partition layouts on running systems usually requires a full rebuild. Build it into your base image, Packer template, or cloud-init config.
Critical mount options (/etc/fstab):
# /tmp — tmpfs with all three restrictions
tmpfs /tmp tmpfs defaults,rw,nosuid,nodev,noexec,relatime 0 0
# /var/tmp — persistent, same restrictions
/dev/sdb1 /var/tmp ext4 defaults,nosuid,nodev,noexec 0 0
# /home — no device files or setuid binaries
/dev/sdc1 /home ext4 defaults,nosuid,nodev 0 0
What each option prevents:
| Mount Option | Attack It Prevents |
|---|---|
nosuid |
setuid/setgid bit privilege escalation |
nodev |
Device file creation used in privesc exploits |
noexec |
Direct binary execution from the partition |
⚠️ Warning —
noexecon/tmp: This breaks Java applications (JVM extracts native libraries to/tmp), Python virtualenv creation, some APT postinstall scripts, and older build tools.Before applying: run your full application stack and confirm nothing executes from
/tmp. If something does, fix the application (e.g.-Djava.io.tmpdir=/var/lib/myapp/tmpfor Java) or document an exception.
Disable unused kernel filesystem modules:
# /etc/modprobe.d/cis-disable-filesystems.conf
install cramfs /bin/true
install freevxfs /bin/true
install jffs2 /bin/true
install hfs /bin/true
install hfsplus /bin/true
install squashfs /bin/true
install udf /bin/true
These filesystems have had exploitable vulnerabilities historically. Disabling them prevents loading even if an attacker attempts to trigger it.
2. Service Reduction
Why it matters: Every running service is an attack surface. Services you did not intentionally install are services you are not monitoring.
# Audit what is actually running
systemctl list-units --type=service --state=running --no-pager
# Disable services CIS Level 1 requires to be off (if not needed)
systemctl disable --now avahi-daemon # mDNS — leaks network topology
systemctl disable --now cups # Printing — no place on a server
systemctl disable --now isc-dhcp-server # DHCP server
systemctl disable --now rpcbind # NFS prerequisite — often left running
systemctl disable --now nfs-server # Network file shares
systemctl disable --now nis # Legacy auth — never needed
# Verify ports are actually closed after disabling
ss -tlnp
⚠️ Warning — Inherited environments: On bare-metal and inherited VM deployments, Avahi broadcasting on production VLANs is genuinely common. So is
rpcbindleft running from a one-time NFS test that no one cleaned up.In cloud instances most of these are absent — but verify with
ss -tlnprather than assume.
→ Internal link opportunity: Link to a post on [Linux service auditing and attack surface reduction] here.
3. Network Kernel Parameters (sysctl)
Why it matters: The Linux kernel defaults enable network features that are useful for routers but dangerous for application servers — ICMP redirects, source routing, and IP forwarding all have documented exploitation paths.
Create a dedicated sysctl file:
# /etc/sysctl.d/99-cis-hardening.conf
# --- IP Forwarding ---
# Disable unless this server is a router or runs containers
net.ipv4.ip_forward = 0
net.ipv6.conf.all.forwarding = 0
# --- ICMP Redirects ---
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
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
# --- 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
# --- Anti-Spoofing ---
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
# --- Logging ---
net.ipv4.conf.all.log_martians = 1
net.ipv4.conf.default.log_martians = 1
# --- ICMP ---
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.icmp_ignore_bogus_error_responses = 1
# --- SYN Flood Protection ---
net.ipv4.tcp_syncookies = 1
# --- IPv6 Router Advertisements ---
net.ipv6.conf.all.accept_ra = 0
net.ipv6.conf.default.accept_ra = 0
Apply without rebooting:
sysctl --system
# or target just your file:
sysctl -p /etc/sysctl.d/99-cis-hardening.conf
Verify a specific setting took effect:
sysctl net.ipv4.conf.all.accept_redirects
# Expected: net.ipv4.conf.all.accept_redirects = 0
⚠️ Warning — Container hosts:
net.ipv4.ip_forward = 0breaks Docker, Podman, and Kubernetes — all require IP forwarding for container networking.On container hosts: either exclude this control entirely, or set it per-namespace. Docker attempts to re-enable forwarding at startup; the conflict produces unpredictable results.
4. SSH Hardening
Why it matters: SSH is the primary administrative interface on most Linux servers and the most brute-forced service on the internet. Weak defaults are directly and trivially exploitable.
→ Internal link opportunity: Link to a dedicated post on [production SSH hardening and secure cipher configuration] here.
# /etc/ssh/sshd_config
# Keys only — no passwords
PasswordAuthentication no
PermitEmptyPasswords no
ChallengeResponseAuthentication no
# No root login
PermitRootLogin no
# Restrict to approved groups only
AllowGroups sshusers sudo
# Limit brute-force impact
MaxAuthTries 4
MaxSessions 10
LoginGraceTime 60
# Terminate idle sessions (5 minutes)
ClientAliveInterval 300
ClientAliveCountMax 0
# Disable unused attack surface
X11Forwarding no
AllowAgentForwarding no
AllowTcpForwarding no
PermitUserEnvironment no
# Modern cryptographic algorithms only
Ciphers aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,aes256-gcm@openssh.com
MACs hmac-sha2-256,hmac-sha2-512,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com
KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group14-sha256
# Verbose logging for audit trail
LogLevel VERBOSE
PrintLastLog yes
Safe apply procedure — do not skip this:
# Step 1: validate config syntax
sshd -t
# Step 2: open a SECOND SSH session before restarting
# (your recovery session if something goes wrong)
# Step 3: restart
systemctl restart sshd
# Step 4: test login from a THIRD terminal before closing anything
⚠️ Warning —
AllowGroupslockout (most common CIS mistake): If you addAllowGroups sshusersand your account is not in that group, your next login will fail — even with valid keys.Always check first:
groups $(whoami) # if 'sshusers' is not listed: usermod -aG sshusers $(whoami)
5. Audit Logging
Why it matters: Without audit logging, you have zero forensic capability after a breach. You cannot determine what was done, when, or by which account. Compliance frameworks require it. Incident response depends on it.
→ Internal link opportunity: Link to a post on [reading Linux audit logs and incident triage] here.
# Install auditd
apt install auditd audispd-plugins -y
systemctl enable --now auditd
Production audit rules (/etc/audit/rules.d/99-cis.rules):
# Delete existing rules first
-D
# Set buffer size (increase on busy systems)
-b 8192
# --- Identity and Authentication Files ---
-w /etc/passwd -p wa -k identity
-w /etc/group -p wa -k identity
-w /etc/shadow -p wa -k identity
-w /etc/gshadow -p wa -k identity
# --- Privilege Configuration ---
-w /etc/sudoers -p wa -k sudoers
-w /etc/sudoers.d -p wa -k sudoers
# --- SSH Configuration ---
-w /etc/ssh/sshd_config -p wa -k sshd_config
# --- Login Events ---
-w /var/log/faillog -p wa -k logins
-w /var/log/lastlog -p wa -k logins
-w /var/log/tallylog -p wa -k logins
# --- Privilege Escalation Syscalls ---
-a always,exit -F arch=b64 -S setuid -k privilege_escalation
-a always,exit -F arch=b64 -S setgid -k privilege_escalation
-a always,exit -F arch=b32 -S setuid -k privilege_escalation
-a always,exit -F arch=b32 -S setgid -k privilege_escalation
# --- Module Loading (attacker persistence vector) ---
-w /sbin/insmod -p x -k modules
-w /sbin/rmmod -p x -k modules
-w /sbin/modprobe -p x -k modules
# Make rules immutable — requires reboot to change
# Comment out during initial setup until rules are confirmed correct
-e 2
Apply and verify:
augenrules --load
auditctl -l | head -20 # verify rules loaded
ausearch -k sudoers # test: search for sudoers events
⚠️ Warning —
-e 2is irreversible until reboot: This makes audit rules immutable. A mistake cannot be fixed without rebooting.Leave it commented out until your ruleset is confirmed correct with
auditctl -landausearch, then add it back.
⚠️ Warning — Disk exhaustion: Verbose audit logging generates significant I/O on busy systems. Monitor
/var/log/audit/size.In
/etc/audit/auditd.conf, setmax_log_file = 100andnum_logs = 10to cap growth.
Common CIS Level 1 Failures and Fixes
| Control | Common Failure | Production Fix |
|---|---|---|
noexec on /tmp |
Java app fails to start | Set -Djava.io.tmpdir to alternate path |
AllowGroups SSH |
Locked out after restart | Add user to group before enabling |
ip_forward = 0 |
Docker networking breaks | Exclude on container hosts, document exception |
-e 2 audit rules |
Can't fix ruleset without reboot | Test and verify before adding -e 2 |
Separate /var/log |
Partition fills up | Size at 10–20GB minimum; monitor actively |
Disabled rpcbind |
NFS mounts stop working | Re-enable if NFS is actually in use |
Tools for Applying and Verifying Ubuntu CIS Hardening
Lynis — Fastest Baseline Audit
apt install lynis -y
lynis audit system
# Show only CIS-related findings
lynis audit system 2>/dev/null | grep -A2 "CIS"
Gives a hardening index score and numbered recommendations. Run before and after hardening to measure delta.
Ubuntu Security Guide (USG) — Canonical's Official Tool
The recommended tool for Ubuntu 22.04+. Canonical-maintained, handles Ubuntu-specific quirks automatically.
apt install ubuntu-security-guide -y
# Audit against CIS Level 1 (read-only, no changes)
usg audit cis_level1_server
# Apply all Level 1 controls
usg fix cis_level1_server
# Generate HTML compliance report for auditors
usg audit cis_level1_server --html-file /tmp/cis-report.html
OpenSCAP — For Formal Compliance Reports
apt install libopenscap8 ssg-ubuntu2204 -y
oscap xccdf eval \
--profile xccdf_org.ssgproject.content_profile_cis_level1_server \
--results /tmp/cis-results.xml \
--report /tmp/cis-report.html \
/usr/share/xml/scap/ssg/content/ssg-ubuntu2204-ds.xml
Produces machine-readable XCCDF results that integrate with GRC platforms like ServiceNow and Archer.
Real-World Lessons from Production Environments
These are not hypothetical. Each of these happened.
Lesson 1 — noexec on /tmp took down a Java application.
The JVM extracted a native library to /tmp on startup. With noexec mounted, extraction succeeded but execution failed with a cryptic error. Fix: set -Djava.io.tmpdir=/var/lib/myapp/tmp in JVM startup args, create the directory with correct permissions.
Lesson 2 — AllowGroups caused a 2am lockout during a maintenance window.
An engineer added AllowGroups sshusers, restarted SSH, and could not log back in. The account was in sudo but not in sshusers. Recovery required the cloud provider's serial console. Mitigation: always run groups $(whoami) first, always keep a second session open during SSH config changes.
Lesson 3 — ip_forward = 0 silently broke Docker networking 20 minutes after applying.
Existing connections survived the sysctl change. New container-to-container connections started failing. The delayed failure made root cause non-obvious. Fix: exclude net.ipv4.ip_forward on container hosts and document the exception.
Lesson 4 — -e 2 locked an incorrect audit ruleset in place until a maintenance window.
A path typo in an audit rule caused warnings but still loaded. With -e 2 active, fixing it required a scheduled reboot. On a production database server, that meant waiting for the next maintenance window. Lesson: always verify with auditctl -l and ausearch before adding -e 2.
Lesson 5 — A routine apt upgrade silently overwrote the hardened sshd_config.
OpenSSH-server reinstalled with defaults, reverting password auth and cipher settings. Scheduled monthly Lynis scans caught it within 30 days. Lesson: manage sshd_config via configuration management (Ansible, Puppet), not by hand. Files managed by hand drift.
Implementation Checklist
Apply in this order. Test between each group.
[ ] Run Lynis baseline audit — record initial score
[ ] Set up separate partitions (or document why not applicable)
[ ] Add mount options to /etc/fstab — test with remount before reboot
[ ] Disable unused filesystem modules in /etc/modprobe.d/
[ ] Disable unused services — verify with ss -tlnp
[ ] Apply sysctl hardening — verify each value with sysctl <key>
[ ] Harden sshd_config — validate with sshd -t, keep second session open
[ ] Install and configure auditd — verify rules with auditctl -l
[ ] Configure rsyslog — verify auth events reach /var/log/auth.log
[ ] Run Lynis post-audit — compare scores
[ ] Run USG or OpenSCAP — generate compliance report
[ ] Document all exceptions with business justification
Best Practices for Sustainable CIS Compliance
Automate everything. Manual hardening drifts. Package updates overwrite configs. New servers get deployed without hardening applied. CIS controls belong in your base image, applied via Ansible or Puppet, and enforced at deployment time — not applied by hand post-deployment.
Document exceptions explicitly.
Some controls cannot be applied — containers need ip_forward, Java apps need executable /tmp. Document every exception with a justification and a compensating control. Auditors expect this and will ask.
Schedule re-audits monthly. Systems drift silently. Run Lynis on a cron schedule. Alert on score drops greater than 5 points. A scheduled audit catches drift before an auditor does.
Treat hardening configs as code.
Store sysctl files, modprobe configs, and sshd_config in version control. Review changes in pull requests. Treat a change to your hardening baseline the same way you treat a change to application code.
Frequently Asked Questions
Is CIS Level 1 Ubuntu enough for production? For most production workloads — web servers, APIs, databases, application servers — yes. CIS Level 1 Ubuntu addresses the most commonly exploited misconfigurations and satisfies hardening requirements for SOC 2, PCI-DSS, and HIPAA. It is a meaningful baseline, not a guarantee. You still need patching, monitoring, and access controls layered on top.
Does CIS Level 1 Ubuntu break applications?
Some controls can, if applied without testing. The most common issues are noexec on /tmp breaking Java and Python workloads, AllowGroups in SSH causing lockouts, and ip_forward = 0 breaking Docker networking. None of these are unavoidable — they require fixing the application, excluding the specific control, or documenting an exception. The key is to audit and test before applying to production.
Does CIS Level 1 hardening affect performance?
Minimally in most cases. Audit logging (auditd) adds disk I/O proportional to activity — on high-throughput systems, monitor /var/log/audit/ size and tune buffer settings. Sysctl changes have negligible performance impact. Mount options (nosuid, nodev, noexec) are essentially free. The performance cost is small and well-justified.
How long does CIS Level 1 Ubuntu hardening take? The audit takes 5–10 minutes. Applying and testing controls incrementally takes 2–4 hours depending on workload complexity. Automating with Ansible or USG and building it into a base image reduces future effort to near zero.
Does CIS Level 1 apply to Ubuntu 22.04 and 24.04?
Yes. CIS maintains separate benchmarks for each Ubuntu LTS release. Ubuntu Security Guide (USG) supports 22.04 and 24.04 natively. OpenSCAP content (ssg-ubuntu2204) covers 22.04; check for ssg-ubuntu2404 availability for 24.04.
Conclusion: Hardening Is a Baseline, Not a Destination
CIS Level 1 Ubuntu hardening is not a checkbox you tick once. It is a baseline you establish, codify in automation, and maintain across the full lifecycle of every server in your fleet.
The controls in this guide — filesystem restrictions, service reduction, network sysctl tuning, SSH hardening, and audit logging — address real attack techniques from breach reports and penetration test findings. They exist because attackers reliably exploit the defaults. Leaving them unaddressed is a known risk, not an unknown one.
The production pitfalls are equally real. Applying these controls without testing has caused outages. The difference between a successful hardening project and a 2am incident is incremental application, open rollback options, and documented exceptions.
Start now:
# Ubuntu 22.04+
apt install ubuntu-security-guide -y && usg audit cis_level1_server
# Any Ubuntu version
apt install lynis -y && lynis audit system
Know your current score. Apply controls in order. Test each group before moving to the next. Document every exception. Re-audit monthly.
A CIS Level 1 compliant Ubuntu server forces attackers to work harder, leaves forensic evidence when they try, and gives you a defensible, auditable baseline. That is exactly what production security requires — and exactly what this guide is built to deliver.
Related reading: NGINX SSL Hardening: From C Grade to A+ on SSL Labs — the same hardening discipline applied to your web server TLS layer. For building forensic capability on hardened systems: Reading Logs Like a Detective: A Field Guide to Incident Triage.