Ansible for Compliance Automation: CIS Benchmarks, STIG, and PCI DSS
By Luca Berton · Published 2024-01-01 · Category: installation
Automate security compliance with Ansible. Implement CIS Benchmarks, DISA STIG, PCI DSS, and SOC 2 hardening across enterprise infrastructure at scale.
Introduction
Security compliance is not optional — regulations like PCI DSS, SOC 2, HIPAA, and government standards like CIS Benchmarks and DISA STIGs require organizations to maintain hardened configurations across their entire infrastructure. Doing this manually is impossible at scale. Ansible transforms compliance from a periodic audit nightmare into continuous, automated enforcement.
See also: AAP 2.6 Compliance and Audit: CIS Benchmarks, STIG, and Regulatory Automation
Compliance Frameworks
| Framework | Scope | Who Needs It | |-----------|-------|-------------| | CIS Benchmarks | OS/app hardening | Everyone (best practice) | | DISA STIG | Government security | US DoD, federal agencies | | PCI DSS | Payment card data | Anyone handling credit cards | | SOC 2 | Service security | SaaS providers, cloud services | | HIPAA | Health data | Healthcare organizations | | NIST 800-53 | Security controls | US federal information systems |
Compliance as Code
The key principle: define compliance rules as Ansible playbooks, then enforce them continuously.
# compliance-baseline.yml
---
- name: Apply CIS Level 1 baseline
hosts: all
become: true
roles:
- cis_level1_filesystem
- cis_level1_services
- cis_level1_network
- cis_level1_logging
- cis_level1_access
See also: Ansible for Financial Services: Compliance, Trading Systems, and Regulatory Automation
CIS Benchmark Implementation
Filesystem Hardening
# CIS 1.1.1 — Disable unused filesystems
- name: "CIS 1.1.1.1 | Disable cramfs"
ansible.builtin.copy:
content: "install cramfs /bin/true\nblacklist cramfs\n"
dest: /etc/modprobe.d/cramfs.conf
mode: '0644'
- name: "CIS 1.1.1.2 | Disable freevxfs"
ansible.builtin.copy:
content: "install freevxfs /bin/true\nblacklist freevxfs\n"
dest: /etc/modprobe.d/freevxfs.conf
# CIS 1.1.2 — /tmp on separate partition with noexec
- name: "CIS 1.1.2 | Ensure /tmp has noexec"
ansible.posix.mount:
name: /tmp
src: "{{ tmp_device }}"
fstype: tmpfs
opts: defaults,noexec,nosuid,nodev
state: mounted
Service Hardening
# CIS 2.1 — Disable unnecessary services
- name: "CIS 2.1 | Disable unneeded services"
ansible.builtin.systemd:
name: "{{ item }}"
state: stopped
enabled: false
masked: true
loop:
- avahi-daemon
- cups
- rpcbind
- nfs-server
- vsftpd
- xinetd
ignore_errors: true # Service may not be installed
# CIS 2.2 — Ensure time synchronization
- name: "CIS 2.2.1 | Ensure chrony is configured"
ansible.builtin.template:
src: chrony.conf.j2
dest: /etc/chrony.conf
notify: restart chronyd
SSH Hardening
# CIS 5.2 — SSH Server Configuration
- name: "CIS 5.2 | Harden SSH configuration"
ansible.builtin.lineinfile:
path: /etc/ssh/sshd_config
regexp: "{{ item.regexp }}"
line: "{{ item.line }}"
loop:
- { regexp: '^#?PermitRootLogin', line: 'PermitRootLogin no' }
- { regexp: '^#?PasswordAuthentication', line: 'PasswordAuthentication no' }
- { regexp: '^#?MaxAuthTries', line: 'MaxAuthTries 4' }
- { regexp: '^#?ClientAliveInterval', line: 'ClientAliveInterval 300' }
- { regexp: '^#?ClientAliveCountMax', line: 'ClientAliveCountMax 3' }
- { regexp: '^#?LoginGraceTime', line: 'LoginGraceTime 60' }
- { regexp: '^#?X11Forwarding', line: 'X11Forwarding no' }
- { regexp: '^#?AllowTcpForwarding', line: 'AllowTcpForwarding no' }
- { regexp: '^#?Protocol', line: 'Protocol 2' }
notify: restart sshd
Audit Logging
# CIS 4.1 — Configure auditd
- name: "CIS 4.1.1 | Ensure auditd is installed"
ansible.builtin.package:
name:
- audit
- audit-libs
state: present
- name: "CIS 4.1.2 | Configure audit rules"
ansible.builtin.template:
src: audit.rules.j2
dest: /etc/audit/rules.d/cis.rules
notify: restart auditd
DISA STIG Automation
# Use the RHEL STIG role from Ansible Galaxy
# ansible-galaxy install redhatofficial.rhel9_stig
- name: Apply DISA STIG to RHEL 9
hosts: government_servers
become: true
vars:
# Override specific controls
rhel9stig_ssh_required: true
rhel9stig_fips_required: true
rhel9stig_aide_required: true
roles:
- redhatofficial.rhel9_stig
See also: Ansible Automation Platform RBAC: Role-Based Access Control for Enterprise Teams
PCI DSS Requirements
# PCI DSS Requirement 2 — Change default passwords
- name: "PCI 2.1 | Ensure no default passwords"
ansible.builtin.user:
name: "{{ item }}"
password_lock: true
loop: "{{ default_accounts }}"
# PCI DSS Requirement 8 — Password policy
- name: "PCI 8.2 | Set password complexity"
ansible.builtin.lineinfile:
path: /etc/security/pwquality.conf
regexp: "{{ item.regexp }}"
line: "{{ item.line }}"
loop:
- { regexp: '^minlen', line: 'minlen = 12' }
- { regexp: '^dcredit', line: 'dcredit = -1' }
- { regexp: '^ucredit', line: 'ucredit = -1' }
- { regexp: '^lcredit', line: 'lcredit = -1' }
- { regexp: '^ocredit', line: 'ocredit = -1' }
# PCI DSS Requirement 10 — Logging
- name: "PCI 10.2 | Ensure audit logging"
ansible.builtin.service:
name: auditd
state: started
enabled: true
Compliance Scanning (Audit Mode)
Run playbooks in check mode to audit without changing anything:
# Dry-run compliance check
ansible-playbook compliance-baseline.yml --check --diff
# Generate compliance report
ansible-playbook compliance-audit.yml -e "report_output=/tmp/compliance-report.html"
Audit Playbook Pattern
- name: Compliance audit
hosts: all
become: true
tasks:
- name: "Check SSH PermitRootLogin"
ansible.builtin.command: grep "^PermitRootLogin no" /etc/ssh/sshd_config
register: ssh_root
changed_when: false
failed_when: false
- name: Record compliance status
ansible.builtin.set_fact:
compliance_results: "{{ compliance_results | default([]) + [
{'control': 'SSH-001', 'name': 'PermitRootLogin', 'status': 'PASS' if ssh_root.rc == 0 else 'FAIL'}
] }}"
- name: Generate report
ansible.builtin.template:
src: compliance-report.j2
dest: "/tmp/compliance-{{ inventory_hostname }}.json"
delegate_to: localhost
Continuous Compliance with AAP
# Schedule compliance checks in AAP:
# 1. Daily audit scan (check mode) → generates report
# 2. Weekly remediation run → enforces baseline
# 3. Alert on drift → notify security team
# Workflow Template:
# [Audit Scan] → [Diff Report] → [Approval Gate] → [Remediate]
Community Roles
# CIS Benchmark roles
ansible-galaxy install alivx.cis_ubuntu_22
ansible-galaxy install MindPointGroup.RHEL9-CIS
# STIG roles
ansible-galaxy install redhatofficial.rhel9_stig
# Hardening
ansible-galaxy install dev-sec.os-hardening
ansible-galaxy install dev-sec.ssh-hardening
Best Practices
Start with check mode — Audit before enforcing to understand the gap Tag controls individually — Run specific controls:--tags "cis_5.2,cis_4.1"
Environment-specific exceptions — Some controls may not apply to all systems
Version your compliance code — Git-managed playbooks with change history
Automate reporting — Generate machine-readable compliance reports (JSON/CSV)
Schedule regular scans — Daily audits, weekly remediation in AAP
Test in staging first — Compliance hardening can break applications
Document exceptions — Track why specific controls are skipped
FAQ
Can Ansible replace a compliance scanner?
Ansible can enforce and audit configurations but may not replace dedicated scanners like OpenSCAP or Qualys for vulnerability assessment. They complement each other — Ansible remediates what scanners find.
How to handle exceptions?
Use variables to skip controls: cis_5_2_enabled: false. Document why in your compliance repository.
CIS Level 1 vs Level 2?
Level 1 is practical for most environments. Level 2 adds stricter controls that may impact usability (e.g., USB storage disabled, specific kernel parameters). Start with Level 1.
Conclusion
Ansible transforms compliance from a manual audit exercise into continuous automated enforcement. By codifying CIS Benchmarks, STIG, and PCI DSS requirements as playbooks, organizations can maintain compliance at scale while reducing the cost and effort of security audits.
Related Articles
• Ansible Roles Complete Guide • Ansible Check Mode Dry Run Guide • Ansible Automation Platform 2.6Category: installation