Non working attempt at getting correct login information working

This commit is contained in:
Kaare Hoff Skovgaard 2025-07-31 10:34:23 +02:00
parent fbe957b046
commit cd4c06686e
Signed by: khs
GPG key ID: C7D890804F01E9F0
4 changed files with 189 additions and 24 deletions

View file

@ -43,6 +43,7 @@ in
system.autoUpgrade = {
enable = true;
flake = upgradePath;
allowReboot = true;
};
systemd.services.nixos-upgrade-prepare-flake = {
wantedBy = [ "nixos-upgrade.service" ];

View file

@ -7,6 +7,17 @@
let
cfg = config.khscodes."mx.kaareskovgaard.net";
passDbFile = "/run/secret/dovecot/passwd";
# This just replicates what simple-nixos-mailserver does, but using the proper usernames
userDbFile = pkgs.writeTextFile {
name = "userdb";
text = lib.concatStringsSep "\n" (
lib.mapAttrsToList (
name: value:
"${name}:::::::"
+ lib.optionalString (value.quota != null) "userdb_quota_rule=*:storage=${value.quota}"
) cfg.accounts
);
};
bogusPasswdFile = pkgs.writeTextFile {
name = "bogus-passwd";
text = "$6$1234";
@ -128,7 +139,7 @@ let
systemAccounts = lib.attrsets.foldlAttrs (
acc: name: value:
if value.isLdapAccount then acc else acc ++ [ name ]
if value.isLdapAccount then acc else acc ++ [ (accountPrimaryEmail name value) ]
) [ ] cfg.accounts;
systemAccountsPassDbTemplateContents =
@ -142,16 +153,14 @@ let
# Just make sure the file is not empty
+ "\n";
# I would like to not use emails as usernames, but this is not something postfix is particularly happy with,
# as far as I can see. I *think* I can do this by randomly selecting an address, ie. the first, as the "primary"
# address, and then map all other addresses to that one. Then create a virtual mailbox map that maps that back
# to the username before delivering it to lmtp. All of this just worked when using ldap, but that meant
# not using code configured accounts set up with opentofu as well, and would also make the email delivery
# take a hard dependency on ldap (and not just the login process).
data = import ./accounts/mailbox_map.nix {
inherit lib accountPrimaryEmail accountAlternativeEmails;
accounts = cfg.accounts;
extraVirtualAliases = { };
};
# Create extra virtual aliases for all accounts (as they are not emails, that maps them to their primary email).
# Then we map those emails back into the account name in the mailbox maps.
extraVirtualAliases = lib.mapAttrs (name: account: accountPrimaryEmail name account) cfg.accounts;
mappedFile = name: "hash:/var/lib/postfix/conf/${name}";
mappedRegexFile = name: "pcre:/var/lib/postfix/conf/${name}";
in
{
options.khscodes."mx.kaareskovgaard.net".accounts = lib.mkOption {
@ -160,8 +169,9 @@ in
};
config = {
mailserver = {
inherit extraVirtualAliases;
loginAccounts = lib.attrsets.mapAttrs (name: value: {
loginAccounts = lib.attrsets.mapAttrs' (name: value: {
name = accountPrimaryEmail name value;
value = {
inherit (value)
name
aliasesRegexp
@ -173,7 +183,20 @@ in
;
aliases = accountAlternativeEmails name value;
hashedPasswordFile = bogusPasswdFile;
};
}) cfg.accounts;
extraVirtualAliases = data.mailserverExtraVirtualAliases;
};
services.postfix = {
mapFiles."valias_maps" = data.valiases_file;
mapFiles."regex_valias_maps" = data.regex_valiases_file;
mapFiles."vaccounts" = lib.mkForce data.vaccounts_file;
mapFiles."regex_vaccounts" = lib.mkForce data.regex_vaccounts_file;
config.virtual_mailbox_maps = lib.mkForce [
(mappedFile "valias_maps")
(mappedRegexFile "regex_valias_maps")
];
};
khscodes.infrastructure.vault-server-approle.policy = {
"mx.kaareskovgaard.net/data/users/*" = {
@ -235,6 +258,7 @@ in
# with our own.
preStart = lib.mkAfter ''
cp ${passDbFile} /run/dovecot2/passwd
cp ${userDbFile} /run/dovecot2/userdb
'';
};
# This prevents local usernames without domain names to get rewritten.

View file

@ -0,0 +1,140 @@
{
lib,
accounts,
accountPrimaryEmail,
accountAlternativeEmails,
extraVirtualAliases ? { },
}:
let
# I would like to not use emails as usernames, but this is not something postfix is particularly happy with,
# as far as I can see. I *think* I can do this by randomly selecting an address, ie. the first, as the "primary"
# address, and then map all other addresses to that one. Then create a virtual mailbox map that maps that back
# to the username before delivering it to lmtp. All of this just worked when using ldap, but that meant
# not using code configured accounts set up with opentofu as well, and would also make the email delivery
# take a hard dependency on ldap (and not just the login process).
# Most of these functions are almost the same as what is found inside nixos-simple-mailserver,
# just modified with this in mind.
# Merge several lookup tables. A lookup table is a attribute set where
# - the key is an address (user@example.com) or a domain (@example.com)
# - the value is a list of addresses
mergeLookupTables = tables: lib.zipAttrsWith (_: v: lib.flatten v) tables;
# lookupTableToString :: Map String [String] -> String
lookupTableToString =
attrs:
let
valueToString = value: lib.concatStringsSep ", " value;
in
lib.concatStringsSep "\n" (
lib.mapAttrsToList (name: value: "${name} ${valueToString value}") attrs
);
# attrsToLookupTable :: Map String (Either String [ String ]) -> Map String [String]
attrsToLookupTable =
aliases:
let
lookupTables = lib.mapAttrsToList (from: to: { "${from}" = to; }) aliases;
in
mergeLookupTables lookupTables;
# valiases_postfix :: Map String [String]
valiases_postfix = mergeLookupTables (
lib.flatten (
lib.mapAttrsToList (
name: value:
let
to = name;
in
map (from: { "${from}" = to; }) (
(accountAlternativeEmails name value) ++ lib.singleton (accountPrimaryEmail name value)
)
) accounts
)
);
# catchAllPostfix :: Map String [String]
catchAllPostfix = mergeLookupTables (
lib.flatten (
lib.mapAttrsToList (
name: value:
let
to = name;
in
map (from: { "@${from}" = to; }) value.catchAll
) accounts
)
);
# mailserverExtraVirtualAliases :: Map String (Map String)
mailserverExtraVirtualAliases = lib.attrsets.mapAttrs (
name: value:
let
value = if lib.lists.isList value then value else [ value ];
to = name;
in
{
name = to;
value = lib.lists.map (
addr:
assert (lib.attrsets.hasAttr addr accounts);
accountPrimaryEmail name accounts."${addr}"
) value;
}
) extraVirtualAliases;
# extra_valiases_postfix :: Map String [String]
extra_valiases_postfix = attrsToLookupTable extraVirtualAliases;
# all_valiases_postfix :: Map String [String]
all_valiases_postfix = mergeLookupTables [
valiases_postfix
extra_valiases_postfix
];
# valiases_file :: Path
valiases_file =
let
content = lookupTableToString (mergeLookupTables [
all_valiases_postfix
catchAllPostfix
]);
in
builtins.toFile "valias" content;
regex_valiases_postfix = mergeLookupTables (
lib.flatten (
lib.mapAttrsToList (
name: value:
let
to = name;
in
map (from: { "${from}" = to; }) value.aliasesRegexp
) accounts
)
);
regex_valiases_file =
let
content = lookupTableToString regex_valiases_postfix;
in
builtins.toFile "regex_valias" content;
# vaccounts_file :: Path
# see
# https://blog.grimneko.de/2011/12/24/a-bunch-of-tips-for-improving-your-postfix-setup/
# for details on how this file looks. By using the same file as valiases,
# every alias is owned (uniquely) by its user.
# The user's own address is already in all_valiases_postfix.
vaccounts_file = builtins.toFile "vaccounts" (lookupTableToString all_valiases_postfix);
regex_vaccounts_file = builtins.toFile "regex_vaccounts" (
lookupTableToString regex_valiases_postfix
);
in
{
inherit
valiases_file
regex_valiases_file
vaccounts_file
regex_vaccounts_file
mailserverExtraVirtualAliases
;
}

View file

@ -41,7 +41,7 @@ in
base = dc=login,dc=kaareskovgaard,dc=net
scope = subtree
pass_attrs = uid=user
pass_filter = (&(class=account)(memberOf=mail_user)(uid=%u)
pass_filter = (&(class=account)(memberOf=mail_user)(uid=%u))
{{- end -}}
'';
destination = secretFile;