--- - name: Security Audit and Hardening hosts: all gather_facts: yes vars: audit_timestamp: "{{ ansible_date_time.iso8601 }}" security_report_dir: "/tmp/security_reports" tasks: - name: Create security reports directory file: path: "{{ security_report_dir }}" state: directory mode: '0755' delegate_to: localhost run_once: true - name: Check system updates shell: | if command -v apt >/dev/null 2>&1; then apt list --upgradable 2>/dev/null | wc -l elif command -v yum >/dev/null 2>&1; then yum check-update --quiet | wc -l else echo "0" fi register: pending_updates changed_when: false ignore_errors: yes - name: Check for security updates shell: | if command -v apt >/dev/null 2>&1; then apt list --upgradable 2>/dev/null | grep -i security | wc -l elif command -v yum >/dev/null 2>&1; then yum --security check-update --quiet 2>/dev/null | wc -l else echo "0" fi register: security_updates changed_when: false ignore_errors: yes - name: Check SSH configuration shell: | echo "=== SSH SECURITY AUDIT ===" if [ -f /etc/ssh/sshd_config ]; then echo "SSH Configuration:" echo "PermitRootLogin: $(grep -E '^PermitRootLogin' /etc/ssh/sshd_config | awk '{print $2}' || echo 'default')" echo "PasswordAuthentication: $(grep -E '^PasswordAuthentication' /etc/ssh/sshd_config | awk '{print $2}' || echo 'default')" echo "Port: $(grep -E '^Port' /etc/ssh/sshd_config | awk '{print $2}' || echo '22')" echo "Protocol: $(grep -E '^Protocol' /etc/ssh/sshd_config | awk '{print $2}' || echo 'default')" else echo "SSH config not accessible" fi register: ssh_audit changed_when: false ignore_errors: yes - name: Check firewall status shell: | echo "=== FIREWALL STATUS ===" if command -v ufw >/dev/null 2>&1; then echo "UFW Status:" ufw status verbose 2>/dev/null || echo "UFW not configured" elif command -v iptables >/dev/null 2>&1; then echo "IPTables Rules:" iptables -L -n | head -20 2>/dev/null || echo "IPTables not accessible" elif command -v firewall-cmd >/dev/null 2>&1; then echo "FirewallD Status:" firewall-cmd --state 2>/dev/null || echo "FirewallD not running" else echo "No firewall tools found" fi register: firewall_audit changed_when: false ignore_errors: yes - name: Check user accounts shell: | echo "=== USER ACCOUNT AUDIT ===" echo "Users with shell access:" grep -E '/bin/(bash|sh|zsh)$' /etc/passwd | cut -d: -f1 | sort echo "" echo "Users with sudo access:" if [ -f /etc/sudoers ]; then grep -E '^[^#]*ALL.*ALL' /etc/sudoers 2>/dev/null | cut -d' ' -f1 || echo "No sudo users found" fi echo "" echo "Recent logins:" last -n 10 2>/dev/null | head -10 || echo "Login history not available" register: user_audit changed_when: false ignore_errors: yes - name: Check file permissions shell: | echo "=== FILE PERMISSIONS AUDIT ===" echo "World-writable files in /etc:" find /etc -type f -perm -002 2>/dev/null | head -10 || echo "None found" echo "" echo "SUID/SGID files:" find /usr -type f \( -perm -4000 -o -perm -2000 \) 2>/dev/null | head -10 || echo "None found" echo "" echo "SSH key permissions:" if [ -d ~/.ssh ]; then ls -la ~/.ssh/ 2>/dev/null || echo "SSH directory not accessible" else echo "No SSH directory found" fi register: permissions_audit changed_when: false ignore_errors: yes - name: Check network security shell: | echo "=== NETWORK SECURITY AUDIT ===" echo "Open ports:" if command -v netstat >/dev/null 2>&1; then netstat -tuln | grep LISTEN | head -10 elif command -v ss >/dev/null 2>&1; then ss -tuln | grep LISTEN | head -10 else echo "No network tools available" fi echo "" echo "Network interfaces:" ip addr show 2>/dev/null | grep -E '^[0-9]+:' || echo "Network info not available" register: network_audit changed_when: false ignore_errors: yes - name: Check system services shell: | echo "=== SERVICE SECURITY AUDIT ===" if command -v systemctl >/dev/null 2>&1; then echo "Running services:" systemctl list-units --type=service --state=running --no-legend | head -15 echo "" echo "Failed services:" systemctl --failed --no-legend | head -5 else echo "Systemd not available" fi register: service_audit changed_when: false ignore_errors: yes - name: Check Docker security (if available) shell: | if command -v docker >/dev/null 2>&1 && docker info >/dev/null 2>&1; then echo "=== DOCKER SECURITY AUDIT ===" echo "Docker daemon info:" docker info --format '{{.SecurityOptions}}' 2>/dev/null || echo "Security options not available" echo "" echo "Privileged containers:" docker ps --format "table {{.Names}}\t{{.Status}}" --filter "label=privileged=true" 2>/dev/null || echo "No privileged containers found" echo "" echo "Containers with host network:" docker ps --format "table {{.Names}}\t{{.Ports}}" | grep -E '0\.0\.0\.0|::' | head -5 || echo "No host network containers found" else echo "Docker not available or not accessible" fi register: docker_audit changed_when: false ignore_errors: yes - name: Calculate security score set_fact: security_score: updates_pending: "{{ pending_updates.stdout | int }}" security_updates_pending: "{{ security_updates.stdout | int }}" ssh_root_login: "{{ 'SECURE' if 'no' in ssh_audit.stdout.lower() else 'INSECURE' }}" ssh_password_auth: "{{ 'SECURE' if 'no' in ssh_audit.stdout.lower() else 'INSECURE' }}" firewall_active: "{{ 'ACTIVE' if 'active' in firewall_audit.stdout.lower() or 'status: active' in firewall_audit.stdout.lower() else 'INACTIVE' }}" overall_risk: >- {{ 'HIGH' if ( (security_updates.stdout | int > 5) or ('yes' in ssh_audit.stdout.lower() and 'PermitRootLogin' in ssh_audit.stdout) or ('inactive' in firewall_audit.stdout.lower()) ) else 'MEDIUM' if ( (pending_updates.stdout | int > 10) or (security_updates.stdout | int > 0) ) else 'LOW' }} - name: Display security audit report debug: msg: | ========================================== 🔒 SECURITY AUDIT REPORT - {{ inventory_hostname }} ========================================== 📊 SECURITY SCORE: {{ security_score.overall_risk }} RISK 🔄 UPDATES: - Pending Updates: {{ security_score.updates_pending }} - Security Updates: {{ security_score.security_updates_pending }} 🔐 SSH SECURITY: - Root Login: {{ security_score.ssh_root_login }} - Password Auth: {{ security_score.ssh_password_auth }} 🛡️ FIREWALL: - Status: {{ security_score.firewall_active }} {{ ssh_audit.stdout }} {{ firewall_audit.stdout }} {{ user_audit.stdout }} {{ permissions_audit.stdout }} {{ network_audit.stdout }} {{ service_audit.stdout }} {{ docker_audit.stdout }} ========================================== - name: Generate JSON security report copy: content: | { "timestamp": "{{ audit_timestamp }}", "hostname": "{{ inventory_hostname }}", "security_score": { "overall_risk": "{{ security_score.overall_risk }}", "updates_pending": {{ security_score.updates_pending }}, "security_updates_pending": {{ security_score.security_updates_pending }}, "ssh_root_login": "{{ security_score.ssh_root_login }}", "ssh_password_auth": "{{ security_score.ssh_password_auth }}", "firewall_active": "{{ security_score.firewall_active }}" }, "audit_details": { "ssh_config": {{ ssh_audit.stdout | to_json }}, "firewall_status": {{ firewall_audit.stdout | to_json }}, "user_accounts": {{ user_audit.stdout | to_json }}, "file_permissions": {{ permissions_audit.stdout | to_json }}, "network_security": {{ network_audit.stdout | to_json }}, "services": {{ service_audit.stdout | to_json }}, "docker_security": {{ docker_audit.stdout | to_json }} }, "recommendations": [ {% if security_score.security_updates_pending | int > 0 %} "Apply {{ security_score.security_updates_pending }} pending security updates", {% endif %} {% if security_score.ssh_root_login == "INSECURE" %} "Disable SSH root login", {% endif %} {% if security_score.firewall_active == "INACTIVE" %} "Enable and configure firewall", {% endif %} {% if security_score.updates_pending | int > 20 %} "Apply system updates ({{ security_score.updates_pending }} pending)", {% endif %} "Regular security monitoring recommended" ] } dest: "{{ security_report_dir }}/{{ inventory_hostname }}_security_{{ ansible_date_time.epoch }}.json" delegate_to: localhost - name: Send security alert for high risk shell: | if command -v curl >/dev/null 2>&1; then curl -d "🚨 HIGH RISK: {{ inventory_hostname }} security audit - {{ security_score.overall_risk }} risk level detected" \ -H "Title: Security Alert" \ -H "Priority: high" \ -H "Tags: security,audit" \ "{{ ntfy_url | default('https://ntfy.sh/REDACTED_TOPIC') }}" || true fi when: security_score.overall_risk == "HIGH" ignore_errors: yes - name: Summary message debug: msg: | 🔒 Security audit complete for {{ inventory_hostname }} 📊 Risk Level: {{ security_score.overall_risk }} 📄 Report saved to: {{ security_report_dir }}/{{ inventory_hostname }}_security_{{ ansible_date_time.epoch }}.json {% if security_score.overall_risk == "HIGH" %} 🚨 HIGH RISK detected - immediate action required! {% elif security_score.overall_risk == "MEDIUM" %} ⚠️ MEDIUM RISK - review and address issues {% else %} ✅ LOW RISK - system appears secure {% endif %} Key Issues: {% if security_score.security_updates_pending | int > 0 %} - {{ security_score.security_updates_pending }} security updates pending {% endif %} {% if security_score.ssh_root_login == "INSECURE" %} - SSH root login enabled {% endif %} {% if security_score.firewall_active == "INACTIVE" %} - Firewall not active {% endif %}