Simon Bruder
242a2315be
This still allows requesting a DSN over submission, so trusted clients are not affected. It only affects sending DSN to other systems, which now no longer takes place. This is done to avoid leaking rspamd internals.
189 lines
5.5 KiB
Nix
189 lines
5.5 KiB
Nix
# SPDX-FileCopyrightText: 2021-2023 Simon Bruder <simon@sbruder.de>
|
||
#
|
||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||
|
||
{ config, lib, pkgs, ... }:
|
||
let
|
||
cfg = config.sbruder.mailserver;
|
||
|
||
listToString = lib.concatStringsSep ",";
|
||
|
||
# List of attribute sets with single key-value pair
|
||
plainAliases = (lib.flatten
|
||
(map
|
||
({ address, aliases, ... }:
|
||
map
|
||
(alias: { "${alias}" = address; })
|
||
(aliases ++ lib.singleton address))
|
||
cfg.users));
|
||
|
||
# Attribute set with every alias mapped to a list of receivers
|
||
mergedAliases = (lib.attrsets.foldAttrs
|
||
(val: col: lib.singleton val ++ col)
|
||
[ ]
|
||
plainAliases);
|
||
|
||
# Contents of the aliases file
|
||
aliasesString = (lib.concatStringsSep
|
||
"\n"
|
||
(lib.mapAttrsToList
|
||
(alias: addresses: "${alias} ${listToString addresses}")
|
||
mergedAliases));
|
||
|
||
valiases = pkgs.writeText "valiases" aliasesString;
|
||
|
||
submissionHeaderCleanupRules = pkgs.writeText "submission_header_cleanup_rules"
|
||
(lib.concatMapStringsSep
|
||
"\n"
|
||
(regex: "${regex} IGNORE")
|
||
cfg.cleanHeaders);
|
||
in
|
||
lib.mkIf cfg.enable {
|
||
security.dhparams.params.postfix = { };
|
||
services.postfix = {
|
||
enable = true;
|
||
|
||
enableSubmission = true; # plain/STARTTLS (latter is forced in submissionOptions)
|
||
enableSubmissions = true; # submission with implicit TLS (TCP/465)
|
||
|
||
hostname = cfg.fqdn;
|
||
networksStyle = "host";
|
||
sslCert = "${cfg.certDir}/fullchain.pem";
|
||
sslKey = "${cfg.certDir}/key.pem";
|
||
|
||
recipientDelimiter = "+";
|
||
|
||
mapFiles = {
|
||
inherit valiases;
|
||
};
|
||
|
||
config = {
|
||
# General
|
||
smtpd_banner = "${cfg.fqdn} ESMTP NO UCE";
|
||
disable_vrfy_command = true; # disable check if mailbox exists
|
||
enable_long_queue_ids = true; # better for debugging
|
||
strict_rfc821_envelopes = true; # only accept properly formatted envelope
|
||
message_size_limit = "50331648"; # 48 MiB
|
||
|
||
virtual_mailbox_domains = listToString cfg.domains;
|
||
virtual_mailbox_maps = "hash:/var/lib/postfix/conf/valiases";
|
||
virtual_alias_maps = "hash:/var/lib/postfix/conf/valiases";
|
||
virtual_transport = "lmtp:unix:/run/dovecot2/dovecot-lmtp";
|
||
|
||
smtpd_recipient_restrictions = listToString [
|
||
"reject_non_fqdn_recipient"
|
||
"reject_rbl_client ix.dnsbl.manitu.net"
|
||
"reject_unknown_recipient_domain"
|
||
"reject_unverified_recipient"
|
||
];
|
||
|
||
smtpd_client_restrictions = listToString [
|
||
"reject_rbl_client ix.dnsbl.manitu.net"
|
||
"reject_unknown_client_hostname"
|
||
];
|
||
|
||
smtpd_sender_restrictions = listToString [
|
||
"reject_non_fqdn_sender"
|
||
"reject_unknown_sender_domain"
|
||
];
|
||
|
||
# generated 2021-02-04, Mozilla Guideline v5.6, Postfix 3.5.6, OpenSSL 1.1.1i, intermediate configuration
|
||
# https://ssl-config.mozilla.org/#server=postfix&version=3.5.6&config=intermediate&openssl=1.1.1i&guideline=5.6
|
||
smtpd_tls_security_level = "may";
|
||
smtpd_tls_auth_only = "yes";
|
||
smtpd_tls_mandatory_protocols = "!SSLv2, !SSLv3, !TLSv1, !TLSv1.1";
|
||
smtpd_tls_protocols = "!SSLv2, !SSLv3, !TLSv1, !TLSv1.1";
|
||
smtpd_tls_mandatory_ciphers = "medium";
|
||
smtpd_tls_loglevel = "1";
|
||
smtpd_tls_received_header = "yes"; # add TLS connection details to Received header
|
||
|
||
tls_medium_cipherlist = listToString [
|
||
"ECDHE-ECDSA-AES128-GCM-SHA256"
|
||
"ECDHE-RSA-AES128-GCM-SHA256"
|
||
"ECDHE-ECDSA-AES256-GCM-SHA384"
|
||
"ECDHE-RSA-AES256-GCM-SHA384"
|
||
"ECDHE-ECDSA-CHACHA20-POLY1305"
|
||
"ECDHE-RSA-CHACHA20-POLY1305"
|
||
"DHE-RSA-AES128-GCM-SHA256"
|
||
"DHE-RSA-AES256-GCM-SHA384"
|
||
];
|
||
tls_preempt_cipherlist = "no";
|
||
|
||
smtpd_tls_dh1024_param_file = config.security.dhparams.params.postfix.path;
|
||
};
|
||
|
||
# plain/STARTTLS (forced with smtpd_tls_security_level)
|
||
submissionOptions = {
|
||
smtpd_tls_security_level = "encrypt";
|
||
smtpd_sasl_auth_enable = "yes";
|
||
smtpd_sasl_type = "dovecot";
|
||
smtpd_sasl_path = "/run/dovecot2/auth";
|
||
|
||
smtpd_sender_login_maps = "hash:/etc/postfix/valiases";
|
||
|
||
smtpd_recipient_restrictions = listToString [ ];
|
||
|
||
smtpd_client_restrictions = listToString [
|
||
"permit_sasl_authenticated"
|
||
"reject"
|
||
];
|
||
|
||
smtpd_sender_restrictions = listToString [
|
||
"reject_sender_login_mismatch"
|
||
];
|
||
|
||
cleanup_service_name = "submission-header-cleanup";
|
||
};
|
||
# implicit TLS
|
||
submissionsOptions = config.services.postfix.submissionOptions;
|
||
|
||
masterConfig = {
|
||
# Postscreen
|
||
smtpd = {
|
||
type = "pass";
|
||
args = [ "-o" "smtpd_discard_ehlo_keywords=silent-discard,dsn" ];
|
||
};
|
||
smtp_inet = {
|
||
# Partially overrides upstream
|
||
name = "smtp";
|
||
type = "inet";
|
||
private = false;
|
||
command = lib.mkForce "postscreen";
|
||
maxproc = 1;
|
||
};
|
||
tlsproxy = {
|
||
maxproc = 0;
|
||
};
|
||
dnsblog = {
|
||
maxproc = 0;
|
||
};
|
||
|
||
# Heder cleanup
|
||
submission-header-cleanup = {
|
||
private = false;
|
||
maxproc = 0;
|
||
command = "cleanup";
|
||
args = [ "-o" "header_checks=pcre:${submissionHeaderCleanupRules}" ];
|
||
};
|
||
};
|
||
};
|
||
|
||
networking.firewall.allowedTCPPorts = [
|
||
25 # SMTP
|
||
587 # SMTP submission
|
||
465 # SMTP submission (implicit TLS)
|
||
];
|
||
|
||
systemd.services.postfix = {
|
||
wants = [ "acme-finished-${cfg.fqdn}.target" ];
|
||
requires = [ "dovecot2.service" ];
|
||
after = [ "acme-finished-${cfg.fqdn}.target" "dovecot2.service" ];
|
||
};
|
||
|
||
security.acme.certs."${cfg.fqdn}".postRun = ''
|
||
if systemctl is-active postfix; then
|
||
systemctl --no-block reload postfix
|
||
fi
|
||
'';
|
||
}
|