Automated, idempotent Linux system hardening pipeline targeting Center for Internet Security (CIS) benchmarks for Ubuntu Server (ARM64).
Security posture improvements are benchmarked using Lynis system audits across sequential hardening milestones:
| Hardening Phase | Lynis Index | Focus Areas | Status |
|---|---|---|---|
| Baseline | 61 |
Stock Ubuntu Server 24.04 LTS installation | Initial State |
| OS Hardening | 71 |
Kernel (sysctl), SSH Daemon (ssh), Filesystem mounts (filesystem) |
Completed |
| Auditing & Detection (Local VM) | 80 |
Kernel Auditing (auditd), Intrusion Prevention (fail2ban), FIM (aide), Rootkits (rkhunter) |
Completed |
| AWS Cloud Hardening & SSM Baseline | 79 |
UFW Firewall, Process Accounting Daemons (acct, sysstat), Zero-Ingress Security Group |
Completed |
The pipeline deploys and hardens instances without exposing administrative network interfaces to the public internet:
[ Developer Workstation ] (macOS / Python venv)
β
β Ansible Playbook via community.aws.aws_ssm transport
βΌ
[ AWS Systems Manager / S3 Staging Bucket ]
β
β Encrypted Agent Channel (Outbound HTTPS 443 Only)
βΌ
[ AWS Custom VPC (10.0.0.0/16) ]
βββ [ Public Subnet (10.0.0.0/24) ]
βββ [ EC2 t4g.micro (ARM64 / Ubuntu 24.04 LTS) ]
βββ Security Group: 0 Inbound Rules (Port 22 Closed)
βββ Host Firewall: UFW Default-Deny Ingress
βββ Auditing Subsystems: auditd + AIDE + fail2ban
βββ Output Artifact: Hardened Golden AMI (ami-0bbf26689dd6f40b6)
- No Open SSH Port: Admin access is managed out-of-band via AWS Systems Manager Session Manager, removing SSH brute-force attack vectors.
- Ephemeral Staging: Playbooks and payload files are staged in a private S3 bucket and led down dynamically by AWS SSM Agent.
- Immutable Snapshot: Compliant image captured as reusable Golden AMI before destroying EC2 instance.
- ASLR Enforcement: Configures
kernel.randomize_va_space = 2to mitigate memory corruption and buffer overflow exploits. - Kernel Pointer & Buffer Restraints: Enforces
kernel.kptr_restrict = 2andkernel.dmesg_restrict = 1to hide sensitive kernel memory addresses from unprivileged users. - Network Defense-in-Depth:
- Mitigates SYN flood denial-of-service via
net.ipv4.tcp_syncookies = 1. - Drops spoofed packets via Reverse Path Filtering (
rp_filter = 1). - Disables ICMP redirect acceptance and packet routing forwarding.
- Enables martian packet logging for source-route anomaly detection.
- Mitigates SYN flood denial-of-service via
- Disables remote root login (
PermitRootLogin no) and blank passwords (PermitEmptyPasswords no). - Enforces key-based authentication exclusively (
PasswordAuthentication no). - Implements connection rate limiting (
MaxAuthTries 3) and idle session termination (ClientAliveInterval 300,ClientAliveCountMax 2). - Disables X11 graphical forwarding (
X11Forwarding no). - Utilizes configuration validation hooks (
validate: /usr/sbin/sshd -t -f %s) to prevent service lockout on syntax errors.
- Legacy Filesystem Blacklisting: Disables obsolete filesystems (
cramfs,freevxfs,jffs2,hfs,hfsplus,udf) via/etc/modprobe.d/overrides to minimize kernel attack surfaces. - Shared Memory Isolation: Restricts
/dev/shmmounts in/etc/fstabwithnodev,nosuid, andnoexecflags.
- Enforces password entropy via
libpam-pwquality(minimum 14 characters, character class diversity, history restrictions). - Establishes password aging thresholds (
PASS_MAX_DAYS 90,PASS_MIN_DAYS 1) and restrictive default umasks (UMASK 027) in/etc/login.defs.
- Deploys
auditdandaudispd-pluginswith persistent buffer management. - Configures immutable (
-e 2) kernel audit rules monitoring modifications to authentication databases (/etc/passwd,/etc/shadow), sudoers privileges, and system network configurations. - Enables process accounting daemons
sysstatandacct.
- Integrates
fail2banwith thesystemdjournal backend. - Configures
ufwwith default-deny ingress policy behind AWS Security Groups.
- Deploys AIDE (Advanced Intrusion Detection Environment) for cryptographic file integrity monitoring.
- Configures RKHunter for automated rootkit, backdoor, and exploit pattern analysis.
- Audits core package authenticity against upstream cryptographic hashes via
debsums. - Restricts compiler access (
gcc,as,g++) to root execution only.
- Deploys authorized access legal notices to
/etc/issue(local console) and/etc/issue.net(remote SSH sessions).
All Ansible tasks, playbooks, and configuration files are validated continuously on pull requests via GitHub Actions:
- YAML Linting: Validates formatting and syntax consistency via
yamllint. - Playbook Syntax Check: Runs
ansible-playbook --syntax-checkprior to staging. - Security & Best Practice Linting: Enforces modern Ansible standards and catches execution antipatterns using
ansible-lint.
ansible-hardening/
βββ .github/
β βββ workflows/
β βββ lint.yml # CI workflow for syntax & security linting
βββ terraform/ # AWS Infrastructure as Code definitions
β βββ main.tf # VPC, IAM Instance Profile, S3 Staging Bucket, EC2
β βββ .terraform.lock.hcl # Deterministic provider version locks
βββ ansible.cfg # Execution settings & privilege escalation
βββ inventory.ini # Target host definitions (SSM transport configuration)
βββ site.yml # Master orchestration playbook
βββ docs/
β βββ images/ # Audit scan evidence and validation metrics
βββ roles/
βββ security/
βββ handlers/
β βββ main.yml # Service notification handlers (e.g., sshd reload)
βββ tasks/
βββ main.yml # Primary task execution entry point
βββ sysctl.yml # Kernel tuning and network defense
βββ ssh.yml # SSH daemon hardening & privilege separation
βββ filesystem.yml # Filesystem restrictions and module blacklisting
βββ pam.yml # PAM policies and password aging
βββ auditd.yml # Linux audit subsystem rules
βββ fail2ban.yml # Dynamic IP banning rules
βββ integrity.yml # AIDE FIM, RKHunter, and compiler restrictions
βββ banners.yml # Compliance login notices
-
Local vs Cloud Score Differences (Lynis 80 vs 79): Local machine hardening scores (80) rely on dedicated disk partitioning. On cloud architectures, the multi-partition scheme adds unnecessary overhead for disk expansion and can break application runtimes. Achieving a 79 is ideal for CIS compliance while maintaining cloud operability.
-
Zero-Ingress Transport: Running Ansible over SSM and not SSH requires standardizing on
ansible_user=ssm-userand routing execution staging through S3 bucket with restrictive IAM access. -
Preserving Hardened Configs During Upgrades: Automated package upgrades (apt-get dist-upgrade) trigger maintainer file overwrite prompts. CIS-managed templates (such as /etc/ssh/sshd_config) need to be retained to maintain configuration baseline integrity.
-
Idempotency in Automation: Designing tasks with validation checks ensures repeated playbook runs enforce baseline state without service interruptions or configuration drift.
- Target Host: Ubuntu Server 24.04+ (ARM64 / x86_64)
- Local Workstation: Python 3.10+ in a virtual environment (
.venv), Terraform 1.5+, AWS CLI configured with appropriate admin permissions. - Required Collections:
ansible-galaxy collection install amazon.aws community.aws ansible.posix
-
Clone the repository:
git clone https://github.com/<your-username>/ansible-hardening.git cd ansible-hardening
-
Configure your inventory: Update inventory.ini with your target host connection settings (or use local execution).
[aws_hardening_targets] i-037ff79be19ce2ed8 [aws_hardening_targets:vars] ansible_connection=community.aws.aws_ssm ansible_aws_ssm_region=us-east-1 ansible_aws_ssm_bucket_name=ansible-ssm-staging-your-bucket-id ansible_user=ssm-user ansible_become=true ansible_python_interpreter=/usr/bin/python3
-
Execute the playbook:
export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES # If running Python under macOS Apple Silicon...resolves POSIX fork() constraints ansible-playbook -i inventory.ini site.yml
-
Initialize Integrity Baselines and Audit via SSM
aws ssm start-session --target <INSTANCE_ID> # Inside SSM Session: sudo aideinit && sudo cp /var/lib/aide/aide.db.new /var/lib/aide/aide.db sudo rkhunter --propupd sudo lynis audit system --quick exit
-
Bake Golden AMI and Destroy
# Bake AMI (From local workstation) aws ec2 create-image \ --instance-id <INSTANCE_ID> \ --name "ubuntu-24.04-arm64-hardened-cis-$(date +%Y%m%d)" \ --description "Ubuntu 24.04 ARM64 CIS hardened image" \ --no-reboot # Wait for availability aws ec2 wait image-available --image-ids <NEW_IMAGE_ID> # Tear down compute infrastructure to stop billing cd terraform terraform destroy -auto-approve