From 294d19e0afe6c8b039603e15bb13963327896018 Mon Sep 17 00:00:00 2001 From: Joshua Tauberer Date: Thu, 21 Aug 2014 10:43:55 +0000 Subject: [PATCH 1/4] rename whats_next.py to status_checks.py --- management/buy_certificate.py | 2 +- management/daemon.py | 2 +- management/{whats_next.py => status_checks.py} | 0 management/web_update.py | 2 +- setup/start.sh | 2 +- 5 files changed, 4 insertions(+), 4 deletions(-) rename management/{whats_next.py => status_checks.py} (100%) diff --git a/management/buy_certificate.py b/management/buy_certificate.py index 8bac8849..9934a032 100755 --- a/management/buy_certificate.py +++ b/management/buy_certificate.py @@ -14,7 +14,7 @@ import rtyaml from utils import load_environment, shell from web_update import get_web_domains, get_domain_ssl_files, get_web_root -from whats_next import check_certificate +from status_checks import check_certificate def buy_ssl_certificate(api_key, domain, command, env): if domain != env['PRIMARY_HOSTNAME'] \ diff --git a/management/daemon.py b/management/daemon.py index 32fbd3c9..b4f52f2f 100755 --- a/management/daemon.py +++ b/management/daemon.py @@ -191,7 +191,7 @@ def web_update(): @app.route('/system/status', methods=["POST"]) @authorized_personnel_only def system_status(): - from whats_next import run_checks + from status_checks import run_checks class WebOutput: def __init__(self): self.items = [] diff --git a/management/whats_next.py b/management/status_checks.py similarity index 100% rename from management/whats_next.py rename to management/status_checks.py diff --git a/management/web_update.py b/management/web_update.py index 4833adb5..2afdaea1 100644 --- a/management/web_update.py +++ b/management/web_update.py @@ -140,7 +140,7 @@ def get_domain_ssl_files(domain, env): # a Subject Alternative Name matching this domain. Don't do this if # the user has uploaded a different private key for this domain. if not ssl_key_is_alt: - from whats_next import check_certificate + from status_checks import check_certificate if check_certificate(domain, ssl_certificate_primary, None) == "OK": ssl_certificate = ssl_certificate_primary diff --git a/setup/start.sh b/setup/start.sh index c210e222..bc4635a9 100755 --- a/setup/start.sh +++ b/setup/start.sh @@ -329,7 +329,7 @@ echo Your Mail-in-a-Box is running. echo echo Please log in to the control panel for further instructions at: echo -if management/whats_next.py --check-primary-hostname; then +if management/status_checks.py --check-primary-hostname; then # Show the nice URL if it appears to be resolving and has a valid certificate. echo https://$PRIMARY_HOSTNAME/admin echo From 2d5097345af70c90a0a920a9a9f2d0554e9ab5ee Mon Sep 17 00:00:00 2001 From: Joshua Tauberer Date: Thu, 21 Aug 2014 11:09:51 +0000 Subject: [PATCH 2/4] move the package update check into the system status checks --- management/daemon.py | 13 ++++------- management/status_checks.py | 46 ++++++++++++++++++++++++++++++++++++- 2 files changed, 50 insertions(+), 9 deletions(-) diff --git a/management/daemon.py b/management/daemon.py index b4f52f2f..fc5b652a 100755 --- a/management/daemon.py +++ b/management/daemon.py @@ -210,14 +210,11 @@ def system_status(): @app.route('/system/updates') @authorized_personnel_only def show_updates(): - utils.shell("check_call", ["/usr/bin/apt-get", "-qq", "update"]) - simulated_install = utils.shell("check_output", ["/usr/bin/apt-get", "-qq", "-s", "upgrade"]) - pkgs = [] - for line in simulated_install.split('\n'): - if re.match(r'^Conf .*', line): continue # remove these lines, not informative - line = re.sub(r'^Inst (.*) \[(.*)\] \((\S*).*', r'Updated Package Available: \1 (\3)', line) # make these lines prettier - pkgs.append(line) - return "\n".join(pkgs) + from status_checks import list_apt_updates + return "".join( + "%s (%s)\n" + % (p["package"], p["version"]) + for p in list_apt_updates()) @app.route('/system/update-packages', methods=["POST"]) @authorized_personnel_only diff --git a/management/status_checks.py b/management/status_checks.py index 8868dedb..27549686 100755 --- a/management/status_checks.py +++ b/management/status_checks.py @@ -6,7 +6,7 @@ __ALL__ = ['check_certificate'] -import os, os.path, re, subprocess +import os, os.path, re, subprocess, datetime import dns.reversename, dns.resolver @@ -36,6 +36,17 @@ def run_system_checks(env): else: env['out'].print_ok("SSH disallows password-based login.") + # Check for any software package updates. + pkgs = list_apt_updates() + if os.path.exists("/var/run/reboot-required"): + env['out'].print_error("System updates have been installed and a reboot of the machine is required.") + elif len(pkgs) == 0: + env['out'].print_ok("System software is up to date.") + else: + env['out'].print_error("There are %d software packages that can be updated." % len(pkgs)) + for p in pkgs: + env['out'].print_line("%s (%s)" % (p["package"], p["version"])) + # Check that the administrator alias exists since that's where all # admin email is automatically directed. check_alias_exists("administrator@" + env['PRIMARY_HOSTNAME'], env) @@ -433,6 +444,39 @@ def check_certificate(domain, ssl_certificate, ssl_private_key): else: return verifyoutput.strip() +_apt_updates = None +def list_apt_updates(): + # See if we have this information cached recently. + # Keep the information for 8 hours. + global _apt_updates + if _apt_updates is not None and _apt_updates[0] > datetime.datetime.now() - datetime.timedelta(hours=8): + return _apt_updates[1] + + # Run apt-get update to refresh package list. + shell("check_call", ["/usr/bin/apt-get", "-qq", "update"]) + + # Run apt-get upgrade in simulate mode to get a list of what + # it would do. + simulated_install = shell("check_output", ["/usr/bin/apt-get", "-qq", "-s", "upgrade"]) + pkgs = [] + for line in simulated_install.split('\n'): + if line.strip() == "": + continue + if re.match(r'^Conf .*', line): + # remove these lines, not informative + continue + m = re.match(r'^Inst (.*) \[(.*)\] \((\S*)', line) + if m: + pkgs.append({ "package": m.group(1), "version": m.group(3), "current_version": m.group(2) }) + else: + pkgs.append({ "package": "[" + line + "]", "version": "", "current_version": "" }) + + # Cache for future requests. + _apt_updates = (datetime.datetime.now(), pkgs) + + return pkgs + + try: terminal_columns = int(shell('check_output', ['stty', 'size']).split()[1]) except: From eab28c97ff4ff04b452d07dd2f7af882f76f0d33 Mon Sep 17 00:00:00 2001 From: Joshua Tauberer Date: Thu, 21 Aug 2014 11:25:06 +0000 Subject: [PATCH 3/4] allow apt to perform security updates on its own --- setup/management.sh | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/setup/management.sh b/setup/management.sh index 0f6e4d0a..b793b990 100755 --- a/setup/management.sh +++ b/setup/management.sh @@ -2,7 +2,7 @@ source setup/functions.sh -apt_install python3-flask links duplicity libyaml-dev python3-dnspython +apt_install python3-flask links duplicity libyaml-dev python3-dnspython unattended-upgrades hide_output pip3 install rtyaml # Create a backup directory and a random key for encrypting backups. @@ -21,6 +21,14 @@ rm -f /etc/init.d/mailinabox ln -s $(pwd)/conf/management-initscript /etc/init.d/mailinabox hide_output update-rc.d mailinabox defaults +# Allow apt to install system updates automatically every day. +cat > /etc/apt/apt.conf.d/02periodic < /etc/cron.daily/mailinabox-backup << EOF; #!/bin/bash From 09d2a08ce620928d0398068197951e5acebca0f0 Mon Sep 17 00:00:00 2001 From: Christian Koptein Date: Thu, 21 Aug 2014 21:51:54 +0200 Subject: [PATCH 4/4] Typo in introduction --- setup/start.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup/start.sh b/setup/start.sh index bc4635a9..9b2ca292 100755 --- a/setup/start.sh +++ b/setup/start.sh @@ -43,7 +43,7 @@ if [ -t 0 ]; then echo "-----------------------------------------------" echo echo "I'm going to ask you a few questions. To change your answers later," - echo "later, just re-run this script." + echo "just re-run this script." fi # Recall the last settings used if we're running this a second time.