nixos-config/modules/mailserver/postfix.nix
Simon Bruder 242a2315be
mailserver: Disallow requesting DSN over SMTP
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.
2024-02-03 01:15:17 +01:00

189 lines
5.5 KiB
Nix
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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"; # 48MiB
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
'';
}