Self-host your own digital island
1# nixos-mailserver: a simple mail server 2# Copyright (C) 2016-2018 Robin Raymond 3# 4# This program is free software: you can redistribute it and/or modify 5# it under the terms of the GNU General Public License as published by 6# the Free Software Foundation, either version 3 of the License, or 7# (at your option) any later version. 8# 9# This program is distributed in the hope that it will be useful, 10# but WITHOUT ANY WARRANTY; without even the implied warranty of 11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12# GNU General Public License for more details. 13# 14# You should have received a copy of the GNU General Public License 15# along with this program. If not, see <http://www.gnu.org/licenses/> 16 17{ config, lib, pkgs, ... }: 18 19with lib; 20 21let 22 cfg = config.mailserver; 23in 24{ 25 options.mailserver = { 26 enable = lib.mkEnableOption "nixos-mailserver"; 27 28 openFirewall = mkOption { 29 type = types.bool; 30 default = true; 31 description = "Automatically open ports in the firewall."; 32 }; 33 34 fqdn = mkOption { 35 type = types.str; 36 example = "mx.example.com"; 37 description = "The fully qualified domain name of the mail server."; 38 }; 39 40 domains = mkOption { 41 type = types.listOf types.str; 42 example = [ "example.com" ]; 43 default = []; 44 description = "The domains that this mail server serves."; 45 }; 46 47 certificateDomains = mkOption { 48 type = types.listOf types.str; 49 example = [ "imap.example.com" "pop3.example.com" ]; 50 default = []; 51 description = "Secondary domains and subdomains for which it is necessary to generate a certificate."; 52 }; 53 54 messageSizeLimit = mkOption { 55 type = types.int; 56 example = 52428800; 57 default = 20971520; 58 description = "Message size limit enforced by Postfix."; 59 }; 60 61 loginAccounts = mkOption { 62 type = types.attrsOf (types.submodule ({ name, ... }: { 63 options = { 64 name = mkOption { 65 type = types.str; 66 example = "user1@example.com"; 67 description = "Username"; 68 }; 69 70 password = mkOption { 71 type = with types; nullOr str; 72 default = null; 73 example = "$6$evQJs5CFQyPAW09S$Cn99Y8.QjZ2IBnSu4qf1vBxDRWkaIZWOtmu1Ddsm3.H3CFpeVc0JU4llIq8HQXgeatvYhh5O33eWG3TSpjzu6/"; 74 description = '' 75 The password. 76 77 Warning: this is stored in plaintext in the Nix store! 78 Use `passwordFile` instead. 79 ''; 80 }; 81 82 passwordFile = mkOption { 83 type = with types; nullOr path; 84 default = null; 85 example = "/run/keys/user1-passwordhash"; 86 description = '' 87 A file containing the user's password. 88 ''; 89 }; 90 91 aliases = mkOption { 92 type = with types; listOf types.str; 93 example = ["abuse@example.com" "postmaster@example.com"]; 94 default = []; 95 description = '' 96 A list of aliases of this login account. 97 Note: Use list entries like "@example.com" to create a catchAll 98 that allows sending from all email addresses in these domain. 99 ''; 100 }; 101 102 catchAll = mkOption { 103 type = with types; listOf (enum cfg.domains); 104 example = ["example.com" "example2.com"]; 105 default = []; 106 description = '' 107 For which domains should this account act as a catch all? 108 Note: Does not allow sending from all addresses of these domains. 109 ''; 110 }; 111 112 quota = mkOption { 113 type = with types; nullOr types.str; 114 default = null; 115 example = "2G"; 116 description = '' 117 Per user quota rules. Accepted sizes are `xx k/M/G/T` with the 118 obvious meaning. Leave blank for the standard quota `100G`. 119 ''; 120 }; 121 122 sieveScript = mkOption { 123 type = with types; nullOr lines; 124 default = null; 125 example = '' 126 require ["fileinto", "mailbox"]; 127 128 if address :is "from" "gitlab@mg.gitlab.com" { 129 fileinto :create "GitLab"; 130 stop; 131 } 132 133 # This must be the last rule, it will check if list-id is set, and 134 # file the message into the Lists folder for further investigation 135 elsif header :matches "list-id" "<?*>" { 136 fileinto :create "Lists"; 137 stop; 138 } 139 ''; 140 description = '' 141 Per-user sieve script. 142 ''; 143 }; 144 145 sendOnly = mkOption { 146 type = types.bool; 147 default = false; 148 description = '' 149 Specifies if the account should be a send-only account. 150 Emails sent to send-only accounts will be rejected from 151 unauthorized senders with the sendOnlyRejectMessage 152 stating the reason. 153 ''; 154 }; 155 156 sendOnlyRejectMessage = mkOption { 157 type = types.str; 158 default = "This account cannot receive emails."; 159 description = '' 160 The message that will be returned to the sender when an email is 161 sent to a send-only account. Only used if the account is marked 162 as send-only. 163 ''; 164 }; 165 }; 166 167 config.name = mkDefault name; 168 })); 169 example = { 170 user1 = { 171 password = "$6$evQJs5CFQyPAW09S$Cn99Y8.QjZ2IBnSu4qf1vBxDRWkaIZWOtmu1Ddsm3.H3CFpeVc0JU4llIq8HQXgeatvYhh5O33eWG3TSpjzu6/"; 172 }; 173 user2 = { 174 password = "$6$oE0ZNv2n7Vk9gOf$9xcZWCCLGdMflIfuA0vR1Q1Xblw6RZqPrP94mEit2/81/7AKj2bqUai5yPyWE.QYPyv6wLMHZvjw3Rlg7yTCD/"; 175 }; 176 }; 177 description = '' 178 The login account of the domain. Every account is mapped to a unix user, 179 e.g. `user1@example.com`. To generate the passwords use `htpasswd` as 180 follows 181 182 ``` 183 nix run nixpkgs.apacheHttpd -c htpasswd -nbB "" "super secret password" | cut -d: -f2 184 ``` 185 ''; 186 default = {}; 187 }; 188 189 indexDir = mkOption { 190 type = types.nullOr types.str; 191 default = null; 192 description = '' 193 Folder to store search indices. If null, indices are stored 194 along with email, which could not necessarily be desirable, 195 especially when the fullTextSearch option is enable since 196 indices it creates are voluminous and do not need to be backed 197 up. 198 199 Be careful when changing this option value since all indices 200 would be recreated at the new location (and clients would need 201 to resynchronize). 202 203 Note the some variables can be used in the file path. See 204 https://doc.dovecot.org/configuration_manual/mail_location/#variables 205 for details. 206 ''; 207 example = "/var/lib/dovecot/indices"; 208 }; 209 210 fullTextSearch = { 211 enable = lib.mkEnableOption "Full text search indexing with xapian. This has significant performance and disk space cost."; 212 autoIndex = mkOption { 213 type = types.bool; 214 default = true; 215 description = "Enable automatic indexing of messages as they are received or modified."; 216 }; 217 autoIndexExclude = mkOption { 218 type = types.listOf types.str; 219 default = [ ]; 220 example = [ "\\Trash" "SomeFolder" "Other/*" ]; 221 description = '' 222 Mailboxes to exclude from automatic indexing. 223 ''; 224 }; 225 226 indexAttachments = mkOption { 227 type = types.bool; 228 default = false; 229 description = "Also index text-only attachements. Binary attachements are never indexed."; 230 }; 231 232 enforced = mkOption { 233 type = types.enum [ "yes" "no" "body" ]; 234 default = "no"; 235 description = '' 236 Fail searches when no index is available. If set to 237 <literal>body</literal>, then only body searches (as opposed to 238 header) are affected. If set to <literal>no</literal>, searches may 239 fall back to a very slow brute force search. 240 ''; 241 }; 242 243 minSize = mkOption { 244 type = types.int; 245 default = 2; 246 description = "Size of the smallest n-gram to index."; 247 }; 248 maxSize = mkOption { 249 type = types.int; 250 default = 20; 251 description = "Size of the largest n-gram to index."; 252 }; 253 memoryLimit = mkOption { 254 type = types.nullOr types.int; 255 default = null; 256 example = 2000; 257 description = "Memory limit for the indexer process, in MiB. If null, leaves the default (which is rather low), and if 0, no limit."; 258 }; 259 260 maintenance = { 261 enable = mkOption { 262 type = types.bool; 263 default = true; 264 description = "Regularly optmize indices, as recommended by upstream."; 265 }; 266 267 onCalendar = mkOption { 268 type = types.str; 269 default = "daily"; 270 description = "When to run the maintenance job. See systemd.time(7) for more information about the format."; 271 }; 272 273 randomizedDelaySec = mkOption { 274 type = types.int; 275 default = 1000; 276 description = "Run the maintenance job not exactly at the time specified with <literal>onCalendar</literal>, but plus or minus this many seconds."; 277 }; 278 }; 279 }; 280 281 lmtpSaveToDetailMailbox = mkOption { 282 type = types.enum ["yes" "no"]; 283 default = "yes"; 284 description = '' 285 If an email address is delimited by a "+", should it be filed into a 286 mailbox matching the string after the "+"? For example, 287 user1+test@example.com would be filed into the mailbox "test". 288 ''; 289 }; 290 291 extraVirtualAliases = mkOption { 292 type = let 293 loginAccount = mkOptionType { 294 name = "Login Account"; 295 check = (account: builtins.elem account (builtins.attrNames cfg.loginAccounts)); 296 }; 297 in with types; attrsOf (either loginAccount (nonEmptyListOf loginAccount)); 298 example = { 299 "info@example.com" = "user1@example.com"; 300 "postmaster@example.com" = "user1@example.com"; 301 "abuse@example.com" = "user1@example.com"; 302 "multi@example.com" = [ "user1@example.com" "user2@example.com" ]; 303 }; 304 description = '' 305 Virtual Aliases. A virtual alias `"info@example.com" = "user1@example.com"` means that 306 all mail to `info@example.com` is forwarded to `user1@example.com`. Note 307 that it is expected that `postmaster@example.com` and `abuse@example.com` is 308 forwarded to some valid email address. (Alternatively you can create login 309 accounts for `postmaster` and (or) `abuse`). Furthermore, it also allows 310 the user `user1@example.com` to send emails as `info@example.com`. 311 It's also possible to create an alias for multiple accounts. In this 312 example all mails for `multi@example.com` will be forwarded to both 313 `user1@example.com` and `user2@example.com`. 314 ''; 315 default = {}; 316 }; 317 318 forwards = mkOption { 319 type = with types; attrsOf (either (listOf str) str); 320 example = { 321 "user@example.com" = "user@elsewhere.com"; 322 }; 323 description = '' 324 To forward mails to an external address. For instance, 325 the value {`"user@example.com" = "user@elsewhere.com";}` 326 means that mails to `user@example.com` are forwarded to 327 `user@elsewhere.com`. The difference with the 328 `extraVirtualAliases` option is that `user@elsewhere.com` 329 can't send mail as `user@example.com`. Also, this option 330 allows to forward mails to external addresses. 331 ''; 332 default = {}; 333 }; 334 335 rejectSender = mkOption { 336 type = types.listOf types.str; 337 example = [ "@example.com" "spammer@example.net" ]; 338 description = '' 339 Reject emails from these addresses from unauthorized senders. 340 Use if a spammer is using the same domain or the same sender over and over. 341 ''; 342 default = []; 343 }; 344 345 rejectRecipients = mkOption { 346 type = types.listOf types.str; 347 example = [ "sales@example.com" "info@example.com" ]; 348 description = '' 349 Reject emails addressed to these local addresses from unauthorized senders. 350 Use if a spammer has found email addresses in a catchall domain but you do 351 not want to disable the catchall. 352 ''; 353 default = []; 354 }; 355 356 vmailUID = mkOption { 357 type = types.int; 358 default = 5000; 359 description = '' 360 The unix UID of the virtual mail user. Be mindful that if this is 361 changed, you will need to manually adjust the permissions of 362 mailDirectory. 363 ''; 364 }; 365 366 vmailUserName = mkOption { 367 type = types.str; 368 default = "virtualMail"; 369 description = '' 370 The user name and group name of the user that owns the directory where all 371 the mail is stored. 372 ''; 373 }; 374 375 vmailGroupName = mkOption { 376 type = types.str; 377 default = "virtualMail"; 378 description = '' 379 The user name and group name of the user that owns the directory where all 380 the mail is stored. 381 ''; 382 }; 383 384 mailDirectory = mkOption { 385 type = types.path; 386 default = "/var/vmail"; 387 description = '' 388 Where to store the mail. 389 ''; 390 }; 391 392 useFsLayout = mkOption { 393 type = types.bool; 394 default = false; 395 description = '' 396 Sets whether dovecot should organize mail in subdirectories: 397 398 - /var/vmail/example.com/user/.folder.subfolder/ (default layout) 399 - /var/vmail/example.com/user/folder/subfolder/ (FS layout) 400 401 See https://wiki2.dovecot.org/MailboxFormat/Maildir for details. 402 ''; 403 }; 404 405 hierarchySeparator = mkOption { 406 type = types.str; 407 default = "."; 408 description = '' 409 The hierarchy separator for mailboxes used by dovecot for the namespace 'inbox'. 410 Dovecot defaults to "." but recommends "/". 411 This affects how mailboxes appear to mail clients and sieve scripts. 412 For instance when using "." then in a sieve script "example.com" would refer to the mailbox "com" in the parent mailbox "example". 413 This does not determine the way your mails are stored on disk. 414 See https://wiki.dovecot.org/Namespaces for details. 415 ''; 416 }; 417 418 mailboxes = mkOption { 419 description = '' 420 The mailboxes for dovecot. 421 Depending on the mail client used it might be necessary to change some mailbox's name. 422 ''; 423 default = { 424 Trash = { 425 auto = "no"; 426 specialUse = "Trash"; 427 }; 428 Junk = { 429 auto = "subscribe"; 430 specialUse = "Junk"; 431 }; 432 Drafts = { 433 auto = "subscribe"; 434 specialUse = "Drafts"; 435 }; 436 Sent = { 437 auto = "subscribe"; 438 specialUse = "Sent"; 439 }; 440 }; 441 }; 442 443 certificateScheme = mkOption { 444 type = types.enum [ 1 2 3 ]; 445 default = 2; 446 description = '' 447 Certificate Files. There are three options for these. 448 449 1) You specify locations and manually copy certificates there. 450 2) You let the server create new (self signed) certificates on the fly. 451 3) You let the server create a certificate via `Let's Encrypt`. Note that 452 this implies that a stripped down webserver has to be started. This also 453 implies that the FQDN must be set as an `A` record to point to the IP of 454 the server. In particular port 80 on the server will be opened. For details 455 on how to set up the domain records, see the guide in the readme. 456 ''; 457 }; 458 459 certificateFile = mkOption { 460 type = types.path; 461 example = "/root/mail-server.crt"; 462 description = '' 463 Scheme 1) 464 Location of the certificate 465 ''; 466 }; 467 468 keyFile = mkOption { 469 type = types.path; 470 example = "/root/mail-server.key"; 471 description = '' 472 Scheme 1) 473 Location of the key file 474 ''; 475 }; 476 477 certificateDirectory = mkOption { 478 type = types.path; 479 default = "/var/certs"; 480 description = '' 481 Scheme 2) 482 This is the folder where the certificate will be created. The name is 483 hardcoded to "cert-DOMAIN.pem" and "key-DOMAIN.pem" and the 484 certificate is valid for 10 years. 485 ''; 486 }; 487 488 enableImap = mkOption { 489 type = types.bool; 490 default = true; 491 description = '' 492 Whether to enable IMAP with STARTTLS on port 143. 493 ''; 494 }; 495 496 enableImapSsl = mkOption { 497 type = types.bool; 498 default = true; 499 description = '' 500 Whether to enable IMAP with TLS in wrapper-mode on port 993. 501 ''; 502 }; 503 504 enableSubmission = mkOption { 505 type = types.bool; 506 default = true; 507 description = '' 508 Whether to enable SMTP with STARTTLS on port 587. 509 ''; 510 }; 511 512 enableSubmissionSsl = mkOption { 513 type = types.bool; 514 default = true; 515 description = '' 516 Whether to enable SMTP with TLS in wrapper-mode on port 465. 517 ''; 518 }; 519 520 enablePop3 = mkOption { 521 type = types.bool; 522 default = false; 523 description = '' 524 Whether to enable POP3 with STARTTLS on port on port 110. 525 ''; 526 }; 527 528 enablePop3Ssl = mkOption { 529 type = types.bool; 530 default = false; 531 description = '' 532 Whether to enable POP3 with TLS in wrapper-mode on port 995. 533 ''; 534 }; 535 536 enableManageSieve = mkOption { 537 type = types.bool; 538 default = false; 539 description = '' 540 Whether to enable ManageSieve, setting this option to true will open 541 port 4190 in the firewall. 542 543 The ManageSieve protocol allows users to manage their Sieve scripts on 544 a remote server with a supported client, including Thunderbird. 545 ''; 546 }; 547 548 sieveDirectory = mkOption { 549 type = types.path; 550 default = "/var/sieve"; 551 description = '' 552 Where to store the sieve scripts. 553 ''; 554 }; 555 556 virusScanning = mkOption { 557 type = types.bool; 558 default = false; 559 description = '' 560 Whether to activate virus scanning. Note that virus scanning is _very_ 561 expensive memory wise. 562 ''; 563 }; 564 565 dkimSigning = mkOption { 566 type = types.bool; 567 default = true; 568 description = '' 569 Whether to activate dkim signing. 570 ''; 571 }; 572 573 dkimSelector = mkOption { 574 type = types.str; 575 default = "mail"; 576 description = '' 577 578 ''; 579 }; 580 581 dkimKeyDirectory = mkOption { 582 type = types.path; 583 default = "/var/dkim"; 584 description = '' 585 586 ''; 587 }; 588 589 dkimKeyBits = mkOption { 590 type = types.int; 591 default = 1024; 592 description = '' 593 How many bits in generated DKIM keys. RFC6376 advises minimum 1024-bit keys. 594 595 If you have already deployed a key with a different number of bits than specified 596 here, then you should use a different selector (dkimSelector). In order to get 597 this package to generate a key with the new number of bits, you will either have to 598 change the selector or delete the old key file. 599 ''; 600 }; 601 602 dkimHeaderCanonicalization = mkOption { 603 type = types.enum ["relaxed" "simple"]; 604 default = "relaxed"; 605 description = '' 606 DKIM canonicalization algorithm for message headers. 607 608 See https://datatracker.ietf.org/doc/html/rfc6376/#section-3.4 for details. 609 ''; 610 }; 611 612 dkimBodyCanonicalization = mkOption { 613 type = types.enum ["relaxed" "simple"]; 614 default = "relaxed"; 615 description = '' 616 DKIM canonicalization algorithm for message bodies. 617 618 See https://datatracker.ietf.org/doc/html/rfc6376/#section-3.4 for details. 619 ''; 620 }; 621 622 debug = mkOption { 623 type = types.bool; 624 default = false; 625 description = '' 626 Whether to enable verbose logging for mailserver related services. This 627 intended be used for development purposes only, you probably don't want 628 to enable this unless you're hacking on nixos-mailserver. 629 ''; 630 }; 631 632 maxConnectionsPerUser = mkOption { 633 type = types.int; 634 default = 100; 635 description = '' 636 Maximum number of IMAP/POP3 connections allowed for a user from each IP address. 637 E.g. a value of 50 allows for 50 IMAP and 50 POP3 connections at the same 638 time for a single user. 639 ''; 640 }; 641 642 localDnsResolver = mkOption { 643 type = types.bool; 644 default = true; 645 description = '' 646 Runs a local DNS resolver (kresd) as recommended when running rspamd. This prevents your log file from filling up with rspamd_monitored_dns_mon entries. 647 ''; 648 }; 649 650 recipientDelimiter = mkOption { 651 type = types.str; 652 default = "+"; 653 description = '' 654 Configure the recipient delimiter. 655 ''; 656 }; 657 658 redis = { 659 address = mkOption { 660 type = types.str; 661 # read the default from nixos' redis module 662 default = let 663 cf = config.services.redis.servers.rspamd.bind; 664 cfdefault = if cf == null then "127.0.0.1" else cf; 665 ips = lib.strings.splitString " " cfdefault; 666 ip = lib.lists.head (ips ++ [ "127.0.0.1" ]); 667 isIpv6 = ip: lib.lists.elem ":" (lib.stringToCharacters ip); 668 in 669 if (ip == "0.0.0.0" || ip == "::") 670 then "127.0.0.1" 671 else if isIpv6 ip then "[${ip}]" else ip; 672 defaultText = lib.literalDocBook "computed from <option>config.services.redis.servers.rspamd.bind</option>"; 673 description = '' 674 Address that rspamd should use to contact redis. 675 ''; 676 }; 677 678 port = mkOption { 679 type = types.port; 680 default = config.services.redis.servers.rspamd.port; 681 defaultText = lib.literalExpression "config.services.redis.servers.rspamd.port"; 682 description = '' 683 Port that rspamd should use to contact redis. 684 ''; 685 }; 686 687 password = mkOption { 688 type = types.nullOr types.str; 689 default = config.services.redis.servers.rspamd.requirePass; 690 defaultText = lib.literalExpression "config.services.redis.servers.rspamd.requirePass"; 691 description = '' 692 Password that rspamd should use to contact redis, or null if not required. 693 ''; 694 }; 695 }; 696 697 rewriteMessageId = mkOption { 698 type = types.bool; 699 default = false; 700 description = '' 701 Rewrites the Message-ID's hostname-part of outgoing emails to the FQDN. 702 Please be aware that this may cause problems with some mail clients 703 relying on the original Message-ID. 704 ''; 705 }; 706 707 sendingFqdn = mkOption { 708 type = types.str; 709 default = cfg.fqdn; 710 defaultText = "config.mailserver.fqdn"; 711 example = "myserver.example.com"; 712 description = '' 713 The fully qualified domain name of the mail server used to 714 identify with remote servers. 715 716 If this server's IP serves purposes other than a mail server, 717 it may be desirable for the server to have a name other than 718 that to which the user will connect. For example, the user 719 might connect to mx.example.com, but the server's IP has 720 reverse DNS that resolves to myserver.example.com; in this 721 scenario, some mail servers may reject or penalize the 722 message. 723 724 This setting allows the server to identify as 725 myserver.example.com when forwarding mail, independently of 726 `fqdn` (which, for SSL reasons, should generally be the name 727 to which the user connects). 728 729 Set this to the name to which the sending IP's reverse DNS 730 resolves. 731 ''; 732 }; 733 734 policydSPFExtraConfig = mkOption { 735 type = types.lines; 736 default = ""; 737 example = '' 738 skip_addresses = 127.0.0.0/8,::ffff:127.0.0.0/104,::1 739 ''; 740 description = '' 741 Extra configuration options for policyd-spf. This can be use to among 742 other things skip spf checking for some IP addresses. 743 ''; 744 }; 745 746 monitoring = { 747 enable = lib.mkEnableOption "monitoring via monit"; 748 749 alertAddress = mkOption { 750 type = types.str; 751 description = '' 752 The email address to send alerts to. 753 ''; 754 }; 755 756 config = mkOption { 757 type = types.str; 758 default = '' 759 set daemon 120 with start delay 60 760 set mailserver 761 localhost 762 763 set httpd port 2812 and use address localhost 764 allow localhost 765 allow admin:obwjoawijerfoijsiwfj29jf2f2jd 766 767 check filesystem root with path / 768 if space usage > 80% then alert 769 if inode usage > 80% then alert 770 771 check system $HOST 772 if cpu usage > 95% for 10 cycles then alert 773 if memory usage > 75% for 5 cycles then alert 774 if swap usage > 20% for 10 cycles then alert 775 if loadavg (1min) > 90 for 15 cycles then alert 776 if loadavg (5min) > 80 for 10 cycles then alert 777 if loadavg (15min) > 70 for 8 cycles then alert 778 779 check process sshd with pidfile /var/run/sshd.pid 780 start program "${pkgs.systemd}/bin/systemctl start sshd" 781 stop program "${pkgs.systemd}/bin/systemctl stop sshd" 782 if failed port 22 protocol ssh for 2 cycles then restart 783 784 check process postfix with pidfile /var/lib/postfix/queue/pid/master.pid 785 start program = "${pkgs.systemd}/bin/systemctl start postfix" 786 stop program = "${pkgs.systemd}/bin/systemctl stop postfix" 787 if failed port 25 protocol smtp for 5 cycles then restart 788 789 check process dovecot with pidfile /var/run/dovecot2/master.pid 790 start program = "${pkgs.systemd}/bin/systemctl start dovecot2" 791 stop program = "${pkgs.systemd}/bin/systemctl stop dovecot2" 792 if failed host ${cfg.fqdn} port 993 type tcpssl sslauto protocol imap for 5 cycles then restart 793 794 check process rspamd with matching "rspamd: main process" 795 start program = "${pkgs.systemd}/bin/systemctl start rspamd" 796 stop program = "${pkgs.systemd}/bin/systemctl stop rspamd" 797 ''; 798 defaultText = lib.literalDocBook "see source"; 799 description = '' 800 The configuration used for monitoring via monit. 801 Use a mail address that you actively check and set it via 'set alert ...'. 802 ''; 803 }; 804 }; 805 806 borgbackup = { 807 enable = lib.mkEnableOption "backup via borgbackup"; 808 809 repoLocation = mkOption { 810 type = types.str; 811 default = "/var/borgbackup"; 812 description = '' 813 The location where borg saves the backups. 814 This can be a local path or a remote location such as user@host:/path/to/repo. 815 It is exported and thus available as an environment variable to cmdPreexec and cmdPostexec. 816 ''; 817 }; 818 819 startAt = mkOption { 820 type = types.str; 821 default = "hourly"; 822 description = "When or how often the backup should run. Must be in the format described in systemd.time 7."; 823 }; 824 825 user = mkOption { 826 type = types.str; 827 default = "virtualMail"; 828 description = "The user borg and its launch script is run as."; 829 }; 830 831 group = mkOption { 832 type = types.str; 833 default = "virtualMail"; 834 description = "The group borg and its launch script is run as."; 835 }; 836 837 compression = { 838 method = mkOption { 839 type = types.nullOr (types.enum ["none" "lz4" "zstd" "zlib" "lzma"]); 840 default = null; 841 description = "Leaving this unset allows borg to choose. The default for borg 1.1.4 is lz4."; 842 }; 843 844 level = mkOption { 845 type = types.nullOr types.int; 846 default = null; 847 description = '' 848 Denotes the level of compression used by borg. 849 Most methods accept levels from 0 to 9 but zstd which accepts values from 1 to 22. 850 If null the decision is left up to borg. 851 ''; 852 }; 853 854 auto = mkOption { 855 type = types.bool; 856 default = false; 857 description = "Leaves it to borg to determine whether an individual file should be compressed."; 858 }; 859 }; 860 861 encryption = { 862 method = mkOption { 863 type = types.enum [ 864 "none" 865 "authenticated" 866 "authenticated-blake2" 867 "repokey" 868 "keyfile" 869 "repokey-blake2" 870 "keyfile-blake2" 871 ]; 872 default = "none"; 873 description = '' 874 The backup can be encrypted by choosing any other value than 'none'. 875 When using encryption the password / passphrase must be provided in passphraseFile. 876 ''; 877 }; 878 879 passphraseFile = mkOption { 880 type = types.nullOr types.path; 881 default = null; 882 description = "Path to a file containing the encryption password or passphrase."; 883 }; 884 }; 885 886 name = mkOption { 887 type = types.str; 888 default = "{hostname}-{user}-{now}"; 889 description = '' 890 The name of the individual backups as used by borg. 891 Certain placeholders will be replaced by borg. 892 ''; 893 }; 894 895 locations = mkOption { 896 type = types.listOf types.path; 897 default = [cfg.mailDirectory]; 898 description = "The locations that are to be backed up by borg."; 899 }; 900 901 extraArgumentsForInit = mkOption { 902 type = types.listOf types.str; 903 default = ["--critical"]; 904 description = "Additional arguments to add to the borg init command line."; 905 }; 906 907 extraArgumentsForCreate = mkOption { 908 type = types.listOf types.str; 909 default = [ ]; 910 description = "Additional arguments to add to the borg create command line e.g. '--stats'."; 911 }; 912 913 cmdPreexec = mkOption { 914 type = types.nullOr types.str; 915 default = null; 916 description = '' 917 The command to be executed before each backup operation. 918 This is called prior to borg init in the same script that runs borg init and create and cmdPostexec. 919 Example: 920 export BORG_RSH="ssh -i /path/to/private/key" 921 ''; 922 }; 923 924 cmdPostexec = mkOption { 925 type = types.nullOr types.str; 926 default = null; 927 description = '' 928 The command to be executed after each backup operation. 929 This is called after borg create completed successfully and in the same script that runs 930 cmdPreexec, borg init and create. 931 ''; 932 }; 933 934 }; 935 936 rebootAfterKernelUpgrade = { 937 enable = mkOption { 938 type = types.bool; 939 default = false; 940 example = true; 941 description = '' 942 Whether to enable automatic reboot after kernel upgrades. 943 This is to be used in conjunction with system.autoUpgrade.enable = true" 944 ''; 945 }; 946 method = mkOption { 947 type = types.enum [ "reboot" "systemctl kexec" ]; 948 default = "reboot"; 949 description = '' 950 Whether to issue a full "reboot" or just a "systemctl kexec"-only reboot. 951 It is recommended to use the default value because the quicker kexec reboot has a number of problems. 952 Also if your server is running in a virtual machine the regular reboot will already be very quick. 953 ''; 954 }; 955 }; 956 957 backup = { 958 enable = lib.mkEnableOption "backup via rsnapshot"; 959 960 snapshotRoot = mkOption { 961 type = types.path; 962 default = "/var/rsnapshot"; 963 description = '' 964 The directory where rsnapshot stores the backup. 965 ''; 966 }; 967 968 cmdPreexec = mkOption { 969 type = types.nullOr types.str; 970 default = null; 971 description = '' 972 The command to be executed before each backup operation. This is wrapped in a shell script to be called by rsnapshot. 973 ''; 974 }; 975 976 cmdPostexec = mkOption { 977 type = types.nullOr types.str; 978 default = null; 979 description = "The command to be executed after each backup operation. This is wrapped in a shell script to be called by rsnapshot."; 980 }; 981 982 retain = { 983 hourly = mkOption { 984 type = types.int; 985 default = 24; 986 description = "How many hourly snapshots are retained."; 987 }; 988 daily = mkOption { 989 type = types.int; 990 default = 7; 991 description = "How many daily snapshots are retained."; 992 }; 993 weekly = mkOption { 994 type = types.int; 995 default = 54; 996 description = "How many weekly snapshots are retained."; 997 }; 998 }; 999 1000 cronIntervals = mkOption { 1001 type = types.attrsOf types.str; 1002 default = { 1003 # minute, hour, day-in-month, month, weekday (0 = sunday) 1004 hourly = " 0 * * * *"; # Every full hour 1005 daily = "30 3 * * *"; # Every day at 3:30 1006 weekly = " 0 5 * * 0"; # Every sunday at 5:00 AM 1007 }; 1008 description = '' 1009 Periodicity at which intervals should be run by cron. 1010 Note that the intervals also have to exist in configuration 1011 as retain options. 1012 ''; 1013 }; 1014 }; 1015 }; 1016 1017 imports = [ 1018 ./borgbackup.nix 1019 ./debug.nix 1020 ./rsnapshot.nix 1021 ./clamav.nix 1022 ./monit.nix 1023 ./users.nix 1024 ./environment.nix 1025 ./networking.nix 1026 ./systemd.nix 1027 ./dovecot.nix 1028 ./opendkim.nix 1029 ./postfix.nix 1030 ./rspamd.nix 1031 ./nginx.nix 1032 ./kresd.nix 1033 ./post-upgrade-check.nix 1034 ]; 1035}