summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAntoine Eiche <lewo@abesis.fr>2023-05-20 00:12:02 +0200
committerAntoine Eiche <lewo@abesis.fr>2023-06-28 23:06:41 +0200
commit870b1d11879324fea77bafdaa5a848a857b21bb2 (patch)
tree85aad50dda13d38d367441318732b392161406e0
parentf7a800ff8ce558380a4fc0cf6170890ff97cea92 (diff)
ldap: do not write password to the Nix store
-rw-r--r--default.nix6
-rw-r--r--mail-server/common.nix21
-rw-r--r--mail-server/dovecot.nix85
-rw-r--r--mail-server/postfix.nix55
-rw-r--r--tests/ldap.nix10
5 files changed, 115 insertions, 62 deletions
diff --git a/default.nix b/default.nix
index 86b436d..f98b4ad 100644
--- a/default.nix
+++ b/default.nix
@@ -240,11 +240,11 @@ in
'';
};
- password = mkOption {
+ passwordFile = mkOption {
type = types.str;
- example = "not$4f3";
+ example = "/run/my-secret";
description = ''
- Password required to authenticate against the LDAP servers.
+ A file containing the password required to authenticate against the LDAP servers.
'';
};
};
diff --git a/mail-server/common.nix b/mail-server/common.nix
index e8beb7a..236530b 100644
--- a/mail-server/common.nix
+++ b/mail-server/common.nix
@@ -45,4 +45,25 @@ in
if value.hashedPasswordFile == null then
builtins.toString (mkHashFile name value.hashedPassword)
else value.hashedPasswordFile) cfg.loginAccounts;
+
+ # Appends the LDAP bind password to files to avoid writing this
+ # password into the Nix store.
+ appendLdapBindPwd = {
+ name, file, prefix, passwordFile, destination
+ }: pkgs.writeScript "append-ldap-bind-pwd-in-${name}" ''
+ #!${pkgs.stdenv.shell}
+ set -euo pipefail
+
+ baseDir=$(dirname ${destination})
+ if (! test -d "$baseDir"); then
+ mkdir -p $baseDir
+ chmod 755 $baseDir
+ fi
+
+ cat ${file} > ${destination}
+ echo -n "${prefix}" >> ${destination}
+ cat ${passwordFile} >> ${destination}
+ chmod 600 ${destination}
+ '';
+
}
diff --git a/mail-server/dovecot.nix b/mail-server/dovecot.nix
index 3ffa232..18b4a50 100644
--- a/mail-server/dovecot.nix
+++ b/mail-server/dovecot.nix
@@ -22,9 +22,10 @@ let
cfg = config.mailserver;
passwdDir = "/run/dovecot2";
- passdbFile = "${passwdDir}/passdb";
+ passwdFile = "${passwdDir}/passwd";
userdbFile = "${passwdDir}/userdb";
-
+ # This file contains the ldap bind password
+ ldapConfFile = "${passwdDir}/dovecot-ldap.conf.ext";
bool2int = x: if x then "1" else "0";
maildirLayoutAppendix = lib.optionalString cfg.useFsLayout ":LAYOUT=fs";
@@ -58,6 +59,41 @@ let
'';
};
+
+ ldapConfig = pkgs.writeTextFile {
+ name = "dovecot-ldap.conf.ext.template";
+ text = ''
+ ldap_version = 3
+ uris = ${lib.concatStringsSep " " cfg.ldap.uris}
+ ${lib.optionalString cfg.ldap.startTls ''
+ tls = yes
+ ''}
+ tls_require_cert = hard
+ tls_ca_cert_file = ${cfg.ldap.tlsCAFile}
+ dn = ${cfg.ldap.bind.dn}
+ sasl_bind = no
+ auth_bind = yes
+ base = ${cfg.ldap.searchBase}
+ scope = ${mkLdapSearchScope cfg.ldap.searchScope}
+ ${lib.optionalString (cfg.ldap.dovecot.userAttrs != "") ''
+ user_attrs = ${cfg.ldap.dovecot.user_attrs}
+ ''}
+ user_filter = ${cfg.ldap.dovecot.userFilter}
+ ${lib.optionalString (cfg.ldap.dovecot.passAttrs != "") ''
+ pass_attrs = ${cfg.ldap.dovecot.passAttrs}
+ ''}
+ pass_filter = ${cfg.ldap.dovecot.passFilter}
+ '';
+ };
+
+ setPwdInLdapConfFile = appendLdapBindPwd {
+ name = "ldap-conf-file";
+ file = ldapConfig;
+ prefix = "dnpass = ";
+ passwordFile = cfg.ldap.bind.passwordFile;
+ destination = ldapConfFile;
+ };
+
genPasswdScript = pkgs.writeScript "generate-password-file" ''
#!${pkgs.stdenv.shell}
@@ -75,7 +111,7 @@ let
fi
done
- cat <<EOF > ${passdbFile}
+ cat <<EOF > ${passwdFile}
${lib.concatStringsSep "\n" (lib.mapAttrsToList (name: value:
"${name}:${"$(head -n 1 ${passwordFiles."${name}"})"}::::::"
) cfg.loginAccounts)}
@@ -90,7 +126,7 @@ let
) cfg.loginAccounts)}
EOF
- chmod 600 ${passdbFile}
+ chmod 600 ${passwdFile}
chmod 600 ${userdbFile}
'';
@@ -226,7 +262,7 @@ in
passdb {
driver = passwd-file
- args = ${passdbFile}
+ args = ${passwdFile}
}
userdb {
@@ -238,12 +274,12 @@ in
${lib.optionalString cfg.ldap.enable ''
passdb {
driver = ldap
- args = /etc/dovecot/dovecot-ldap.conf.ext
+ args = ${ldapConfFile}
}
userdb {
driver = ldap
- args = /etc/dovecot/dovecot-ldap.conf.ext
+ args = ${ldapConfFile}
default_fields = home=/var/vmail/ldap/%u uid=${toString cfg.vmailUID} gid=${toString cfg.vmailUID}
}
''}
@@ -310,37 +346,6 @@ in
'';
};
- environment.etc = lib.optionalAttrs (cfg.ldap.enable) {
- "dovecot/dovecot-ldap.conf.ext" = {
- mode = "0600";
- uid = config.ids.uids.dovecot2;
- gid = config.ids.gids.dovecot2;
- text = ''
- ldap_version = 3
- uris = ${lib.concatStringsSep " " cfg.ldap.uris}
- ${lib.optionalString cfg.ldap.startTls ''
- tls = yes
- ''}
- tls_require_cert = hard
- tls_ca_cert_file = ${cfg.ldap.tlsCAFile}
- dn = ${cfg.ldap.bind.dn}
- dnpass = ${cfg.ldap.bind.password}
- sasl_bind = no
- auth_bind = yes
- base = ${cfg.ldap.searchBase}
- scope = ${mkLdapSearchScope cfg.ldap.searchScope}
- ${lib.optionalString (cfg.ldap.dovecot.userAttrs != "") ''
- user_attrs = ${cfg.ldap.dovecot.user_attrs}
- ''}
- user_filter = ${cfg.ldap.dovecot.userFilter}
- ${lib.optionalString (cfg.ldap.dovecot.passAttrs != "") ''
- pass_attrs = ${cfg.ldap.dovecot.passAttrs}
- ''}
- pass_filter = ${cfg.ldap.dovecot.passFilter}
- '';
- };
- };
-
systemd.services.dovecot2 = {
preStart = ''
${genPasswdScript}
@@ -351,10 +356,10 @@ in
${pkgs.dovecot_pigeonhole}/bin/sievec "$k"
done
chown -R '${dovecot2Cfg.mailUser}:${dovecot2Cfg.mailGroup}' '${stateDir}/imap_sieve'
- '';
+ '' + (lib.optionalString cfg.ldap.enable setPwdInLdapConfFile);
};
- systemd.services.postfix.restartTriggers = [ genPasswdScript ];
+ systemd.services.postfix.restartTriggers = [ genPasswdScript ] ++ (lib.optional cfg.ldap.enable [setPwdInLdapConfFile]);
systemd.services.dovecot-fts-xapian-optimize = lib.mkIf (cfg.fullTextSearch.enable && cfg.fullTextSearch.maintenance.enable) {
description = "Optimize dovecot indices for fts_xapian";
diff --git a/mail-server/postfix.nix b/mail-server/postfix.nix
index 576b8f7..ad7ce35 100644
--- a/mail-server/postfix.nix
+++ b/mail-server/postfix.nix
@@ -133,13 +133,13 @@ let
smtpd_sasl_security_options = "noanonymous";
smtpd_sasl_local_domain = "$myhostname";
smtpd_client_restrictions = "permit_sasl_authenticated,reject";
- smtpd_sender_login_maps = "hash:/etc/postfix/vaccounts${lib.optionalString cfg.ldap.enable ",ldap:${ldapSenderLoginMap}"}";
+ smtpd_sender_login_maps = "hash:/etc/postfix/vaccounts${lib.optionalString cfg.ldap.enable ",ldap:${ldapSenderLoginMapFile}"}";
smtpd_sender_restrictions = "reject_sender_login_mismatch";
smtpd_recipient_restrictions = "reject_non_fqdn_recipient,reject_unknown_recipient_domain,permit_sasl_authenticated,reject";
cleanup_service_name = "submission-header-cleanup";
};
- commonLdapConfig = lib.optionalString (cfg.ldap.enable) ''
+ commonLdapConfig = ''
server_host = ${lib.concatStringsSep " " cfg.ldap.uris}
start_tls = ${if cfg.ldap.startTls then "yes" else "no"}
version = 3
@@ -151,26 +151,47 @@ let
bind = yes
bind_dn = ${cfg.ldap.bind.dn}
- bind_pw = ${cfg.ldap.bind.password}
'';
- ldapSenderLoginMap = lib.optionalString (cfg.ldap.enable)
- (pkgs.writeText "ldap-sender-login-map.cf" ''
- ${commonLdapConfig}
- query_filter = ${cfg.ldap.postfix.filter}
- result_attribute = ${cfg.ldap.postfix.mailAttribute}
- '');
-
- ldapVirtualMailboxMap = lib.optionalString (cfg.ldap.enable)
- (pkgs.writeText "ldap-virtual-mailbox-map.cf" ''
- ${commonLdapConfig}
- query_filter = ${cfg.ldap.postfix.filter}
- result_attribute = ${cfg.ldap.postfix.uidAttribute}
- '');
+ ldapSenderLoginMap = pkgs.writeText "ldap-sender-login-map.cf" ''
+ ${commonLdapConfig}
+ query_filter = ${cfg.ldap.postfix.filter}
+ result_attribute = ${cfg.ldap.postfix.mailAttribute}
+ '';
+ ldapSenderLoginMapFile = "/run/postfix/ldap-sender-login-map.cf";
+ appendPwdInSenderLoginMap = appendLdapBindPwd {
+ name = "ldap-sender-login-map";
+ file = ldapSenderLoginMap;
+ prefix = "bind_pw = ";
+ passwordFile = cfg.ldap.bind.passwordFile;
+ destination = ldapSenderLoginMapFile;
+ };
+
+ ldapVirtualMailboxMap = pkgs.writeText "ldap-virtual-mailbox-map.cf" ''
+ ${commonLdapConfig}
+ query_filter = ${cfg.ldap.postfix.filter}
+ result_attribute = ${cfg.ldap.postfix.uidAttribute}
+ '';
+ ldapVirtualMailboxMapFile = "/run/postfix/ldap-virtual-mailbox-map.cf";
+ appendPwdInVirtualMailboxMap = appendLdapBindPwd {
+ name = "ldap-virtual-mailbox-map";
+ file = ldapVirtualMailboxMap;
+ prefix = "bind_pw = ";
+ passwordFile = cfg.ldap.bind.passwordFile;
+ destination = ldapVirtualMailboxMapFile;
+ };
in
{
config = with cfg; lib.mkIf enable {
+ systemd.services.postfix-setup = lib.mkIf cfg.ldap.enable {
+ preStart = ''
+ ${appendPwdInVirtualMailboxMap}
+ ${appendPwdInSenderLoginMap}
+ '';
+ restartTriggers = [ appendPwdInVirtualMailboxMap appendPwdInSenderLoginMap ];
+ };
+
services.postfix = {
enable = true;
hostname = "${sendingFqdn}";
@@ -202,7 +223,7 @@ in
virtual_mailbox_maps = [
(mappedFile "valias")
] ++ lib.optionals (cfg.ldap.enable) [
- "ldap:${ldapVirtualMailboxMap}"
+ "ldap:${ldapVirtualMailboxMapFile}"
];
virtual_transport = "lmtp:unix:/run/dovecot2/dovecot-lmtp";
# Avoid leakage of X-Original-To, X-Delivered-To headers between recipients
diff --git a/tests/ldap.nix b/tests/ldap.nix
index 6c8308d..172a77d 100644
--- a/tests/ldap.nix
+++ b/tests/ldap.nix
@@ -28,6 +28,8 @@ pkgs.nixosTest {
${pkgs.python3}/bin/python ${../scripts/mail-check.py} $@
'')];
+ environment.etc.bind-password.text = bindPassword;
+
services.openldap = {
enable = true;
settings = {
@@ -45,7 +47,7 @@ pkgs.nixosTest {
"olcMdbConfig"
];
olcDatabase = "{1}mdb";
- olcDbDirectory = "/var/lib/openldap";
+ olcDbDirectory = "/var/lib/openldap/example";
olcSuffix = "dc=example";
};
};
@@ -96,7 +98,7 @@ pkgs.nixosTest {
];
bind = {
dn = "cn=mail,dc=example";
- password = bindPassword;
+ passwordFile = "/etc/bind-password";
};
searchBase = "ou=users,dc=example";
searchScope = "sub";
@@ -141,6 +143,10 @@ pkgs.nixosTest {
machine.succeed("doveadm user -u alice@example.com")
machine.succeed("doveadm user -u bob@example.com")
+ with subtest("Files containing secrets are only readable by root"):
+ machine.succeed("ls -l /run/postfix/*.cf | grep -e '-rw------- 1 root root'")
+ machine.succeed("ls -l /run/dovecot2/dovecot-ldap.conf.ext | grep -e '-rw------- 1 root root'")
+
with subtest("Test account/mail address binding"):
machine.fail(" ".join([
"mail-check send-and-read",