Merge branch 'cleanup' into 'master'
treewide: remove global `with lib` and overly broad `with cfg` See merge request simple-nixos-mailserver/nixos-mailserver!416
This commit is contained in:
commit
67b0a7e946
8 changed files with 723 additions and 721 deletions
53
default.nix
53
default.nix
|
@ -21,9 +21,20 @@
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
|
|
||||||
with lib;
|
|
||||||
|
|
||||||
let
|
let
|
||||||
|
inherit (lib)
|
||||||
|
literalExpression
|
||||||
|
literalMD
|
||||||
|
mkDefault
|
||||||
|
mkEnableOption
|
||||||
|
mkOption
|
||||||
|
mkOptionType
|
||||||
|
mkRemovedOptionModule
|
||||||
|
mkRenamedOptionModule
|
||||||
|
types
|
||||||
|
warn
|
||||||
|
;
|
||||||
|
|
||||||
cfg = config.mailserver;
|
cfg = config.mailserver;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
|
@ -269,7 +280,7 @@ in
|
||||||
tlsCAFile = mkOption {
|
tlsCAFile = mkOption {
|
||||||
type = types.path;
|
type = types.path;
|
||||||
default = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
|
default = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
|
||||||
defaultText = lib.literalMD "see [source](https://gitlab.com/simple-nixos-mailserver/nixos-mailserver/-/blob/master/default.nix)";
|
defaultText = literalMD "see [source](https://gitlab.com/simple-nixos-mailserver/nixos-mailserver/-/blob/master/default.nix)";
|
||||||
description = ''
|
description = ''
|
||||||
Certifificate trust anchors used to verify the LDAP server certificate.
|
Certifificate trust anchors used to verify the LDAP server certificate.
|
||||||
'';
|
'';
|
||||||
|
@ -1064,7 +1075,7 @@ in
|
||||||
type = types.str;
|
type = types.str;
|
||||||
# read the default from nixos' redis module
|
# read the default from nixos' redis module
|
||||||
default = config.services.redis.servers.rspamd.unixSocket;
|
default = config.services.redis.servers.rspamd.unixSocket;
|
||||||
defaultText = lib.literalExpression "config.services.redis.servers.rspamd.unixSocket";
|
defaultText = literalExpression "config.services.redis.servers.rspamd.unixSocket";
|
||||||
description = ''
|
description = ''
|
||||||
Path, IP address or hostname that Rspamd should use to contact Redis.
|
Path, IP address or hostname that Rspamd should use to contact Redis.
|
||||||
'';
|
'';
|
||||||
|
@ -1073,7 +1084,7 @@ in
|
||||||
port = mkOption {
|
port = mkOption {
|
||||||
type = with types; nullOr port;
|
type = with types; nullOr port;
|
||||||
default = null;
|
default = null;
|
||||||
example = lib.literalExpression "config.services.redis.servers.rspamd.port";
|
example = literalExpression "config.services.redis.servers.rspamd.port";
|
||||||
description = ''
|
description = ''
|
||||||
Port that Rspamd should use to contact Redis.
|
Port that Rspamd should use to contact Redis.
|
||||||
'';
|
'';
|
||||||
|
@ -1082,7 +1093,7 @@ in
|
||||||
password = mkOption {
|
password = mkOption {
|
||||||
type = types.nullOr types.str;
|
type = types.nullOr types.str;
|
||||||
default = config.services.redis.servers.rspamd.requirePass;
|
default = config.services.redis.servers.rspamd.requirePass;
|
||||||
defaultText = lib.literalExpression "config.services.redis.servers.rspamd.requirePass";
|
defaultText = literalExpression "config.services.redis.servers.rspamd.requirePass";
|
||||||
description = ''
|
description = ''
|
||||||
Password that rspamd should use to contact redis, or null if not required.
|
Password that rspamd should use to contact redis, or null if not required.
|
||||||
'';
|
'';
|
||||||
|
@ -1102,7 +1113,7 @@ in
|
||||||
sendingFqdn = mkOption {
|
sendingFqdn = mkOption {
|
||||||
type = types.str;
|
type = types.str;
|
||||||
default = cfg.fqdn;
|
default = cfg.fqdn;
|
||||||
defaultText = lib.literalMD "{option}`mailserver.fqdn`";
|
defaultText = literalMD "{option}`mailserver.fqdn`";
|
||||||
example = "myserver.example.com";
|
example = "myserver.example.com";
|
||||||
description = ''
|
description = ''
|
||||||
The fully qualified domain name of the mail server used to
|
The fully qualified domain name of the mail server used to
|
||||||
|
@ -1178,7 +1189,7 @@ in
|
||||||
start program = "${pkgs.systemd}/bin/systemctl start rspamd"
|
start program = "${pkgs.systemd}/bin/systemctl start rspamd"
|
||||||
stop program = "${pkgs.systemd}/bin/systemctl stop rspamd"
|
stop program = "${pkgs.systemd}/bin/systemctl stop rspamd"
|
||||||
'';
|
'';
|
||||||
defaultText = lib.literalMD "see [source](https://gitlab.com/simple-nixos-mailserver/nixos-mailserver/-/blob/master/default.nix)";
|
defaultText = literalMD "see [source](https://gitlab.com/simple-nixos-mailserver/nixos-mailserver/-/blob/master/default.nix)";
|
||||||
description = ''
|
description = ''
|
||||||
The configuration used for monitoring via monit.
|
The configuration used for monitoring via monit.
|
||||||
Use a mail address that you actively check and set it via 'set alert ...'.
|
Use a mail address that you actively check and set it via 'set alert ...'.
|
||||||
|
@ -1287,7 +1298,7 @@ in
|
||||||
locations = mkOption {
|
locations = mkOption {
|
||||||
type = types.listOf types.path;
|
type = types.listOf types.path;
|
||||||
default = [ cfg.mailDirectory ];
|
default = [ cfg.mailDirectory ];
|
||||||
defaultText = lib.literalExpression "[ config.mailserver.mailDirectory ]";
|
defaultText = literalExpression "[ config.mailserver.mailDirectory ]";
|
||||||
description = "The locations that are to be backed up by borg.";
|
description = "The locations that are to be backed up by borg.";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1388,29 +1399,29 @@ in
|
||||||
};
|
};
|
||||||
|
|
||||||
imports = [
|
imports = [
|
||||||
(lib.mkRemovedOptionModule [ "mailserver" "fullTextSearch" "maintenance" "enable" ] ''
|
(mkRemovedOptionModule [ "mailserver" "fullTextSearch" "maintenance" "enable" ] ''
|
||||||
This option is not needed for fts-flatcurve
|
This option is not needed for fts-flatcurve
|
||||||
'')
|
'')
|
||||||
(lib.mkRemovedOptionModule [ "mailserver" "fullTextSearch" "maintenance" "onCalendar" ] ''
|
(mkRemovedOptionModule [ "mailserver" "fullTextSearch" "maintenance" "onCalendar" ] ''
|
||||||
This option is not needed for fts-flatcurve
|
This option is not needed for fts-flatcurve
|
||||||
'')
|
'')
|
||||||
(lib.mkRemovedOptionModule [ "mailserver" "fullTextSearch" "maintenance" "randomizedDelaySec" ] ''
|
(mkRemovedOptionModule [ "mailserver" "fullTextSearch" "maintenance" "randomizedDelaySec" ] ''
|
||||||
This option is not needed for fts-flatcurve
|
This option is not needed for fts-flatcurve
|
||||||
'')
|
'')
|
||||||
(lib.mkRemovedOptionModule [ "mailserver" "fullTextSearch" "minSize" ] ''
|
(mkRemovedOptionModule [ "mailserver" "fullTextSearch" "minSize" ] ''
|
||||||
This option is not supported by fts-flatcurve
|
This option is not supported by fts-flatcurve
|
||||||
'')
|
'')
|
||||||
(lib.mkRemovedOptionModule [ "mailserver" "fullTextSearch" "maxSize" ] ''
|
(mkRemovedOptionModule [ "mailserver" "fullTextSearch" "maxSize" ] ''
|
||||||
This option is not needed since fts-xapian 1.8.3
|
This option is not needed since fts-xapian 1.8.3
|
||||||
'')
|
'')
|
||||||
(lib.mkRemovedOptionModule [ "mailserver" "fullTextSearch" "indexAttachments" ] ''
|
(mkRemovedOptionModule [ "mailserver" "fullTextSearch" "indexAttachments" ] ''
|
||||||
Text attachments are always indexed since fts-xapian 1.4.8
|
Text attachments are always indexed since fts-xapian 1.4.8
|
||||||
'')
|
'')
|
||||||
(lib.mkRenamedOptionModule
|
(mkRenamedOptionModule
|
||||||
[ "mailserver" "rebootAfterKernelUpgrade" "enable" ]
|
[ "mailserver" "rebootAfterKernelUpgrade" "enable" ]
|
||||||
[ "system" "autoUpgrade" "allowReboot" ]
|
[ "system" "autoUpgrade" "allowReboot" ]
|
||||||
)
|
)
|
||||||
(lib.mkRemovedOptionModule [ "mailserver" "rebootAfterKernelUpgrade" "method" ] ''
|
(mkRemovedOptionModule [ "mailserver" "rebootAfterKernelUpgrade" "method" ] ''
|
||||||
Use `system.autoUpgrade` instead.
|
Use `system.autoUpgrade` instead.
|
||||||
'')
|
'')
|
||||||
./mail-server/assertions.nix
|
./mail-server/assertions.nix
|
||||||
|
@ -1427,17 +1438,17 @@ in
|
||||||
./mail-server/rspamd.nix
|
./mail-server/rspamd.nix
|
||||||
./mail-server/nginx.nix
|
./mail-server/nginx.nix
|
||||||
./mail-server/kresd.nix
|
./mail-server/kresd.nix
|
||||||
(lib.mkRemovedOptionModule [ "mailserver" "policydSPFExtraConfig" ] ''
|
(mkRemovedOptionModule [ "mailserver" "policydSPFExtraConfig" ] ''
|
||||||
SPF checking has been migrated to Rspamd, which makes this config redundant. Please look into the rspamd config to migrate your settings.
|
SPF checking has been migrated to Rspamd, which makes this config redundant. Please look into the rspamd config to migrate your settings.
|
||||||
It may be that they are redundant and are already configured in rspamd like for skip_addresses.
|
It may be that they are redundant and are already configured in rspamd like for skip_addresses.
|
||||||
'')
|
'')
|
||||||
(lib.mkRemovedOptionModule [ "mailserver" "dkimHeaderCanonicalization" ] ''
|
(mkRemovedOptionModule [ "mailserver" "dkimHeaderCanonicalization" ] ''
|
||||||
DKIM signing has been migrated to Rspamd, which always uses relaxed canonicalization.
|
DKIM signing has been migrated to Rspamd, which always uses relaxed canonicalization.
|
||||||
'')
|
'')
|
||||||
(lib.mkRemovedOptionModule [ "mailserver" "dkimBodyCanonicalization" ] ''
|
(mkRemovedOptionModule [ "mailserver" "dkimBodyCanonicalization" ] ''
|
||||||
DKIM signing has been migrated to Rspamd, which always uses relaxed canonicalization.
|
DKIM signing has been migrated to Rspamd, which always uses relaxed canonicalization.
|
||||||
'')
|
'')
|
||||||
(lib.mkRemovedOptionModule [ "mailserver" "smtpdForbidBareNewline" ] ''
|
(mkRemovedOptionModule [ "mailserver" "smtpdForbidBareNewline" ] ''
|
||||||
The workaround for the SMTP Smuggling attack is default enabled in Postfix >3.9. Use `services.postfix.config.smtpd_forbid_bare_newline` if you need to deviate from its default.
|
The workaround for the SMTP Smuggling attack is default enabled in Postfix >3.9. Use `services.postfix.config.smtpd_forbid_bare_newline` if you need to deviate from its default.
|
||||||
'')
|
'')
|
||||||
];
|
];
|
||||||
|
|
|
@ -163,283 +163,281 @@ let
|
||||||
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
config =
|
config = lib.mkIf cfg.enable {
|
||||||
with cfg;
|
assertions = [
|
||||||
lib.mkIf enable {
|
{
|
||||||
assertions = [
|
assertion = junkMailboxNumber == 1;
|
||||||
|
message = "nixos-mailserver requires exactly one dovecot mailbox with the 'special use' flag set to 'Junk' (${builtins.toString junkMailboxNumber} have been found)";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
warnings =
|
||||||
|
lib.optional
|
||||||
|
(
|
||||||
|
(builtins.length cfg.fullTextSearch.languages > 1)
|
||||||
|
&& (builtins.elem "stopwords" cfg.fullTextSearch.filters)
|
||||||
|
)
|
||||||
|
''
|
||||||
|
Using stopwords in `mailserver.fullTextSearch.filters` with multiple
|
||||||
|
languages in `mailserver.fullTextSearch.languages` configured WILL
|
||||||
|
cause some searches to fail.
|
||||||
|
|
||||||
|
The recommended solution is to NOT use the stopword filter when
|
||||||
|
multiple languages are present in the configuration.
|
||||||
|
'';
|
||||||
|
|
||||||
|
# for sieve-test. Shelling it in on demand usually doesnt' work, as it reads
|
||||||
|
# the global config and tries to open shared libraries configured in there,
|
||||||
|
# which are usually not compatible.
|
||||||
|
environment.systemPackages = [
|
||||||
|
pkgs.dovecot_pigeonhole
|
||||||
|
] ++ lib.optional cfg.fullTextSearch.enable pkgs.dovecot-fts-flatcurve;
|
||||||
|
|
||||||
|
# For compatibility with python imaplib
|
||||||
|
environment.etc."dovecot/modules".source = "/run/current-system/sw/lib/dovecot/modules";
|
||||||
|
|
||||||
|
services.dovecot2 = {
|
||||||
|
enable = true;
|
||||||
|
enableImap = cfg.enableImap || cfg.enableImapSsl;
|
||||||
|
enablePop3 = cfg.enablePop3 || cfg.enablePop3Ssl;
|
||||||
|
enablePAM = false;
|
||||||
|
enableQuota = true;
|
||||||
|
mailGroup = cfg.vmailGroupName;
|
||||||
|
mailUser = cfg.vmailUserName;
|
||||||
|
mailLocation = dovecotMaildir;
|
||||||
|
sslServerCert = certificatePath;
|
||||||
|
sslServerKey = keyPath;
|
||||||
|
enableDHE = lib.mkDefault false;
|
||||||
|
enableLmtp = true;
|
||||||
|
mailPlugins.globally.enable = lib.optionals cfg.fullTextSearch.enable [
|
||||||
|
"fts"
|
||||||
|
"fts_flatcurve"
|
||||||
|
];
|
||||||
|
protocols = lib.optional cfg.enableManageSieve "sieve";
|
||||||
|
|
||||||
|
pluginSettings = {
|
||||||
|
sieve = "file:${cfg.sieveDirectory}/%{user}/scripts;active=${cfg.sieveDirectory}/%{user}/active.sieve";
|
||||||
|
sieve_default = "file:${cfg.sieveDirectory}/%{user}/default.sieve";
|
||||||
|
sieve_default_name = "default";
|
||||||
|
} // (lib.optionalAttrs cfg.fullTextSearch.enable ftsPluginSettings);
|
||||||
|
|
||||||
|
sieve = {
|
||||||
|
extensions = [
|
||||||
|
"fileinto"
|
||||||
|
];
|
||||||
|
|
||||||
|
scripts.after = builtins.toFile "spam.sieve" ''
|
||||||
|
require "fileinto";
|
||||||
|
|
||||||
|
if header :is "X-Spam" "Yes" {
|
||||||
|
fileinto "${junkMailboxName}";
|
||||||
|
stop;
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
|
||||||
|
pipeBins = map lib.getExe [
|
||||||
|
(pkgs.writeShellScriptBin "rspamd-learn-ham.sh" "exec ${pkgs.rspamd}/bin/rspamc -h /run/rspamd/worker-controller.sock learn_ham")
|
||||||
|
(pkgs.writeShellScriptBin "rspamd-learn-spam.sh" "exec ${pkgs.rspamd}/bin/rspamc -h /run/rspamd/worker-controller.sock learn_spam")
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
imapsieve.mailbox = [
|
||||||
{
|
{
|
||||||
assertion = junkMailboxNumber == 1;
|
name = junkMailboxName;
|
||||||
message = "nixos-mailserver requires exactly one dovecot mailbox with the 'special use' flag set to 'Junk' (${builtins.toString junkMailboxNumber} have been found)";
|
causes = [
|
||||||
|
"COPY"
|
||||||
|
"APPEND"
|
||||||
|
];
|
||||||
|
before = ./dovecot/imap_sieve/report-spam.sieve;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "*";
|
||||||
|
from = junkMailboxName;
|
||||||
|
causes = [ "COPY" ];
|
||||||
|
before = ./dovecot/imap_sieve/report-ham.sieve;
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
warnings =
|
mailboxes = cfg.mailboxes;
|
||||||
lib.optional
|
|
||||||
(
|
|
||||||
(builtins.length cfg.fullTextSearch.languages > 1)
|
|
||||||
&& (builtins.elem "stopwords" cfg.fullTextSearch.filters)
|
|
||||||
)
|
|
||||||
''
|
|
||||||
Using stopwords in `mailserver.fullTextSearch.filters` with multiple
|
|
||||||
languages in `mailserver.fullTextSearch.languages` configured WILL
|
|
||||||
cause some searches to fail.
|
|
||||||
|
|
||||||
The recommended solution is to NOT use the stopword filter when
|
extraConfig = ''
|
||||||
multiple languages are present in the configuration.
|
#Extra Config
|
||||||
'';
|
${lib.optionalString cfg.debug ''
|
||||||
|
mail_debug = yes
|
||||||
|
auth_debug = yes
|
||||||
|
verbose_ssl = yes
|
||||||
|
''}
|
||||||
|
|
||||||
# for sieve-test. Shelling it in on demand usually doesnt' work, as it reads
|
${lib.optionalString (cfg.enableImap || cfg.enableImapSsl) ''
|
||||||
# the global config and tries to open shared libraries configured in there,
|
service imap-login {
|
||||||
# which are usually not compatible.
|
inet_listener imap {
|
||||||
environment.systemPackages = [
|
${
|
||||||
pkgs.dovecot_pigeonhole
|
if cfg.enableImap then
|
||||||
] ++ lib.optional cfg.fullTextSearch.enable pkgs.dovecot-fts-flatcurve;
|
''
|
||||||
|
port = 143
|
||||||
# For compatibility with python imaplib
|
''
|
||||||
environment.etc."dovecot/modules".source = "/run/current-system/sw/lib/dovecot/modules";
|
else
|
||||||
|
''
|
||||||
services.dovecot2 = {
|
# see https://dovecot.org/pipermail/dovecot/2010-March/047479.html
|
||||||
enable = true;
|
port = 0
|
||||||
enableImap = enableImap || enableImapSsl;
|
''
|
||||||
enablePop3 = enablePop3 || enablePop3Ssl;
|
|
||||||
enablePAM = false;
|
|
||||||
enableQuota = true;
|
|
||||||
mailGroup = vmailGroupName;
|
|
||||||
mailUser = vmailUserName;
|
|
||||||
mailLocation = dovecotMaildir;
|
|
||||||
sslServerCert = certificatePath;
|
|
||||||
sslServerKey = keyPath;
|
|
||||||
enableDHE = lib.mkDefault false;
|
|
||||||
enableLmtp = true;
|
|
||||||
mailPlugins.globally.enable = lib.optionals cfg.fullTextSearch.enable [
|
|
||||||
"fts"
|
|
||||||
"fts_flatcurve"
|
|
||||||
];
|
|
||||||
protocols = lib.optional cfg.enableManageSieve "sieve";
|
|
||||||
|
|
||||||
pluginSettings = {
|
|
||||||
sieve = "file:${cfg.sieveDirectory}/%{user}/scripts;active=${cfg.sieveDirectory}/%{user}/active.sieve";
|
|
||||||
sieve_default = "file:${cfg.sieveDirectory}/%{user}/default.sieve";
|
|
||||||
sieve_default_name = "default";
|
|
||||||
} // (lib.optionalAttrs cfg.fullTextSearch.enable ftsPluginSettings);
|
|
||||||
|
|
||||||
sieve = {
|
|
||||||
extensions = [
|
|
||||||
"fileinto"
|
|
||||||
];
|
|
||||||
|
|
||||||
scripts.after = builtins.toFile "spam.sieve" ''
|
|
||||||
require "fileinto";
|
|
||||||
|
|
||||||
if header :is "X-Spam" "Yes" {
|
|
||||||
fileinto "${junkMailboxName}";
|
|
||||||
stop;
|
|
||||||
}
|
|
||||||
'';
|
|
||||||
|
|
||||||
pipeBins = map lib.getExe [
|
|
||||||
(pkgs.writeShellScriptBin "rspamd-learn-ham.sh" "exec ${pkgs.rspamd}/bin/rspamc -h /run/rspamd/worker-controller.sock learn_ham")
|
|
||||||
(pkgs.writeShellScriptBin "rspamd-learn-spam.sh" "exec ${pkgs.rspamd}/bin/rspamc -h /run/rspamd/worker-controller.sock learn_spam")
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
imapsieve.mailbox = [
|
|
||||||
{
|
|
||||||
name = junkMailboxName;
|
|
||||||
causes = [
|
|
||||||
"COPY"
|
|
||||||
"APPEND"
|
|
||||||
];
|
|
||||||
before = ./dovecot/imap_sieve/report-spam.sieve;
|
|
||||||
}
|
|
||||||
{
|
|
||||||
name = "*";
|
|
||||||
from = junkMailboxName;
|
|
||||||
causes = [ "COPY" ];
|
|
||||||
before = ./dovecot/imap_sieve/report-ham.sieve;
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
mailboxes = cfg.mailboxes;
|
|
||||||
|
|
||||||
extraConfig = ''
|
|
||||||
#Extra Config
|
|
||||||
${lib.optionalString debug ''
|
|
||||||
mail_debug = yes
|
|
||||||
auth_debug = yes
|
|
||||||
verbose_ssl = yes
|
|
||||||
''}
|
|
||||||
|
|
||||||
${lib.optionalString (cfg.enableImap || cfg.enableImapSsl) ''
|
|
||||||
service imap-login {
|
|
||||||
inet_listener imap {
|
|
||||||
${
|
|
||||||
if cfg.enableImap then
|
|
||||||
''
|
|
||||||
port = 143
|
|
||||||
''
|
|
||||||
else
|
|
||||||
''
|
|
||||||
# see https://dovecot.org/pipermail/dovecot/2010-March/047479.html
|
|
||||||
port = 0
|
|
||||||
''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
inet_listener imaps {
|
|
||||||
${
|
|
||||||
if cfg.enableImapSsl then
|
|
||||||
''
|
|
||||||
port = 993
|
|
||||||
ssl = yes
|
|
||||||
''
|
|
||||||
else
|
|
||||||
''
|
|
||||||
# see https://dovecot.org/pipermail/dovecot/2010-March/047479.html
|
|
||||||
port = 0
|
|
||||||
''
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
''}
|
inet_listener imaps {
|
||||||
${lib.optionalString (cfg.enablePop3 || cfg.enablePop3Ssl) ''
|
${
|
||||||
service pop3-login {
|
if cfg.enableImapSsl then
|
||||||
inet_listener pop3 {
|
''
|
||||||
${
|
port = 993
|
||||||
if cfg.enablePop3 then
|
ssl = yes
|
||||||
''
|
''
|
||||||
port = 110
|
else
|
||||||
''
|
''
|
||||||
else
|
# see https://dovecot.org/pipermail/dovecot/2010-March/047479.html
|
||||||
''
|
port = 0
|
||||||
# see https://dovecot.org/pipermail/dovecot/2010-March/047479.html
|
''
|
||||||
port = 0
|
|
||||||
''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
inet_listener pop3s {
|
|
||||||
${
|
|
||||||
if cfg.enablePop3Ssl then
|
|
||||||
''
|
|
||||||
port = 995
|
|
||||||
ssl = yes
|
|
||||||
''
|
|
||||||
else
|
|
||||||
''
|
|
||||||
# see https://dovecot.org/pipermail/dovecot/2010-March/047479.html
|
|
||||||
port = 0
|
|
||||||
''
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
''}
|
|
||||||
|
|
||||||
protocol imap {
|
|
||||||
mail_max_userip_connections = ${toString cfg.maxConnectionsPerUser}
|
|
||||||
mail_plugins = $mail_plugins imap_sieve
|
|
||||||
}
|
}
|
||||||
|
''}
|
||||||
service imap {
|
${lib.optionalString (cfg.enablePop3 || cfg.enablePop3Ssl) ''
|
||||||
vsz_limit = ${builtins.toString cfg.imapMemoryLimit} MB
|
service pop3-login {
|
||||||
}
|
inet_listener pop3 {
|
||||||
|
${
|
||||||
protocol pop3 {
|
if cfg.enablePop3 then
|
||||||
mail_max_userip_connections = ${toString cfg.maxConnectionsPerUser}
|
''
|
||||||
}
|
port = 110
|
||||||
|
''
|
||||||
mail_access_groups = ${vmailGroupName}
|
else
|
||||||
|
''
|
||||||
# https://ssl-config.mozilla.org/#server=dovecot&version=2.3.21&config=intermediate&openssl=3.4.1&guideline=5.7
|
# see https://dovecot.org/pipermail/dovecot/2010-March/047479.html
|
||||||
ssl = required
|
port = 0
|
||||||
ssl_min_protocol = TLSv1.2
|
''
|
||||||
ssl_prefer_server_ciphers = no
|
}
|
||||||
ssl_curve_list = X25519:prime256v1:secp384r1
|
|
||||||
|
|
||||||
service lmtp {
|
|
||||||
unix_listener dovecot-lmtp {
|
|
||||||
group = ${postfixCfg.group}
|
|
||||||
mode = 0600
|
|
||||||
user = ${postfixCfg.user}
|
|
||||||
}
|
}
|
||||||
vsz_limit = ${builtins.toString cfg.lmtpMemoryLimit} MB
|
inet_listener pop3s {
|
||||||
}
|
${
|
||||||
|
if cfg.enablePop3Ssl then
|
||||||
service quota-status {
|
''
|
||||||
inet_listener {
|
port = 995
|
||||||
port = 0
|
ssl = yes
|
||||||
|
''
|
||||||
|
else
|
||||||
|
''
|
||||||
|
# see https://dovecot.org/pipermail/dovecot/2010-March/047479.html
|
||||||
|
port = 0
|
||||||
|
''
|
||||||
|
}
|
||||||
}
|
}
|
||||||
unix_listener quota-status {
|
|
||||||
user = postfix
|
|
||||||
}
|
|
||||||
vsz_limit = ${builtins.toString cfg.quotaStatusMemoryLimit} MB
|
|
||||||
}
|
}
|
||||||
|
''}
|
||||||
|
|
||||||
recipient_delimiter = ${cfg.recipientDelimiter}
|
protocol imap {
|
||||||
lmtp_save_to_detail_mailbox = ${cfg.lmtpSaveToDetailMailbox}
|
mail_max_userip_connections = ${toString cfg.maxConnectionsPerUser}
|
||||||
|
mail_plugins = $mail_plugins imap_sieve
|
||||||
|
}
|
||||||
|
|
||||||
protocol lmtp {
|
service imap {
|
||||||
mail_plugins = $mail_plugins sieve
|
vsz_limit = ${builtins.toString cfg.imapMemoryLimit} MB
|
||||||
|
}
|
||||||
|
|
||||||
|
protocol pop3 {
|
||||||
|
mail_max_userip_connections = ${toString cfg.maxConnectionsPerUser}
|
||||||
|
}
|
||||||
|
|
||||||
|
mail_access_groups = ${cfg.vmailGroupName}
|
||||||
|
|
||||||
|
# https://ssl-config.mozilla.org/#server=dovecot&version=2.3.21&config=intermediate&openssl=3.4.1&guideline=5.7
|
||||||
|
ssl = required
|
||||||
|
ssl_min_protocol = TLSv1.2
|
||||||
|
ssl_prefer_server_ciphers = no
|
||||||
|
ssl_curve_list = X25519:prime256v1:secp384r1
|
||||||
|
|
||||||
|
service lmtp {
|
||||||
|
unix_listener dovecot-lmtp {
|
||||||
|
group = ${postfixCfg.group}
|
||||||
|
mode = 0600
|
||||||
|
user = ${postfixCfg.user}
|
||||||
}
|
}
|
||||||
|
vsz_limit = ${builtins.toString cfg.lmtpMemoryLimit} MB
|
||||||
|
}
|
||||||
|
|
||||||
|
service quota-status {
|
||||||
|
inet_listener {
|
||||||
|
port = 0
|
||||||
|
}
|
||||||
|
unix_listener quota-status {
|
||||||
|
user = postfix
|
||||||
|
}
|
||||||
|
vsz_limit = ${builtins.toString cfg.quotaStatusMemoryLimit} MB
|
||||||
|
}
|
||||||
|
|
||||||
|
recipient_delimiter = ${cfg.recipientDelimiter}
|
||||||
|
lmtp_save_to_detail_mailbox = ${cfg.lmtpSaveToDetailMailbox}
|
||||||
|
|
||||||
|
protocol lmtp {
|
||||||
|
mail_plugins = $mail_plugins sieve
|
||||||
|
}
|
||||||
|
|
||||||
|
passdb {
|
||||||
|
driver = passwd-file
|
||||||
|
args = ${passwdFile}
|
||||||
|
}
|
||||||
|
|
||||||
|
userdb {
|
||||||
|
driver = passwd-file
|
||||||
|
args = ${userdbFile}
|
||||||
|
default_fields = uid=${builtins.toString cfg.vmailUID} gid=${builtins.toString cfg.vmailUID} home=${cfg.mailDirectory}
|
||||||
|
}
|
||||||
|
|
||||||
|
${lib.optionalString cfg.ldap.enable ''
|
||||||
passdb {
|
passdb {
|
||||||
driver = passwd-file
|
driver = ldap
|
||||||
args = ${passwdFile}
|
args = ${ldapConfFile}
|
||||||
}
|
}
|
||||||
|
|
||||||
userdb {
|
userdb {
|
||||||
driver = passwd-file
|
driver = ldap
|
||||||
args = ${userdbFile}
|
args = ${ldapConfFile}
|
||||||
default_fields = uid=${builtins.toString cfg.vmailUID} gid=${builtins.toString cfg.vmailUID} home=${cfg.mailDirectory}
|
default_fields = home=${cfg.mailDirectory}/ldap/%{user} uid=${toString cfg.vmailUID} gid=${toString cfg.vmailUID}
|
||||||
}
|
}
|
||||||
|
''}
|
||||||
|
|
||||||
${lib.optionalString cfg.ldap.enable ''
|
service auth {
|
||||||
passdb {
|
unix_listener auth {
|
||||||
driver = ldap
|
mode = 0660
|
||||||
args = ${ldapConfFile}
|
user = ${postfixCfg.user}
|
||||||
}
|
group = ${postfixCfg.group}
|
||||||
|
|
||||||
userdb {
|
|
||||||
driver = ldap
|
|
||||||
args = ${ldapConfFile}
|
|
||||||
default_fields = home=${cfg.mailDirectory}/ldap/%{user} uid=${toString cfg.vmailUID} gid=${toString cfg.vmailUID}
|
|
||||||
}
|
|
||||||
''}
|
|
||||||
|
|
||||||
service auth {
|
|
||||||
unix_listener auth {
|
|
||||||
mode = 0660
|
|
||||||
user = ${postfixCfg.user}
|
|
||||||
group = ${postfixCfg.group}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
auth_mechanisms = plain login
|
auth_mechanisms = plain login
|
||||||
|
|
||||||
namespace inbox {
|
namespace inbox {
|
||||||
separator = ${cfg.hierarchySeparator}
|
separator = ${cfg.hierarchySeparator}
|
||||||
inbox = yes
|
inbox = yes
|
||||||
}
|
}
|
||||||
|
|
||||||
service indexer-worker {
|
service indexer-worker {
|
||||||
${lib.optionalString (cfg.fullTextSearch.memoryLimit != null) ''
|
${lib.optionalString (cfg.fullTextSearch.memoryLimit != null) ''
|
||||||
vsz_limit = ${toString (cfg.fullTextSearch.memoryLimit * 1024 * 1024)}
|
vsz_limit = ${toString (cfg.fullTextSearch.memoryLimit * 1024 * 1024)}
|
||||||
''}
|
''}
|
||||||
}
|
}
|
||||||
|
|
||||||
lda_mailbox_autosubscribe = yes
|
lda_mailbox_autosubscribe = yes
|
||||||
lda_mailbox_autocreate = yes
|
lda_mailbox_autocreate = yes
|
||||||
'';
|
'';
|
||||||
};
|
|
||||||
|
|
||||||
systemd.services.dovecot2 = {
|
|
||||||
preStart =
|
|
||||||
''
|
|
||||||
${genPasswdScript}
|
|
||||||
''
|
|
||||||
+ (lib.optionalString cfg.ldap.enable setPwdInLdapConfFile);
|
|
||||||
};
|
|
||||||
|
|
||||||
systemd.services.postfix.restartTriggers = [
|
|
||||||
genPasswdScript
|
|
||||||
] ++ (lib.optional cfg.ldap.enable [ setPwdInLdapConfFile ]);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
systemd.services.dovecot2 = {
|
||||||
|
preStart =
|
||||||
|
''
|
||||||
|
${genPasswdScript}
|
||||||
|
''
|
||||||
|
+ (lib.optionalString cfg.ldap.enable setPwdInLdapConfFile);
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services.postfix.restartTriggers = [
|
||||||
|
genPasswdScript
|
||||||
|
] ++ (lib.optional cfg.ldap.enable [ setPwdInLdapConfFile ]);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,17 +25,15 @@ let
|
||||||
cfg = config.mailserver;
|
cfg = config.mailserver;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
config =
|
config = lib.mkIf cfg.enable {
|
||||||
with cfg;
|
environment.systemPackages =
|
||||||
lib.mkIf enable {
|
with pkgs;
|
||||||
environment.systemPackages =
|
[
|
||||||
with pkgs;
|
dovecot
|
||||||
[
|
openssh
|
||||||
dovecot
|
postfix
|
||||||
openssh
|
rspamd
|
||||||
postfix
|
]
|
||||||
rspamd
|
++ (if cfg.certificateScheme == "selfsigned" then [ openssl ] else [ ]);
|
||||||
]
|
};
|
||||||
++ (if certificateScheme == "selfsigned" then [ openssl ] else [ ]);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,21 +20,19 @@ let
|
||||||
cfg = config.mailserver;
|
cfg = config.mailserver;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
config =
|
config = lib.mkIf (cfg.enable && cfg.openFirewall) {
|
||||||
with cfg;
|
|
||||||
lib.mkIf (enable && openFirewall) {
|
|
||||||
|
|
||||||
networking.firewall = {
|
networking.firewall = {
|
||||||
allowedTCPPorts =
|
allowedTCPPorts =
|
||||||
[ 25 ]
|
[ 25 ]
|
||||||
++ lib.optional enableSubmission 587
|
++ lib.optional cfg.enableSubmission 587
|
||||||
++ lib.optional enableSubmissionSsl 465
|
++ lib.optional cfg.enableSubmissionSsl 465
|
||||||
++ lib.optional enableImap 143
|
++ lib.optional cfg.enableImap 143
|
||||||
++ lib.optional enableImapSsl 993
|
++ lib.optional cfg.enableImapSsl 993
|
||||||
++ lib.optional enablePop3 110
|
++ lib.optional cfg.enablePop3 110
|
||||||
++ lib.optional enablePop3Ssl 995
|
++ lib.optional cfg.enablePop3Ssl 995
|
||||||
++ lib.optional enableManageSieve 4190
|
++ lib.optional cfg.enableManageSieve 4190
|
||||||
++ lib.optional (certificateScheme == "acme-nginx") 80;
|
++ lib.optional (cfg.certificateScheme == "acme-nginx") 80;
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -233,183 +233,181 @@ let
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
config =
|
config = lib.mkIf cfg.enable {
|
||||||
with cfg;
|
|
||||||
lib.mkIf enable {
|
|
||||||
|
|
||||||
systemd.services.postfix-setup = lib.mkIf cfg.ldap.enable {
|
systemd.services.postfix-setup = lib.mkIf cfg.ldap.enable {
|
||||||
preStart = ''
|
preStart = ''
|
||||||
${appendPwdInVirtualMailboxMap}
|
${appendPwdInVirtualMailboxMap}
|
||||||
${appendPwdInSenderLoginMap}
|
${appendPwdInSenderLoginMap}
|
||||||
'';
|
'';
|
||||||
restartTriggers = [
|
restartTriggers = [
|
||||||
appendPwdInVirtualMailboxMap
|
appendPwdInVirtualMailboxMap
|
||||||
appendPwdInSenderLoginMap
|
appendPwdInSenderLoginMap
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
services.postfix = {
|
||||||
|
enable = true;
|
||||||
|
hostname = "${cfg.sendingFqdn}";
|
||||||
|
networksStyle = "host";
|
||||||
|
mapFiles."valias" = valiases_file;
|
||||||
|
mapFiles."regex_valias" = regex_valiases_file;
|
||||||
|
mapFiles."vaccounts" = vaccounts_file;
|
||||||
|
mapFiles."regex_vaccounts" = regex_vaccounts_file;
|
||||||
|
mapFiles."denied_recipients" = denied_recipients_file;
|
||||||
|
mapFiles."reject_senders" = reject_senders_file;
|
||||||
|
mapFiles."reject_recipients" = reject_recipients_file;
|
||||||
|
enableSubmission = cfg.enableSubmission;
|
||||||
|
enableSubmissions = cfg.enableSubmissionSsl;
|
||||||
|
virtual = lookupTableToString (mergeLookupTables [
|
||||||
|
all_valiases_postfix
|
||||||
|
catchAllPostfix
|
||||||
|
forwards
|
||||||
|
]);
|
||||||
|
|
||||||
|
config = {
|
||||||
|
smtpd_tls_chain_files = [
|
||||||
|
"${keyPath}"
|
||||||
|
"${certificatePath}"
|
||||||
];
|
];
|
||||||
|
|
||||||
|
# Extra Config
|
||||||
|
mydestination = "";
|
||||||
|
recipient_delimiter = cfg.recipientDelimiter;
|
||||||
|
smtpd_banner = "${cfg.fqdn} ESMTP NO UCE";
|
||||||
|
disable_vrfy_command = true;
|
||||||
|
message_size_limit = toString cfg.messageSizeLimit;
|
||||||
|
|
||||||
|
# virtual mail system
|
||||||
|
virtual_uid_maps = "static:5000";
|
||||||
|
virtual_gid_maps = "static:5000";
|
||||||
|
virtual_mailbox_base = cfg.mailDirectory;
|
||||||
|
virtual_mailbox_domains = vhosts_file;
|
||||||
|
virtual_mailbox_maps =
|
||||||
|
[
|
||||||
|
(mappedFile "valias")
|
||||||
|
]
|
||||||
|
++ lib.optionals cfg.ldap.enable [
|
||||||
|
"ldap:${ldapVirtualMailboxMapFile}"
|
||||||
|
]
|
||||||
|
++ lib.optionals (regex_valiases_postfix != { }) [
|
||||||
|
(mappedRegexFile "regex_valias")
|
||||||
|
];
|
||||||
|
virtual_alias_maps = lib.mkAfter (
|
||||||
|
lib.optionals (regex_valiases_postfix != { }) [
|
||||||
|
(mappedRegexFile "regex_valias")
|
||||||
|
]
|
||||||
|
);
|
||||||
|
virtual_transport = "lmtp:unix:/run/dovecot2/dovecot-lmtp";
|
||||||
|
# Avoid leakage of X-Original-To, X-Delivered-To headers between recipients
|
||||||
|
lmtp_destination_recipient_limit = "1";
|
||||||
|
|
||||||
|
# sasl with dovecot
|
||||||
|
smtpd_sasl_type = "dovecot";
|
||||||
|
smtpd_sasl_path = "/run/dovecot2/auth";
|
||||||
|
smtpd_sasl_auth_enable = true;
|
||||||
|
smtpd_relay_restrictions = [
|
||||||
|
"permit_mynetworks"
|
||||||
|
"permit_sasl_authenticated"
|
||||||
|
"reject_unauth_destination"
|
||||||
|
];
|
||||||
|
|
||||||
|
# reject selected senders
|
||||||
|
smtpd_sender_restrictions = [
|
||||||
|
"check_sender_access ${mappedFile "reject_senders"}"
|
||||||
|
];
|
||||||
|
|
||||||
|
smtpd_recipient_restrictions = [
|
||||||
|
# reject selected recipients
|
||||||
|
"check_recipient_access ${mappedFile "denied_recipients"}"
|
||||||
|
"check_recipient_access ${mappedFile "reject_recipients"}"
|
||||||
|
# quota checking
|
||||||
|
"check_policy_service unix:/run/dovecot2/quota-status"
|
||||||
|
];
|
||||||
|
|
||||||
|
# TLS for incoming mail is optional
|
||||||
|
smtpd_tls_security_level = "may";
|
||||||
|
|
||||||
|
# But required for authentication attempts
|
||||||
|
smtpd_tls_auth_only = true;
|
||||||
|
|
||||||
|
# TLS versions supported for the SMTP server
|
||||||
|
smtpd_tls_protocols = ">=TLSv1.2";
|
||||||
|
smtpd_tls_mandatory_protocols = ">=TLSv1.2";
|
||||||
|
|
||||||
|
# Require ciphersuites that OpenSSL classifies as "High"
|
||||||
|
smtpd_tls_ciphers = "high";
|
||||||
|
smtpd_tls_mandatory_ciphers = "high";
|
||||||
|
|
||||||
|
# Exclude cipher suites with undesirable properties
|
||||||
|
smtpd_tls_exclude_ciphers = "eNULL, aNULL";
|
||||||
|
smtpd_tls_mandatory_exclude_ciphers = "eNULL, aNULL";
|
||||||
|
|
||||||
|
# Opportunistic DANE support when delivering mail to other servers
|
||||||
|
# https://www.postfix.org/postconf.5.html#smtp_tls_security_level
|
||||||
|
smtp_dns_support_level = "dnssec";
|
||||||
|
smtp_tls_security_level = "dane";
|
||||||
|
|
||||||
|
# TLS versions supported for the SMTP client
|
||||||
|
smtp_tls_protocols = ">=TLSv1.2";
|
||||||
|
smtp_tls_mandatory_protocols = ">=TLSv1.2";
|
||||||
|
|
||||||
|
# Require ciphersuites that OpenSSL classifies as "High"
|
||||||
|
smtp_tls_ciphers = "high";
|
||||||
|
smtp_tls_mandatory_ciphers = "high";
|
||||||
|
|
||||||
|
# Exclude ciphersuites with undesirable properties
|
||||||
|
smtp_tls_exclude_ciphers = "eNULL, aNULL";
|
||||||
|
smtp_tls_mandatory_exclude_ciphers = "eNULL, aNULL";
|
||||||
|
|
||||||
|
# Restrict and prioritize the following curves in the given order
|
||||||
|
# Excludes curves that have no widespread support, so we don't bloat the handshake needlessly.
|
||||||
|
# https://www.postfix.org/postconf.5.html#tls_eecdh_auto_curves
|
||||||
|
# https://ssl-config.mozilla.org/#server=postfix&version=3.10&config=intermediate&openssl=3.4.1&guideline=5.7
|
||||||
|
tls_eecdh_auto_curves = [
|
||||||
|
"X25519"
|
||||||
|
"prime256v1"
|
||||||
|
"secp384r1"
|
||||||
|
];
|
||||||
|
|
||||||
|
# Disable FFDHE on TLSv1.3 because it is slower than elliptic curves
|
||||||
|
# https://www.postfix.org/postconf.5.html#tls_ffdhe_auto_groups
|
||||||
|
tls_ffdhe_auto_groups = [ ];
|
||||||
|
|
||||||
|
# As long as all cipher suites are considered safe, let the client use its preferred cipher
|
||||||
|
tls_preempt_cipherlist = false;
|
||||||
|
|
||||||
|
# Log only a summary message on TLS handshake completion
|
||||||
|
smtp_tls_loglevel = "1";
|
||||||
|
smtpd_tls_loglevel = "1";
|
||||||
|
|
||||||
|
smtpd_milters = smtpdMilters;
|
||||||
|
non_smtpd_milters = lib.mkIf cfg.dkimSigning [ "unix:/run/rspamd/rspamd-milter.sock" ];
|
||||||
|
milter_protocol = "6";
|
||||||
|
milter_mail_macros = "i {mail_addr} {client_addr} {client_name} {auth_authen}";
|
||||||
};
|
};
|
||||||
|
|
||||||
services.postfix = {
|
submissionOptions = submissionOptions;
|
||||||
enable = true;
|
submissionsOptions = submissionOptions;
|
||||||
hostname = "${sendingFqdn}";
|
|
||||||
networksStyle = "host";
|
|
||||||
mapFiles."valias" = valiases_file;
|
|
||||||
mapFiles."regex_valias" = regex_valiases_file;
|
|
||||||
mapFiles."vaccounts" = vaccounts_file;
|
|
||||||
mapFiles."regex_vaccounts" = regex_vaccounts_file;
|
|
||||||
mapFiles."denied_recipients" = denied_recipients_file;
|
|
||||||
mapFiles."reject_senders" = reject_senders_file;
|
|
||||||
mapFiles."reject_recipients" = reject_recipients_file;
|
|
||||||
enableSubmission = cfg.enableSubmission;
|
|
||||||
enableSubmissions = cfg.enableSubmissionSsl;
|
|
||||||
virtual = lookupTableToString (mergeLookupTables [
|
|
||||||
all_valiases_postfix
|
|
||||||
catchAllPostfix
|
|
||||||
forwards
|
|
||||||
]);
|
|
||||||
|
|
||||||
config = {
|
masterConfig = {
|
||||||
smtpd_tls_chain_files = [
|
"lmtp" = {
|
||||||
"${keyPath}"
|
# Add headers when delivering, see http://www.postfix.org/smtp.8.html
|
||||||
"${certificatePath}"
|
# D => Delivered-To, O => X-Original-To, R => Return-Path
|
||||||
];
|
args = [ "flags=O" ];
|
||||||
|
|
||||||
# Extra Config
|
|
||||||
mydestination = "";
|
|
||||||
recipient_delimiter = cfg.recipientDelimiter;
|
|
||||||
smtpd_banner = "${fqdn} ESMTP NO UCE";
|
|
||||||
disable_vrfy_command = true;
|
|
||||||
message_size_limit = toString cfg.messageSizeLimit;
|
|
||||||
|
|
||||||
# virtual mail system
|
|
||||||
virtual_uid_maps = "static:5000";
|
|
||||||
virtual_gid_maps = "static:5000";
|
|
||||||
virtual_mailbox_base = mailDirectory;
|
|
||||||
virtual_mailbox_domains = vhosts_file;
|
|
||||||
virtual_mailbox_maps =
|
|
||||||
[
|
|
||||||
(mappedFile "valias")
|
|
||||||
]
|
|
||||||
++ lib.optionals cfg.ldap.enable [
|
|
||||||
"ldap:${ldapVirtualMailboxMapFile}"
|
|
||||||
]
|
|
||||||
++ lib.optionals (regex_valiases_postfix != { }) [
|
|
||||||
(mappedRegexFile "regex_valias")
|
|
||||||
];
|
|
||||||
virtual_alias_maps = lib.mkAfter (
|
|
||||||
lib.optionals (regex_valiases_postfix != { }) [
|
|
||||||
(mappedRegexFile "regex_valias")
|
|
||||||
]
|
|
||||||
);
|
|
||||||
virtual_transport = "lmtp:unix:/run/dovecot2/dovecot-lmtp";
|
|
||||||
# Avoid leakage of X-Original-To, X-Delivered-To headers between recipients
|
|
||||||
lmtp_destination_recipient_limit = "1";
|
|
||||||
|
|
||||||
# sasl with dovecot
|
|
||||||
smtpd_sasl_type = "dovecot";
|
|
||||||
smtpd_sasl_path = "/run/dovecot2/auth";
|
|
||||||
smtpd_sasl_auth_enable = true;
|
|
||||||
smtpd_relay_restrictions = [
|
|
||||||
"permit_mynetworks"
|
|
||||||
"permit_sasl_authenticated"
|
|
||||||
"reject_unauth_destination"
|
|
||||||
];
|
|
||||||
|
|
||||||
# reject selected senders
|
|
||||||
smtpd_sender_restrictions = [
|
|
||||||
"check_sender_access ${mappedFile "reject_senders"}"
|
|
||||||
];
|
|
||||||
|
|
||||||
smtpd_recipient_restrictions = [
|
|
||||||
# reject selected recipients
|
|
||||||
"check_recipient_access ${mappedFile "denied_recipients"}"
|
|
||||||
"check_recipient_access ${mappedFile "reject_recipients"}"
|
|
||||||
# quota checking
|
|
||||||
"check_policy_service unix:/run/dovecot2/quota-status"
|
|
||||||
];
|
|
||||||
|
|
||||||
# TLS for incoming mail is optional
|
|
||||||
smtpd_tls_security_level = "may";
|
|
||||||
|
|
||||||
# But required for authentication attempts
|
|
||||||
smtpd_tls_auth_only = true;
|
|
||||||
|
|
||||||
# TLS versions supported for the SMTP server
|
|
||||||
smtpd_tls_protocols = ">=TLSv1.2";
|
|
||||||
smtpd_tls_mandatory_protocols = ">=TLSv1.2";
|
|
||||||
|
|
||||||
# Require ciphersuites that OpenSSL classifies as "High"
|
|
||||||
smtpd_tls_ciphers = "high";
|
|
||||||
smtpd_tls_mandatory_ciphers = "high";
|
|
||||||
|
|
||||||
# Exclude cipher suites with undesirable properties
|
|
||||||
smtpd_tls_exclude_ciphers = "eNULL, aNULL";
|
|
||||||
smtpd_tls_mandatory_exclude_ciphers = "eNULL, aNULL";
|
|
||||||
|
|
||||||
# Opportunistic DANE support when delivering mail to other servers
|
|
||||||
# https://www.postfix.org/postconf.5.html#smtp_tls_security_level
|
|
||||||
smtp_dns_support_level = "dnssec";
|
|
||||||
smtp_tls_security_level = "dane";
|
|
||||||
|
|
||||||
# TLS versions supported for the SMTP client
|
|
||||||
smtp_tls_protocols = ">=TLSv1.2";
|
|
||||||
smtp_tls_mandatory_protocols = ">=TLSv1.2";
|
|
||||||
|
|
||||||
# Require ciphersuites that OpenSSL classifies as "High"
|
|
||||||
smtp_tls_ciphers = "high";
|
|
||||||
smtp_tls_mandatory_ciphers = "high";
|
|
||||||
|
|
||||||
# Exclude ciphersuites with undesirable properties
|
|
||||||
smtp_tls_exclude_ciphers = "eNULL, aNULL";
|
|
||||||
smtp_tls_mandatory_exclude_ciphers = "eNULL, aNULL";
|
|
||||||
|
|
||||||
# Restrict and prioritize the following curves in the given order
|
|
||||||
# Excludes curves that have no widespread support, so we don't bloat the handshake needlessly.
|
|
||||||
# https://www.postfix.org/postconf.5.html#tls_eecdh_auto_curves
|
|
||||||
# https://ssl-config.mozilla.org/#server=postfix&version=3.10&config=intermediate&openssl=3.4.1&guideline=5.7
|
|
||||||
tls_eecdh_auto_curves = [
|
|
||||||
"X25519"
|
|
||||||
"prime256v1"
|
|
||||||
"secp384r1"
|
|
||||||
];
|
|
||||||
|
|
||||||
# Disable FFDHE on TLSv1.3 because it is slower than elliptic curves
|
|
||||||
# https://www.postfix.org/postconf.5.html#tls_ffdhe_auto_groups
|
|
||||||
tls_ffdhe_auto_groups = [ ];
|
|
||||||
|
|
||||||
# As long as all cipher suites are considered safe, let the client use its preferred cipher
|
|
||||||
tls_preempt_cipherlist = false;
|
|
||||||
|
|
||||||
# Log only a summary message on TLS handshake completion
|
|
||||||
smtp_tls_loglevel = "1";
|
|
||||||
smtpd_tls_loglevel = "1";
|
|
||||||
|
|
||||||
smtpd_milters = smtpdMilters;
|
|
||||||
non_smtpd_milters = lib.mkIf cfg.dkimSigning [ "unix:/run/rspamd/rspamd-milter.sock" ];
|
|
||||||
milter_protocol = "6";
|
|
||||||
milter_mail_macros = "i {mail_addr} {client_addr} {client_name} {auth_authen}";
|
|
||||||
};
|
};
|
||||||
|
"submission-header-cleanup" = {
|
||||||
submissionOptions = submissionOptions;
|
type = "unix";
|
||||||
submissionsOptions = submissionOptions;
|
private = false;
|
||||||
|
chroot = false;
|
||||||
masterConfig = {
|
maxproc = 0;
|
||||||
"lmtp" = {
|
command = "cleanup";
|
||||||
# Add headers when delivering, see http://www.postfix.org/smtp.8.html
|
args = [
|
||||||
# D => Delivered-To, O => X-Original-To, R => Return-Path
|
"-o"
|
||||||
args = [ "flags=O" ];
|
"header_checks=pcre:${submissionHeaderCleanupRules}"
|
||||||
};
|
];
|
||||||
"submission-header-cleanup" = {
|
|
||||||
type = "unix";
|
|
||||||
private = false;
|
|
||||||
chroot = false;
|
|
||||||
maxproc = 0;
|
|
||||||
command = "cleanup";
|
|
||||||
args = [
|
|
||||||
"-o"
|
|
||||||
"header_checks=pcre:${submissionHeaderCleanupRules}"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,9 +21,12 @@
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
|
|
||||||
with lib;
|
|
||||||
|
|
||||||
let
|
let
|
||||||
|
inherit (lib)
|
||||||
|
optionalString
|
||||||
|
mkIf
|
||||||
|
;
|
||||||
|
|
||||||
cfg = config.mailserver;
|
cfg = config.mailserver;
|
||||||
|
|
||||||
preexecDefined = cfg.backup.cmdPreexec != null;
|
preexecDefined = cfg.backup.cmdPreexec != null;
|
||||||
|
|
|
@ -52,223 +52,221 @@ let
|
||||||
'';
|
'';
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
config =
|
config = lib.mkIf cfg.enable {
|
||||||
with cfg;
|
environment.systemPackages = lib.mkBefore [
|
||||||
lib.mkIf enable {
|
(pkgs.runCommand "rspamc-wrapped"
|
||||||
environment.systemPackages = lib.mkBefore [
|
{
|
||||||
(pkgs.runCommand "rspamc-wrapped"
|
nativeBuildInputs = with pkgs; [ makeWrapper ];
|
||||||
{
|
}
|
||||||
nativeBuildInputs = with pkgs; [ makeWrapper ];
|
''
|
||||||
}
|
makeWrapper ${pkgs.rspamd}/bin/rspamc $out/bin/rspamc \
|
||||||
''
|
--add-flags "-h /run/rspamd/worker-controller.sock"
|
||||||
makeWrapper ${pkgs.rspamd}/bin/rspamc $out/bin/rspamc \
|
''
|
||||||
--add-flags "-h /run/rspamd/worker-controller.sock"
|
)
|
||||||
''
|
];
|
||||||
)
|
|
||||||
];
|
|
||||||
|
|
||||||
services.rspamd = {
|
services.rspamd = {
|
||||||
enable = true;
|
enable = true;
|
||||||
inherit debug;
|
inherit (cfg) debug;
|
||||||
locals = {
|
locals = {
|
||||||
"milter_headers.conf" = {
|
"milter_headers.conf" = {
|
||||||
text = ''
|
text = ''
|
||||||
extended_spam_headers = true;
|
extended_spam_headers = true;
|
||||||
'';
|
'';
|
||||||
};
|
|
||||||
"redis.conf" = {
|
|
||||||
text =
|
|
||||||
''
|
|
||||||
servers = "${
|
|
||||||
if cfg.redis.port == null then
|
|
||||||
cfg.redis.address
|
|
||||||
else
|
|
||||||
"${cfg.redis.address}:${toString cfg.redis.port}"
|
|
||||||
}";
|
|
||||||
''
|
|
||||||
+ (lib.optionalString (cfg.redis.password != null) ''
|
|
||||||
password = "${cfg.redis.password}";
|
|
||||||
'');
|
|
||||||
};
|
|
||||||
"classifier-bayes.conf" = {
|
|
||||||
text = ''
|
|
||||||
cache {
|
|
||||||
backend = "redis";
|
|
||||||
}
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
"antivirus.conf" = lib.mkIf cfg.virusScanning {
|
|
||||||
text = ''
|
|
||||||
clamav {
|
|
||||||
action = "reject";
|
|
||||||
symbol = "CLAM_VIRUS";
|
|
||||||
type = "clamav";
|
|
||||||
log_clean = true;
|
|
||||||
servers = "/run/clamav/clamd.ctl";
|
|
||||||
scan_mime_parts = false; # scan mail as a whole unit, not parts. seems to be needed to work at all
|
|
||||||
}
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
"dkim_signing.conf" = {
|
|
||||||
text = ''
|
|
||||||
enabled = ${lib.boolToString cfg.dkimSigning};
|
|
||||||
path = "${cfg.dkimKeyDirectory}/$domain.$selector.key";
|
|
||||||
selector = "${cfg.dkimSelector}";
|
|
||||||
# Allow for usernames w/o domain part
|
|
||||||
allow_username_mismatch = true
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
"dmarc.conf" = {
|
|
||||||
text = ''
|
|
||||||
${lib.optionalString cfg.dmarcReporting.enable ''
|
|
||||||
reporting {
|
|
||||||
enabled = true;
|
|
||||||
email = "${cfg.dmarcReporting.email}";
|
|
||||||
domain = "${cfg.dmarcReporting.domain}";
|
|
||||||
org_name = "${cfg.dmarcReporting.organizationName}";
|
|
||||||
from_name = "${cfg.dmarcReporting.fromName}";
|
|
||||||
msgid_from = "${cfg.dmarcReporting.domain}";
|
|
||||||
${lib.optionalString (cfg.dmarcReporting.excludeDomains != [ ]) ''
|
|
||||||
exclude_domains = ${builtins.toJSON cfg.dmarcReporting.excludeDomains};
|
|
||||||
''}
|
|
||||||
}''}
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
"redis.conf" = {
|
||||||
workers.rspamd_proxy = {
|
text =
|
||||||
type = "rspamd_proxy";
|
''
|
||||||
bindSockets = [
|
servers = "${
|
||||||
{
|
if cfg.redis.port == null then
|
||||||
socket = "/run/rspamd/rspamd-milter.sock";
|
cfg.redis.address
|
||||||
mode = "0664";
|
else
|
||||||
}
|
"${cfg.redis.address}:${toString cfg.redis.port}"
|
||||||
];
|
}";
|
||||||
count = 1; # Do not spawn too many processes of this type
|
''
|
||||||
extraConfig = ''
|
+ (lib.optionalString (cfg.redis.password != null) ''
|
||||||
milter = yes; # Enable milter mode
|
password = "${cfg.redis.password}";
|
||||||
timeout = 120s; # Needed for Milter usually
|
'');
|
||||||
|
};
|
||||||
upstream "local" {
|
"classifier-bayes.conf" = {
|
||||||
default = yes; # Self-scan upstreams are always default
|
text = ''
|
||||||
self_scan = yes; # Enable self-scan
|
cache {
|
||||||
|
backend = "redis";
|
||||||
}
|
}
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
workers.controller = {
|
"antivirus.conf" = lib.mkIf cfg.virusScanning {
|
||||||
type = "controller";
|
text = ''
|
||||||
count = 1;
|
clamav {
|
||||||
bindSockets = [
|
action = "reject";
|
||||||
{
|
symbol = "CLAM_VIRUS";
|
||||||
socket = "/run/rspamd/worker-controller.sock";
|
type = "clamav";
|
||||||
mode = "0666";
|
log_clean = true;
|
||||||
|
servers = "/run/clamav/clamd.ctl";
|
||||||
|
scan_mime_parts = false; # scan mail as a whole unit, not parts. seems to be needed to work at all
|
||||||
}
|
}
|
||||||
];
|
|
||||||
includes = [ ];
|
|
||||||
extraConfig = ''
|
|
||||||
static_dir = "''${WWWDIR}"; # Serve the web UI static assets
|
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
"dkim_signing.conf" = {
|
||||||
};
|
text = ''
|
||||||
|
enabled = ${lib.boolToString cfg.dkimSigning};
|
||||||
services.redis.servers.rspamd.enable = lib.mkDefault true;
|
path = "${cfg.dkimKeyDirectory}/$domain.$selector.key";
|
||||||
|
selector = "${cfg.dkimSelector}";
|
||||||
systemd.tmpfiles.settings."10-rspamd.conf" = {
|
# Allow for usernames w/o domain part
|
||||||
"${cfg.dkimKeyDirectory}" = {
|
allow_username_mismatch = true
|
||||||
d = {
|
'';
|
||||||
# Create /var/dkim owned by rspamd user/group
|
};
|
||||||
user = rspamdUser;
|
"dmarc.conf" = {
|
||||||
group = rspamdGroup;
|
text = ''
|
||||||
};
|
${lib.optionalString cfg.dmarcReporting.enable ''
|
||||||
Z = {
|
reporting {
|
||||||
# Recursively adjust permissions in /var/dkim
|
enabled = true;
|
||||||
user = rspamdUser;
|
email = "${cfg.dmarcReporting.email}";
|
||||||
group = rspamdGroup;
|
domain = "${cfg.dmarcReporting.domain}";
|
||||||
};
|
org_name = "${cfg.dmarcReporting.organizationName}";
|
||||||
|
from_name = "${cfg.dmarcReporting.fromName}";
|
||||||
|
msgid_from = "${cfg.dmarcReporting.domain}";
|
||||||
|
${lib.optionalString (cfg.dmarcReporting.excludeDomains != [ ]) ''
|
||||||
|
exclude_domains = ${builtins.toJSON cfg.dmarcReporting.excludeDomains};
|
||||||
|
''}
|
||||||
|
}''}
|
||||||
|
'';
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
systemd.services.rspamd = {
|
workers.rspamd_proxy = {
|
||||||
requires = [ "redis-rspamd.service" ] ++ (lib.optional cfg.virusScanning "clamav-daemon.service");
|
type = "rspamd_proxy";
|
||||||
after = [ "redis-rspamd.service" ] ++ (lib.optional cfg.virusScanning "clamav-daemon.service");
|
bindSockets = [
|
||||||
serviceConfig = lib.mkMerge [
|
|
||||||
{
|
{
|
||||||
SupplementaryGroups = [ config.services.redis.servers.rspamd.group ];
|
socket = "/run/rspamd/rspamd-milter.sock";
|
||||||
|
mode = "0664";
|
||||||
}
|
}
|
||||||
(lib.optionalAttrs cfg.dkimSigning {
|
|
||||||
ExecStartPre = map createDkimKeypair cfg.domains;
|
|
||||||
ReadWritePaths = [ cfg.dkimKeyDirectory ];
|
|
||||||
})
|
|
||||||
];
|
];
|
||||||
};
|
count = 1; # Do not spawn too many processes of this type
|
||||||
|
extraConfig = ''
|
||||||
|
milter = yes; # Enable milter mode
|
||||||
|
timeout = 120s; # Needed for Milter usually
|
||||||
|
|
||||||
systemd.services.rspamd-dmarc-reporter = lib.optionalAttrs cfg.dmarcReporting.enable {
|
upstream "local" {
|
||||||
# Explicitly select yesterday's date to work around broken
|
default = yes; # Self-scan upstreams are always default
|
||||||
# default behaviour when called without a date.
|
self_scan = yes; # Enable self-scan
|
||||||
# https://github.com/rspamd/rspamd/issues/4062
|
}
|
||||||
script = ''
|
|
||||||
${pkgs.rspamd}/bin/rspamadm dmarc_report $(date -d "yesterday" "+%Y%m%d")
|
|
||||||
'';
|
'';
|
||||||
serviceConfig = {
|
|
||||||
User = "${config.services.rspamd.user}";
|
|
||||||
Group = "${config.services.rspamd.group}";
|
|
||||||
|
|
||||||
AmbientCapabilities = [ ];
|
|
||||||
CapabilityBoundingSet = "";
|
|
||||||
DevicePolicy = "closed";
|
|
||||||
IPAddressAllow = "localhost";
|
|
||||||
LockPersonality = true;
|
|
||||||
NoNewPrivileges = true;
|
|
||||||
PrivateDevices = true;
|
|
||||||
PrivateMounts = true;
|
|
||||||
PrivateTmp = true;
|
|
||||||
PrivateUsers = true;
|
|
||||||
ProtectClock = true;
|
|
||||||
ProtectControlGroups = true;
|
|
||||||
ProtectHome = true;
|
|
||||||
ProtectHostname = true;
|
|
||||||
ProtectKernelLogs = true;
|
|
||||||
ProtectKernelModules = true;
|
|
||||||
ProtectKernelTunables = true;
|
|
||||||
ProtectProc = "invisible";
|
|
||||||
ProcSubset = "pid";
|
|
||||||
ProtectSystem = "strict";
|
|
||||||
RemoveIPC = true;
|
|
||||||
RestrictAddressFamilies = [
|
|
||||||
"AF_INET"
|
|
||||||
"AF_INET6"
|
|
||||||
];
|
|
||||||
RestrictNamespaces = true;
|
|
||||||
RestrictRealtime = true;
|
|
||||||
RestrictSUIDSGID = true;
|
|
||||||
SystemCallArchitectures = "native";
|
|
||||||
SystemCallFilter = [
|
|
||||||
"@system-service"
|
|
||||||
"~@privileged"
|
|
||||||
];
|
|
||||||
UMask = "0077";
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
workers.controller = {
|
||||||
systemd.timers.rspamd-dmarc-reporter = lib.optionalAttrs cfg.dmarcReporting.enable {
|
type = "controller";
|
||||||
description = "Daily delivery of aggregated DMARC reports";
|
count = 1;
|
||||||
wantedBy = [
|
bindSockets = [
|
||||||
"timers.target"
|
{
|
||||||
|
socket = "/run/rspamd/worker-controller.sock";
|
||||||
|
mode = "0666";
|
||||||
|
}
|
||||||
];
|
];
|
||||||
timerConfig = {
|
includes = [ ];
|
||||||
OnCalendar = "daily";
|
extraConfig = ''
|
||||||
Persistent = true;
|
static_dir = "''${WWWDIR}"; # Serve the web UI static assets
|
||||||
RandomizedDelaySec = 86400;
|
'';
|
||||||
FixedRandomDelay = true;
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
services.redis.servers.rspamd.enable = lib.mkDefault true;
|
||||||
|
|
||||||
|
systemd.tmpfiles.settings."10-rspamd.conf" = {
|
||||||
|
"${cfg.dkimKeyDirectory}" = {
|
||||||
|
d = {
|
||||||
|
# Create /var/dkim owned by rspamd user/group
|
||||||
|
user = rspamdUser;
|
||||||
|
group = rspamdGroup;
|
||||||
|
};
|
||||||
|
Z = {
|
||||||
|
# Recursively adjust permissions in /var/dkim
|
||||||
|
user = rspamdUser;
|
||||||
|
group = rspamdGroup;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
systemd.services.postfix = {
|
|
||||||
after = [ rspamdSocket ];
|
|
||||||
requires = [ rspamdSocket ];
|
|
||||||
};
|
|
||||||
|
|
||||||
users.extraUsers.${postfixCfg.user}.extraGroups = [ rspamdCfg.group ];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
systemd.services.rspamd = {
|
||||||
|
requires = [ "redis-rspamd.service" ] ++ (lib.optional cfg.virusScanning "clamav-daemon.service");
|
||||||
|
after = [ "redis-rspamd.service" ] ++ (lib.optional cfg.virusScanning "clamav-daemon.service");
|
||||||
|
serviceConfig = lib.mkMerge [
|
||||||
|
{
|
||||||
|
SupplementaryGroups = [ config.services.redis.servers.rspamd.group ];
|
||||||
|
}
|
||||||
|
(lib.optionalAttrs cfg.dkimSigning {
|
||||||
|
ExecStartPre = map createDkimKeypair cfg.domains;
|
||||||
|
ReadWritePaths = [ cfg.dkimKeyDirectory ];
|
||||||
|
})
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services.rspamd-dmarc-reporter = lib.optionalAttrs cfg.dmarcReporting.enable {
|
||||||
|
# Explicitly select yesterday's date to work around broken
|
||||||
|
# default behaviour when called without a date.
|
||||||
|
# https://github.com/rspamd/rspamd/issues/4062
|
||||||
|
script = ''
|
||||||
|
${pkgs.rspamd}/bin/rspamadm dmarc_report $(date -d "yesterday" "+%Y%m%d")
|
||||||
|
'';
|
||||||
|
serviceConfig = {
|
||||||
|
User = "${config.services.rspamd.user}";
|
||||||
|
Group = "${config.services.rspamd.group}";
|
||||||
|
|
||||||
|
AmbientCapabilities = [ ];
|
||||||
|
CapabilityBoundingSet = "";
|
||||||
|
DevicePolicy = "closed";
|
||||||
|
IPAddressAllow = "localhost";
|
||||||
|
LockPersonality = true;
|
||||||
|
NoNewPrivileges = true;
|
||||||
|
PrivateDevices = true;
|
||||||
|
PrivateMounts = true;
|
||||||
|
PrivateTmp = true;
|
||||||
|
PrivateUsers = true;
|
||||||
|
ProtectClock = true;
|
||||||
|
ProtectControlGroups = true;
|
||||||
|
ProtectHome = true;
|
||||||
|
ProtectHostname = true;
|
||||||
|
ProtectKernelLogs = true;
|
||||||
|
ProtectKernelModules = true;
|
||||||
|
ProtectKernelTunables = true;
|
||||||
|
ProtectProc = "invisible";
|
||||||
|
ProcSubset = "pid";
|
||||||
|
ProtectSystem = "strict";
|
||||||
|
RemoveIPC = true;
|
||||||
|
RestrictAddressFamilies = [
|
||||||
|
"AF_INET"
|
||||||
|
"AF_INET6"
|
||||||
|
];
|
||||||
|
RestrictNamespaces = true;
|
||||||
|
RestrictRealtime = true;
|
||||||
|
RestrictSUIDSGID = true;
|
||||||
|
SystemCallArchitectures = "native";
|
||||||
|
SystemCallFilter = [
|
||||||
|
"@system-service"
|
||||||
|
"~@privileged"
|
||||||
|
];
|
||||||
|
UMask = "0077";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.timers.rspamd-dmarc-reporter = lib.optionalAttrs cfg.dmarcReporting.enable {
|
||||||
|
description = "Daily delivery of aggregated DMARC reports";
|
||||||
|
wantedBy = [
|
||||||
|
"timers.target"
|
||||||
|
];
|
||||||
|
timerConfig = {
|
||||||
|
OnCalendar = "daily";
|
||||||
|
Persistent = true;
|
||||||
|
RandomizedDelaySec = 86400;
|
||||||
|
FixedRandomDelay = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services.postfix = {
|
||||||
|
after = [ rspamdSocket ];
|
||||||
|
requires = [ rspamdSocket ];
|
||||||
|
};
|
||||||
|
|
||||||
|
users.extraUsers.${postfixCfg.user}.extraGroups = [ rspamdCfg.group ];
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,61 +32,59 @@ let
|
||||||
[ "acme-finished-${cfg.fqdn}.target" ];
|
[ "acme-finished-${cfg.fqdn}.target" ];
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
config =
|
config = lib.mkIf cfg.enable {
|
||||||
with cfg;
|
# Create self signed certificate
|
||||||
lib.mkIf enable {
|
systemd.services.mailserver-selfsigned-certificate =
|
||||||
# Create self signed certificate
|
lib.mkIf (cfg.certificateScheme == "selfsigned")
|
||||||
systemd.services.mailserver-selfsigned-certificate =
|
{
|
||||||
lib.mkIf (cfg.certificateScheme == "selfsigned")
|
after = [ "local-fs.target" ];
|
||||||
{
|
script = ''
|
||||||
after = [ "local-fs.target" ];
|
# Create certificates if they do not exist yet
|
||||||
script = ''
|
dir="${cfg.certificateDirectory}"
|
||||||
# Create certificates if they do not exist yet
|
fqdn="${cfg.fqdn}"
|
||||||
dir="${cfg.certificateDirectory}"
|
[[ $fqdn == /* ]] && fqdn=$(< "$fqdn")
|
||||||
fqdn="${cfg.fqdn}"
|
key="$dir/key-${cfg.fqdn}.pem";
|
||||||
[[ $fqdn == /* ]] && fqdn=$(< "$fqdn")
|
cert="$dir/cert-${cfg.fqdn}.pem";
|
||||||
key="$dir/key-${cfg.fqdn}.pem";
|
|
||||||
cert="$dir/cert-${cfg.fqdn}.pem";
|
|
||||||
|
|
||||||
if [[ ! -f $key || ! -f $cert ]]; then
|
if [[ ! -f $key || ! -f $cert ]]; then
|
||||||
mkdir -p "${cfg.certificateDirectory}"
|
mkdir -p "${cfg.certificateDirectory}"
|
||||||
(umask 077; "${pkgs.openssl}/bin/openssl" genrsa -out "$key" 2048) &&
|
(umask 077; "${pkgs.openssl}/bin/openssl" genrsa -out "$key" 2048) &&
|
||||||
"${pkgs.openssl}/bin/openssl" req -new -key "$key" -x509 -subj "/CN=$fqdn" \
|
"${pkgs.openssl}/bin/openssl" req -new -key "$key" -x509 -subj "/CN=$fqdn" \
|
||||||
-days 3650 -out "$cert"
|
-days 3650 -out "$cert"
|
||||||
fi
|
fi
|
||||||
'';
|
|
||||||
serviceConfig = {
|
|
||||||
Type = "oneshot";
|
|
||||||
PrivateTmp = true;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
# Create maildir folder before dovecot startup
|
|
||||||
systemd.services.dovecot2 = {
|
|
||||||
wants = certificatesDeps;
|
|
||||||
after = certificatesDeps;
|
|
||||||
preStart =
|
|
||||||
let
|
|
||||||
directories = lib.strings.escapeShellArgs (
|
|
||||||
[ mailDirectory ] ++ lib.optional (cfg.indexDir != null) cfg.indexDir
|
|
||||||
);
|
|
||||||
in
|
|
||||||
''
|
|
||||||
# Create mail directory and set permissions. See
|
|
||||||
# <https://doc.dovecot.org/main/core/config/shared_mailboxes.html#filesystem-permissions-1>.
|
|
||||||
# Prevent world-readable paths, even temporarily.
|
|
||||||
umask 007
|
|
||||||
mkdir -p ${directories}
|
|
||||||
chgrp "${vmailGroupName}" ${directories}
|
|
||||||
chmod 02770 ${directories}
|
|
||||||
'';
|
'';
|
||||||
};
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
PrivateTmp = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
# Postfix requires dovecot lmtp socket, dovecot auth socket and certificate to work
|
# Create maildir folder before dovecot startup
|
||||||
systemd.services.postfix = {
|
systemd.services.dovecot2 = {
|
||||||
wants = certificatesDeps;
|
wants = certificatesDeps;
|
||||||
after = [ "dovecot2.service" ] ++ lib.optional cfg.dkimSigning "rspamd.service" ++ certificatesDeps;
|
after = certificatesDeps;
|
||||||
requires = [ "dovecot2.service" ] ++ lib.optional cfg.dkimSigning "rspamd.service";
|
preStart =
|
||||||
};
|
let
|
||||||
|
directories = lib.strings.escapeShellArgs (
|
||||||
|
[ cfg.mailDirectory ] ++ lib.optional (cfg.indexDir != null) cfg.indexDir
|
||||||
|
);
|
||||||
|
in
|
||||||
|
''
|
||||||
|
# Create mail directory and set permissions. See
|
||||||
|
# <https://doc.dovecot.org/main/core/config/shared_mailboxes.html#filesystem-permissions-1>.
|
||||||
|
# Prevent world-readable paths, even temporarily.
|
||||||
|
umask 007
|
||||||
|
mkdir -p ${directories}
|
||||||
|
chgrp "${cfg.vmailGroupName}" ${directories}
|
||||||
|
chmod 02770 ${directories}
|
||||||
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# Postfix requires dovecot lmtp socket, dovecot auth socket and certificate to work
|
||||||
|
systemd.services.postfix = {
|
||||||
|
wants = certificatesDeps;
|
||||||
|
after = [ "dovecot2.service" ] ++ lib.optional cfg.dkimSigning "rspamd.service" ++ certificatesDeps;
|
||||||
|
requires = [ "dovecot2.service" ] ++ lib.optional cfg.dkimSigning "rspamd.service";
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue