support unhashed password files
This commit is contained in:
+13
@@ -178,6 +178,19 @@ in
|
|||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
passwordFile = mkOption {
|
||||||
|
type =
|
||||||
|
with types;
|
||||||
|
nullOr (pathWith {
|
||||||
|
inStore = false;
|
||||||
|
});
|
||||||
|
default = null;
|
||||||
|
example = "/run/keys/user1-password";
|
||||||
|
description = ''
|
||||||
|
A file containing the user's plain text password. The value will be hashed at runtime.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
aliases = mkOption {
|
aliases = mkOption {
|
||||||
type = with types; listOf types.str;
|
type = with types; listOf types.str;
|
||||||
example = [
|
example = [
|
||||||
|
|||||||
@@ -45,12 +45,19 @@ rec {
|
|||||||
in
|
in
|
||||||
lib.mapAttrs (
|
lib.mapAttrs (
|
||||||
name: value:
|
name: value:
|
||||||
if value.hashedPasswordFile == null then
|
if value.hashedPasswordFile != null then
|
||||||
|
value.hashedPasswordFile
|
||||||
|
else if value.hashedPassword != null then
|
||||||
builtins.toString (mkHashFile name value.hashedPassword)
|
builtins.toString (mkHashFile name value.hashedPassword)
|
||||||
else
|
else
|
||||||
value.hashedPasswordFile
|
value.passwordFile
|
||||||
) cfg.loginAccounts;
|
) cfg.loginAccounts;
|
||||||
|
|
||||||
|
# Collect accounts with plain text passwords that require hashing
|
||||||
|
accountsWithPlaintextPasswordFiles = lib.filter (
|
||||||
|
name: cfg.loginAccounts.${name}.passwordFile != null
|
||||||
|
) (builtins.attrNames cfg.loginAccounts);
|
||||||
|
|
||||||
# Appends the LDAP bind password to files to avoid writing this
|
# Appends the LDAP bind password to files to avoid writing this
|
||||||
# password into the Nix store.
|
# password into the Nix store.
|
||||||
appendLdapBindPwd =
|
appendLdapBindPwd =
|
||||||
|
|||||||
@@ -121,7 +121,11 @@ let
|
|||||||
cat <<EOF > ${passwdFile}
|
cat <<EOF > ${passwdFile}
|
||||||
${lib.concatStringsSep "\n" (
|
${lib.concatStringsSep "\n" (
|
||||||
lib.mapAttrsToList (
|
lib.mapAttrsToList (
|
||||||
name: _: "${name}:${"$(head -n 1 ${passwordFiles."${name}"})"}::::::"
|
name: _:
|
||||||
|
if lib.elem name accountsWithPlaintextPasswordFiles then
|
||||||
|
"${name}:${"$(sed -n '1{p;p;q}' ${passwordFiles."${name}"} | ${lib.getExe' pkgs.dovecot "doveadm"} pw)"}::::::"
|
||||||
|
else
|
||||||
|
"${name}:${"$(head -n 1 ${passwordFiles."${name}"})"}::::::"
|
||||||
) cfg.loginAccounts
|
) cfg.loginAccounts
|
||||||
)}
|
)}
|
||||||
EOF
|
EOF
|
||||||
|
|||||||
@@ -90,8 +90,15 @@ in
|
|||||||
config = lib.mkIf enable {
|
config = lib.mkIf enable {
|
||||||
# assert that all accounts provide a password
|
# assert that all accounts provide a password
|
||||||
assertions = map (acct: {
|
assertions = map (acct: {
|
||||||
assertion = acct.hashedPassword != null || acct.hashedPasswordFile != null;
|
assertion =
|
||||||
message = "${acct.name} must provide either a hashed password or a password hash file";
|
lib.length (
|
||||||
|
lib.filter (value: value != null) [
|
||||||
|
acct.hashedPassword
|
||||||
|
acct.hashedPasswordFile
|
||||||
|
acct.passwordFile
|
||||||
|
]
|
||||||
|
) == 1;
|
||||||
|
message = "Login account ${acct.name} must provide exactly one of password file, hashed password, or hashed password file";
|
||||||
}) (lib.attrValues loginAccounts);
|
}) (lib.attrValues loginAccounts);
|
||||||
|
|
||||||
# warn for accounts that specify both password and file
|
# warn for accounts that specify both password and file
|
||||||
|
|||||||
@@ -69,6 +69,15 @@ in
|
|||||||
netcat
|
netcat
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
systemd.tmpfiles.settings."mailserver-test-passwords" = {
|
||||||
|
"/run/passwords/user3" = {
|
||||||
|
f = {
|
||||||
|
argument = "my-password";
|
||||||
|
mode = "0600";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
mailserver = {
|
mailserver = {
|
||||||
enable = true;
|
enable = true;
|
||||||
fqdn = "mail.example.com";
|
fqdn = "mail.example.com";
|
||||||
@@ -86,6 +95,9 @@ in
|
|||||||
hashedPasswordFile = hashedPasswordFile;
|
hashedPasswordFile = hashedPasswordFile;
|
||||||
aliasesRegexp = [ ''/^user2.*@domain\.com$/'' ];
|
aliasesRegexp = [ ''/^user2.*@domain\.com$/'' ];
|
||||||
};
|
};
|
||||||
|
"user3@example.com" = {
|
||||||
|
passwordFile = "/run/passwords/user3";
|
||||||
|
};
|
||||||
"send-only@example.com" = {
|
"send-only@example.com" = {
|
||||||
hashedPasswordFile = hashPassword "send-only";
|
hashedPasswordFile = hashPassword "send-only";
|
||||||
sendOnly = true;
|
sendOnly = true;
|
||||||
@@ -223,6 +235,25 @@ in
|
|||||||
"set +o pipefail; curl --unix-socket /run/rspamd/worker-controller.sock http://localhost/ | grep -q '<body>'"
|
"set +o pipefail; curl --unix-socket /run/rspamd/worker-controller.sock http://localhost/ | grep -q '<body>'"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
with subtest("user with plaintext password file can send and receive"):
|
||||||
|
machine.succeed(
|
||||||
|
" ".join(
|
||||||
|
[
|
||||||
|
"mail-check send-and-read",
|
||||||
|
"--smtp-port 587",
|
||||||
|
"--smtp-starttls",
|
||||||
|
"--smtp-host localhost",
|
||||||
|
"--imap-host localhost",
|
||||||
|
"--imap-username user3@example.com",
|
||||||
|
"--from-addr user3@example.com",
|
||||||
|
"--to-addr user3@example.com",
|
||||||
|
"--src-password-file ${passwordFile}",
|
||||||
|
"--dst-password-file ${passwordFile}",
|
||||||
|
"--ignore-dkim-spf",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
with subtest("imap port 143 is closed and imaps is serving SSL"):
|
with subtest("imap port 143 is closed and imaps is serving SSL"):
|
||||||
machine.wait_for_closed_port(143)
|
machine.wait_for_closed_port(143)
|
||||||
machine.wait_for_open_port(993)
|
machine.wait_for_open_port(993)
|
||||||
|
|||||||
Reference in New Issue
Block a user