diff options
Diffstat (limited to 'nixos/modules/services/mail')
-rw-r--r-- | nixos/modules/services/mail/dovecot.nix | 14 | ||||
-rw-r--r-- | nixos/modules/services/mail/exim.nix | 9 | ||||
-rw-r--r-- | nixos/modules/services/mail/freepops.nix | 89 | ||||
-rw-r--r-- | nixos/modules/services/mail/mailhog.nix | 68 | ||||
-rw-r--r-- | nixos/modules/services/mail/mailman.nix | 49 | ||||
-rw-r--r-- | nixos/modules/services/mail/mailman.xml | 45 | ||||
-rw-r--r-- | nixos/modules/services/mail/mlmmj.nix | 43 | ||||
-rw-r--r-- | nixos/modules/services/mail/nullmailer.nix | 1 | ||||
-rw-r--r-- | nixos/modules/services/mail/opendkim.nix | 30 | ||||
-rw-r--r-- | nixos/modules/services/mail/postfix.nix | 54 | ||||
-rw-r--r-- | nixos/modules/services/mail/postgrey.nix | 2 | ||||
-rw-r--r-- | nixos/modules/services/mail/roundcube.nix | 7 | ||||
-rw-r--r-- | nixos/modules/services/mail/rspamd.nix | 48 | ||||
-rw-r--r-- | nixos/modules/services/mail/spamassassin.nix | 65 | ||||
-rw-r--r-- | nixos/modules/services/mail/sympa.nix | 4 |
15 files changed, 320 insertions, 208 deletions
diff --git a/nixos/modules/services/mail/dovecot.nix b/nixos/modules/services/mail/dovecot.nix index c166ef68f29..1ccfb357750 100644 --- a/nixos/modules/services/mail/dovecot.nix +++ b/nixos/modules/services/mail/dovecot.nix @@ -84,11 +84,9 @@ let ( optionalString (cfg.mailboxes != {}) '' - protocol imap { - namespace inbox { - inbox=yes - ${concatStringsSep "\n" (map mailboxConfig (attrValues cfg.mailboxes))} - } + namespace inbox { + inbox=yes + ${concatStringsSep "\n" (map mailboxConfig (attrValues cfg.mailboxes))} } '' ) @@ -407,7 +405,7 @@ in }; } // optionalAttrs (cfg.createMailUser && cfg.mailUser != null) { ${cfg.mailUser} = - { description = "Virtual Mail User"; } // optionalAttrs (cfg.mailGroup != null) + { description = "Virtual Mail User"; isSystemUser = true; } // optionalAttrs (cfg.mailGroup != null) { group = cfg.mailGroup; }; }; @@ -429,12 +427,12 @@ in wantedBy = [ "multi-user.target" ]; restartTriggers = [ cfg.configFile modulesDir ]; + startLimitIntervalSec = 60; # 1 min serviceConfig = { ExecStart = "${dovecotPkg}/sbin/dovecot -F"; ExecReload = "${dovecotPkg}/sbin/doveadm reload"; Restart = "on-failure"; RestartSec = "1s"; - StartLimitInterval = "1min"; RuntimeDirectory = [ "dovecot2" ]; }; @@ -465,7 +463,7 @@ in environment.systemPackages = [ dovecotPkg ]; warnings = mkIf (any isList options.services.dovecot2.mailboxes.definitions) [ - "Declaring `services.dovecot2.mailboxes' as a list is deprecated and will break eval in 21.03! See the release notes for more info for migration." + "Declaring `services.dovecot2.mailboxes' as a list is deprecated and will break eval in 21.05! See the release notes for more info for migration." ]; assertions = [ diff --git a/nixos/modules/services/mail/exim.nix b/nixos/modules/services/mail/exim.nix index 892fbd33214..8927d84b478 100644 --- a/nixos/modules/services/mail/exim.nix +++ b/nixos/modules/services/mail/exim.nix @@ -67,6 +67,13 @@ in ''; }; + queueRunnerInterval = mkOption { + type = types.str; + default = "5m"; + description = '' + How often to spawn a new queue runner. + ''; + }; }; }; @@ -104,7 +111,7 @@ in wantedBy = [ "multi-user.target" ]; restartTriggers = [ config.environment.etc."exim.conf".source ]; serviceConfig = { - ExecStart = "${cfg.package}/bin/exim -bdf -q30m"; + ExecStart = "${cfg.package}/bin/exim -bdf -q${cfg.queueRunnerInterval}"; ExecReload = "${coreutils}/bin/kill -HUP $MAINPID"; }; preStart = '' diff --git a/nixos/modules/services/mail/freepops.nix b/nixos/modules/services/mail/freepops.nix deleted file mode 100644 index 5b729ca50a5..00000000000 --- a/nixos/modules/services/mail/freepops.nix +++ /dev/null @@ -1,89 +0,0 @@ -{ config, lib, pkgs, ... }: - -with lib; - -let - cfg = config.services.mail.freepopsd; -in - -{ - options = { - services.mail.freepopsd = { - enable = mkOption { - default = false; - type = with types; bool; - description = '' - Enables Freepops, a POP3 webmail wrapper. - ''; - }; - - port = mkOption { - default = 2000; - type = with types; uniq int; - description = '' - Port on which the pop server will listen. - ''; - }; - - threads = mkOption { - default = 5; - type = with types; uniq int; - description = '' - Max simultaneous connections. - ''; - }; - - bind = mkOption { - default = "0.0.0.0"; - type = types.str; - description = '' - Bind over an IPv4 address instead of any. - ''; - }; - - logFile = mkOption { - default = "/var/log/freepopsd"; - example = "syslog"; - type = types.str; - description = '' - Filename of the log file or syslog to rely on the logging daemon. - ''; - }; - - suid = { - user = mkOption { - default = "nobody"; - type = types.str; - description = '' - User name under which freepopsd will be after binding the port. - ''; - }; - - group = mkOption { - default = "nogroup"; - type = types.str; - description = '' - Group under which freepopsd will be after binding the port. - ''; - }; - }; - - }; - }; - - config = mkIf cfg.enable { - systemd.services.freepopsd = { - description = "Freepopsd (webmail over POP3)"; - after = [ "network.target" ]; - wantedBy = [ "multi-user.target" ]; - script = '' - ${pkgs.freepops}/bin/freepopsd \ - -p ${toString cfg.port} \ - -t ${toString cfg.threads} \ - -b ${cfg.bind} \ - -vv -l ${cfg.logFile} \ - -s ${cfg.suid.user}.${cfg.suid.group} - ''; - }; - }; -} diff --git a/nixos/modules/services/mail/mailhog.nix b/nixos/modules/services/mail/mailhog.nix index 0f998c6d0ea..b113f4ff3de 100644 --- a/nixos/modules/services/mail/mailhog.nix +++ b/nixos/modules/services/mail/mailhog.nix @@ -4,17 +4,59 @@ with lib; let cfg = config.services.mailhog; -in { + + args = lib.concatStringsSep " " ( + [ + "-api-bind-addr :${toString cfg.apiPort}" + "-smtp-bind-addr :${toString cfg.smtpPort}" + "-ui-bind-addr :${toString cfg.uiPort}" + "-storage ${cfg.storage}" + ] ++ lib.optional (cfg.storage == "maildir") + "-maildir-path $STATE_DIRECTORY" + ++ cfg.extraArgs + ); + +in +{ ###### interface + imports = [ + (mkRemovedOptionModule [ "services" "mailhog" "user" ] "") + ]; + options = { services.mailhog = { enable = mkEnableOption "MailHog"; - user = mkOption { - type = types.str; - default = "mailhog"; - description = "User account under which mailhog runs."; + + storage = mkOption { + type = types.enum [ "maildir" "memory" ]; + default = "memory"; + description = "Store mails on disk or in memory."; + }; + + apiPort = mkOption { + type = types.port; + default = 8025; + description = "Port on which the API endpoint will listen."; + }; + + smtpPort = mkOption { + type = types.port; + default = 1025; + description = "Port on which the SMTP endpoint will listen."; + }; + + uiPort = mkOption { + type = types.port; + default = 8025; + description = "Port on which the HTTP UI will listen."; + }; + + extraArgs = mkOption { + type = types.listOf types.str; + default = []; + description = "List of additional arguments to pass to the MailHog process."; }; }; }; @@ -24,20 +66,16 @@ in { config = mkIf cfg.enable { - users.users.mailhog = { - name = cfg.user; - description = "MailHog service user"; - isSystemUser = true; - }; - systemd.services.mailhog = { - description = "MailHog service"; + description = "MailHog - Web and API based SMTP testing"; after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; serviceConfig = { - Type = "simple"; - ExecStart = "${pkgs.mailhog}/bin/MailHog"; - User = cfg.user; + Type = "exec"; + ExecStart = "${pkgs.mailhog}/bin/MailHog ${args}"; + DynamicUser = true; + Restart = "on-failure"; + StateDirectory = "mailhog"; }; }; }; diff --git a/nixos/modules/services/mail/mailman.nix b/nixos/modules/services/mail/mailman.nix index 5c61cfbebf6..831175d5625 100644 --- a/nixos/modules/services/mail/mailman.nix +++ b/nixos/modules/services/mail/mailman.nix @@ -38,7 +38,7 @@ let 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" '' + postfixMtaConfig = pkgs.writeText "mailman-postfix.cfg" '' [postfix] postmap_command: ${pkgs.postfix}/bin/postmap transport_file_type: hash @@ -81,7 +81,7 @@ in { enable = mkOption { type = types.bool; default = false; - description = "Enable Mailman on this host. Requires an active Postfix installation."; + description = "Enable Mailman on this host. Requires an active MTA on the host (e.g. Postfix)."; }; package = mkOption { @@ -92,6 +92,20 @@ in { description = "Mailman package to use"; }; + enablePostfix = mkOption { + type = types.bool; + default = true; + example = false; + description = '' + Enable Postfix integration. Requires an active Postfix installation. + + If you want to use another MTA, set this option to false and configure + settings in services.mailman.settings.mta. + + Refer to the Mailman manual for more info. + ''; + }; + siteOwner = mkOption { type = types.str; example = "postmaster@example.org"; @@ -151,7 +165,7 @@ in { baseUrl = mkOption { type = types.str; - default = "http://localhost/hyperkitty/"; + default = "http://localhost:18507/archives/"; description = '' Where can Mailman connect to Hyperkitty's internal API, preferably on localhost? @@ -182,7 +196,7 @@ in { pid_file = "/run/mailman/master.pid"; }; - mta.configuration = lib.mkDefault "${mtaConfig}"; + mta.configuration = lib.mkDefault (if cfg.enablePostfix then "${postfixMtaConfig}" else throw "When Mailman Postfix integration is disabled, set `services.mailman.settings.mta.configuration` to the path of the config file required to integrate with your MTA."); "archiver.hyperkitty" = lib.mkIf cfg.hyperkitty.enable { class = "mailman_hyperkitty.Archiver"; @@ -211,14 +225,22 @@ in { See <https://mailman.readthedocs.io/en/latest/src/mailman/docs/mta.html>. ''; }; - in [ + in (lib.optionals cfg.enablePostfix [ { assertion = postfix.enable; - message = "Mailman requires Postfix"; + message = '' + Mailman's default NixOS configuration requires Postfix to be enabled. + + If you want to use another MTA, set services.mailman.enablePostfix + to false and configure settings in services.mailman.settings.mta. + + Refer to <https://mailman.readthedocs.io/en/latest/src/mailman/docs/mta.html> + for more info. + ''; } (requirePostfixHash [ "relayDomains" ] "postfix_domains") (requirePostfixHash [ "config" "transport_maps" ] "postfix_lmtp") (requirePostfixHash [ "config" "local_recipient_maps" ] "postfix_lmtp") - ]; + ]); users.users.mailman = { description = "GNU Mailman"; @@ -241,7 +263,8 @@ in { # settings_local.json is loaded. os.environ["SECRET_KEY"] = "" - from mailman_web.settings import * + from mailman_web.settings.base import * + from mailman_web.settings.mailman import * import json @@ -275,7 +298,7 @@ in { ''; }) ]; - services.postfix = { + services.postfix = lib.mkIf cfg.enablePostfix { recipientDelimiter = "+"; # bake recipient addresses in mail envelopes via VERP config = { owner_request_special = "no"; # Mailman handles -owner addresses on its own @@ -310,6 +333,7 @@ in { 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 ]; + serviceConfig.Type = "oneshot"; script = '' mailmanDir=/var/lib/mailman mailmanWebDir=/var/lib/mailman-web @@ -345,7 +369,7 @@ in { mailman-web-setup = { description = "Prepare mailman-web files and database"; - before = [ "uwsgi.service" "mailman-uwsgi.service" ]; + before = [ "mailman-uwsgi.service" ]; requiredBy = [ "mailman-uwsgi.service" ]; restartTriggers = [ config.environment.etc."mailman3/settings.py".source ]; script = '' @@ -368,6 +392,7 @@ in { plugins = ["python3"]; home = pythonEnv; module = "mailman_web.wsgi"; + http = "127.0.0.1:18507"; }; uwsgiConfigFile = pkgs.writeText "uwsgi-mailman.json" (builtins.toJSON uwsgiConfig); in { @@ -421,7 +446,7 @@ in { inherit startAt; restartTriggers = [ config.environment.etc."mailman3/settings.py".source ]; serviceConfig = { - ExecStart = "${pythonEnv}/bin/mailman-web runjobs minutely"; + ExecStart = "${pythonEnv}/bin/mailman-web runjobs ${name}"; User = cfg.webUser; Group = "mailman"; WorkingDirectory = "/var/lib/mailman-web"; @@ -430,7 +455,7 @@ in { }; meta = { - maintainers = with lib.maintainers; [ lheckemann ]; + maintainers = with lib.maintainers; [ lheckemann qyliss ]; doc = ./mailman.xml; }; diff --git a/nixos/modules/services/mail/mailman.xml b/nixos/modules/services/mail/mailman.xml index cbe50ed0b91..27247fb064f 100644 --- a/nixos/modules/services/mail/mailman.xml +++ b/nixos/modules/services/mail/mailman.xml @@ -13,9 +13,9 @@ </para> <section xml:id="module-services-mailman-basic-usage"> - <title>Basic usage</title> + <title>Basic usage with Postfix</title> <para> - For a basic configuration, the following settings are suggested: + For a basic configuration with Postfix as the MTA, the following settings are suggested: <programlisting>{ config, ... }: { services.postfix = { enable = true; @@ -31,11 +31,11 @@ <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.mailman.webHosts">webHosts</link> = ["lists.example.org"]; + <link linkend="opt-services.mailman.siteOwner">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 ]; + <link linkend="opt-networking.firewall.allowedTCPPorts">networking.firewall.allowedTCPPorts</link> = [ 25 80 443 ]; }</programlisting> </para> <para> @@ -56,4 +56,39 @@ necessary, but outside the scope of the Mailman module. </para> </section> + <section xml:id="module-services-mailman-other-mtas"> + <title>Using with other MTAs</title> + <para> + Mailman also supports other MTA, though with a little bit more configuration. For example, to use Mailman with Exim, you can use the following settings: + <programlisting>{ config, ... }: { + services = { + mailman = { + enable = true; + siteOwner = "mailman@example.org"; + <link linkend="opt-services.mailman.enablePostfix">enablePostfix</link> = false; + settings.mta = { + incoming = "mailman.mta.exim4.LMTP"; + outgoing = "mailman.mta.deliver.deliver"; + lmtp_host = "localhost"; + lmtp_port = "8024"; + smtp_host = "localhost"; + smtp_port = "25"; + configuration = "python:mailman.config.exim4"; + }; + }; + exim = { + enable = true; + # You can configure Exim in a separate file to reduce configuration.nix clutter + config = builtins.readFile ./exim.conf; + }; + }; +}</programlisting> + </para> + <para> + The exim config needs some special additions to work with Mailman. Currently + NixOS can't manage Exim config with such granularity. Please refer to + <link xlink:href="https://mailman.readthedocs.io/en/latest/src/mailman/docs/mta.html">Mailman documentation</link> + for more info on configuring Mailman for working with Exim. + </para> + </section> </chapter> diff --git a/nixos/modules/services/mail/mlmmj.nix b/nixos/modules/services/mail/mlmmj.nix index d58d93c4214..fd74f2dc5f0 100644 --- a/nixos/modules/services/mail/mlmmj.nix +++ b/nixos/modules/services/mail/mlmmj.nix @@ -16,7 +16,14 @@ let alias = domain: list: "${list}: \"|${pkgs.mlmmj}/bin/mlmmj-receive -L ${listDir domain list}/\""; subjectPrefix = list: "[${list}]"; listAddress = domain: list: "${list}@${domain}"; - customHeaders = domain: list: [ "List-Id: ${list}" "Reply-To: ${list}@${domain}" ]; + customHeaders = domain: list: [ + "List-Id: ${list}" + "Reply-To: ${list}@${domain}" + "List-Post: <mailto:${list}@${domain}>" + "List-Help: <mailto:${list}+help@${domain}>" + "List-Subscribe: <mailto:${list}+subscribe@${domain}>" + "List-Unsubscribe: <mailto:${list}+unsubscribe@${domain}>" + ]; footer = domain: list: "To unsubscribe send a mail to ${list}+unsubscribe@${domain}"; createList = d: l: let ctlDir = listCtl d l; in @@ -110,17 +117,29 @@ in services.postfix = { enable = true; recipientDelimiter= "+"; - extraMasterConf = '' - mlmmj unix - n n - - pipe flags=ORhu user=mlmmj argv=${pkgs.mlmmj}/bin/mlmmj-receive -F -L ${spoolDir}/$nexthop - ''; + masterConfig.mlmmj = { + type = "unix"; + private = true; + privileged = true; + chroot = false; + wakeup = 0; + command = "pipe"; + args = [ + "flags=ORhu" + "user=mlmmj" + "argv=${pkgs.mlmmj}/bin/mlmmj-receive" + "-F" + "-L" + "${spoolDir}/$nexthop" + ]; + }; extraAliases = concatMapLines (alias cfg.listDomain) cfg.mailLists; - extraConfig = '' - transport_maps = hash:${stateDir}/transports - virtual_alias_maps = hash:${stateDir}/virtuals - propagate_unmatched_extensions = virtual - ''; + extraConfig = "propagate_unmatched_extensions = virtual"; + + virtual = concatMapLines (virtual cfg.listDomain) cfg.mailLists; + transport = concatMapLines (transport cfg.listDomain) cfg.mailLists; }; environment.systemPackages = [ pkgs.mlmmj ]; @@ -129,10 +148,8 @@ in ${pkgs.coreutils}/bin/mkdir -p ${stateDir} ${spoolDir}/${cfg.listDomain} ${pkgs.coreutils}/bin/chown -R ${cfg.user}:${cfg.group} ${spoolDir} ${concatMapLines (createList cfg.listDomain) cfg.mailLists} - echo "${concatMapLines (virtual cfg.listDomain) cfg.mailLists}" > ${stateDir}/virtuals - echo "${concatMapLines (transport cfg.listDomain) cfg.mailLists}" > ${stateDir}/transports - ${pkgs.postfix}/bin/postmap ${stateDir}/virtuals - ${pkgs.postfix}/bin/postmap ${stateDir}/transports + ${pkgs.postfix}/bin/postmap /etc/postfix/virtual + ${pkgs.postfix}/bin/postmap /etc/postfix/transport ''; systemd.services.mlmmj-maintd = { diff --git a/nixos/modules/services/mail/nullmailer.nix b/nixos/modules/services/mail/nullmailer.nix index fe3f8ef9b39..09874ca0ed7 100644 --- a/nixos/modules/services/mail/nullmailer.nix +++ b/nixos/modules/services/mail/nullmailer.nix @@ -204,6 +204,7 @@ with lib; users.${cfg.user} = { description = "Nullmailer relay-only mta user"; group = cfg.group; + isSystemUser = true; }; groups.${cfg.group} = { }; diff --git a/nixos/modules/services/mail/opendkim.nix b/nixos/modules/services/mail/opendkim.nix index eb6a426684d..beff57613af 100644 --- a/nixos/modules/services/mail/opendkim.nix +++ b/nixos/modules/services/mail/opendkim.nix @@ -129,6 +129,36 @@ in { User = cfg.user; Group = cfg.group; RuntimeDirectory = optional (cfg.socket == defaultSock) "opendkim"; + StateDirectory = "opendkim"; + StateDirectoryMode = "0700"; + ReadWritePaths = [ cfg.keyPath ]; + + AmbientCapabilities = []; + CapabilityBoundingSet = ""; + DevicePolicy = "closed"; + LockPersonality = true; + MemoryDenyWriteExecute = true; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateMounts = true; + PrivateTmp = true; + PrivateUsers = true; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectSystem = "strict"; + RemoveIPC = true; + RestrictAddressFamilies = [ "AF_INET" "AF_INET6 AF_UNIX" ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + SystemCallFilter = [ "@system-service" "~@privileged @resources" ]; + UMask = "0077"; }; }; diff --git a/nixos/modules/services/mail/postfix.nix b/nixos/modules/services/mail/postfix.nix index fd4d16cdc37..9b0a5bba2fe 100644 --- a/nixos/modules/services/mail/postfix.nix +++ b/nixos/modules/services/mail/postfix.nix @@ -11,6 +11,7 @@ let haveAliases = cfg.postmasterAlias != "" || cfg.rootAlias != "" || cfg.extraAliases != ""; + haveCanonical = cfg.canonical != ""; haveTransport = cfg.transport != ""; haveVirtual = cfg.virtual != ""; haveLocalRecipients = cfg.localRecipients != null; @@ -25,8 +26,6 @@ let clientRestrictions = concatStringsSep ", " (clientAccess ++ dnsBl); - smtpTlsSecurityLevel = if cfg.useDane then "dane" else "may"; - mainCf = let escape = replaceStrings ["$"] ["$$"]; mkList = items: "\n " + concatStringsSep ",\n " items; @@ -52,7 +51,7 @@ let }; type = mkOption { - type = types.enum [ "inet" "unix" "fifo" "pass" ]; + type = types.enum [ "inet" "unix" "unix-dgram" "fifo" "pass" ]; default = "unix"; example = "inet"; description = "The type of the service"; @@ -195,7 +194,7 @@ let # We need to handle the last column specially here, because it's # open-ended (command + args). lines = [ labels labelDefaults ] ++ (map (l: init l ++ [""]) masterCf); - in fold foldLine (genList (const 0) (length labels)) lines; + in foldr foldLine (genList (const 0) (length labels)) lines; # Pad a string with spaces from the right (opposite of fixedWidthString). pad = width: str: let @@ -204,7 +203,7 @@ let in str + optionalString (padWidth > 0) padding; # It's + 2 here, because that's the amount of spacing between columns. - fullWidth = fold (width: acc: acc + width + 2) 0 maxWidths; + fullWidth = foldr (width: acc: acc + width + 2) 0 maxWidths; formatLine = line: concatStringsSep " " (zipListsWith pad maxWidths line); @@ -246,6 +245,7 @@ let ; aliasesFile = pkgs.writeText "postfix-aliases" aliases; + canonicalFile = pkgs.writeText "postfix-canonical" cfg.canonical; virtualFile = pkgs.writeText "postfix-virtual" cfg.virtual; localRecipientMapFile = pkgs.writeText "postfix-local-recipient-map" (concatMapStrings (x: x + " ACCEPT\n") cfg.localRecipients); checkClientAccessFile = pkgs.writeText "postfix-check-client-access" cfg.dnsBlacklistOverrides; @@ -510,14 +510,6 @@ in ''; }; - useDane = mkOption { - type = types.bool; - default = false; - description = '' - Sets smtp_tls_security_level to "dane" rather than "may". See postconf(5) for details. - ''; - }; - sslCert = mkOption { type = types.str; default = ""; @@ -539,6 +531,15 @@ in "; }; + canonical = mkOption { + type = types.lines; + default = ""; + description = '' + Entries for the <citerefentry><refentrytitle>canonical</refentrytitle> + <manvolnum>5</manvolnum></citerefentry> table. + ''; + }; + virtual = mkOption { type = types.lines; default = ""; @@ -570,6 +571,7 @@ in transport = mkOption { default = ""; + type = types.lines; description = " Entries for the transport map, cf. man-page transport(8). "; @@ -583,6 +585,7 @@ in dnsBlacklistOverrides = mkOption { default = ""; + type = types.lines; description = "contents of check_client_access for overriding dnsBlacklists"; }; @@ -770,7 +773,7 @@ in }; services.postfix.config = (mapAttrs (_: v: mkDefault v) { - compatibility_level = "9999"; + compatibility_level = pkgs.postfix.version; mail_owner = cfg.user; default_privs = "nobody"; @@ -819,13 +822,13 @@ in // optionalAttrs cfg.enableHeaderChecks { header_checks = [ "regexp:/etc/postfix/header_checks" ]; } // optionalAttrs (cfg.tlsTrustedAuthorities != "") { smtp_tls_CAfile = cfg.tlsTrustedAuthorities; - smtp_tls_security_level = smtpTlsSecurityLevel; + smtp_tls_security_level = mkDefault "may"; } // optionalAttrs (cfg.sslCert != "") { smtp_tls_cert_file = cfg.sslCert; smtp_tls_key_file = cfg.sslKey; - smtp_tls_security_level = smtpTlsSecurityLevel; + smtp_tls_security_level = mkDefault "may"; smtpd_tls_cert_file = cfg.sslCert; smtpd_tls_key_file = cfg.sslKey; @@ -834,12 +837,6 @@ in }; services.postfix.masterConfig = { - smtp_inet = { - name = "smtp"; - type = "inet"; - private = false; - command = "smtpd"; - }; pickup = { private = false; wakeup = 60; @@ -921,6 +918,12 @@ in in concatLists (mapAttrsToList mkKeyVal cfg.submissionOptions); }; } // optionalAttrs cfg.enableSmtp { + smtp_inet = { + name = "smtp"; + type = "inet"; + private = false; + command = "smtpd"; + }; smtp = {}; relay = { command = "smtp"; @@ -949,6 +952,9 @@ in (mkIf haveAliases { services.postfix.aliasFiles.aliases = aliasesFile; }) + (mkIf haveCanonical { + services.postfix.mapFiles.canonical = canonicalFile; + }) (mkIf haveTransport { services.postfix.mapFiles.transport = transportFile; }) @@ -969,5 +975,9 @@ in imports = [ (mkRemovedOptionModule [ "services" "postfix" "sslCACert" ] "services.postfix.sslCACert was replaced by services.postfix.tlsTrustedAuthorities. In case you intend that your server should validate requested client certificates use services.postfix.extraConfig.") + + (mkChangedOptionModule [ "services" "postfix" "useDane" ] + [ "services" "postfix" "config" "smtp_tls_security_level" ] + (config: mkIf config.services.postfix.useDane "dane")) ]; } diff --git a/nixos/modules/services/mail/postgrey.nix b/nixos/modules/services/mail/postgrey.nix index 709f6b21aa0..7c206e3725e 100644 --- a/nixos/modules/services/mail/postgrey.nix +++ b/nixos/modules/services/mail/postgrey.nix @@ -163,7 +163,7 @@ in { systemd.services.postgrey = let bind-flag = if cfg.socket ? path then - ''--unix=${cfg.socket.path} --socketmode=${cfg.socket.mode}'' + "--unix=${cfg.socket.path} --socketmode=${cfg.socket.mode}" else ''--inet=${optionalString (cfg.socket.addr != null) (cfg.socket.addr + ":")}${toString cfg.socket.port}''; in { diff --git a/nixos/modules/services/mail/roundcube.nix b/nixos/modules/services/mail/roundcube.nix index a0bbab64985..f9b63000473 100644 --- a/nixos/modules/services/mail/roundcube.nix +++ b/nixos/modules/services/mail/roundcube.nix @@ -7,7 +7,7 @@ let fpm = config.services.phpfpm.pools.roundcube; localDB = cfg.database.host == "localhost"; user = cfg.database.username; - phpWithPspell = pkgs.php.withExtensions ({ enabled, all }: [ all.pspell ] ++ enabled); + phpWithPspell = pkgs.php74.withExtensions ({ enabled, all }: [ all.pspell ] ++ enabled); in { options.services.roundcube = { @@ -204,6 +204,11 @@ in }; systemd.services.phpfpm-roundcube.after = [ "roundcube-setup.service" ]; + # Restart on config changes. + systemd.services.phpfpm-roundcube.restartTriggers = [ + config.environment.etc."roundcube/config.inc.php".source + ]; + systemd.services.roundcube-setup = mkMerge [ (mkIf (cfg.database.host == "localhost") { requires = [ "postgresql.service" ]; diff --git a/nixos/modules/services/mail/rspamd.nix b/nixos/modules/services/mail/rspamd.nix index aacdbe2aeed..473ddd52357 100644 --- a/nixos/modules/services/mail/rspamd.nix +++ b/nixos/modules/services/mail/rspamd.nix @@ -153,7 +153,7 @@ let ${concatStringsSep "\n" (mapAttrsToList (name: value: let includeName = if name == "rspamd_proxy" then "proxy" else name; - tryOverride = if value.extraConfig == "" then "true" else "false"; + tryOverride = boolToString (value.extraConfig == ""); in '' worker "${value.type}" { type = "${value.type}"; @@ -371,6 +371,9 @@ in }; services.postfix.config = mkIf cfg.postfix.enable cfg.postfix.config; + systemd.services.postfix.serviceConfig.SupplementaryGroups = + mkIf cfg.postfix.enable [ postfixCfg.group ]; + # Allow users to run 'rspamc' and 'rspamadm'. environment.systemPackages = [ pkgs.rspamd ]; @@ -394,21 +397,50 @@ in restartTriggers = [ rspamdDir ]; serviceConfig = { - ExecStart = "${pkgs.rspamd}/bin/rspamd ${optionalString cfg.debug "-d"} --user=${cfg.user} --group=${cfg.group} --pid=/run/rspamd.pid -c /etc/rspamd/rspamd.conf -f"; + ExecStart = "${pkgs.rspamd}/bin/rspamd ${optionalString cfg.debug "-d"} -c /etc/rspamd/rspamd.conf -f"; Restart = "always"; + + User = "${cfg.user}"; + Group = "${cfg.group}"; + SupplementaryGroups = mkIf cfg.postfix.enable [ postfixCfg.group ]; + RuntimeDirectory = "rspamd"; + RuntimeDirectoryMode = "0755"; + StateDirectory = "rspamd"; + StateDirectoryMode = "0700"; + + AmbientCapabilities = []; + CapabilityBoundingSet = ""; + DevicePolicy = "closed"; + LockPersonality = true; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateMounts = true; PrivateTmp = true; + # we need to chown socket to rspamd-milter + PrivateUsers = !cfg.postfix.enable; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectSystem = "strict"; + RemoveIPC = true; + RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + SystemCallFilter = "@system-service"; + UMask = "0077"; }; - - preStart = '' - ${pkgs.coreutils}/bin/mkdir -p /var/lib/rspamd - ${pkgs.coreutils}/bin/chown ${cfg.user}:${cfg.group} /var/lib/rspamd - ''; }; }; imports = [ (mkRemovedOptionModule [ "services" "rspamd" "socketActivation" ] - "Socket activation never worked correctly and could at this time not be fixed and so was removed") + "Socket activation never worked correctly and could at this time not be fixed and so was removed") (mkRenamedOptionModule [ "services" "rspamd" "bindSocket" ] [ "services" "rspamd" "workers" "normal" "bindSockets" ]) (mkRenamedOptionModule [ "services" "rspamd" "bindUISocket" ] [ "services" "rspamd" "workers" "controller" "bindSockets" ]) (mkRemovedOptionModule [ "services" "rmilter" ] "Use services.rspamd.* instead to set up milter service") diff --git a/nixos/modules/services/mail/spamassassin.nix b/nixos/modules/services/mail/spamassassin.nix index 4e642542ec6..ac878222b26 100644 --- a/nixos/modules/services/mail/spamassassin.nix +++ b/nixos/modules/services/mail/spamassassin.nix @@ -126,19 +126,36 @@ in }; systemd.services.sa-update = { + # Needs to be able to contact the update server. + wants = [ "network-online.target" ]; + after = [ "network-online.target" ]; + + serviceConfig = { + Type = "oneshot"; + User = "spamd"; + Group = "spamd"; + StateDirectory = "spamassassin"; + ExecStartPost = "+${pkgs.systemd}/bin/systemctl -q --no-block try-reload-or-restart spamd.service"; + }; + script = '' set +e - ${pkgs.su}/bin/su -s "${pkgs.bash}/bin/bash" -c "${pkgs.spamassassin}/bin/sa-update --gpghomedir=/var/lib/spamassassin/sa-update-keys/" spamd - - v=$? + ${pkgs.spamassassin}/bin/sa-update --verbose --gpghomedir=/var/lib/spamassassin/sa-update-keys/ + rc=$? set -e - if [ $v -gt 1 ]; then - echo "sa-update execution error" - exit $v + + if [[ $rc -gt 1 ]]; then + # sa-update failed. + exit $rc fi - if [ $v -eq 0 ]; then - systemctl reload spamd.service + + if [[ $rc -eq 1 ]]; then + # No update was available, exit successfully. + exit 0 fi + + # An update was available and installed. Compile the rules. + ${pkgs.spamassassin}/bin/sa-compile ''; }; @@ -153,32 +170,22 @@ in }; systemd.services.spamd = { - description = "Spam Assassin Server"; + description = "SpamAssassin Server"; wantedBy = [ "multi-user.target" ]; - after = [ "network.target" ]; + wants = [ "sa-update.service" ]; + after = [ + "network.target" + "sa-update.service" + ]; serviceConfig = { - ExecStart = "${pkgs.spamassassin}/bin/spamd ${optionalString cfg.debug "-D"} --username=spamd --groupname=spamd --virtual-config-dir=/var/lib/spamassassin/user-%u --allow-tell --pidfile=/run/spamd.pid"; - ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + User = "spamd"; + Group = "spamd"; + ExecStart = "+${pkgs.spamassassin}/bin/spamd ${optionalString cfg.debug "-D"} --username=spamd --groupname=spamd --virtual-config-dir=%S/spamassassin/user-%u --allow-tell --pidfile=/run/spamd.pid"; + ExecReload = "+${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + StateDirectory = "spamassassin"; }; - - # 0 and 1 no error, exitcode > 1 means error: - # https://spamassassin.apache.org/full/3.1.x/doc/sa-update.html#exit_codes - preStart = '' - echo "Recreating '/var/lib/spamasassin' with creating '3.004001' (or similar) and 'sa-update-keys'" - mkdir -p /var/lib/spamassassin - chown spamd:spamd /var/lib/spamassassin -R - set +e - ${pkgs.su}/bin/su -s "${pkgs.bash}/bin/bash" -c "${pkgs.spamassassin}/bin/sa-update --gpghomedir=/var/lib/spamassassin/sa-update-keys/" spamd - v=$? - set -e - if [ $v -gt 1 ]; then - echo "sa-update execution error" - exit $v - fi - chown spamd:spamd /var/lib/spamassassin -R - ''; }; }; } diff --git a/nixos/modules/services/mail/sympa.nix b/nixos/modules/services/mail/sympa.nix index 0cad09927b2..491b6dba9aa 100644 --- a/nixos/modules/services/mail/sympa.nix +++ b/nixos/modules/services/mail/sympa.nix @@ -513,10 +513,6 @@ in 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}/lib/sympa/cgi/wwsympa.fcgi; ''; }) // { "/static-sympa/".alias = "${dataDir}/static_content/"; |