LDAP with password
This commit is contained in:
parent
2ebd024977
commit
5b7eb05635
83
dovecot.nix
83
dovecot.nix
|
@ -54,7 +54,6 @@ in {
|
|||
description = "Port on which to serve metrics data.";
|
||||
default = 5034;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
mail-user = mkOption {
|
||||
|
@ -153,7 +152,7 @@ in {
|
|||
specialUse = "Sent";
|
||||
};
|
||||
Archive = {
|
||||
atuo = "no";
|
||||
auto = "no";
|
||||
specialUse = "Archive";
|
||||
};
|
||||
Flagged = {
|
||||
|
@ -180,44 +179,9 @@ in {
|
|||
default = 5;
|
||||
};
|
||||
|
||||
ldap = let
|
||||
ldapOpts = {
|
||||
options = {
|
||||
host = mkOption {
|
||||
type = str;
|
||||
description = "LDAP hostname.";
|
||||
};
|
||||
|
||||
port = mkOption {
|
||||
type = str;
|
||||
description = "Port on which LDAP is listening.";
|
||||
};
|
||||
|
||||
base = mkOption {
|
||||
type = str;
|
||||
description = "Base of the LDAP server database.";
|
||||
example = "dc=mydomain,dc=org";
|
||||
};
|
||||
|
||||
bind-dn = mkOption {
|
||||
type = str;
|
||||
description = ''
|
||||
DN used for fetching user information.
|
||||
|
||||
Needs access to homeDirectory, uidNumber, gidNumber, and uid, but not
|
||||
password attributes.
|
||||
'';
|
||||
};
|
||||
|
||||
bind-password-file = mkOption {
|
||||
type = str;
|
||||
description = "Path to file containing bind password for bind-dn.";
|
||||
};
|
||||
};
|
||||
};
|
||||
in mkOption {
|
||||
type = nullOr ldapOpts;
|
||||
default = null;
|
||||
ldap-conf = mkOption {
|
||||
type = str;
|
||||
description = "Path to LDAP dovecot2 configuration.";
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -240,7 +204,7 @@ in {
|
|||
group = cfg.mail-group;
|
||||
};
|
||||
"${cfg.metrics.user}" = {
|
||||
isSystemUser = true;
|
||||
isySstemUser = true;
|
||||
group = cfg.metrics.group;
|
||||
};
|
||||
};
|
||||
|
@ -306,6 +270,9 @@ in {
|
|||
name = "rspam_pipe_bin";
|
||||
paths = [ learnHam learnSpam ];
|
||||
};
|
||||
|
||||
mailUserUid = config.users.users."${cfg.mail-user}".uid;
|
||||
mailUserGid = config.users.group."${cfg.mail-group}".gid;
|
||||
in ''
|
||||
## Extra Config
|
||||
|
||||
|
@ -331,6 +298,8 @@ in {
|
|||
# When looking up usernames, just use the name, not the full address
|
||||
auth_username_format = %n
|
||||
|
||||
auth_mechanisms = login plain
|
||||
|
||||
service lmtp {
|
||||
# Enable logging in debug mode
|
||||
${optionalString cfg.debug "executable = lmtp -L"}
|
||||
|
@ -346,14 +315,13 @@ in {
|
|||
# user = root
|
||||
}
|
||||
|
||||
auth_mechanisms = login plain
|
||||
|
||||
${optionalString (cfg.dovecot.ldap != null) ''
|
||||
passdb {
|
||||
driver = ldap
|
||||
args = ${cfg.dovecot.ldap.generated-ldap-config}
|
||||
}
|
||||
''}
|
||||
passdb {
|
||||
driver = ldap
|
||||
args = ${cfg.ldap-conf}
|
||||
}
|
||||
|
||||
# All users map to one actual system user
|
||||
userdb {
|
||||
driver = static
|
||||
args = uid=${
|
||||
|
@ -361,23 +329,6 @@ in {
|
|||
} home=${cfg.state-directory}/mail/%u
|
||||
}
|
||||
|
||||
# Used by postfix to authorize users
|
||||
service auth {
|
||||
inet_listener auth {
|
||||
address = 0.0.0.0
|
||||
port = ${toString cfg.ports.auth}
|
||||
}
|
||||
inet_listener auth-userdb {
|
||||
address = 0.0.0.0
|
||||
port = ${toString cfg.ports.userdb}
|
||||
}
|
||||
}
|
||||
|
||||
service auth-worker {
|
||||
user = ${config.services.dovecot2.user}
|
||||
idle_kill = 3
|
||||
}
|
||||
|
||||
service imap {
|
||||
vsz_limit = 1024M
|
||||
}
|
||||
|
@ -402,7 +353,7 @@ in {
|
|||
imapsieve_mailbox2_causes = COPY
|
||||
imapsieve_mailbox2_before = file:${sievePath}/report-ham.sieve
|
||||
|
||||
sieve_pipe_bin_dir = ${pipeBin}/pipe/bin
|
||||
sieve_pipe_bin_dir = ${pipeBin}/bin
|
||||
sieve_global_extensions = +vnd.dovecot.pipe +vnd.dovecot.environment
|
||||
}
|
||||
|
||||
|
|
|
@ -75,6 +75,44 @@ in {
|
|||
};
|
||||
};
|
||||
|
||||
fudo.secrets.host-secrets."${hostname}" = {
|
||||
dovecotLdapConfig = {
|
||||
source-file = pkgs.writeText "dovecot-ldap.conf"
|
||||
(concatStringsSep "\n" [
|
||||
"uris = ldap://ldap-proxymine:${ldapPort}"
|
||||
"ldap_version = 3"
|
||||
"dn = ${cfg.ldap.bind-dn}"
|
||||
"dnpass = ${readFile cfg.ldap.bind-password-file}"
|
||||
"auth_bind = yes"
|
||||
"auth_bind_userdn = uid=%u,${cfg.ldap.member-ou},${cfg.ldap.base}"
|
||||
"base = ${cfg.ldap.base}"
|
||||
]);
|
||||
target-file = "/run/dovecot-secret/ldap.conf";
|
||||
};
|
||||
};
|
||||
|
||||
users.users = {
|
||||
mailserver-dovecot = {
|
||||
uid = 4455;
|
||||
isSystemUser = true;
|
||||
};
|
||||
mailserver-antivirus = {
|
||||
uid = 4456;
|
||||
isSystemUser = true;
|
||||
|
||||
};
|
||||
mailserver-dkim = {
|
||||
uid = 4457;
|
||||
isSystemUser = true;
|
||||
};
|
||||
};
|
||||
|
||||
systemd.tmpfiles.rules = [
|
||||
"d ${cfg.state-directory}/dovecot 0700 mailserver-dovecot - - -"
|
||||
"d ${cfg.state-directory}/antivirus 0700 mailserver-antivirus - - -"
|
||||
"d ${cfg.state-directory}/dkim 0700 mailserver-dkim - - -"
|
||||
];
|
||||
|
||||
virtualisation.arion.projects.mail-server.settings = let
|
||||
image = { pkgs, ... }: {
|
||||
project.name = "fudo-mailserver";
|
||||
|
@ -89,6 +127,10 @@ in {
|
|||
authPort = 5447;
|
||||
userdbPort = 5448;
|
||||
metricsPort = 5034;
|
||||
mkUserMap = username:
|
||||
let uid = config.users.users."${username}".uid;
|
||||
in "${uid}:${uid}";
|
||||
|
||||
in {
|
||||
smtp = {
|
||||
networks = [
|
||||
|
@ -96,14 +138,19 @@ in {
|
|||
# Needs access to internet to forward emails
|
||||
"external_network"
|
||||
];
|
||||
volumes = [
|
||||
"${hostSecrets.dovecotLdapConfig.target-file}:/run/dovecot2/conf.d/ldap.conf:ro"
|
||||
];
|
||||
ports = [ "25:25" "587:587" "465:465" "2525:2525" ];
|
||||
nixos = {
|
||||
useSystemd = true;
|
||||
configuration = [
|
||||
(import ./postfix.nix)
|
||||
(import ./dovecot.nix)
|
||||
{
|
||||
boot.tmpOnTmpfs = true;
|
||||
system.nssModules = lib.mkForce [ ];
|
||||
|
||||
fudo.mail.postfix = {
|
||||
enable = true;
|
||||
debug = cfg.debug;
|
||||
|
@ -127,6 +174,19 @@ in {
|
|||
sasl-domain = cfg.sasl-domain;
|
||||
message-size-limit = cfg.message-size-limit;
|
||||
ports = { metrics = metricsPort; };
|
||||
rspamd-server = {
|
||||
host = "antispam";
|
||||
port = antispamPort;
|
||||
};
|
||||
lmtp-server = {
|
||||
host = "imap";
|
||||
port = lmtpPort;
|
||||
};
|
||||
dkim-server = {
|
||||
host = "dkim";
|
||||
port = dkimPort;
|
||||
};
|
||||
ldap-conf = "/run/dovecot2/conf.d/ldap.conf";
|
||||
};
|
||||
}
|
||||
];
|
||||
|
@ -135,6 +195,11 @@ in {
|
|||
imap = {
|
||||
networks = [ "internal_network" ];
|
||||
ports = [ "143:143" "993:993" ];
|
||||
user = mkUserMap "mailserver-dovecot";
|
||||
volumes = [
|
||||
"${cfg.state-directory}/dovecot:/state"
|
||||
"${hostSecrets.dovecotLdapConfig.target-file}:/run/dovecot2/conf.d/ldap.conf:ro"
|
||||
];
|
||||
nixos = {
|
||||
useSystemd = true;
|
||||
configuration = [
|
||||
|
@ -145,7 +210,7 @@ in {
|
|||
fudo.mail.dovecot = {
|
||||
enable = true;
|
||||
debug = cfg.debug;
|
||||
state-directory = "${cfg.state-directory}/dovecot";
|
||||
state-directory = "/state";
|
||||
ports = {
|
||||
lmtp = lmtpPort;
|
||||
auth = authPort;
|
||||
|
@ -162,13 +227,7 @@ in {
|
|||
host = "antispam";
|
||||
port = antispamPort;
|
||||
};
|
||||
ldap = mkIf cfg.ldap-proxy {
|
||||
host = "ldap-proxy";
|
||||
port = 3389;
|
||||
base = cfg.ldap.base;
|
||||
bind-dn = cfg.ldap.bind-dn;
|
||||
bind-password-file = cfg.ldap.bind-password-file;
|
||||
};
|
||||
ldap-conf = "/run/dovecot2/conf.d/ldap.conf";
|
||||
};
|
||||
}
|
||||
];
|
||||
|
@ -219,6 +278,8 @@ in {
|
|||
# Needs external access for database updates
|
||||
"external_network"
|
||||
];
|
||||
user = mkUserMap "mailserver-antivirus";
|
||||
volumes = [ "${cfg.state-directory}/antivirus:/state" ];
|
||||
nixos = {
|
||||
useSystemd = true;
|
||||
configuration = [
|
||||
|
@ -228,7 +289,7 @@ in {
|
|||
system.nssModules = lib.mkForce [ ];
|
||||
fudo.mail.clamav = {
|
||||
enable = true;
|
||||
state-directory = "${cfg.state-directory}/rspamd";
|
||||
state-directory = "/state";
|
||||
port = antispamPort;
|
||||
};
|
||||
}
|
||||
|
@ -237,6 +298,8 @@ in {
|
|||
};
|
||||
dkim = {
|
||||
networks = [ "internal_network" ];
|
||||
user = mkUserMap "mailserver-dkim";
|
||||
volumes = [ "${cfg.state-directory}/dkim:/state" ];
|
||||
nixos = {
|
||||
useSystemd = true;
|
||||
configuration = [
|
||||
|
@ -250,7 +313,7 @@ in {
|
|||
domains = [ cfg.primary-domain ] ++ cfg.extra-domains;
|
||||
};
|
||||
port = dkimPort;
|
||||
state-directory = "${cfg.state-directory}/dkim";
|
||||
state-directory = "/state";
|
||||
}
|
||||
];
|
||||
};
|
||||
|
|
194
postfix.nix
194
postfix.nix
|
@ -118,6 +118,44 @@ in {
|
|||
default = 1725;
|
||||
};
|
||||
};
|
||||
|
||||
ldap-conf = mkOption {
|
||||
type = str;
|
||||
description = "Path to LDAP dovecot2 configuration.";
|
||||
};
|
||||
|
||||
rspamd-server = {
|
||||
host = mkOption {
|
||||
type = str;
|
||||
description = "Hostname of rspamd server.";
|
||||
};
|
||||
port = mkOption {
|
||||
type = port;
|
||||
description = "Port on which rspamd is running.";
|
||||
};
|
||||
};
|
||||
|
||||
lmtp-server = {
|
||||
host = mkOption {
|
||||
type = str;
|
||||
description = "Hostname of lmtp server.";
|
||||
};
|
||||
port = mkOption {
|
||||
type = port;
|
||||
description = "Port on which lmtp is running.";
|
||||
};
|
||||
};
|
||||
|
||||
dkim-server = {
|
||||
host = mkOption {
|
||||
type = str;
|
||||
description = "Hostname of dkim server.";
|
||||
};
|
||||
port = mkOption {
|
||||
type = port;
|
||||
description = "Port on which dkim is running.";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
|
@ -140,6 +178,44 @@ in {
|
|||
port = cfg.metrics-port;
|
||||
};
|
||||
|
||||
dovecot2 = {
|
||||
enable = true;
|
||||
enablePAM = false;
|
||||
enableImap = false;
|
||||
extraConfig = ''
|
||||
# Extra Config
|
||||
${lib.optionalString cfg.debug "auth_debug = yes"}
|
||||
|
||||
# When looking up usernames, just use the name, not the full address
|
||||
auth_username_format = %n
|
||||
|
||||
auth_mechanisms = login plain
|
||||
|
||||
passdb {
|
||||
driver = ldap
|
||||
args = ${ldapConfig}
|
||||
}
|
||||
|
||||
userdb = {
|
||||
driver = ldap
|
||||
args = ${ldap-conf}
|
||||
}
|
||||
|
||||
service auth {
|
||||
unix_listener auth {
|
||||
mode = 0600
|
||||
user = ${config.services.postfix.user}
|
||||
group = ${config.services.postfix.group}
|
||||
}
|
||||
}
|
||||
|
||||
service auth-worker {
|
||||
user = ${config.services.dovecot2.user}
|
||||
idle_kill = 3
|
||||
}
|
||||
'';
|
||||
};
|
||||
|
||||
postfix = {
|
||||
enable = true;
|
||||
|
||||
|
@ -199,6 +275,52 @@ in {
|
|||
pcreFile = name: "pcre:/var/lib/postfix/conf/${name}";
|
||||
mappedFile = name: "hash:/var/lib/postfix/conf/${name}";
|
||||
|
||||
sender-restrictions = [
|
||||
"check_sender_access ${mapped-file "reject_senders"}"
|
||||
"reject_sender_login_mismatch"
|
||||
"reject_non_fqdn_sender"
|
||||
"reject_unknown_sender_domain"
|
||||
"permit_mynetworks"
|
||||
"permit_sasl_authenticated"
|
||||
] ++ (map (blacklist: "reject_rbl_client ${blacklist}")
|
||||
cfg.blacklist.dns) ++ [ "reject" ];
|
||||
|
||||
relay-restrictions = [
|
||||
"reject_unauth_destination"
|
||||
"reject_unauth_pipelining"
|
||||
"reject_unauth_destination"
|
||||
"reject_unknown_sender_domain"
|
||||
"permit_mynetworks"
|
||||
"permit_sasl_authenticated"
|
||||
] ++ (map (blacklist: "reject_rbl_client ${blacklist}")
|
||||
cfg.blacklist.dns) ++ [ "reject" ];
|
||||
|
||||
recipient-restrictions = [
|
||||
"check_sender_access ${mapped-file "reject_recipients"}"
|
||||
"reject_unknown_sender_domain"
|
||||
"reject_unknown_recipient_domain"
|
||||
"reject_unauth_pipelining"
|
||||
"reject_unauth_destination"
|
||||
"reject_invalid_hostname"
|
||||
"reject_non_fqdn_hostname"
|
||||
"reject_non_fqdn_sender"
|
||||
"reject_non_fqdn_recipient"
|
||||
"check_policy_service unix:private/policy-spf"
|
||||
] ++ (map (blacklist: "reject_rbl_client ${blacklist}")
|
||||
cfg.blacklist.dns)
|
||||
++ [ "permit_mynetworks" "permit_sasl_authenticated" "reject" ];
|
||||
|
||||
client-restrictions =
|
||||
[ "permit_sasl_authenticated" "permit_mynetworks" "reject" ];
|
||||
|
||||
helo-restrictions = [
|
||||
"permit_mynetworks"
|
||||
"reject_invalid_hostname"
|
||||
"reject_non_fqdn_helo_hostname"
|
||||
"reject_unknown_helo_hostname"
|
||||
] ++ (map (blacklist: "reject_rbl_client ${blacklist}")
|
||||
cfg.blacklist.dns) ++ [ "permit" ];
|
||||
|
||||
in {
|
||||
virtual_mailbox_domains = allDomains;
|
||||
virtual_mailbox_maps = mappedFile "virtual_mailbox_map";
|
||||
|
@ -209,7 +331,8 @@ in {
|
|||
# virtual_gid_maps = let gid = config.users.groups."${cfg.group}".gid;
|
||||
# in "static: ${toString gid}";
|
||||
|
||||
virtual_transport = "lmtp:unix:/run/dovecot2/dovecot-lmtp";
|
||||
virtual_transport =
|
||||
"lmtp:inet:${cfg.lmtp-server.host}:${cfg.lmtp-server.port}";
|
||||
|
||||
message_size_limit = toString (cfg.message-size-limit * 1024 * 1024);
|
||||
|
||||
|
@ -226,6 +349,7 @@ in {
|
|||
smtpd_sasl_path = "/run/dovecot2/auth";
|
||||
smtpd_sasl_auth_enable = "yes";
|
||||
smtpd_sasl_local_domain = cfg.sasl-domain;
|
||||
smtp_sasl_authenticated_header = "yes";
|
||||
|
||||
smtpd_sasl_security_options = "noanonymous";
|
||||
smtpd_sasl_tls_security_options = "noanonymous";
|
||||
|
@ -241,47 +365,24 @@ in {
|
|||
"i {mail_addr} {client_addr} {client_name} {auth_type} {auth_authen} {auth_author} {mail_addr} {mail_host} {mail_mailer}";
|
||||
|
||||
smtpd_milters = [
|
||||
"unix:/run/rspamd/rspamd-milter.sock"
|
||||
"unix:/var/run/opendkim/opendkim.sock"
|
||||
"inet:${cfg.rspamd-server.host}:${cfg.rspamd-server.port}"
|
||||
"inet:${cfg.dkim.host}:${cfg.dkim.port}"
|
||||
];
|
||||
|
||||
non_smtpd_milters = [
|
||||
"unix:/run/rspamd/rspamd-milter.sock"
|
||||
"unix:/var/run/opendkim/opendkim.sock"
|
||||
"inet:${cfg.rspamd-server.host}:${cfg.rspamd-server.port}"
|
||||
"inet:${cfg.dkim.host}:${cfg.dkim.port}"
|
||||
];
|
||||
|
||||
smtpd_relay_restrictions = [
|
||||
"permit_mynetworks"
|
||||
"permit_sasl_authenticated"
|
||||
"reject_unauth_destination"
|
||||
"reject_unauth_pipelining"
|
||||
"reject_unauth_destination"
|
||||
"reject_unknown_sender_domain"
|
||||
];
|
||||
helo_required = true;
|
||||
|
||||
smtpd_sender_restrictions = [
|
||||
"check_sender_access ${mapped-file "reject_senders"}"
|
||||
"permit_mynetworks"
|
||||
"permit_sasl_authenticated"
|
||||
"reject_unknown_sender_domain"
|
||||
];
|
||||
smtpd_relay_restrictions = relay-restrictions;
|
||||
|
||||
smtpd_recipient_restrictions = [
|
||||
"check_sender_access ${mapped-file "reject_recipients"}"
|
||||
"permit_mynetworks"
|
||||
"permit_sasl_authenticated"
|
||||
"check_policy_service unix:private/policy-spf"
|
||||
"reject_unknown_recipient_domain"
|
||||
"reject_unauth_pipelining"
|
||||
"reject_unauth_destination"
|
||||
"reject_invalid_hostname"
|
||||
"reject_non_fqdn_hostname"
|
||||
"reject_non_fqdn_sender"
|
||||
"reject_non_fqdn_recipient"
|
||||
];
|
||||
smtpd_sender_restrictions = sender-restrictions;
|
||||
|
||||
smtpd_helo_restrictions =
|
||||
[ "permit_mynetworks" "reject_invalid_hostname" "permit" ];
|
||||
smtpd_recipient_restrictions = recipient-restrictions;
|
||||
|
||||
smtpd_helo_restrictions = helo-restrictions;
|
||||
|
||||
# Handled by submission
|
||||
smtpd_tls_security_level = "may";
|
||||
|
@ -328,16 +429,16 @@ in {
|
|||
smtpd_sasl_path = "/run/dovecot2/auth";
|
||||
smtpd_sasl_security_options = "noanonymous";
|
||||
smtpd_sasl_local_domain = cfg.domain;
|
||||
smtpd_client_restrictions = "permit_sasl_authenticated,reject";
|
||||
smtpd_sender_restrictions =
|
||||
"reject_sender_login_mismatch,reject_unknown_sender_domain";
|
||||
smtpd_recipient_restrictions =
|
||||
"reject_non_fqdn_recipient,reject_unknown_recipient_domain,permit_sasl_authenticated,reject";
|
||||
smtpd_client_restrictions = client-restrictions;
|
||||
smtpd_sender_restrictions = sender-restrictions;
|
||||
smtpd_recipient_restrictions = recipient-restrictions;
|
||||
cleanup_service_name = "submission-header-cleanup";
|
||||
};
|
||||
|
||||
masterConfig = {
|
||||
"policy-spf" = let
|
||||
# See: http://www.postfix.org/smtp.8.html
|
||||
lmtp.args = [ "flags=DO" ];
|
||||
policy-spf = let
|
||||
policySpfFile = pkgs.writeText "policyd-spf.conf"
|
||||
(cfg.postfix.policy-spf.extraConfig
|
||||
+ (lib.optionalString cfg.debug "debugLevel = 4"));
|
||||
|
@ -352,18 +453,19 @@ in {
|
|||
"${policydSpf}"
|
||||
];
|
||||
};
|
||||
"submission-header-cleanup" = let
|
||||
submission-header-cleanup = let
|
||||
submissionHeaderCleanupRules =
|
||||
pkgs.writeText "submission_header_cleanup_rules" ''
|
||||
# Removes sensitive headers from mails handed in via the submission port.
|
||||
# See https://thomas-leister.de/mailserver-debian-stretch/
|
||||
# Uses "pcre" style regex.
|
||||
|
||||
/^Received:/ IGNORE
|
||||
/^X-Originating-IP:/ IGNORE
|
||||
/^X-Mailer:/ IGNORE
|
||||
/^User-Agent:/ IGNORE
|
||||
/^X-Enigmail:/ IGNORE
|
||||
/^Received:/ IGNORE
|
||||
/^X-Originating-IP:/ IGNORE
|
||||
/^X-Mailer:/ IGNORE
|
||||
/^User-Agent:/ IGNORE
|
||||
/^X-Enigmail:/ IGNORE
|
||||
/^Message-ID:\s+<(.*?)@.*?>/ REPLACE Message-ID: <$1@${cfg.hostname}>
|
||||
'';
|
||||
in {
|
||||
type = "unix";
|
||||
|
|
|
@ -62,6 +62,12 @@ in {
|
|||
scan_mime_parts = false; # scan mail as a whole unit, not parts. seems to be needed to work at all
|
||||
}
|
||||
'';
|
||||
|
||||
"rbl.conf".text = ''
|
||||
rbls {
|
||||
an_rbl
|
||||
}
|
||||
'';
|
||||
};
|
||||
|
||||
overrides."milter_headers.conf".text = "extended_spam_headers = true;";
|
||||
|
|
Loading…
Reference in New Issue