Ansible PCI DSS — Payment Card Industry Compliance
Introduction
Payment Card Industry Compliance. This guide covers implementing security controls, automating compliance checks, and maintaining audit-ready infrastructure with Ansible playbooks.
Overview
Security automation with Ansible ensures consistent policy enforcement across your entire fleet. Instead of manually configuring each server, define your security baseline as code and apply it uniformly.
Security Baseline Playbook
---
- name: Apply security baseline
hosts: all
become: true
vars:
security_ssh_port: 22
security_password_max_age: 90
security_password_min_length: 14
security_failed_login_attempts: 5
security_lockout_time: 900
tasks:
- name: Ensure security packages are installed
ansible.builtin.package:
name:
- fail2ban
- aide
- auditd
- rkhunter
state: present
- name: Configure SSH hardening
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: '^#?X11Forwarding', line: 'X11Forwarding no' }
- { regexp: '^#?MaxAuthTries', line: 'MaxAuthTries 3' }
- { regexp: '^#?ClientAliveInterval', line: 'ClientAliveInterval 300' }
- { regexp: '^#?ClientAliveCountMax', line: 'ClientAliveCountMax 2' }
notify: Restart SSH
- name: Set password policy
ansible.builtin.lineinfile:
path: /etc/login.defs
regexp: "{{ item.regexp }}"
line: "{{ item.line }}"
loop:
- { regexp: '^PASS_MAX_DAYS', line: 'PASS_MAX_DAYS {{ security_password_max_age }}' }
- { regexp: '^PASS_MIN_LEN', line: 'PASS_MIN_LEN {{ security_password_min_length }}' }
- { regexp: '^PASS_WARN_AGE', line: 'PASS_WARN_AGE 14' }
Audit and Compliance Checks
- name: Check for unauthorized SUID binaries
ansible.builtin.command:
cmd: find / -perm -4000 -type f 2>/dev/null
register: suid_files
changed_when: false
- name: Verify file permissions
ansible.builtin.file:
path: "{{ item.path }}"
mode: "{{ item.mode }}"
owner: root
group: root
loop:
- { path: '/etc/passwd', mode: '0644' }
- { path: '/etc/shadow', mode: '0640' }
- { path: '/etc/group', mode: '0644' }
- { path: '/etc/gshadow', mode: '0640' }
- name: Ensure auditd is running
ansible.builtin.systemd:
name: auditd
state: started
enabled: true
- name: Configure audit rules
ansible.builtin.copy:
content: |
# Monitor authentication events
-w /etc/passwd -p wa -k identity
-w /etc/shadow -p wa -k identity
-w /etc/group -p wa -k identity
-w /var/log/faillog -p wa -k logins
-w /var/log/lastlog -p wa -k logins
# Monitor sudo usage
-w /etc/sudoers -p wa -k sudo_changes
-w /etc/sudoers.d/ -p wa -k sudo_changes
dest: /etc/audit/rules.d/security.rules
mode: '0640'
notify: Restart auditd
Firewall Configuration
- name: Configure firewall (UFW)
community.general.ufw:
rule: "{{ item.rule }}"
port: "{{ item.port }}"
proto: "{{ item.proto | default('tcp') }}"
loop:
- { rule: allow, port: '22' }
- { rule: allow, port: '80' }
- { rule: allow, port: '443' }
- name: Set default deny policy
community.general.ufw:
default: deny
direction: incoming
- name: Enable firewall
community.general.ufw:
state: enabled
Compliance Report
- name: Generate compliance report
ansible.builtin.template:
src: compliance-report.j2
dest: "/var/log/compliance-{{ ansible_date_time.date }}.txt"
mode: '0600'
- name: Check compliance status
ansible.builtin.assert:
that:
- ansible_facts['os_family'] in ['Debian', 'RedHat']
- suid_files.stdout_lines | length < 50
fail_msg: "Compliance check failed"
success_msg: "All compliance checks passed"
Handlers
handlers:
- name: Restart SSH
ansible.builtin.systemd:
name: sshd
state: restarted
- name: Restart auditd
ansible.builtin.systemd:
name: auditd
state: restarted
Scheduled Compliance Scans
---
- name: Automated compliance scan
hosts: all
become: true
tasks:
- name: Run AIDE integrity check
ansible.builtin.command:
cmd: aide --check
register: aide_result
changed_when: false
failed_when: false
- name: Alert on integrity violations
ansible.builtin.debug:
msg: "AIDE detected changes on {{ inventory_hostname }}"
when: aide_result.rc != 0
Troubleshooting
| Issue | Solution |
|---|---|
| SSH lockout | Ensure SSH key auth works before disabling passwords |
| Audit log full | Configure log rotation for /var/log/audit/ |
| False positives | Tune rules to exclude known-good changes |
| Performance impact | Schedule intensive scans during maintenance windows |
Best Practices
- Start with a baseline — apply minimum security standards to all hosts
- Layer controls — combine network, host, and application security
- Automate scanning — run compliance checks on schedule
- Version control everything — track security policy changes in Git
- Test before enforcing — use
--check --diffmode first - Document exceptions — maintain a risk register for accepted deviations
Conclusion
Security automation with Ansible transforms manual, error-prone compliance work into repeatable, auditable code. Apply these patterns to your environment, customize the controls for your compliance framework, and run regular scans to maintain your security posture.