at 16.09-beta 17 kB view raw
1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 7 cfg = config.services.postfix; 8 user = cfg.user; 9 group = cfg.group; 10 setgidGroup = cfg.setgidGroup; 11 12 haveAliases = cfg.postmasterAlias != "" || cfg.rootAlias != "" || cfg.extraAliases != ""; 13 haveTransport = cfg.transport != ""; 14 haveVirtual = cfg.virtual != ""; 15 16 clientAccess = 17 if (cfg.dnsBlacklistOverrides != "") 18 then [ "check_client_access hash:/etc/postfix/client_access" ] 19 else []; 20 21 dnsBl = 22 if (cfg.dnsBlacklists != []) 23 then [ (concatStringsSep ", " (map (s: "reject_rbl_client " + s) cfg.dnsBlacklists)) ] 24 else []; 25 26 clientRestrictions = concatStringsSep ", " (clientAccess ++ dnsBl); 27 28 mainCf = 29 '' 30 compatibility_level = 9999 31 32 mail_owner = ${user} 33 default_privs = nobody 34 35 # NixOS specific locations 36 data_directory = /var/lib/postfix/data 37 queue_directory = /var/lib/postfix/queue 38 39 # Default location of everything in package 40 meta_directory = ${pkgs.postfix}/etc/postfix 41 command_directory = ${pkgs.postfix}/bin 42 sample_directory = /etc/postfix 43 newaliases_path = ${pkgs.postfix}/bin/newaliases 44 mailq_path = ${pkgs.postfix}/bin/mailq 45 readme_directory = no 46 sendmail_path = ${pkgs.postfix}/bin/sendmail 47 daemon_directory = ${pkgs.postfix}/libexec/postfix 48 manpage_directory = ${pkgs.postfix}/share/man 49 html_directory = ${pkgs.postfix}/share/postfix/doc/html 50 shlib_directory = no 51 52 '' 53 + optionalString config.networking.enableIPv6 '' 54 inet_protocols = all 55 '' 56 + (if cfg.networks != null then 57 '' 58 mynetworks = ${concatStringsSep ", " cfg.networks} 59 '' 60 else if cfg.networksStyle != "" then 61 '' 62 mynetworks_style = ${cfg.networksStyle} 63 '' 64 else 65 "") 66 + optionalString (cfg.hostname != "") '' 67 myhostname = ${cfg.hostname} 68 '' 69 + optionalString (cfg.domain != "") '' 70 mydomain = ${cfg.domain} 71 '' 72 + optionalString (cfg.origin != "") '' 73 myorigin = ${cfg.origin} 74 '' 75 + optionalString (cfg.destination != null) '' 76 mydestination = ${concatStringsSep ", " cfg.destination} 77 '' 78 + optionalString (cfg.relayDomains != null) '' 79 relay_domains = ${concatStringsSep ", " cfg.relayDomains} 80 '' 81 + '' 82 local_recipient_maps = 83 84 relayhost = ${if cfg.lookupMX || cfg.relayHost == "" then 85 cfg.relayHost 86 else 87 "[" + cfg.relayHost + "]"} 88 89 mail_spool_directory = /var/spool/mail/ 90 91 setgid_group = ${setgidGroup} 92 '' 93 + optionalString (cfg.sslCert != "") '' 94 95 smtp_tls_CAfile = ${cfg.sslCACert} 96 smtp_tls_cert_file = ${cfg.sslCert} 97 smtp_tls_key_file = ${cfg.sslKey} 98 99 smtp_use_tls = yes 100 101 smtpd_tls_CAfile = ${cfg.sslCACert} 102 smtpd_tls_cert_file = ${cfg.sslCert} 103 smtpd_tls_key_file = ${cfg.sslKey} 104 105 smtpd_use_tls = yes 106 '' 107 + optionalString (cfg.recipientDelimiter != "") '' 108 recipient_delimiter = ${cfg.recipientDelimiter} 109 '' 110 + optionalString haveAliases '' 111 alias_maps = hash:/etc/postfix/aliases 112 '' 113 + optionalString haveTransport '' 114 transport_maps = hash:/etc/postfix/transport 115 '' 116 + optionalString haveVirtual '' 117 virtual_alias_maps = hash:/etc/postfix/virtual 118 '' 119 + optionalString (cfg.dnsBlacklists != []) '' 120 smtpd_client_restrictions = ${clientRestrictions} 121 '' 122 + cfg.extraConfig; 123 124 masterCf = '' 125 # ========================================================================== 126 # service type private unpriv chroot wakeup maxproc command + args 127 # (yes) (yes) (no) (never) (100) 128 # ========================================================================== 129 smtp inet n - n - - smtpd 130 '' + optionalString cfg.enableSubmission '' 131 submission inet n - n - - smtpd 132 ${concatStringsSep "\n " (mapAttrsToList (x: y: "-o " + x + "=" + y) cfg.submissionOptions)} 133 '' 134 + '' 135 pickup unix n - n 60 1 pickup 136 cleanup unix n - n - 0 cleanup 137 qmgr unix n - n 300 1 qmgr 138 tlsmgr unix - - n 1000? 1 tlsmgr 139 rewrite unix - - n - - trivial-rewrite 140 bounce unix - - n - 0 bounce 141 defer unix - - n - 0 bounce 142 trace unix - - n - 0 bounce 143 verify unix - - n - 1 verify 144 flush unix n - n 1000? 0 flush 145 proxymap unix - - n - - proxymap 146 proxywrite unix - - n - 1 proxymap 147 '' 148 + optionalString cfg.enableSmtp '' 149 smtp unix - - n - - smtp 150 relay unix - - n - - smtp 151 -o smtp_fallback_relay= 152 # -o smtp_helo_timeout=5 -o smtp_connect_timeout=5 153 '' 154 + '' 155 showq unix n - n - - showq 156 error unix - - n - - error 157 retry unix - - n - - error 158 discard unix - - n - - discard 159 local unix - n n - - local 160 virtual unix - n n - - virtual 161 lmtp unix - - n - - lmtp 162 anvil unix - - n - 1 anvil 163 scache unix - - n - 1 scache 164 ${cfg.extraMasterConf} 165 ''; 166 167 aliases = 168 optionalString (cfg.postmasterAlias != "") '' 169 postmaster: ${cfg.postmasterAlias} 170 '' 171 + optionalString (cfg.rootAlias != "") '' 172 root: ${cfg.rootAlias} 173 '' 174 + cfg.extraAliases 175 ; 176 177 aliasesFile = pkgs.writeText "postfix-aliases" aliases; 178 virtualFile = pkgs.writeText "postfix-virtual" cfg.virtual; 179 checkClientAccessFile = pkgs.writeText "postfix-check-client-access" cfg.dnsBlacklistOverrides; 180 mainCfFile = pkgs.writeText "postfix-main.cf" mainCf; 181 masterCfFile = pkgs.writeText "postfix-master.cf" masterCf; 182 transportFile = pkgs.writeText "postfix-transport" cfg.transport; 183 184in 185 186{ 187 188 ###### interface 189 190 options = { 191 192 services.postfix = { 193 194 enable = mkOption { 195 type = types.bool; 196 default = false; 197 description = "Whether to run the Postfix mail server."; 198 }; 199 200 enableSmtp = mkOption { 201 default = true; 202 description = "Whether to enable smtp in master.cf."; 203 }; 204 205 enableSubmission = mkOption { 206 type = types.bool; 207 default = false; 208 description = "Whether to enable smtp submission"; 209 }; 210 211 submissionOptions = mkOption { 212 type = types.attrs; 213 default = { "smtpd_tls_security_level" = "encrypt"; 214 "smtpd_sasl_auth_enable" = "yes"; 215 "smtpd_client_restrictions" = "permit_sasl_authenticated,reject"; 216 "milter_macro_daemon_name" = "ORIGINATING"; 217 }; 218 description = "Options for the submission config in master.cf"; 219 example = { "smtpd_tls_security_level" = "encrypt"; 220 "smtpd_sasl_auth_enable" = "yes"; 221 "smtpd_sasl_type" = "dovecot"; 222 "smtpd_client_restrictions" = "permit_sasl_authenticated,reject"; 223 "milter_macro_daemon_name" = "ORIGINATING"; 224 }; 225 }; 226 227 setSendmail = mkOption { 228 type = types.bool; 229 default = true; 230 description = "Whether to set the system sendmail to postfix's."; 231 }; 232 233 user = mkOption { 234 type = types.str; 235 default = "postfix"; 236 description = "What to call the Postfix user (must be used only for postfix)."; 237 }; 238 239 group = mkOption { 240 type = types.str; 241 default = "postfix"; 242 description = "What to call the Postfix group (must be used only for postfix)."; 243 }; 244 245 setgidGroup = mkOption { 246 type = types.str; 247 default = "postdrop"; 248 description = " 249 How to call postfix setgid group (for postdrop). Should 250 be uniquely used group. 251 "; 252 }; 253 254 networks = mkOption { 255 type = types.nullOr (types.listOf types.str); 256 default = null; 257 example = ["192.168.0.1/24"]; 258 description = " 259 Net masks for trusted - allowed to relay mail to third parties - 260 hosts. Leave empty to use mynetworks_style configuration or use 261 default (localhost-only). 262 "; 263 }; 264 265 networksStyle = mkOption { 266 type = types.str; 267 default = ""; 268 description = " 269 Name of standard way of trusted network specification to use, 270 leave blank if you specify it explicitly or if you want to use 271 default (localhost-only). 272 "; 273 }; 274 275 hostname = mkOption { 276 type = types.str; 277 default = ""; 278 description =" 279 Hostname to use. Leave blank to use just the hostname of machine. 280 It should be FQDN. 281 "; 282 }; 283 284 domain = mkOption { 285 type = types.str; 286 default = ""; 287 description =" 288 Domain to use. Leave blank to use hostname minus first component. 289 "; 290 }; 291 292 origin = mkOption { 293 type = types.str; 294 default = ""; 295 description =" 296 Origin to use in outgoing e-mail. Leave blank to use hostname. 297 "; 298 }; 299 300 destination = mkOption { 301 type = types.nullOr (types.listOf types.str); 302 default = null; 303 example = ["localhost"]; 304 description = " 305 Full (!) list of domains we deliver locally. Leave blank for 306 acceptable Postfix default. 307 "; 308 }; 309 310 relayDomains = mkOption { 311 type = types.nullOr (types.listOf types.str); 312 default = null; 313 example = ["localdomain"]; 314 description = " 315 List of domains we agree to relay to. Default is empty. 316 "; 317 }; 318 319 relayHost = mkOption { 320 type = types.str; 321 default = ""; 322 description = " 323 Mail relay for outbound mail. 324 "; 325 }; 326 327 lookupMX = mkOption { 328 type = types.bool; 329 default = false; 330 description = " 331 Whether relay specified is just domain whose MX must be used. 332 "; 333 }; 334 335 postmasterAlias = mkOption { 336 type = types.str; 337 default = "root"; 338 description = "Who should receive postmaster e-mail."; 339 }; 340 341 rootAlias = mkOption { 342 type = types.str; 343 default = ""; 344 description = " 345 Who should receive root e-mail. Blank for no redirection. 346 "; 347 }; 348 349 extraAliases = mkOption { 350 type = types.lines; 351 default = ""; 352 description = " 353 Additional entries to put verbatim into aliases file, cf. man-page aliases(8). 354 "; 355 }; 356 357 extraConfig = mkOption { 358 type = types.lines; 359 default = ""; 360 description = " 361 Extra lines to be added verbatim to the main.cf configuration file. 362 "; 363 }; 364 365 sslCert = mkOption { 366 type = types.str; 367 default = ""; 368 description = "SSL certificate to use."; 369 }; 370 371 sslCACert = mkOption { 372 type = types.str; 373 default = ""; 374 description = "SSL certificate of CA."; 375 }; 376 377 sslKey = mkOption { 378 type = types.str; 379 default = ""; 380 description = "SSL key to use."; 381 }; 382 383 recipientDelimiter = mkOption { 384 type = types.str; 385 default = ""; 386 example = "+"; 387 description = " 388 Delimiter for address extension: so mail to user+test can be handled by ~user/.forward+test 389 "; 390 }; 391 392 virtual = mkOption { 393 type = types.lines; 394 default = ""; 395 description = " 396 Entries for the virtual alias map, cf. man-page virtual(8). 397 "; 398 }; 399 400 transport = mkOption { 401 default = ""; 402 description = " 403 Entries for the transport map, cf. man-page transport(8). 404 "; 405 }; 406 407 dnsBlacklists = mkOption { 408 default = []; 409 type = with types; listOf string; 410 description = "dns blacklist servers to use with smtpd_client_restrictions"; 411 }; 412 413 dnsBlacklistOverrides = mkOption { 414 default = ""; 415 description = "contents of check_client_access for overriding dnsBlacklists"; 416 }; 417 418 extraMasterConf = mkOption { 419 type = types.lines; 420 default = ""; 421 example = "submission inet n - n - - smtpd"; 422 description = "Extra lines to append to the generated master.cf file."; 423 }; 424 425 aliasFiles = mkOption { 426 type = types.attrsOf types.path; 427 default = {}; 428 description = "Aliases' tables to be compiled and placed into /var/lib/postfix/conf."; 429 }; 430 431 mapFiles = mkOption { 432 type = types.attrsOf types.path; 433 default = {}; 434 description = "Maps to be compiled and placed into /var/lib/postfix/conf."; 435 }; 436 437 }; 438 439 }; 440 441 442 ###### implementation 443 444 config = mkIf config.services.postfix.enable (mkMerge [ 445 { 446 447 environment = { 448 etc = singleton 449 { source = "/var/lib/postfix/conf"; 450 target = "postfix"; 451 }; 452 453 # This makes comfortable for root to run 'postqueue' for example. 454 systemPackages = [ pkgs.postfix ]; 455 }; 456 457 services.mail.sendmailSetuidWrapper = mkIf config.services.postfix.setSendmail { 458 program = "sendmail"; 459 source = "${pkgs.postfix}/bin/sendmail"; 460 group = setgidGroup; 461 setuid = false; 462 setgid = true; 463 }; 464 465 users.extraUsers = optional (user == "postfix") 466 { name = "postfix"; 467 description = "Postfix mail server user"; 468 uid = config.ids.uids.postfix; 469 group = group; 470 }; 471 472 users.extraGroups = 473 optional (group == "postfix") 474 { name = group; 475 gid = config.ids.gids.postfix; 476 } 477 ++ optional (setgidGroup == "postdrop") 478 { name = setgidGroup; 479 gid = config.ids.gids.postdrop; 480 }; 481 482 systemd.services.postfix = 483 { description = "Postfix mail server"; 484 485 wantedBy = [ "multi-user.target" ]; 486 after = [ "network.target" ]; 487 path = [ pkgs.postfix ]; 488 489 serviceConfig = { 490 Type = "forking"; 491 Restart = "always"; 492 PIDFile = "/var/lib/postfix/queue/pid/master.pid"; 493 ExecStart = "${pkgs.postfix}/bin/postfix start"; 494 ExecStop = "${pkgs.postfix}/bin/postfix stop"; 495 ExecReload = "${pkgs.postfix}/bin/postfix reload"; 496 }; 497 498 preStart = '' 499 # Backwards compatibility 500 if [ ! -d /var/lib/postfix ] && [ -d /var/postfix ]; then 501 mkdir -p /var/lib 502 mv /var/postfix /var/lib/postfix 503 fi 504 505 # All permissions set according ${pkgs.postfix}/etc/postfix/postfix-files script 506 mkdir -p /var/lib/postfix /var/lib/postfix/queue/{pid,public,maildrop} 507 chmod 0755 /var/lib/postfix 508 chown root:root /var/lib/postfix 509 510 rm -rf /var/lib/postfix/conf 511 mkdir -p /var/lib/postfix/conf 512 chmod 0755 /var/lib/postfix/conf 513 ln -sf ${pkgs.postfix}/etc/postfix/postfix-files /var/lib/postfix/conf/postfix-files 514 ln -sf ${mainCfFile} /var/lib/postfix/conf/main.cf 515 ln -sf ${masterCfFile} /var/lib/postfix/conf/master.cf 516 517 ${concatStringsSep "\n" (mapAttrsToList (to: from: '' 518 ln -sf ${from} /var/lib/postfix/conf/${to} 519 ${pkgs.postfix}/bin/postalias /var/lib/postfix/conf/${to} 520 '') cfg.aliasFiles)} 521 ${concatStringsSep "\n" (mapAttrsToList (to: from: '' 522 ln -sf ${from} /var/lib/postfix/conf/${to} 523 ${pkgs.postfix}/bin/postmap /var/lib/postfix/conf/${to} 524 '') cfg.mapFiles)} 525 526 mkdir -p /var/spool/mail 527 chown root:root /var/spool/mail 528 chmod a+rwxt /var/spool/mail 529 ln -sf /var/spool/mail /var/ 530 531 #Finally delegate to postfix checking remain directories in /var/lib/postfix and set permissions on them 532 ${pkgs.postfix}/bin/postfix set-permissions config_directory=/var/lib/postfix/conf 533 ''; 534 }; 535 } 536 537 (mkIf haveAliases { 538 services.postfix.aliasFiles."aliases" = aliasesFile; 539 }) 540 (mkIf haveTransport { 541 services.postfix.mapFiles."transport" = transportFile; 542 }) 543 (mkIf haveVirtual { 544 services.postfix.mapFiles."virtual" = virtualFile; 545 }) 546 (mkIf (cfg.dnsBlacklists != []) { 547 services.postfix.mapFiles."client_access" = checkClientAccessFile; 548 }) 549 ]); 550 551}