From c397d1909fa35a2181fbc7f450334069906482d3 Mon Sep 17 00:00:00 2001 From: Alyssa Ross Date: Sun, 20 Oct 2019 17:41:50 +0000 Subject: [PATCH] nixos/mailman: don't keep secrets in the Nix store This replaces all Mailman secrets with ones that are generated the first time the service is run. This replaces the hyperkittyApiKey option, which would lead to a secret in the world-readable store. Even worse were the secrets hard-coded into mailman-web, which are not just world-readable, but identical for all users! services.mailman.hyperkittyApiKey has been removed, and so can no longer be used to determine whether to enable Hyperkitty. In its place, there is a new option, services.mailman.hyperkitty.enable. For consistency, services.mailman.hyperkittyBaseUrl has been renamed to services.mailman.hyperkitty.baseUrl. --- nixos/modules/services/mail/mailman.nix | 103 ++++++++++++++++-------- pkgs/servers/mail/mailman/settings.py | 4 - 2 files changed, 70 insertions(+), 37 deletions(-) diff --git a/nixos/modules/services/mail/mailman.nix b/nixos/modules/services/mail/mailman.nix index 2619dda763c..cabb87d1845 100644 --- a/nixos/modules/services/mail/mailman.nix +++ b/nixos/modules/services/mail/mailman.nix @@ -53,30 +53,42 @@ let etc_dir: /etc ext_dir: $etc_dir/mailman.d pid_file: /run/mailman/master.pid - '' + optionalString (cfg.hyperkittyApiKey != null) '' + '' + optionalString cfg.hyperkitty.enable '' + [archiver.hyperkitty] class: mailman_hyperkitty.Archiver enable: yes - configuration: ${pkgs.writeText "mailman-hyperkitty.cfg" mailmanHyperkittyCfg} + configuration: /var/lib/mailman/mailman-hyperkitty.cfg ''; - mailmanHyperkittyCfg = '' + mailmanHyperkittyCfg = pkgs.writeText "mailman-hyperkitty.cfg" '' [general] # This is your HyperKitty installation, preferably on the localhost. This # address will be used by Mailman to forward incoming emails to HyperKitty # for archiving. It does not need to be publicly available, in fact it's # better if it is not. - base_url: ${cfg.hyperkittyBaseUrl} + base_url: ${cfg.hyperkitty.baseUrl} # Shared API key, must be the identical to the value in HyperKitty's # settings. - api_key: ${cfg.hyperkittyApiKey} + api_key: @API_KEY@ ''; in { ###### interface + imports = [ + (mkRenamedOptionModule [ "services" "mailman" "hyperkittyBaseUrl" ] + [ "services" "mailman" "hyperkitty" "baseUrl" ]) + + (mkRemovedOptionModule [ "services" "mailman" "hyperkittyApiKey" ] '' + The Hyperkitty API key is now generated on first run, and not + stored in the world-readable Nix store. To continue using + Hyperkitty, you must set services.mailman.hyperkitty.enable = true. + '') + ]; + options = { services.mailman = { @@ -128,24 +140,17 @@ in { ''; }; - hyperkittyBaseUrl = mkOption { - type = types.str; - default = "http://localhost/hyperkitty/"; - description = '' - Where can Mailman connect to Hyperkitty's internal API, preferably on - localhost? - ''; - }; + hyperkitty = { + enable = mkEnableOption "the Hyperkitty archiver for Mailman"; - hyperkittyApiKey = mkOption { - type = types.nullOr types.str; - default = null; - description = '' - The shared secret used to authenticate Mailman's internal - communication with Hyperkitty. Must be set to enable support for the - Hyperkitty archiver. Note that this secret is going to be visible to - all local users in the Nix store. - ''; + baseUrl = mkOption { + type = types.str; + default = "http://localhost/hyperkitty/"; + description = '' + Where can Mailman connect to Hyperkitty's internal API, preferably on + localhost? + ''; + }; }; }; @@ -187,13 +192,47 @@ in { ExecStop = "${mailmanExe}/bin/mailman stop"; User = "mailman"; Type = "forking"; - StateDirectory = "mailman"; - StateDirectoryMode = "0700"; RuntimeDirectory = "mailman"; PIDFile = "/run/mailman/master.pid"; }; }; + systemd.services.mailman-secrets = { + description = "Generate Hyperkitty API key"; + before = [ "mailman.service" "mailman-web.service" "hyperkitty.service" "httpd.service" "uwsgi.service" ]; + requiredBy = [ "mailman.service" "mailman-web.service" "hyperkitty.service" "httpd.service" "uwsgi.service" ]; + script = '' + mailmanDir=/var/lib/mailman + mailmanWebDir=/var/lib/mailman-web + + mailmanCfg=$mailmanDir/mailman-hyperkitty.cfg + hyperkittyCfg=$mailmanWebDir/settings_local.py + + [ -e $mailmanCfg -o -e $hyperkittyCfg ] && exit 0 + + install -m 0700 -o mailman -g nogroup -d $mailmanDir + install -m 0700 -o ${cfg.webUser} -g nogroup -d $mailmanWebDir + + hyperkittyApiKey=$(tr -dc A-Za-z0-9 < /dev/urandom | head -c 64) + secretKey=$(tr -dc A-Za-z0-9 < /dev/urandom | head -c 64) + + hyperkittyCfgTmp=$(mktemp) + echo "MAILMAN_ARCHIVER_KEY='$hyperkittyApiKey'" >>"$hyperkittyCfgTmp" + echo "SECRET_KEY='$secretKey'" >>"$hyperkittyCfgTmp" + chown ${cfg.webUser} "$hyperkittyCfgTmp" + + mailmanCfgTmp=$(mktemp) + sed "s/@API_KEY@/$hyperkittyApiKey/g" ${mailmanHyperkittyCfg} >"$mailmanCfgTmp" + chown mailman "$mailmanCfgTmp" + + mv -n "$hyperkittyCfgTmp" $hyperkittyCfg + mv -n "$mailmanCfgTmp" $mailmanCfg + ''; + serviceConfig = { + Type = "oneshot"; + }; + }; + systemd.services.mailman-web = { description = "Init Postorius DB"; before = [ "httpd.service" ]; @@ -207,8 +246,6 @@ in { serviceConfig = { User = cfg.webUser; Type = "oneshot"; - StateDirectory = "mailman-web"; - StateDirectoryMode = "0700"; WorkingDirectory = "/var/lib/mailman-web"; }; }; @@ -223,7 +260,7 @@ in { }; systemd.services.hyperkitty = { - enable = cfg.hyperkittyApiKey != null; + inherit (cfg.hyperkitty) enable; description = "GNU Hyperkitty QCluster Process"; after = [ "network.target" ]; wantedBy = [ "mailman.service" "multi-user.target" ]; @@ -235,7 +272,7 @@ in { }; systemd.services.hyperkitty-minutely = { - enable = cfg.hyperkittyApiKey != null; + inherit (cfg.hyperkitty) enable; description = "Trigger minutely Hyperkitty events"; startAt = "minutely"; serviceConfig = { @@ -246,7 +283,7 @@ in { }; systemd.services.hyperkitty-quarter-hourly = { - enable = cfg.hyperkittyApiKey != null; + inherit (cfg.hyperkitty) enable; description = "Trigger quarter-hourly Hyperkitty events"; startAt = "*:00/15"; serviceConfig = { @@ -257,7 +294,7 @@ in { }; systemd.services.hyperkitty-hourly = { - enable = cfg.hyperkittyApiKey != null; + inherit (cfg.hyperkitty) enable; description = "Trigger hourly Hyperkitty events"; startAt = "hourly"; serviceConfig = { @@ -268,7 +305,7 @@ in { }; systemd.services.hyperkitty-daily = { - enable = cfg.hyperkittyApiKey != null; + inherit (cfg.hyperkitty) enable; description = "Trigger daily Hyperkitty events"; startAt = "daily"; serviceConfig = { @@ -279,7 +316,7 @@ in { }; systemd.services.hyperkitty-weekly = { - enable = cfg.hyperkittyApiKey != null; + inherit (cfg.hyperkitty) enable; description = "Trigger weekly Hyperkitty events"; startAt = "weekly"; serviceConfig = { @@ -290,7 +327,7 @@ in { }; systemd.services.hyperkitty-yearly = { - enable = cfg.hyperkittyApiKey != null; + inherit (cfg.hyperkitty) enable; description = "Trigger yearly Hyperkitty events"; startAt = "yearly"; serviceConfig = { diff --git a/pkgs/servers/mail/mailman/settings.py b/pkgs/servers/mail/mailman/settings.py index 5f246db41ad..7c3cec92002 100644 --- a/pkgs/servers/mail/mailman/settings.py +++ b/pkgs/servers/mail/mailman/settings.py @@ -39,9 +39,6 @@ BASE_DIR = os.path.dirname(os.path.abspath(__file__)) # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/1.9/howto/deployment/checklist/ -# SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = '$!-7^wl#wiifjbh)5@f7ji%x!vp7s1vzbvwt26hxv$idixq0u0' - # SECURITY WARNING: don't run with debug turned on in production! DEBUG = False @@ -64,7 +61,6 @@ ALLOWED_HOSTS = [ MAILMAN_REST_API_URL = 'http://localhost:8001' MAILMAN_REST_API_USER = 'restadmin' MAILMAN_REST_API_PASS = 'restpass' -MAILMAN_ARCHIVER_KEY = "@ARCHIVER_KEY@" MAILMAN_ARCHIVER_FROM = ('127.0.0.1', '::1') # Application definition