From 1c01ebffa950bf25911a092e7b46d556297ca541 Mon Sep 17 00:00:00 2001
From: Julien Moutinho <julm@sourcephile.fr>
Date: Mon, 5 Apr 2021 13:20:52 +0200
Subject: [PATCH] discourse: prepare postfix, postgresql and redis

---
 hosts/losurdo.nix                       |   4 +-
 hosts/losurdo/fail2ban.nix              |   2 +-
 hosts/losurdo/postfix.nix               | 318 ++++++++++++++++++++++++
 hosts/losurdo/postfix/sourcephile.nix   |  31 +++
 hosts/losurdo/postgresql.nix            |  27 +-
 hosts/losurdo/redis.nix                 |  39 +++
 hosts/mermet/postfix.nix                |  54 +---
 hosts/mermet/postfix/autogeree.net.nix  |   5 +-
 hosts/mermet/postfix/sourcephile.fr.nix |   7 +-
 nixos/defaults.nix                      |   1 +
 nixos/modules.nix                       |   1 +
 nixos/modules/services/mail/postfix.nix |  47 ++++
 12 files changed, 468 insertions(+), 68 deletions(-)
 create mode 100644 hosts/losurdo/postfix.nix
 create mode 100644 hosts/losurdo/postfix/sourcephile.nix
 create mode 100644 hosts/losurdo/redis.nix
 create mode 100644 nixos/modules/services/mail/postfix.nix

diff --git a/hosts/losurdo.nix b/hosts/losurdo.nix
index 72d6389..98baa27 100644
--- a/hosts/losurdo.nix
+++ b/hosts/losurdo.nix
@@ -32,7 +32,9 @@ modules = [
   losurdo/hardware.nix
   losurdo/networking.nix
   losurdo/nginx.nix
-  #losurdo/postgresql.nix
+  losurdo/postfix.nix
+  losurdo/postgresql.nix
+  losurdo/redis.nix
   losurdo/sanoid.nix
   losurdo/security.nix
   losurdo/syncoid.nix
diff --git a/hosts/losurdo/fail2ban.nix b/hosts/losurdo/fail2ban.nix
index 528695e..854c22a 100644
--- a/hosts/losurdo/fail2ban.nix
+++ b/hosts/losurdo/fail2ban.nix
@@ -1,7 +1,7 @@
 { pkgs, lib, config, hosts, ... }:
 {
 services.openssh.logLevel = "VERBOSE";
-services.postgresql.settings.log_line_prefix = "%h ";
+services.postgresql.logLinePrefix = "%h ";
 /*
 systemd.services.nftables.postStart = '' systemctl reload fail2ban '';
 */
diff --git a/hosts/losurdo/postfix.nix b/hosts/losurdo/postfix.nix
new file mode 100644
index 0000000..45fce73
--- /dev/null
+++ b/hosts/losurdo/postfix.nix
@@ -0,0 +1,318 @@
+{ pkgs, lib, config, ... }:
+let
+  inherit (lib) types;
+  inherit (config) networking users;
+  inherit (config.services) postfix;
+in
+{
+imports = [
+  #postfix/autogeree.net.nix
+  #postfix/sourcephile.fr.nix
+];
+users.groups.acme.members = [ postfix.user ];
+/*
+networking.nftables.ruleset = ''
+  add rule inet filter net2fw tcp dport 25 counter accept comment "SMTP"
+  add rule inet filter net2fw tcp dport 465 counter accept comment "submissions"
+  add rule inet filter fw2net meta skuid ${postfix.user} tcp dport 25 counter accept comment "SMTP"
+'';
+*/
+services.postfix = {
+  enable = true;
+  networksStyle = "host";
+  hostname ="${networking.hostName}.${networking.domain}";
+  domain = networking.domain;
+  origin = "$myhostname";
+  destination = [
+    "localhost"
+    "localhost.localdomain"
+    "$myhostname"
+  ];
+  postmasterAlias = "root";
+  rootAlias = "root@${networking.domain}";
+  sslKey = "/var/lib/acme/${networking.domain}/key.pem";
+  sslCert = "/var/lib/acme/${networking.domain}/fullchain.pem";
+  networks = [
+    "127.0.0.0/8"
+    "[::1]/128"
+  ];
+  setSendmail = true;
+  # Parse the extension in email address, eg. contact+extension@
+  recipientDelimiter = "+";
+  config = {
+    debug_peer_level = "4";
+    debug_peer_list = [
+      #"chomsky.autogeree.net"
+      #"localhost"
+      #"mail.sourcephile.fr"
+    ];
+
+    #
+    # Sending to the world
+    #
+    # Appending .domain is the MUA's job
+    append_dot_mydomain = false;
+    smtp_body_checks = "";
+    #smtp_cname_overrides_servername = false;
+    smtp_connect_timeout = "60s";
+    #smtp_header_checks = "regexp:/var/lib/postfix/smtp_header_checks";
+    smtp_mime_header_checks = "";
+    smtp_nested_header_checks = "";
+    smtp_tls_exclude_ciphers = [ "ADH" "MD5" "CAMELLIA" "SEED" "3DES" "DES" "RC4" "eNULL" "aNULL" ];
+    #smtp_tls_fingerprint_digest = "sha1";
+    smtp_tls_loglevel = "1";
+    #smtp_tls_note_starttls_offer = true;
+    #smtp_tls_policy_maps = "hash:/var/lib/postfix/conf/tls_policy";
+    # Only allow TLSv* protocols
+    smtp_tls_protocols = [ "!SSLv2" "!SSLv3" ];
+    #smtp_tls_scert_verifydepth = "5";
+    #smtp_tls_secure_cert_match = [ "nexthop" "dot-nexthop" ];
+    smtp_tls_security_level = "may";
+    smtp_tls_session_cache_database = "btree:$data_directory/smtp_tls_session_cache";
+    #smtp_tls_session_cache_timeout = "3600s";
+    #smtp_tls_verify_cert_match = "hostname";
+
+    #
+    # Receiving from the world
+    #
+    message_size_limit = "20480000";
+    maximal_queue_lifetime = "5d";
+    default_extra_recipient_limit = "5000";
+    line_length_limit = "2048";
+    duplicate_filter_limit = "5000";
+    # Stops mail from poorly written software
+    strict_rfc821_envelopes = true;
+    mime_header_checks = [];
+    milter_header_checks = [];
+    nested_header_checks = [];
+    body_checks = [];
+    content_filter = "";
+    permit_mx_backup_networks = [];
+    propagate_unmatched_extensions = [ "canonical" "virtual" "alias" ];
+    #masquerade_classes = [ "envelope_sender" "header_sender" "header_recipient" ];
+    #masquerade_domains = "";
+    #masquerade_exceptions = "root";
+    queue_minfree = "0";
+    # Stops some techniques used to harvest email addresses
+    disable_vrfy_command = true;
+    enable_long_queue_ids = false;
+    # Useful to test restrictions
+    smtpd_authorized_xclient_hosts = "127.0.0.1";
+    smtpd_banner = "$myhostname ESMTP $mail_name (NixOS)";
+    smtpd_client_connection_count_limit = "50";
+    smtpd_client_connection_rate_limit = "0";
+    smtpd_client_event_limit_exceptions = "$mynetworks";
+    smtpd_client_message_rate_limit = "0";
+    smtpd_client_new_tls_session_rate_limit = "0";
+    smtpd_client_port_logging = false;
+    smtpd_client_recipient_rate_limit = "0";
+    # Ban 5 sec on error
+    smtpd_error_sleep_time = "5";
+    # Needed to enforce reject_unknown_helo_hostname
+    smtpd_helo_required = true;
+    smtpd_helo_restrictions = [
+      "reject_invalid_helo_hostname"
+      "reject_non_fqdn_helo_hostname"
+      # Don't talk to mail systems that don't know their own hostname.
+      "reject_unknown_helo_hostname"
+      "permit"
+    ];
+    smtpd_client_restrictions = [
+    ];
+    # Set in postfix/*.nix and used in submissions/smptd
+    # with reject_sender_login_mismatch
+    smtpd_sender_login_maps = [];
+    smtpd_sender_restrictions = [
+      "reject_non_fqdn_sender"
+      "permit"
+    ];
+    smtpd_reject_unlisted_recipient = true;
+    # Check the RCPT TO, before smtpd_recipient_restrictions
+    # Restrictions based on what is allowed or not,
+    # these are applied before smtpd_recipient_restrictions
+    smtpd_relay_restrictions = [
+      "permit_mynetworks"
+      # Check the recipient's address in virtual_mailbox_domains and virtual_mailbox_maps
+      "permit_auth_destination"
+      # The world is only authorized to use our relay for the above destinations.
+      "reject"
+    ];
+    # Restrictions based on what is working or not
+    smtpd_recipient_restrictions = [
+      # Reject if the domain is not fully qualified
+      "reject_non_fqdn_recipient"
+      # Reject if the domain is not working, even before bothering to check the address
+      "reject_unknown_recipient_domain"
+      # Reject if the address is not working
+      # WARNING: this does not work if the recipient is greylisting.
+      # WARNING: verify(8) has a cache, dumpable if verify(8) is stopped, with:
+      # postmap -s btree:/var/lib/postfix/data/verify_cache
+      #"reject_unverified_recipient"
+      "permit"
+    ];
+    # Trust the verify database
+    #unverified_recipient_reject_code = "550";
+    smtpd_data_restrictions = [
+      # Force the smtpd's client to wait OK before sending
+      "reject_unauth_pipelining"
+      "permit"
+    ];
+    smtpd_end_of_data_restrictions = [
+      # Enforce mail volume quota via policy service callouts.
+      #check_policy_service unix:private/policy
+    ];
+    #smtpd_milters = "";
+    smtpd_peername_lookup = true;
+    smtpd_recipient_limit = "5000";
+    smtpd_recipient_overshoot_limit = "5000";
+    #smtpd_restriction_classes = "";
+    #smtpd_sasl_auth_enable = true;
+    #smtpd_sasl_path = "private/auth";
+    #smtpd_sasl_security_options = "noanonymous";
+    #smtpd_sasl_type = "dovecot";
+    smtpd_starttls_timeout = "300s";
+    #smtpd_tls_always_issue_session_ids = true;
+    #smtpd_tls_CApath = "/etc/postfix/x509/ca/";
+    smtpd_tls_ask_ccert = false;
+    #smtpd_tls_ccert_verifydepth = "5";
+    smtpd_tls_ciphers = "high";
+    smtpd_tls_eecdh_grade = "auto";
+    # Disable weak ciphers as reported by https://ssl-tools.net
+    # https://serverfault.com/questions/744168/how-to-disable-rc4-on-postfix
+    smtpd_tls_exclude_ciphers = [ "ADH" "MD5" "CAMELLIA" "SEED" "3DES" "DES" "RC4" "eNULL" "aNULL" ];
+    smtpd_tls_fingerprint_digest = "sha512";
+    # Log only a summary message on TLS handshake completion
+    smtpd_tls_loglevel = "1";
+    smtpd_tls_mandatory_ciphers = "high";
+    smtpd_tls_mandatory_protocols = [ "!SSLv2" "!SSLv3" ];
+    # Only allow TLSv*
+    smtpd_tls_protocols = [ "!SSLv2" "!SSLv3" ];
+    #smtpd_tls_received_header = false;
+    smtpd_tls_req_ccert = false;
+    # Postfix 2.3 and later
+    # encrypt
+    #  Mandatory TLS encryption: announce STARTTLS support to SMTP clients, and require that clients use TLS
+    #  encryption. According to [1720]RFC 2487 this MUST NOT be applied in case of a publicly-referenced
+    #  SMTP server. Instead, this option should be used only on dedicated servers.
+    smtpd_tls_security_level = "may";
+    smtpd_tls_session_cache_database = "btree:$data_directory/smtpd_tls_session_cache";
+    #smtpd_tls_session_cache_timeout = "3600s";
+    #smtpd_tls_chain_files =
+
+    relayhost = [];
+    #relay_clientcerts = hash:/var/lib/postfix/conf/relay_clientcerts
+    # This is where to put backup MX domains
+    relay_domains = [];
+    relay_recipient_maps = [];
+
+    # Use a non blocking source of randomness
+    tls_random_source = "dev:/dev/urandom";
+    # Map each domain to a specific X.509 certificate
+    tls_server_sni_maps = "hash:/run/keys/postfix-sni";
+
+    # Only explicitely aliased accounts have a mail, not all the passwd
+    local_recipient_maps = "$alias_maps";
+    # Note that the local transport rewrites the envelope recipient
+    # according to the alias_maps, and thus the aliasing is transparent
+    # to the nexthop (eg. dovecot)
+    #local_transport = local:$myhostname
+    # No console bell on new mail
+    biff = false;
+    forward_path = [
+      /*
+      "$home/.forward''${recipient_delimiter}''${extension}"
+      */
+      "$home/.forward"
+    ];
+
+    # Filled by the postfix/*.nix
+    virtual_mailbox_domains = [];
+    # Completed by the postfix/*.nix
+    virtual_mailbox_maps = [
+      "hash:/etc/postfix/virtual"
+    ];
+    #virtual_transport = "lmtp:unix:private/dovecot-lmtp";
+    /*
+    dovecot_destination_recipient_limit = "1";
+    virtual_transport = "dovecot";
+    */
+
+    # There is no fallback
+    fallback_transport = "";
+  };
+  virtualMapType = "hash";
+  masterConfig =
+    let
+      mkVal = value:
+        if lib.isList value
+        then lib.concatStringsSep "," value
+        else
+          if value == true then "yes"
+          else if value == false then "no"
+          else toString value;
+      mkKeyVal = opt: val: [ "-o" (opt + "=" + mkVal val) ];
+      mkArgs = args: lib.concatLists (lib.mapAttrsToList mkKeyVal args);
+    in {
+    pickup = {
+      args = mkArgs {
+        cleanup_service_name = "submissions-header-cleanup";
+      };
+    };
+    /*
+    # Implicit TLS on port 465
+    # https://tools.ietf.org/html/rfc8314#section-3.3
+    submissions = {
+      type = "inet";
+      private = false;
+      command = "smtpd";
+      args = mkArgs {
+        syslog_name = "postfix/submissions";
+        # Implicit TLS, not STARTTLS
+        smtpd_tls_wrappermode = true;
+        smtpd_tls_mandatory_protocols = [
+          "TLSv1.3"
+          # FIXME: to be removed when K-9 Mail will support TLSv1.3,
+          # K-9 Mail 5.600 does not.
+          "TLSv1.2"
+        ];
+        milter_macro_daemon_name = "ORIGINATING";
+        smtpd_helo_restrictions = [
+          "permit_sasl_authenticated"
+        ] ++ postfix.config.smtpd_helo_restrictions;
+        smtpd_relay_restrictions = [
+          # SASL authorizes to send to the world
+          "permit_sasl_authenticated"
+          "reject"
+        ];
+        smtpd_sasl_auth_enable = true;
+        smtpd_sasl_type = "dovecot";
+        smtpd_sasl_path = "private/auth";
+        smtpd_sasl_local_domain = "";
+        # Offer SASL authentication only after a TLS-encrypted session has been established
+        smtpd_tls_auth_only = true;
+        smtpd_sasl_tls_security_options = [ "noanonymous" ];
+        # Do not put SASL logins in mail headers
+        smtpd_sasl_authenticated_header = false;
+        # Who cares about (old) Outlook
+        broken_sasl_auth_clients = false;
+        smtpd_sender_restrictions = [
+          "reject_non_fqdn_sender"
+          # Check that the SASL user is using only its own
+          # mail addresses on the envelope, as indicated in smtpd_sender_login_maps
+          "reject_sender_login_mismatch"
+          "permit"
+        ];
+        # No X.509 certificates for users, for now
+        smtpd_tls_ask_ccert = false;
+        smtpd_tls_ccert_verifydepth = 0;
+        smtpd_tls_loglevel = 1;
+        smtpd_tls_req_ccert = false;
+        cleanup_service_name = "submissions-header-cleanup";
+      };
+    };
+    */
+  };
+  extraMasterConf = ''
+  '';
+};
+}
diff --git a/hosts/losurdo/postfix/sourcephile.nix b/hosts/losurdo/postfix/sourcephile.nix
new file mode 100644
index 0000000..36fe1a8
--- /dev/null
+++ b/hosts/losurdo/postfix/sourcephile.nix
@@ -0,0 +1,31 @@
+{ pkgs, lib, config, ... }:
+let domain = "sourcephile.fr"; in
+{
+services.postfix = {
+  extraAliases = ''
+  '';
+  virtual = ''
+    root@${domain} julm+root@${domain}
+  '';
+  tls_server_sni_maps =
+    let chain = [
+      "/var/lib/acme/${domain}/key.pem"
+      "/var/lib/acme/${domain}/fullchain.pem"
+    ]; in {
+    "smtp.${domain}" = chain;
+    "mail.${domain}" = chain;
+  };
+  config = {
+    virtual_mailbox_domains = [
+      domain
+    ];
+  };
+};
+security.acme.certs."${domain}" = {
+  postRun = "systemctl reload postfix";
+};
+systemd.services.postfix = {
+  wants = [ "acme-selfsigned-${domain}.service" "acme-${domain}.service"];
+  after = [ "acme-selfsigned-${domain}.service" ];
+};
+}
diff --git a/hosts/losurdo/postgresql.nix b/hosts/losurdo/postgresql.nix
index 3622384..33824c7 100644
--- a/hosts/losurdo/postgresql.nix
+++ b/hosts/losurdo/postgresql.nix
@@ -1,32 +1,37 @@
 { pkgs, lib, config, hostName, ... }:
 let
-  inherit (config) networking;
+  inherit (config.networking) domain;
   inherit (config.services) postgresql;
   inherit (config.users) users;
 in
 {
 imports =
+  [];
+  /*
   map (eta: (import postgresql/openconcerto.nix) eta) [
     {db = "openconcerto1";}
     {db = "openconcerto2";}
     {db = "lbec";}
     {db = "lbm";}
   ];
+  */
+/*
 networking.nftables.ruleset = ''
   add rule inet filter net2fw tcp dport 5432 counter accept comment "PostgreSQL"
 '';
+*/
 users.groups.acme.members = [ users."postgres".name ];
-security.acme.certs."${networking.domain}" = {
+security.acme.certs."${domain}" = {
   postRun = "systemctl reload postgresql";
 };
 systemd.services.postgresql = {
-  wants = [ "acme-selfsigned-${networking.domain}.service" "acme-${networking.domain}.service"];
-  after = [ "acme-selfsigned-${networking.domain}.service" ];
+  wants = [ "acme-selfsigned-${domain}.service" "acme-${domain}.service"];
+  after = [ "acme-selfsigned-${domain}.service" ];
 };
 services.postgresql = {
   enable = true;
-  package = pkgs.postgresql_9_6;
-  enableTCPIP = true;
+  package = pkgs.postgresql_12;
+  #enableTCPIP = true;
   settings = {
     # ZFS is Copy on Write (CoW). As a result, it’s not possible
     # to have a torn page because a page can’t be partially written
@@ -37,10 +42,10 @@ services.postgresql = {
     log_hostname = false;
     max_connections = 25;
     max_locks_per_transaction = 1024;
-    password_encryption = "on"; # FIXME: replace md5 by scram-sha-256, which requires postfix >= 11
-    ssl = true;
-    ssl_cert_file = "/var/lib/acme/${networking.domain}/fullchain.pem";
-    ssl_key_file = "/var/lib/acme/${networking.domain}/key.pem";
+    password_encryption = "scram-sha-256"; # requires postfix >= 11
+    ssl = false;
+    ssl_cert_file = "/var/lib/acme/${domain}/fullchain.pem";
+    ssl_key_file = "/var/lib/acme/${domain}/key.pem";
     unix_socket_permissions = "0770";
   };
   authentication = lib.mkForce ''
@@ -67,7 +72,7 @@ fileSystems."/var/lib/postgresql" = {
 services.syncoid.commands = {
   "${hostName}/var/postgresql" = {
     sendOptions = "raw";
-    target = "backup@mermet.${networking.domain}:rpool/backup/${hostName}/var/postgresql";
+    target = "backup@mermet.${domain}:rpool/backup/${hostName}/var/postgresql";
   };
 };
 services.sanoid.datasets = {
diff --git a/hosts/losurdo/redis.nix b/hosts/losurdo/redis.nix
new file mode 100644
index 0000000..5b1f904
--- /dev/null
+++ b/hosts/losurdo/redis.nix
@@ -0,0 +1,39 @@
+{ pkgs, lib, config, ... }:
+let
+  inherit (config.networking) hostName;
+  inherit (config.users) users;
+in
+{
+fileSystems."/var/lib/redis" = {
+  device = "${hostName}/var/redis";
+  fsType = "zfs";
+};
+
+services.redis = {
+  enable = true;
+  unixSocket = "/run/redis/redis.socket";
+  databases = 16;
+  syslog = true;
+  bind = "127.0.0.1 ::1";
+  settings = {
+    unixsocketperm = "660";
+    maxclients = 1024;
+    maxmemory = "500mb";
+    maxmemory-policy = "volatile-ttl";
+  };
+};
+
+users.users.redis.group = "redis";
+users.groups."redis" = {};
+
+systemd.services.redis.serviceConfig = {
+  RuntimeDirectoryMode = "0750";
+  Group = users.redis.group;
+};
+
+# As suggested by redis in its logs
+boot.kernel.sysctl."vm.overcommit_memory" = 1;
+system.activationScripts.redis = ''
+  echo never >/sys/kernel/mm/transparent_hugepage/enabled
+'';
+}
diff --git a/hosts/mermet/postfix.nix b/hosts/mermet/postfix.nix
index d7dec64..fc0eb00 100644
--- a/hosts/mermet/postfix.nix
+++ b/hosts/mermet/postfix.nix
@@ -1,38 +1,16 @@
 { pkgs, lib, config, ... }:
 let
-  inherit (builtins) attrNames concatStringsSep readFile toPath;
+  inherit (builtins) attrNames readFile toPath;
   inherit (lib) types;
-  inherit (pkgs.lib) loadFile unlines unwords unlinesAttrs;
   inherit (config) networking users;
   inherit (config.services) postfix dovecot2 openldap;
-  domains = [
-    "sourcephile.fr"
-    "autogeree.net"
-  ];
 in
 {
-imports = map (domain: (./postfix + "/${domain}.nix")) domains;
-options = {
-  services.postfix = {
-    tls_server_sni_maps = lib.mkOption {
-      type = types.attrsOf (types.listOf types.path);
-      default = {};
-      apply = m: pkgs.writeText "sni" (lib.concatStringsSep "\n" (lib.mapAttrsToList (domain: x509: ''
-        ${domain} ${unwords x509}
-      '') m));
-    };
-  };
-};
-config = {
+imports = [
+  postfix/autogeree.net.nix
+  postfix/sourcephile.fr.nix
+];
 users.groups.acme.members = [ postfix.user ];
-systemd.services.postfix = {
-  wants = ["openldap.service"];
-  after = ["openldap.service"];
-  preStart = ''
-    install -m 400 -o root -g root ${postfix.tls_server_sni_maps} /run/keys/postfix-sni
-    ${pkgs.postfix}/bin/postmap -F hash:/run/keys/postfix-sni
-  '';
-};
 networking.nftables.ruleset = ''
   add rule inet filter net2fw tcp dport 25 counter accept comment "SMTP"
   add rule inet filter net2fw tcp dport 465 counter accept comment "submissions"
@@ -266,7 +244,7 @@ services.postfix = {
     let
       mkVal = value:
         if lib.isList value
-        then concatStringsSep "," value
+        then lib.concatStringsSep "," value
         else
           if value == true then "yes"
           else if value == false then "no"
@@ -330,25 +308,6 @@ services.postfix = {
         cleanup_service_name = "submissions-header-cleanup";
       };
     };
-    submissions-header-cleanup = {
-      type = "unix";
-      private = false;
-      maxproc = 0;
-      command = "cleanup";
-      args = mkArgs {
-        header_checks = "pcre:" + pkgs.writeText "submission_header_cleanup_rules" ''
-          # Removes sensitive headers from mails handed in via the submission or smtps port.
-          # See https://thomas-leister.de/mailserver-debian-stretch/
-          # Uses "pcre" style regex.
-
-          /^Received:/         IGNORE
-          /^User-Agent:/       IGNORE
-          /^X-Enigmail:/       IGNORE
-          /^X-Mailer:/         IGNORE
-          /^X-Originating-IP:/ IGNORE
-        '';
-      };
-    };
   };
   extraMasterConf = ''
     #spfcheck    unix  -        n       n       -        0        spawn
@@ -434,5 +393,4 @@ services.postfix = {
    #noclue    unix  -       n       n       -       -       pipe
    #  flags=q user=noclue argv=/usr/local/bin/noclue-delivery ${recipient} ${sender}
 };
-};
 }
diff --git a/hosts/mermet/postfix/autogeree.net.nix b/hosts/mermet/postfix/autogeree.net.nix
index 9609404..cbdacfc 100644
--- a/hosts/mermet/postfix/autogeree.net.nix
+++ b/hosts/mermet/postfix/autogeree.net.nix
@@ -1,6 +1,5 @@
 { pkgs, lib, config, ... }:
 let
-  inherit (pkgs.lib) loadFile;
   domain = "autogeree.net";
   domainSuffix = "dc=autogeree,dc=net";
 in
@@ -62,7 +61,7 @@ security.acme.certs."${domain}" = {
   postRun = "systemctl reload postfix";
 };
 systemd.services.postfix = {
-  wants = [ "acme-selfsigned-${domain}.service" "acme-${domain}.service"];
-  after = [ "acme-selfsigned-${domain}.service" ];
+  wants = [ "openldap.service" "acme-selfsigned-${domain}.service" "acme-${domain}.service"];
+  after = [ "openldap.service" "acme-selfsigned-${domain}.service" ];
 };
 }
diff --git a/hosts/mermet/postfix/sourcephile.fr.nix b/hosts/mermet/postfix/sourcephile.fr.nix
index 8584a89..3e8b9a9 100644
--- a/hosts/mermet/postfix/sourcephile.fr.nix
+++ b/hosts/mermet/postfix/sourcephile.fr.nix
@@ -1,6 +1,5 @@
 { pkgs, lib, config, ... }:
 let
-  inherit (pkgs.lib) loadFile;
   domain = "sourcephile.fr";
   domainSuffix = "dc=sourcephile,dc=fr";
 in
@@ -16,7 +15,7 @@ services.postfix = {
     ecole@${domain}         public-inbox@localhost
     environnement@${domain} public-inbox@localhost
     labo@${domain}          public-inbox@localhost
-    hosts@${domain}      public-inbox@localhost
+    hosts@${domain}         public-inbox@localhost
     pont@${domain}          public-inbox@localhost
     test@${domain}          public-inbox@localhost
   '';
@@ -73,7 +72,7 @@ security.acme.certs."${domain}" = {
   postRun = "systemctl reload postfix";
 };
 systemd.services.postfix = {
-  wants = [ "acme-selfsigned-${domain}.service" "acme-${domain}.service"];
-  after = [ "acme-selfsigned-${domain}.service" ];
+  wants = [ "openldap.service" "acme-selfsigned-${domain}.service" "acme-${domain}.service"];
+  after = [ "openldap.service" "acme-selfsigned-${domain}.service" ];
 };
 }
diff --git a/nixos/defaults.nix b/nixos/defaults.nix
index 0c7ccaf..b8446c4 100644
--- a/nixos/defaults.nix
+++ b/nixos/defaults.nix
@@ -127,6 +127,7 @@ environment.systemPackages = with pkgs; [
   nethogs
   nload
   nmon
+  #ntop
   pv
   rdfind
   smem
diff --git a/nixos/modules.nix b/nixos/modules.nix
index 5e43a91..32e922e 100644
--- a/nixos/modules.nix
+++ b/nixos/modules.nix
@@ -5,6 +5,7 @@
 imports = [
   #modules/services/databases/openldap.nix
   modules/services/mail/public-inbox.nix
+  modules/services/mail/postfix.nix
   modules/services/networking/upnpc.nix
   #modules/services/security/tor.nix
   #modules/services/backup/syncoid.nix
diff --git a/nixos/modules/services/mail/postfix.nix b/nixos/modules/services/mail/postfix.nix
new file mode 100644
index 0000000..916dffa
--- /dev/null
+++ b/nixos/modules/services/mail/postfix.nix
@@ -0,0 +1,47 @@
+{ pkgs, lib, config, ... }:
+let
+  inherit (lib) types;
+  inherit (config.services) postfix;
+in
+{
+options = {
+  services.postfix = {
+    tls_server_sni_maps = lib.mkOption {
+      type = types.attrsOf (types.listOf types.path);
+      default = {};
+      apply = m: pkgs.writeText "sni" (lib.concatStringsSep "\n" (lib.mapAttrsToList (domain: x509: ''
+        ${domain} ${lib.concatStringsSep " " x509}
+      '') m));
+    };
+  };
+};
+config = {
+  systemd.services.postfix = {
+    preStart = ''
+      install -m 400 -o root -g root ${postfix.tls_server_sni_maps} /run/keys/postfix-sni
+      ${pkgs.postfix}/bin/postmap -F hash:/run/keys/postfix-sni
+    '';
+  };
+  services.postfix = {
+    masterConfig = {
+      submissions-header-cleanup = {
+        type = "unix";
+        private = false;
+        maxproc = 0;
+        command = "cleanup";
+        args = ["-o" ("header_checks=pcre:" + pkgs.writeText "submission_header_cleanup_rules" ''
+          # Removes sensitive headers from mails handed in via the submission or smtps port.
+          # See https://thomas-leister.de/mailserver-debian-stretch/
+          # Uses "pcre" style regex.
+
+          /^Received:/         IGNORE
+          /^User-Agent:/       IGNORE
+          /^X-Enigmail:/       IGNORE
+          /^X-Mailer:/         IGNORE
+          /^X-Originating-IP:/ IGNORE
+        '')];
+      };
+    };
+  };
+};
+}
-- 
2.47.2