From 381e1b285c19eac45af6df55706956050d5d2e4a Mon Sep 17 00:00:00 2001 From: Ruakij Date: Thu, 22 Oct 2026 09:00:33 +0000 Subject: [PATCH] Add an unattended_upgrades role that installs only Debian and Ubuntu security updates, with no automatic reboot --- unattended_upgrades/defaults/main.yml | 16 +++++++++ unattended_upgrades/tasks/main.yml | 33 +++++++++++++++++++ .../templates/20auto-upgrades.j2 | 4 +++ .../templates/52unattended-upgrades-local.j2 | 30 +++++++++++++++++ 4 files changed, 83 insertions(+) create mode 100644 unattended_upgrades/defaults/main.yml create mode 100644 unattended_upgrades/tasks/main.yml create mode 100644 unattended_upgrades/templates/20auto-upgrades.j2 create mode 100644 unattended_upgrades/templates/52unattended-upgrades-local.j2 diff --git a/unattended_upgrades/defaults/main.yml b/unattended_upgrades/defaults/main.yml new file mode 100644 index 0000000..6ac3ac1 --- /dev/null +++ b/unattended_upgrades/defaults/main.yml @@ -0,0 +1,16 @@ +--- +unattended_upgrades: + # Security updates only. Third-party repositories (Docker, Kopia, CrowdSec, NetBird, Proxmox, GitLab) stay out: + # an update of docker-ce or a pve package restarts every container or guest. + # Keyed by ansible_distribution; Debian lists go to Origins-Pattern, Ubuntu lists to Allowed-Origins. + origins_pattern: + Debian: + - origin=Debian,codename=${distro_codename}-security,label=Debian-Security + allowed_origins: + Ubuntu: + - ${distro_id}:${distro_codename}-security + - ${distro_id}ESMApps:${distro_codename}-apps-security + - ${distro_id}ESM:${distro_codename}-infra-security + # Recipient of the report mail; unset sends none, as no host has an outgoing mail setup. + mail: + mail_report: only-on-error diff --git a/unattended_upgrades/tasks/main.yml b/unattended_upgrades/tasks/main.yml new file mode 100644 index 0000000..5ea73cc --- /dev/null +++ b/unattended_upgrades/tasks/main.yml @@ -0,0 +1,33 @@ +--- +- name: Require an allowed origin for the distribution + ansible.builtin.assert: + that: + - (unattended_upgrades.origins_pattern[ansible_distribution] | default([]) + unattended_upgrades.allowed_origins[ansible_distribution] | default([])) | length > 0 + fail_msg: unattended_upgrades has no origins for {{ ansible_distribution }} + +- name: Install unattended-upgrades + ansible.builtin.apt: + name: unattended-upgrades + state: present + +- name: Enable the daily package list update and upgrade + ansible.builtin.template: + src: 20auto-upgrades.j2 + dest: /etc/apt/apt.conf.d/20auto-upgrades + mode: "0644" + +# Separate file, the package owns 50unattended-upgrades. +- name: Configure unattended-upgrades + ansible.builtin.template: + src: 52unattended-upgrades-local.j2 + dest: /etc/apt/apt.conf.d/52unattended-upgrades-local + mode: "0644" + +- name: Enable the apt timers + ansible.builtin.systemd: + name: "{{ item }}" + enabled: true + state: started + loop: + - apt-daily.timer + - apt-daily-upgrade.timer diff --git a/unattended_upgrades/templates/20auto-upgrades.j2 b/unattended_upgrades/templates/20auto-upgrades.j2 new file mode 100644 index 0000000..ed8e2d2 --- /dev/null +++ b/unattended_upgrades/templates/20auto-upgrades.j2 @@ -0,0 +1,4 @@ +// {{ ansible_managed }} +APT::Periodic::Update-Package-Lists "1"; +APT::Periodic::Unattended-Upgrade "1"; +APT::Periodic::AutocleanInterval "7"; diff --git a/unattended_upgrades/templates/52unattended-upgrades-local.j2 b/unattended_upgrades/templates/52unattended-upgrades-local.j2 new file mode 100644 index 0000000..8cc9a0a --- /dev/null +++ b/unattended_upgrades/templates/52unattended-upgrades-local.j2 @@ -0,0 +1,30 @@ +// {{ ansible_managed }} +// Replaces the origins of 50unattended-upgrades; apt list options append otherwise. +#clear Unattended-Upgrade::Allowed-Origins; +#clear Unattended-Upgrade::Origins-Pattern; +{% set patterns = unattended_upgrades.origins_pattern[ansible_distribution] | default([]) %} +{% if patterns %} +Unattended-Upgrade::Origins-Pattern { +{% for origin in patterns %} + "{{ origin }}"; +{% endfor %} +}; +{% endif %} +{% set allowed = unattended_upgrades.allowed_origins[ansible_distribution] | default([]) %} +{% if allowed %} +Unattended-Upgrade::Allowed-Origins { +{% for origin in allowed %} + "{{ origin }}"; +{% endfor %} +}; +{% endif %} + +// No automatic reboot, the owner schedules those. +Unattended-Upgrade::Automatic-Reboot "false"; +Unattended-Upgrade::Remove-Unused-Kernel-Packages "true"; +Unattended-Upgrade::Remove-New-Unused-Dependencies "true"; +Unattended-Upgrade::Remove-Unused-Dependencies "false"; +{% if unattended_upgrades.mail %} +Unattended-Upgrade::Mail "{{ unattended_upgrades.mail }}"; +Unattended-Upgrade::MailReport "{{ unattended_upgrades.mail_report }}"; +{% endif %}