about summary refs log tree commit diff
path: root/nixpkgs/nixos/modules/services/mail/dovecot.nix
diff options
context:
space:
mode:
authorAlyssa Ross <hi@alyssa.is>2019-01-07 02:18:36 +0000
committerAlyssa Ross <hi@alyssa.is>2019-01-07 02:18:47 +0000
commit36f56d99fa0a0765c9f1de4a5f17a9b05830c3f2 (patch)
treeb3faaf573407b32aa645237a4d16b82778a39a92 /nixpkgs/nixos/modules/services/mail/dovecot.nix
parent4e31070265257dc67d120c27e0f75c2344fdfa9a (diff)
parentabf060725d7614bd3b9f96764262dfbc2f9c2199 (diff)
downloadnixlib-36f56d99fa0a0765c9f1de4a5f17a9b05830c3f2.tar
nixlib-36f56d99fa0a0765c9f1de4a5f17a9b05830c3f2.tar.gz
nixlib-36f56d99fa0a0765c9f1de4a5f17a9b05830c3f2.tar.bz2
nixlib-36f56d99fa0a0765c9f1de4a5f17a9b05830c3f2.tar.lz
nixlib-36f56d99fa0a0765c9f1de4a5f17a9b05830c3f2.tar.xz
nixlib-36f56d99fa0a0765c9f1de4a5f17a9b05830c3f2.tar.zst
nixlib-36f56d99fa0a0765c9f1de4a5f17a9b05830c3f2.zip
Add 'nixpkgs/' from commit 'abf060725d7614bd3b9f96764262dfbc2f9c2199'
git-subtree-dir: nixpkgs
git-subtree-mainline: 4e31070265257dc67d120c27e0f75c2344fdfa9a
git-subtree-split: abf060725d7614bd3b9f96764262dfbc2f9c2199
Diffstat (limited to 'nixpkgs/nixos/modules/services/mail/dovecot.nix')
-rw-r--r--nixpkgs/nixos/modules/services/mail/dovecot.nix401
1 files changed, 401 insertions, 0 deletions
diff --git a/nixpkgs/nixos/modules/services/mail/dovecot.nix b/nixpkgs/nixos/modules/services/mail/dovecot.nix
new file mode 100644
index 000000000000..30ad7d82fb80
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/mail/dovecot.nix
@@ -0,0 +1,401 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.dovecot2;
+  dovecotPkg = pkgs.dovecot;
+
+  baseDir = "/run/dovecot2";
+  stateDir = "/var/lib/dovecot";
+
+  dovecotConf = concatStrings [
+    ''
+      base_dir = ${baseDir}
+      protocols = ${concatStringsSep " " cfg.protocols}
+      sendmail_path = /run/wrappers/bin/sendmail
+    ''
+
+    (if isNull cfg.sslServerCert then ''
+      ssl = no
+      disable_plaintext_auth = no
+    '' else ''
+      ssl_cert = <${cfg.sslServerCert}
+      ssl_key = <${cfg.sslServerKey}
+      ${optionalString (!(isNull cfg.sslCACert)) ("ssl_ca = <" + cfg.sslCACert)}
+      ssl_dh = <${config.security.dhparams.params.dovecot2.path}
+      disable_plaintext_auth = yes
+    '')
+
+    ''
+      default_internal_user = ${cfg.user}
+      default_internal_group = ${cfg.group}
+      ${optionalString (cfg.mailUser != null) "mail_uid = ${cfg.mailUser}"}
+      ${optionalString (cfg.mailGroup != null) "mail_gid = ${cfg.mailGroup}"}
+
+      mail_location = ${cfg.mailLocation}
+
+      maildir_copy_with_hardlinks = yes
+      pop3_uidl_format = %08Xv%08Xu
+
+      auth_mechanisms = plain login
+
+      service auth {
+        user = root
+      }
+    ''
+
+    (optionalString cfg.enablePAM ''
+      userdb {
+        driver = passwd
+      }
+
+      passdb {
+        driver = pam
+        args = ${optionalString cfg.showPAMFailure "failure_show_msg=yes"} dovecot2
+      }
+    '')
+
+    (optionalString (cfg.sieveScripts != {}) ''
+      plugin {
+        ${concatStringsSep "\n" (mapAttrsToList (to: from: "sieve_${to} = ${stateDir}/sieve/${to}") cfg.sieveScripts)}
+      }
+    '')
+
+    (optionalString (cfg.mailboxes != []) ''
+      protocol imap {
+        namespace inbox {
+          inbox=yes
+          ${concatStringsSep "\n" (map mailboxConfig cfg.mailboxes)}
+        }
+      }
+    '')
+
+    (optionalString cfg.enableQuota ''
+      mail_plugins = $mail_plugins quota
+      service quota-status {
+        executable = ${dovecotPkg}/libexec/dovecot/quota-status -p postfix
+        inet_listener {
+          port = ${cfg.quotaPort}
+        }
+        client_limit = 1
+      }
+
+      protocol imap {
+        mail_plugins = $mail_plugins imap_quota
+      }
+
+      plugin {
+        quota_rule = *:storage=${cfg.quotaGlobalPerUser} 
+        quota = maildir:User quota # per virtual mail user quota # BUG/FIXME broken, we couldn't get this working
+        quota_status_success = DUNNO
+        quota_status_nouser = DUNNO
+        quota_status_overquota = "552 5.2.2 Mailbox is full"
+        quota_grace = 10%%
+      }
+    '')
+
+    cfg.extraConfig
+  ];
+
+  modulesDir = pkgs.symlinkJoin {
+    name = "dovecot-modules";
+    paths = map (pkg: "${pkg}/lib/dovecot") ([ dovecotPkg ] ++ map (module: module.override { dovecot = dovecotPkg; }) cfg.modules);
+  };
+
+  mailboxConfig = mailbox: ''
+    mailbox "${mailbox.name}" {
+      auto = ${toString mailbox.auto}
+  '' + optionalString (mailbox.specialUse != null) ''
+      special_use = \${toString mailbox.specialUse}
+  '' + "}";
+
+  mailboxes = { ... }: {
+    options = {
+      name = mkOption {
+        type = types.strMatching ''[^"]+'';
+        example = "Spam";
+        description = "The name of the mailbox.";
+      };
+      auto = mkOption {
+        type = types.enum [ "no" "create" "subscribe" ];
+        default = "no";
+        example = "subscribe";
+        description = "Whether to automatically create or create and subscribe to the mailbox or not.";
+      };
+      specialUse = mkOption {
+        type = types.nullOr (types.enum [ "All" "Archive" "Drafts" "Flagged" "Junk" "Sent" "Trash" ]);
+        default = null;
+        example = "Junk";
+        description = "Null if no special use flag is set. Other than that every use flag mentioned in the RFC is valid.";
+      };
+    };
+  };
+in
+{
+
+  options.services.dovecot2 = {
+    enable = mkEnableOption "Dovecot 2.x POP3/IMAP server";
+
+    enablePop3 = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Start the POP3 listener (when Dovecot is enabled).";
+    };
+
+    enableImap = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Start the IMAP listener (when Dovecot is enabled).";
+    };
+
+    enableLmtp = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Start the LMTP listener (when Dovecot is enabled).";
+    };
+
+    protocols = mkOption {
+      type = types.listOf types.str;
+      default = [ ];
+      description = "Additional listeners to start when Dovecot is enabled.";
+    };
+
+    user = mkOption {
+      type = types.str;
+      default = "dovecot2";
+      description = "Dovecot user name.";
+    };
+
+    group = mkOption {
+      type = types.str;
+      default = "dovecot2";
+      description = "Dovecot group name.";
+    };
+
+    extraConfig = mkOption {
+      type = types.lines;
+      default = "";
+      example = "mail_debug = yes";
+      description = "Additional entries to put verbatim into Dovecot's config file.";
+    };
+
+    configFile = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = "Config file used for the whole dovecot configuration.";
+      apply = v: if v != null then v else pkgs.writeText "dovecot.conf" dovecotConf;
+    };
+
+    mailLocation = mkOption {
+      type = types.str;
+      default = "maildir:/var/spool/mail/%u"; /* Same as inbox, as postfix */
+      example = "maildir:~/mail:INBOX=/var/spool/mail/%u";
+      description = ''
+        Location that dovecot will use for mail folders. Dovecot mail_location option.
+      '';
+    };
+
+    mailUser = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = "Default user to store mail for virtual users.";
+    };
+
+    mailGroup = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = "Default group to store mail for virtual users.";
+    };
+
+    createMailUser = mkOption {
+      type = types.bool;
+      default = true;
+      description = ''Whether to automatically create the user
+        given in <option>services.dovecot.user</option> and the group
+        given in <option>services.dovecot.group</option>.'';
+    };
+
+    modules = mkOption {
+      type = types.listOf types.package;
+      default = [];
+      example = literalExample "[ pkgs.dovecot_pigeonhole ]";
+      description = ''
+        Symlinks the contents of lib/dovecot of every given package into
+        /etc/dovecot/modules. This will make the given modules available
+        if a dovecot package with the module_dir patch applied is being used.
+      '';
+    };
+
+    sslCACert = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = "Path to the server's CA certificate key.";
+    };
+
+    sslServerCert = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = "Path to the server's public key.";
+    };
+
+    sslServerKey = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = "Path to the server's private key.";
+    };
+
+    enablePAM = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Whether to create a own Dovecot PAM service and configure PAM user logins.";
+    };
+
+    sieveScripts = mkOption {
+      type = types.attrsOf types.path;
+      default = {};
+      description = "Sieve scripts to be executed. Key is a sequence, e.g. 'before2', 'after' etc.";
+    };
+
+    showPAMFailure = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Show the PAM failure message on authentication error (useful for OTPW).";
+    };
+
+    mailboxes = mkOption {
+      type = types.listOf (types.submodule mailboxes);
+      default = [];
+      example = [ { name = "Spam"; specialUse = "Junk"; auto = "create"; } ];
+      description = "Configure mailboxes and auto create or subscribe them.";
+    };
+
+    enableQuota = mkOption {
+      type = types.bool;
+      default = false;
+      example = true;
+      description = "Whether to enable the dovecot quota service.";
+    };
+
+    quotaPort = mkOption {
+      type = types.str;
+      default = "12340";
+      description = ''
+        The Port the dovecot quota service binds to.
+        If using postfix, add check_policy_service inet:localhost:12340 to your smtpd_recipient_restrictions in your postfix config.
+      '';
+    };
+    quotaGlobalPerUser = mkOption {
+      type = types.str;
+      default = "100G";
+      example = "10G";
+      description = "Quota limit for the user in bytes. Supports suffixes b, k, M, G, T and %.";
+    };
+
+  };
+
+
+  config = mkIf cfg.enable {
+    security.pam.services.dovecot2 = mkIf cfg.enablePAM {};
+
+    security.dhparams = mkIf (! isNull cfg.sslServerCert) {
+      enable = true;
+      params.dovecot2 = {};
+    };
+   services.dovecot2.protocols =
+     optional cfg.enableImap "imap"
+     ++ optional cfg.enablePop3 "pop3"
+     ++ optional cfg.enableLmtp "lmtp";
+
+    users.users = [
+      { name = "dovenull";
+        uid = config.ids.uids.dovenull2;
+        description = "Dovecot user for untrusted logins";
+        group = "dovenull";
+      }
+    ] ++ optional (cfg.user == "dovecot2")
+         { name = "dovecot2";
+           uid = config.ids.uids.dovecot2;
+           description = "Dovecot user";
+           group = cfg.group;
+         }
+      ++ optional (cfg.createMailUser && cfg.mailUser != null)
+         ({ name = cfg.mailUser;
+            description = "Virtual Mail User";
+         } // optionalAttrs (cfg.mailGroup != null) {
+           group = cfg.mailGroup;
+         });
+
+    users.groups = optional (cfg.group == "dovecot2")
+      { name = "dovecot2";
+        gid = config.ids.gids.dovecot2;
+      }
+    ++ optional (cfg.createMailUser && cfg.mailGroup != null)
+      { name = cfg.mailGroup;
+      }
+    ++ singleton
+      { name = "dovenull";
+        gid = config.ids.gids.dovenull2;
+      };
+
+    environment.etc."dovecot/modules".source = modulesDir;
+    environment.etc."dovecot/dovecot.conf".source = cfg.configFile;
+
+    systemd.services.dovecot2 = {
+      description = "Dovecot IMAP/POP3 server";
+
+      after = [ "keys.target" "network.target" ];
+      wants = [ "keys.target" ];
+      wantedBy = [ "multi-user.target" ];
+      restartTriggers = [ cfg.configFile ];
+
+      serviceConfig = {
+        ExecStart = "${dovecotPkg}/sbin/dovecot -F";
+        ExecReload = "${dovecotPkg}/sbin/doveadm reload";
+        Restart = "on-failure";
+        RestartSec = "1s";
+        StartLimitInterval = "1min";
+        RuntimeDirectory = [ "dovecot2" ];
+      };
+
+      # When copying sieve scripts preserve the original time stamp
+      # (should be 0) so that the compiled sieve script is newer than
+      # the source file and Dovecot won't try to compile it.
+      preStart = ''
+        rm -rf ${stateDir}/sieve
+      '' + optionalString (cfg.sieveScripts != {}) ''
+        mkdir -p ${stateDir}/sieve
+        ${concatStringsSep "\n" (mapAttrsToList (to: from: ''
+          if [ -d '${from}' ]; then
+            mkdir '${stateDir}/sieve/${to}'
+            cp -p "${from}/"*.sieve '${stateDir}/sieve/${to}'
+          else
+            cp -p '${from}' '${stateDir}/sieve/${to}'
+          fi
+          ${pkgs.dovecot_pigeonhole}/bin/sievec '${stateDir}/sieve/${to}'
+        '') cfg.sieveScripts)}
+        chown -R '${cfg.mailUser}:${cfg.mailGroup}' '${stateDir}/sieve'
+      '';
+    };
+
+    environment.systemPackages = [ dovecotPkg ];
+
+    assertions = [
+      { assertion = intersectLists cfg.protocols [ "pop3" "imap" ] != [];
+        message = "dovecot needs at least one of the IMAP or POP3 listeners enabled";
+      }
+      { assertion = isNull cfg.sslServerCert == isNull cfg.sslServerKey
+          && (!(isNull cfg.sslCACert) -> !(isNull cfg.sslServerCert || isNull cfg.sslServerKey));
+        message = "dovecot needs both sslServerCert and sslServerKey defined for working crypto";
+      }
+      { assertion = cfg.showPAMFailure -> cfg.enablePAM;
+        message = "dovecot is configured with showPAMFailure while enablePAM is disabled";
+      }
+      { assertion = (cfg.sieveScripts != {}) -> ((cfg.mailUser != null) && (cfg.mailGroup != null));
+        message = "dovecot requires mailUser and mailGroup to be set when sieveScripts is set";
+      }
+    ];
+
+  };
+
+}