summary refs log tree commit diff
path: root/nixos/modules/services/mail/postfix.nix
diff options
context:
space:
mode:
Diffstat (limited to 'nixos/modules/services/mail/postfix.nix')
-rw-r--r--nixos/modules/services/mail/postfix.nix405
1 files changed, 405 insertions, 0 deletions
diff --git a/nixos/modules/services/mail/postfix.nix b/nixos/modules/services/mail/postfix.nix
new file mode 100644
index 00000000000..2633289b46d
--- /dev/null
+++ b/nixos/modules/services/mail/postfix.nix
@@ -0,0 +1,405 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.postfix;
+  user = cfg.user;
+  group = cfg.group;
+  setgidGroup = cfg.setgidGroup;
+
+  mainCf =
+    ''
+      queue_directory = /var/postfix/queue
+      command_directory = ${pkgs.postfix}/sbin
+      daemon_directory = ${pkgs.postfix}/libexec/postfix
+
+      mail_owner = ${user}
+      default_privs = nobody
+
+    ''
+    + optionalString config.networking.enableIPv6 ''
+      inet_protocols = all
+    ''
+    + (if cfg.networks != null then
+        ''
+          mynetworks = ${concatStringsSep ", " cfg.networks}
+        ''
+      else if cfg.networksStyle != "" then
+        ''
+          mynetworks_style = ${cfg.networksStyle}
+        ''
+      else
+        # Postfix default is subnet, but let's play safe
+        ''
+          mynetworks_style = host
+        '')
+    + optionalString (cfg.hostname != "") ''
+      myhostname = ${cfg.hostname}
+    ''
+    + optionalString (cfg.domain != "") ''
+      mydomain = ${cfg.domain}
+    ''
+    + optionalString (cfg.origin != "") ''
+      myorigin = ${cfg.origin}
+    ''
+    + optionalString (cfg.destination != null) ''
+      mydestination = ${concatStringsSep ", " cfg.destination}
+    ''
+    + optionalString (cfg.relayDomains != null) ''
+      relay_domains = ${concatStringsSep ", " cfg.relayDomains}
+    ''
+    + ''
+      local_recipient_maps =
+
+      relayhost = ${if cfg.lookupMX || cfg.relayHost == "" then
+          cfg.relayHost
+        else
+          "[" + cfg.relayHost + "]"}
+
+      alias_maps = hash:/var/postfix/conf/aliases
+
+      mail_spool_directory = /var/spool/mail/
+
+      setgid_group = ${setgidGroup}
+    ''
+    + optionalString (cfg.sslCert != "") ''
+
+      smtp_tls_CAfile = ${cfg.sslCACert}
+      smtp_tls_cert_file = ${cfg.sslCert}
+      smtp_tls_key_file = ${cfg.sslKey}
+
+      smtp_use_tls = yes
+
+      smtpd_tls_CAfile = ${cfg.sslCACert}
+      smtpd_tls_cert_file = ${cfg.sslCert}
+      smtpd_tls_key_file = ${cfg.sslKey}
+
+      smtpd_use_tls = yes
+
+      recipientDelimiter = ${cfg.recipientDelimiter}
+    ''
+    + optionalString (cfg.virtual != "") ''
+      virtual_alias_maps = hash:/etc/postfix/virtual
+    ''
+    + cfg.extraConfig;
+
+  masterCf = ''
+    # ==========================================================================
+    # service type  private unpriv  chroot  wakeup  maxproc command + args
+    #               (yes)   (yes)   (yes)   (never) (100)
+    # ==========================================================================
+    smtp      inet  n       -       n       -       -       smtpd
+    #submission inet n       -       n       -       -       smtpd
+    #  -o smtpd_tls_security_level=encrypt
+    #  -o smtpd_sasl_auth_enable=yes
+    #  -o smtpd_client_restrictions=permit_sasl_authenticated,reject
+    #  -o milter_macro_daemon_name=ORIGINATING
+    pickup    fifo  n       -       n       60      1       pickup
+    cleanup   unix  n       -       n       -       0       cleanup
+    qmgr      fifo  n       -       n       300     1       qmgr
+    tlsmgr    unix  -       -       n       1000?   1       tlsmgr
+    rewrite   unix  -       -       n       -       -       trivial-rewrite
+    bounce    unix  -       -       n       -       0       bounce
+    defer     unix  -       -       n       -       0       bounce
+    trace     unix  -       -       n       -       0       bounce
+    verify    unix  -       -       n       -       1       verify
+    flush     unix  n       -       n       1000?   0       flush
+    proxymap  unix  -       -       n       -       -       proxymap
+    proxywrite unix -       -       n       -       1       proxymap
+    smtp      unix  -       -       n       -       -       smtp
+    relay     unix  -       -       n       -       -       smtp
+    	      -o smtp_fallback_relay=
+    #       -o smtp_helo_timeout=5 -o smtp_connect_timeout=5
+    showq     unix  n       -       n       -       -       showq
+    error     unix  -       -       n       -       -       error
+    retry     unix  -       -       n       -       -       error
+    discard   unix  -       -       n       -       -       discard
+    local     unix  -       n       n       -       -       local
+    virtual   unix  -       n       n       -       -       virtual
+    lmtp      unix  -       -       n       -       -       lmtp
+    anvil     unix  -       -       n       -       1       anvil
+    scache    unix  -       -       n       -       1       scache
+    ${cfg.extraMasterConf}
+  '';
+
+  aliases =
+    optionalString (cfg.postmasterAlias != "") ''
+      postmaster: ${cfg.postmasterAlias}
+    ''
+    + optionalString (cfg.rootAlias != "") ''
+      root: ${cfg.rootAlias}
+    ''
+    + cfg.extraAliases
+  ;
+
+  aliasesFile = pkgs.writeText "postfix-aliases" aliases;
+  virtualFile = pkgs.writeText "postfix-virtual" cfg.virtual;
+  mainCfFile = pkgs.writeText "postfix-main.cf" mainCf;
+  masterCfFile = pkgs.writeText "postfix-master.cf" masterCf;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.postfix = {
+
+      enable = mkOption {
+        default = false;
+        description = "Whether to run the Postfix mail server.";
+      };
+
+      setSendmail = mkOption {
+        default = true;
+        description = "Whether to set the system sendmail to postfix's.";
+      };
+
+      user = mkOption {
+        default = "postfix";
+        description = "What to call the Postfix user (must be used only for postfix).";
+      };
+
+      group = mkOption {
+        default = "postfix";
+        description = "What to call the Postfix group (must be used only for postfix).";
+      };
+
+      setgidGroup = mkOption {
+        default = "postdrop";
+        description = "
+          How to call postfix setgid group (for postdrop). Should
+          be uniquely used group.
+        ";
+      };
+
+      networks = mkOption {
+        default = null;
+        example = ["192.168.0.1/24"];
+        description = "
+          Net masks for trusted - allowed to relay mail to third parties -
+          hosts. Leave empty to use mynetworks_style configuration or use
+          default (localhost-only).
+        ";
+      };
+
+      networksStyle = mkOption {
+        default = "";
+        description = "
+          Name of standard way of trusted network specification to use,
+          leave blank if you specify it explicitly or if you want to use
+          default (localhost-only).
+        ";
+      };
+
+      hostname = mkOption {
+        default = "";
+        description ="
+          Hostname to use. Leave blank to use just the hostname of machine.
+          It should be FQDN.
+        ";
+      };
+
+      domain = mkOption {
+        default = "";
+        description ="
+          Domain to use. Leave blank to use hostname minus first component.
+        ";
+      };
+
+      origin = mkOption {
+        default = "";
+        description ="
+          Origin to use in outgoing e-mail. Leave blank to use hostname.
+        ";
+      };
+
+      destination = mkOption {
+        default = null;
+        example = ["localhost"];
+        description = "
+          Full (!) list of domains we deliver locally. Leave blank for
+          acceptable Postfix default.
+        ";
+      };
+
+      relayDomains = mkOption {
+        default = null;
+        example = ["localdomain"];
+        description = "
+          List of domains we agree to relay to. Default is the same as
+          destination.
+        ";
+      };
+
+      relayHost = mkOption {
+        default = "";
+        description = "
+          Mail relay for outbound mail.
+        ";
+      };
+
+      lookupMX = mkOption {
+        default = false;
+        description = "
+          Whether relay specified is just domain whose MX must be used.
+        ";
+      };
+
+      postmasterAlias = mkOption {
+        default = "root";
+        description = "Who should receive postmaster e-mail.";
+      };
+
+      rootAlias = mkOption {
+        default = "";
+        description = "
+          Who should receive root e-mail. Blank for no redirection.
+        ";
+      };
+
+      extraAliases = mkOption {
+        default = "";
+        description = "
+          Additional entries to put verbatim into aliases file.
+        ";
+      };
+
+      extraConfig = mkOption {
+        default = "";
+        description = "
+          Extra lines to be added verbatim to the main.cf configuration file.
+        ";
+      };
+
+      sslCert = mkOption {
+        default = "";
+        description = "SSL certificate to use.";
+      };
+
+      sslCACert = mkOption {
+        default = "";
+        description = "SSL certificate of CA.";
+      };
+
+      sslKey = mkOption {
+        default = "";
+        description = "SSL key to use.";
+      };
+
+      recipientDelimiter = mkOption {
+        default = "";
+        example = "+";
+        description = "
+          Delimiter for address extension: so mail to user+test can be handled by ~user/.forward+test
+        ";
+      };
+
+      virtual = mkOption {
+        default = "";
+        description = "
+          Entries for the virtual alias map.
+        ";
+      };
+
+      extraMasterConf = mkOption {
+        default = "";
+        example = "submission inet n - n - - smtpd";
+        description = "Extra lines to append to the generated master.cf file.";
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.postfix.enable {
+
+    environment = {
+      etc = singleton
+        { source = "/var/postfix/conf";
+          target = "postfix";
+        };
+
+      # This makes comfortable for root to run 'postqueue' for example.
+      systemPackages = [ pkgs.postfix ];
+    };
+
+    services.mail.sendmailSetuidWrapper = mkIf config.services.postfix.setSendmail {
+      program = "sendmail";
+      source = "${pkgs.postfix}/bin/sendmail";
+      owner = "nobody";
+      group = "postdrop";
+      setuid = false;
+      setgid = true;
+    };
+
+    users.extraUsers = singleton
+      { name = user;
+        description = "Postfix mail server user";
+        uid = config.ids.uids.postfix;
+        group = group;
+      };
+
+    users.extraGroups =
+      [ { name = group;
+          gid = config.ids.gids.postfix;
+        }
+        { name = setgidGroup;
+          gid = config.ids.gids.postdrop;
+        }
+      ];
+
+    jobs.postfix =
+      # I copy _lots_ of shipped configuration filed
+      # that can be left as is. I am afraid the exact
+      # will list slightly change in next Postfix
+      # release, so listing them all one-by-one in an
+      # accurate way is unlikely to be better.
+      { description = "Postfix mail server";
+
+        wantedBy = [ "multi-user.target" ];
+        after = [ "network.target" ];
+
+        daemonType = "fork";
+
+        preStart =
+          ''
+            if ! [ -d /var/spool/postfix ]; then
+              ${pkgs.coreutils}/bin/mkdir -p /var/spool/mail /var/postfix/conf /var/postfix/queue
+            fi
+
+            ${pkgs.coreutils}/bin/chown -R ${user}:${group} /var/postfix
+            ${pkgs.coreutils}/bin/chown -R ${user}:${setgidGroup} /var/postfix/queue
+            ${pkgs.coreutils}/bin/chmod -R ug+rwX /var/postfix/queue
+            ${pkgs.coreutils}/bin/chown root:root /var/spool/mail
+            ${pkgs.coreutils}/bin/chmod a+rwxt /var/spool/mail
+
+            ln -sf "${pkgs.postfix}/share/postfix/conf/"* /var/postfix/conf
+
+            ln -sf ${aliasesFile} /var/postfix/conf/aliases
+            ln -sf ${virtualFile} /var/postfix/conf/virtual
+            ln -sf ${mainCfFile} /var/postfix/conf/main.cf
+            ln -sf ${masterCfFile} /var/postfix/conf/master.cf
+
+            ${pkgs.postfix}/sbin/postalias -c /var/postfix/conf /var/postfix/conf/aliases
+            ${pkgs.postfix}/sbin/postmap -c /var/postfix/conf /var/postfix/conf/virtual
+
+            ${pkgs.postfix}/sbin/postfix -c /var/postfix/conf start
+          '';
+
+        preStop = ''
+            ${pkgs.postfix}/sbin/postfix -c /var/postfix/conf stop
+        '';
+
+      };
+
+  };
+
+}