feat: stalwart mail-server
This commit is contained in:
parent
85feeb7b3f
commit
a565033341
16 changed files with 1192 additions and 442 deletions
|
|
@ -59,6 +59,33 @@ in
|
|||
];
|
||||
})
|
||||
|
||||
(import ../../modules/stalwart.nix {
|
||||
enableNginx = true;
|
||||
domain = "pre7780.dn";
|
||||
adminPassFile = config.sops.secrets."stalwart/adminPassword".path;
|
||||
dbPassFile = config.sops.secrets."stalwart/db".path;
|
||||
acmeConf = {
|
||||
directory = "https://ca.net.dn/acme/acme/directory";
|
||||
ca_bundle = "${"" + ../../extra/ca.crt}";
|
||||
challenge = "dns-01";
|
||||
origin = "pre7780.dn";
|
||||
contact = "admin@pre7780.dn";
|
||||
domains = [
|
||||
"pre7780.dn"
|
||||
"mx1.pre7780.dn"
|
||||
];
|
||||
default = true;
|
||||
provider = "rfc2136-tsig";
|
||||
host = "10.0.0.1";
|
||||
renew-before = "1d";
|
||||
port = 5359;
|
||||
cache = "${config.services.stalwart-mail.dataDir}/acme";
|
||||
key = "stalwart";
|
||||
tsig-algorithm = "hmac-sha512";
|
||||
secret = "%{file:${config.sops.secrets."stalwart/tsig".path}}%";
|
||||
};
|
||||
})
|
||||
|
||||
../../modules/davinci-resolve.nix
|
||||
../../modules/webcam.nix
|
||||
../../modules/postgresql.nix
|
||||
|
|
|
|||
|
|
@ -1,13 +1,20 @@
|
|||
{ ... }:
|
||||
{ config, ... }:
|
||||
{
|
||||
networking.firewall.allowedTCPPorts = [
|
||||
443
|
||||
80
|
||||
];
|
||||
|
||||
security.acme = {
|
||||
acceptTerms = true;
|
||||
defaults = {
|
||||
validMinDays = 2;
|
||||
server = "https://10.0.0.1:${toString 8443}/acme/acme/directory";
|
||||
server = "https://ca.net.dn/acme/acme/directory";
|
||||
renewInterval = "daily";
|
||||
email = "danny@net.dn";
|
||||
webroot = "/var/lib/acme/acme-challenge";
|
||||
dnsProvider = "pdns";
|
||||
dnsPropagationCheck = false;
|
||||
environmentFile = config.sops.secrets."acme/pdns".path;
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,12 @@ openldap:
|
|||
adminPassword: ENC[AES256_GCM,data:jEGuzgs5QTWfdyJenC3t3g==,iv:StfFOcvbDapnma6eAlpaGiBWnqiD3I/wfQsMBzufol0=,tag:892q7N4KrsSQoZYGy6CQrA==,type:str]
|
||||
lam:
|
||||
env: ENC[AES256_GCM,data:f1LlC/VvilH8o2Ra7MrSHsMEGlGw3LOV2O9JJf9f,iv:u7cXM8n3jJeLBfxXtA0QMyijBqTcC+yJeW/OO9JuZMI=,tag:QL5FkcCPI5Gxudi0NmCZWg==,type:str]
|
||||
stalwart:
|
||||
adminPassword: ENC[AES256_GCM,data:6tUL7b2s3gLtF4Ors9CgYQ==,iv:9UQowgXKr9HR/poELP6SZijp3c2HVTHzEfwf1tZI/3w=,tag:KIOiYEwLsZLH31E2Xb478A==,type:str]
|
||||
tsig: ENC[AES256_GCM,data:wxsM/dbkW2fNf86b6TsLRNAce19h7mBEuSzFT84aIlaVZA/S29g1U4/CAwD4b+h/XfBgpZQCJf/9yT3yo6dbGAIAk5UgjV2cNY9pO1/uF1T6xoKDgfRZxA==,iv:9BvP8vQkTTEaNgYUPfQcfEMcWqDyD045EPBr7NyHmO4=,tag:coBBAe62kpe/L0S6V8NhXg==,type:str]
|
||||
db: ENC[AES256_GCM,data:ZRZ2ZzUotYMe2GfkMS7o7dz0aGg=,iv:ys6ogueueESp0y6A+hUG9zTnqmCVobuIzyqA4WVtewo=,tag:p74G+8XhMcpgDnIfh1aXTg==,type:str]
|
||||
acme:
|
||||
pdns: ENC[AES256_GCM,data:+InGSnaGIFVtDRlVltzWbZfquzodHUQrPeMRBnVNB9mrajlKr5dFK6DD8dXAvN7UjZFBfrgZefOPkmLR2ncLXGOV2Kl7jorVw50Y0f0iKl7mqwHaZKaQdk7cpGDkCrt/LvfbP9x7gVrs6pQpsU+c/P5rbBLRyejchh/WtiyzgowYIJxYohggeG09+l7YI3FR6U5wiymIRISpNBGEhwG0q17qdAhdtc49qP/K,iv:JcSlxAwHwU528S7iSpAnSbUZw7iO+LMjR3qGwRHp+Zk=,tag:twf2WOQb/yZ3GtN/hlikMA==,type:str]
|
||||
sops:
|
||||
age:
|
||||
- recipient: age1uvsvf5ljaezh5wze32p685kfentyle0l2mvysc67yvgct2h4850qqph9lv
|
||||
|
|
@ -19,7 +25,7 @@ sops:
|
|||
MEdmWkFwNXZoR1ZVRnQ0aWlkYzZwSmsK0EFecUIdqlDKX08oRCoDQQ3QCX1wzb8w
|
||||
lghDJhWlfuKr+X24GoE4UK04aJVLqVMRRI4BJW+LQXeHS+dWKu3mQA==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
lastmodified: "2025-06-15T08:18:50Z"
|
||||
mac: ENC[AES256_GCM,data:sq+/fpOeNO5wn9S1kFqzRy6xCOVkSBcAkral7MTn4UxRebBDa78KF76Nsba0+o5bzwCchoGl/TC6vySIzGq8FUYwd1tQ9nH5DlqYBVVRgRlKLRyhxXf14BTyYgzHzFuRWdFyY8i4j0flZtlDHk4dVQrE4OhHvhLQ2Zvet5HQ20I=,iv:qoPZ+8tAHJxcR53M2PNwukYgdguSRrAVB+FtKYbf+aM=,tag:FYaPzh6o0ZI27Ul5jEhgVg==,type:str]
|
||||
lastmodified: "2025-09-16T04:39:12Z"
|
||||
mac: ENC[AES256_GCM,data:yRVAJz73AqlBm6fxeTehfSqlTLyRYIsPjC/5igpnGC8URUiK66SUtHJSE3196AaPV+CWJrxrXfNWoCmZsP85Rr5V9nw31ZF1boaAc0YzRQBxVmBBlAK7+9Z5KADShAetYNwk9qtCrXd6S8mCwmZjNJaN/Rthy3hchxzAB0/79R4=,iv:QeNUZfmnCx4QF/0wjU/JJRu6umNFC/weW2BJx+7OaPo=,tag:KsityLnPYhugFL4c6wrs6Q==,type:str]
|
||||
unencrypted_suffix: _unencrypted
|
||||
version: 3.10.2
|
||||
|
|
|
|||
|
|
@ -15,6 +15,36 @@
|
|||
group = config.services.dovecot2.group;
|
||||
mode = "0660";
|
||||
};
|
||||
|
||||
"stalwart/adminPassword" =
|
||||
let
|
||||
inherit (config.users.users.stalwart-mail) name group;
|
||||
in
|
||||
lib.mkIf config.services.stalwart-mail.enable {
|
||||
inherit group;
|
||||
owner = name;
|
||||
};
|
||||
"stalwart/tsig" =
|
||||
let
|
||||
inherit (config.users.users.stalwart-mail) name group;
|
||||
in
|
||||
lib.mkIf config.services.stalwart-mail.enable {
|
||||
inherit group;
|
||||
owner = name;
|
||||
};
|
||||
"stalwart/db" =
|
||||
let
|
||||
inherit (config.users.users.stalwart-mail) name group;
|
||||
in
|
||||
lib.mkIf config.services.stalwart-mail.enable {
|
||||
inherit group;
|
||||
owner = name;
|
||||
};
|
||||
"acme/pdns" = {
|
||||
mode = "0660";
|
||||
owner = "acme";
|
||||
group = "acme";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,6 +64,14 @@
|
|||
|
||||
locations."/".proxyPass = "http://10.0.0.130:8001/phone.html";
|
||||
};
|
||||
|
||||
"ca.net.dn" = {
|
||||
enableACME = true;
|
||||
forceSSL = true;
|
||||
locations."/" = {
|
||||
proxyPass = "https://10.0.0.1:8443/";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,11 +33,20 @@ sops:
|
|||
- recipient: age1z6f643a6vqm7cqh6fna5dhmxfkgwxgqy8kg9s0vf9uxhaswtngtspmqsjw
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBuKzJObXlPVUJzUkEyZXlV
|
||||
Q0tEbzBPTy9kUXIwVmJkckUyWklUMzhCcTE0Ckh3bXIwRkpESTJYeTBPMGhQYk9y
|
||||
L2NQTWFuMWVqYzJHZGhTaHpDRE5CRGMKLS0tIEsybHdPMk9JeEM2cXFwdlpOeXRj
|
||||
Qm0wbmNGZDZwZlNTOVl0WVh5RXNxK2cK1Fwbgl5kKAFyrIIhBP+X4ZKFS4Xl39QY
|
||||
11qkglNgro/JBFJ/W7Hj5wtEd8QToiJM1RW0lQaI25sneQ2v6L5pDA==
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBuYWpiZ0h3VURrcW0rV0Vj
|
||||
SFJwMlRUMHUyS1FGTEo3cHZJc1Z6a3FWbmtRCkdoZXhwOGJQNlV2dU8wRFRMUHVv
|
||||
QzhxU3RiVHl5UVpUNk10S2VRVy95OHMKLS0tIE9zbUNUU3ZINU1JNGtmd2trS2tI
|
||||
d3YxREtHcTBJYU1sNU9vMGZTUGh6NXMKtGKMnnamCAeftkQ0+Ygb/yg1NdyKDz1W
|
||||
UjYvW2PYKzkx8IWmIgzdAI3fWDOiE7tmBTMlX9C3/2PKR6dCc/a+SQ==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1uvsvf5ljaezh5wze32p685kfentyle0l2mvysc67yvgct2h4850qqph9lv
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBxc3pna3R3aG85bmt2WERa
|
||||
aG9TaDBKTlNMTUVwaFlIdkV0UmFJQStYSHdvCmNuYWJpN2M3QjRkV2s0MHJ4TzZP
|
||||
ZkhKc0xPUFBrblVFR1U4SUdjYzQ2cm8KLS0tIDVuNW9tRGoxanVKOUJYa2QwNFNz
|
||||
OTRiU0cxeXp5K1FjaWRGTnBHcnpUYmcKVVlueEj/DELe9Xi9iaBddpPPRmoUmD48
|
||||
wyjtlvKzS20zishE/D7GkHZ2ZdNsLD3AOnYZ6r6ATAndssC2YT/SXA==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
lastmodified: "2025-08-29T07:40:00Z"
|
||||
mac: ENC[AES256_GCM,data:QeQ5NOrcq3uNmt+MiVF+Jr3JWWBNGPw5A8pSdd1WR426WWqHTRP7NHAaVbS3st9VSmoYY5NI6JKeizuAq/NCvzOZL3Idy9mP+3HD9VZwn1GNSEGfhn+KZT02AY0JHq29KxcZlAYiWZOL4p+blG2aWfGm9+zy1GHoEXoo3OVhaEg=,iv:8uIoOE0ZJZYGZoQaskCXQKr7vl6wjsmJ4iudhvtgqtY=,tag:fRLGalP92dDyF8q+zT97BQ==,type:str]
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
username,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
inherit username;
|
||||
|
||||
|
|
@ -13,10 +12,10 @@ let
|
|||
sshPortsString = builtins.concatStringsSep ", " (builtins.map (p: builtins.toString p) sshPorts);
|
||||
|
||||
personal = {
|
||||
inherit (config.networking) domain;
|
||||
ip = "10.0.0.1/24";
|
||||
interface = "wg0";
|
||||
port = 51820;
|
||||
domain = config.networking.domain;
|
||||
range = "10.0.0.0/24";
|
||||
full = "10.0.0.1/25";
|
||||
restrict = "10.0.0.128/25";
|
||||
|
|
@ -160,11 +159,13 @@ in
|
|||
kube.port
|
||||
25565
|
||||
kube.masterAPIServerPort
|
||||
5359
|
||||
];
|
||||
allowedTCPPorts = sshPorts ++ [
|
||||
53
|
||||
25565
|
||||
kube.masterAPIServerPort
|
||||
5359
|
||||
];
|
||||
};
|
||||
|
||||
|
|
@ -237,8 +238,7 @@ in
|
|||
listenPort = personal.port;
|
||||
privateKeyFile = config.sops.secrets."wireguard/privateKey".path;
|
||||
peers = builtins.map (r: {
|
||||
publicKey = r.publicKey;
|
||||
allowedIPs = r.allowedIPs;
|
||||
inherit (r) publicKey allowedIPs;
|
||||
}) (fullRoute ++ meshRoute);
|
||||
};
|
||||
|
||||
|
|
@ -254,31 +254,31 @@ in
|
|||
extraHosts = "${kube.masterIP} ${kube.masterHostname}";
|
||||
};
|
||||
|
||||
services.postgresql = {
|
||||
enable = lib.mkDefault true;
|
||||
authentication = ''
|
||||
host powerdnsadmin powerdnsadmin 127.0.0.1/32 trust
|
||||
'';
|
||||
ensureUsers = [
|
||||
{
|
||||
name = "powerdnsadmin";
|
||||
ensureDBOwnership = true;
|
||||
}
|
||||
{
|
||||
name = "pdns";
|
||||
ensureDBOwnership = true;
|
||||
}
|
||||
];
|
||||
ensureDatabases = [
|
||||
"powerdnsadmin"
|
||||
"pdns"
|
||||
];
|
||||
};
|
||||
|
||||
services = {
|
||||
dbus.enable = true;
|
||||
blueman.enable = true;
|
||||
|
||||
postgresql = {
|
||||
enable = lib.mkDefault true;
|
||||
authentication = ''
|
||||
host powerdnsadmin powerdnsadmin 127.0.0.1/32 trust
|
||||
'';
|
||||
ensureUsers = [
|
||||
{
|
||||
name = "powerdnsadmin";
|
||||
ensureDBOwnership = true;
|
||||
}
|
||||
{
|
||||
name = "pdns";
|
||||
ensureDBOwnership = true;
|
||||
}
|
||||
];
|
||||
ensureDatabases = [
|
||||
"powerdnsadmin"
|
||||
"pdns"
|
||||
];
|
||||
};
|
||||
|
||||
openssh = {
|
||||
enable = true;
|
||||
ports = sshPorts;
|
||||
|
|
@ -293,6 +293,7 @@ in
|
|||
enable = true;
|
||||
extraConfig = ''
|
||||
launch=gpgsql
|
||||
loglevel=6
|
||||
webserver-password=$WEB_PASSWORD
|
||||
api=yes
|
||||
api-key=$WEB_PASSWORD
|
||||
|
|
@ -302,6 +303,8 @@ in
|
|||
webserver=yes
|
||||
webserver-port=8081
|
||||
local-port=5359
|
||||
dnsupdate=yes
|
||||
allow-dnsupdate-from=10.0.0.0/24
|
||||
'';
|
||||
secretFile = config.sops.secrets.powerdns.path;
|
||||
};
|
||||
|
|
@ -310,6 +313,7 @@ in
|
|||
enable = true;
|
||||
forwardZones = {
|
||||
"${config.networking.domain}." = "127.0.0.1:5359";
|
||||
"pre7780.dn." = "127.0.0.1:5359";
|
||||
};
|
||||
forwardZonesRecurse = {
|
||||
"." = "8.8.8.8";
|
||||
|
|
@ -380,11 +384,16 @@ in
|
|||
};
|
||||
};
|
||||
|
||||
systemd.services.raspamd-trainer = {
|
||||
after = [ "pdns-recursor.service" ];
|
||||
};
|
||||
|
||||
services.nginx.virtualHosts = {
|
||||
"powerdns.${config.networking.domain}" = {
|
||||
enableACME = true;
|
||||
forceSSL = true;
|
||||
locations."/".proxyPass = "http://localhost:8000";
|
||||
locations."/api".proxyPass = "http://127.0.0.1:8081";
|
||||
locations."/".proxyPass = "http://127.0.0.1:8000";
|
||||
};
|
||||
|
||||
"uptime.${config.networking.domain}" = {
|
||||
|
|
|
|||
|
|
@ -41,7 +41,6 @@ in
|
|||
postRun = ''
|
||||
systemctl restart postfix.service
|
||||
systemctl restart dovecot.service
|
||||
systemctl restart rspamd-trainer.service
|
||||
'';
|
||||
};
|
||||
"${cfg.domain}" = {
|
||||
|
|
|
|||
118
system/modules/stalwart.nix
Normal file
118
system/modules/stalwart.nix
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
{
|
||||
adminPassFile,
|
||||
dbPassFile,
|
||||
domain ? null,
|
||||
acmeConf ? null,
|
||||
enableNginx ? true,
|
||||
}:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (lib) mkIf;
|
||||
in
|
||||
{
|
||||
services.postgresql = {
|
||||
enable = true;
|
||||
ensureDatabases = [
|
||||
"stalwart"
|
||||
];
|
||||
ensureUsers = [
|
||||
{
|
||||
name = "stalwart";
|
||||
ensureDBOwnership = true;
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
services.stalwart-mail = {
|
||||
enable = true;
|
||||
openFirewall = true;
|
||||
settings = {
|
||||
server = {
|
||||
hostname = if (domain != null) then "mx1.${domain}" else config.networking.fqdn;
|
||||
auto-ban.scan.rate = "1000/1d";
|
||||
tls = {
|
||||
enable = true;
|
||||
implicit = true;
|
||||
};
|
||||
listener = {
|
||||
smtp = {
|
||||
protocol = "smtp";
|
||||
bind = "[::]:25";
|
||||
};
|
||||
submissions = {
|
||||
protocol = "smtp";
|
||||
bind = "[::]:465";
|
||||
tls.implicit = true;
|
||||
};
|
||||
imaps = {
|
||||
protocol = "imap";
|
||||
bind = "[::]:993";
|
||||
tls.implicit = true;
|
||||
};
|
||||
management = {
|
||||
protocol = "http";
|
||||
bind = [ "127.0.0.1:8080" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
lookup.default = {
|
||||
hostname = "mx1.${domain}";
|
||||
domain = "${domain}";
|
||||
};
|
||||
acme."step-ca" = mkIf (acmeConf != null) acmeConf;
|
||||
session.auth = {
|
||||
mechanisms = "[plain]";
|
||||
directory = "'in-memory'";
|
||||
require = true;
|
||||
allow-plain-text = true;
|
||||
};
|
||||
storage.data = "db";
|
||||
store."db" = {
|
||||
type = "postgresql";
|
||||
host = "localhost";
|
||||
port = 5432;
|
||||
database = "stalwart";
|
||||
user = "stalwart";
|
||||
password = "%{file:${dbPassFile}}%";
|
||||
};
|
||||
directory = {
|
||||
"imap".lookup.domains = [ domain ];
|
||||
"in-memory" = {
|
||||
type = "memory";
|
||||
principals = [
|
||||
{
|
||||
name = "admin";
|
||||
class = "admin";
|
||||
secret = "%{file:${adminPassFile}}%";
|
||||
email = [ "admin@${domain}" ];
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
authentication.fallback-admin = {
|
||||
user = "admin";
|
||||
secret = "%{file:${adminPassFile}}%";
|
||||
};
|
||||
tracer."stdout" = {
|
||||
enable = true;
|
||||
type = "console";
|
||||
level = "debug";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
services.nginx = mkIf enableNginx {
|
||||
enable = true;
|
||||
virtualHosts = {
|
||||
"mail.${domain}" = {
|
||||
locations."/".proxyPass = "http://127.0.0.1:8080";
|
||||
enableACME = true;
|
||||
forceSSL = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue