Skip to content

Commit

Permalink
Add a basic noble migration check script
Browse files Browse the repository at this point in the history
Perform a number of checks to ensure the system is ready for the noble
migration. The results are written to a JSON file in /etc/ that other
things like the JI and the upgrade script itself can read from.

The script is run hourly on a systemd timer but can also be run
interactively for administrators who want slightly more details.

Refs #7322.
  • Loading branch information
legoktm committed Nov 12, 2024
1 parent 543691c commit d685227
Show file tree
Hide file tree
Showing 5 changed files with 181 additions and 0 deletions.
20 changes: 20 additions & 0 deletions molecule/testinfra/common/test_release_upgrades.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import time

import pytest
import testutils

test_vars = testutils.securedrop_test_vars
Expand Down Expand Up @@ -27,3 +30,20 @@ def test_release_manager_upgrade_channel(host):
_, channel = raw_output.split("=")

assert channel == "never"


def test_migration_check(host):
"""Verify our migration check script works"""
if host.system_info.codename != "focal":
pytest.skip("only applicable/testable on focal")

with host.sudo():
# remove state file so we can see if it works
if host.file("/etc/securedrop-noble-migration.json").exists:
host.run("rm /etc/securedrop-noble-migration.json")
cmd = host.run("systemctl start securedrop-noble-migration-check")
assert cmd.rc == 0
while host.service("securedrop-noble-migration-check").is_running:
time.sleep(1)

assert host.file("/etc/securedrop-noble-migration.json").exists
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[Unit]
Description=Check noble migration readiness

[Service]
Type=oneshot
ExecStart=/usr/bin/securedrop-noble-migration-check.py
User=root
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[Unit]
Description=Check noble migration readiness

[Timer]
OnCalendar=hourly
Persistent=true
RandomizedDelaySec=5m

[Install]
WantedBy=timers.target
142 changes: 142 additions & 0 deletions securedrop/debian/config/usr/bin/securedrop-noble-migration-check.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
#!/usr/bin/python3
"""
Check migration of a SecureDrop server from focal to noble
This script is run as root on both the app and mon servers.
"""

import grp
import json
import os
import shutil
import subprocess
import sys
import traceback
from pathlib import Path
from urllib.parse import urlparse


def os_codename() -> str:
with open("/etc/os-release") as f:
os_release = f.readlines()
for line in os_release:
if line.startswith("VERSION_CODENAME="):
return line.split("=")[1].strip().strip('"')
raise ValueError("Could not determine Ubuntu codename")


def check_ssh_group() -> bool:
"""Check the UNIX "ssh" group is empty"""
try:
group = grp.getgrnam("ssh")
except KeyError:
print("ssh: group does not exist")
return True

if len(group.gr_mem) > 0:
print(f"ssh: group is not empty: {group.gr_mem}")
return False

print("ssh: group is empty")
return True


def check_ufw_removed() -> bool:
"""Check that ufw is removed"""
if Path("/usr/sbin/ufw").exists():
print("ufw: ufw is still installed")
return False
else:
print("ufw: ufw was removed")
return True


def check_free_space() -> bool:
"""Check the root partition has more than 10G free"""
root = shutil.disk_usage("/")
if root.free < (10 * 1024 * 1024 * 1024):
print("free space: not enough free space")
return False
else:
print("free space: enough free space")
return True


def check_apt() -> bool:
"""Verify only expected sources are configured"""
try:
output = subprocess.check_output(["apt-get", "indextargets"], text=True)
except subprocess.CalledProcessError:
print("apt: error invoking apt-get indextargets")
return False

for line in output.splitlines():
if line.startswith("URI:"):
parsed = urlparse(line.split(":", 1)[1].strip())
if parsed.hostname not in [
"archive.ubuntu.com",
"security.ubuntu.com",
"apt.freedom.press",
"apt-test.freedom.press",
]:
print(f"apt: unexpected source: {parsed.hostname}")
return False

print("apt: all sources are expected")
return True


def check_systemd() -> bool:
""" "No systemd units are failed"""
try:
subprocess.check_output(["systemctl", "is-failed"])
print("systemd: some systemd units are failed")
return False
except subprocess.CalledProcessError:
print("systemd: all systemd units are running")
return True


def main() -> None:
if os.geteuid() != 0:
print("You need to run this as root")
sys.exit(1)

state_path = Path("/etc/securedrop-noble-migration.json")
codename = os_codename()

if codename != "focal":
print(f"Unsupported Ubuntu version: {codename}")
# nothing to do, write an empty JSON blob
state_path.write_text(json.dumps({}))
return

state = {}
state["ssh"] = check_ssh_group()
state["ufw"] = check_ufw_removed()
state["free_space"] = check_free_space()
state["apt"] = check_apt()
state["systemd"] = check_systemd()

state_path.write_text(json.dumps(state))

if all(state.values()):
print("All ready for migration!")
sys.exit(0)
else:
print("""\
Some errors were found that will block migration.
If you are unsure what to do, please contact the SecureDrop
support team: <https://docs.securedrop.org/en/stable/getting_support.html>.
""")
sys.exit(1)


if __name__ == "__main__":
try:
main()
except Exception as e:
traceback.print_exc()
Path("/etc/securedrop-noble-migration.json").write_text(json.dumps({"error": str(e)}))
2 changes: 2 additions & 0 deletions securedrop/debian/rules
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ override_dh_systemd_enable:
dh_systemd_enable --no-enable securedrop-remove-pending-sources.service
dh_systemd_enable --no-enable securedrop-remove-packages.service
dh_systemd_enable --no-enable securedrop-cleanup-ossec.service
dh_systemd_enable --no-enable securedrop-noble-migration-check.service
dh_systemd_enable

# This is basically the same as the enable stanza above, just whether the
Expand All @@ -95,4 +96,5 @@ override_dh_systemd_start:
dh_systemd_start --no-start securedrop-remove-pending-sources.service
dh_systemd_start --no-start securedrop-remove-packages.service
dh_systemd_start --no-start securedrop-cleanup-ossec.service
dh_systemd_start --no-start securedrop-noble-migration-check.service
dh_systemd_start

0 comments on commit d685227

Please sign in to comment.