Revert "Attempt at using stalwart again"
This reverts commit 2d3e02ad78
.
This commit is contained in:
parent
ad84cfae7e
commit
9af8f29b48
11 changed files with 162 additions and 649 deletions
|
@ -1,40 +1,9 @@
|
||||||
{ lib, config, ... }:
|
{ lib, config, ... }:
|
||||||
let
|
let
|
||||||
cfg = config.khscodes.infrastructure.mailserver;
|
cfg = config.khscodes.infrastructure.mailserver;
|
||||||
fqdn = config.khscodes.networking.fqdn;
|
|
||||||
acmeDir = config.security.acme.certs.${fqdn}.directory;
|
|
||||||
# extraDomainNames = lib.lists.filter (d: d != fqdn) (lib.lists.map (d: "mx.${d}") cfg.domains);
|
|
||||||
extraDomainNames = [ ];
|
|
||||||
user = "stalwart-mail";
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
config = lib.mkIf cfg.enable {
|
config = lib.mkIf cfg.enable {
|
||||||
khscodes.services.nginx.virtualHosts."${fqdn}" = {
|
khscodes.services.nginx.virtualHosts."${config.khscodes.networking.fqdn}" = { };
|
||||||
locations."/" = {
|
|
||||||
proxyPass = "http://127.0.0.1:8080";
|
|
||||||
proxyWebsockets = true;
|
|
||||||
recommendedProxySettings = true;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
services.stalwart-mail.settings = {
|
|
||||||
certificate.default = {
|
|
||||||
cert = "%{file:${acmeDir}/fullchain.pem}%";
|
|
||||||
private-key = "%{file:${config.security.acme.certs.${fqdn}.directory}/key.pem}%";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
security.acme.certs.${fqdn} = {
|
|
||||||
inherit extraDomainNames;
|
|
||||||
postRun = ''
|
|
||||||
systemctl restart stalwart-mail.service
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
systemd.services.stalwart-mail = {
|
|
||||||
after = [ "acme-selfsigned-${fqdn}.service" ];
|
|
||||||
wants = [ "acme-finished-${fqdn}.service" ];
|
|
||||||
serviceConfig.ReadOnlyPaths = [ acmeDir ];
|
|
||||||
};
|
|
||||||
users.users.${user}.extraGroups = [
|
|
||||||
config.security.acme.certs.${fqdn}.group
|
|
||||||
];
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,67 +0,0 @@
|
||||||
{ config, lib, ... }:
|
|
||||||
let
|
|
||||||
cfg = config.khscodes.infrastructure.mailserver;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
config = lib.mkIf cfg.enable {
|
|
||||||
khscodes.infrastructure.vault-server-approle.policy = {
|
|
||||||
# TODO: Make this configurable
|
|
||||||
"mx.kaareskovgaard.net/data/logins/admin" = {
|
|
||||||
capabilities = [ "read" ];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
khscodes.services.vault-agent.templates = [
|
|
||||||
{
|
|
||||||
contents = ''
|
|
||||||
{{- with secret "mx.kaareskovgaard.net/data/logins/admin" -}}
|
|
||||||
{{ .Data.data.hashed_password }}
|
|
||||||
{{- end -}}
|
|
||||||
'';
|
|
||||||
destination = "/run/secret/stalwart/users/admin";
|
|
||||||
owner = "stalwart-mail";
|
|
||||||
group = "stalwart-mail";
|
|
||||||
restartUnits = [ "stalwart-mail.service" ];
|
|
||||||
}
|
|
||||||
];
|
|
||||||
systemd.services.stalwart-mail = {
|
|
||||||
unitConfig.ConditionPathExists = [ "/run/secret/stalwart/users/admin" ];
|
|
||||||
serviceConfig.ReadOnlyPaths = [ "/run/secret/stalwart/users/admin" ];
|
|
||||||
};
|
|
||||||
services.stalwart-mail = {
|
|
||||||
settings = {
|
|
||||||
authentication.fallback-admin = {
|
|
||||||
user = "admin";
|
|
||||||
secret = "%{file:/run/secret/stalwart/users/admin}%";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
khscodes.infrastructure.provisioning.pre.modules = [
|
|
||||||
{
|
|
||||||
terraform.required_providers.random = {
|
|
||||||
source = "hashicorp/random";
|
|
||||||
version = "3.7.2";
|
|
||||||
};
|
|
||||||
provider.random = { };
|
|
||||||
|
|
||||||
resource.random_password.stalwart_fallback_admin_password = {
|
|
||||||
length = 48;
|
|
||||||
numeric = true;
|
|
||||||
lower = true;
|
|
||||||
upper = true;
|
|
||||||
special = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
resource.vault_kv_secret_v2.stalwart_fallback_admin_password = {
|
|
||||||
mount = "mx.kaareskovgaard.net";
|
|
||||||
name = "logins/admin";
|
|
||||||
data_json = ''
|
|
||||||
{
|
|
||||||
"hashed_password": ''${ jsonencode(resource.random_password.stalwart_fallback_admin_password.bcrypt_hash) },
|
|
||||||
"password": ''${ jsonencode(resource.random_password.stalwart_fallback_admin_password.result) }
|
|
||||||
}
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
config,
|
config,
|
||||||
lib,
|
lib,
|
||||||
pkgs,
|
inputs,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
|
@ -17,8 +17,7 @@ in
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
imports = [
|
imports = [
|
||||||
# inputs.simple-nixos-mailserver.nixosModules.mailserver
|
inputs.simple-nixos-mailserver.nixosModules.mailserver
|
||||||
./admin_password.nix
|
|
||||||
./acme.nix
|
./acme.nix
|
||||||
./dmarc.nix
|
./dmarc.nix
|
||||||
./dane.nix
|
./dane.nix
|
||||||
|
@ -29,58 +28,8 @@ in
|
||||||
./prometheus.nix
|
./prometheus.nix
|
||||||
./openid-connect.nix
|
./openid-connect.nix
|
||||||
./ldap.nix
|
./ldap.nix
|
||||||
./package/nixos-module.nix
|
|
||||||
];
|
];
|
||||||
config = lib.mkIf cfg.enable {
|
config = lib.mkIf cfg.enable {
|
||||||
services.stalwart-mail = {
|
|
||||||
enable = true;
|
|
||||||
package = pkgs.callPackage ./package/package.nix { };
|
|
||||||
settings = {
|
|
||||||
http = {
|
|
||||||
url = "https://${fqdn}";
|
|
||||||
use-x-forwarded = true;
|
|
||||||
};
|
|
||||||
server = {
|
|
||||||
hostname = fqdn;
|
|
||||||
tls = {
|
|
||||||
enable = true;
|
|
||||||
certificate = "default";
|
|
||||||
implicit = true;
|
|
||||||
ignore-client-order = true;
|
|
||||||
};
|
|
||||||
listener = {
|
|
||||||
smtp = {
|
|
||||||
protocol = "smtp";
|
|
||||||
bind = "[::]:25";
|
|
||||||
};
|
|
||||||
submissions = {
|
|
||||||
bind = "[::]:465";
|
|
||||||
protocol = "smtp";
|
|
||||||
tls.implicit = true;
|
|
||||||
};
|
|
||||||
imaps = {
|
|
||||||
bind = "[::]:993";
|
|
||||||
protocol = "imap";
|
|
||||||
tls.implicit = true;
|
|
||||||
};
|
|
||||||
jmap = {
|
|
||||||
bind = "[::]:8080";
|
|
||||||
url = "https://${fqdn}";
|
|
||||||
protocol = "jmap";
|
|
||||||
};
|
|
||||||
management = {
|
|
||||||
bind = "[::]:8080";
|
|
||||||
protocol = "http";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
lookup.default = {
|
|
||||||
hostname = fqdn;
|
|
||||||
domain = "kaareskovgaard.net";
|
|
||||||
};
|
|
||||||
spam-filter.resource = "${config.services.stalwart-mail.package.spam-filter}/spam-filter.toml";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
# TODO: Include a similiar rule for openstack
|
# TODO: Include a similiar rule for openstack
|
||||||
khscodes.infrastructure.hetzner-instance.extraFirewallRules = [
|
khscodes.infrastructure.hetzner-instance.extraFirewallRules = [
|
||||||
{
|
{
|
||||||
|
@ -129,6 +78,38 @@ in
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
];
|
];
|
||||||
|
mailserver = {
|
||||||
|
enable = true;
|
||||||
|
enableImap = false;
|
||||||
|
enableImapSsl = true;
|
||||||
|
enableSubmission = false;
|
||||||
|
enableSubmissionSsl = true;
|
||||||
|
fqdn = config.khscodes.networking.fqdn;
|
||||||
|
useUTF8FolderNames = true;
|
||||||
|
domains = cfg.domains;
|
||||||
|
certificateScheme = "acme";
|
||||||
|
};
|
||||||
|
services.fail2ban.jails = {
|
||||||
|
postfix = {
|
||||||
|
settings = {
|
||||||
|
enabled = true;
|
||||||
|
mode = "aggressive";
|
||||||
|
findtime = 600;
|
||||||
|
bantime = "1d";
|
||||||
|
maxretry = 3;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
dovecot = {
|
||||||
|
settings = {
|
||||||
|
enabled = true;
|
||||||
|
mode = "aggressive";
|
||||||
|
findtime = 600;
|
||||||
|
bantime = "1d";
|
||||||
|
maxretry = 3;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
networking.firewall.allowedTCPPorts = [
|
networking.firewall.allowedTCPPorts = [
|
||||||
25
|
25
|
||||||
465
|
465
|
||||||
|
|
|
@ -9,82 +9,28 @@ let
|
||||||
publicKeyEnd = ''"-----END PUBLIC KEY-----\n"'';
|
publicKeyEnd = ''"-----END PUBLIC KEY-----\n"'';
|
||||||
rsaKeyPath = domain: "/run/secret/dkim/${domain}.snm_rsa.key";
|
rsaKeyPath = domain: "/run/secret/dkim/${domain}.snm_rsa.key";
|
||||||
ed25519KeyPath = domain: "/run/secret/dkim/${domain}.snm_ed25519.key";
|
ed25519KeyPath = domain: "/run/secret/dkim/${domain}.snm_ed25519.key";
|
||||||
keyFiles = lib.lists.flatten (
|
domainKeyPaths = lib.lists.flatten (
|
||||||
lib.lists.map (domain: [
|
lib.lists.map (domain: [
|
||||||
(rsaKeyPath domain)
|
(rsaKeyPath domain)
|
||||||
(ed25519KeyPath domain)
|
(ed25519KeyPath domain)
|
||||||
]) cfg.domains
|
]) cfg.domains
|
||||||
);
|
);
|
||||||
ifthen = condition: expr: {
|
# Currently (2025-07-25) I canot get rspamd to sign with ed25519 key,
|
||||||
"if" = condition;
|
# it appears it attempts to parse it as an RSA key
|
||||||
"then" = expr;
|
# {
|
||||||
};
|
# path: "${ed25519KeyPath domain}";
|
||||||
otherwise = expr: { "else" = expr; };
|
# selector: "snm_ed25519";
|
||||||
authDkimForDomain = domain: [
|
# }
|
||||||
(ifthen "sender_domain = '${domain}'" "['${domain}_rsa', '${domain}_ed25519']")
|
dkimSigningForDomain = domain: ''
|
||||||
];
|
${domain} {
|
||||||
authDkim = lib.lists.flatten (lib.lists.map authDkimForDomain cfg.domains);
|
selectors [
|
||||||
signatureForDomain = domain: [
|
|
||||||
{
|
{
|
||||||
name = "${domain}_rsa";
|
path: "${rsaKeyPath domain}";
|
||||||
value = {
|
selector: "snm_rsa";
|
||||||
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 =
|
dkimPublicKey =
|
||||||
tls_key:
|
tls_key:
|
||||||
''''${ replace(trimprefix(trimsuffix(${tls_key}.public_key_pem, ${publicKeyEnd}), ${publicKeyBegin}), "\n", "") }'';
|
''''${ replace(trimprefix(trimsuffix(${tls_key}.public_key_pem, ${publicKeyEnd}), ${publicKeyBegin}), "\n", "") }'';
|
||||||
|
@ -190,10 +136,10 @@ in
|
||||||
'';
|
'';
|
||||||
destination = rsaKeyPath domain;
|
destination = rsaKeyPath domain;
|
||||||
perms = "0600";
|
perms = "0600";
|
||||||
owner = "stalwart-mail";
|
owner = "rspamd";
|
||||||
group = "stalwart-mail";
|
group = "rspamd";
|
||||||
restartUnits = [
|
restartUnits = [
|
||||||
"stalwart-mail.service"
|
"rspamd.service"
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
|
@ -204,25 +150,47 @@ in
|
||||||
'';
|
'';
|
||||||
destination = ed25519KeyPath domain;
|
destination = ed25519KeyPath domain;
|
||||||
perms = "0600";
|
perms = "0600";
|
||||||
owner = "stalwart-mail";
|
owner = "rspamd";
|
||||||
group = "stalwart-mail";
|
group = "rspamd";
|
||||||
restartUnits = [
|
restartUnits = [
|
||||||
"stalwart-mail.service"
|
"rspamd.service"
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
]) cfg.domains
|
]) cfg.domains
|
||||||
);
|
);
|
||||||
services.stalwart-mail.settings = {
|
mailserver = {
|
||||||
auth.dkim = {
|
dkimSigning = false;
|
||||||
sign = authDkim ++ [
|
|
||||||
(otherwise false)
|
|
||||||
];
|
|
||||||
};
|
};
|
||||||
|
services.rspamd.locals."dkim_signing.conf" = lib.mkForce {
|
||||||
|
text = ''
|
||||||
|
enabled = true;
|
||||||
|
allow_username_mismatch = true;
|
||||||
|
domain {
|
||||||
|
${lib.strings.concatStringsSep "\n " (lib.lists.map dkimSigningForDomain cfg.domains)}
|
||||||
}
|
}
|
||||||
// dkimSignatures;
|
'';
|
||||||
systemd.services.stalwart-mail = {
|
};
|
||||||
unitConfig.ConditionPathExists = keyFiles;
|
services.rspamd.locals."arc.conf" = lib.mkForce {
|
||||||
serviceConfig.ReadOnlyPaths = keyFiles;
|
text = ''
|
||||||
|
enabled = true;
|
||||||
|
allow_username_mismatch = true;
|
||||||
|
domain {
|
||||||
|
${lib.strings.concatStringsSep "\n " (lib.lists.map dkimSigningForDomain cfg.domains)}
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
services.postfix.config = {
|
||||||
|
# Need to include this as I disabled the in built support for dkim signing
|
||||||
|
# without this postfix won't forward the mails to rspamd to be signed.
|
||||||
|
non_smtpd_milters = [ "unix:/run/rspamd/rspamd-milter.sock" ];
|
||||||
|
};
|
||||||
|
systemd.services.rspamd = {
|
||||||
|
unitConfig = {
|
||||||
|
ConditionPathExists = domainKeyPaths;
|
||||||
|
};
|
||||||
|
serviceConfig = {
|
||||||
|
ReadOnlyPaths = domainKeyPaths;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,43 +16,44 @@ in
|
||||||
};
|
};
|
||||||
|
|
||||||
config = lib.mkIf cfg.enable {
|
config = lib.mkIf cfg.enable {
|
||||||
services.stalwart-mail.settings = {
|
mailserver.ldap = {
|
||||||
storage = {
|
enable = true;
|
||||||
directory = "ldap";
|
uris = [ "ldaps://login.kaareskovgaard.net" ];
|
||||||
};
|
searchBase = "dc=login,dc=kaareskovgaard,dc=net";
|
||||||
directory.ldap = {
|
searchScope = "sub";
|
||||||
type = "ldap";
|
|
||||||
url = "ldaps://login.kaareskovgaard.net";
|
|
||||||
base-dn = "dc=login,dc=kaareskovgaard,dc=net";
|
|
||||||
bind = {
|
bind = {
|
||||||
dn = "dn=token";
|
dn = "dn=token";
|
||||||
secret = "%{env:STALWART_LDAP_SECRET}%";
|
passwordFile = secretFile;
|
||||||
auth = {
|
|
||||||
method = "lookup";
|
|
||||||
};
|
};
|
||||||
|
dovecot = {
|
||||||
|
# Map LDAP uid to dovecot user, and ldap userPassword to dovecot password
|
||||||
|
passAttrs = "uid=user";
|
||||||
|
passFilter = "(&(class=account)(memberOf=mail_user)(uid=%u))";
|
||||||
|
# This filter is used both when receiving mail (thus needing to lookup by mail address, and when authenticating, requriing the lookup by uid.)
|
||||||
|
# Note that the pass filter only allows looking up by uid, so should still only be able to authenticate using that.
|
||||||
|
userFilter = "(&(class=account)(memberOf=mail_user)(|(mail=%u)(uid=%u)))";
|
||||||
|
userAttrs = "uid=user";
|
||||||
};
|
};
|
||||||
filter = {
|
postfix = {
|
||||||
name = "(&(class=account)(memberOf=mail_user)(uid=?))";
|
filter = "(&(class=account)(memberOf=mail_user)(mail=%s))";
|
||||||
email = "(&(class=account)(memberOf=mail_user)(mail=?))";
|
mailAttribute = "uid";
|
||||||
};
|
uidAttribute = "uid";
|
||||||
attributes = {
|
|
||||||
name = "name";
|
|
||||||
class = "class";
|
|
||||||
description = "name";
|
|
||||||
groups = "memberOf";
|
|
||||||
email = "mail;primary";
|
|
||||||
email-alias = "mail;alternative";
|
|
||||||
quota = "diskQuota";
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
systemd.services = {
|
systemd.services = {
|
||||||
stalwart-mail = {
|
dovecot2 = {
|
||||||
unitConfig = {
|
unitConfig = {
|
||||||
ConditionPathExists = [ secretFile ];
|
ConditionPathExists = [ secretFile ];
|
||||||
};
|
};
|
||||||
serviceConfig = {
|
};
|
||||||
EnvironmentFile = secretFile;
|
postfix = {
|
||||||
|
unitConfig = {
|
||||||
|
ConditionPathExists = [ secretFile ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
postfix-setup = {
|
||||||
|
unitConfig = {
|
||||||
|
ConditionPathExists = [ secretFile ];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -61,14 +62,15 @@ in
|
||||||
{
|
{
|
||||||
contents = ''
|
contents = ''
|
||||||
{{- with secret "${cfg.ldap.mount}/data/${cfg.ldap.path}" -}}
|
{{- with secret "${cfg.ldap.mount}/data/${cfg.ldap.path}" -}}
|
||||||
STALWART_LDAP_SECRET={{ .Data.data.apiToken }}
|
{{ .Data.data.apiToken }}
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
'';
|
'';
|
||||||
destination = secretFile;
|
destination = secretFile;
|
||||||
owner = "stalwart-mail";
|
owner = "dovecot";
|
||||||
group = "stalwart-mail";
|
group = "dovecot";
|
||||||
restartUnits = [
|
restartUnits = [
|
||||||
"stalwart-mail.service"
|
"dovecot2.service"
|
||||||
|
"postfix.service"
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
|
@ -5,41 +5,41 @@ let
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
config = lib.mkIf cfg.enable {
|
config = lib.mkIf cfg.enable {
|
||||||
# khscodes.services.vault-agent.templates = [
|
khscodes.services.vault-agent.templates = [
|
||||||
# {
|
{
|
||||||
# contents = ''
|
contents = ''
|
||||||
# {{- with secret "kanidm/data/apps/dovecot" -}}
|
{{- with secret "kanidm/data/apps/dovecot" -}}
|
||||||
# scope = email openid profile
|
scope = email openid profile
|
||||||
# username_attribute = username
|
username_attribute = username
|
||||||
# debug = yes
|
debug = yes
|
||||||
# introspection_url = https://dovecot:{{ .Data.data.basic_secret }}@login.kaareskovgaard.net/oauth2/token/introspect
|
introspection_url = https://dovecot:{{ .Data.data.basic_secret }}@login.kaareskovgaard.net/oauth2/token/introspect
|
||||||
# introspection_mode = post
|
introspection_mode = post
|
||||||
# {{- end -}}
|
{{- end -}}
|
||||||
# '';
|
'';
|
||||||
# destination = oauthConfigFile;
|
destination = oauthConfigFile;
|
||||||
# perms = "0600";
|
perms = "0600";
|
||||||
# owner = "root";
|
owner = "root";
|
||||||
# group = "root";
|
group = "root";
|
||||||
# restartUnits = [ "dovecot2.service" ];
|
restartUnits = [ "dovecot2.service" ];
|
||||||
# }
|
}
|
||||||
# ];
|
];
|
||||||
# services.dovecot2.extraConfig = ''
|
services.dovecot2.extraConfig = ''
|
||||||
# auth_mechanisms = $auth_mechanisms oauthbearer xoauth2
|
auth_mechanisms = $auth_mechanisms oauthbearer xoauth2
|
||||||
|
|
||||||
# passdb {
|
passdb {
|
||||||
# driver = oauth2
|
driver = oauth2
|
||||||
# mechanisms = xoauth2 oauthbearer
|
mechanisms = xoauth2 oauthbearer
|
||||||
# args = ${oauthConfigFile}
|
args = ${oauthConfigFile}
|
||||||
# }
|
}
|
||||||
# '';
|
'';
|
||||||
# systemd.services.dovecot2 = {
|
systemd.services.dovecot2 = {
|
||||||
# serviceConfig.ReadOnlyPaths = [
|
serviceConfig.ReadOnlyPaths = [
|
||||||
# oauthConfigFile
|
oauthConfigFile
|
||||||
# ];
|
];
|
||||||
# unitConfig.ConditionPathExists = [
|
unitConfig.ConditionPathExists = [
|
||||||
# oauthConfigFile
|
oauthConfigFile
|
||||||
# ];
|
];
|
||||||
# };
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
# This file contains patches for Nixos 25.05 to be compatible with new stalwart mail
|
|
||||||
{
|
|
||||||
lib,
|
|
||||||
config,
|
|
||||||
pkgs,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
let
|
|
||||||
configFormat = pkgs.formats.toml { };
|
|
||||||
configFile = configFormat.generate "stalwart-mail.toml" config.services.stalwart-mail.settings;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
systemd.services.stalwart-mail = lib.mkIf config.services.stalwart-mail.enable {
|
|
||||||
serviceConfig = {
|
|
||||||
User = "stalwart-mail";
|
|
||||||
Group = "stalwart-mail";
|
|
||||||
ExecStart = lib.mkForce [
|
|
||||||
""
|
|
||||||
"${lib.getExe config.services.stalwart-mail.package} --config=${configFile}"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,194 +0,0 @@
|
||||||
{
|
|
||||||
lib,
|
|
||||||
rustPlatform,
|
|
||||||
fetchFromGitHub,
|
|
||||||
pkg-config,
|
|
||||||
protobuf,
|
|
||||||
bzip2,
|
|
||||||
openssl,
|
|
||||||
sqlite,
|
|
||||||
foundationdb,
|
|
||||||
zstd,
|
|
||||||
stdenv,
|
|
||||||
nix-update-script,
|
|
||||||
nixosTests,
|
|
||||||
rocksdb,
|
|
||||||
callPackage,
|
|
||||||
withFoundationdb ? false,
|
|
||||||
stalwartEnterprise ? false,
|
|
||||||
}:
|
|
||||||
|
|
||||||
rustPlatform.buildRustPackage (finalAttrs: {
|
|
||||||
pname = "stalwart-mail" + (lib.optionalString stalwartEnterprise "-enterprise");
|
|
||||||
version = "0.13.2";
|
|
||||||
|
|
||||||
src = fetchFromGitHub {
|
|
||||||
owner = "stalwartlabs";
|
|
||||||
repo = "stalwart";
|
|
||||||
rev = "51a0a1445d74a8cfb880e9d88f5be390fa0e9365";
|
|
||||||
hash = "sha256-VdeHb1HVGXA5RPenhhK4r/kkQiLG8/4qhdxoJ3xIqR4=";
|
|
||||||
};
|
|
||||||
|
|
||||||
cargoHash = "sha256-Wu6skjs3Stux5nCX++yoQPeA33Qln67GoKcob++Ldng=";
|
|
||||||
|
|
||||||
nativeBuildInputs = [
|
|
||||||
pkg-config
|
|
||||||
protobuf
|
|
||||||
rustPlatform.bindgenHook
|
|
||||||
];
|
|
||||||
|
|
||||||
buildInputs = [
|
|
||||||
bzip2
|
|
||||||
openssl
|
|
||||||
sqlite
|
|
||||||
zstd
|
|
||||||
]
|
|
||||||
++ lib.optionals (stdenv.hostPlatform.isLinux && withFoundationdb) [ foundationdb ];
|
|
||||||
|
|
||||||
# Issue: https://github.com/stalwartlabs/stalwart/issues/1104
|
|
||||||
buildNoDefaultFeatures = true;
|
|
||||||
buildFeatures = [
|
|
||||||
"postgres"
|
|
||||||
"rocks"
|
|
||||||
"elastic"
|
|
||||||
"redis"
|
|
||||||
]
|
|
||||||
++ lib.optionals withFoundationdb [ "foundationdb" ]
|
|
||||||
++ lib.optionals stalwartEnterprise [ "enterprise" ];
|
|
||||||
|
|
||||||
env = {
|
|
||||||
OPENSSL_NO_VENDOR = true;
|
|
||||||
ZSTD_SYS_USE_PKG_CONFIG = true;
|
|
||||||
ROCKSDB_INCLUDE_DIR = "${rocksdb}/include";
|
|
||||||
ROCKSDB_LIB_DIR = "${rocksdb}/lib";
|
|
||||||
};
|
|
||||||
|
|
||||||
postInstall = ''
|
|
||||||
mkdir -p $out/etc/stalwart
|
|
||||||
|
|
||||||
mkdir -p $out/lib/systemd/system
|
|
||||||
|
|
||||||
substitute resources/systemd/stalwart-mail.service $out/lib/systemd/system/stalwart-mail.service \
|
|
||||||
--replace "__PATH__" "$out"
|
|
||||||
'';
|
|
||||||
|
|
||||||
checkFlags = lib.forEach [
|
|
||||||
# Require running mysql, postgresql daemon
|
|
||||||
"directory::imap::imap_directory"
|
|
||||||
"directory::internal::internal_directory"
|
|
||||||
"directory::ldap::ldap_directory"
|
|
||||||
"directory::sql::sql_directory"
|
|
||||||
"directory::oidc::oidc_directory"
|
|
||||||
"store::blob::blob_tests"
|
|
||||||
"store::lookup::lookup_tests"
|
|
||||||
"smtp::lookup::sql::lookup_sql"
|
|
||||||
# thread 'directory::smtp::lmtp_directory' panicked at tests/src/store/mod.rs:122:44:
|
|
||||||
# called `Result::unwrap()` on an `Err` value: Os { code: 2, kind: NotFound, message: "No such file or directory" }
|
|
||||||
"directory::smtp::lmtp_directory"
|
|
||||||
# thread 'imap::imap_tests' panicked at tests/src/imap/mod.rs:436:14:
|
|
||||||
# Missing store type. Try running `STORE=<store_type> cargo test`: NotPresent
|
|
||||||
"imap::imap_tests"
|
|
||||||
# thread 'jmap::jmap_tests' panicked at tests/src/jmap/mod.rs:303:14:
|
|
||||||
# Missing store type. Try running `STORE=<store_type> cargo test`: NotPresent
|
|
||||||
"jmap::jmap_tests"
|
|
||||||
# Failed to read system DNS config: io error: No such file or directory (os error 2)
|
|
||||||
"smtp::inbound::data::data"
|
|
||||||
# Expected "X-My-Header: true" but got Received: from foobar.net (unknown [10.0.0.123])
|
|
||||||
"smtp::inbound::scripts::sieve_scripts"
|
|
||||||
# thread 'smtp::outbound::lmtp::lmtp_delivery' panicked at tests/src/smtp/session.rs:313:13:
|
|
||||||
# Expected "<invalid@domain.org> (failed to lookup" but got From: "Mail Delivery Subsystem" <MAILER-DAEMON@localhost>
|
|
||||||
"smtp::outbound::lmtp::lmtp_delivery"
|
|
||||||
# thread 'smtp::outbound::extensions::extensions' panicked at tests/src/smtp/inbound/mod.rs:45:23:
|
|
||||||
# No queue event received.
|
|
||||||
"smtp::outbound::extensions::extensions"
|
|
||||||
# panicked at tests/src/smtp/outbound/smtp.rs:173:5:
|
|
||||||
"smtp::outbound::smtp::smtp_delivery"
|
|
||||||
# panicked at tests/src/smtp/outbound/lmtp.rs
|
|
||||||
"smtp::outbound::lmtp::lmtp_delivery"
|
|
||||||
# thread 'smtp::queue::retry::queue_retry' panicked at tests/src/smtp/queue/retry.rs:119:5:
|
|
||||||
# assertion `left == right` failed
|
|
||||||
# left: [1, 2, 2]
|
|
||||||
# right: [1, 2, 3]
|
|
||||||
"smtp::queue::retry::queue_retry"
|
|
||||||
# thread 'smtp::queue::virtualq::virtual_queue' panicked at /build/source/crates/store/src/dispatch/store.rs:548:14:
|
|
||||||
# called `Result::unwrap()` on an `Err` value: Error(Event { inner: Store(MysqlError), keys: [(Reason, String("Input/output error: Input/output error: Connection refused (os error 111)")), (CausedBy, String("crates/store/src/dispatch/store.rs:301"))] })
|
|
||||||
"smtp::queue::virtualq::virtual_queue"
|
|
||||||
# Missing store type. Try running `STORE=<store_type> cargo test`: NotPresent
|
|
||||||
"store::store_tests"
|
|
||||||
# Missing store type. Try running `STORE=<store_type> cargo test`: NotPresent
|
|
||||||
"cluster::cluster_tests"
|
|
||||||
# Missing store type. Try running `STORE=<store_type> cargo test`: NotPresent
|
|
||||||
"webdav::webdav_tests"
|
|
||||||
# thread 'config::parser::tests::toml_parse' panicked at crates/utils/src/config/parser.rs:463:58:
|
|
||||||
# called `Result::unwrap()` on an `Err` value: "Expected ['\\n'] but found '!' in value at line 70."
|
|
||||||
"config::parser::tests::toml_parse"
|
|
||||||
# error[E0432]: unresolved import `r2d2_sqlite`
|
|
||||||
# use of undeclared crate or module `r2d2_sqlite`
|
|
||||||
"backend::sqlite::pool::SqliteConnectionManager::with_init"
|
|
||||||
# thread 'smtp::reporting::analyze::report_analyze' panicked at tests/src/smtp/reporting/analyze.rs:88:5:
|
|
||||||
# assertion `left == right` failed
|
|
||||||
# left: 0
|
|
||||||
# right: 12
|
|
||||||
"smtp::reporting::analyze::report_analyze"
|
|
||||||
# thread 'smtp::inbound::dmarc::dmarc' panicked at tests/src/smtp/inbound/mod.rs:59:26:
|
|
||||||
# Expected empty queue but got Reload
|
|
||||||
"smtp::inbound::dmarc::dmarc"
|
|
||||||
# thread 'smtp::queue::concurrent::concurrent_queue' panicked at tests/src/smtp/inbound/mod.rs:65:9:
|
|
||||||
# assertion `left == right` failed
|
|
||||||
"smtp::queue::concurrent::concurrent_queue"
|
|
||||||
# Failed to read system DNS config: io error: No such file or directory (os error 2)
|
|
||||||
"smtp::inbound::auth::auth"
|
|
||||||
# Failed to read system DNS config: io error: No such file or directory (os error 2)
|
|
||||||
"smtp::inbound::antispam::antispam"
|
|
||||||
# Failed to read system DNS config: io error: No such file or directory (os error 2)
|
|
||||||
"smtp::inbound::vrfy::vrfy_expn"
|
|
||||||
# thread 'smtp::management::queue::manage_queue' panicked at tests/src/smtp/inbound/mod.rs:45:23:
|
|
||||||
# No queue event received.
|
|
||||||
# NOTE: Test unreliable on high load systems
|
|
||||||
"smtp::management::queue::manage_queue"
|
|
||||||
# thread 'responses::tests::parse_responses' panicked at crates/dav-proto/src/responses/mod.rs:671:17:
|
|
||||||
# assertion `left == right` failed: failed for 008.xml
|
|
||||||
# left: ElementEnd
|
|
||||||
# right: Bytes([...])
|
|
||||||
"responses::tests::parse_responses"
|
|
||||||
] (test: "--skip=${test}");
|
|
||||||
|
|
||||||
doCheck = !(stdenv.hostPlatform.isLinux && stdenv.hostPlatform.isAarch64);
|
|
||||||
|
|
||||||
# Allow network access during tests on Darwin/macOS
|
|
||||||
__darwinAllowLocalNetworking = true;
|
|
||||||
|
|
||||||
passthru = {
|
|
||||||
inherit rocksdb; # make used rocksdb version available (e.g., for backup scripts)
|
|
||||||
webadmin = callPackage ./webadmin.nix { };
|
|
||||||
spam-filter = callPackage ./spam-filter.nix { };
|
|
||||||
updateScript = nix-update-script { };
|
|
||||||
tests.stalwart-mail = nixosTests.stalwart-mail;
|
|
||||||
};
|
|
||||||
|
|
||||||
meta = {
|
|
||||||
description = "Secure & Modern All-in-One Mail Server (IMAP, JMAP, SMTP)";
|
|
||||||
homepage = "https://github.com/stalwartlabs/mail-server";
|
|
||||||
changelog = "https://github.com/stalwartlabs/mail-server/blob/main/CHANGELOG.md";
|
|
||||||
license = [
|
|
||||||
lib.licenses.agpl3Only
|
|
||||||
]
|
|
||||||
++ lib.optionals stalwartEnterprise [
|
|
||||||
{
|
|
||||||
fullName = "Stalwart Enterprise License 1.0 (SELv1) Agreement";
|
|
||||||
url = "https://github.com/stalwartlabs/mail-server/blob/main/LICENSES/LicenseRef-SEL.txt";
|
|
||||||
free = false;
|
|
||||||
redistributable = false;
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
mainProgram = "stalwart";
|
|
||||||
maintainers = with lib.maintainers; [
|
|
||||||
happysalada
|
|
||||||
onny
|
|
||||||
oddlama
|
|
||||||
pandapip1
|
|
||||||
norpol
|
|
||||||
];
|
|
||||||
};
|
|
||||||
})
|
|
|
@ -1,43 +0,0 @@
|
||||||
{
|
|
||||||
lib,
|
|
||||||
fetchFromGitHub,
|
|
||||||
stdenv,
|
|
||||||
stalwart-mail,
|
|
||||||
nix-update-script,
|
|
||||||
}:
|
|
||||||
|
|
||||||
stdenv.mkDerivation (finalAttrs: {
|
|
||||||
pname = "spam-filter";
|
|
||||||
version = "2.0.3";
|
|
||||||
|
|
||||||
src = fetchFromGitHub {
|
|
||||||
owner = "stalwartlabs";
|
|
||||||
repo = "spam-filter";
|
|
||||||
tag = "v${finalAttrs.version}";
|
|
||||||
hash = "sha256-NhD/qUiGhgESwR2IOzAHfDATRlgWMcCktlktvVfDONk=";
|
|
||||||
};
|
|
||||||
|
|
||||||
buildPhase = ''
|
|
||||||
bash ./build.sh
|
|
||||||
'';
|
|
||||||
|
|
||||||
installPhase = ''
|
|
||||||
mkdir -p $out
|
|
||||||
cp spam-filter.toml $out/
|
|
||||||
'';
|
|
||||||
|
|
||||||
passthru = {
|
|
||||||
updateScript = nix-update-script { };
|
|
||||||
};
|
|
||||||
|
|
||||||
meta = {
|
|
||||||
description = "Secure & modern all-in-one mail server Stalwart (spam-filter module)";
|
|
||||||
homepage = "https://github.com/stalwartlabs/spam-filter";
|
|
||||||
changelog = "https://github.com/stalwartlabs/spam-filter/blob/${finalAttrs.src.tag}/CHANGELOG.md";
|
|
||||||
license = with lib.licenses; [
|
|
||||||
mit
|
|
||||||
asl20
|
|
||||||
];
|
|
||||||
inherit (stalwart-mail.meta) maintainers;
|
|
||||||
};
|
|
||||||
})
|
|
|
@ -1,77 +0,0 @@
|
||||||
{
|
|
||||||
lib,
|
|
||||||
rustPlatform,
|
|
||||||
stalwart-mail,
|
|
||||||
fetchFromGitHub,
|
|
||||||
trunk,
|
|
||||||
tailwindcss_3,
|
|
||||||
fetchNpmDeps,
|
|
||||||
nix-update-script,
|
|
||||||
nodejs,
|
|
||||||
npmHooks,
|
|
||||||
llvmPackages,
|
|
||||||
wasm-bindgen-cli_0_2_93,
|
|
||||||
binaryen,
|
|
||||||
zip,
|
|
||||||
}:
|
|
||||||
|
|
||||||
rustPlatform.buildRustPackage (finalAttrs: {
|
|
||||||
pname = "webadmin";
|
|
||||||
version = "0.1.31";
|
|
||||||
|
|
||||||
src = fetchFromGitHub {
|
|
||||||
owner = "stalwartlabs";
|
|
||||||
repo = "webadmin";
|
|
||||||
rev = "6f1368b8a1160341b385980accea489ee0e45440";
|
|
||||||
hash = "sha256-/EWn/wiY6zFNhObfo11OkoGufcUODMYs18P3vTBbB8s=";
|
|
||||||
};
|
|
||||||
|
|
||||||
npmDeps = fetchNpmDeps {
|
|
||||||
name = "${finalAttrs.pname}-npm-deps";
|
|
||||||
hash = "sha256-na1HEueX8w7kuDp8LEtJ0nD1Yv39cyk6sEMpS1zix2s=";
|
|
||||||
};
|
|
||||||
|
|
||||||
cargoHash = "sha256-Q05+wH9+NfkfmEDJFLuWVQ7wuDeEu9h1XmOMN6SYdyU=";
|
|
||||||
|
|
||||||
postPatch = ''
|
|
||||||
# Using local tailwindcss for compilation
|
|
||||||
substituteInPlace Trunk.toml --replace-fail "npx tailwindcss" "tailwindcss"
|
|
||||||
'';
|
|
||||||
|
|
||||||
nativeBuildInputs = [
|
|
||||||
binaryen
|
|
||||||
llvmPackages.bintools-unwrapped
|
|
||||||
nodejs
|
|
||||||
npmHooks.npmConfigHook
|
|
||||||
tailwindcss_3
|
|
||||||
trunk
|
|
||||||
# needs to match with wasm-bindgen version in upstreams Cargo.lock
|
|
||||||
wasm-bindgen-cli_0_2_93
|
|
||||||
|
|
||||||
zip
|
|
||||||
];
|
|
||||||
|
|
||||||
NODE_PATH = "$npmDeps";
|
|
||||||
|
|
||||||
buildPhase = ''
|
|
||||||
trunk build --offline --frozen --release
|
|
||||||
'';
|
|
||||||
|
|
||||||
installPhase = ''
|
|
||||||
cd dist
|
|
||||||
mkdir -p $out
|
|
||||||
zip -r $out/webadmin.zip *
|
|
||||||
'';
|
|
||||||
|
|
||||||
passthru = {
|
|
||||||
updateScript = nix-update-script { };
|
|
||||||
};
|
|
||||||
|
|
||||||
meta = {
|
|
||||||
description = "Secure & modern all-in-one mail server Stalwart (webadmin module)";
|
|
||||||
homepage = "https://github.com/stalwartlabs/webadmin";
|
|
||||||
changelog = "https://github.com/stalwartlabs/webadmin/blob/${finalAttrs.src.tag}/CHANGELOG.md";
|
|
||||||
license = lib.licenses.agpl3Only;
|
|
||||||
inherit (stalwart-mail.meta) maintainers;
|
|
||||||
};
|
|
||||||
})
|
|
|
@ -1,16 +1,13 @@
|
||||||
{ config, lib, ... }:
|
{ config, lib, ... }:
|
||||||
let
|
let
|
||||||
fqdn = config.khscodes.networking.fqdn;
|
|
||||||
cfg = config.khscodes.infrastructure.mailserver;
|
cfg = config.khscodes.infrastructure.mailserver;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
config = lib.mkIf cfg.enable {
|
config = lib.mkIf cfg.enable {
|
||||||
services.stalwart-mail.settings.metrics.prometheus = {
|
|
||||||
|
services.prometheus.exporters.postfix = {
|
||||||
enable = true;
|
enable = true;
|
||||||
};
|
};
|
||||||
# Don't expose the endpoint
|
khscodes.infrastructure.vault-prometheus-sender.exporters.enabled = [ "postfix" ];
|
||||||
khscodes.services.nginx.virtualHosts."${fqdn}".locations."=/metrics/prometheus" = {
|
|
||||||
return = 404;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue