Hardening Ubuntu Server 24.04 LTS with CIS Benchmarks
Executive Summary
This project implements a complete security hardening solution for Ubuntu Server 24.04 LTS, combining CIS Benchmark v1.0.0 controls with Lynis security auditing and comprehensive security tooling. The solution provides both standalone Bash scripts for manual execution and a production-ready Ansible role for automated deployment at scale.
Key Results
| Metric | Before | After | Change |
|---|---|---|---|
| Lynis Hardening Score | 62 | 84 | +22 points |
| Unconfined Profiles | 7 | 0 | -7 |
| Lynis Suggestions | 35 | 23 | -12 |
| Security Tools Installed | 0 | 7 | +7 |
Key Features
- 30+ CIS Controls across 6 security domains (Levels 1 and 2)
- Lynis Integration with automated scoring and improvement tracking
- Security Tools Suite including AIDE, rkhunter, chkrootkit, and Fail2Ban
- Enterprise nftables Firewall with SSH rate limiting and default-deny policy
- Complete Workflow Automation from initial analysis to final reporting
- Ansible Vault Integration for secure GRUB password management
Project Scope
Security Domains Covered
| Domain | Description | Controls |
|---|---|---|
| Filesystem | USB storage, mount options, unused filesystems | 5 |
| Mandatory Access Control | AppArmor configuration and enforcement | 2 |
| Bootloader | GRUB password protection and permissions | 2 |
| Kernel | ASLR, ptrace scope, core dumps, memory protection | 3 |
| Services | Unnecessary service removal and configuration | 3 |
| Network | IP forwarding, ICMP, SYN cookies, protocol disabling | 8 |
| Firewall | nftables with default deny, rate limiting | 4 |
| SSH | Ciphers, MACs, authentication hardening | 6 |
| Authentication | Password policies, PAM, sudo configuration | 8 |
| Auditing | auditd rules and configuration | 6 |
| File Integrity | AIDE and debsums verification | 2 |
CIS Controls Implemented
| Section | Control ID | Description | Level |
|---|---|---|---|
| Filesystem | 1.1.1.9 | Disable USB storage kernel module | L1 |
| Filesystem | 1.1.1.10 | Disable unused filesystems | L1 |
| Filesystem | 1.1.2.1.1 | Configure /tmp as separate partition | L1 |
| Filesystem | 1.1.2.1.4 | Set noexec option on /tmp | L1 |
| Filesystem | 1.1.2.2.4 | Set noexec option on /dev/shm | L1 |
| AppArmor | 1.3.1.2 | Enable AppArmor in bootloader | L1 |
| AppArmor | 1.3.1.4 | Enforce all AppArmor profiles | L2 |
| Bootloader | 1.4.1 | Set bootloader password | L1 |
| Bootloader | 1.4.2 | Restrict bootloader config permissions | L1 |
| Kernel | 1.5.1 | Enable ASLR | L1 |
| Kernel | 1.5.2 | Restrict ptrace scope | L1 |
| Kernel | 1.5.3 | Restrict core dumps | L1 |
| Services | 2.1.1 | Disable autofs service | L1 |
| Services | 2.1.21 | Configure MTA for local-only mode | L1 |
| Services | 2.2.4 | Remove telnet client | L1 |
| Network | 3.1.2 | Disable wireless interfaces | L1 |
| Network | 3.3.1 | Disable IP forwarding | L1 |
| Network | 3.3.2 | Disable packet redirect sending | L1 |
| Network | 3.3.3 | Reject ICMP redirects | L1 |
| Network | 3.3.4 | Ignore broadcast ICMP | L1 |
| Network | 3.3.5 | Reject secure ICMP redirects | L1 |
| Network | 3.3.9 | Log suspicious packets (martians) | L1 |
| Network | 3.3.10 | Enable TCP SYN cookies | L1 |
| Firewall | x | Configure nftables firewall | L1 |
| SSH | x | SSH server hardening | L1 |
| Sudo | x | Sudo configuration | L1 |
| PAM | x | PAM password policies | L1 |
| Auth | 5.4.1.4 | Enforce SHA512 password hashing | L1 |
| Auth | 5.4.1.5 | Configure inactive password lock | L1 |
| Auth | 5.4.2.1 | Ensure root is only UID 0 account | L1 |
| Logging | 6.1.3.4 | Configure rsyslog file permissions | L1 |
| Audit | 6.2.1.2 | Enable auditd service | L2 |
| Audit | 6.2.2.3 | Configure disk full action | L2 |
| Audit | x | Comprehensive audit rules | L2 |
| Audit | 6.2.3.20 | Make audit configuration immutable | L2 |
| Integrity | 6.3.1 | Install and configure AIDE | L1 |
Architecture
Hardening Workflow
The solution implements a six-phase hardening workflow:
| Phase | Description | Output |
|---|---|---|
| 1. Initial Analysis | Collect baseline state with Lynis audit | Score: 62, baseline metrics |
| 2. Install Tools | Deploy security tools suite | 7 tools configured |
| 3. Apply CIS Controls | Implement 30+ CIS Benchmark controls | Kernel, SSH, PAM hardened |
| 4. Lynis Improvements | Additional hardening based on Lynis suggestions | 12 additional fixes |
| 5. Configure Firewall | Deploy nftables with SSH rate limiting | Default-deny policy |
| 6. Generate Report | Post-hardening analysis and comparison | Score: 84 (+22 points) |
Design Principles
True Idempotency
The Ansible role prioritizes native modules over shell commands:
| Operation | Module Used | Alternative Avoided |
|---|---|---|
| Mount configuration | ansible.posix.mount |
shell: echo >> /etc/fstab |
| Kernel parameters | ansible.posix.sysctl |
shell: sysctl -w |
| PAM limits | community.general.pam_limits |
shell: echo >> limits.conf |
| Service management | ansible.builtin.systemd |
shell: systemctl |
| Package installation | ansible.builtin.apt |
shell: apt-get |
Secret Management
Control 1.4.1 (GRUB bootloader password) uses Ansible Vault for encryption:
# group_vars/all/vault.yml (encrypted)
cis_grub_password_hash: "grub.pbkdf2.sha512.600000.ABC123..."
This eliminates interactive password prompts, enabling CI/CD pipeline compatibility.
Level-Based Profiles
The role supports both CIS Level 1 and Level 2 profiles:
Level 1 (default): Practical security for most environments
- Audit rules without immutable flag
- AppArmor profiles as-is
Level 2: Enhanced security for high-risk environments
- Immutable audit configuration (requires reboot to modify)
- All AppArmor profiles in enforce mode
- Compiler removal option
Hardening Results
Kernel Security Parameters
| Parameter | Before | After | CIS Control |
|---|---|---|---|
| kernel.yama.ptrace_scope | 1 | 2 | 1.5.2 |
| net.ipv4.conf.all.log_martians | 0 | 1 | 3.3.9 |
| net.ipv4.conf.all.secure_redirects | 1 | 0 | 3.3.5 |
| kernel.kptr_restrict | 1 | 2 | Lynis |
| kernel.sysrq | 176 | 0 | Lynis |
| net.ipv4.tcp_rfc1337 | 0 | 1 | Lynis |
| fs.protected_fifos | 1 | 2 | Lynis |
| net.ipv6.conf.all.accept_ra | 1 | 0 | Lynis |
SSH Hardening Applied
| Parameter | Before | After | CIS Control |
|---|---|---|---|
| MaxAuthTries | 6 | 3 | 5.1.6 |
| ClientAliveInterval | 0 | 300 | 5.1.7 |
| PermitRootLogin | yes | no | 5.1.20 |
| X11Forwarding | yes | no | x |
| LogLevel | INFO | VERBOSE | x |
| Banner | none | /etc/issue.net | x |
| MaxStartups | default | 10:30:60 | x |
AppArmor Improvements
Applied 119 profiles in enforce mode, eliminating all unconfined profiles.
Security Tools Integration
Installed Security Tools
| Tool | Purpose | Configuration |
|---|---|---|
| Lynis | Security auditing and scoring | Automated scans with report generation |
| AIDE | File integrity monitoring | Daily cron checks, database initialization |
| rkhunter | Rootkit detection | Daily scans, automatic updates |
| chkrootkit | Rootkit verification | Daily verification runs |
| Fail2Ban | Intrusion prevention | SSH jail with nftables integration |
| auditd | System auditing | Comprehensive audit rules |
| debsums | Package verification | Weekly integrity checks |
Lynis Score Progression
| Phase | Score | Change |
|---|---|---|
| Initial (default Ubuntu 24.04) | 62 | baseline |
| After CIS controls | ~75 | +13 |
| After Lynis improvements | 84 | +9 |
Additional Lynis Improvements Applied
| Category | Improvements |
|---|---|
| Kernel | dmesg_restrict, kptr_restrict, sysrq disabled |
| Network | DCCP, SCTP, RDS, TIPC protocols disabled |
| Permissions | Secure cron directories (0700), restrictive UMASK (027) |
| Services | Security banners configured |
| Shell | 15-minute timeout (TMOUT=900) |
| PAM | libpam-tmpdir installed |
nftables Firewall Configuration
The firewall implements a hardened configuration with SSH rate limiting:
Features
- Default-Deny Policy: All incoming traffic blocked by default
- Stateful Filtering: Established/related connections allowed
- SSH Rate Limiting: 4 connections/minute per source IP (anti-brute-force)
- ICMP Control: Essential ICMP types only (echo, unreachable, time-exceeded)
- IPv6 Blocking: Complete IPv6 traffic blocking (configurable)
- Logging: Rate-limited logging of dropped packets
Configuration
table inet filter {
set ssh_limit {
type ipv4_addr
size 65535
flags dynamic,timeout
timeout 1m
}
chain input {
type filter hook input priority 0; policy drop;
ct state established,related accept
ct state invalid drop
iif "lo" accept
iif != "lo" ip daddr 127.0.0.0/8 drop
# ICMP essential types
ip protocol icmp icmp type { echo-reply, destination-unreachable,
echo-request, time-exceeded, parameter-problem } accept
# SSH with rate limiting
tcp dport 22 ct state new add @ssh_limit {
ip saddr limit rate 4/minute burst 8 packets
} accept
tcp dport 22 ct state new drop
# Log and drop everything else
limit rate 5/minute burst 10 packets log prefix "nftables-dropped: "
counter drop
}
chain forward {
type filter hook forward priority 0; policy drop;
counter drop
}
chain output {
type filter hook output priority 0; policy accept;
}
}
table ip6 filter {
chain input { type filter hook input priority 0; policy drop; }
chain forward { type filter hook forward priority 0; policy drop; }
chain output { type filter hook output priority 0; policy drop; }
}
Usage
Full Automated Workflow (Bash)
# Clone the repository
git clone https://github.com/xoelrdgz/hardening-project.git
cd hardening-project
# Run complete hardening with all features
sudo ./scripts/00-cis-hardening-main.sh full
# With custom firewall ports
sudo ./scripts/00-cis-hardening-main.sh full --ssh-port 22 --tcp-ports 80,443
Step-by-Step Execution
# Phase 1: Initial analysis with Lynis
sudo ./scripts/00-cis-hardening-main.sh analyze --phase initial
# Phase 2: Install security tools
sudo ./scripts/00-cis-hardening-main.sh tools
# Phase 3: Apply CIS controls
sudo ./scripts/00-cis-hardening-main.sh harden
# Phase 4: Lynis improvements
sudo ./scripts/00-cis-hardening-main.sh lynis-improve
# Phase 5: Configure firewall
sudo ./scripts/00-cis-hardening-main.sh firewall
# Phase 6: Generate report
sudo ./scripts/00-cis-hardening-main.sh analyze --phase post
sudo ./scripts/00-cis-hardening-main.sh report
Ansible Deployment
cd ansible
# Install dependencies
ansible-galaxy collection install -r requirements.yml
# Dry run (check mode)
ansible-playbook site.yml --check --diff --ask-vault-pass
# Apply Level 1 hardening
ansible-playbook site.yml --ask-vault-pass
# Apply Level 2 hardening
ansible-playbook site.yml -e "cis_level=2" --ask-vault-pass
# Run specific sections
ansible-playbook site.yml --tags "security_tools,lynis_improvements" --ask-vault-pass
Validation
After applying hardening controls:
# Run Lynis audit
sudo lynis audit system
# Verify kernel parameters
sysctl kernel.randomize_va_space # Expected: 2
sysctl kernel.yama.ptrace_scope # Expected: 2
sysctl net.ipv4.ip_forward # Expected: 0
sysctl net.ipv4.tcp_syncookies # Expected: 1
# Verify security services
systemctl is-enabled auditd fail2ban nftables
# Verify firewall
nft list ruleset
# Verify file integrity tools
aide --check
rkhunter --check
# Verify AppArmor (all profiles enforced)
aa-status | grep -E "profiles|processes"
# Verify mount options
findmnt /tmp -o OPTIONS | grep noexec
findmnt /dev/shm -o OPTIONS | grep noexec
Technical Highlights
Challenges Solved
| Challenge | Solution |
|---|---|
| Interactive GRUB password prompts | Ansible Vault with pre-generated hash |
| Audit immutable lockouts | Pre-flight safety checks before enabling |
| Firewall SSH lockouts | Rate limiting instead of blocking |
| Inconsistent Lynis scores | Systematic approach with documented improvements |
| Shell command non-idempotency | Native Ansible modules throughout |
Key Learnings
- Enterprise security hardening requires systematic, auditable approaches
- Automation with proper safeguards prevents operational incidents
- Security tools integration provides defense-in-depth
- Lynis scoring provides measurable security improvement metrics
- Handler-based notifications allow controlled maintenance windows
- Documentation is critical for reproducibility and compliance audits