From f5a57c6c40d69f600fcb4e46d2adaa0affc56f9d Mon Sep 17 00:00:00 2001 From: Linus Heckemann Date: Tue, 21 Apr 2020 13:56:52 +0200 Subject: [PATCH 1/7] mailman-web: remove django version checks and override This is nonsense! Postorius and Hyperkitty don't even support 1.11 anymore. --- pkgs/servers/mail/mailman/web.nix | 8 +++++--- pkgs/top-level/all-packages.nix | 6 +----- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/pkgs/servers/mail/mailman/web.nix b/pkgs/servers/mail/mailman/web.nix index f770f2e4489d..53fdf851cfe8 100644 --- a/pkgs/servers/mail/mailman/web.nix +++ b/pkgs/servers/mail/mailman/web.nix @@ -1,6 +1,5 @@ { buildPythonPackage, lib, fetchgit, isPy3k , git, makeWrapper, sassc, hyperkitty, postorius, whoosh -, django }: buildPythonPackage rec { @@ -17,8 +16,13 @@ buildPythonPackage rec { # This is just so people installing from pip also get uwsgi # installed, AFAICT. + + # Django is depended on transitively by hyperkitty and postorius, + # and mailman_web has overly restrictive version bounds on it, so + # let's remove it. postPatch = '' sed -i '/^ uwsgi$/d' setup.cfg + sed -i '/^ Django/d' setup.cfg ''; nativeBuildInputs = [ git makeWrapper ]; @@ -36,7 +40,5 @@ buildPythonPackage rec { description = "Django project for Mailman 3 web interface"; license = licenses.gpl3; maintainers = with maintainers; [ peti qyliss ]; - # mailman-web requires django < 2.2 - broken = versionOlder "2.2" django.version; }; } diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index 0a0bb8018efe..42cc71cda63e 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -15830,11 +15830,7 @@ in mailman-rss = callPackage ../development/python-modules/mailman-rss { }; - mailman-web = with (python3.override { - packageOverrides = self: super: { - django = self.django_1_11; - }; - }).pkgs; toPythonApplication mailman-web; + mailman-web = with python3.pkgs; toPythonApplication mailman-web; mattermost = callPackage ../servers/mattermost { }; matterircd = callPackage ../servers/mattermost/matterircd.nix { }; From b478e0043c53964c99cc9a145c155a673af3c7d8 Mon Sep 17 00:00:00 2001 From: Linus Heckemann Date: Wed, 15 Apr 2020 14:02:59 +0200 Subject: [PATCH 2/7] nixos/mailman: refactor - Add serve.enable option, which configures uwsgi and nginx to serve the mailman-web application; - Configure services to log to the journal, where possible. Mailman Core does not provide any options for this, but will now log to /var/log/mailman; - Use a unified python environment for all components, with an extraPackages option to allow use of postgres support and similar; - Configure mailman's postfix module such that it can generate the domain and lmtp maps; - Fix formatting for option examples; - Provide a mailman-web user to run the uwsgi service by default - Refactor Hyperkitty's periodic jobs to reduce repetition in the expressions; - Remove service dependencies not related to functionality included in the module, such as httpd -- these should be configured in user config when used; - Move static files root to /var/lib/mailman-web-static by default. This avoids permission issues when a static file web server attempts to access /var/lib/mailman which is private to mailman. The location can still be changed by setting services.mailman.webSettings.STATIC_ROOT; - Remove the webRoot option, which seems to have been included by accident, being an unsuitable directory for serving via HTTP. - Rename mailman-web.service to mailman-web-setup.service, since it doesn't actually serve mailman-web. There is now a mailman-uwsgi.service if serve.enable is set to true. --- nixos/modules/services/mail/mailman.nix | 389 +++++++++++++----------- 1 file changed, 206 insertions(+), 183 deletions(-) diff --git a/nixos/modules/services/mail/mailman.nix b/nixos/modules/services/mail/mailman.nix index f5e78b182933..4167b2ca85f1 100644 --- a/nixos/modules/services/mail/mailman.nix +++ b/nixos/modules/services/mail/mailman.nix @@ -6,6 +6,11 @@ let cfg = config.services.mailman; + pythonEnv = pkgs.python3.withPackages (ps: + [ps.mailman ps.mailman-web] + ++ lib.optional cfg.hyperkitty.enable ps.mailman-hyperkitty + ++ cfg.extraPythonPackages); + # This deliberately doesn't use recursiveUpdate so users can # override the defaults. settings = { @@ -13,12 +18,28 @@ let SERVER_EMAIL = cfg.siteOwner; ALLOWED_HOSTS = [ "localhost" "127.0.0.1" ] ++ cfg.webHosts; COMPRESS_OFFLINE = true; - STATIC_ROOT = "/var/lib/mailman-web/static"; + STATIC_ROOT = "/var/lib/mailman-web-static"; MEDIA_ROOT = "/var/lib/mailman-web/media"; + LOGGING = { + version = 1; + disable_existing_loggers = true; + handlers.console.class = "logging.StreamHandler"; + loggers.django = { + handlers = [ "console" ]; + level = "INFO"; + }; + }; } // cfg.webSettings; settingsJSON = pkgs.writeText "settings.json" (builtins.toJSON settings); + # TODO: Should this be RFC42-ised so that users can set additional options without modifying the module? + mtaConfig = pkgs.writeText "mailman-postfix.cfg" '' + [postfix] + postmap_command: ${pkgs.postfix}/bin/postmap + transport_file_type: hash + ''; + mailmanCfg = '' [mailman] site_owner: ${cfg.siteOwner} @@ -29,11 +50,14 @@ let var_dir: /var/lib/mailman queue_dir: $var_dir/queue template_dir: $var_dir/templates - log_dir: $var_dir/log + log_dir: /var/log/mailman lock_dir: $var_dir/lock etc_dir: /etc ext_dir: $etc_dir/mailman.d pid_file: /run/mailman/master.pid + + [mta] + configuration: ${mtaConfig} '' + optionalString cfg.hyperkitty.enable '' [archiver.hyperkitty] @@ -84,7 +108,7 @@ in { type = types.package; default = pkgs.mailman; defaultText = "pkgs.mailman"; - example = "pkgs.mailman.override { archivers = []; }"; + example = literalExample "pkgs.mailman.override { archivers = []; }"; description = "Mailman package to use"; }; @@ -98,18 +122,6 @@ in { ''; }; - webRoot = mkOption { - type = types.path; - default = "${pkgs.mailman-web}/${pkgs.python3.sitePackages}"; - defaultText = "\${pkgs.mailman-web}/\${pkgs.python3.sitePackages}"; - description = '' - The web root for the Hyperkity + Postorius apps provided by Mailman. - This variable can be set, of course, but it mainly exists so that site - admins can refer to it in their own hand-written web server - configuration files. - ''; - }; - webHosts = mkOption { type = types.listOf types.str; default = []; @@ -124,7 +136,7 @@ in { webUser = mkOption { type = types.str; - default = config.services.httpd.user; + default = "mailman-web"; description = '' User to run mailman-web as ''; @@ -138,6 +150,16 @@ in { ''; }; + serve = { + enable = mkEnableOption "Automatic nginx and uwsgi setup for mailman-web"; + }; + + extraPythonPackages = mkOption { + description = "Packages to add to the python environment used by mailman and mailman-web"; + type = types.listOf types.package; + default = []; + }; + hyperkitty = { enable = mkEnableOption "the Hyperkitty archiver for Mailman"; @@ -183,7 +205,17 @@ in { (requirePostfixHash [ "config" "local_recipient_maps" ] "postfix_lmtp") ]; - users.users.mailman = { description = "GNU Mailman"; isSystemUser = true; }; + users.users.mailman = { + description = "GNU Mailman"; + isSystemUser = true; + group = "mailman"; + }; + users.users.mailman-web = lib.mkIf (cfg.webUser == "mailman-web") { + description = "GNU Mailman web interface"; + isSystemUser = true; + group = "mailman"; + }; + users.groups.mailman = {}; environment.etc."mailman.cfg".text = mailmanCfg; @@ -205,7 +237,28 @@ in { globals().update(json.load(f)) ''; - environment.systemPackages = [ cfg.package ] ++ (with pkgs; [ mailman-web ]); + services.nginx = mkIf cfg.serve.enable { + enable = mkDefault true; + virtualHosts."${lib.head cfg.webHosts}" = { + serverAliases = cfg.webHosts; + locations = { + "/".extraConfig = "uwsgi_pass unix:/run/mailman-web.socket;"; + "/static/".alias = settings.STATIC_ROOT + "/"; + }; + }; + }; + + environment.systemPackages = [ (pkgs.buildEnv { + name = "mailman-tools"; + # We don't want to pollute the system PATH with a python + # interpreter etc. so let's pick only the stuff we actually + # want from pythonEnv + pathsToLink = ["/bin"]; + paths = [pythonEnv]; + postBuild = '' + find $out/bin/ -mindepth 1 -not -name "mailman*" -delete + ''; + }) ]; services.postfix = { recipientDelimiter = "+"; # bake recipient addresses in mail envelopes via VERP @@ -214,181 +267,151 @@ in { }; }; - systemd.services.mailman = { - description = "GNU Mailman Master Process"; - after = [ "network.target" ]; - restartTriggers = [ config.environment.etc."mailman.cfg".source ]; - wantedBy = [ "multi-user.target" ]; - serviceConfig = { - ExecStart = "${cfg.package}/bin/mailman start"; - ExecStop = "${cfg.package}/bin/mailman stop"; - User = "mailman"; - Type = "forking"; - RuntimeDirectory = "mailman"; - PIDFile = "/run/mailman/master.pid"; - }; + systemd.sockets.mailman-uwsgi = lib.mkIf cfg.serve.enable { + wantedBy = ["sockets.target"]; + before = ["nginx.service"]; + socketConfig.ListenStream = "/run/mailman-web.socket"; }; - - systemd.services.mailman-settings = { - description = "Generate settings files (including secrets) for Mailman"; - before = [ "mailman.service" "mailman-web.service" "hyperkitty.service" "httpd.service" "uwsgi.service" ]; - requiredBy = [ "mailman.service" "mailman-web.service" "hyperkitty.service" "httpd.service" "uwsgi.service" ]; - path = with pkgs; [ jq ]; - script = '' - mailmanDir=/var/lib/mailman - mailmanWebDir=/var/lib/mailman-web - - mailmanCfg=$mailmanDir/mailman-hyperkitty.cfg - mailmanWebCfg=$mailmanWebDir/settings_local.json - - install -m 0700 -o mailman -g nogroup -d $mailmanDir - install -m 0700 -o ${cfg.webUser} -g nogroup -d $mailmanWebDir - - if [ ! -e $mailmanWebCfg ]; then - hyperkittyApiKey=$(tr -dc A-Za-z0-9 < /dev/urandom | head -c 64) - secretKey=$(tr -dc A-Za-z0-9 < /dev/urandom | head -c 64) - - mailmanWebCfgTmp=$(mktemp) - jq -n '.MAILMAN_ARCHIVER_KEY=$archiver_key | .SECRET_KEY=$secret_key' \ - --arg archiver_key "$hyperkittyApiKey" \ - --arg secret_key "$secretKey" \ - >"$mailmanWebCfgTmp" - chown ${cfg.webUser} "$mailmanWebCfgTmp" - mv -n "$mailmanWebCfgTmp" $mailmanWebCfg - fi - - hyperkittyApiKey="$(jq -r .MAILMAN_ARCHIVER_KEY $mailmanWebCfg)" - mailmanCfgTmp=$(mktemp) - sed "s/@API_KEY@/$hyperkittyApiKey/g" ${mailmanHyperkittyCfg} >"$mailmanCfgTmp" - chown mailman "$mailmanCfgTmp" - mv "$mailmanCfgTmp" $mailmanCfg - ''; - serviceConfig = { - Type = "oneshot"; - # RemainAfterExit makes restartIfChanged work for this service, so - # downstream services will get updated automatically when things like - # services.mailman.hyperkitty.baseUrl change. Otherwise users have to - # restart things manually, which is confusing. - RemainAfterExit = "yes"; + systemd.services = { + mailman = { + description = "GNU Mailman Master Process"; + after = [ "network.target" ]; + restartTriggers = [ config.environment.etc."mailman.cfg".source ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + ExecStart = "${pythonEnv}/bin/mailman start"; + ExecStop = "${pythonEnv}/bin/mailman stop"; + User = "mailman"; + Group = "mailman"; + Type = "forking"; + RuntimeDirectory = "mailman"; + LogsDirectory = "mailman"; + PIDFile = "/run/mailman/master.pid"; + }; }; - }; - systemd.services.mailman-web = { - description = "Init Postorius DB"; - before = [ "httpd.service" "uwsgi.service" ]; - requiredBy = [ "httpd.service" "uwsgi.service" ]; - restartTriggers = [ config.environment.etc."mailman3/settings.py".source ]; - script = '' - ${pkgs.mailman-web}/bin/mailman-web migrate - rm -rf static - ${pkgs.mailman-web}/bin/mailman-web collectstatic - ${pkgs.mailman-web}/bin/mailman-web compress - ''; - serviceConfig = { - User = cfg.webUser; - Type = "oneshot"; - # Similar to mailman-settings.service, this makes restartTriggers work - # properly for this service. - RemainAfterExit = "yes"; - WorkingDirectory = "/var/lib/mailman-web"; + mailman-settings = { + description = "Generate settings files (including secrets) for Mailman"; + before = [ "mailman.service" "mailman-web-setup.service" "mailman-uwsgi.service" "hyperkitty.service" ]; + requiredBy = [ "mailman.service" "mailman-web-setup.service" "mailman-uwsgi.service" "hyperkitty.service" ]; + path = with pkgs; [ jq ]; + script = '' + mailmanDir=/var/lib/mailman + mailmanWebDir=/var/lib/mailman-web + + mailmanCfg=$mailmanDir/mailman-hyperkitty.cfg + mailmanWebCfg=$mailmanWebDir/settings_local.json + + install -m 0775 -o mailman -g mailman -d /var/lib/mailman-web-static + install -m 0770 -o mailman -g mailman -d $mailmanDir + install -m 0770 -o ${cfg.webUser} -g mailman -d $mailmanWebDir + + if [ ! -e $mailmanWebCfg ]; then + hyperkittyApiKey=$(tr -dc A-Za-z0-9 < /dev/urandom | head -c 64) + secretKey=$(tr -dc A-Za-z0-9 < /dev/urandom | head -c 64) + + mailmanWebCfgTmp=$(mktemp) + jq -n '.MAILMAN_ARCHIVER_KEY=$archiver_key | .SECRET_KEY=$secret_key' \ + --arg archiver_key "$hyperkittyApiKey" \ + --arg secret_key "$secretKey" \ + >"$mailmanWebCfgTmp" + chown root:mailman "$mailmanWebCfgTmp" + chmod 440 "$mailmanWebCfgTmp" + mv -n "$mailmanWebCfgTmp" "$mailmanWebCfg" + fi + + hyperkittyApiKey="$(jq -r .MAILMAN_ARCHIVER_KEY "$mailmanWebCfg")" + mailmanCfgTmp=$(mktemp) + sed "s/@API_KEY@/$hyperkittyApiKey/g" ${mailmanHyperkittyCfg} >"$mailmanCfgTmp" + chown mailman:mailman "$mailmanCfgTmp" + mv "$mailmanCfgTmp" "$mailmanCfg" + ''; }; - }; - systemd.services.mailman-daily = { - description = "Trigger daily Mailman events"; - startAt = "daily"; - restartTriggers = [ config.environment.etc."mailman.cfg".source ]; - serviceConfig = { - ExecStart = "${cfg.package}/bin/mailman digests --send"; - User = "mailman"; + mailman-web-setup = { + description = "Prepare mailman-web files and database"; + before = [ "uwsgi.service" "mailman-uwsgi.service" ]; + requiredBy = [ "mailman-uwsgi.service" ]; + restartTriggers = [ config.environment.etc."mailman3/settings.py".source ]; + script = '' + find "${settings.STATIC_ROOT}/" -mindepth 1 -delete + ${pythonEnv}/bin/mailman-web migrate + ${pythonEnv}/bin/mailman-web collectstatic + ${pythonEnv}/bin/mailman-web compress + ''; + serviceConfig = { + User = cfg.webUser; + Group = "mailman"; + Type = "oneshot"; + WorkingDirectory = "/var/lib/mailman-web"; + }; }; - }; - systemd.services.hyperkitty = { - inherit (cfg.hyperkitty) enable; - description = "GNU Hyperkitty QCluster Process"; - after = [ "network.target" ]; - restartTriggers = [ config.environment.etc."mailman3/settings.py".source ]; - wantedBy = [ "mailman.service" "multi-user.target" ]; - serviceConfig = { - ExecStart = "${pkgs.mailman-web}/bin/mailman-web qcluster"; - User = cfg.webUser; - WorkingDirectory = "/var/lib/mailman-web"; + mailman-uwsgi = mkIf cfg.serve.enable (let + uwsgiConfig.uwsgi = { + type = "normal"; + plugins = ["python3"]; + home = pythonEnv; + module = "mailman_web.wsgi"; + }; + uwsgiConfigFile = pkgs.writeText "uwsgi-mailman.json" (builtins.toJSON uwsgiConfig); + in { + wantedBy = ["multi-user.target"]; + requires = ["mailman-uwsgi.socket" "mailman-web-setup.service"]; + restartTriggers = [ config.environment.etc."mailman3/settings.py".source ]; + serviceConfig = { + # Since the mailman-web settings.py obstinately creates a logs + # dir in the cwd, change to the (writable) runtime directory before + # starting uwsgi. + ExecStart = "${pkgs.coreutils}/bin/env -C $RUNTIME_DIRECTORY ${pkgs.uwsgi.override { plugins = ["python3"]; }}/bin/uwsgi --json ${uwsgiConfigFile}"; + User = cfg.webUser; + Group = "mailman"; + RuntimeDirectory = "mailman-uwsgi"; + }; + }); + + mailman-daily = { + description = "Trigger daily Mailman events"; + startAt = "daily"; + restartTriggers = [ config.environment.etc."mailman.cfg".source ]; + serviceConfig = { + ExecStart = "${pythonEnv}/bin/mailman digests --send"; + User = "mailman"; + Group = "mailman"; + }; }; - }; - systemd.services.hyperkitty-minutely = { - inherit (cfg.hyperkitty) enable; - description = "Trigger minutely Hyperkitty events"; - startAt = "minutely"; - restartTriggers = [ config.environment.etc."mailman3/settings.py".source ]; - serviceConfig = { - ExecStart = "${pkgs.mailman-web}/bin/mailman-web runjobs minutely"; - User = cfg.webUser; - WorkingDirectory = "/var/lib/mailman-web"; + hyperkitty = lib.mkIf cfg.hyperkitty.enable { + description = "GNU Hyperkitty QCluster Process"; + after = [ "network.target" ]; + restartTriggers = [ config.environment.etc."mailman3/settings.py".source ]; + wantedBy = [ "mailman.service" "multi-user.target" ]; + serviceConfig = { + ExecStart = "${pythonEnv}/bin/mailman-web qcluster"; + User = cfg.webUser; + Group = "mailman"; + WorkingDirectory = "/var/lib/mailman-web"; + }; }; - }; - - systemd.services.hyperkitty-quarter-hourly = { - inherit (cfg.hyperkitty) enable; - description = "Trigger quarter-hourly Hyperkitty events"; - startAt = "*:00/15"; - restartTriggers = [ config.environment.etc."mailman3/settings.py".source ]; - serviceConfig = { - ExecStart = "${pkgs.mailman-web}/bin/mailman-web runjobs quarter_hourly"; - User = cfg.webUser; - WorkingDirectory = "/var/lib/mailman-web"; - }; - }; - - systemd.services.hyperkitty-hourly = { - inherit (cfg.hyperkitty) enable; - description = "Trigger hourly Hyperkitty events"; - startAt = "hourly"; - restartTriggers = [ config.environment.etc."mailman3/settings.py".source ]; - serviceConfig = { - ExecStart = "${pkgs.mailman-web}/bin/mailman-web runjobs hourly"; - User = cfg.webUser; - WorkingDirectory = "/var/lib/mailman-web"; - }; - }; - - systemd.services.hyperkitty-daily = { - inherit (cfg.hyperkitty) enable; - description = "Trigger daily Hyperkitty events"; - startAt = "daily"; - restartTriggers = [ config.environment.etc."mailman3/settings.py".source ]; - serviceConfig = { - ExecStart = "${pkgs.mailman-web}/bin/mailman-web runjobs daily"; - User = cfg.webUser; - WorkingDirectory = "/var/lib/mailman-web"; - }; - }; - - systemd.services.hyperkitty-weekly = { - inherit (cfg.hyperkitty) enable; - description = "Trigger weekly Hyperkitty events"; - startAt = "weekly"; - restartTriggers = [ config.environment.etc."mailman3/settings.py".source ]; - serviceConfig = { - ExecStart = "${pkgs.mailman-web}/bin/mailman-web runjobs weekly"; - User = cfg.webUser; - WorkingDirectory = "/var/lib/mailman-web"; - }; - }; - - systemd.services.hyperkitty-yearly = { - inherit (cfg.hyperkitty) enable; - description = "Trigger yearly Hyperkitty events"; - startAt = "yearly"; - restartTriggers = [ config.environment.etc."mailman3/settings.py".source ]; - serviceConfig = { - ExecStart = "${pkgs.mailman-web}/bin/mailman-web runjobs yearly"; - User = cfg.webUser; - WorkingDirectory = "/var/lib/mailman-web"; - }; - }; - + } // flip lib.mapAttrs' { + "minutely" = "minutely"; + "quarter_hourly" = "*:00/15"; + "hourly" = "hourly"; + "daily" = "daily"; + "weekly" = "weekly"; + "yearly" = "yearly"; + } (name: startAt: + lib.nameValuePair "hyperkitty-${name}" (lib.mkIf cfg.hyperkitty.enable { + description = "Trigger ${name} Hyperkitty events"; + inherit startAt; + restartTriggers = [ config.environment.etc."mailman3/settings.py".source ]; + serviceConfig = { + ExecStart = "${pythonEnv}/bin/mailman-web runjobs minutely"; + User = cfg.webUser; + Group = "mailman"; + WorkingDirectory = "/var/lib/mailman-web"; + }; + })); }; } From 32c556b039c6d262cd2ccedcb4fb92c2c08e89dc Mon Sep 17 00:00:00 2001 From: Linus Heckemann Date: Mon, 27 Apr 2020 18:03:44 +0200 Subject: [PATCH 3/7] nixos/mailman: document, add maintainers --- nixos/modules/services/mail/mailman.nix | 5 +++ nixos/modules/services/mail/mailman.xml | 59 +++++++++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 nixos/modules/services/mail/mailman.xml diff --git a/nixos/modules/services/mail/mailman.nix b/nixos/modules/services/mail/mailman.nix index 4167b2ca85f1..69ae4263b61c 100644 --- a/nixos/modules/services/mail/mailman.nix +++ b/nixos/modules/services/mail/mailman.nix @@ -414,4 +414,9 @@ in { })); }; + meta = { + maintainers = with lib.maintainers; [ lheckemann ]; + doc = ./mailman.xml; + }; + } diff --git a/nixos/modules/services/mail/mailman.xml b/nixos/modules/services/mail/mailman.xml new file mode 100644 index 000000000000..cbe50ed0b917 --- /dev/null +++ b/nixos/modules/services/mail/mailman.xml @@ -0,0 +1,59 @@ + + Mailman + + Mailman is free + software for managing electronic mail discussion and e-newsletter + lists. Mailman and its web interface can be configured using the + corresponding NixOS module. Note that this service is best used with + an existing, securely configured Postfix setup, as it does not automatically configure this. + + +
+ Basic usage + + For a basic configuration, the following settings are suggested: + { config, ... }: { + services.postfix = { + enable = true; + relayDomains = ["hash:/var/lib/mailman/data/postfix_domains"]; + sslCert = config.security.acme.certs."lists.example.org".directory + "/full.pem"; + sslKey = config.security.acme.certs."lists.example.org".directory + "/key.pem"; + config = { + transport_maps = ["hash:/var/lib/mailman/data/postfix_lmtp"]; + local_recipient_maps = ["hash:/var/lib/mailman/data/postfix_lmtp"]; + }; + }; + services.mailman = { + enable = true; + serve.enable = true; + hyperkitty.enable = true; + webHosts = ["lists.example.org"]; + siteOwner = "mailman@example.org"; + }; + services.nginx.virtualHosts."lists.example.org".enableACME = true; + networking.firewall.allowedTCPPorts = [ 25 80 443 ]; +} + + + DNS records will also be required: + + AAAA and A records pointing to the host in question, in order for browsers to be able to discover the address of the web server; + An MX record pointing to a domain name at which the host is reachable, in order for other mail servers to be able to deliver emails to the mailing lists it hosts. + + + + After this has been done and appropriate DNS records have been + set up, the Postorius mailing list manager and the Hyperkitty + archive browser will be available at + https://lists.example.org/. Note that this setup is not + sufficient to deliver emails to most email providers nor to + avoid spam -- a number of additional measures for authenticating + incoming and outgoing mails, such as SPF, DMARC and DKIM are + necessary, but outside the scope of the Mailman module. + +
+
From 1b8af3e1ae75df048fbdfb3acf6842f13de3e38f Mon Sep 17 00:00:00 2001 From: Linus Heckemann Date: Thu, 30 Apr 2020 08:18:52 +0200 Subject: [PATCH 4/7] nixos/mailman: fix clearing static files --- nixos/modules/services/mail/mailman.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nixos/modules/services/mail/mailman.nix b/nixos/modules/services/mail/mailman.nix index 69ae4263b61c..7f9a7cc072ee 100644 --- a/nixos/modules/services/mail/mailman.nix +++ b/nixos/modules/services/mail/mailman.nix @@ -334,7 +334,7 @@ in { requiredBy = [ "mailman-uwsgi.service" ]; restartTriggers = [ config.environment.etc."mailman3/settings.py".source ]; script = '' - find "${settings.STATIC_ROOT}/" -mindepth 1 -delete + [[ -e "${settings.STATIC_ROOT}" ]] && find "${settings.STATIC_ROOT}/" -mindepth 1 -delete ${pythonEnv}/bin/mailman-web migrate ${pythonEnv}/bin/mailman-web collectstatic ${pythonEnv}/bin/mailman-web compress From 3dbbc786f51eb3f74fc75b1d179b3c447e315ecc Mon Sep 17 00:00:00 2001 From: Linus Heckemann Date: Fri, 22 May 2020 16:52:10 +0200 Subject: [PATCH 5/7] nixos/mailman: RFC42-ise --- nixos/modules/services/mail/mailman.nix | 67 ++++++++++++++----------- 1 file changed, 37 insertions(+), 30 deletions(-) diff --git a/nixos/modules/services/mail/mailman.nix b/nixos/modules/services/mail/mailman.nix index 7f9a7cc072ee..a604f32b3371 100644 --- a/nixos/modules/services/mail/mailman.nix +++ b/nixos/modules/services/mail/mailman.nix @@ -13,7 +13,7 @@ let # This deliberately doesn't use recursiveUpdate so users can # override the defaults. - settings = { + webSettings = { DEFAULT_FROM_EMAIL = cfg.siteOwner; SERVER_EMAIL = cfg.siteOwner; ALLOWED_HOSTS = [ "localhost" "127.0.0.1" ] ++ cfg.webHosts; @@ -31,7 +31,7 @@ let }; } // cfg.webSettings; - settingsJSON = pkgs.writeText "settings.json" (builtins.toJSON settings); + webSettingsJSON = pkgs.writeText "settings.json" (builtins.toJSON webSettings); # TODO: Should this be RFC42-ised so that users can set additional options without modifying the module? mtaConfig = pkgs.writeText "mailman-postfix.cfg" '' @@ -40,31 +40,7 @@ let transport_file_type: hash ''; - mailmanCfg = '' - [mailman] - site_owner: ${cfg.siteOwner} - layout: fhs - - [paths.fhs] - bin_dir: ${pkgs.python3Packages.mailman}/bin - var_dir: /var/lib/mailman - queue_dir: $var_dir/queue - template_dir: $var_dir/templates - log_dir: /var/log/mailman - lock_dir: $var_dir/lock - etc_dir: /etc - ext_dir: $etc_dir/mailman.d - pid_file: /run/mailman/master.pid - - [mta] - configuration: ${mtaConfig} - '' + optionalString cfg.hyperkitty.enable '' - - [archiver.hyperkitty] - class: mailman_hyperkitty.Archiver - enable: yes - configuration: /var/lib/mailman/mailman-hyperkitty.cfg - ''; + mailmanCfg = lib.generators.toINI {} cfg.settings; mailmanHyperkittyCfg = pkgs.writeText "mailman-hyperkitty.cfg" '' [general] @@ -160,6 +136,12 @@ in { default = []; }; + settings = mkOption { + description = "Settings for mailman.cfg"; + type = types.attrsOf (types.attrsOf types.str); + default = {}; + }; + hyperkitty = { enable = mkEnableOption "the Hyperkitty archiver for Mailman"; @@ -180,6 +162,31 @@ in { config = mkIf cfg.enable { + services.mailman.settings = { + mailman.site_owner = lib.mkDefault cfg.siteOwner; + mailman.layout = "fhs"; + + "paths.fhs" = { + bin_dir = "${pkgs.python3Packages.mailman}/bin"; + var_dir = "/var/lib/mailman"; + queue_dir = "$var_dir/queue"; + template_dir = "$var_dir/templates"; + log_dir = "/var/log/mailman"; + lock_dir = "$var_dir/lock"; + etc_dir = "/etc"; + ext_dir = "$etc_dir/mailman.d"; + pid_file = "/run/mailman/master.pid"; + }; + + mta.configuration = lib.mkDefault "${mtaConfig}"; + + "archiver.hyperkitty" = lib.mkIf cfg.hyperkitty.enable { + class = "mailman_hyperkitty.Archiver"; + enable = "yes"; + configuration = "/var/lib/mailman/mailman-hyperkitty.cfg"; + }; + }; + assertions = let inherit (config.services) postfix; @@ -230,7 +237,7 @@ in { import json - with open('${settingsJSON}') as f: + with open('${webSettingsJSON}') as f: globals().update(json.load(f)) with open('/var/lib/mailman-web/settings_local.json') as f: @@ -243,7 +250,7 @@ in { serverAliases = cfg.webHosts; locations = { "/".extraConfig = "uwsgi_pass unix:/run/mailman-web.socket;"; - "/static/".alias = settings.STATIC_ROOT + "/"; + "/static/".alias = webSettings.STATIC_ROOT + "/"; }; }; }; @@ -334,7 +341,7 @@ in { requiredBy = [ "mailman-uwsgi.service" ]; restartTriggers = [ config.environment.etc."mailman3/settings.py".source ]; script = '' - [[ -e "${settings.STATIC_ROOT}" ]] && find "${settings.STATIC_ROOT}/" -mindepth 1 -delete + [[ -e "${webSettings.STATIC_ROOT}" ]] && find "${webSettings.STATIC_ROOT}/" -mindepth 1 -delete ${pythonEnv}/bin/mailman-web migrate ${pythonEnv}/bin/mailman-web collectstatic ${pythonEnv}/bin/mailman-web compress From 176bc68a699ad62156c6d727babe07fe0fe1294d Mon Sep 17 00:00:00 2001 From: Linus Heckemann Date: Fri, 22 May 2020 14:12:43 +0200 Subject: [PATCH 6/7] mailman: log to journal --- nixos/modules/services/mail/mailman.nix | 6 +++++- pkgs/servers/mail/mailman/default.nix | 16 ++++++++++++++-- pkgs/servers/mail/mailman/log-stderr.patch | 13 +++++++++++++ 3 files changed, 32 insertions(+), 3 deletions(-) create mode 100644 pkgs/servers/mail/mailman/log-stderr.patch diff --git a/nixos/modules/services/mail/mailman.nix b/nixos/modules/services/mail/mailman.nix index a604f32b3371..3744214ca49f 100644 --- a/nixos/modules/services/mail/mailman.nix +++ b/nixos/modules/services/mail/mailman.nix @@ -185,7 +185,11 @@ in { enable = "yes"; configuration = "/var/lib/mailman/mailman-hyperkitty.cfg"; }; - }; + } // (let + loggerNames = ["root" "archiver" "bounce" "config" "database" "debug" "error" "fromusenet" "http" "locks" "mischief" "plugins" "runner" "smtp"]; + loggerSectionNames = map (n: "logging.${n}") loggerNames; + in lib.genAttrs loggerSectionNames(name: { handler = "stderr"; }) + ); assertions = let inherit (config.services) postfix; diff --git a/pkgs/servers/mail/mailman/default.nix b/pkgs/servers/mail/mailman/default.nix index 37b4d29eeb16..879fd19adb0a 100644 --- a/pkgs/servers/mail/mailman/default.nix +++ b/pkgs/servers/mail/mailman/default.nix @@ -1,4 +1,4 @@ -{ stdenv, buildPythonPackage, fetchPypi, isPy3k, alembic, aiosmtpd, dnspython +{ stdenv, buildPythonPackage, fetchPypi, fetchpatch, isPy3k, alembic, aiosmtpd, dnspython , flufl_bounce, flufl_i18n, flufl_lock, lazr_config, lazr_delegates, passlib , requests, zope_configuration, click, falcon, importlib-resources , zope_component, lynx, postfix, authheaders, gunicorn @@ -20,7 +20,19 @@ buildPythonPackage rec { zope_component authheaders gunicorn ]; - patchPhase = '' + patches = [ + (fetchpatch { + url = https://gitlab.com/mailman/mailman/-/commit/4b206e2a5267a0e17f345fd7b2d957122ba57566.patch; + sha256 = "06axmrn74p81wvcki36c7gfj5fp5q15zxz2yl3lrvijic7hbs4n2"; + }) + (fetchpatch { + url = https://gitlab.com/mailman/mailman/-/commit/9613154f3c04fa2383fbf017031ef263c291418d.patch; + sha256 = "0vyw87s857vfxbf7kihwb6w094xyxmxbi1bpdqi3ybjamjycp55r"; + }) + ./log-stderr.patch + ]; + + postPatch = '' substituteInPlace src/mailman/config/postfix.cfg \ --replace /usr/sbin/postmap ${postfix}/bin/postmap substituteInPlace src/mailman/config/schema.cfg \ diff --git a/pkgs/servers/mail/mailman/log-stderr.patch b/pkgs/servers/mail/mailman/log-stderr.patch new file mode 100644 index 000000000000..2edbe1f18313 --- /dev/null +++ b/pkgs/servers/mail/mailman/log-stderr.patch @@ -0,0 +1,13 @@ +diff --git a/src/mailman/core/logging.py b/src/mailman/core/logging.py +index f8f87279f..7ff13b003 100644 +--- a/src/mailman/core/logging.py ++++ b/src/mailman/core/logging.py +@@ -142,6 +142,8 @@ def _init_logger(propagate, sub_name, log, logger_config): + address, facility = _get_syslog_params(logger_config) + handler = logging.handlers.SysLogHandler( + address=address, facility=facility) ++ elif logger_config.handler == 'stderr': ++ handler = logging.StreamHandler(sys.stderr) + else: + path_str = logger_config.path + path_abs = os.path.normpath(os.path.join(config.LOG_DIR, path_str)) From d5cc8fb892da85550375bc533e0db5470dd34396 Mon Sep 17 00:00:00 2001 From: Linus Heckemann Date: Tue, 26 May 2020 10:36:01 +0200 Subject: [PATCH 7/7] nixos/mailman: fix search index location --- nixos/modules/services/mail/mailman.nix | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/nixos/modules/services/mail/mailman.nix b/nixos/modules/services/mail/mailman.nix index 3744214ca49f..5c61cfbebf6c 100644 --- a/nixos/modules/services/mail/mailman.nix +++ b/nixos/modules/services/mail/mailman.nix @@ -29,6 +29,10 @@ let level = "INFO"; }; }; + HAYSTACK_CONNECTIONS.default = { + ENGINE = "haystack.backends.whoosh_backend.WhooshEngine"; + PATH = "/var/lib/mailman-web/fulltext-index"; + }; } // cfg.webSettings; webSettingsJSON = pkgs.writeText "settings.json" (builtins.toJSON webSettings);