Implement standardized playbook organization with master orchestrator and Ansible collections requirements for extended functionality. Playbook Structure: playbooks/ ├── gather_system_info.yml # System inventory gathering ├── deploy_vm.yml # VM deployment (placeholder) ├── security_audit.yml # Security compliance checking (placeholder) ├── maintenance.yml # Routine maintenance tasks (placeholder) ├── backup.yml # Backup operations (placeholder) └── disaster_recovery.yml # DR procedures (placeholder) Master Playbook (site.yml): - Entry point for all infrastructure operations - Import structure for modular playbook organization - Tag-based execution for selective operations - Pre-flight checks and validations - Comprehensive documentation and usage examples Collections Requirements (collections/requirements.yml): - community.general: Essential utilities and modules - community.libvirt: KVM/libvirt management - ansible.posix: POSIX system administration - amazon.aws: AWS infrastructure management (optional) - Community versions for open-source compatibility Implemented Playbooks: 1. gather_system_info.yml: - Comprehensive system information gathering - Uses system_info role - Statistics export to ./stats/machines/ - Health checks and validation - Tag support: install, gather, export, validate, health-check 2. Placeholder Playbooks (documented structure): - deploy_vm.yml: VM provisioning with deploy_linux_vm role - security_audit.yml: CIS benchmark compliance checking - maintenance.yml: Updates, cleanup, optimization - backup.yml: Backup operations orchestration - disaster_recovery.yml: DR procedures and testing site.yml Master Playbook Features: - Central orchestration point - Import-based playbook inclusion - Tag inheritance and selective execution - Environment-aware (development, staging, production) - Pre-flight validation checks - Error handling and rollback support - Comprehensive inline documentation Usage Examples: ```bash # Run all playbooks ansible-playbook site.yml # Run specific playbook ansible-playbook site.yml --tags gather_info # Gather system information only ansible-playbook playbooks/gather_system_info.yml # Check syntax ansible-playbook site.yml --syntax-check # Dry run ansible-playbook site.yml --check # Limit to specific hosts ansible-playbook site.yml -l webservers ``` Collections Management: - Install: ansible-galaxy collection install -r collections/requirements.yml - Update: ansible-galaxy collection install -r collections/requirements.yml --upgrade - Location: ./collections/ (local) and ~/.ansible/collections (user) - Version pinning for stability - Community alternatives for RHEL-free deployments CLAUDE.md Compliance: ✅ Playbooks in ./playbooks/ directory ✅ Master playbook (site.yml) at root ✅ Tag-based execution support ✅ Modular organization with import_playbook ✅ Collections requirements documented ✅ Clear separation: playbooks (lasting) vs plays (temporary) Benefits: - Standardized playbook organization - Easy-to-navigate structure - Tag-based selective execution - Collection dependency management - Scalable to 100+ playbooks - Clear entry point (site.yml) - Environment isolation Next Steps: 1. Install collections: ansible-galaxy collection install -r collections/requirements.yml 2. Implement placeholder playbooks as needed 3. Add role-specific playbooks to playbooks/ directory 4. Create temporary plays in plays/ directory (per CLAUDE.md) 5. Test site.yml orchestration: ansible-playbook site.yml --check 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
398 lines
14 KiB
YAML
398 lines
14 KiB
YAML
---
|
|
# =============================================================================
|
|
# Security Audit and Compliance Playbook
|
|
# =============================================================================
|
|
#
|
|
# This playbook performs comprehensive security audits across all managed hosts
|
|
# including configuration compliance, vulnerability checks, and security posture
|
|
# assessment.
|
|
#
|
|
# Usage:
|
|
# ansible-playbook playbooks/security_audit.yml
|
|
# ansible-playbook playbooks/security_audit.yml --limit production
|
|
# ansible-playbook playbooks/security_audit.yml --tags selinux,firewall
|
|
#
|
|
# Tags:
|
|
# audit - All audit tasks
|
|
# selinux - SELinux status checks
|
|
# apparmor - AppArmor status checks
|
|
# firewall - Firewall configuration checks
|
|
# ssh - SSH configuration audit
|
|
# packages - Package and update checks
|
|
# users - User and permission audits
|
|
# network - Network security checks
|
|
# compliance - Compliance verification
|
|
# report - Generate audit reports
|
|
#
|
|
# =============================================================================
|
|
|
|
- name: Security Audit and Compliance Check
|
|
hosts: all
|
|
become: true
|
|
gather_facts: true
|
|
|
|
vars:
|
|
audit_timestamp: "{{ ansible_date_time.iso8601 }}"
|
|
audit_report_dir: "./reports/security_audit/{{ ansible_date_time.date }}"
|
|
failed_checks: []
|
|
|
|
pre_tasks:
|
|
- name: Create audit report directory
|
|
file:
|
|
path: "{{ audit_report_dir }}"
|
|
state: directory
|
|
mode: '0755'
|
|
delegate_to: localhost
|
|
become: false
|
|
run_once: true
|
|
tags: [always]
|
|
|
|
- name: Display audit banner
|
|
debug:
|
|
msg:
|
|
- "========================================="
|
|
- "Security Audit Starting"
|
|
- "========================================="
|
|
- "Host: {{ inventory_hostname }}"
|
|
- "Environment: {{ environment | default('unknown') }}"
|
|
- "Timestamp: {{ audit_timestamp }}"
|
|
- "========================================="
|
|
tags: [always]
|
|
|
|
tasks:
|
|
# =========================================================================
|
|
# SELinux Audit (RHEL/CentOS/Rocky/Alma)
|
|
# =========================================================================
|
|
|
|
- name: Check SELinux status
|
|
command: getenforce
|
|
register: audit_selinux_status
|
|
changed_when: false
|
|
failed_when: false
|
|
when: ansible_os_family == "RedHat"
|
|
tags: [audit, selinux, compliance]
|
|
|
|
- name: Verify SELinux is enforcing
|
|
assert:
|
|
that:
|
|
- audit_selinux_status.stdout == "Enforcing"
|
|
fail_msg: "SELinux is not in enforcing mode (current: {{ audit_selinux_status.stdout }})"
|
|
success_msg: "SELinux is enforcing"
|
|
when: ansible_os_family == "RedHat"
|
|
ignore_errors: true
|
|
register: audit_selinux_check
|
|
tags: [audit, selinux, compliance]
|
|
|
|
- name: Check SELinux denials
|
|
shell: ausearch -m avc -ts recent 2>/dev/null | wc -l
|
|
register: audit_selinux_denials
|
|
changed_when: false
|
|
failed_when: false
|
|
when: ansible_os_family == "RedHat"
|
|
tags: [audit, selinux]
|
|
|
|
# =========================================================================
|
|
# AppArmor Audit (Debian/Ubuntu)
|
|
# =========================================================================
|
|
|
|
- name: Check AppArmor status
|
|
command: aa-status --json
|
|
register: audit_apparmor_status
|
|
changed_when: false
|
|
failed_when: false
|
|
when: ansible_os_family == "Debian"
|
|
tags: [audit, apparmor, compliance]
|
|
|
|
- name: Verify AppArmor is enabled
|
|
assert:
|
|
that:
|
|
- "'profiles' in audit_apparmor_status.stdout"
|
|
fail_msg: "AppArmor is not properly enabled"
|
|
success_msg: "AppArmor is enabled and active"
|
|
when: ansible_os_family == "Debian"
|
|
ignore_errors: true
|
|
register: audit_apparmor_check
|
|
tags: [audit, apparmor, compliance]
|
|
|
|
# =========================================================================
|
|
# Firewall Audit
|
|
# =========================================================================
|
|
|
|
- name: Check firewalld status (RHEL)
|
|
systemd:
|
|
name: firewalld
|
|
state: started
|
|
check_mode: true
|
|
register: audit_firewalld_status
|
|
when: ansible_os_family == "RedHat"
|
|
tags: [audit, firewall, compliance]
|
|
|
|
- name: Get firewalld configuration (RHEL)
|
|
command: firewall-cmd --list-all
|
|
register: audit_firewalld_config
|
|
changed_when: false
|
|
when: ansible_os_family == "RedHat"
|
|
tags: [audit, firewall]
|
|
|
|
- name: Check UFW status (Debian)
|
|
command: ufw status verbose
|
|
register: audit_ufw_status
|
|
changed_when: false
|
|
when: ansible_os_family == "Debian"
|
|
tags: [audit, firewall, compliance]
|
|
|
|
- name: Verify firewall is active (Debian)
|
|
assert:
|
|
that:
|
|
- "'Status: active' in audit_ufw_status.stdout"
|
|
fail_msg: "UFW firewall is not active"
|
|
success_msg: "UFW firewall is active"
|
|
when: ansible_os_family == "Debian"
|
|
ignore_errors: true
|
|
register: audit_ufw_check
|
|
tags: [audit, firewall, compliance]
|
|
|
|
# =========================================================================
|
|
# SSH Configuration Audit
|
|
# =========================================================================
|
|
|
|
- name: Check SSH configuration for security settings
|
|
shell: sshd -T 2>/dev/null | grep -E "^(permitrootlogin|passwordauthentication|gssapiauthentication|maxauthtries)"
|
|
register: audit_ssh_config
|
|
changed_when: false
|
|
tags: [audit, ssh, compliance]
|
|
|
|
- name: Verify SSH hardening settings
|
|
assert:
|
|
that:
|
|
- "'permitrootlogin no' in audit_ssh_config.stdout.lower()"
|
|
- "'passwordauthentication no' in audit_ssh_config.stdout.lower()"
|
|
fail_msg: "SSH is not properly hardened"
|
|
success_msg: "SSH configuration meets security requirements"
|
|
ignore_errors: true
|
|
register: audit_ssh_check
|
|
tags: [audit, ssh, compliance]
|
|
|
|
# =========================================================================
|
|
# Package and Update Audit
|
|
# =========================================================================
|
|
|
|
- name: Check for available security updates (Debian)
|
|
shell: apt list --upgradable 2>/dev/null | grep -i security | wc -l
|
|
register: audit_debian_updates
|
|
changed_when: false
|
|
when: ansible_os_family == "Debian"
|
|
tags: [audit, packages]
|
|
|
|
- name: Check for available security updates (RHEL)
|
|
shell: dnf updateinfo list security 2>/dev/null | grep -E "^(Important|Critical)" | wc -l
|
|
register: audit_rhel_updates
|
|
changed_when: false
|
|
when: ansible_os_family == "RedHat"
|
|
tags: [audit, packages]
|
|
|
|
- name: Verify unattended-upgrades is enabled (Debian)
|
|
systemd:
|
|
name: unattended-upgrades
|
|
state: started
|
|
check_mode: true
|
|
register: audit_unattended_upgrades
|
|
when: ansible_os_family == "Debian"
|
|
tags: [audit, packages, compliance]
|
|
|
|
- name: Verify dnf-automatic is enabled (RHEL)
|
|
systemd:
|
|
name: dnf-automatic.timer
|
|
state: started
|
|
check_mode: true
|
|
register: audit_dnf_automatic
|
|
when: ansible_os_family == "RedHat"
|
|
tags: [audit, packages, compliance]
|
|
|
|
# =========================================================================
|
|
# User and Permission Audit
|
|
# =========================================================================
|
|
|
|
- name: Check for users with UID 0 (root-equivalent)
|
|
shell: awk -F':' '($3 == 0) { print $1 }' /etc/passwd
|
|
register: audit_uid0_users
|
|
changed_when: false
|
|
tags: [audit, users, compliance]
|
|
|
|
- name: Verify only root has UID 0
|
|
assert:
|
|
that:
|
|
- audit_uid0_users.stdout_lines == ['root']
|
|
fail_msg: "Multiple users with UID 0 detected: {{ audit_uid0_users.stdout_lines }}"
|
|
success_msg: "Only root has UID 0"
|
|
ignore_errors: true
|
|
register: audit_uid0_check
|
|
tags: [audit, users, compliance]
|
|
|
|
- name: Check for users with empty passwords
|
|
shell: awk -F':' '($2 == "" || $2 == "!") { print $1 }' /etc/shadow
|
|
register: audit_empty_passwords
|
|
changed_when: false
|
|
failed_when: false
|
|
tags: [audit, users, compliance]
|
|
|
|
- name: Check sudoers configuration
|
|
command: visudo -c
|
|
register: audit_sudoers
|
|
changed_when: false
|
|
failed_when: false
|
|
tags: [audit, users]
|
|
|
|
- name: Find world-writable files
|
|
shell: find / -xdev -type f -perm -0002 -not -path "/proc/*" -not -path "/sys/*" 2>/dev/null | head -20
|
|
register: audit_world_writable
|
|
changed_when: false
|
|
failed_when: false
|
|
async: 60
|
|
poll: 5
|
|
tags: [audit, users]
|
|
|
|
# =========================================================================
|
|
# Network Security Audit
|
|
# =========================================================================
|
|
|
|
- name: Check listening ports
|
|
shell: ss -tulpn | grep LISTEN
|
|
register: audit_listening_ports
|
|
changed_when: false
|
|
tags: [audit, network]
|
|
|
|
- name: Check for promiscuous network interfaces
|
|
shell: ip link show | grep -i promisc
|
|
register: audit_promisc_interfaces
|
|
changed_when: false
|
|
failed_when: false
|
|
tags: [audit, network]
|
|
|
|
- name: Verify IP forwarding is disabled (unless router)
|
|
sysctl:
|
|
name: net.ipv4.ip_forward
|
|
value: '0'
|
|
check_mode: true
|
|
register: audit_ip_forward
|
|
tags: [audit, network, compliance]
|
|
|
|
# =========================================================================
|
|
# Audit Logging
|
|
# =========================================================================
|
|
|
|
- name: Check auditd status
|
|
systemd:
|
|
name: auditd
|
|
state: started
|
|
check_mode: true
|
|
register: audit_auditd_status
|
|
tags: [audit, compliance]
|
|
|
|
- name: Check audit log size
|
|
stat:
|
|
path: /var/log/audit/audit.log
|
|
register: audit_log_stat
|
|
tags: [audit]
|
|
|
|
# =========================================================================
|
|
# File Integrity Monitoring
|
|
# =========================================================================
|
|
|
|
- name: Check if AIDE is installed
|
|
package:
|
|
name: aide
|
|
state: present
|
|
check_mode: true
|
|
register: audit_aide_installed
|
|
tags: [audit, compliance]
|
|
|
|
- name: Check AIDE database exists
|
|
stat:
|
|
path: /var/lib/aide/aide.db
|
|
register: audit_aide_db
|
|
when: audit_aide_installed.changed == false
|
|
tags: [audit]
|
|
|
|
# =========================================================================
|
|
# Compliance Checks
|
|
# =========================================================================
|
|
|
|
- name: Verify timezone is set to UTC
|
|
command: timedatectl show --property=Timezone --value
|
|
register: audit_timezone
|
|
changed_when: false
|
|
tags: [audit, compliance]
|
|
|
|
- name: Verify NTP is synchronized
|
|
command: timedatectl show --property=NTPSynchronized --value
|
|
register: audit_ntp_sync
|
|
changed_when: false
|
|
tags: [audit, compliance]
|
|
|
|
- name: Check kernel parameters (sysctl)
|
|
command: sysctl -a
|
|
register: audit_sysctl
|
|
changed_when: false
|
|
tags: [audit, compliance]
|
|
|
|
post_tasks:
|
|
# =========================================================================
|
|
# Generate Audit Report
|
|
# =========================================================================
|
|
|
|
- name: Generate audit report
|
|
template:
|
|
src: ../templates/security_audit_report.j2
|
|
dest: "{{ audit_report_dir }}/{{ inventory_hostname }}_audit_report.txt"
|
|
delegate_to: localhost
|
|
become: false
|
|
when: false # Enable when template is created
|
|
tags: [report]
|
|
|
|
- name: Display audit summary
|
|
debug:
|
|
msg:
|
|
- "========================================="
|
|
- "Security Audit Summary"
|
|
- "========================================="
|
|
- "Host: {{ inventory_hostname }}"
|
|
- "Environment: {{ environment | default('unknown') }}"
|
|
- ""
|
|
- "=== Security Modules ==="
|
|
- "{% if ansible_os_family == 'RedHat' %}SELinux: {{ audit_selinux_status.stdout | default('N/A') }}{% endif %}"
|
|
- "{% if ansible_os_family == 'Debian' %}AppArmor: Active{% endif %}"
|
|
- ""
|
|
- "=== Firewall ==="
|
|
- "{% if ansible_os_family == 'RedHat' %}Firewalld: {{ 'Active' if audit_firewalld_status is not failed else 'Inactive' }}{% endif %}"
|
|
- "{% if ansible_os_family == 'Debian' %}UFW: {{ 'Active' if 'Status: active' in audit_ufw_status.stdout else 'Inactive' }}{% endif %}"
|
|
- ""
|
|
- "=== SSH Security ==="
|
|
- "Root Login: {{ 'Disabled' if 'permitrootlogin no' in audit_ssh_config.stdout.lower() else 'ENABLED - ACTION REQUIRED' }}"
|
|
- "Password Auth: {{ 'Disabled' if 'passwordauthentication no' in audit_ssh_config.stdout.lower() else 'ENABLED - ACTION REQUIRED' }}"
|
|
- ""
|
|
- "=== Updates ==="
|
|
- "{% if ansible_os_family == 'Debian' %}Security updates available: {{ audit_debian_updates.stdout }}{% endif %}"
|
|
- "{% if ansible_os_family == 'RedHat' %}Critical/Important updates: {{ audit_rhel_updates.stdout }}{% endif %}"
|
|
- ""
|
|
- "=== Users ==="
|
|
- "UID 0 users: {{ audit_uid0_users.stdout_lines | join(', ') }}"
|
|
- ""
|
|
- "=== Audit Logging ==="
|
|
- "Auditd: {{ 'Active' if audit_auditd_status is not failed else 'Inactive' }}"
|
|
- "AIDE: {{ 'Installed' if audit_aide_installed.changed == false else 'Not Installed' }}"
|
|
- ""
|
|
- "=== Compliance ==="
|
|
- "Timezone: {{ audit_timezone.stdout }}"
|
|
- "NTP Sync: {{ audit_ntp_sync.stdout }}"
|
|
- ""
|
|
- "Report saved to: {{ audit_report_dir }}"
|
|
- "========================================="
|
|
tags: [always]
|
|
|
|
# =============================================================================
|
|
# Audit Report
|
|
# =============================================================================
|
|
# Reports are saved to: ./reports/security_audit/<date>/<hostname>_audit_report.txt
|
|
# =============================================================================
|