--- - name: Security Audit - Generate Reports hosts: all:!localhost become: true gather_facts: true tasks: - name: Create audit directory ansible.builtin.file: path: "/tmp/security-audit-{{ inventory_hostname }}" state: directory mode: '0755' delegate_to: localhost become: false - name: Collect SSH configuration ansible.builtin.shell: | sshd -T 2>/dev/null | grep -E '(permit|password|pubkey|port|authentication)' || echo "Unable to check SSH config" register: ssh_check changed_when: false failed_when: false - name: Collect firewall status ansible.builtin.shell: | if command -v ufw >/dev/null 2>&1; then ufw status numbered 2>/dev/null || echo "UFW not active" else echo "No firewall detected" fi register: firewall_check changed_when: false - name: Collect open ports ansible.builtin.shell: ss -tlnp | grep LISTEN register: ports_check changed_when: false - name: Collect sudo users ansible.builtin.shell: getent group sudo 2>/dev/null || getent group wheel 2>/dev/null || echo "No sudo group" register: sudo_check changed_when: false - name: Collect password authentication users ansible.builtin.shell: | awk -F: '($2 != "!" && $2 != "*" && $2 != "") {print $1}' /etc/shadow 2>/dev/null | head -20 || echo "Unable to check" register: pass_users_check changed_when: false failed_when: false - name: Collect recent failed logins ansible.builtin.shell: | journalctl -u sshd --no-pager -n 50 2>/dev/null | grep -i "failed\|authentication failure" | tail -10 || echo "No recent failures or unable to check" register: failed_logins_check changed_when: false failed_when: false - name: Check automatic updates ansible.builtin.shell: | if [ -f /etc/apt/apt.conf.d/20auto-upgrades ]; then echo "Automatic updates: ENABLED" cat /etc/apt/apt.conf.d/20auto-upgrades else echo "Automatic updates: NOT CONFIGURED" fi register: auto_updates_check changed_when: false - name: Check for available security updates ansible.builtin.shell: | apt-get update -qq 2>&1 | head -5 apt list --upgradable 2>/dev/null | grep -i security | wc -l || echo "0" register: security_updates_check changed_when: false failed_when: false - name: Generate security report ansible.builtin.copy: content: | ╔════════════════════════════════════════════════════════════════╗ ║ Security Audit Report: {{ inventory_hostname }} ║ IP: {{ ansible_host }} ║ Date: {{ ansible_date_time.iso8601 }} ╚════════════════════════════════════════════════════════════════╝ === SYSTEM INFORMATION === OS: {{ ansible_distribution }} {{ ansible_distribution_version }} Kernel: {{ ansible_kernel }} Architecture: {{ ansible_architecture }} === SSH CONFIGURATION === {{ ssh_check.stdout }} === FIREWALL STATUS === {{ firewall_check.stdout }} === OPEN NETWORK PORTS === {{ ports_check.stdout }} === SUDO USERS === {{ sudo_check.stdout }} === USERS WITH PASSWORD AUTH === {{ pass_users_check.stdout }} === RECENT FAILED LOGIN ATTEMPTS === {{ failed_logins_check.stdout }} === AUTOMATIC UPDATES === {{ auto_updates_check.stdout }} === AVAILABLE SECURITY UPDATES === Security updates available: {{ security_updates_check.stdout_lines[-1] | default('Unknown') }} dest: "/tmp/security-audit-{{ inventory_hostname }}/report.txt" mode: '0644' delegate_to: localhost become: false - name: Generate Summary Report hosts: localhost gather_facts: false tasks: - name: Find all audit reports ansible.builtin.find: paths: /tmp patterns: "security-audit-*/report.txt" recurse: true register: audit_reports - name: Display report locations ansible.builtin.debug: msg: | ╔════════════════════════════════════════════════════════════════╗ ║ Security Audit Complete ║ ╚════════════════════════════════════════════════════════════════╝ Reports generated for {{ audit_reports.files | length }} servers View individual reports: {% for file in audit_reports.files %} - {{ file.path }} {% endfor %} View all reports: cat /tmp/security-audit-*/report.txt Create consolidated report: cat /tmp/security-audit-*/report.txt > /tmp/security-audit-full-report.txt