Sanitized mirror from private repository - 2026-04-19 07:39:14 UTC
This commit is contained in:
318
ansible/automation/playbooks/security_updates.yml
Normal file
318
ansible/automation/playbooks/security_updates.yml
Normal file
@@ -0,0 +1,318 @@
|
||||
---
|
||||
# Security Updates Playbook
|
||||
# Automated security patches and system updates
|
||||
# Usage: ansible-playbook playbooks/security_updates.yml
|
||||
# Usage: ansible-playbook playbooks/security_updates.yml -e "reboot_if_required=true"
|
||||
# Usage: ansible-playbook playbooks/security_updates.yml -e "security_only=true"
|
||||
|
||||
- name: Apply Security Updates
|
||||
hosts: "{{ host_target | default('debian_clients') }}"
|
||||
gather_facts: yes
|
||||
become: yes
|
||||
vars:
|
||||
security_only: "{{ security_only | default(true) }}"
|
||||
reboot_if_required: "{{ reboot_if_required | default(false) }}"
|
||||
backup_before_update: "{{ backup_before_update | default(true) }}"
|
||||
max_reboot_wait: "{{ max_reboot_wait | default(300) }}"
|
||||
update_docker: "{{ update_docker | default(false) }}"
|
||||
|
||||
tasks:
|
||||
- name: Check if host is reachable
|
||||
ping:
|
||||
register: ping_result
|
||||
|
||||
- name: Create update log directory
|
||||
file:
|
||||
path: "/var/log/ansible_updates"
|
||||
state: directory
|
||||
mode: '0755'
|
||||
|
||||
- name: Get pre-update system info
|
||||
shell: |
|
||||
echo "=== PRE-UPDATE SYSTEM INFO ==="
|
||||
echo "Date: {{ ansible_date_time.iso8601 }}"
|
||||
echo "Host: {{ inventory_hostname }}"
|
||||
echo "Kernel: $(uname -r)"
|
||||
echo "Uptime: $(uptime)"
|
||||
echo ""
|
||||
|
||||
echo "=== CURRENT PACKAGES ==="
|
||||
dpkg -l | grep -E "(linux-image|linux-headers)" || echo "No kernel packages found"
|
||||
echo ""
|
||||
|
||||
echo "=== SECURITY UPDATES AVAILABLE ==="
|
||||
apt list --upgradable 2>/dev/null | grep -i security || echo "No security updates available"
|
||||
echo ""
|
||||
|
||||
echo "=== DISK SPACE ==="
|
||||
df -h /
|
||||
echo ""
|
||||
|
||||
echo "=== RUNNING SERVICES ==="
|
||||
systemctl list-units --type=service --state=running | head -10
|
||||
register: pre_update_info
|
||||
changed_when: false
|
||||
|
||||
- name: Display update plan
|
||||
debug:
|
||||
msg: |
|
||||
🔒 SECURITY UPDATE PLAN
|
||||
=======================
|
||||
🖥️ Host: {{ inventory_hostname }}
|
||||
📅 Date: {{ ansible_date_time.date }}
|
||||
🔐 Security Only: {{ security_only }}
|
||||
🔄 Reboot if Required: {{ reboot_if_required }}
|
||||
💾 Backup First: {{ backup_before_update }}
|
||||
🐳 Update Docker: {{ update_docker }}
|
||||
|
||||
{{ pre_update_info.stdout }}
|
||||
|
||||
- name: Backup critical configs before update
|
||||
shell: |
|
||||
backup_dir="/var/backups/pre-update-{{ ansible_date_time.epoch }}"
|
||||
mkdir -p "$backup_dir"
|
||||
|
||||
echo "Creating pre-update backup..."
|
||||
|
||||
# Backup critical system configs
|
||||
cp -r /etc/ssh "$backup_dir/" 2>/dev/null || echo "SSH config backup failed"
|
||||
cp -r /etc/nginx "$backup_dir/" 2>/dev/null || echo "Nginx config not found"
|
||||
cp -r /etc/systemd "$backup_dir/" 2>/dev/null || echo "Systemd config backup failed"
|
||||
|
||||
# Backup package list
|
||||
dpkg --get-selections > "$backup_dir/package_list.txt"
|
||||
|
||||
# Backup Docker configs if they exist
|
||||
if [ -d "/opt/docker" ]; then
|
||||
tar -czf "$backup_dir/docker_configs.tar.gz" /opt/docker 2>/dev/null || echo "Docker config backup failed"
|
||||
fi
|
||||
|
||||
echo "✅ Backup created at $backup_dir"
|
||||
ls -la "$backup_dir"
|
||||
register: backup_result
|
||||
when: backup_before_update | bool
|
||||
|
||||
- name: Update package cache
|
||||
apt:
|
||||
update_cache: yes
|
||||
cache_valid_time: 0
|
||||
register: cache_update
|
||||
|
||||
- name: Check for available security updates
|
||||
shell: |
|
||||
apt list --upgradable 2>/dev/null | grep -c security || echo "0"
|
||||
register: security_updates_count
|
||||
changed_when: false
|
||||
|
||||
- name: Check for kernel updates
|
||||
shell: |
|
||||
apt list --upgradable 2>/dev/null | grep -E "(linux-image|linux-headers)" | wc -l
|
||||
register: kernel_updates_count
|
||||
changed_when: false
|
||||
|
||||
- name: Apply security updates only
|
||||
apt:
|
||||
upgrade: safe
|
||||
autoremove: yes
|
||||
autoclean: yes
|
||||
register: security_update_result
|
||||
when:
|
||||
- security_only | bool
|
||||
- security_updates_count.stdout | int > 0
|
||||
|
||||
- name: Apply all updates (if not security only)
|
||||
apt:
|
||||
upgrade: dist
|
||||
autoremove: yes
|
||||
autoclean: yes
|
||||
register: full_update_result
|
||||
when:
|
||||
- not security_only | bool
|
||||
|
||||
- name: Update Docker (if requested)
|
||||
block:
|
||||
- name: Add Docker GPG key
|
||||
apt_key:
|
||||
url: https://download.docker.com/linux/ubuntu/gpg
|
||||
state: present
|
||||
|
||||
- name: Add Docker repository
|
||||
apt_repository:
|
||||
repo: "deb [arch=amd64] https://download.docker.com/linux/ubuntu {{ ansible_distribution_release }} stable"
|
||||
state: present
|
||||
|
||||
- name: Update Docker packages
|
||||
apt:
|
||||
name:
|
||||
- docker-ce
|
||||
- docker-ce-cli
|
||||
- containerd.io
|
||||
state: latest
|
||||
register: docker_update_result
|
||||
|
||||
- name: Restart Docker service
|
||||
systemd:
|
||||
name: docker
|
||||
state: restarted
|
||||
enabled: yes
|
||||
when: docker_update_result.changed
|
||||
|
||||
when: update_docker | bool
|
||||
|
||||
- name: Check if reboot is required
|
||||
stat:
|
||||
path: /var/run/reboot-required
|
||||
register: reboot_required_file
|
||||
|
||||
- name: Display reboot requirement
|
||||
debug:
|
||||
msg: |
|
||||
🔄 REBOOT STATUS
|
||||
================
|
||||
Reboot Required: {{ reboot_required_file.stat.exists }}
|
||||
Kernel Updates: {{ kernel_updates_count.stdout }}
|
||||
Auto Reboot: {{ reboot_if_required }}
|
||||
|
||||
- name: Create update report
|
||||
shell: |
|
||||
report_file="/var/log/ansible_updates/update_report_{{ ansible_date_time.epoch }}.txt"
|
||||
|
||||
echo "🔒 SECURITY UPDATE REPORT - {{ inventory_hostname }}" > "$report_file"
|
||||
echo "=================================================" >> "$report_file"
|
||||
echo "Date: {{ ansible_date_time.iso8601 }}" >> "$report_file"
|
||||
echo "Host: {{ inventory_hostname }}" >> "$report_file"
|
||||
echo "Security Only: {{ security_only }}" >> "$report_file"
|
||||
echo "Reboot Required: {{ reboot_required_file.stat.exists }}" >> "$report_file"
|
||||
echo "" >> "$report_file"
|
||||
|
||||
echo "=== PRE-UPDATE INFO ===" >> "$report_file"
|
||||
echo "{{ pre_update_info.stdout }}" >> "$report_file"
|
||||
echo "" >> "$report_file"
|
||||
|
||||
echo "=== UPDATE RESULTS ===" >> "$report_file"
|
||||
{% if security_only %}
|
||||
{% if security_update_result is defined %}
|
||||
echo "Security updates applied: {{ security_update_result.changed }}" >> "$report_file"
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% if full_update_result is defined %}
|
||||
echo "Full system update applied: {{ full_update_result.changed }}" >> "$report_file"
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if update_docker and docker_update_result is defined %}
|
||||
echo "Docker updated: {{ docker_update_result.changed }}" >> "$report_file"
|
||||
{% endif %}
|
||||
|
||||
echo "" >> "$report_file"
|
||||
echo "=== POST-UPDATE INFO ===" >> "$report_file"
|
||||
echo "Kernel: $(uname -r)" >> "$report_file"
|
||||
echo "Uptime: $(uptime)" >> "$report_file"
|
||||
echo "Available updates: $(apt list --upgradable 2>/dev/null | wc -l)" >> "$report_file"
|
||||
|
||||
{% if backup_before_update %}
|
||||
echo "" >> "$report_file"
|
||||
echo "=== BACKUP INFO ===" >> "$report_file"
|
||||
echo "{{ backup_result.stdout }}" >> "$report_file"
|
||||
{% endif %}
|
||||
|
||||
cat "$report_file"
|
||||
register: update_report
|
||||
|
||||
- name: Notify about pending reboot
|
||||
debug:
|
||||
msg: |
|
||||
⚠️ REBOOT REQUIRED
|
||||
===================
|
||||
Host: {{ inventory_hostname }}
|
||||
Reason: System updates require reboot
|
||||
Kernel updates: {{ kernel_updates_count.stdout }}
|
||||
|
||||
Manual reboot command: sudo reboot
|
||||
Or run with: -e "reboot_if_required=true"
|
||||
when:
|
||||
- reboot_required_file.stat.exists
|
||||
- not reboot_if_required | bool
|
||||
|
||||
- name: Reboot system if required and authorized
|
||||
reboot:
|
||||
reboot_timeout: "{{ max_reboot_wait }}"
|
||||
msg: "Rebooting for security updates"
|
||||
pre_reboot_delay: 10
|
||||
when:
|
||||
- reboot_required_file.stat.exists
|
||||
- reboot_if_required | bool
|
||||
register: reboot_result
|
||||
|
||||
- name: Wait for system to come back online
|
||||
wait_for_connection:
|
||||
timeout: "{{ max_reboot_wait }}"
|
||||
delay: 30
|
||||
when: reboot_result is defined and reboot_result.changed
|
||||
|
||||
- name: Verify services after reboot
|
||||
ansible.builtin.systemd:
|
||||
name: "{{ item }}"
|
||||
loop:
|
||||
- ssh
|
||||
- docker
|
||||
- tailscaled
|
||||
register: service_checks
|
||||
failed_when: false
|
||||
changed_when: false
|
||||
when: reboot_result is defined and reboot_result.changed
|
||||
|
||||
- name: Final security check
|
||||
shell: |
|
||||
echo "=== FINAL SECURITY STATUS ==="
|
||||
echo "Available security updates: $(apt list --upgradable 2>/dev/null | grep -c security || echo '0')"
|
||||
echo "Reboot required: $([ -f /var/run/reboot-required ] && echo 'Yes' || echo 'No')"
|
||||
echo "Last update: {{ ansible_date_time.iso8601 }}"
|
||||
echo ""
|
||||
|
||||
echo "=== SYSTEM HARDENING CHECK ==="
|
||||
echo "SSH root login: $(grep PermitRootLogin /etc/ssh/sshd_config | head -1 || echo 'Not configured')"
|
||||
echo "Firewall status: $(ufw status | head -1 || echo 'UFW not available')"
|
||||
echo "Fail2ban status: $(systemctl is-active fail2ban 2>/dev/null || echo 'Not running')"
|
||||
echo "Automatic updates: $(systemctl is-enabled unattended-upgrades 2>/dev/null || echo 'Not configured')"
|
||||
register: final_security_check
|
||||
changed_when: false
|
||||
|
||||
- name: Display update summary
|
||||
debug:
|
||||
msg: |
|
||||
|
||||
✅ SECURITY UPDATE COMPLETE - {{ inventory_hostname }}
|
||||
=============================================
|
||||
|
||||
📅 Update Date: {{ ansible_date_time.date }}
|
||||
🔐 Security Only: {{ security_only }}
|
||||
🔄 Reboot Performed: {{ reboot_result.changed if reboot_result is defined else 'No' }}
|
||||
|
||||
{{ update_report.stdout }}
|
||||
|
||||
{{ final_security_check.stdout }}
|
||||
|
||||
{% if post_reboot_verification is defined %}
|
||||
🔍 POST-REBOOT VERIFICATION:
|
||||
{{ post_reboot_verification.stdout }}
|
||||
{% endif %}
|
||||
|
||||
📄 Full report: /var/log/ansible_updates/update_report_{{ ansible_date_time.epoch }}.txt
|
||||
|
||||
🔍 Next Steps:
|
||||
- Monitor system stability
|
||||
- Check service functionality
|
||||
- Review security hardening: ansible-playbook playbooks/security_audit.yml
|
||||
|
||||
=============================================
|
||||
|
||||
- name: Send update notification (if configured)
|
||||
debug:
|
||||
msg: |
|
||||
📧 UPDATE NOTIFICATION
|
||||
Host: {{ inventory_hostname }}
|
||||
Status: Updates applied successfully
|
||||
Reboot: {{ 'Required' if reboot_required_file.stat.exists else 'Not required' }}
|
||||
Security updates: {{ security_updates_count.stdout }}
|
||||
when: send_notifications | default(false) | bool
|
||||
Reference in New Issue
Block a user