From 3652efdb00eeaf215ba39c0845998a37a4ef5b34 Mon Sep 17 00:00:00 2001 From: Richard Marko Date: Fri, 26 Jul 2019 00:25:24 +0200 Subject: [PATCH 1/5] perlPackages.MIMELiteHTML: init at 1.24 --- pkgs/top-level/perl-packages.nix | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/pkgs/top-level/perl-packages.nix b/pkgs/top-level/perl-packages.nix index 9de64cfb886e..ea42111b4b65 100644 --- a/pkgs/top-level/perl-packages.nix +++ b/pkgs/top-level/perl-packages.nix @@ -11310,6 +11310,21 @@ let }; }; + MIMELiteHTML = buildPerlPackage { + pname = "MIME-Lite-HTML"; + version = "1.24"; + src = fetchurl { + url = mirror://cpan/authors/id/A/AL/ALIAN/MIME-Lite-HTML-1.24.tar.gz; + sha256 = "db603ccbf6653bcd28cfa824d72e511ead019fc8afb9f1854ec872db2d3cd8da"; + }; + doCheck = false; + propagatedBuildInputs = [ HTMLParser LWP MIMELite URI ]; + meta = { + description = "Provide routine to transform a HTML page in a MIME-Lite mail"; + license = with stdenv.lib.licenses; [ artistic1 gpl1Plus ]; + }; + }; + MIMETools = buildPerlPackage { pname = "MIME-tools"; version = "5.509"; From 02493a6e65e89092817db558f24bb93e442026d3 Mon Sep 17 00:00:00 2001 From: Richard Marko Date: Fri, 26 Jul 2019 00:23:19 +0200 Subject: [PATCH 2/5] perlPackages.HTMLStripScriptsParser: init at 1.03 --- pkgs/top-level/perl-packages.nix | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/pkgs/top-level/perl-packages.nix b/pkgs/top-level/perl-packages.nix index ea42111b4b65..b4233508e3b8 100644 --- a/pkgs/top-level/perl-packages.nix +++ b/pkgs/top-level/perl-packages.nix @@ -8456,6 +8456,20 @@ let buildInputs = [ TestDifferences TestMemoryCycle ]; }; + HTMLStripScriptsParser = buildPerlPackage { + pname = "HTML-StripScripts-Parser"; + version = "1.03"; + src = fetchurl { + url = mirror://cpan/authors/id/D/DR/DRTECH/HTML-StripScripts-Parser-1.03.tar.gz; + sha256 = "478c1a4e46eb77fa7bce96ba288168f0b98c27f250e00dc6312365081aed3407"; + }; + propagatedBuildInputs = [ HTMLParser HTMLStripScripts ]; + meta = { + description = "XSS filter using HTML::Parser"; + license = with stdenv.lib.licenses; [ artistic1 gpl1Plus ]; + }; + }; + HTMLTableExtract = buildPerlPackage { pname = "HTML-TableExtract"; version = "2.13"; From 80098e9a71719f7fd571270983f3ff6356927223 Mon Sep 17 00:00:00 2001 From: Richard Marko Date: Fri, 26 Jul 2019 00:22:17 +0200 Subject: [PATCH 3/5] perlPackages.HTMLStripScripts: init at 1.06 --- pkgs/top-level/perl-packages.nix | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/pkgs/top-level/perl-packages.nix b/pkgs/top-level/perl-packages.nix index b4233508e3b8..de165bcfc23f 100644 --- a/pkgs/top-level/perl-packages.nix +++ b/pkgs/top-level/perl-packages.nix @@ -8456,6 +8456,19 @@ let buildInputs = [ TestDifferences TestMemoryCycle ]; }; + HTMLStripScripts = buildPerlPackage { + pname = "HTML-StripScripts"; + version = "1.06"; + src = fetchurl { + url = mirror://cpan/authors/id/D/DR/DRTECH/HTML-StripScripts-1.06.tar.gz; + sha256 = "222bfb7ec1fdfa465e32da3dc4abed2edc7364bbe19e8e3c513c7d585b0109ad"; + }; + meta = { + description = "Strip scripting constructs out of HTML"; + license = with stdenv.lib.licenses; [ artistic1 gpl1Plus ]; + }; + }; + HTMLStripScriptsParser = buildPerlPackage { pname = "HTML-StripScripts-Parser"; version = "1.03"; From e39d7fab273e62fbe9b2a506641361b3f056184a Mon Sep 17 00:00:00 2001 From: Richard Marko Date: Sat, 6 Jul 2019 20:54:16 +0200 Subject: [PATCH 4/5] sympa: init at 6.2.52 --- pkgs/servers/mail/sympa/default.nix | 116 ++++++++++++++++++++++++ pkgs/servers/mail/sympa/make-docs.patch | 11 +++ pkgs/top-level/all-packages.nix | 2 + 3 files changed, 129 insertions(+) create mode 100644 pkgs/servers/mail/sympa/default.nix create mode 100644 pkgs/servers/mail/sympa/make-docs.patch diff --git a/pkgs/servers/mail/sympa/default.nix b/pkgs/servers/mail/sympa/default.nix new file mode 100644 index 000000000000..46cbcc61f94c --- /dev/null +++ b/pkgs/servers/mail/sympa/default.nix @@ -0,0 +1,116 @@ +{ stdenv, perl, fetchFromGitHub, autoreconfHook +}: + +let + dataDir = "/var/lib/sympa"; + runtimeDir = "/run/sympa"; + perlEnv = perl.withPackages (p: with p; [ + ArchiveZip + CGI + CGIFast + ClassSingleton + DateTime + DBI + DateTimeFormatMail + DateTimeTimeZone + DigestMD5 + Encode + FCGI + FileCopyRecursive + FileNFSLock + FilePath + HTMLParser + HTMLFormatter + HTMLTree + HTMLStripScriptsParser + IO + IOStringy + LWP + libintl_perl + + MHonArc + MIMEBase64 + MIMECharset + MIMETools + MIMEEncWords + MIMELiteHTML + MailTools + NetCIDR + ScalarListUtils + SysSyslog + TermProgressBar + TemplateToolkit + URI + UnicodeLineBreak + XMLLibXML + + ### Features + Clone + CryptEksblowfish + + DBDPg + DBDSQLite + DBDmysql + + DataPassword + EncodeLocale + IOSocketSSL + MailDKIM + NetDNS + NetLDAP + NetSMTP + SOAPLite + ]); +in +stdenv.mkDerivation rec { + pname = "sympa"; + version = "6.2.52"; + + src = fetchFromGitHub { + owner = "sympa-community"; + repo = pname; + rev = version; + sha256 = "071kx6ryifs2f6fhfky9g297frzp5584kn444af1vb2imzydsbnh"; + }; + + configureFlags = [ + "--without-initdir" + "--without-unitsdir" + "--without-smrshdir" + + "--with-lockdir=${runtimeDir}" + "--with-piddir=${runtimeDir}" + "--with-confdir=${dataDir}/etc" + "--sysconfdir=${dataDir}/etc" + "--with-spooldir=${dataDir}/spool" + "--with-expldir=${dataDir}/list_data" + ]; + nativeBuildInputs = [ autoreconfHook ]; + buildInputs = [ perlEnv ]; + patches = [ ./make-docs.patch ]; + + prePatch = '' + patchShebangs po/sympa/add-lang.pl + ''; + + preInstall = '' + mkdir "$TMP/bin" + for i in chown chgrp chmod; do + echo '#!${stdenv.shell}' >> "$TMP/bin/$i" + chmod +x "$TMP/bin/$i" + done + PATH="$TMP/bin:$PATH" + ''; + + postInstall = '' + rm -rf "$TMP/bin" + ''; + + meta = with stdenv.lib; { + description = "Open source mailing list manager"; + homepage = "https://www.sympa.org"; + license = licenses.gpl2; + maintainers = with maintainers; [ sorki mmilata ]; + platforms = platforms.all; + }; +} diff --git a/pkgs/servers/mail/sympa/make-docs.patch b/pkgs/servers/mail/sympa/make-docs.patch new file mode 100644 index 000000000000..adefeafdeef7 --- /dev/null +++ b/pkgs/servers/mail/sympa/make-docs.patch @@ -0,0 +1,11 @@ +diff -ur sympa-6.2.44-orig/doc/Makefile.am sympa-6.2.44/doc/Makefile.am +--- sympa-6.2.44-orig/doc/Makefile.am 2019-08-29 01:57:43.512539087 +0200 ++++ sympa-6.2.44/doc/Makefile.am 2019-08-29 01:58:24.393531358 +0200 +@@ -83,6 +83,4 @@ + --lax --release="$(VERSION)" $< $@ + + .podpl.pod: +- $(AM_V_GEN)PERL5LIB=$(top_builddir)/src/lib:$(top_srcdir)/src/lib; \ +- export PERL5LIB; \ +- $(PERL) $< --top_srcdir=$(top_srcdir) > $*.pod ++ $(AM_V_GEN)$(PERL) -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib $< --top_srcdir=$(top_srcdir) > $*.pod diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index 0c9b909a9d06..1cda87f6f818 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -15449,6 +15449,8 @@ in pshs = callPackage ../servers/http/pshs { }; + sympa = callPackage ../servers/mail/sympa { }; + system-sendmail = lowPrio (callPackage ../servers/mail/system-sendmail { }); # PulseAudio daemons From 097ab90850d968c90444f221841b2a7949cf216a Mon Sep 17 00:00:00 2001 From: Martin Milata Date: Sat, 6 Jul 2019 20:56:30 +0200 Subject: [PATCH 5/5] nixos/sympa: init module --- nixos/modules/module-list.nix | 1 + nixos/modules/services/mail/sympa.nix | 596 ++++++++++++++++++++++++++ nixos/tests/all-tests.nix | 1 + nixos/tests/sympa.nix | 36 ++ 4 files changed, 634 insertions(+) create mode 100644 nixos/modules/services/mail/sympa.nix create mode 100644 nixos/tests/sympa.nix diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 878b77969af1..eaa7038a28db 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -401,6 +401,7 @@ ./services/mail/rspamd.nix ./services/mail/rss2email.nix ./services/mail/roundcube.nix + ./services/mail/sympa.nix ./services/mail/nullmailer.nix ./services/misc/airsonic.nix ./services/misc/apache-kafka.nix diff --git a/nixos/modules/services/mail/sympa.nix b/nixos/modules/services/mail/sympa.nix new file mode 100644 index 000000000000..c3ae9d4255b0 --- /dev/null +++ b/nixos/modules/services/mail/sympa.nix @@ -0,0 +1,596 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.sympa; + dataDir = "/var/lib/sympa"; + user = "sympa"; + group = "sympa"; + pkg = pkgs.sympa; + fqdns = attrNames cfg.domains; + usingNginx = cfg.web.enable && cfg.web.server == "nginx"; + mysqlLocal = cfg.database.createLocally && cfg.database.type == "MySQL"; + pgsqlLocal = cfg.database.createLocally && cfg.database.type == "PostgreSQL"; + + sympaSubServices = [ + "sympa-archive.service" + "sympa-bounce.service" + "sympa-bulk.service" + "sympa-task.service" + ]; + + # common for all services including wwsympa + commonServiceConfig = { + StateDirectory = "sympa"; + ProtectHome = true; + ProtectSystem = "full"; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectControlGroups = true; + }; + + # wwsympa has its own service config + sympaServiceConfig = srv: { + Type = "simple"; + Restart = "always"; + ExecStart = "${pkg}/bin/${srv}.pl --foreground"; + PIDFile = "/run/sympa/${srv}.pid"; + User = user; + Group = group; + + # avoid duplicating log messageges in journal + StandardError = "null"; + } // commonServiceConfig; + + configVal = value: + if isBool value then + if value then "on" else "off" + else toString value; + configGenerator = c: concatStrings (flip mapAttrsToList c (key: val: "${key}\t${configVal val}\n")); + + mainConfig = pkgs.writeText "sympa.conf" (configGenerator cfg.settings); + robotConfig = fqdn: domain: pkgs.writeText "${fqdn}-robot.conf" (configGenerator domain.settings); + + transport = pkgs.writeText "transport.sympa" (concatStringsSep "\n" (flip map fqdns (domain: '' + ${domain} error:User unknown in recipient table + sympa@${domain} sympa:sympa@${domain} + listmaster@${domain} sympa:listmaster@${domain} + bounce@${domain} sympabounce:sympa@${domain} + abuse-feedback-report@${domain} sympabounce:sympa@${domain} + ''))); + + virtual = pkgs.writeText "virtual.sympa" (concatStringsSep "\n" (flip map fqdns (domain: '' + sympa-request@${domain} postmaster@localhost + sympa-owner@${domain} postmaster@localhost + ''))); + + listAliases = pkgs.writeText "list_aliases.tt2" '' + #--- [% list.name %]@[% list.domain %]: list transport map created at [% date %] + [% list.name %]@[% list.domain %] sympa:[% list.name %]@[% list.domain %] + [% list.name %]-request@[% list.domain %] sympa:[% list.name %]-request@[% list.domain %] + [% list.name %]-editor@[% list.domain %] sympa:[% list.name %]-editor@[% list.domain %] + #[% list.name %]-subscribe@[% list.domain %] sympa:[% list.name %]-subscribe@[%list.domain %] + [% list.name %]-unsubscribe@[% list.domain %] sympa:[% list.name %]-unsubscribe@[% list.domain %] + [% list.name %][% return_path_suffix %]@[% list.domain %] sympabounce:[% list.name %]@[% list.domain %] + ''; + + enabledFiles = filterAttrs (n: v: v.enable) cfg.settingsFile; +in +{ + + ###### interface + options.services.sympa = with types; { + + enable = mkEnableOption "Sympa mailing list manager"; + + lang = mkOption { + type = str; + default = "en_US"; + example = "cs"; + description = '' + Default Sympa language. + See + for available options. + ''; + }; + + listMasters = mkOption { + type = listOf str; + example = [ "postmaster@sympa.example.org" ]; + description = '' + The list of the email addresses of the listmasters + (users authorized to perform global server commands). + ''; + }; + + mainDomain = mkOption { + type = nullOr str; + default = null; + example = "lists.example.org"; + description = '' + Main domain to be used in sympa.conf. + If null, one of the is chosen for you. + ''; + }; + + domains = mkOption { + type = attrsOf (submodule ({ name, config, ... }: { + options = { + webHost = mkOption { + type = nullOr str; + default = null; + example = "archive.example.org"; + description = '' + Domain part of the web interface URL (no web interface for this domain if null). + DNS record of type A (or AAAA or CNAME) has to exist with this value. + ''; + }; + webLocation = mkOption { + type = str; + default = "/"; + example = "/sympa"; + description = "URL path part of the web interface."; + }; + settings = mkOption { + type = attrsOf (oneOf [ str int bool ]); + default = {}; + example = { + default_max_list_members = 3; + }; + description = '' + The robot.conf configuration file as key value set. + See + for list of configuration parameters. + ''; + }; + }; + + config.settings = mkIf (cfg.web.enable && config.webHost != null) { + wwsympa_url = mkDefault "https://${config.webHost}${strings.removeSuffix "/" config.webLocation}"; + }; + })); + + description = '' + Email domains handled by this instance. There have + to be MX records for keys of this attribute set. + ''; + example = literalExample '' + { + "lists.example.org" = { + webHost = "lists.example.org"; + webLocation = "/"; + }; + "sympa.example.com" = { + webHost = "example.com"; + webLocation = "/sympa"; + }; + } + ''; + }; + + database = { + type = mkOption { + type = enum [ "SQLite" "PostgreSQL" "MySQL" ]; + default = "SQLite"; + example = "MySQL"; + description = "Database engine to use."; + }; + + host = mkOption { + type = nullOr str; + default = null; + description = '' + Database host address. + + For MySQL, use localhost to connect using Unix domain socket. + + For PostgreSQL, use path to directory (e.g. /run/postgresql) + to connect using Unix domain socket located in this directory. + + Use null to fall back on Sympa default, or when using + . + ''; + }; + + port = mkOption { + type = nullOr port; + default = null; + description = "Database port. Use null for default port."; + }; + + name = mkOption { + type = str; + default = if cfg.database.type == "SQLite" then "${dataDir}/sympa.sqlite" else "sympa"; + defaultText = ''if database.type == "SQLite" then "${dataDir}/sympa.sqlite" else "sympa"''; + description = '' + Database name. When using SQLite this must be an absolute + path to the database file. + ''; + }; + + user = mkOption { + type = nullOr str; + default = user; + description = "Database user. The system user name is used as a default."; + }; + + passwordFile = mkOption { + type = nullOr path; + default = null; + example = "/run/keys/sympa-dbpassword"; + description = '' + A file containing the password for . + ''; + }; + + createLocally = mkOption { + type = bool; + default = true; + description = "Whether to create a local database automatically."; + }; + }; + + web = { + enable = mkOption { + type = bool; + default = true; + description = "Whether to enable Sympa web interface."; + }; + + server = mkOption { + type = enum [ "nginx" "none" ]; + default = "nginx"; + description = '' + The webserver used for the Sympa web interface. Set it to `none` if you want to configure it yourself. + Further nginx configuration can be done by adapting + . + ''; + }; + + https = mkOption { + type = bool; + default = true; + description = '' + Whether to use HTTPS. When nginx integration is enabled, this option forces SSL and enables ACME. + Please note that Sympa web interface always uses https links even when this option is disabled. + ''; + }; + + fcgiProcs = mkOption { + type = ints.positive; + default = 2; + description = "Number of FastCGI processes to fork."; + }; + }; + + mta = { + type = mkOption { + type = enum [ "postfix" "none" ]; + default = "postfix"; + description = '' + Mail transfer agent (MTA) integration. Use none if you want to configure it yourself. + + The postfix integration sets up local Postfix instance that will pass incoming + messages from configured domains to Sympa. You still need to configure at least outgoing message + handling using e.g. . + ''; + }; + }; + + settings = mkOption { + type = attrsOf (oneOf [ str int bool ]); + default = {}; + example = literalExample '' + { + default_home = "lists"; + viewlogs_page_size = 50; + } + ''; + description = '' + The sympa.conf configuration file as key value set. + See + for list of configuration parameters. + ''; + }; + + settingsFile = mkOption { + type = attrsOf (submodule ({ name, config, ... }: { + options = { + enable = mkOption { + type = bool; + default = true; + description = "Whether this file should be generated. This option allows specific files to be disabled."; + }; + text = mkOption { + default = null; + type = nullOr lines; + description = "Text of the file."; + }; + source = mkOption { + type = path; + description = "Path of the source file."; + }; + }; + + config.source = mkIf (config.text != null) (mkDefault (pkgs.writeText "sympa-${baseNameOf name}" config.text)); + })); + default = {}; + example = literalExample '' + { + "list_data/lists.example.org/help" = { + text = "subject This list provides help to users"; + }; + } + ''; + description = "Set of files to be linked in ${dataDir}."; + }; + }; + + ###### implementation + + config = mkIf cfg.enable { + + services.sympa.settings = (mapAttrs (_: v: mkDefault v) { + domain = if cfg.mainDomain != null then cfg.mainDomain else head fqdns; + listmaster = concatStringsSep "," cfg.listMasters; + lang = cfg.lang; + + home = "${dataDir}/list_data"; + arc_path = "${dataDir}/arc"; + bounce_path = "${dataDir}/bounce"; + + sendmail = "${pkgs.system-sendmail}/bin/sendmail"; + + db_type = cfg.database.type; + db_name = cfg.database.name; + } + // (optionalAttrs (cfg.database.host != null) { + db_host = cfg.database.host; + }) + // (optionalAttrs mysqlLocal { + db_host = "localhost"; # use unix domain socket + }) + // (optionalAttrs pgsqlLocal { + db_host = "/run/postgresql"; # use unix domain socket + }) + // (optionalAttrs (cfg.database.port != null) { + db_port = cfg.database.port; + }) + // (optionalAttrs (cfg.database.user != null) { + db_user = cfg.database.user; + }) + // (optionalAttrs (cfg.mta.type == "postfix") { + sendmail_aliases = "${dataDir}/sympa_transport"; + aliases_program = "${pkgs.postfix}/bin/postmap"; + aliases_db_type = "hash"; + }) + // (optionalAttrs cfg.web.enable { + static_content_path = "${dataDir}/static_content"; + css_path = "${dataDir}/static_content/css"; + pictures_path = "${dataDir}/static_content/pictures"; + mhonarc = "${pkgs.perlPackages.MHonArc}/bin/mhonarc"; + })); + + services.sympa.settingsFile = { + "virtual.sympa" = mkDefault { source = virtual; }; + "transport.sympa" = mkDefault { source = transport; }; + "etc/list_aliases.tt2" = mkDefault { source = listAliases; }; + } + // (flip mapAttrs' cfg.domains (fqdn: domain: + nameValuePair "etc/${fqdn}/robot.conf" (mkDefault { source = robotConfig fqdn domain; }))); + + environment = { + systemPackages = [ pkg ]; + }; + + users.users.${user} = { + description = "Sympa mailing list manager user"; + group = group; + home = dataDir; + createHome = false; + isSystemUser = true; + }; + + users.groups.${group} = {}; + + assertions = [ + { assertion = cfg.database.createLocally -> cfg.database.user == user; + message = "services.sympa.database.user must be set to ${user} if services.sympa.database.createLocally is set to true"; + } + { assertion = cfg.database.createLocally -> cfg.database.passwordFile == null; + message = "a password cannot be specified if services.sympa.database.createLocally is set to true"; + } + ]; + + systemd.tmpfiles.rules = [ + "d ${dataDir} 0711 ${user} ${group} - -" + "d ${dataDir}/etc 0700 ${user} ${group} - -" + "d ${dataDir}/spool 0700 ${user} ${group} - -" + "d ${dataDir}/list_data 0700 ${user} ${group} - -" + "d ${dataDir}/arc 0700 ${user} ${group} - -" + "d ${dataDir}/bounce 0700 ${user} ${group} - -" + "f ${dataDir}/sympa_transport 0600 ${user} ${group} - -" + + # force-copy static_content so it's up to date with package + # set permissions for wwsympa which needs write access (...) + "R ${dataDir}/static_content - - - - -" + "C ${dataDir}/static_content 0711 ${user} ${group} - ${pkg}/static_content" + "e ${dataDir}/static_content/* 0711 ${user} ${group} - -" + + "d /run/sympa 0755 ${user} ${group} - -" + ] + ++ (flip concatMap fqdns (fqdn: [ + "d ${dataDir}/etc/${fqdn} 0700 ${user} ${group} - -" + "d ${dataDir}/list_data/${fqdn} 0700 ${user} ${group} - -" + ])) + #++ (flip mapAttrsToList enabledFiles (k: v: + # "L+ ${dataDir}/${k} - - - - ${v.source}" + #)) + ++ (concatLists (flip mapAttrsToList enabledFiles (k: v: [ + # sympa doesn't handle symlinks well (e.g. fails to create locks) + # force-copy instead + "R ${dataDir}/${k} - - - - -" + "C ${dataDir}/${k} 0700 ${user} ${group} - ${v.source}" + ]))); + + systemd.services.sympa = { + description = "Sympa mailing list manager"; + + wantedBy = [ "multi-user.target" ]; + after = [ "network-online.target" ]; + wants = sympaSubServices; + before = sympaSubServices; + serviceConfig = sympaServiceConfig "sympa_msg"; + + preStart = '' + umask 0077 + + cp -f ${mainConfig} ${dataDir}/etc/sympa.conf + ${optionalString (cfg.database.passwordFile != null) '' + chmod u+w ${dataDir}/etc/sympa.conf + echo -n "db_passwd " >> ${dataDir}/etc/sympa.conf + cat ${cfg.database.passwordFile} >> ${dataDir}/etc/sympa.conf + ''} + + ${optionalString (cfg.mta.type == "postfix") '' + ${pkgs.postfix}/bin/postmap hash:${dataDir}/virtual.sympa + ${pkgs.postfix}/bin/postmap hash:${dataDir}/transport.sympa + ''} + ${pkg}/bin/sympa_newaliases.pl + ${pkg}/bin/sympa.pl --health_check + ''; + }; + systemd.services.sympa-archive = { + description = "Sympa mailing list manager (archiving)"; + bindsTo = [ "sympa.service" ]; + serviceConfig = sympaServiceConfig "archived"; + }; + systemd.services.sympa-bounce = { + description = "Sympa mailing list manager (bounce processing)"; + bindsTo = [ "sympa.service" ]; + serviceConfig = sympaServiceConfig "bounced"; + }; + systemd.services.sympa-bulk = { + description = "Sympa mailing list manager (message distribution)"; + bindsTo = [ "sympa.service" ]; + serviceConfig = sympaServiceConfig "bulk"; + }; + systemd.services.sympa-task = { + description = "Sympa mailing list manager (task management)"; + bindsTo = [ "sympa.service" ]; + serviceConfig = sympaServiceConfig "task_manager"; + }; + + systemd.services.wwsympa = mkIf usingNginx { + wantedBy = [ "multi-user.target" ]; + after = [ "sympa.service" ]; + serviceConfig = { + Type = "forking"; + PIDFile = "/run/sympa/wwsympa.pid"; + Restart = "always"; + ExecStart = ''${pkgs.spawn_fcgi}/bin/spawn-fcgi \ + -u ${user} \ + -g ${group} \ + -U nginx \ + -M 0600 \ + -F ${toString cfg.web.fcgiProcs} \ + -P /run/sympa/wwsympa.pid \ + -s /run/sympa/wwsympa.socket \ + -- ${pkg}/bin/wwsympa.fcgi + ''; + + } // commonServiceConfig; + }; + + services.nginx.enable = mkIf usingNginx true; + services.nginx.virtualHosts = mkIf usingNginx (let + vHosts = unique (remove null (mapAttrsToList (_k: v: v.webHost) cfg.domains)); + hostLocations = host: map (v: v.webLocation) (filter (v: v.webHost == host) (attrValues cfg.domains)); + httpsOpts = optionalAttrs cfg.web.https { forceSSL = mkDefault true; enableACME = mkDefault true; }; + in + genAttrs vHosts (host: { + locations = genAttrs (hostLocations host) (loc: { + extraConfig = '' + include ${config.services.nginx.package}/conf/fastcgi_params; + + fastcgi_pass unix:/run/sympa/wwsympa.socket; + fastcgi_split_path_info ^(${loc})(.*)$; + + fastcgi_param PATH_INFO $fastcgi_path_info; + fastcgi_param SCRIPT_FILENAME ${pkg}/bin/wwsympa.fcgi; + ''; + }) // { + "/static-sympa/".alias = "${dataDir}/static_content/"; + }; + } // httpsOpts)); + + services.postfix = mkIf (cfg.mta.type == "postfix") { + enable = true; + recipientDelimiter = "+"; + config = { + virtual_alias_maps = [ "hash:${dataDir}/virtual.sympa" ]; + virtual_mailbox_maps = [ + "hash:${dataDir}/transport.sympa" + "hash:${dataDir}/sympa_transport" + "hash:${dataDir}/virtual.sympa" + ]; + virtual_mailbox_domains = [ "hash:${dataDir}/transport.sympa" ]; + transport_maps = [ + "hash:${dataDir}/transport.sympa" + "hash:${dataDir}/sympa_transport" + ]; + }; + masterConfig = { + "sympa" = { + type = "unix"; + privileged = true; + chroot = false; + command = "pipe"; + args = [ + "flags=hqRu" + "user=${user}" + "argv=${pkg}/bin/queue" + "\${nexthop}" + ]; + }; + "sympabounce" = { + type = "unix"; + privileged = true; + chroot = false; + command = "pipe"; + args = [ + "flags=hqRu" + "user=${user}" + "argv=${pkg}/bin/bouncequeue" + "\${nexthop}" + ]; + }; + }; + }; + + services.mysql = optionalAttrs mysqlLocal { + enable = true; + package = mkDefault pkgs.mariadb; + ensureDatabases = [ cfg.database.name ]; + ensureUsers = [ + { name = cfg.database.user; + ensurePermissions = { "${cfg.database.name}.*" = "ALL PRIVILEGES"; }; + } + ]; + }; + + services.postgresql = optionalAttrs pgsqlLocal { + enable = true; + ensureDatabases = [ cfg.database.name ]; + ensureUsers = [ + { name = cfg.database.user; + ensurePermissions = { "DATABASE ${cfg.database.name}" = "ALL PRIVILEGES"; }; + } + ]; + }; + + }; + + meta.maintainers = with maintainers; [ mmilata sorki ]; +} diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index e1c299b8413e..83366c271a21 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -269,6 +269,7 @@ in strongswan-swanctl = handleTest ./strongswan-swanctl.nix {}; sudo = handleTest ./sudo.nix {}; switchTest = handleTest ./switch-test.nix {}; + sympa = handleTest ./sympa.nix {}; syncthing-init = handleTest ./syncthing-init.nix {}; syncthing-relay = handleTest ./syncthing-relay.nix {}; systemd = handleTest ./systemd.nix {}; diff --git a/nixos/tests/sympa.nix b/nixos/tests/sympa.nix new file mode 100644 index 000000000000..280691f7cb40 --- /dev/null +++ b/nixos/tests/sympa.nix @@ -0,0 +1,36 @@ +import ./make-test-python.nix ({ pkgs, lib, ... }: { + name = "sympa"; + meta.maintainers = with lib.maintainers; [ mmilata ]; + + machine = + { ... }: + { + virtualisation.memorySize = 1024; + + services.sympa = { + enable = true; + domains = { + "lists.example.org" = { + webHost = "localhost"; + }; + }; + listMasters = [ "joe@example.org" ]; + web.enable = true; + web.https = false; + database = { + type = "PostgreSQL"; + createLocally = true; + }; + }; + }; + + testScript = '' + start_all() + + machine.wait_for_unit("sympa.service") + machine.wait_for_unit("wwsympa.service") + assert "Mailing lists service" in machine.succeed( + "curl --insecure -L http://localhost/" + ) + ''; +})