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:
parent
01660a3389
commit
4e717fab96
8 changed files with 58 additions and 141 deletions
|
@ -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
|
||||||
|
|
BIN
hosts/envoy/secrets/idmail-admin-hash.age
Normal file
BIN
hosts/envoy/secrets/idmail-admin-hash.age
Normal file
Binary file not shown.
|
@ -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¬±‘†»Å�
|
|
@ -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";
|
||||||
|
|
|
@ -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
|
|
Binary file not shown.
Binary file not shown.
|
@ -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ɨndÖ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�«ù
|
Loading…
Add table
Add a link
Reference in a new issue