at 18.03-beta 12 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 canCreateMailUserGroup = cfg.mailUser != null && cfg.mailGroup != null; 13 14 dovecotConf = concatStrings [ 15 '' 16 base_dir = ${baseDir} 17 protocols = ${concatStringsSep " " cfg.protocols} 18 sendmail_path = /run/wrappers/bin/sendmail 19 '' 20 21 (if isNull cfg.sslServerCert then '' 22 ssl = no 23 disable_plaintext_auth = no 24 '' else '' 25 ssl_cert = <${cfg.sslServerCert} 26 ssl_key = <${cfg.sslServerKey} 27 ${optionalString (!(isNull cfg.sslCACert)) ("ssl_ca = <" + cfg.sslCACert)} 28 disable_plaintext_auth = yes 29 '') 30 31 '' 32 default_internal_user = ${cfg.user} 33 ${optionalString (cfg.mailUser != null) "mail_uid = ${cfg.mailUser}"} 34 ${optionalString (cfg.mailGroup != null) "mail_gid = ${cfg.mailGroup}"} 35 36 mail_location = ${cfg.mailLocation} 37 38 maildir_copy_with_hardlinks = yes 39 pop3_uidl_format = %08Xv%08Xu 40 41 auth_mechanisms = plain login 42 43 service auth { 44 user = root 45 } 46 '' 47 48 (optionalString cfg.enablePAM '' 49 userdb { 50 driver = passwd 51 } 52 53 passdb { 54 driver = pam 55 args = ${optionalString cfg.showPAMFailure "failure_show_msg=yes"} dovecot2 56 } 57 '') 58 59 (optionalString (cfg.sieveScripts != {}) '' 60 plugin { 61 ${concatStringsSep "\n" (mapAttrsToList (to: from: "sieve_${to} = ${stateDir}/sieve/${to}") cfg.sieveScripts)} 62 } 63 '') 64 65 (optionalString (cfg.mailboxes != []) '' 66 protocol imap { 67 namespace inbox { 68 inbox=yes 69 ${concatStringsSep "\n" (map mailboxConfig cfg.mailboxes)} 70 } 71 } 72 '') 73 74 (optionalString cfg.enableQuota '' 75 mail_plugins = $mail_plugins quota 76 service quota-status { 77 executable = ${dovecotPkg}/libexec/dovecot/quota-status -p postfix 78 inet_listener { 79 port = ${cfg.quotaPort} 80 } 81 client_limit = 1 82 } 83 84 protocol imap { 85 mail_plugins = $mail_plugins imap_quota 86 } 87 88 plugin { 89 quota_rule = *:storage=${cfg.quotaGlobalPerUser} 90 quota = maildir:User quota # per virtual mail user quota # BUG/FIXME broken, we couldn't get this working 91 quota_status_success = DUNNO 92 quota_status_nouser = DUNNO 93 quota_status_overquota = "552 5.2.2 Mailbox is full" 94 quota_grace = 10%% 95 } 96 '') 97 98 cfg.extraConfig 99 ]; 100 101 modulesDir = pkgs.symlinkJoin { 102 name = "dovecot-modules"; 103 paths = map (pkg: "${pkg}/lib/dovecot") ([ dovecotPkg ] ++ map (module: module.override { dovecot = dovecotPkg; }) cfg.modules); 104 }; 105 106 mailboxConfig = mailbox: '' 107 mailbox "${mailbox.name}" { 108 auto = ${toString mailbox.auto} 109 '' + optionalString (mailbox.specialUse != null) '' 110 special_use = \${toString mailbox.specialUse} 111 '' + "}"; 112 113 mailboxes = { lib, pkgs, ... }: { 114 options = { 115 name = mkOption { 116 type = types.strMatching ''[^"]+''; 117 example = "Spam"; 118 description = "The name of the mailbox."; 119 }; 120 auto = mkOption { 121 type = types.enum [ "no" "create" "subscribe" ]; 122 default = "no"; 123 example = "subscribe"; 124 description = "Whether to automatically create or create and subscribe to the mailbox or not."; 125 }; 126 specialUse = mkOption { 127 type = types.nullOr (types.enum [ "All" "Archive" "Drafts" "Flagged" "Junk" "Sent" "Trash" ]); 128 default = null; 129 example = "Junk"; 130 description = "Null if no special use flag is set. Other than that every use flag mentioned in the RFC is valid."; 131 }; 132 }; 133 }; 134in 135{ 136 137 options.services.dovecot2 = { 138 enable = mkEnableOption "Dovecot 2.x POP3/IMAP server"; 139 140 enablePop3 = mkOption { 141 type = types.bool; 142 default = false; 143 description = "Start the POP3 listener (when Dovecot is enabled)."; 144 }; 145 146 enableImap = mkOption { 147 type = types.bool; 148 default = true; 149 description = "Start the IMAP listener (when Dovecot is enabled)."; 150 }; 151 152 enableLmtp = mkOption { 153 type = types.bool; 154 default = false; 155 description = "Start the LMTP listener (when Dovecot is enabled)."; 156 }; 157 158 protocols = mkOption { 159 type = types.listOf types.str; 160 default = [ ]; 161 description = "Additional listeners to start when Dovecot is enabled."; 162 }; 163 164 user = mkOption { 165 type = types.str; 166 default = "dovecot2"; 167 description = "Dovecot user name."; 168 }; 169 170 group = mkOption { 171 type = types.str; 172 default = "dovecot2"; 173 description = "Dovecot group name."; 174 }; 175 176 extraConfig = mkOption { 177 type = types.lines; 178 default = ""; 179 example = "mail_debug = yes"; 180 description = "Additional entries to put verbatim into Dovecot's config file."; 181 }; 182 183 configFile = mkOption { 184 type = types.nullOr types.str; 185 default = null; 186 description = "Config file used for the whole dovecot configuration."; 187 apply = v: if v != null then v else pkgs.writeText "dovecot.conf" dovecotConf; 188 }; 189 190 mailLocation = mkOption { 191 type = types.str; 192 default = "maildir:/var/spool/mail/%u"; /* Same as inbox, as postfix */ 193 example = "maildir:~/mail:INBOX=/var/spool/mail/%u"; 194 description = '' 195 Location that dovecot will use for mail folders. Dovecot mail_location option. 196 ''; 197 }; 198 199 mailUser = mkOption { 200 type = types.nullOr types.str; 201 default = null; 202 description = "Default user to store mail for virtual users."; 203 }; 204 205 mailGroup = mkOption { 206 type = types.nullOr types.str; 207 default = null; 208 description = "Default group to store mail for virtual users."; 209 }; 210 211 createMailUser = mkOption { 212 type = types.bool; 213 default = true; 214 description = ''Whether to automatically create the user 215 given in <option>services.dovecot.user</option> and the group 216 given in <option>services.dovecot.group</option>.''; 217 }; 218 219 modules = mkOption { 220 type = types.listOf types.package; 221 default = []; 222 example = literalExample "[ pkgs.dovecot_pigeonhole ]"; 223 description = '' 224 Symlinks the contents of lib/dovecot of every given package into 225 /etc/dovecot/modules. This will make the given modules available 226 if a dovecot package with the module_dir patch applied is being used. 227 ''; 228 }; 229 230 sslCACert = mkOption { 231 type = types.nullOr types.str; 232 default = null; 233 description = "Path to the server's CA certificate key."; 234 }; 235 236 sslServerCert = mkOption { 237 type = types.nullOr types.str; 238 default = null; 239 description = "Path to the server's public key."; 240 }; 241 242 sslServerKey = mkOption { 243 type = types.nullOr types.str; 244 default = null; 245 description = "Path to the server's private key."; 246 }; 247 248 enablePAM = mkOption { 249 type = types.bool; 250 default = true; 251 description = "Whether to create a own Dovecot PAM service and configure PAM user logins."; 252 }; 253 254 sieveScripts = mkOption { 255 type = types.attrsOf types.path; 256 default = {}; 257 description = "Sieve scripts to be executed. Key is a sequence, e.g. 'before2', 'after' etc."; 258 }; 259 260 showPAMFailure = mkOption { 261 type = types.bool; 262 default = false; 263 description = "Show the PAM failure message on authentication error (useful for OTPW)."; 264 }; 265 266 mailboxes = mkOption { 267 type = types.listOf (types.submodule mailboxes); 268 default = []; 269 example = [ { name = "Spam"; specialUse = "Junk"; auto = "create"; } ]; 270 description = "Configure mailboxes and auto create or subscribe them."; 271 }; 272 273 enableQuota = mkOption { 274 type = types.bool; 275 default = false; 276 example = true; 277 description = "Whether to enable the dovecot quota service."; 278 }; 279 280 quotaPort = mkOption { 281 type = types.str; 282 default = "12340"; 283 description = '' 284 The Port the dovecot quota service binds to. 285 If using postfix, add check_policy_service inet:localhost:12340 to your smtpd_recipient_restrictions in your postfix config. 286 ''; 287 }; 288 quotaGlobalPerUser = mkOption { 289 type = types.str; 290 default = "100G"; 291 example = "10G"; 292 description = "Quota limit for the user in bytes. Supports suffixes b, k, M, G, T and %."; 293 }; 294 295 }; 296 297 298 config = mkIf cfg.enable { 299 300 security.pam.services.dovecot2 = mkIf cfg.enablePAM {}; 301 302 services.dovecot2.protocols = 303 optional cfg.enableImap "imap" 304 ++ optional cfg.enablePop3 "pop3" 305 ++ optional cfg.enableLmtp "lmtp"; 306 307 users.extraUsers = [ 308 { name = "dovenull"; 309 uid = config.ids.uids.dovenull2; 310 description = "Dovecot user for untrusted logins"; 311 group = cfg.group; 312 } 313 ] ++ optional (cfg.user == "dovecot2") 314 { name = "dovecot2"; 315 uid = config.ids.uids.dovecot2; 316 description = "Dovecot user"; 317 group = cfg.group; 318 } 319 ++ optional (cfg.createMailUser && cfg.mailUser != null) 320 ({ name = cfg.mailUser; 321 description = "Virtual Mail User"; 322 } // optionalAttrs (cfg.mailGroup != null) { 323 group = cfg.mailGroup; 324 }); 325 326 users.extraGroups = optional (cfg.group == "dovecot2") 327 { name = "dovecot2"; 328 gid = config.ids.gids.dovecot2; 329 } 330 ++ optional (cfg.createMailUser && cfg.mailGroup != null) 331 { name = cfg.mailGroup; 332 }; 333 334 environment.etc."dovecot/modules".source = modulesDir; 335 environment.etc."dovecot/dovecot.conf".source = cfg.configFile; 336 337 systemd.services.dovecot2 = { 338 description = "Dovecot IMAP/POP3 server"; 339 340 after = [ "keys.target" "network.target" ]; 341 wants = [ "keys.target" ]; 342 wantedBy = [ "multi-user.target" ]; 343 restartTriggers = [ cfg.configFile ]; 344 345 serviceConfig = { 346 ExecStart = "${dovecotPkg}/sbin/dovecot -F"; 347 ExecReload = "${dovecotPkg}/sbin/doveadm reload"; 348 Restart = "on-failure"; 349 RestartSec = "1s"; 350 StartLimitInterval = "1min"; 351 RuntimeDirectory = [ "dovecot2" ]; 352 }; 353 354 # When copying sieve scripts preserve the original time stamp 355 # (should be 0) so that the compiled sieve script is newer than 356 # the source file and Dovecot won't try to compile it. 357 preStart = '' 358 rm -rf ${stateDir}/sieve 359 '' + optionalString (cfg.sieveScripts != {}) '' 360 mkdir -p ${stateDir}/sieve 361 ${concatStringsSep "\n" (mapAttrsToList (to: from: '' 362 if [ -d '${from}' ]; then 363 mkdir '${stateDir}/sieve/${to}' 364 cp -p "${from}/"*.sieve '${stateDir}/sieve/${to}' 365 else 366 cp -p '${from}' '${stateDir}/sieve/${to}' 367 fi 368 ${pkgs.dovecot_pigeonhole}/bin/sievec '${stateDir}/sieve/${to}' 369 '') cfg.sieveScripts)} 370 chown -R '${cfg.mailUser}:${cfg.mailGroup}' '${stateDir}/sieve' 371 ''; 372 }; 373 374 environment.systemPackages = [ dovecotPkg ]; 375 376 assertions = [ 377 { assertion = intersectLists cfg.protocols [ "pop3" "imap" ] != []; 378 message = "dovecot needs at least one of the IMAP or POP3 listeners enabled"; 379 } 380 { assertion = isNull cfg.sslServerCert == isNull cfg.sslServerKey 381 && (!(isNull cfg.sslCACert) -> !(isNull cfg.sslServerCert || isNull cfg.sslServerKey)); 382 message = "dovecot needs both sslServerCert and sslServerKey defined for working crypto"; 383 } 384 { assertion = cfg.showPAMFailure -> cfg.enablePAM; 385 message = "dovecot is configured with showPAMFailure while enablePAM is disabled"; 386 } 387 { assertion = (cfg.sieveScripts != {}) -> ((cfg.mailUser != null) && (cfg.mailGroup != null)); 388 message = "dovecot requires mailUser and mailGroup to be set when sieveScripts is set"; 389 } 390 ]; 391 392 }; 393 394}