Non working parts: 1. OIDC login, stalwart assumes the entire token is base64 encoded, which it is not. 2. Apparently there's no support for mixed directories, allowing both logins from ldap and from internal database. I want this in order to support accounts for services as well as persons.
232 lines
7.1 KiB
Nix
232 lines
7.1 KiB
Nix
{
|
|
lib,
|
|
config,
|
|
...
|
|
}:
|
|
let
|
|
cfg = config.khscodes.infrastructure.mailserver;
|
|
publicKeyBegin = ''"-----BEGIN PUBLIC KEY-----\n"'';
|
|
publicKeyEnd = ''"-----END PUBLIC KEY-----\n"'';
|
|
rsaKeyPath = domain: "/run/secret/dkim/${domain}.snm_rsa.key";
|
|
ed25519KeyPath = domain: "/run/secret/dkim/${domain}.snm_ed25519.key";
|
|
keyFiles = lib.lists.flatten (
|
|
lib.lists.map (domain: [
|
|
(rsaKeyPath domain)
|
|
(ed25519KeyPath domain)
|
|
]) cfg.domains
|
|
);
|
|
ifthen = condition: expr: {
|
|
"if" = condition;
|
|
"then" = expr;
|
|
};
|
|
otherwise = expr: { "else" = expr; };
|
|
authDkimForDomain = domain: [
|
|
(ifthen "sender_domain = '${domain}'" "['${domain}_rsa', '${domain}_ed25519']")
|
|
];
|
|
authDkim = lib.lists.flatten (lib.lists.map authDkimForDomain cfg.domains);
|
|
signatureForDomain = domain: [
|
|
{
|
|
name = "${domain}_rsa";
|
|
value = {
|
|
inherit domain;
|
|
private-key = "%{file:${rsaKeyPath domain}}%";
|
|
selector = "snm_rsa";
|
|
headers = [
|
|
"From"
|
|
"To"
|
|
"Cc"
|
|
"Date"
|
|
"Subject"
|
|
"Message-ID"
|
|
"Organization"
|
|
"MIME-Version"
|
|
"Content-Type"
|
|
"In-Reply-To"
|
|
"References"
|
|
"List-Id"
|
|
"User-Agent"
|
|
"Thread-Topic"
|
|
"Thread-Index"
|
|
];
|
|
algorithm = "rsa-sha256";
|
|
canonicalization = "relaxed/relaxed";
|
|
report = true;
|
|
};
|
|
}
|
|
{
|
|
name = "${domain}_ed25519";
|
|
value = {
|
|
inherit domain;
|
|
private-key = "%{file:${ed25519KeyPath domain}}%";
|
|
selector = "snm_ed25519";
|
|
headers = [
|
|
"From"
|
|
"To"
|
|
"Cc"
|
|
"Date"
|
|
"Subject"
|
|
"Message-ID"
|
|
"Organization"
|
|
"MIME-Version"
|
|
"Content-Type"
|
|
"In-Reply-To"
|
|
"References"
|
|
"List-Id"
|
|
"User-Agent"
|
|
"Thread-Topic"
|
|
"Thread-Index"
|
|
];
|
|
algorithm = "ed25519-sha256";
|
|
canonicalization = "relaxed/relaxed";
|
|
report = true;
|
|
};
|
|
}
|
|
];
|
|
dkimSignatures = {
|
|
signature = lib.listToAttrs (lib.lists.flatten (lib.lists.map signatureForDomain cfg.domains));
|
|
};
|
|
dkimPublicKey =
|
|
tls_key:
|
|
''''${ replace(trimprefix(trimsuffix(${tls_key}.public_key_pem, ${publicKeyEnd}), ${publicKeyBegin}), "\n", "") }'';
|
|
in
|
|
{
|
|
options.khscodes.infrastructure.mailserver.dkim = {
|
|
vault = {
|
|
mount = lib.mkOption {
|
|
type = lib.types.str;
|
|
};
|
|
mountExpr = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "\${ vault_mount.${lib.khscodes.sanitize-terraform-name cfg.dkim.vault.mount}.path }";
|
|
};
|
|
prefixPath = lib.mkOption {
|
|
type = lib.types.str;
|
|
};
|
|
};
|
|
};
|
|
config = lib.mkIf (cfg.enable) {
|
|
khscodes.infrastructure.vault-server-approle.policy = {
|
|
"${cfg.dkim.vault.mount}/data/${cfg.dkim.vault.prefixPath}/*" = {
|
|
capabilities = [ "read" ];
|
|
};
|
|
};
|
|
khscodes.infrastructure.provisioning.pre.modules = [
|
|
(
|
|
{ ... }:
|
|
{
|
|
terraform.required_providers.tls = {
|
|
source = "hashicorp/tls";
|
|
version = "4.1.0";
|
|
};
|
|
provider.tls = { };
|
|
|
|
khscodes.cloudflare.enable = true;
|
|
resource.tls_private_key =
|
|
(lib.listToAttrs (
|
|
lib.lists.map (domain: {
|
|
name = "${lib.khscodes.sanitize-terraform-name domain}_dkim_rsa";
|
|
value = {
|
|
algorithm = "RSA";
|
|
rsa_bits = 2048;
|
|
};
|
|
}) cfg.domains
|
|
))
|
|
// (lib.listToAttrs (
|
|
lib.lists.map (domain: {
|
|
name = "${lib.khscodes.sanitize-terraform-name domain}_dkim_ed25519";
|
|
value = {
|
|
algorithm = "ED25519";
|
|
};
|
|
}) cfg.domains
|
|
));
|
|
|
|
khscodes.cloudflare.dns.txtRecords =
|
|
(lib.lists.map (domain: {
|
|
fqdn = "snm_rsa._domainkey.${domain}";
|
|
content = ''"''${ join("\" \"", regexall(".{1,255}", "v=DKIM1;k=rsa;p=${dkimPublicKey "tls_private_key.${lib.khscodes.sanitize-terraform-name domain}_dkim_rsa"}" )) }"'';
|
|
ttl = 600;
|
|
}) cfg.domains)
|
|
++ (lib.lists.map (domain: {
|
|
fqdn = "snm_ed25519._domainkey.${domain}";
|
|
content = ''"''${ join("\" \"", regexall(".{1,255}", "v=DKIM1;k=ed25519;p=${dkimPublicKey "tls_private_key.${lib.khscodes.sanitize-terraform-name domain}_dkim_ed25519"}" )) }"'';
|
|
ttl = 600;
|
|
}) cfg.domains);
|
|
|
|
resource.vault_kv_secret_v2 =
|
|
(lib.listToAttrs (
|
|
lib.lists.map (domain: {
|
|
name = "${lib.khscodes.sanitize-terraform-name domain}_dkim_rsa";
|
|
value = {
|
|
mount = cfg.dkim.vault.mountExpr;
|
|
name = cfg.dkim.vault.prefixPath + "/${domain}/rsa";
|
|
data_json = ''
|
|
{ "dkim_private_key": ''${ jsonencode(resource.tls_private_key.${lib.khscodes.sanitize-terraform-name domain}_dkim_rsa.private_key_pem) } }
|
|
'';
|
|
};
|
|
}) cfg.domains
|
|
))
|
|
// (lib.listToAttrs (
|
|
lib.lists.map (domain: {
|
|
name = "${lib.khscodes.sanitize-terraform-name domain}_dkim_ed25519";
|
|
value = {
|
|
mount = cfg.dkim.vault.mountExpr;
|
|
name = cfg.dkim.vault.prefixPath + "/${domain}/ed25519";
|
|
data_json = ''
|
|
{ "dkim_private_key": ''${ jsonencode(resource.tls_private_key.${lib.khscodes.sanitize-terraform-name domain}_dkim_ed25519.private_key_pem) } }
|
|
'';
|
|
};
|
|
}) cfg.domains
|
|
));
|
|
}
|
|
)
|
|
];
|
|
khscodes.services.vault-agent.templates = lib.lists.flatten (
|
|
lib.lists.map (domain: [
|
|
{
|
|
contents = ''
|
|
{{- with secret "${cfg.dkim.vault.mount}/data/${cfg.dkim.vault.prefixPath}/${domain}/rsa" -}}
|
|
{{ .Data.data.dkim_private_key }}
|
|
{{- end -}}
|
|
'';
|
|
destination = rsaKeyPath domain;
|
|
perms = "0600";
|
|
owner = "stalwart-mail";
|
|
group = "stalwart-mail";
|
|
restartUnits = [
|
|
"stalwart-mail.service"
|
|
];
|
|
}
|
|
{
|
|
contents = ''
|
|
{{- with secret "${cfg.dkim.vault.mount}/data/${cfg.dkim.vault.prefixPath}/${domain}/ed25519" -}}
|
|
{{ .Data.data.dkim_private_key }}
|
|
{{- end -}}
|
|
'';
|
|
destination = ed25519KeyPath domain;
|
|
perms = "0600";
|
|
owner = "stalwart-mail";
|
|
group = "stalwart-mail";
|
|
restartUnits = [
|
|
"stalwart-mail.service"
|
|
];
|
|
}
|
|
]) cfg.domains
|
|
);
|
|
services.stalwart-mail.settings = {
|
|
config.local-keys = [
|
|
"auth.*"
|
|
"signature.*"
|
|
];
|
|
auth.dkim = {
|
|
sign = authDkim ++ [
|
|
(otherwise false)
|
|
];
|
|
};
|
|
}
|
|
// dkimSignatures;
|
|
systemd.services.stalwart-mail = {
|
|
unitConfig.ConditionPathExists = keyFiles;
|
|
serviceConfig.ReadOnlyPaths = keyFiles;
|
|
};
|
|
};
|
|
}
|