new-site/infra/ansible/roles/security-updates/tasks/main.yml
justin 97e8664cbf Add security-updates Ansible role for automated patching
Comprehensive security update automation:

1. Debian OS (unattended-upgrades) — tightened to security-only:
   - Removed general Debian updates (prevents feature/breaking changes)
   - Only Debian-Security origins auto-installed
   - Email admin on every upgrade via ops@performancewest.net
   - Auto-reboot at 4 AM if kernel update requires it
   - needrestart auto-restarts services after library updates

2. Docker CE — major version guard:
   - Patch updates within pinned major version auto-applied
   - Major version jumps held + admin alerted for manual review
   - docker-ce, docker-ce-cli, containerd.io all version-guarded

3. Container base images — daily at 3:30 AM:
   - Pulls latest base images for all docker-compose services
   - Compares image digests — only rebuilds if changed
   - Restarts only affected services (not full stack)
   - Alerts admin on rebuild failures requiring manual intervention
   - Covers both prod and dev compose projects

4. k3s — weekly Sunday at 3:45 AM:
   - Patch updates within current minor auto-applied
   - Minor/major upgrades alert admin for manual review
   - Verifies node Ready status after update
   - Alerts on failures with investigation instructions

5. Admin notifications via SMTP:
   - [INFO] for successful patches
   - [WARNING] for available major upgrades needing review
   - [CRITICAL] for failures requiring immediate intervention
   - Falls back to syslog if SMTP unavailable

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-30 01:24:57 -05:00

122 lines
4.5 KiB
YAML

---
# ══════════════════════════════════════════════════════════════════════════════
# Security Updates Role
#
# 1. Configures apt to allow Docker CE security patches (not major upgrades)
# 2. Deploys a container image update script (pulls new base images, rebuilds
# if changed, restarts affected services)
# 3. Deploys a k3s patch update script (stays on current channel, patch only)
# 4. Sets up systemd timers for all of the above
# 5. Sends admin email when manual intervention is required
# ══════════════════════════════════════════════════════════════════════════════
# ── 1. Docker CE: pin major version, allow patch updates ────────────────────
- name: Install apt-mark hold for Docker CE (prevent major version jumps)
ansible.builtin.shell: |
current=$(dpkg-query -W -f='${Version}' docker-ce 2>/dev/null | grep -oP '^\d+' || echo "")
if [ -n "$current" ] && [ "$current" = "{{ docker_major_version_pin }}" ]; then
# Same major — ensure NOT held so patches flow through unattended-upgrades
apt-mark unhold docker-ce docker-ce-cli containerd.io 2>/dev/null || true
else
# Different major — hold to prevent auto-upgrade
apt-mark hold docker-ce docker-ce-cli containerd.io 2>/dev/null || true
fi
changed_when: false
- name: Deploy Docker version guard script
ansible.builtin.template:
src: docker-version-guard.sh.j2
dest: /usr/local/bin/pw-docker-version-guard
owner: root
group: root
mode: "0755"
# ── 2. Container base image security updates ───────────────────────────────
- name: Deploy container image update script
ansible.builtin.template:
src: container-security-update.sh.j2
dest: /usr/local/bin/pw-container-security-update
owner: root
group: root
mode: "0755"
- name: Deploy container update systemd service
ansible.builtin.template:
src: pw-container-update.service.j2
dest: /etc/systemd/system/pw-container-update.service
owner: root
group: root
mode: "0644"
notify: Reload systemd
- name: Deploy container update systemd timer
ansible.builtin.template:
src: pw-container-update.timer.j2
dest: /etc/systemd/system/pw-container-update.timer
owner: root
group: root
mode: "0644"
notify:
- Reload systemd
- Enable container update timer
# ── 3. k3s patch updates ───────────────────────────────────────────────────
- name: Deploy k3s patch update script
ansible.builtin.template:
src: k3s-security-update.sh.j2
dest: /usr/local/bin/pw-k3s-security-update
owner: root
group: root
mode: "0755"
- name: Deploy k3s update systemd service
ansible.builtin.template:
src: pw-k3s-update.service.j2
dest: /etc/systemd/system/pw-k3s-update.service
owner: root
group: root
mode: "0644"
notify: Reload systemd
- name: Deploy k3s update systemd timer
ansible.builtin.template:
src: pw-k3s-update.timer.j2
dest: /etc/systemd/system/pw-k3s-update.timer
owner: root
group: root
mode: "0644"
notify:
- Reload systemd
- Enable k3s update timer
# ── 4. Admin alert helper ──────────────────────────────────────────────────
- name: Deploy admin alert script
ansible.builtin.template:
src: pw-security-alert.sh.j2
dest: /usr/local/bin/pw-security-alert
owner: root
group: root
mode: "0755"
# ── 5. Ensure needrestart is installed for library/service detection ───────
- name: Install needrestart for detecting outdated services
ansible.builtin.apt:
name: needrestart
state: present
- name: Configure needrestart — auto-restart services, never interactive
ansible.builtin.copy:
content: |
# Auto-restart services after library updates (no prompts)
$nrconf{restart} = 'a';
# Don't restart these (handled by our own scripts)
$nrconf{blacklist_rc} = [qr(^docker), qr(^containerd), qr(^k3s)];
dest: /etc/needrestart/conf.d/pw-security.conf
owner: root
group: root
mode: "0644"