|
|
|
@ -9,7 +9,8 @@ let |
|
|
|
|
group = cfg.group; |
|
|
|
|
setgidGroup = cfg.setgidGroup; |
|
|
|
|
|
|
|
|
|
haveAliases = cfg.postmasterAlias != "" || cfg.rootAlias != "" || cfg.extraAliases != ""; |
|
|
|
|
haveAliases = cfg.postmasterAlias != "" || cfg.rootAlias != "" |
|
|
|
|
|| cfg.extraAliases != ""; |
|
|
|
|
haveTransport = cfg.transport != ""; |
|
|
|
|
haveVirtual = cfg.virtual != ""; |
|
|
|
|
|
|
|
|
@ -25,149 +26,275 @@ let |
|
|
|
|
|
|
|
|
|
clientRestrictions = concatStringsSep ", " (clientAccess ++ dnsBl); |
|
|
|
|
|
|
|
|
|
mainCf = |
|
|
|
|
'' |
|
|
|
|
compatibility_level = 9999 |
|
|
|
|
|
|
|
|
|
mail_owner = ${user} |
|
|
|
|
default_privs = nobody |
|
|
|
|
|
|
|
|
|
# NixOS specific locations |
|
|
|
|
data_directory = /var/lib/postfix/data |
|
|
|
|
queue_directory = /var/lib/postfix/queue |
|
|
|
|
|
|
|
|
|
# Default location of everything in package |
|
|
|
|
meta_directory = ${pkgs.postfix}/etc/postfix |
|
|
|
|
command_directory = ${pkgs.postfix}/bin |
|
|
|
|
sample_directory = /etc/postfix |
|
|
|
|
newaliases_path = ${pkgs.postfix}/bin/newaliases |
|
|
|
|
mailq_path = ${pkgs.postfix}/bin/mailq |
|
|
|
|
readme_directory = no |
|
|
|
|
sendmail_path = ${pkgs.postfix}/bin/sendmail |
|
|
|
|
daemon_directory = ${pkgs.postfix}/libexec/postfix |
|
|
|
|
manpage_directory = ${pkgs.postfix}/share/man |
|
|
|
|
html_directory = ${pkgs.postfix}/share/postfix/doc/html |
|
|
|
|
shlib_directory = no |
|
|
|
|
mainCf = let |
|
|
|
|
escape = replaceStrings ["$"] ["$$"]; |
|
|
|
|
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; |
|
|
|
|
|
|
|
|
|
defaultConf = { |
|
|
|
|
compatibility_level = "9999"; |
|
|
|
|
mail_owner = user; |
|
|
|
|
default_privs = "nobody"; |
|
|
|
|
|
|
|
|
|
# NixOS specific locations |
|
|
|
|
data_directory = "/var/lib/postfix/data"; |
|
|
|
|
queue_directory = "/var/lib/postfix/queue"; |
|
|
|
|
|
|
|
|
|
# Default location of everything in package |
|
|
|
|
meta_directory = "${pkgs.postfix}/etc/postfix"; |
|
|
|
|
command_directory = "${pkgs.postfix}/bin"; |
|
|
|
|
sample_directory = "/etc/postfix"; |
|
|
|
|
newaliases_path = "${pkgs.postfix}/bin/newaliases"; |
|
|
|
|
mailq_path = "${pkgs.postfix}/bin/mailq"; |
|
|
|
|
readme_directory = false; |
|
|
|
|
sendmail_path = "${pkgs.postfix}/bin/sendmail"; |
|
|
|
|
daemon_directory = "${pkgs.postfix}/libexec/postfix"; |
|
|
|
|
manpage_directory = "${pkgs.postfix}/share/man"; |
|
|
|
|
html_directory = "${pkgs.postfix}/share/postfix/doc/html"; |
|
|
|
|
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; |
|
|
|
|
|
|
|
|
|
smtpd_tls_CAfile = cfg.sslCACert; |
|
|
|
|
smtpd_tls_cert_file = cfg.sslCert; |
|
|
|
|
smtpd_tls_key_file = cfg.sslKey; |
|
|
|
|
|
|
|
|
|
smtpd_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 + "]"} |
|
|
|
|
masterCfOptions = { options, config, name, ... }: { |
|
|
|
|
options = { |
|
|
|
|
name = mkOption { |
|
|
|
|
type = types.str; |
|
|
|
|
default = name; |
|
|
|
|
example = "smtp"; |
|
|
|
|
description = '' |
|
|
|
|
The name of the service to run. Defaults to the attribute set key. |
|
|
|
|
''; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
mail_spool_directory = /var/spool/mail/ |
|
|
|
|
type = mkOption { |
|
|
|
|
type = types.enum [ "inet" "unix" "fifo" "pass" ]; |
|
|
|
|
default = "unix"; |
|
|
|
|
example = "inet"; |
|
|
|
|
description = "The type of the service"; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
setgid_group = ${setgidGroup} |
|
|
|
|
'' |
|
|
|
|
+ optionalString (cfg.sslCert != "") '' |
|
|
|
|
private = mkOption { |
|
|
|
|
type = types.bool; |
|
|
|
|
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>. |
|
|
|
|
''; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
smtp_tls_CAfile = ${cfg.sslCACert} |
|
|
|
|
smtp_tls_cert_file = ${cfg.sslCert} |
|
|
|
|
smtp_tls_key_file = ${cfg.sslKey} |
|
|
|
|
privileged = mkOption { |
|
|
|
|
type = types.bool; |
|
|
|
|
example = true; |
|
|
|
|
description = ""; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
smtp_use_tls = yes |
|
|
|
|
chroot = mkOption { |
|
|
|
|
type = types.bool; |
|
|
|
|
example = true; |
|
|
|
|
description = '' |
|
|
|
|
Whether the service is chrooted to have only access to the |
|
|
|
|
<option>services.postfix.queueDir</option> and the closure of |
|
|
|
|
store paths specified by the <option>program</option> option. |
|
|
|
|
''; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
smtpd_tls_CAfile = ${cfg.sslCACert} |
|
|
|
|
smtpd_tls_cert_file = ${cfg.sslCert} |
|
|
|
|
smtpd_tls_key_file = ${cfg.sslKey} |
|
|
|
|
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. |
|
|
|
|
''; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
smtpd_use_tls = yes |
|
|
|
|
'' |
|
|
|
|
+ optionalString (cfg.recipientDelimiter != "") '' |
|
|
|
|
recipient_delimiter = ${cfg.recipientDelimiter} |
|
|
|
|
'' |
|
|
|
|
+ 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 = '' |
|
|
|
|
# ========================================================================== |
|
|
|
|
# service type private unpriv chroot wakeup maxproc command + args |
|
|
|
|
# (yes) (yes) (no) (never) (100) |
|
|
|
|
# ========================================================================== |
|
|
|
|
smtp inet n - n - - smtpd |
|
|
|
|
'' + optionalString cfg.enableSubmission '' |
|
|
|
|
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 = |
|
|
|
|
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 != "") '' |
|
|
|
|
postmaster: ${cfg.postmasterAlias} |
|
|
|
|
postmaster${seperator} ${cfg.postmasterAlias} |
|
|
|
|
'' |
|
|
|
|
+ optionalString (cfg.rootAlias != "") '' |
|
|
|
|
root: ${cfg.rootAlias} |
|
|
|
|
root${seperator} ${cfg.rootAlias} |
|
|
|
|
'' |
|
|
|
|
+ cfg.extraAliases |
|
|
|
|
; |
|
|
|
@ -176,8 +303,9 @@ let |
|
|
|
|
virtualFile = pkgs.writeText "postfix-virtual" cfg.virtual; |
|
|
|
|
checkClientAccessFile = pkgs.writeText "postfix-check-client-access" cfg.dnsBlacklistOverrides; |
|
|
|
|
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; |
|
|
|
|
headerChecksFile = pkgs.writeText "postfix-header-checks" headerChecks; |
|
|
|
|
|
|
|
|
|
in |
|
|
|
|
|
|
|
|
@ -199,27 +327,29 @@ in |
|
|
|
|
default = true; |
|
|
|
|
description = "Whether to enable smtp in master.cf."; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
enableSubmission = mkOption { |
|
|
|
|
type = types.bool; |
|
|
|
|
default = false; |
|
|
|
|
description = "Whether to enable smtp submission"; |
|
|
|
|
description = "Whether to enable smtp submission."; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
submissionOptions = mkOption { |
|
|
|
|
type = types.attrs; |
|
|
|
|
default = { "smtpd_tls_security_level" = "encrypt"; |
|
|
|
|
"smtpd_sasl_auth_enable" = "yes"; |
|
|
|
|
"smtpd_client_restrictions" = "permit_sasl_authenticated,reject"; |
|
|
|
|
"milter_macro_daemon_name" = "ORIGINATING"; |
|
|
|
|
}; |
|
|
|
|
default = { |
|
|
|
|
smtpd_tls_security_level = "encrypt"; |
|
|
|
|
smtpd_sasl_auth_enable = "yes"; |
|
|
|
|
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"; |
|
|
|
|
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 { |
|
|
|
@ -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 { |
|
|
|
|
type = types.lines; |
|
|
|
|
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 { |
|
|
|
|
default = ""; |
|
|
|
|
description = " |
|
|
|
@ -413,6 +570,22 @@ in |
|
|
|
|
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 { |
|
|
|
|
type = types.lines; |
|
|
|
|
default = ""; |
|
|
|
@ -420,6 +593,27 @@ in |
|
|
|
|
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 { |
|
|
|
|
type = types.attrsOf types.path; |
|
|
|
|
default = {}; |
|
|
|
@ -530,6 +724,101 @@ in |
|
|
|
|
${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 { |
|
|
|
@ -541,9 +830,14 @@ in |
|
|
|
|
(mkIf haveVirtual { |
|
|
|
|
services.postfix.mapFiles."virtual" = virtualFile; |
|
|
|
|
}) |
|
|
|
|
(mkIf cfg.enableHeaderChecks { |
|
|
|
|
services.postfix.mapFiles."header_checks" = headerChecksFile; |
|
|
|
|
}) |
|
|
|
|
(mkIf (cfg.dnsBlacklists != []) { |
|
|
|
|
services.postfix.mapFiles."client_access" = checkClientAccessFile; |
|
|
|
|
}) |
|
|
|
|
(mkIf (cfg.extraConfig != "") { |
|
|
|
|
warnings = [ "The services.postfix.extraConfig option was deprecated. Please use services.postfix.config instead." ]; |
|
|
|
|
}) |
|
|
|
|
]); |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|