Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Makes unattended-upgrades sequencing more similar to cron-apt #5855

Merged
merged 7 commits into from
Mar 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions install_files/ansible-base/roles/common/defaults/main.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
---
# Set the time at which the servers reboot to apply nightly updates
# and aid in clearing memory. Only the hour is configurable.
daily_reboot_time: 4 # An integer between 0 and 23

disabled_kernel_modules:
- btusb
- bluetooth
Expand Down Expand Up @@ -53,3 +49,19 @@ unused_packages:
- wireless-tools
- wpasupplicant
- snapd

# Template declaration for setting the upgrade time to a predictable time,
# matching the 'daily_reboot_time' time via sdconfig.
unattended_upgrades_timer_overrides:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Initially i thought this was a template block but it's just declaring the variables 👍

- src: apt-daily-timer-override.j2
dest: /etc/systemd/system/apt-daily.timer.d/override.conf
- src: apt-daily-upgrade-timer-override.j2
dest: /etc/systemd/system/apt-daily-upgrade.timer.d/override.conf

# Set the time at which the servers reboot to apply nightly updates
# and aid in clearing memory. Only the hour is configurable, via sdconfig.
# The other options are for unattended-upgrades, when to run
# 'apt-get update' and 'apt-get upgrade'.
daily_reboot_time: 4 # An integer between 0 and 23
daily_update_time: "{{ (daily_reboot_time|int - 2) % 24 }}"
daily_upgrade_time: "{{ (daily_reboot_time|int - 1) % 24 }}"
4 changes: 4 additions & 0 deletions install_files/ansible-base/roles/common/handlers/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,7 @@
- name: update apt cache
apt:
update_cache: yes

- name: systemd daemon-reload
systemd:
daemon_reload: yes
1 change: 1 addition & 0 deletions install_files/ansible-base/roles/common/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
when:
- ansible_distribution_release == "focal"
tags:
- ua
- reboot

- include: remove_unused_packages.yml
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,24 @@
# Configuration for unattended upgrades is almost exclusively managed by the
# securedrop-config package under Focal.

- name: Create override dirs for apt-daily timers
file:
state: directory
mode: "0755"
path: "{{ item.dest|dirname }}"
with_items: "{{ unattended_upgrades_timer_overrides }}"

- name: Add overrides for apt-daily timers
template:
src: "{{ item.src }}"
dest: "{{ item.dest }}"
mode: "0644"
notify: systemd daemon-reload
with_items: "{{ unattended_upgrades_timer_overrides }}"

# Ensure daemon-reload has happened before starting/enabling
- meta: flush_handlers
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


- name: Ensure apt-daily and apt-daily-upgrade services are unmasked, started and enabled.
systemd:
name: "{{ item }}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// time instead of immediately
// Default: "now"
Unattended-Upgrade::Automatic-Reboot-Time "{{ daily_reboot_time }}:00";
APT::Periodic::Enable "1";
{% endif %}
// Don't install packages from "Recommends" field, we'll manage dependencies
// explicitly to avoid pulling in packages from e.g. universe.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[Timer]
OnCalendar=
OnCalendar=*-*-* {{ daily_update_time }}:00
RandomizedDelaySec=20m
Persistent=true
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[Timer]
OnCalendar=
OnCalendar=*-*-* {{ daily_upgrade_time }}:00
RandomizedDelaySec=20m
Original file line number Diff line number Diff line change
Expand Up @@ -62,5 +62,7 @@ Unattended-Upgrade::Automatic-Reboot-WithUsers "true";
// Here we set the dpkg options to force the old conffile if it's already present
// or force the default config if no config is present
// see https://github.com/freedomofpress/securedrop/pull/911
Dpkg::Options "force-confdef";
Dpkg::Options "force-confold";
Dpkg::Options {
"--force-confdef";
"--force-confold";
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's the change that will re-close #5849.

91 changes: 61 additions & 30 deletions molecule/testinfra/common/test_automatic_updates.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,18 +187,46 @@ def test_cron_apt_cron_jobs(host, cron_job):
assert not f.exists


def test_unattended_upgrades_config(host):
apt_config_options = {
"APT::Install-Recommends": "false",
"Dpkg::Options": [
"--force-confold",
"--force-confdef",
],
"APT::Periodic::Update-Package-Lists": "1",
"APT::Periodic::Unattended-Upgrade": "1",
"APT::Periodic::AutocleanInterval": "1",
"APT::Periodic::Enable": "1",
"Unattended-Upgrade::AutoFixInterruptedDpkg": "true",
"Unattended-Upgrade::Automatic-Reboot": "true",
"Unattended-Upgrade::Automatic-Reboot-Time": "4:00",
"Unattended-Upgrade::Automatic-Reboot-WithUsers": "true",
"Unattended-Upgrade::Origins-Pattern": [
"origin=${distro_id},archive=${distro_codename}",
"origin=${distro_id},archive=${distro_codename}-security",
"origin=${distro_id},archive=${distro_codename}-updates",
"origin=SecureDrop,codename=${distro_codename}",
],
}


@pytest.mark.parametrize("k, v", apt_config_options.items())
def test_unattended_upgrades_config(host, k, v):
"""
Ensures the apt and unattended-upgrades config is correct only under Ubuntu Focal
"""
Ensures the 50unattended-upgrades config is correct only under Ubuntu Focal
"""
f = host.file('/etc/apt/apt.conf.d/50unattended-upgrades')
if host.system_info.codename != "xenial":
assert f.is_file
assert f.user == "root"
assert f.mode == 0o644
assert f.contains("origin=SecureDrop,codename=${distro_codename}")
assert f.contains('Dpkg::Options "force-confold";')
assert f.contains('Dpkg::Options "force-confdef";')
if host.system_info.codename == "xenial":
return True
# Dump apt config to inspect end state, apt will build config
# from all conf files on disk, e.g. 80securedrop.
c = host.run("apt-config dump --format '%v%n' {}".format(k))
assert c.rc == 0
# Some values are lists, so support that in the params
if hasattr(v, "__getitem__"):
for i in v:
assert i in c.stdout
else:
assert v in c.stdout


def test_unattended_securedrop_specific(host):
Expand All @@ -218,25 +246,6 @@ def test_unattended_securedrop_specific(host):
assert not f.contains("Automatic-Reboot-Time")


@pytest.mark.parametrize('option', [
'APT::Periodic::Update-Package-Lists "1";',
'APT::Periodic::Unattended-Upgrade "1";',
'APT::Periodic::AutocleanInterval "1";',
])
def test_auto_upgrades_config(host, option):
"""
Ensures the 20auto-upgrades config is correct only under Ubuntu Focal
"""
f = host.file('/etc/apt/apt.conf.d/20auto-upgrades')
if host.system_info.codename == "xenial":
assert not f.exists
else:
assert f.is_file
assert f.user == "root"
assert f.mode == 0o644
assert f.contains('^{}$'.format(option))


def test_unattended_upgrades_functional(host):
"""
Ensure unatteded-upgrades completes successfully and ensures all packages
Expand Down Expand Up @@ -275,6 +284,28 @@ def test_apt_daily_services_and_timers_enabled(host, service):
assert s.is_enabled


def test_apt_daily_timer_schedule(host):
"""
Timer for running apt-daily, i.e. 'apt-get update', should be 2h
before the daily_reboot_time.
"""
if host.system_info.codename != "xenial":
c = host.run("systemctl show apt-daily.timer")
assert "TimersCalendar={ OnCalendar=*-*-* 02:00:00 ;" in c.stdout
assert "RandomizedDelayUSec=20m" in c.stdout


def test_apt_daily_upgrade_timer_schedule(host):
"""
Timer for running apt-daily-upgrade, i.e. 'apt-get upgrade', should be 1h
before the daily_reboot_time, and 1h after the apt-daily time.
"""
if host.system_info.codename != "xenial":
c = host.run("systemctl show apt-daily-upgrade.timer")
assert "TimersCalendar={ OnCalendar=*-*-* 03:00:00 ;" in c.stdout
assert "RandomizedDelayUSec=20m" in c.stdout


def test_reboot_required_cron(host):
"""
Unatteded-upgrades does not reboot the system if the updates don't require it.
Expand Down