1
1
Fork 1
mirror of https://github.com/oddlama/nix-config.git synced 2025-10-10 23:00:39 +02:00

feat: finish stalwart setup

This commit is contained in:
oddlama 2024-07-31 14:57:56 +02:00
parent 01660a3389
commit 4e717fab96
No known key found for this signature in database
GPG key ID: 14EFE510775FE39A
8 changed files with 58 additions and 141 deletions

View file

@ -1,4 +1,8 @@
{config, ...}: let {
config,
lib,
...
}: let
mailDomains = config.repo.secrets.global.domains.mail; mailDomains = config.repo.secrets.global.domains.mail;
primaryDomain = mailDomains.primary; primaryDomain = mailDomains.primary;
idmailDomain = "alias.${primaryDomain}"; idmailDomain = "alias.${primaryDomain}";
@ -13,6 +17,12 @@ in {
# } # }
#]; #];
age.secrets.idmail-admin-hash = {
rekeyFile = ./secrets/idmail-admin-hash.age;
mode = "440";
group = "stalwart-mail";
};
globals.services.idmail.domain = idmailDomain; globals.services.idmail.domain = idmailDomain;
globals.monitoring.http.idmail = { globals.monitoring.http.idmail = {
url = "https://${idmailDomain}"; url = "https://${idmailDomain}";
@ -28,24 +38,12 @@ in {
enable = true; enable = true;
users.admin = { users.admin = {
admin = true; admin = true;
# FIXME: 8e8e1c2eb2f1b8c84f1ef294d2fd746b password_hash = "%{file:${config.age.secrets.idmail-admin-hash.path}}%";
password_hash = "$argon2id$v=19$m=4096,t=3,p=1$c29tZXJhbmRvbXNhbHQ$Hf0sBCqn5Zp5+7LalZNLKhG0exNsXN2M5T+y3QAjpMM";
}; };
# users.test.password_hash = "$argon2id$v=19$m=4096,t=3,p=1$YXJnbGluYXJsZ2luMjRvaQ$DXdfVNRSFS1QSvJo7OmXIhAYYtT/D92Ku16DiJwxn8U"; domains = lib.genAttrs mailDomains.all (_: {
# domains."example.com" = { owner = "admin";
# owner = "admin"; public = true;
# public = true; });
# };
# mailboxes."me@example.com" = {
# password_hash = "$argon2id$v=19$m=4096,t=3,p=1$YXJnbGluYXJsZ2luMjRvaQ$fiD9Bp3KidVI/E+mGudu6+h9XmF9TU9Bx4VGX0PniDE";
# owner = "test";
# api_token = "%{file:${pkgs.writeText "token" token}}%";
# };
# aliases."somealias@example.com" = {
# target = "me@example.com";
# owner = "me@example.com";
# comment = "Used for xyz";
# };
}; };
}; };
systemd.services.idmail.serviceConfig.RestartSec = "60"; # Retry every minute systemd.services.idmail.serviceConfig.RestartSec = "60"; # Retry every minute

Binary file not shown.

View file

@ -1,11 +1,11 @@
age-encryption.org/v1 age-encryption.org/v1
-> X25519 4dtyNzQ1aoj+se7IxhCnN9A8bOmhPxm3bibijfCNjVw -> X25519 t0FJIrbn5q7oX4+1tHvjDnWDCiD6NMkNw7Aq2MfSXw4
PoRE6VrM+shoneZJAS+Fh/kIjn9tX6mw9Kr2vD3xOSE W6aq9jnVOH9W+pjsrSCZG1BJXSNojhiUrTgzANFpM9w
-> piv-p256 xqSe8Q AwvKCfsTHQh3Z05VZ3kRtaa90pqyR3tY+wKwwibfdvzF -> piv-p256 xqSe8Q A3qjOwYgwBFDf0beUSyw3nHhkO9ZhsJzGHBh4BTw7+tS
dFjn+siQjWdhMVCGsiZyFNBykTrCIrHr9zt3aRxtSQc ffyaSOMoOHiIXfXvCJY/apYkEc7wZgkhOGTNT9O7oJ0
-> A'xo-grease KM'D -> n[k#S-grease /{ w79 (TV$':-8 4E
4D/ij+JrWVbUTv75EljIaE8L9JhFP3Dz hxVz/9v74X2gEt9y0yvKMClVgId3mAl5PVisyL0r8WUn4extTHoh8qj2fSFl++54
--- inyuu2A3QIBGnRj8WyQKX8+XdVDBCmANdyaHkQ0ZS7s c0aRZZ5Y+Pdqu/7FyignIhV4WbUtverMWhWdRAhGsXqFm/8ejLqPfELQ54w
”_hð”u‘㳊ÏPÖ?I'ó°Fi�ñÙÞ¶3»ÓÊý¸e‰t)ÚÃàEñ‡›à,�¯ ·÷ê«3ŸrQ”X?mo¬ÃÕ—YuÒHi90öÆ7€áé*•§ßÕZH—]z¨:XzÖ --- rTgyJNg/7gotGoTSMt5jDxSFE0tM8CP+azNlDTRjCow
oóT™3(DšØþ7¨à�ð®÷á LiÀºÝ“kþÃñ.?�N@1 �l·H΢æÐ�m°@8N�^òU•u• Ô
×ô@¯�fšû³UúÝZ„<ñη(©E‘ï–TG:¹sÇÑ‚—Æ‘“4€<â5UÞšÖª²·½‘R*_`BoŒ˜æ ¦Ïƒ/íò¬ØÇ³§FIHÍÂk+*è+;#3ÒvÖx×~¯3¢Ú‚VZ߯‡ïî¿oó£Ñ’ZC|ñßÂÕ›T*ˆÛ‡“^-ªÑ .71Ñé€cÐ÷+g¬±‘†»Å�

View file

@ -44,10 +44,6 @@ in {
services.stalwart-mail = { services.stalwart-mail = {
enable = true; enable = true;
package = pkgs.stalwart-mail.overrideAttrs (old: {
patches = old.patches ++ [./stalwart.patch];
});
settings = let settings = let
case = field: check: value: data: { case = field: check: value: data: {
"if" = field; "if" = field;
@ -64,7 +60,7 @@ in {
lib.mkForce { lib.mkForce {
authentication.fallback-admin = { authentication.fallback-admin = {
user = "admin"; user = "admin";
secret = "%{file:/run/stalwart-mail/admin-hash}%"; secret = "%{file:${config.age.secrets.stalwart-admin-hash.path}}%";
}; };
tracer.stdout = { tracer.stdout = {
@ -336,9 +332,8 @@ in {
default = true; default = true;
}; };
lookup.default.hostname = config.networking.fqdn; lookup.default.hostname = stalwartDomain;
server = { server = {
hostname = config.networking.fqdn;
tls = { tls = {
certificate = "default"; certificate = "default";
ignore-client-order = true; ignore-client-order = true;
@ -366,7 +361,8 @@ in {
# jmap, web interface # jmap, web interface
protocol = "http"; protocol = "http";
bind = "[::]:8080"; bind = "[::]:8080";
url = "https://${stalwartDomain}/jmap"; url = "https://${stalwartDomain}";
use-x-forwarded = true;
}; };
sieve = { sieve = {
protocol = "managesieve"; protocol = "managesieve";
@ -376,19 +372,6 @@ in {
}; };
}; };
#queue.outbound.next-hop = [
# (case "rcpt_domain" "in-list" "default/domains" "local")
# (otherwise false)
#];
#queue.schedule = {
# retry = ["2m" "5m" "10m" "15m" "30m" "1h" "2h"];
# notify = ["1d" "3d"];
# expire = "5d";
#};
# XXX: needed? jmap.directory = "idmail";
imap = { imap = {
request.max-size = 52428800; request.max-size = 52428800;
auth = { auth = {
@ -443,7 +426,6 @@ in {
}; };
session.rcpt = { session.rcpt = {
# XXX: needed? directory = "idmail";
catch-all = true; catch-all = true;
relay = [ relay = [
(is-authenticated true) (is-authenticated true)
@ -456,23 +438,31 @@ in {
services.nginx = { services.nginx = {
upstreams.stalwart = { upstreams.stalwart = {
servers."localhost:8080" = {}; servers."127.0.0.1:8080" = {};
extraConfig = '' extraConfig = ''
zone stalwart 64k; zone stalwart 64k;
keepalive 2; keepalive 2;
''; '';
}; };
virtualHosts.${stalwartDomain} = { virtualHosts =
forceSSL = true; {
useACMEWildcardHost = true; ${stalwartDomain} = {
extraConfig = '' forceSSL = true;
client_max_body_size 512M; useACMEWildcardHost = true;
''; extraConfig = ''
locations."/" = { client_max_body_size 512M;
proxyPass = "http://stalwart"; '';
proxyWebsockets = true; locations."/" = {
}; proxyPass = "http://stalwart";
}; proxyWebsockets = true;
};
};
}
// lib.genAttrs ["autoconfig.${primaryDomain}" "autodiscover.${primaryDomain}" "mta-sts.${primaryDomain}"] (_: {
forceSSL = true;
useACMEWildcardHost = true;
locations."/".proxyPass = "http://stalwart";
});
}; };
systemd.services.stalwart-mail = let systemd.services.stalwart-mail = let
@ -482,9 +472,6 @@ in {
in { in {
preStart = lib.mkAfter '' preStart = lib.mkAfter ''
cat ${configFile} > /run/stalwart-mail/config.toml cat ${configFile} > /run/stalwart-mail/config.toml
${pkgs.gnugrep}/bin/grep -v '^\s*$\|^\s*#' \
< ${config.age.secrets.stalwart-admin-hash.path} \
| tr -d '\n' > /run/stalwart-mail/admin-hash
''; '';
serviceConfig = { serviceConfig = {
RuntimeDirectory = "stalwart-mail"; RuntimeDirectory = "stalwart-mail";

View file

@ -1,76 +0,0 @@
diff --git a/crates/directory/src/backend/sql/lookup.rs b/crates/directory/src/backend/sql/lookup.rs
index e49c5dab..18c9005e 100644
--- a/crates/directory/src/backend/sql/lookup.rs
+++ b/crates/directory/src/backend/sql/lookup.rs
@@ -25,6 +25,7 @@ impl SqlDirectory {
QueryBy::Name(username) => {
account_name = username.to_string();
+ tracing::info!(context = "directory", event = "sql lookup", username = username, "Name -> query_name");
self.store
.query::<NamedRows>(&self.mappings.query_name, vec![username.into()])
.await?
@@ -37,6 +38,7 @@ impl SqlDirectory {
}
account_id = Some(uid);
+ tracing::info!(context = "directory", event = "sql lookup", username = account_name, "Id -> query_name");
self.store
.query::<NamedRows>(
&self.mappings.query_name,
@@ -53,6 +55,7 @@ impl SqlDirectory {
account_name = username.to_string();
secret = secret_.into();
+ tracing::info!(context = "directory", event = "sql lookup", username = username, "Credentials -> query_name");
self.store
.query::<NamedRows>(&self.mappings.query_name, vec![username.into()])
.await?
@@ -112,6 +115,7 @@ impl SqlDirectory {
// Obtain emails
if !self.mappings.query_emails.is_empty() {
+ tracing::info!(context = "directory", event = "sql lookup", principal_name = principal.name, "Query emails");
principal.emails = self
.store
.query::<Rows>(
@@ -126,6 +130,7 @@ impl SqlDirectory {
}
pub async fn email_to_ids(&self, address: &str) -> crate::Result<Vec<u32>> {
+ tracing::info!(context = "directory", event = "sql lookup", address = address, "Query recipients");
let names = self
.store
.query::<Rows>(&self.mappings.query_recipients, vec![address.into()])
@@ -143,6 +148,7 @@ impl SqlDirectory {
}
pub async fn rcpt(&self, address: &str) -> crate::Result<bool> {
+ tracing::info!(context = "directory", event = "sql lookup", address = address, "Query rcpt");
self.store
.query::<bool>(
&self.mappings.query_recipients,
@@ -153,6 +159,7 @@ impl SqlDirectory {
}
pub async fn vrfy(&self, address: &str) -> crate::Result<Vec<String>> {
+ tracing::info!(context = "directory", event = "sql lookup", address = address, "Query vrfy");
self.store
.query::<Rows>(
&self.mappings.query_verify,
@@ -164,6 +171,7 @@ impl SqlDirectory {
}
pub async fn expn(&self, address: &str) -> crate::Result<Vec<String>> {
+ tracing::info!(context = "directory", event = "sql lookup", address = address, "Query expn");
self.store
.query::<Rows>(
&self.mappings.query_expand,
@@ -175,6 +183,7 @@ impl SqlDirectory {
}
pub async fn is_local_domain(&self, domain: &str) -> crate::Result<bool> {
+ tracing::info!(context = "directory", event = "sql lookup", domain = domain, "Query is local domain");
self.store
.query::<bool>(&self.mappings.query_domains, vec![domain.into()])
.await

View file

@ -0,0 +1,8 @@
age-encryption.org/v1
-> ssh-ed25519 rz300w go7St2w4V+OeN5VgddWbw0WHuMJrZFNAE6ybU9tAbmI
sGjDHvZO56PPiIB0CvCuR1tyk2UzObeZk4IRoi+hvWA
-> wxzPB1{z-grease {^`is<&c [D`g<
Gurn/IfyXKKbuyKJNnlHfxwSOBKyqj3SlW3cxqcw6CwoL9Gc0UXeWlOgEHPzas9L
v2un
--- dmrjPRsNFeNkTPWOD1MbORG3X2AuOBp1lbFIS64kPyg
8¢² (èüyýZ\{j~ãZɨn dÖHÊò´UØÆ±þw 3?nÿ ÅnQº÷ÌóP>P8J0 ¨Ã"Œ_ÉÒÜcìHt§•üßp¨Nh�=8%ËHÌE¬uÍHÅrÓ.Q+u�ìÈp4\oÎTÆóêN[ŒnXô!ŒtÚ;z­�«ù