From 4aa3e486409d46cfd68e0902a766bbc779a5d680 Mon Sep 17 00:00:00 2001 From: Yorick van Pelt Date: Mon, 19 Sep 2016 20:54:40 +0200 Subject: [PATCH] fix mail on pennyworth --- modules/mailz.nix | 181 +++++++++++--------------- modules/nginx.nix | 1 + pennyworth/configuration.nix | 25 +--- pennyworth/hardware-configuration.nix | 2 + 4 files changed, 89 insertions(+), 120 deletions(-) diff --git a/modules/mailz.nix b/modules/mailz.nix index bfabcba..b37a41f 100644 --- a/modules/mailz.nix +++ b/modules/mailz.nix @@ -14,24 +14,8 @@ with lib; let cfg = config.services.mailz; - - # Convert: - # - # { - # a = { aliases = [ "x", "y" ]; }; - # b = { aliases = [ "x" ]; }; - # } - # - # To: - # - # { - # x = [ "a" "b" ]; - # y = [ "a" ]; - # } - aliases = foldAttrs (user: users: [user] ++ users) [ ] - (flatten (flip mapAttrsToList cfg.users - (user: options: flip map options.aliases - (alias: { ${alias} = user; })))); + + alldomains = lib.concatLists (mapAttrsToList (n: usr: usr.domains) cfg.users); files = { credentials = pkgs.writeText "credentials" @@ -45,20 +29,8 @@ let (flip mapAttrsToList cfg.users (user: options: "${user}:${options.password}:::::"))); - recipients = pkgs.writeText "recipients" - (concatStringsSep "\n" - (flip concatMap cfg.domains (domain: - (map (user: "${user}@${domain}") - (attrNames cfg.users ++ flatten ((flip mapAttrsToList) cfg.users - (user: options: options.aliases))))))); - - aliases = pkgs.writeText "aliases" - (concatStringsSep "\n" - (flip mapAttrsToList aliases - (alias: users: "${alias} ${concatStringsSep "," users}"))); - domains = pkgs.writeText "domains" - (concatStringsSep "\n" cfg.domains); + (concatStringsSep "\n" alldomains); spamassassinSieve = pkgs.writeText "spamassassin.sieve" '' require "fileinto"; @@ -67,15 +39,9 @@ let } ''; - # From - regex = pkgs.writeText "filter-regex.conf" '' - helo ! ^\[ - helo ^\. - helo \.$ - helo ^[^\.]*$ - ''; }; + in { @@ -119,17 +85,15 @@ in description = "Size of the generated DKIM key."; }; - domains = mkOption { - type = types.listOf types.str; - description = "The domains to look for"; - example = ["example.com"]; + mainUser = mkOption { + example = "root"; + type = types.str; }; keydir = mkOption { type = types.str; description = "The place to look for the ssl key"; default = "${config.security.acme.directory}/${cfg.domain}"; - example = ["example.com"]; }; users = mkOption { @@ -147,19 +111,16 @@ in smtpctl encrypt. ''; }; - - aliases = mkOption { + domains = mkOption { type = types.listOf types.str; - default = [ ]; - example = [ "postmaster" ]; - description = "A list of aliases for this user."; + example = ["example.com"]; }; + }; example = { "foo" = { password = "encrypted"; - aliases = [ "postmaster" ]; }; "bar" = { password = "encrypted"; @@ -170,62 +131,69 @@ in }; config = mkIf (cfg.users != { }) { - nixpkgs.config.packageOverrides = pkgs: { - # opensmtpd = overrideDerivation pkgs.opensmtpd (oldAttrs: { - # # Needed to listen on both IPv4 and IPv6 - # patches = oldAttrs.patches ++ [ ./opensmtpd.diff ]; - # }); - opensmtpd-extras = pkgs.opensmtpd-extras.override { - # Needed to have PRNG working in chroot (for dkim-signer) - openssl = pkgs.libressl; - }; - }; - system.activationScripts.mailz = '' # Make sure SpamAssassin database is present - if ! [ -d /etc/spamassassin ]; then - cp -r ${pkgs.spamassassin}/share/spamassassin /etc - fi + #if ! [ -d /etc/spamassassin ]; then + # cp -r ${pkgs.spamassassin}/share/spamassassin /etc + #fi # Make sure a DKIM private key exist - if ! [ -d ${cfg.dkimDirectory}/${head cfg.domains} ]; then - mkdir -p ${cfg.dkimDirectory}/${head cfg.domains} - chmod 700 ${cfg.dkimDirectory}/${head cfg.domains} - ${pkgs.opendkim}/bin/opendkim-genkey --bits ${toString cfg.dkimBits} --domain ${head cfg.domains} --directory ${cfg.dkimDirectory}/${head cfg.domains} + if ! [ -d ${cfg.dkimDirectory} ]; then + mkdir -p ${cfg.dkimDirectory} + chmod 700 ${cfg.dkimDirectory} + chown ${config.services.rmilter.user} ${cfg.dkimDirectory} fi - ''; - - services.spamassassin.enable = true; - # it turns out that the dkim header domain does not have to match the from address - # but it would be a nice-to-have - services.opensmtpd = { + # Generate missing keys + '' + + (lib.concatMapStringsSep "\n" (domain: '' + if ! [ -e ${cfg.dkimDirectory}/${domain}.default.key ]; then + ${pkgs.opendkim}/bin/opendkim-genkey --bits ${toString cfg.dkimBits} --domain ${domain} --directory ${cfg.dkimDirectory} --selector default + mv ${cfg.dkimDirectory}/default.private ${cfg.dkimDirectory}/${domain}.default.key + mv ${cfg.dkimDirectory}/default.txt ${cfg.dkimDirectory}/${domain}.default.txt + chown ${config.services.rmilter.user} ${cfg.dkimDirectory}/${domain}.default.* + fi + '') alldomains); + services.rspamd.enable = true; + services.rmilter = { enable = true; - serverConfiguration = '' - filter filter-pause pause - filter filter-regex regex "${files.regex}" - filter filter-spamassassin spamassassin "-saccept" - filter filter-dkim-signer dkim-signer "-d${head cfg.domains}" "-p${cfg.dkimDirectory}/${head cfg.domains}/default.private" - filter in chain filter-pause filter-regex filter-spamassassin - filter out chain filter-dkim-signer - - pki ${cfg.domain} certificate "${cfg.keydir}/fullchain.pem" - pki ${cfg.domain} key "${cfg.keydir}/key.pem" - - table credentials file:${files.credentials} - table recipients file:${files.recipients} - table aliases file:${files.aliases} - table domains file:${files.domains} - - listen on 0.0.0.0 port 25 hostname ${cfg.domain} filter in tls pki ${cfg.domain} - #listen on :: port 25 hostname ${cfg.domain} filter in tls pki ${cfg.domain} - listen on 0.0.0.0 port 587 hostname ${cfg.domain} filter out tls-require pki ${cfg.domain} auth - #listen on :: port 587 hostname ${cfg.domain} filter out tls-require pki ${cfg.domain} auth - enqueuer filter out - - accept from any for domain recipient alias deliver to lmtp localhost:24 - accept from local for any relay + socketActivation = false; + #debug = true; + rspamd.enable = true; + postfix.enable = true; + extraConfig = '' + dkim { + domain { + key = ${cfg.dkimDirectory}; + domain = "*"; + selector = "default"; + }; + header_canon = relaxed; + body_canon = relaxed; + sign_alg = sha256; + }; ''; - procPackages = [ pkgs.opensmtpd-extras ]; + }; + + services.postfix = { + enable = true; + destination = alldomains ++ ["$myhostname" "localhost.$mydomain" "$mydomain" "localhost"]; + sslCert = "${cfg.keydir}/fullchain.pem"; + sslKey = "${cfg.keydir}/key.pem"; + postmasterAlias = cfg.mainUser; + enableSubmission = true; + virtual = lib.concatStringsSep "\n" (lib.mapAttrsToList (name: usr: + lib.concatMapStringsSep "\n" (dom: "@${dom} ${name}") usr.domains) cfg.users); + extraConfig = '' + mailbox_transport = lmtp:unix:dovecot-lmtp + ''; + submissionOptions = { + "smtpd_tls_security_level" = "encrypt"; + "smtpd_sasl_auth_enable" = "yes"; + "smtpd_sasl_type" = "dovecot"; + "smtpd_sasl_path" = "/var/lib/postfix/auth"; + "smtpd_client_restrictions" = "permit_sasl_authenticated,reject"; + #"milter_macro_daemon_name" = "ORIGINATING"; + }; }; services.dovecot2 = { @@ -241,12 +209,21 @@ in enablePAM = false; sieveScripts = { before = files.spamassassinSieve; }; extraConfig = '' - postmaster_address = postmaster@${head cfg.domains} + postmaster_address = postmaster@${head alldomains} service lmtp { - inet_listener lmtp { - address = 127.0.0.1 ::1 - port = 24 + unix_listener /var/lib/postfix/queue/dovecot-lmtp { + mode = 0660 + user = postfix + group = postfix + } + } + service auth { + unix_listener /var/lib/postfix/auth { + mode = 0660 + # Assuming the default Postfix user and group + user = postfix + group = postfix } } diff --git a/modules/nginx.nix b/modules/nginx.nix index 443c87b..d9e28c1 100644 --- a/modules/nginx.nix +++ b/modules/nginx.nix @@ -156,6 +156,7 @@ in mkdir -p /etc/nginx/ ${pkgs.openssl}/bin/openssl dhparam -out /etc/nginx/dhparam.pem 2048 fi + # self-sign certs in case an invalid vhost is looked up dir=${cfg.no_vhost_keydir} mkdir -m 0700 -p $dir if ! [[ -e $dir/key.pem ]]; then diff --git a/pennyworth/configuration.nix b/pennyworth/configuration.nix index f869db1..85825f2 100644 --- a/pennyworth/configuration.nix +++ b/pennyworth/configuration.nix @@ -23,6 +23,8 @@ in networking.hostName = secrets.hostnames.pennyworth; + services.nixosManual.enable = false; + environment.noXlibs = true; services.openssh.enable = true; @@ -37,15 +39,14 @@ in services.mailz = { domain = config.networking.hostName; keydir = acmeKeyDir; - domains = secrets.email_domains; + mainUser = "yorick"; users = { - yorick = { - password = secrets.yorick_mailPassword; - aliases = ["postmaster" "me" "ik" "info" "~"]; + yorick = with secrets; { + password = yorick_mailPassword; + domains = email_domains; }; }; }; - # website + lets encrypt challenge hosting nginxssl = { enable = true; @@ -63,6 +64,7 @@ in # Let's Encrypt configuration. + security.acme.preliminarySelfsigned = true; security.acme.certs."yori.cc" = { email = secrets.email; extraDomains = { @@ -73,19 +75,6 @@ in systemctl restart prosody.service ''; }; - # Generate a dummy self-signed certificate until we get one from - # Let's Encrypt. - system.activationScripts.letsEncryptKeys = - '' - dir=${acmeKeyDir} - mkdir -m 0700 -p $dir - if ! [[ -e $dir/key.pem ]]; then - ${pkgs.openssl}/bin/openssl genrsa -passout pass:foo -des3 -out $dir/key-in.pem 1024 - ${pkgs.openssl}/bin/openssl req -passin pass:foo -new -key $dir/key-in.pem -out $dir/key.csr \ - -subj "/C=NL/CN=www.example.com" - ${pkgs.openssl}/bin/openssl rsa -passin pass:foo -in $dir/key-in.pem -out $dir/key.pem - ${pkgs.openssl}/bin/openssl x509 -req -days 365 -in $dir/key.csr -signkey $dir/key.pem -out $dir/fullchain.pem - fi ''; # hidden SSH service diff --git a/pennyworth/hardware-configuration.nix b/pennyworth/hardware-configuration.nix index 7bd23d9..fd492f4 100644 --- a/pennyworth/hardware-configuration.nix +++ b/pennyworth/hardware-configuration.nix @@ -21,9 +21,11 @@ in path = "/old-root/boot"; devices = ["nodev"]; }]; + splashImage = null; }; initrd.availableKernelModules = [ "xen_blkfront" ]; }; + sound.enable = false; networking = { usePredictableInterfaceNames = false; # only eth0 interfaces.eth0 = {