postfix: complete remake of postfix service (#27276)
This commit is contained in:
parent
8a35f751d1
commit
af7c7b42c1
@ -9,7 +9,8 @@ let
|
|||||||
group = cfg.group;
|
group = cfg.group;
|
||||||
setgidGroup = cfg.setgidGroup;
|
setgidGroup = cfg.setgidGroup;
|
||||||
|
|
||||||
haveAliases = cfg.postmasterAlias != "" || cfg.rootAlias != "" || cfg.extraAliases != "";
|
haveAliases = cfg.postmasterAlias != "" || cfg.rootAlias != ""
|
||||||
|
|| cfg.extraAliases != "";
|
||||||
haveTransport = cfg.transport != "";
|
haveTransport = cfg.transport != "";
|
||||||
haveVirtual = cfg.virtual != "";
|
haveVirtual = cfg.virtual != "";
|
||||||
|
|
||||||
@ -25,149 +26,275 @@ let
|
|||||||
|
|
||||||
clientRestrictions = concatStringsSep ", " (clientAccess ++ dnsBl);
|
clientRestrictions = concatStringsSep ", " (clientAccess ++ dnsBl);
|
||||||
|
|
||||||
mainCf =
|
mainCf = let
|
||||||
''
|
escape = replaceStrings ["$"] ["$$"];
|
||||||
compatibility_level = 9999
|
mkList = items: "\n " + concatMapStringsSep "\n " escape items;
|
||||||
|
mkVal = value:
|
||||||
|
if isList value then mkList value
|
||||||
|
else " " + (if value == true then "yes"
|
||||||
|
else if value == false then "no"
|
||||||
|
else toString value);
|
||||||
|
mkEntry = name: value: "${escape name} =${mkVal value}";
|
||||||
|
in
|
||||||
|
concatStringsSep "\n" (mapAttrsToList mkEntry (recursiveUpdate defaultConf cfg.config))
|
||||||
|
+ "\n" + cfg.extraConfig;
|
||||||
|
|
||||||
mail_owner = ${user}
|
defaultConf = {
|
||||||
default_privs = nobody
|
compatibility_level = "9999";
|
||||||
|
mail_owner = user;
|
||||||
|
default_privs = "nobody";
|
||||||
|
|
||||||
# NixOS specific locations
|
# NixOS specific locations
|
||||||
data_directory = /var/lib/postfix/data
|
data_directory = "/var/lib/postfix/data";
|
||||||
queue_directory = /var/lib/postfix/queue
|
queue_directory = "/var/lib/postfix/queue";
|
||||||
|
|
||||||
# Default location of everything in package
|
# Default location of everything in package
|
||||||
meta_directory = ${pkgs.postfix}/etc/postfix
|
meta_directory = "${pkgs.postfix}/etc/postfix";
|
||||||
command_directory = ${pkgs.postfix}/bin
|
command_directory = "${pkgs.postfix}/bin";
|
||||||
sample_directory = /etc/postfix
|
sample_directory = "/etc/postfix";
|
||||||
newaliases_path = ${pkgs.postfix}/bin/newaliases
|
newaliases_path = "${pkgs.postfix}/bin/newaliases";
|
||||||
mailq_path = ${pkgs.postfix}/bin/mailq
|
mailq_path = "${pkgs.postfix}/bin/mailq";
|
||||||
readme_directory = no
|
readme_directory = false;
|
||||||
sendmail_path = ${pkgs.postfix}/bin/sendmail
|
sendmail_path = "${pkgs.postfix}/bin/sendmail";
|
||||||
daemon_directory = ${pkgs.postfix}/libexec/postfix
|
daemon_directory = "${pkgs.postfix}/libexec/postfix";
|
||||||
manpage_directory = ${pkgs.postfix}/share/man
|
manpage_directory = "${pkgs.postfix}/share/man";
|
||||||
html_directory = ${pkgs.postfix}/share/postfix/doc/html
|
html_directory = "${pkgs.postfix}/share/postfix/doc/html";
|
||||||
shlib_directory = no
|
shlib_directory = false;
|
||||||
|
relayhost = if cfg.lookupMX || cfg.relayHost == ""
|
||||||
|
then cfg.relayHost
|
||||||
|
else "[${cfg.relayHost}]";
|
||||||
|
mail_spool_directory = "/var/spool/mail/";
|
||||||
|
setgid_group = setgidGroup;
|
||||||
|
}
|
||||||
|
// optionalAttrs config.networking.enableIPv6 { inet_protocols = "all"; }
|
||||||
|
// optionalAttrs (cfg.networks != null) { mynetworks = cfg.networks; }
|
||||||
|
// optionalAttrs (cfg.networksStyle != "") { mynetworks_style = cfg.networksStyle; }
|
||||||
|
// optionalAttrs (cfg.hostname != "") { myhostname = cfg.hostname; }
|
||||||
|
// optionalAttrs (cfg.domain != "") { mydomain = cfg.domain; }
|
||||||
|
// optionalAttrs (cfg.origin != "") { myorigin = cfg.origin; }
|
||||||
|
// optionalAttrs (cfg.destination != null) { mydestination = cfg.destination; }
|
||||||
|
// optionalAttrs (cfg.relayDomains != null) { relay_domains = cfg.relayDomains; }
|
||||||
|
// optionalAttrs (cfg.recipientDelimiter != "") { recipient_delimiter = cfg.recipientDelimiter; }
|
||||||
|
// optionalAttrs haveAliases { alias_maps = "${cfg.aliasMapType}:/etc/postfix/aliases"; }
|
||||||
|
// optionalAttrs haveTransport { transport_maps = "hash:/etc/postfx/transport"; }
|
||||||
|
// optionalAttrs haveVirtual { virtual_alias_maps = "${cfg.virtualMapType}:/etc/postfix/virtual"; }
|
||||||
|
// optionalAttrs (cfg.dnsBlacklists != []) { smtpd_client_restrictions = clientRestrictions; }
|
||||||
|
// optionalAttrs cfg.enableHeaderChecks { header_checks = "regexp:/etc/postfix/header_checks"; }
|
||||||
|
// optionalAttrs (cfg.sslCert != "") {
|
||||||
|
smtp_tls_CAfile = cfg.sslCACert;
|
||||||
|
smtp_tls_cert_file = cfg.sslCert;
|
||||||
|
smtp_tls_key_file = cfg.sslKey;
|
||||||
|
|
||||||
''
|
smtp_use_tls = true;
|
||||||
+ optionalString config.networking.enableIPv6 ''
|
|
||||||
inet_protocols = all
|
|
||||||
''
|
|
||||||
+ (if cfg.networks != null then
|
|
||||||
''
|
|
||||||
mynetworks = ${concatStringsSep ", " cfg.networks}
|
|
||||||
''
|
|
||||||
else if cfg.networksStyle != "" then
|
|
||||||
''
|
|
||||||
mynetworks_style = ${cfg.networksStyle}
|
|
||||||
''
|
|
||||||
else
|
|
||||||
"")
|
|
||||||
+ optionalString (cfg.hostname != "") ''
|
|
||||||
myhostname = ${cfg.hostname}
|
|
||||||
''
|
|
||||||
+ optionalString (cfg.domain != "") ''
|
|
||||||
mydomain = ${cfg.domain}
|
|
||||||
''
|
|
||||||
+ optionalString (cfg.origin != "") ''
|
|
||||||
myorigin = ${cfg.origin}
|
|
||||||
''
|
|
||||||
+ optionalString (cfg.destination != null) ''
|
|
||||||
mydestination = ${concatStringsSep ", " cfg.destination}
|
|
||||||
''
|
|
||||||
+ optionalString (cfg.relayDomains != null) ''
|
|
||||||
relay_domains = ${concatStringsSep ", " cfg.relayDomains}
|
|
||||||
''
|
|
||||||
+ ''
|
|
||||||
relayhost = ${if cfg.lookupMX || cfg.relayHost == "" then
|
|
||||||
cfg.relayHost
|
|
||||||
else
|
|
||||||
"[" + cfg.relayHost + "]"}
|
|
||||||
|
|
||||||
mail_spool_directory = /var/spool/mail/
|
smtpd_tls_CAfile = cfg.sslCACert;
|
||||||
|
smtpd_tls_cert_file = cfg.sslCert;
|
||||||
|
smtpd_tls_key_file = cfg.sslKey;
|
||||||
|
|
||||||
setgid_group = ${setgidGroup}
|
smtpd_use_tls = true;
|
||||||
''
|
};
|
||||||
+ optionalString (cfg.sslCert != "") ''
|
|
||||||
|
|
||||||
smtp_tls_CAfile = ${cfg.sslCACert}
|
masterCfOptions = { options, config, name, ... }: {
|
||||||
smtp_tls_cert_file = ${cfg.sslCert}
|
options = {
|
||||||
smtp_tls_key_file = ${cfg.sslKey}
|
name = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = name;
|
||||||
|
example = "smtp";
|
||||||
|
description = ''
|
||||||
|
The name of the service to run. Defaults to the attribute set key.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
smtp_use_tls = yes
|
type = mkOption {
|
||||||
|
type = types.enum [ "inet" "unix" "fifo" "pass" ];
|
||||||
|
default = "unix";
|
||||||
|
example = "inet";
|
||||||
|
description = "The type of the service";
|
||||||
|
};
|
||||||
|
|
||||||
smtpd_tls_CAfile = ${cfg.sslCACert}
|
private = mkOption {
|
||||||
smtpd_tls_cert_file = ${cfg.sslCert}
|
type = types.bool;
|
||||||
smtpd_tls_key_file = ${cfg.sslKey}
|
example = false;
|
||||||
|
description = ''
|
||||||
|
Whether the service's sockets and storage directory is restricted to
|
||||||
|
be only available via the mail system. If <literal>null</literal> is
|
||||||
|
given it uses the postfix default <literal>true</literal>.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
smtpd_use_tls = yes
|
privileged = mkOption {
|
||||||
''
|
type = types.bool;
|
||||||
+ optionalString (cfg.recipientDelimiter != "") ''
|
example = true;
|
||||||
recipient_delimiter = ${cfg.recipientDelimiter}
|
description = "";
|
||||||
''
|
};
|
||||||
+ optionalString haveAliases ''
|
|
||||||
alias_maps = hash:/etc/postfix/aliases
|
|
||||||
''
|
|
||||||
+ optionalString haveTransport ''
|
|
||||||
transport_maps = hash:/etc/postfix/transport
|
|
||||||
''
|
|
||||||
+ optionalString haveVirtual ''
|
|
||||||
virtual_alias_maps = hash:/etc/postfix/virtual
|
|
||||||
''
|
|
||||||
+ optionalString (cfg.dnsBlacklists != []) ''
|
|
||||||
smtpd_client_restrictions = ${clientRestrictions}
|
|
||||||
''
|
|
||||||
+ cfg.extraConfig;
|
|
||||||
|
|
||||||
masterCf = ''
|
chroot = mkOption {
|
||||||
# ==========================================================================
|
type = types.bool;
|
||||||
# service type private unpriv chroot wakeup maxproc command + args
|
example = true;
|
||||||
# (yes) (yes) (no) (never) (100)
|
description = ''
|
||||||
# ==========================================================================
|
Whether the service is chrooted to have only access to the
|
||||||
smtp inet n - n - - smtpd
|
<option>services.postfix.queueDir</option> and the closure of
|
||||||
'' + optionalString cfg.enableSubmission ''
|
store paths specified by the <option>program</option> option.
|
||||||
submission inet n - n - - smtpd
|
'';
|
||||||
${concatStringsSep "\n " (mapAttrsToList (x: y: "-o " + x + "=" + y) cfg.submissionOptions)}
|
};
|
||||||
''
|
|
||||||
+ ''
|
|
||||||
pickup unix n - n 60 1 pickup
|
|
||||||
cleanup unix n - n - 0 cleanup
|
|
||||||
qmgr unix n - n 300 1 qmgr
|
|
||||||
tlsmgr unix - - n 1000? 1 tlsmgr
|
|
||||||
rewrite unix - - n - - trivial-rewrite
|
|
||||||
bounce unix - - n - 0 bounce
|
|
||||||
defer unix - - n - 0 bounce
|
|
||||||
trace unix - - n - 0 bounce
|
|
||||||
verify unix - - n - 1 verify
|
|
||||||
flush unix n - n 1000? 0 flush
|
|
||||||
proxymap unix - - n - - proxymap
|
|
||||||
proxywrite unix - - n - 1 proxymap
|
|
||||||
''
|
|
||||||
+ optionalString cfg.enableSmtp ''
|
|
||||||
smtp unix - - n - - smtp
|
|
||||||
relay unix - - n - - smtp
|
|
||||||
-o smtp_fallback_relay=
|
|
||||||
# -o smtp_helo_timeout=5 -o smtp_connect_timeout=5
|
|
||||||
''
|
|
||||||
+ ''
|
|
||||||
showq unix n - n - - showq
|
|
||||||
error unix - - n - - error
|
|
||||||
retry unix - - n - - error
|
|
||||||
discard unix - - n - - discard
|
|
||||||
local unix - n n - - local
|
|
||||||
virtual unix - n n - - virtual
|
|
||||||
lmtp unix - - n - - lmtp
|
|
||||||
anvil unix - - n - 1 anvil
|
|
||||||
scache unix - - n - 1 scache
|
|
||||||
${cfg.extraMasterConf}
|
|
||||||
'';
|
|
||||||
|
|
||||||
aliases =
|
wakeup = mkOption {
|
||||||
|
type = types.int;
|
||||||
|
example = 60;
|
||||||
|
description = ''
|
||||||
|
Automatically wake up the service after the specified number of
|
||||||
|
seconds. If <literal>0</literal> is given, never wake the service
|
||||||
|
up.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
wakeupUnusedComponent = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
example = false;
|
||||||
|
description = ''
|
||||||
|
If set to <literal>false</literal> the component will only be woken
|
||||||
|
up if it is used. This is equivalent to postfix' notion of adding a
|
||||||
|
question mark behind the wakeup time in
|
||||||
|
<filename>master.cf</filename>
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
maxproc = mkOption {
|
||||||
|
type = types.int;
|
||||||
|
example = 1;
|
||||||
|
description = ''
|
||||||
|
The maximum number of processes to spawn for this service. If the
|
||||||
|
value is <literal>0</literal> it doesn't have any limit. If
|
||||||
|
<literal>null</literal> is given it uses the postfix default of
|
||||||
|
<literal>100</literal>.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
command = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = name;
|
||||||
|
example = "smtpd";
|
||||||
|
description = ''
|
||||||
|
A program name specifying a Postfix service/daemon process.
|
||||||
|
By default it's the attribute <option>name</option>.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
args = mkOption {
|
||||||
|
type = types.listOf types.str;
|
||||||
|
default = [];
|
||||||
|
example = [ "-o" "smtp_helo_timeout=5" ];
|
||||||
|
description = ''
|
||||||
|
Arguments to pass to the <option>command</option>. There is no shell
|
||||||
|
processing involved and shell syntax is passed verbatim to the
|
||||||
|
process.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
rawEntry = mkOption {
|
||||||
|
type = types.listOf types.str;
|
||||||
|
default = [];
|
||||||
|
internal = true;
|
||||||
|
description = ''
|
||||||
|
The raw configuration line for the <filename>master.cf</filename>.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config.rawEntry = let
|
||||||
|
mkBool = bool: if bool then "y" else "n";
|
||||||
|
mkArg = arg: "${optionalString (hasPrefix "-" arg) "\n "}${arg}";
|
||||||
|
|
||||||
|
maybeOption = fun: option:
|
||||||
|
if options.${option}.isDefined then fun config.${option} else "-";
|
||||||
|
|
||||||
|
# This is special, because we have two options for this value.
|
||||||
|
wakeup = let
|
||||||
|
wakeupDefined = options.wakeup.isDefined;
|
||||||
|
wakeupUCDefined = options.wakeupUnusedComponent.isDefined;
|
||||||
|
finalValue = toString config.wakeup
|
||||||
|
+ optionalString (!config.wakeupUnusedComponent) "?";
|
||||||
|
in if wakeupDefined && wakeupUCDefined then finalValue else "-";
|
||||||
|
|
||||||
|
in [
|
||||||
|
config.name
|
||||||
|
config.type
|
||||||
|
(maybeOption mkBool "private")
|
||||||
|
(maybeOption (b: mkBool (!b)) "privileged")
|
||||||
|
(maybeOption mkBool "chroot")
|
||||||
|
wakeup
|
||||||
|
(maybeOption toString "maxproc")
|
||||||
|
(config.command + " " + concatMapStringsSep " " mkArg config.args)
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
masterCfContent = let
|
||||||
|
|
||||||
|
labels = [
|
||||||
|
"# service" "type" "private" "unpriv" "chroot" "wakeup" "maxproc"
|
||||||
|
"command + args"
|
||||||
|
];
|
||||||
|
|
||||||
|
labelDefaults = [
|
||||||
|
"# " "" "(yes)" "(yes)" "(no)" "(never)" "(100)" "" ""
|
||||||
|
];
|
||||||
|
|
||||||
|
masterCf = mapAttrsToList (const (getAttr "rawEntry")) cfg.masterConfig;
|
||||||
|
|
||||||
|
# A list of the maximum width of the columns across all lines and labels
|
||||||
|
maxWidths = let
|
||||||
|
foldLine = line: acc: let
|
||||||
|
columnLengths = map stringLength line;
|
||||||
|
in zipListsWith max acc columnLengths;
|
||||||
|
# We need to handle the last column specially here, because it's
|
||||||
|
# open-ended (command + args).
|
||||||
|
lines = [ labels labelDefaults ] ++ (map (l: init l ++ [""]) masterCf);
|
||||||
|
in fold foldLine (genList (const 0) (length labels)) lines;
|
||||||
|
|
||||||
|
# Pad a string with spaces from the right (opposite of fixedWidthString).
|
||||||
|
pad = width: str: let
|
||||||
|
padWidth = width - stringLength str;
|
||||||
|
padding = concatStrings (genList (const " ") padWidth);
|
||||||
|
in str + optionalString (padWidth > 0) padding;
|
||||||
|
|
||||||
|
# It's + 2 here, because that's the amount of spacing between columns.
|
||||||
|
fullWidth = fold (width: acc: acc + width + 2) 0 maxWidths;
|
||||||
|
|
||||||
|
formatLine = line: concatStringsSep " " (zipListsWith pad maxWidths line);
|
||||||
|
|
||||||
|
formattedLabels = let
|
||||||
|
sep = "# " + concatStrings (genList (const "=") (fullWidth + 5));
|
||||||
|
lines = [ sep (formatLine labels) (formatLine labelDefaults) sep ];
|
||||||
|
in concatStringsSep "\n" lines;
|
||||||
|
|
||||||
|
in formattedLabels + "\n" + concatMapStringsSep "\n" formatLine masterCf + "\n";
|
||||||
|
|
||||||
|
headerCheckOptions = { ... }:
|
||||||
|
{
|
||||||
|
options = {
|
||||||
|
pattern = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "/^.*/";
|
||||||
|
example = "/^X-Mailer:/";
|
||||||
|
description = "A regexp pattern matching the header";
|
||||||
|
};
|
||||||
|
action = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "DUNNO";
|
||||||
|
example = "BCC mail@example.com";
|
||||||
|
description = "The action to be executed when the pattern is matched";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
headerChecks = concatStringsSep "\n" (map (x: "${x.pattern} ${x.action}") cfg.headerChecks) + cfg.extraHeaderChecks;
|
||||||
|
|
||||||
|
aliases = let seperator = if cfg.aliasMapType == "hash" then ":" else ""; in
|
||||||
optionalString (cfg.postmasterAlias != "") ''
|
optionalString (cfg.postmasterAlias != "") ''
|
||||||
postmaster: ${cfg.postmasterAlias}
|
postmaster${seperator} ${cfg.postmasterAlias}
|
||||||
''
|
''
|
||||||
+ optionalString (cfg.rootAlias != "") ''
|
+ optionalString (cfg.rootAlias != "") ''
|
||||||
root: ${cfg.rootAlias}
|
root${seperator} ${cfg.rootAlias}
|
||||||
''
|
''
|
||||||
+ cfg.extraAliases
|
+ cfg.extraAliases
|
||||||
;
|
;
|
||||||
@ -176,8 +303,9 @@ let
|
|||||||
virtualFile = pkgs.writeText "postfix-virtual" cfg.virtual;
|
virtualFile = pkgs.writeText "postfix-virtual" cfg.virtual;
|
||||||
checkClientAccessFile = pkgs.writeText "postfix-check-client-access" cfg.dnsBlacklistOverrides;
|
checkClientAccessFile = pkgs.writeText "postfix-check-client-access" cfg.dnsBlacklistOverrides;
|
||||||
mainCfFile = pkgs.writeText "postfix-main.cf" mainCf;
|
mainCfFile = pkgs.writeText "postfix-main.cf" mainCf;
|
||||||
masterCfFile = pkgs.writeText "postfix-master.cf" masterCf;
|
masterCfFile = pkgs.writeText "postfix-master.cf" masterCfContent;
|
||||||
transportFile = pkgs.writeText "postfix-transport" cfg.transport;
|
transportFile = pkgs.writeText "postfix-transport" cfg.transport;
|
||||||
|
headerChecksFile = pkgs.writeText "postfix-header-checks" headerChecks;
|
||||||
|
|
||||||
in
|
in
|
||||||
|
|
||||||
@ -199,27 +327,29 @@ in
|
|||||||
default = true;
|
default = true;
|
||||||
description = "Whether to enable smtp in master.cf.";
|
description = "Whether to enable smtp in master.cf.";
|
||||||
};
|
};
|
||||||
|
|
||||||
enableSubmission = mkOption {
|
enableSubmission = mkOption {
|
||||||
type = types.bool;
|
type = types.bool;
|
||||||
default = false;
|
default = false;
|
||||||
description = "Whether to enable smtp submission";
|
description = "Whether to enable smtp submission.";
|
||||||
};
|
};
|
||||||
|
|
||||||
submissionOptions = mkOption {
|
submissionOptions = mkOption {
|
||||||
type = types.attrs;
|
type = types.attrs;
|
||||||
default = { "smtpd_tls_security_level" = "encrypt";
|
default = {
|
||||||
"smtpd_sasl_auth_enable" = "yes";
|
smtpd_tls_security_level = "encrypt";
|
||||||
"smtpd_client_restrictions" = "permit_sasl_authenticated,reject";
|
smtpd_sasl_auth_enable = "yes";
|
||||||
"milter_macro_daemon_name" = "ORIGINATING";
|
smtpd_client_restrictions = "permit_sasl_authenticated,reject";
|
||||||
};
|
milter_macro_daemon_name = "ORIGINATING";
|
||||||
|
};
|
||||||
|
example = {
|
||||||
|
smtpd_tls_security_level = "encrypt";
|
||||||
|
smtpd_sasl_auth_enable = "yes";
|
||||||
|
smtpd_sasl_type = "dovecot";
|
||||||
|
smtpd_client_restrictions = "permit_sasl_authenticated,reject";
|
||||||
|
milter_macro_daemon_name = "ORIGINATING";
|
||||||
|
};
|
||||||
description = "Options for the submission config in master.cf";
|
description = "Options for the submission config in master.cf";
|
||||||
example = { "smtpd_tls_security_level" = "encrypt";
|
|
||||||
"smtpd_sasl_auth_enable" = "yes";
|
|
||||||
"smtpd_sasl_type" = "dovecot";
|
|
||||||
"smtpd_client_restrictions" = "permit_sasl_authenticated,reject";
|
|
||||||
"milter_macro_daemon_name" = "ORIGINATING";
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
setSendmail = mkOption {
|
setSendmail = mkOption {
|
||||||
@ -352,6 +482,25 @@ in
|
|||||||
";
|
";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
aliasMapType = mkOption {
|
||||||
|
type = with types; enum [ "hash" "regexp" "pcre" ];
|
||||||
|
default = "hash";
|
||||||
|
example = "regexp";
|
||||||
|
description = "The format the alias map should have. Use regexp if you want to use regular expressions.";
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkOption {
|
||||||
|
type = with types; attrsOf (either bool (either str (listOf str)));
|
||||||
|
default = defaultConf;
|
||||||
|
description = ''
|
||||||
|
The main.cf configuration file as key value set.
|
||||||
|
'';
|
||||||
|
example = {
|
||||||
|
mail_owner = "postfix";
|
||||||
|
smtp_use_tls = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
extraConfig = mkOption {
|
extraConfig = mkOption {
|
||||||
type = types.lines;
|
type = types.lines;
|
||||||
default = "";
|
default = "";
|
||||||
@ -395,6 +544,14 @@ in
|
|||||||
";
|
";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
virtualMapType = mkOption {
|
||||||
|
type = types.enum ["hash" "regexp" "pcre"];
|
||||||
|
default = "hash";
|
||||||
|
description = ''
|
||||||
|
What type of virtual alias map file to use. Use <literal>"regexp"</literal> for regular expressions.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
transport = mkOption {
|
transport = mkOption {
|
||||||
default = "";
|
default = "";
|
||||||
description = "
|
description = "
|
||||||
@ -413,6 +570,22 @@ in
|
|||||||
description = "contents of check_client_access for overriding dnsBlacklists";
|
description = "contents of check_client_access for overriding dnsBlacklists";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
masterConfig = mkOption {
|
||||||
|
type = types.attrsOf (types.submodule masterCfOptions);
|
||||||
|
default = {};
|
||||||
|
example =
|
||||||
|
{ submission = {
|
||||||
|
type = "inet";
|
||||||
|
args = [ "-o" "smtpd_tls_security_level=encrypt" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description = ''
|
||||||
|
An attribute set of service options, which correspond to the service
|
||||||
|
definitions usually done within the Postfix
|
||||||
|
<filename>master.cf</filename> file.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
extraMasterConf = mkOption {
|
extraMasterConf = mkOption {
|
||||||
type = types.lines;
|
type = types.lines;
|
||||||
default = "";
|
default = "";
|
||||||
@ -420,6 +593,27 @@ in
|
|||||||
description = "Extra lines to append to the generated master.cf file.";
|
description = "Extra lines to append to the generated master.cf file.";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enableHeaderChecks = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
example = true;
|
||||||
|
description = "Whether to enable postfix header checks";
|
||||||
|
};
|
||||||
|
|
||||||
|
headerChecks = mkOption {
|
||||||
|
type = types.listOf (types.submodule headerCheckOptions);
|
||||||
|
default = [];
|
||||||
|
example = [ { pattern = "/^X-Spam-Flag:/"; action = "REDIRECT spam@example.com"; } ];
|
||||||
|
description = "Postfix header checks.";
|
||||||
|
};
|
||||||
|
|
||||||
|
extraHeaderChecks = mkOption {
|
||||||
|
type = types.lines;
|
||||||
|
default = "";
|
||||||
|
example = "/^X-Spam-Flag:/ REDIRECT spam@example.com";
|
||||||
|
description = "Extra lines to /etc/postfix/header_checks file.";
|
||||||
|
};
|
||||||
|
|
||||||
aliasFiles = mkOption {
|
aliasFiles = mkOption {
|
||||||
type = types.attrsOf types.path;
|
type = types.attrsOf types.path;
|
||||||
default = {};
|
default = {};
|
||||||
@ -530,6 +724,101 @@ in
|
|||||||
${pkgs.postfix}/bin/postfix set-permissions config_directory=/var/lib/postfix/conf
|
${pkgs.postfix}/bin/postfix set-permissions config_directory=/var/lib/postfix/conf
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
services.postfix.masterConfig = {
|
||||||
|
smtp_inet = {
|
||||||
|
name = "smtp";
|
||||||
|
type = "inet";
|
||||||
|
private = false;
|
||||||
|
command = "smtpd";
|
||||||
|
};
|
||||||
|
pickup = {
|
||||||
|
private = false;
|
||||||
|
wakeup = 60;
|
||||||
|
maxproc = 1;
|
||||||
|
};
|
||||||
|
cleanup = {
|
||||||
|
private = false;
|
||||||
|
maxproc = 0;
|
||||||
|
};
|
||||||
|
qmgr = {
|
||||||
|
private = false;
|
||||||
|
wakeup = 300;
|
||||||
|
maxproc = 1;
|
||||||
|
};
|
||||||
|
tlsmgr = {
|
||||||
|
wakeup = 1000;
|
||||||
|
wakeupUnusedComponent = false;
|
||||||
|
maxproc = 1;
|
||||||
|
};
|
||||||
|
rewrite = {
|
||||||
|
command = "trivial-rewrite";
|
||||||
|
};
|
||||||
|
bounce = {
|
||||||
|
maxproc = 0;
|
||||||
|
};
|
||||||
|
defer = {
|
||||||
|
maxproc = 0;
|
||||||
|
command = "bounce";
|
||||||
|
};
|
||||||
|
trace = {
|
||||||
|
maxproc = 0;
|
||||||
|
command = "bounce";
|
||||||
|
};
|
||||||
|
verify = {
|
||||||
|
maxproc = 1;
|
||||||
|
};
|
||||||
|
flush = {
|
||||||
|
private = false;
|
||||||
|
wakeup = 1000;
|
||||||
|
wakeupUnusedComponent = false;
|
||||||
|
maxproc = 0;
|
||||||
|
};
|
||||||
|
proxymap = {
|
||||||
|
command = "proxymap";
|
||||||
|
};
|
||||||
|
proxywrite = {
|
||||||
|
maxproc = 1;
|
||||||
|
command = "proxymap";
|
||||||
|
};
|
||||||
|
showq = {
|
||||||
|
private = false;
|
||||||
|
};
|
||||||
|
error = {};
|
||||||
|
retry = {
|
||||||
|
command = "error";
|
||||||
|
};
|
||||||
|
discard = {};
|
||||||
|
local = {
|
||||||
|
privileged = true;
|
||||||
|
};
|
||||||
|
virtual = {
|
||||||
|
privileged = true;
|
||||||
|
};
|
||||||
|
lmtp = {
|
||||||
|
};
|
||||||
|
anvil = {
|
||||||
|
maxproc = 1;
|
||||||
|
};
|
||||||
|
scache = {
|
||||||
|
maxproc = 1;
|
||||||
|
};
|
||||||
|
} // optionalAttrs cfg.enableSubmission {
|
||||||
|
submission = {
|
||||||
|
type = "inet";
|
||||||
|
private = false;
|
||||||
|
command = "smtpd";
|
||||||
|
args = let
|
||||||
|
mkKeyVal = opt: val: [ "-o" (opt + "=" + val) ];
|
||||||
|
in concatLists (mapAttrsToList mkKeyVal cfg.submissionOptions);
|
||||||
|
};
|
||||||
|
} // optionalAttrs cfg.enableSmtp {
|
||||||
|
smtp = {};
|
||||||
|
relay = {
|
||||||
|
command = "smtp";
|
||||||
|
args = [ "-o" "smtp_fallback_relay=" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
(mkIf haveAliases {
|
(mkIf haveAliases {
|
||||||
@ -541,9 +830,14 @@ in
|
|||||||
(mkIf haveVirtual {
|
(mkIf haveVirtual {
|
||||||
services.postfix.mapFiles."virtual" = virtualFile;
|
services.postfix.mapFiles."virtual" = virtualFile;
|
||||||
})
|
})
|
||||||
|
(mkIf cfg.enableHeaderChecks {
|
||||||
|
services.postfix.mapFiles."header_checks" = headerChecksFile;
|
||||||
|
})
|
||||||
(mkIf (cfg.dnsBlacklists != []) {
|
(mkIf (cfg.dnsBlacklists != []) {
|
||||||
services.postfix.mapFiles."client_access" = checkClientAccessFile;
|
services.postfix.mapFiles."client_access" = checkClientAccessFile;
|
||||||
})
|
})
|
||||||
|
(mkIf (cfg.extraConfig != "") {
|
||||||
|
warnings = [ "The services.postfix.extraConfig option was deprecated. Please use services.postfix.config instead." ];
|
||||||
|
})
|
||||||
]);
|
]);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user