2025-06-18 19:53:44 +03:00
|
|
|
{
|
|
|
|
mailserver-service-account-name,
|
|
|
|
mailserver-service-account-token-name,
|
|
|
|
mailserver-service-account-token-fp,
|
2025-03-29 03:53:09 +04:00
|
|
|
}:
|
2025-06-18 19:53:44 +03:00
|
|
|
{
|
|
|
|
config,
|
|
|
|
lib,
|
|
|
|
pkgs,
|
|
|
|
...
|
|
|
|
}@nixos-args:
|
2024-12-20 18:41:07 +04:00
|
|
|
let
|
|
|
|
inherit (import ./common.nix nixos-args)
|
2025-01-31 14:31:09 +04:00
|
|
|
appendSetting
|
2025-01-17 15:53:21 +04:00
|
|
|
auth-passthru
|
2024-12-20 18:41:07 +04:00
|
|
|
cfg
|
|
|
|
domain
|
2025-01-31 14:31:09 +04:00
|
|
|
group
|
2025-01-17 15:53:21 +04:00
|
|
|
is-auth-enabled
|
2024-12-20 18:41:07 +04:00
|
|
|
;
|
|
|
|
|
2025-04-17 13:12:23 +04:00
|
|
|
runtime-folder = group;
|
|
|
|
keysPath = auth-passthru.keys-path;
|
2024-12-30 05:44:47 +04:00
|
|
|
|
2025-03-29 03:53:09 +04:00
|
|
|
# create service account token, needed for LDAP
|
2025-06-18 19:53:44 +03:00
|
|
|
kanidmExecStartPostScript = pkgs.writeShellScript "mailserver-kanidm-ExecStartPost-script.sh" ''
|
|
|
|
export HOME=$RUNTIME_DIRECTORY/client_home
|
|
|
|
readonly KANIDM="${pkgs.kanidm}/bin/kanidm"
|
2025-03-29 03:53:09 +04:00
|
|
|
|
2025-06-18 19:53:44 +03:00
|
|
|
# get Kanidm service account for mailserver
|
|
|
|
KANIDM_SERVICE_ACCOUNT="$($KANIDM service-account list --name idm_admin | grep -E "^name: ${mailserver-service-account-name}$")"
|
|
|
|
echo KANIDM_SERVICE_ACCOUNT: "$KANIDM_SERVICE_ACCOUNT"
|
|
|
|
if [ -n "$KANIDM_SERVICE_ACCOUNT" ]
|
|
|
|
then
|
|
|
|
echo "kanidm service account \"${mailserver-service-account-name}\" is found"
|
|
|
|
else
|
|
|
|
echo "kanidm service account \"${mailserver-service-account-name}\" is not found"
|
|
|
|
echo "creating new kanidm service account \"${mailserver-service-account-name}\""
|
|
|
|
if $KANIDM service-account create --name idm_admin ${mailserver-service-account-name} ${mailserver-service-account-name} idm_admin
|
|
|
|
then
|
|
|
|
"kanidm service account \"${mailserver-service-account-name}\" created"
|
|
|
|
else
|
|
|
|
echo "error: cannot create kanidm service account \"${mailserver-service-account-name}\""
|
|
|
|
exit 1
|
|
|
|
fi
|
|
|
|
fi
|
2025-03-29 03:53:09 +04:00
|
|
|
|
2025-06-18 19:53:44 +03:00
|
|
|
# add Kanidm service account to `idm_mail_servers` group
|
|
|
|
$KANIDM group add-members idm_mail_servers ${mailserver-service-account-name}
|
2025-03-29 03:53:09 +04:00
|
|
|
|
2025-06-18 19:53:44 +03:00
|
|
|
# create a new read-only token for mailserver
|
|
|
|
if ! KANIDM_SERVICE_ACCOUNT_TOKEN_JSON="$($KANIDM service-account api-token generate --name idm_admin ${mailserver-service-account-name} ${mailserver-service-account-token-name} --output json)"
|
|
|
|
then
|
|
|
|
echo "error: kanidm CLI returns an error when trying to generate service-account api-token"
|
|
|
|
exit 1
|
|
|
|
fi
|
|
|
|
if ! KANIDM_SERVICE_ACCOUNT_TOKEN="$(echo "$KANIDM_SERVICE_ACCOUNT_TOKEN_JSON" | ${lib.getExe pkgs.jq} -r .result)"
|
|
|
|
then
|
|
|
|
echo "error: cannot get service-account API token from JSON"
|
|
|
|
exit 1
|
|
|
|
fi
|
2025-03-29 03:53:09 +04:00
|
|
|
|
2025-06-18 19:53:44 +03:00
|
|
|
if ! install --mode=640 \
|
|
|
|
<(printf "%s" "$KANIDM_SERVICE_ACCOUNT_TOKEN") \
|
|
|
|
${mailserver-service-account-token-fp}
|
|
|
|
then
|
|
|
|
echo "error: cannot write token to \"${mailserver-service-account-token-fp}\""
|
|
|
|
exit 1
|
|
|
|
fi
|
|
|
|
'';
|
2025-03-29 03:53:09 +04:00
|
|
|
|
2025-04-17 13:12:23 +04:00
|
|
|
ldapConfFile = "/run/${runtime-folder}/dovecot-ldap.conf.ext";
|
2025-06-18 19:53:44 +03:00
|
|
|
mkLdapSearchScope =
|
|
|
|
scope:
|
|
|
|
(
|
|
|
|
if scope == "sub" then
|
|
|
|
"subtree"
|
|
|
|
else if scope == "one" then
|
|
|
|
"onelevel"
|
|
|
|
else
|
|
|
|
scope
|
|
|
|
);
|
2024-12-20 18:41:07 +04:00
|
|
|
dovecot-ldap-config = pkgs.writeTextFile {
|
|
|
|
name = "dovecot-ldap.conf.ext.template";
|
|
|
|
text = ''
|
|
|
|
ldap_version = 3
|
|
|
|
uris = ${lib.concatStringsSep " " config.mailserver.ldap.uris}
|
|
|
|
${lib.optionalString config.mailserver.ldap.startTls ''
|
2025-06-18 19:53:44 +03:00
|
|
|
tls = yes
|
2024-12-20 18:41:07 +04:00
|
|
|
''}
|
2024-12-27 07:46:36 +04:00
|
|
|
tls_require_cert = hard
|
|
|
|
tls_ca_cert_file = ${config.mailserver.ldap.tlsCAFile}
|
2024-12-20 18:41:07 +04:00
|
|
|
dn = ${config.mailserver.ldap.bind.dn}
|
|
|
|
sasl_bind = no
|
|
|
|
auth_bind = no
|
|
|
|
base = ${config.mailserver.ldap.searchBase}
|
|
|
|
scope = ${mkLdapSearchScope config.mailserver.ldap.searchScope}
|
|
|
|
${lib.optionalString (config.mailserver.ldap.dovecot.userAttrs != null) ''
|
2025-06-18 19:53:44 +03:00
|
|
|
user_attrs = ${config.mailserver.ldap.dovecot.userAttrs}
|
2024-12-20 18:41:07 +04:00
|
|
|
''}
|
|
|
|
user_filter = ${config.mailserver.ldap.dovecot.userFilter}
|
|
|
|
'';
|
|
|
|
};
|
2025-01-31 14:31:09 +04:00
|
|
|
setPwdInLdapConfFile = appendSetting {
|
2024-12-20 18:41:07 +04:00
|
|
|
name = "ldap-conf-file";
|
|
|
|
file = dovecot-ldap-config;
|
|
|
|
prefix = ''dnpass = "'';
|
|
|
|
suffix = ''"'';
|
|
|
|
passwordFile = config.mailserver.ldap.bind.passwordFile;
|
|
|
|
destination = ldapConfFile;
|
|
|
|
};
|
2025-01-31 14:31:09 +04:00
|
|
|
oauth-client-id = "mailserver";
|
2025-06-18 19:53:44 +03:00
|
|
|
oauth-client-secret-fp = "${keysPath}/${group}/kanidm-oauth-client-secret";
|
|
|
|
oauth-secret-ExecStartPreScript = pkgs.writeShellScript "${oauth-client-id}-kanidm-ExecStartPre-script.sh" ''
|
2025-01-31 14:31:09 +04:00
|
|
|
set -o xtrace
|
|
|
|
[ -f "${oauth-client-secret-fp}" ] || \
|
2025-04-17 12:49:50 +04:00
|
|
|
"${lib.getExe pkgs.openssl}" rand -base64 32 | tr "\n:@/+=" "012345" > "${oauth-client-secret-fp}"
|
2025-01-31 14:31:09 +04:00
|
|
|
'';
|
2025-04-17 13:12:23 +04:00
|
|
|
dovecot-oauth2-conf-fp = "/run/${runtime-folder}/dovecot-oauth2.conf.ext";
|
2025-01-31 14:31:09 +04:00
|
|
|
write-dovecot-oauth2-conf = appendSetting {
|
|
|
|
name = "oauth2-conf-file";
|
|
|
|
file = builtins.toFile "dovecot-oauth2.conf.ext.template" ''
|
2024-12-20 18:41:07 +04:00
|
|
|
introspection_mode = post
|
|
|
|
username_attribute = username
|
|
|
|
scope = email profile openid
|
|
|
|
tls_ca_cert_file = /etc/ssl/certs/ca-certificates.crt
|
|
|
|
active_attribute = active
|
|
|
|
active_value = true
|
2025-01-31 14:31:09 +04:00
|
|
|
openid_configuration_url = ${auth-passthru.oauth2-discovery-url oauth-client-id}
|
2024-12-26 18:27:25 +04:00
|
|
|
debug = "no"
|
2024-12-20 18:41:07 +04:00
|
|
|
'';
|
2025-06-18 19:53:44 +03:00
|
|
|
prefix =
|
|
|
|
''introspection_url = "'' + (auth-passthru.oauth2-introspection-url-prefix oauth-client-id);
|
2025-01-31 14:31:09 +04:00
|
|
|
suffix = auth-passthru.oauth2-introspection-url-postfix + ''"'';
|
|
|
|
passwordFile = oauth-client-secret-fp;
|
|
|
|
destination = dovecot-oauth2-conf-fp;
|
2024-12-20 18:41:07 +04:00
|
|
|
};
|
|
|
|
in
|
2025-01-25 01:08:41 +04:00
|
|
|
{
|
2025-04-17 13:12:23 +04:00
|
|
|
# for dovecot2 to have access to get through ${keysPath} directory
|
2025-01-31 14:31:09 +04:00
|
|
|
users.groups.keys.members = [ group ];
|
2025-04-17 13:12:23 +04:00
|
|
|
systemd.tmpfiles.settings."kanidm-secrets"."${keysPath}/${group}".d = {
|
|
|
|
user = "kanidm";
|
|
|
|
inherit group;
|
|
|
|
mode = "2750";
|
|
|
|
};
|
2025-01-31 14:31:09 +04:00
|
|
|
|
2024-12-20 18:41:07 +04:00
|
|
|
mailserver.ldap = {
|
|
|
|
# note: in `ldapsearch` first comes filter, then attributes
|
|
|
|
dovecot.userAttrs = "+"; # all operational attributes
|
|
|
|
# TODO: investigate whether "mail=%u" is better than:
|
|
|
|
# dovecot.userFilter = "(&(class=person)(uid=%n))";
|
|
|
|
};
|
|
|
|
|
|
|
|
services.dovecot2.extraConfig = ''
|
2025-03-28 17:23:41 +03:00
|
|
|
auth_mechanisms = xoauth2 oauthbearer plain login
|
2024-12-20 18:41:07 +04:00
|
|
|
|
|
|
|
passdb {
|
|
|
|
driver = oauth2
|
|
|
|
mechanisms = xoauth2 oauthbearer
|
2025-01-31 14:31:09 +04:00
|
|
|
args = ${dovecot-oauth2-conf-fp}
|
2024-12-20 18:41:07 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
userdb {
|
|
|
|
driver = static
|
|
|
|
args = uid=virtualMail gid=virtualMail home=/var/vmail/${domain}/%u
|
|
|
|
}
|
|
|
|
|
|
|
|
# provide SASL via unix socket to postfix
|
|
|
|
service auth {
|
|
|
|
unix_listener /var/lib/postfix/private-auth {
|
|
|
|
mode = 0660
|
|
|
|
user = postfix
|
|
|
|
group = postfix
|
|
|
|
}
|
|
|
|
}
|
|
|
|
service auth {
|
|
|
|
unix_listener auth-userdb {
|
|
|
|
mode = 0660
|
2024-12-30 05:44:47 +04:00
|
|
|
user = ${config.services.dovecot2.user}
|
2024-12-20 18:41:07 +04:00
|
|
|
}
|
|
|
|
unix_listener dovecot-auth {
|
|
|
|
mode = 0660
|
|
|
|
# Assuming the default Postfix user and group
|
|
|
|
user = postfix
|
|
|
|
group = postfix
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
userdb {
|
|
|
|
driver = ldap
|
|
|
|
args = ${ldapConfFile}
|
|
|
|
default_fields = home=/var/vmail/${domain}/%u uid=${toString config.mailserver.vmailUID} gid=${toString config.mailserver.vmailUID}
|
|
|
|
}
|
|
|
|
'';
|
|
|
|
services.dovecot2.enablePAM = false;
|
|
|
|
systemd.services.dovecot2 = {
|
2025-01-31 14:31:09 +04:00
|
|
|
preStart = setPwdInLdapConfFile + "\n" + write-dovecot-oauth2-conf + "\n";
|
2024-12-27 07:46:36 +04:00
|
|
|
after = [ auth-passthru.oauth2-systemd-service ];
|
2025-02-03 01:04:19 +04:00
|
|
|
requires = [ auth-passthru.oauth2-systemd-service ];
|
2025-04-17 13:12:23 +04:00
|
|
|
serviceConfig.RuntimeDirectory = lib.mkForce [ runtime-folder ];
|
2024-12-20 18:41:07 +04:00
|
|
|
};
|
|
|
|
|
2025-03-29 01:34:26 +04:00
|
|
|
systemd.services.kanidm.serviceConfig.ExecStartPre = lib.mkBefore [
|
2025-01-31 14:31:09 +04:00
|
|
|
("-" + oauth-secret-ExecStartPreScript)
|
|
|
|
];
|
2025-03-29 03:53:09 +04:00
|
|
|
systemd.services.kanidm.serviceConfig.ExecStartPost = lib.mkAfter [
|
|
|
|
("-" + kanidmExecStartPostScript)
|
|
|
|
];
|
|
|
|
|
2025-01-31 14:31:09 +04:00
|
|
|
systemd.services.postfix.restartTriggers = [
|
|
|
|
setPwdInLdapConfFile
|
|
|
|
write-dovecot-oauth2-conf
|
|
|
|
];
|
|
|
|
selfprivacy.passthru.mailserver = {
|
|
|
|
inherit oauth-client-id oauth-client-secret-fp;
|
|
|
|
};
|
2024-12-20 18:41:07 +04:00
|
|
|
}
|