at 17.09-beta 8.4 kB view raw
1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 cfg = config.services.dovecot2; 7 dovecotPkg = pkgs.dovecot; 8 9 baseDir = "/run/dovecot2"; 10 stateDir = "/var/lib/dovecot"; 11 12 dovecotConf = concatStrings [ 13 '' 14 base_dir = ${baseDir} 15 protocols = ${concatStringsSep " " cfg.protocols} 16 sendmail_path = /run/wrappers/bin/sendmail 17 '' 18 19 (if isNull cfg.sslServerCert then '' 20 ssl = no 21 disable_plaintext_auth = no 22 '' else '' 23 ssl_cert = <${cfg.sslServerCert} 24 ssl_key = <${cfg.sslServerKey} 25 ${optionalString (!(isNull cfg.sslCACert)) ("ssl_ca = <" + cfg.sslCACert)} 26 disable_plaintext_auth = yes 27 '') 28 29 '' 30 default_internal_user = ${cfg.user} 31 ${optionalString (cfg.mailUser != null) "mail_uid = ${cfg.mailUser}"} 32 ${optionalString (cfg.mailGroup != null) "mail_gid = ${cfg.mailGroup}"} 33 34 mail_location = ${cfg.mailLocation} 35 36 maildir_copy_with_hardlinks = yes 37 pop3_uidl_format = %08Xv%08Xu 38 39 auth_mechanisms = plain login 40 41 service auth { 42 user = root 43 } 44 '' 45 46 (optionalString cfg.enablePAM '' 47 userdb { 48 driver = passwd 49 } 50 51 passdb { 52 driver = pam 53 args = ${optionalString cfg.showPAMFailure "failure_show_msg=yes"} dovecot2 54 } 55 '') 56 57 (optionalString (cfg.sieveScripts != {}) '' 58 plugin { 59 ${concatStringsSep "\n" (mapAttrsToList (to: from: "sieve_${to} = ${stateDir}/sieve/${to}") cfg.sieveScripts)} 60 } 61 '') 62 63 cfg.extraConfig 64 ]; 65 66 modulesDir = pkgs.symlinkJoin { 67 name = "dovecot-modules"; 68 paths = map (pkg: "${pkg}/lib/dovecot") ([ dovecotPkg ] ++ map (module: module.override { dovecot = dovecotPkg; }) cfg.modules); 69 }; 70 71in 72{ 73 74 options.services.dovecot2 = { 75 enable = mkEnableOption "Dovecot 2.x POP3/IMAP server"; 76 77 enablePop3 = mkOption { 78 type = types.bool; 79 default = true; 80 description = "Start the POP3 listener (when Dovecot is enabled)."; 81 }; 82 83 enableImap = mkOption { 84 type = types.bool; 85 default = true; 86 description = "Start the IMAP listener (when Dovecot is enabled)."; 87 }; 88 89 enableLmtp = mkOption { 90 type = types.bool; 91 default = false; 92 description = "Start the LMTP listener (when Dovecot is enabled)."; 93 }; 94 95 protocols = mkOption { 96 type = types.listOf types.str; 97 default = [ ]; 98 description = "Additional listeners to start when Dovecot is enabled."; 99 }; 100 101 user = mkOption { 102 type = types.str; 103 default = "dovecot2"; 104 description = "Dovecot user name."; 105 }; 106 107 group = mkOption { 108 type = types.str; 109 default = "dovecot2"; 110 description = "Dovecot group name."; 111 }; 112 113 extraConfig = mkOption { 114 type = types.lines; 115 default = ""; 116 example = "mail_debug = yes"; 117 description = "Additional entries to put verbatim into Dovecot's config file."; 118 }; 119 120 configFile = mkOption { 121 type = types.nullOr types.str; 122 default = null; 123 description = "Config file used for the whole dovecot configuration."; 124 apply = v: if v != null then v else pkgs.writeText "dovecot.conf" dovecotConf; 125 }; 126 127 mailLocation = mkOption { 128 type = types.str; 129 default = "maildir:/var/spool/mail/%u"; /* Same as inbox, as postfix */ 130 example = "maildir:~/mail:INBOX=/var/spool/mail/%u"; 131 description = '' 132 Location that dovecot will use for mail folders. Dovecot mail_location option. 133 ''; 134 }; 135 136 mailUser = mkOption { 137 type = types.nullOr types.str; 138 default = null; 139 description = "Default user to store mail for virtual users."; 140 }; 141 142 mailGroup = mkOption { 143 type = types.nullOr types.str; 144 default = null; 145 description = "Default group to store mail for virtual users."; 146 }; 147 148 modules = mkOption { 149 type = types.listOf types.package; 150 default = []; 151 example = literalExample "[ pkgs.dovecot_pigeonhole ]"; 152 description = '' 153 Symlinks the contents of lib/dovecot of every given package into 154 /etc/dovecot/modules. This will make the given modules available 155 if a dovecot package with the module_dir patch applied is being used. 156 ''; 157 }; 158 159 sslCACert = mkOption { 160 type = types.nullOr types.str; 161 default = null; 162 description = "Path to the server's CA certificate key."; 163 }; 164 165 sslServerCert = mkOption { 166 type = types.nullOr types.str; 167 default = null; 168 description = "Path to the server's public key."; 169 }; 170 171 sslServerKey = mkOption { 172 type = types.nullOr types.str; 173 default = null; 174 description = "Path to the server's private key."; 175 }; 176 177 enablePAM = mkOption { 178 type = types.bool; 179 default = true; 180 description = "Whether to create a own Dovecot PAM service and configure PAM user logins."; 181 }; 182 183 sieveScripts = mkOption { 184 type = types.attrsOf types.path; 185 default = {}; 186 description = "Sieve scripts to be executed. Key is a sequence, e.g. 'before2', 'after' etc."; 187 }; 188 189 showPAMFailure = mkOption { 190 type = types.bool; 191 default = false; 192 description = "Show the PAM failure message on authentication error (useful for OTPW)."; 193 }; 194 }; 195 196 197 config = mkIf cfg.enable { 198 199 security.pam.services.dovecot2 = mkIf cfg.enablePAM {}; 200 201 services.dovecot2.protocols = 202 optional cfg.enableImap "imap" 203 ++ optional cfg.enablePop3 "pop3" 204 ++ optional cfg.enableLmtp "lmtp"; 205 206 users.extraUsers = [ 207 { name = "dovenull"; 208 uid = config.ids.uids.dovenull2; 209 description = "Dovecot user for untrusted logins"; 210 group = cfg.group; 211 } 212 ] ++ optional (cfg.user == "dovecot2") 213 { name = "dovecot2"; 214 uid = config.ids.uids.dovecot2; 215 description = "Dovecot user"; 216 group = cfg.group; 217 }; 218 219 users.extraGroups = optional (cfg.group == "dovecot2") 220 { name = "dovecot2"; 221 gid = config.ids.gids.dovecot2; 222 }; 223 224 environment.etc."dovecot/modules".source = modulesDir; 225 environment.etc."dovecot/dovecot.conf".source = cfg.configFile; 226 227 systemd.services.dovecot2 = { 228 description = "Dovecot IMAP/POP3 server"; 229 230 after = [ "keys.target" "network.target" ]; 231 wants = [ "keys.target" ]; 232 wantedBy = [ "multi-user.target" ]; 233 restartTriggers = [ cfg.configFile ]; 234 235 serviceConfig = { 236 ExecStart = "${dovecotPkg}/sbin/dovecot -F"; 237 ExecReload = "${dovecotPkg}/sbin/doveadm reload"; 238 Restart = "on-failure"; 239 RestartSec = "1s"; 240 StartLimitInterval = "1min"; 241 RuntimeDirectory = [ "dovecot2" ]; 242 }; 243 244 # When copying sieve scripts preserve the original time stamp 245 # (should be 0) so that the compiled sieve script is newer than 246 # the source file and Dovecot won't try to compile it. 247 preStart = '' 248 rm -rf ${stateDir}/sieve 249 '' + optionalString (cfg.sieveScripts != {}) '' 250 mkdir -p ${stateDir}/sieve 251 ${concatStringsSep "\n" (mapAttrsToList (to: from: '' 252 if [ -d '${from}' ]; then 253 mkdir '${stateDir}/sieve/${to}' 254 cp -p "${from}/"*.sieve '${stateDir}/sieve/${to}' 255 else 256 cp -p '${from}' '${stateDir}/sieve/${to}' 257 fi 258 ${pkgs.dovecot_pigeonhole}/bin/sievec '${stateDir}/sieve/${to}' 259 '') cfg.sieveScripts)} 260 chown -R '${cfg.mailUser}:${cfg.mailGroup}' '${stateDir}/sieve' 261 ''; 262 }; 263 264 environment.systemPackages = [ dovecotPkg ]; 265 266 assertions = [ 267 { assertion = intersectLists cfg.protocols [ "pop3" "imap" ] != []; 268 message = "dovecot needs at least one of the IMAP or POP3 listeners enabled"; 269 } 270 { assertion = isNull cfg.sslServerCert == isNull cfg.sslServerKey 271 && (!(isNull cfg.sslCACert) -> !(isNull cfg.sslServerCert || isNull cfg.sslServerKey)); 272 message = "dovecot needs both sslServerCert and sslServerKey defined for working crypto"; 273 } 274 { assertion = cfg.showPAMFailure -> cfg.enablePAM; 275 message = "dovecot is configured with showPAMFailure while enablePAM is disabled"; 276 } 277 { assertion = (cfg.sieveScripts != {}) -> ((cfg.mailUser != null) && (cfg.mailGroup != null)); 278 message = "dovecot requires mailUser and mailGroup to be set when sieveScripts is set"; 279 } 280 ]; 281 282 }; 283 284}