Merge pull request #86177 from mayflower/mailman-upstream
Mailman refactor
This commit is contained in:
commit
aea806b8ea
@ -6,42 +6,46 @@ let
|
|||||||
|
|
||||||
cfg = config.services.mailman;
|
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
|
# This deliberately doesn't use recursiveUpdate so users can
|
||||||
# override the defaults.
|
# override the defaults.
|
||||||
settings = {
|
webSettings = {
|
||||||
DEFAULT_FROM_EMAIL = cfg.siteOwner;
|
DEFAULT_FROM_EMAIL = cfg.siteOwner;
|
||||||
SERVER_EMAIL = cfg.siteOwner;
|
SERVER_EMAIL = cfg.siteOwner;
|
||||||
ALLOWED_HOSTS = [ "localhost" "127.0.0.1" ] ++ cfg.webHosts;
|
ALLOWED_HOSTS = [ "localhost" "127.0.0.1" ] ++ cfg.webHosts;
|
||||||
COMPRESS_OFFLINE = true;
|
COMPRESS_OFFLINE = true;
|
||||||
STATIC_ROOT = "/var/lib/mailman-web/static";
|
STATIC_ROOT = "/var/lib/mailman-web-static";
|
||||||
MEDIA_ROOT = "/var/lib/mailman-web/media";
|
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";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
HAYSTACK_CONNECTIONS.default = {
|
||||||
|
ENGINE = "haystack.backends.whoosh_backend.WhooshEngine";
|
||||||
|
PATH = "/var/lib/mailman-web/fulltext-index";
|
||||||
|
};
|
||||||
} // cfg.webSettings;
|
} // cfg.webSettings;
|
||||||
|
|
||||||
settingsJSON = pkgs.writeText "settings.json" (builtins.toJSON settings);
|
webSettingsJSON = pkgs.writeText "settings.json" (builtins.toJSON webSettings);
|
||||||
|
|
||||||
mailmanCfg = ''
|
# TODO: Should this be RFC42-ised so that users can set additional options without modifying the module?
|
||||||
[mailman]
|
mtaConfig = pkgs.writeText "mailman-postfix.cfg" ''
|
||||||
site_owner: ${cfg.siteOwner}
|
[postfix]
|
||||||
layout: fhs
|
postmap_command: ${pkgs.postfix}/bin/postmap
|
||||||
|
transport_file_type: hash
|
||||||
[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_dir/log
|
|
||||||
lock_dir: $var_dir/lock
|
|
||||||
etc_dir: /etc
|
|
||||||
ext_dir: $etc_dir/mailman.d
|
|
||||||
pid_file: /run/mailman/master.pid
|
|
||||||
'' + 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" ''
|
mailmanHyperkittyCfg = pkgs.writeText "mailman-hyperkitty.cfg" ''
|
||||||
[general]
|
[general]
|
||||||
# This is your HyperKitty installation, preferably on the localhost. This
|
# This is your HyperKitty installation, preferably on the localhost. This
|
||||||
@ -84,7 +88,7 @@ in {
|
|||||||
type = types.package;
|
type = types.package;
|
||||||
default = pkgs.mailman;
|
default = pkgs.mailman;
|
||||||
defaultText = "pkgs.mailman";
|
defaultText = "pkgs.mailman";
|
||||||
example = "pkgs.mailman.override { archivers = []; }";
|
example = literalExample "pkgs.mailman.override { archivers = []; }";
|
||||||
description = "Mailman package to use";
|
description = "Mailman package to use";
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -98,18 +102,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 {
|
webHosts = mkOption {
|
||||||
type = types.listOf types.str;
|
type = types.listOf types.str;
|
||||||
default = [];
|
default = [];
|
||||||
@ -124,7 +116,7 @@ in {
|
|||||||
|
|
||||||
webUser = mkOption {
|
webUser = mkOption {
|
||||||
type = types.str;
|
type = types.str;
|
||||||
default = config.services.httpd.user;
|
default = "mailman-web";
|
||||||
description = ''
|
description = ''
|
||||||
User to run mailman-web as
|
User to run mailman-web as
|
||||||
'';
|
'';
|
||||||
@ -138,6 +130,22 @@ 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 = [];
|
||||||
|
};
|
||||||
|
|
||||||
|
settings = mkOption {
|
||||||
|
description = "Settings for mailman.cfg";
|
||||||
|
type = types.attrsOf (types.attrsOf types.str);
|
||||||
|
default = {};
|
||||||
|
};
|
||||||
|
|
||||||
hyperkitty = {
|
hyperkitty = {
|
||||||
enable = mkEnableOption "the Hyperkitty archiver for Mailman";
|
enable = mkEnableOption "the Hyperkitty archiver for Mailman";
|
||||||
|
|
||||||
@ -158,6 +166,35 @@ in {
|
|||||||
|
|
||||||
config = mkIf cfg.enable {
|
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";
|
||||||
|
};
|
||||||
|
} // (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
|
assertions = let
|
||||||
inherit (config.services) postfix;
|
inherit (config.services) postfix;
|
||||||
|
|
||||||
@ -183,7 +220,17 @@ in {
|
|||||||
(requirePostfixHash [ "config" "local_recipient_maps" ] "postfix_lmtp")
|
(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;
|
environment.etc."mailman.cfg".text = mailmanCfg;
|
||||||
|
|
||||||
@ -198,14 +245,35 @@ in {
|
|||||||
|
|
||||||
import json
|
import json
|
||||||
|
|
||||||
with open('${settingsJSON}') as f:
|
with open('${webSettingsJSON}') as f:
|
||||||
globals().update(json.load(f))
|
globals().update(json.load(f))
|
||||||
|
|
||||||
with open('/var/lib/mailman-web/settings_local.json') as f:
|
with open('/var/lib/mailman-web/settings_local.json') as f:
|
||||||
globals().update(json.load(f))
|
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 = webSettings.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 = {
|
services.postfix = {
|
||||||
recipientDelimiter = "+"; # bake recipient addresses in mail envelopes via VERP
|
recipientDelimiter = "+"; # bake recipient addresses in mail envelopes via VERP
|
||||||
@ -214,25 +282,33 @@ in {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
systemd.services.mailman = {
|
systemd.sockets.mailman-uwsgi = lib.mkIf cfg.serve.enable {
|
||||||
|
wantedBy = ["sockets.target"];
|
||||||
|
before = ["nginx.service"];
|
||||||
|
socketConfig.ListenStream = "/run/mailman-web.socket";
|
||||||
|
};
|
||||||
|
systemd.services = {
|
||||||
|
mailman = {
|
||||||
description = "GNU Mailman Master Process";
|
description = "GNU Mailman Master Process";
|
||||||
after = [ "network.target" ];
|
after = [ "network.target" ];
|
||||||
restartTriggers = [ config.environment.etc."mailman.cfg".source ];
|
restartTriggers = [ config.environment.etc."mailman.cfg".source ];
|
||||||
wantedBy = [ "multi-user.target" ];
|
wantedBy = [ "multi-user.target" ];
|
||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
ExecStart = "${cfg.package}/bin/mailman start";
|
ExecStart = "${pythonEnv}/bin/mailman start";
|
||||||
ExecStop = "${cfg.package}/bin/mailman stop";
|
ExecStop = "${pythonEnv}/bin/mailman stop";
|
||||||
User = "mailman";
|
User = "mailman";
|
||||||
|
Group = "mailman";
|
||||||
Type = "forking";
|
Type = "forking";
|
||||||
RuntimeDirectory = "mailman";
|
RuntimeDirectory = "mailman";
|
||||||
|
LogsDirectory = "mailman";
|
||||||
PIDFile = "/run/mailman/master.pid";
|
PIDFile = "/run/mailman/master.pid";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
systemd.services.mailman-settings = {
|
mailman-settings = {
|
||||||
description = "Generate settings files (including secrets) for Mailman";
|
description = "Generate settings files (including secrets) for Mailman";
|
||||||
before = [ "mailman.service" "mailman-web.service" "hyperkitty.service" "httpd.service" "uwsgi.service" ];
|
before = [ "mailman.service" "mailman-web-setup.service" "mailman-uwsgi.service" "hyperkitty.service" ];
|
||||||
requiredBy = [ "mailman.service" "mailman-web.service" "hyperkitty.service" "httpd.service" "uwsgi.service" ];
|
requiredBy = [ "mailman.service" "mailman-web-setup.service" "mailman-uwsgi.service" "hyperkitty.service" ];
|
||||||
path = with pkgs; [ jq ];
|
path = with pkgs; [ jq ];
|
||||||
script = ''
|
script = ''
|
||||||
mailmanDir=/var/lib/mailman
|
mailmanDir=/var/lib/mailman
|
||||||
@ -241,8 +317,9 @@ in {
|
|||||||
mailmanCfg=$mailmanDir/mailman-hyperkitty.cfg
|
mailmanCfg=$mailmanDir/mailman-hyperkitty.cfg
|
||||||
mailmanWebCfg=$mailmanWebDir/settings_local.json
|
mailmanWebCfg=$mailmanWebDir/settings_local.json
|
||||||
|
|
||||||
install -m 0700 -o mailman -g nogroup -d $mailmanDir
|
install -m 0775 -o mailman -g mailman -d /var/lib/mailman-web-static
|
||||||
install -m 0700 -o ${cfg.webUser} -g nogroup -d $mailmanWebDir
|
install -m 0770 -o mailman -g mailman -d $mailmanDir
|
||||||
|
install -m 0770 -o ${cfg.webUser} -g mailman -d $mailmanWebDir
|
||||||
|
|
||||||
if [ ! -e $mailmanWebCfg ]; then
|
if [ ! -e $mailmanWebCfg ]; then
|
||||||
hyperkittyApiKey=$(tr -dc A-Za-z0-9 < /dev/urandom | head -c 64)
|
hyperkittyApiKey=$(tr -dc A-Za-z0-9 < /dev/urandom | head -c 64)
|
||||||
@ -253,142 +330,108 @@ in {
|
|||||||
--arg archiver_key "$hyperkittyApiKey" \
|
--arg archiver_key "$hyperkittyApiKey" \
|
||||||
--arg secret_key "$secretKey" \
|
--arg secret_key "$secretKey" \
|
||||||
>"$mailmanWebCfgTmp"
|
>"$mailmanWebCfgTmp"
|
||||||
chown ${cfg.webUser} "$mailmanWebCfgTmp"
|
chown root:mailman "$mailmanWebCfgTmp"
|
||||||
mv -n "$mailmanWebCfgTmp" $mailmanWebCfg
|
chmod 440 "$mailmanWebCfgTmp"
|
||||||
|
mv -n "$mailmanWebCfgTmp" "$mailmanWebCfg"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
hyperkittyApiKey="$(jq -r .MAILMAN_ARCHIVER_KEY $mailmanWebCfg)"
|
hyperkittyApiKey="$(jq -r .MAILMAN_ARCHIVER_KEY "$mailmanWebCfg")"
|
||||||
mailmanCfgTmp=$(mktemp)
|
mailmanCfgTmp=$(mktemp)
|
||||||
sed "s/@API_KEY@/$hyperkittyApiKey/g" ${mailmanHyperkittyCfg} >"$mailmanCfgTmp"
|
sed "s/@API_KEY@/$hyperkittyApiKey/g" ${mailmanHyperkittyCfg} >"$mailmanCfgTmp"
|
||||||
chown mailman "$mailmanCfgTmp"
|
chown mailman:mailman "$mailmanCfgTmp"
|
||||||
mv "$mailmanCfgTmp" $mailmanCfg
|
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-web = {
|
mailman-web-setup = {
|
||||||
description = "Init Postorius DB";
|
description = "Prepare mailman-web files and database";
|
||||||
before = [ "httpd.service" "uwsgi.service" ];
|
before = [ "uwsgi.service" "mailman-uwsgi.service" ];
|
||||||
requiredBy = [ "httpd.service" "uwsgi.service" ];
|
requiredBy = [ "mailman-uwsgi.service" ];
|
||||||
restartTriggers = [ config.environment.etc."mailman3/settings.py".source ];
|
restartTriggers = [ config.environment.etc."mailman3/settings.py".source ];
|
||||||
script = ''
|
script = ''
|
||||||
${pkgs.mailman-web}/bin/mailman-web migrate
|
[[ -e "${webSettings.STATIC_ROOT}" ]] && find "${webSettings.STATIC_ROOT}/" -mindepth 1 -delete
|
||||||
rm -rf static
|
${pythonEnv}/bin/mailman-web migrate
|
||||||
${pkgs.mailman-web}/bin/mailman-web collectstatic
|
${pythonEnv}/bin/mailman-web collectstatic
|
||||||
${pkgs.mailman-web}/bin/mailman-web compress
|
${pythonEnv}/bin/mailman-web compress
|
||||||
'';
|
'';
|
||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
User = cfg.webUser;
|
User = cfg.webUser;
|
||||||
|
Group = "mailman";
|
||||||
Type = "oneshot";
|
Type = "oneshot";
|
||||||
# Similar to mailman-settings.service, this makes restartTriggers work
|
|
||||||
# properly for this service.
|
|
||||||
RemainAfterExit = "yes";
|
|
||||||
WorkingDirectory = "/var/lib/mailman-web";
|
WorkingDirectory = "/var/lib/mailman-web";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
systemd.services.mailman-daily = {
|
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";
|
description = "Trigger daily Mailman events";
|
||||||
startAt = "daily";
|
startAt = "daily";
|
||||||
restartTriggers = [ config.environment.etc."mailman.cfg".source ];
|
restartTriggers = [ config.environment.etc."mailman.cfg".source ];
|
||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
ExecStart = "${cfg.package}/bin/mailman digests --send";
|
ExecStart = "${pythonEnv}/bin/mailman digests --send";
|
||||||
User = "mailman";
|
User = "mailman";
|
||||||
|
Group = "mailman";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
systemd.services.hyperkitty = {
|
hyperkitty = lib.mkIf cfg.hyperkitty.enable {
|
||||||
inherit (cfg.hyperkitty) enable;
|
|
||||||
description = "GNU Hyperkitty QCluster Process";
|
description = "GNU Hyperkitty QCluster Process";
|
||||||
after = [ "network.target" ];
|
after = [ "network.target" ];
|
||||||
restartTriggers = [ config.environment.etc."mailman3/settings.py".source ];
|
restartTriggers = [ config.environment.etc."mailman3/settings.py".source ];
|
||||||
wantedBy = [ "mailman.service" "multi-user.target" ];
|
wantedBy = [ "mailman.service" "multi-user.target" ];
|
||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
ExecStart = "${pkgs.mailman-web}/bin/mailman-web qcluster";
|
ExecStart = "${pythonEnv}/bin/mailman-web qcluster";
|
||||||
User = cfg.webUser;
|
User = cfg.webUser;
|
||||||
|
Group = "mailman";
|
||||||
WorkingDirectory = "/var/lib/mailman-web";
|
WorkingDirectory = "/var/lib/mailman-web";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
} // flip lib.mapAttrs' {
|
||||||
systemd.services.hyperkitty-minutely = {
|
"minutely" = "minutely";
|
||||||
inherit (cfg.hyperkitty) enable;
|
"quarter_hourly" = "*:00/15";
|
||||||
description = "Trigger minutely Hyperkitty events";
|
"hourly" = "hourly";
|
||||||
startAt = "minutely";
|
"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 ];
|
restartTriggers = [ config.environment.etc."mailman3/settings.py".source ];
|
||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
ExecStart = "${pkgs.mailman-web}/bin/mailman-web runjobs minutely";
|
ExecStart = "${pythonEnv}/bin/mailman-web runjobs minutely";
|
||||||
User = cfg.webUser;
|
|
||||||
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;
|
User = cfg.webUser;
|
||||||
|
Group = "mailman";
|
||||||
WorkingDirectory = "/var/lib/mailman-web";
|
WorkingDirectory = "/var/lib/mailman-web";
|
||||||
};
|
};
|
||||||
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
meta = {
|
||||||
|
maintainers = with lib.maintainers; [ lheckemann ];
|
||||||
|
doc = ./mailman.xml;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
59
nixos/modules/services/mail/mailman.xml
Normal file
59
nixos/modules/services/mail/mailman.xml
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
<chapter xmlns="http://docbook.org/ns/docbook"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns:xi="http://www.w3.org/2001/XInclude"
|
||||||
|
version="5.0"
|
||||||
|
xml:id="module-services-mailman">
|
||||||
|
<title>Mailman</title>
|
||||||
|
<para>
|
||||||
|
<link xlink:href="https://www.list.org">Mailman</link> 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.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<section xml:id="module-services-mailman-basic-usage">
|
||||||
|
<title>Basic usage</title>
|
||||||
|
<para>
|
||||||
|
For a basic configuration, the following settings are suggested:
|
||||||
|
<programlisting>{ 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 = {
|
||||||
|
<link linkend="opt-services.mailman.enable">enable</link> = true;
|
||||||
|
<link linkend="opt-services.mailman.serve.enable">serve.enable</link> = true;
|
||||||
|
<link linkend="opt-services.mailman.hyperkitty.enable">hyperkitty.enable</link> = true;
|
||||||
|
<link linkend="opt-services.mailman.hyperkitty.enable">webHosts</link> = ["lists.example.org"];
|
||||||
|
<link linkend="opt-services.mailman.hyperkitty.enable">siteOwner</link> = "mailman@example.org";
|
||||||
|
};
|
||||||
|
<link linkend="opt-services.nginx.virtualHosts._name_.enableACME">services.nginx.virtualHosts."lists.example.org".enableACME</link> = true;
|
||||||
|
<link linkend="opt-services.mailman.hyperkitty.enable">networking.firewall.allowedTCPPorts</link> = [ 25 80 443 ];
|
||||||
|
}</programlisting>
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
DNS records will also be required:
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem><para><literal>AAAA</literal> and <literal>A</literal> records pointing to the host in question, in order for browsers to be able to discover the address of the web server;</para></listitem>
|
||||||
|
<listitem><para>An <literal>MX</literal> 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.</para></listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
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.
|
||||||
|
</para>
|
||||||
|
</section>
|
||||||
|
</chapter>
|
@ -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
|
, flufl_bounce, flufl_i18n, flufl_lock, lazr_config, lazr_delegates, passlib
|
||||||
, requests, zope_configuration, click, falcon, importlib-resources
|
, requests, zope_configuration, click, falcon, importlib-resources
|
||||||
, zope_component, lynx, postfix, authheaders, gunicorn
|
, zope_component, lynx, postfix, authheaders, gunicorn
|
||||||
@ -20,7 +20,19 @@ buildPythonPackage rec {
|
|||||||
zope_component authheaders gunicorn
|
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 \
|
substituteInPlace src/mailman/config/postfix.cfg \
|
||||||
--replace /usr/sbin/postmap ${postfix}/bin/postmap
|
--replace /usr/sbin/postmap ${postfix}/bin/postmap
|
||||||
substituteInPlace src/mailman/config/schema.cfg \
|
substituteInPlace src/mailman/config/schema.cfg \
|
||||||
|
13
pkgs/servers/mail/mailman/log-stderr.patch
Normal file
13
pkgs/servers/mail/mailman/log-stderr.patch
Normal file
@ -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))
|
@ -1,6 +1,5 @@
|
|||||||
{ buildPythonPackage, lib, fetchgit, isPy3k
|
{ buildPythonPackage, lib, fetchgit, isPy3k
|
||||||
, git, makeWrapper, sassc, hyperkitty, postorius, whoosh
|
, git, makeWrapper, sassc, hyperkitty, postorius, whoosh
|
||||||
, django
|
|
||||||
}:
|
}:
|
||||||
|
|
||||||
buildPythonPackage rec {
|
buildPythonPackage rec {
|
||||||
@ -17,8 +16,13 @@ buildPythonPackage rec {
|
|||||||
|
|
||||||
# This is just so people installing from pip also get uwsgi
|
# This is just so people installing from pip also get uwsgi
|
||||||
# installed, AFAICT.
|
# 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 = ''
|
postPatch = ''
|
||||||
sed -i '/^ uwsgi$/d' setup.cfg
|
sed -i '/^ uwsgi$/d' setup.cfg
|
||||||
|
sed -i '/^ Django/d' setup.cfg
|
||||||
'';
|
'';
|
||||||
|
|
||||||
nativeBuildInputs = [ git makeWrapper ];
|
nativeBuildInputs = [ git makeWrapper ];
|
||||||
@ -36,7 +40,5 @@ buildPythonPackage rec {
|
|||||||
description = "Django project for Mailman 3 web interface";
|
description = "Django project for Mailman 3 web interface";
|
||||||
license = licenses.gpl3;
|
license = licenses.gpl3;
|
||||||
maintainers = with maintainers; [ peti qyliss ];
|
maintainers = with maintainers; [ peti qyliss ];
|
||||||
# mailman-web requires django < 2.2
|
|
||||||
broken = versionOlder "2.2" django.version;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -15831,11 +15831,7 @@ in
|
|||||||
|
|
||||||
mailman-rss = callPackage ../development/python-modules/mailman-rss { };
|
mailman-rss = callPackage ../development/python-modules/mailman-rss { };
|
||||||
|
|
||||||
mailman-web = with (python3.override {
|
mailman-web = with python3.pkgs; toPythonApplication mailman-web;
|
||||||
packageOverrides = self: super: {
|
|
||||||
django = self.django_1_11;
|
|
||||||
};
|
|
||||||
}).pkgs; toPythonApplication mailman-web;
|
|
||||||
|
|
||||||
mattermost = callPackage ../servers/mattermost { };
|
mattermost = callPackage ../servers/mattermost { };
|
||||||
matterircd = callPackage ../servers/mattermost/matterircd.nix { };
|
matterircd = callPackage ../servers/mattermost/matterircd.nix { };
|
||||||
|
Loading…
x
Reference in New Issue
Block a user