My Nix Configuration

[systems.prefect] enable stalwart-mail

pyrox.dev ab166eb3 92053af4

verified
+1
lib/data/default.nix
···
{
data.hosts = builtins.fromTOML (builtins.readFile ./hosts.toml);
data.services = builtins.fromTOML (builtins.readFile ./services.toml);
data.tsNet = "coelacanth-dragon.ts.net";
}
···
{
data.hosts = builtins.fromTOML (builtins.readFile ./hosts.toml);
data.services = builtins.fromTOML (builtins.readFile ./services.toml);
+
data.mail = builtins.fromTOML (builtins.readFile ./mail.toml);
data.tsNet = "coelacanth-dragon.ts.net";
}
+10
lib/data/mail.toml
···
···
+
extUrl = "mail.pyrox.dev"
+
extIPs = ["5.161.140.5", "2a01:4ff:f0:98bf:0:0:0:1"]
+
# internal port is 40k+real mail port
+
intSMTP = 40587
+
intSMTPS = 40465
+
intIMAP = 40143
+
intIMAPS = 40993
+
intManageSieve = 44190
+
intHTTPS = 40443
+
intHTTP = 40080
+1
systems/x86_64-linux/prefect/default.nix
···
./services/fail2ban.nix
# ./services/headscale.nix
./services/mailserver
# ./services/netdata.nix
./services/nginx
./services/prometheus.nix
···
./services/fail2ban.nix
# ./services/headscale.nix
./services/mailserver
+
./services/mailserver/stalwart
# ./services/netdata.nix
./services/nginx
./services/prometheus.nix
+2
systems/x86_64-linux/prefect/firewall.nix
···
636
993
4130
6900
8000
];
allowedUDPPorts = [
636
4367
6900
···
636
993
4130
+
4190
6900
8000
];
allowedUDPPorts = [
+
80
636
4367
6900
+10
systems/x86_64-linux/prefect/secrets/secrets.nix
···
yubi-main
yubi-back
];
}
···
yubi-main
yubi-back
];
+
"stalwart-secret-rsa.age".publicKeys = [
+
prefect
+
yubi-main
+
yubi-back
+
];
+
"stalwart-secret-ed25519.age".publicKeys = [
+
prefect
+
yubi-main
+
yubi-back
+
];
}
+19
systems/x86_64-linux/prefect/secrets/stalwart-secret-ed25519.age
···
···
+
age-encryption.org/v1
+
-> ssh-ed25519 LcWOqQ iqNMs1zctbP+bDdN4c3RGhckhCvrTPJbcTHUQnu88ko
+
x5T5SxnBpMRgAHbYY1YU0Yy7XVlGRZ2WnM7ICDFmjbE
+
-> ssh-ed25519 ihSg8g g0enzjgGoNx7kHNuq7lSys3G3MeQeusWuRZ0oIrP0UE
+
V6mXoy2LFKubv4f1Fwa0Wx15D7OI3FWeBovhDcuGaMQ
+
-> ssh-rsa fFaiTA
+
b9TNMiCUmYELGyGF5YzGR6KOA1w6dUteVArMlg1ZPn1HW2W/wrhhAEmTN/hAnEPR
+
9AFTDJupQ/6aacyIUnQlkXjI7QZeVbKXJ39VHva1rTXCamAKAAjVBdzcLtEbl4Z5
+
iZuv6bEDcggZ1/4PmvmMtH5ljkGZWmbRSXwSqn0xF2P9VY4QMVK86WWpobk+St1X
+
EQqEDiGrPtgwHMCOfL1wb7lZD6t2wwMlRfnTee/5qDWTSDme6QYPsAFmJOpQGCYT
+
BOQOlZegH/KCl7qai8qBOKwEWTVMbyblihCp4vaVTgpkOiHO6PM1HS4JrXMZJeDr
+
30m8oeGT7/PoO14tWZvU2jCWGO/M6tiiif5I+cOymFnGCxmHPpuO/+GPOQ+f4bKb
+
zw5hh6Bqpj9bp6QoTLfzzKnrpSk/n26e89nejha0iLDwP+QQyZZos9vss3B6Fkmu
+
bZUuifW7eSuI7Y7l4RkuvCOEIJf+vkW7bF0e1BDpd6n5B9Jp/c5ghvWzgoyCyDud
+
1Xf0UxiFM90AUR8ml3QtclaRewfMr/b69qgR2olwBVrctjC/CSXAJC3bih6LZLdA
+
nKHWyqWjWfsxCPWZUmFHMcu1ogFVsnlEdr+UoYCN1oXMwZfgxQnkfLFfcLZFW3+a
+
JeXgm10wb2+DpmltSi7fLgQOB5462VQ+SvnFFwWFRN8
+
--- xB0nATcSCPeSRGpbNMeSuUCJ3rxQhruXmqczOcTvMj0
+
�EՊ�[�7]COII�IU [1�:?�wfq��;m�+ %9�������d}ՈQ!y �����IVz;`�᭹�33L ;�Z� �����J�kҕW<W,f2������"V�������G<�q'0*���~!�7��&��-��i� e�e�'�^
systems/x86_64-linux/prefect/secrets/stalwart-secret-rsa.age

This is a binary file and will not be displayed.

+101 -50
systems/x86_64-linux/prefect/services/caddy.nix
···
{ pkgs, lib, ... }:
let
pns = lib.py.data.services;
-
# mail = lib.py.data.mail;
marvin = "http://${lib.py.data.hosts.marvin.ts.ip4}";
marvinIP = lib.py.data.hosts.marvin.ts.ip4;
tsNet = lib.py.data.tsNet;
···
handle /metrics* {
respond @blocked "Access Denied" 403
}
-
'';
-
};
-
-
# MTA-STS Setup for mailserver
-
"mta-sts.pyrox.dev" = {
-
extraConfig = ''
-
header Content-Type text/plain; charset=utf-8
-
respond /.well-known/mta-sts.txt <<END
-
version: STSv1
-
mode: enforce
-
mx: mail.pyrox.dev
-
mx:mail2.pyrox.dev
-
max_age: 2419200
-
END 200
'';
};
···
reverse_proxy ${marvin}:${toString pns.pinchflat.port}
'';
};
-
# "mail.pyrox.dev:80" = {
-
# extraConfig = ''
-
# reverse_proxy ${marvin}:${mail.intHTTP}
-
# '';
-
# };
};
# Mail Config
globalConfig = ''
···
ref refs/heads/pages
refresh_period 10m
}
layer4 {
:22 {
@a ssh
route @a {
-
proxy ${marvinIP}:2222
}
}
}
'';
-
# TODO: Move the below section to global options once stalwart is working
-
# extraConfig = ''
-
# layer4 {
-
# 0.0.0.0:465 {
-
# route {
-
# proxy {
-
# proxy_protocol v2
-
# upstream ${marvinIP}:mail.intSMTPS}
-
# }
-
# }
-
# }
-
# 0.0.0.0:993 {
-
# route {
-
# proxy {
-
# proxy_protocol v2
-
# upstream ${marvinIP}:mail.intIMAPS}
-
# }
-
# }
-
# }
-
# 0.0.0.0:4190 {
-
# route {
-
# proxy {
-
# proxy_protocol v2
-
# upstream ${marvinIP}:mail.intManageSieve}
-
# }
-
# }
-
# }
-
# }
-
# '';
};
systemd.services.caddy.serviceConfig.CapabilityBoundingSet = "CAP_NET_BIND_SERVICE";
systemd.services.caddy.serviceConfig.AmbientCapabilities = "CAP_NET_BIND_SERVICE";
···
{ pkgs, lib, ... }:
let
pns = lib.py.data.services;
+
mail = lib.py.data.mail;
marvin = "http://${lib.py.data.hosts.marvin.ts.ip4}";
marvinIP = lib.py.data.hosts.marvin.ts.ip4;
tsNet = lib.py.data.tsNet;
···
handle /metrics* {
respond @blocked "Access Denied" 403
}
'';
};
···
reverse_proxy ${marvin}:${toString pns.pinchflat.port}
'';
};
+
+
"http://mail.pyrox.dev" = {
+
serverAliases = [
+
"http://mta-sts.pyrox.dev"
+
"http://autodiscover.pyrox.dev"
+
"http://autoconfig.pyrox.dev"
+
];
+
extraConfig = ''
+
reverse_proxy 127.0.0.1:${toString mail.intHTTP} {
+
transport http {
+
proxy_protocol v2
+
}
+
}
+
+
'';
+
};
};
# Mail Config
globalConfig = ''
···
ref refs/heads/pages
refresh_period 10m
}
+
servers :80 {
+
listener_wrappers {
+
layer4 {
+
@maildomains http host mail.pyrox.dev mta-sts.pyrox.dev autoconfig.pyrox.dev autodiscover.pyrox.dev
+
route @maildomains {
+
subroute {
+
@a http
+
route @a {
+
proxy {
+
proxy_protocol v2
+
upstream 127.0.0.1:${toString mail.intHTTP}
+
}
+
}
+
}
+
}
+
}
+
http_redirect
+
}
+
}
+
servers :443 {
+
listener_wrappers {
+
layer4 {
+
@maildomains tls sni mail.pyrox.dev mta-sts.pyrox.dev autoconfig.pyrox.dev autodiscover.pyrox.dev
+
route @maildomains {
+
proxy {
+
proxy_protocol v2
+
upstream 127.0.0.1:${toString mail.intHTTPS}
+
}
+
}
+
}
+
tls
+
}
+
}
layer4 {
:22 {
@a ssh
route @a {
+
proxy {
+
upstream ${marvinIP}:2222
+
}
+
}
+
}
+
:2025 {
+
route {
+
proxy {
+
proxy_protocol v2
+
upstream 127.0.0.1:40025
+
}
+
}
+
}
+
:2143 {
+
route {
+
proxy {
+
proxy_protocol v2
+
upstream 127.0.0.1:${toString mail.intIMAP}
+
}
+
}
+
}
+
:2465 {
+
route {
+
proxy {
+
proxy_protocol v2
+
upstream 127.0.0.1:${toString mail.intSMTPS}
+
}
+
}
+
}
+
:2587 {
+
route {
+
proxy {
+
proxy_protocol v2
+
upstream 127.0.0.1:${toString mail.intSMTP}
+
}
+
}
+
}
+
:2993 {
+
route {
+
proxy {
+
proxy_protocol v2
+
upstream 127.0.0.1:${toString mail.intIMAPS}
+
}
+
}
+
}
+
:24190 {
+
route {
+
proxy {
+
proxy_protocol v2
+
upstream 127.0.0.1:${toString mail.intManageSieve}
+
}
}
}
}
'';
};
systemd.services.caddy.serviceConfig.CapabilityBoundingSet = "CAP_NET_BIND_SERVICE";
systemd.services.caddy.serviceConfig.AmbientCapabilities = "CAP_NET_BIND_SERVICE";
+17
systems/x86_64-linux/prefect/services/mailserver/stalwart/acme.nix
···
···
+
{ cfg }:
+
{
+
letsencrypt = {
+
directory = "https://acme-staging-v02.api.letsencrypt.org/directory";
+
challenge = "http-01";
+
contact = [ "pyrox@pyrox.dev" ];
+
domains = [
+
"mail.pyrox.dev"
+
"mta-sts.pyrox.dev"
+
"autoconfig.pyrox.dev"
+
"autodiscover.pyrox.dev"
+
];
+
cache = "${cfg.dataDir}/acme/certs";
+
renew-before = "30d";
+
default = true;
+
};
+
}
+22
systems/x86_64-linux/prefect/services/mailserver/stalwart/auth.nix
···
···
+
{ ifThen, otherwise }:
+
let
+
relVer = [
+
(ifThen "listener = 'smtp'" "relaxed")
+
(otherwise "disable")
+
];
+
in
+
{
+
dkim = {
+
sign = [
+
(ifThen "sender_domain = 'pyrox.dev'" "'rsa'")
+
(ifThen "sender_domain = 'pyrox.dev'" "'ed25519'")
+
(otherwise false)
+
];
+
};
+
spf.verify.ehlo = relVer;
+
spf.verify.mail-from = relVer;
+
dmarc.verify = relVer;
+
iprev.verify = relVer;
+
arc.seal = "'ed25519'";
+
arc.verify = "relaxed";
+
}
+25
systems/x86_64-linux/prefect/services/mailserver/stalwart/auto-ban.nix
···
···
+
# Strict Auto-ban
+
# https://stalw.art/docs/server/auto-ban
+
{
+
auth.rate = "15/1d";
+
abuse.rate = "15/1d";
+
loiter.rate = "15/1d";
+
scan = {
+
rate = "20/1d";
+
paths = [
+
"*.php*"
+
"*.cgi*"
+
"*.asp*"
+
"*/wp-*"
+
"*/php*"
+
"*/cgi-bin*"
+
"*xmlrpc*"
+
"*../*"
+
"*/..*"
+
"*joomla*"
+
"*wordpress*"
+
"*drupal*"
+
"/.git*"
+
];
+
};
+
}
+25
systems/x86_64-linux/prefect/services/mailserver/stalwart/calendar.nix
···
···
+
# Calendar settings
+
# https://stalw.art/docs/collaboration/calendar
+
{
+
max-recurrence-expansions = 2048;
+
# 512 KiB
+
max-size = 524288;
+
max-attendees-per-instance = 20;
+
default.href-name = "default";
+
default.display-name = "Personal";
+
# Scheduling
+
# https://stalw.art/docs/collaboration/scheduling
+
scheduling.enable = true;
+
# 1 MiB
+
scheduling.inbound.max-size = 1048576;
+
scheduling.outbound.max-recipients = 100;
+
scheduling.inbox.auto-expunge = "30d";
+
scheduling.http-rsvp.enable = true;
+
scheduling.http-rsvp.expiration = "7d";
+
# Notifications
+
# https://stalw.art/docs/collaboration/notifications
+
alarms.enable = true;
+
alarms.minimum-interval = "1h";
+
alarms.from.name = "PyroNet Calendars";
+
alarms.from.email = "calendar-notifs@pyrox.dev";
+
}
+181
systems/x86_64-linux/prefect/services/mailserver/stalwart/default.nix
···
···
+
{
+
config,
+
lib,
+
...
+
}:
+
let
+
d = lib.py.data.mail;
+
cfg = config.services.stalwart-mail;
+
sec = config.age.secrets;
+
creds = config.services.stalwart-mail.credentials;
+
isAuthenticated = d: {
+
"if" = "!is_empty(authenticated_as)";
+
"then" = d;
+
};
+
otherwise = d: {
+
"else" = d;
+
};
+
ifThen = f: d: {
+
"if" = f;
+
"then" = d;
+
};
+
smSecret = {
+
owner = "stalwart-mail";
+
group = "stalwart-mail";
+
};
+
in
+
{
+
services.stalwart-mail = {
+
enable = true;
+
dataDir = "/var/lib/stalwart";
+
credentials = {
+
rsa_private_key = sec.stalwart-secret-rsa.path;
+
ed25519_private_key = sec.stalwart-secret-ed25519.path;
+
};
+
settings = {
+
tracer.stdout.level = "debug";
+
config = {
+
local-keys = [
+
"acme.*"
+
"asn.*"
+
"auth.*"
+
"authentication.*"
+
"auto-ban.*"
+
"calendar.*"
+
"certificate.*"
+
"changes.*"
+
"cluster.*"
+
"config.*"
+
"contacts.*"
+
"directory.*"
+
"http.*"
+
"imap.*"
+
"jmap.*"
+
"queue.*"
+
"report.*"
+
"resolver.*"
+
"server.*"
+
"session.*"
+
"storage.*"
+
"store.*"
+
"tracer.*"
+
"webadmin.*"
+
"form.*"
+
"email.*"
+
"spam-filter.*"
+
];
+
};
+
server = import ./server.nix { inherit d; };
+
# ACME for certs, using TLS-ALPN-01 Challenges(one fewer ports open)
+
# https://stalw.art/docs/server/tls/acme/configuration
+
acme = import ./acme.nix { inherit cfg; };
+
# HTTP Configuration
+
# https://stalw.art/docs/http/overview
+
http = {
+
url = "'https://${d.extUrl}'";
+
hsts = true;
+
};
+
# Disable HTTP Forms submission
+
# https://stalw.art/docs/http/form-submission
+
form.enable = false;
+
# Storage Settings
+
# https://stalw.art/docs/storage/overview
+
store = {
+
data = {
+
type = "rocksdb";
+
path = "${cfg.dataDir}/db";
+
purge.frequency = "0 3 *";
+
};
+
blob = {
+
type = "fs";
+
path = "${cfg.dataDir}/blobs";
+
depth = 2;
+
compression = "lz4";
+
purge.frequency = "0 4 *";
+
};
+
db.path = "${cfg.dataDir}/db2";
+
};
+
storage = {
+
data = "data";
+
blob = "blob";
+
fts = "data";
+
lookup = "data";
+
directory = "default";
+
};
+
directory = {
+
default = {
+
type = "internal";
+
store = "data";
+
};
+
};
+
# ASN/GeoIP Lookups
+
# https://stalw.art/docs/server/asn
+
asn = {
+
type = "dns";
+
separator = "|";
+
zone.ipv4 = "origin.asn.cymru.com";
+
zone.ipv6 = "origin6.asn.cymru.com";
+
index.asn = 0;
+
index.asn-name = 1;
+
index.country = 2;
+
};
+
auto-ban = import ./auto-ban.nix;
+
# JMAP Settings
+
# https://stalw.art/docs/email/jmap
+
jmap = {
+
mailbox.max-depth = 10;
+
mailbox.max-name-length = 255;
+
# 50 MB
+
email.max-attachment-size = 50 * 1000 * 1000;
+
# 75 MB
+
email.max-size = 75 * 1000 * 1000;
+
email.parse.max-items = 10;
+
};
+
imap = import ./imap.nix;
+
# Maintainance
+
# https://stalw.art/docs/email/maintenance
+
email.auto-expunge = "180d";
+
changes.max-history = 10000;
+
session = import ./session.nix { inherit isAuthenticated otherwise; };
+
queue = import ./queue.nix { inherit d ifThen otherwise; };
+
# DNS Settings
+
# https://stalw.art/docs/mta/outbound/dns
+
resolver = {
+
custom = [
+
"tls://dns11.quad9.net"
+
"tcp://1.1.1.1"
+
];
+
concurrency = 2;
+
preserve-intermediates = true;
+
timeout = "5s";
+
attempts = 3;
+
edns = true;
+
};
+
report = import ./report.nix { inherit d; };
+
calendar = import ./calendar.nix;
+
# Authentication
+
auth = import ./auth.nix { inherit ifThen otherwise; };
+
# Contacts
+
# https://stalw.art/docs/collaboration/contact
+
contacts = {
+
# 512 KiB
+
max-size = 524288;
+
default.href-name = "default";
+
default.display-name = "Contacts";
+
};
+
# Spam Filtering
+
# https://stalw.art/docs/spamfilter/overview
+
spam-filter = {
+
card-is-ham = true;
+
};
+
};
+
};
+
age.secrets = {
+
stalwart-secret-rsa = smSecret // {
+
file = ../../../secrets/stalwart-secret-rsa.age;
+
};
+
stalwart-secret-ed25519 = smSecret // {
+
file = ../../../secrets/stalwart-secret-ed25519.age;
+
};
+
};
+
}
+42
systems/x86_64-linux/prefect/services/mailserver/stalwart/imap.nix
···
···
+
# https://stalw.art/docs/email/imap
+
{
+
# 50 MiB
+
request.max-size = 52428800;
+
auth.max-failures = 3;
+
auth.allow-plain-text = false;
+
folders =
+
let
+
folder = {
+
create = true;
+
subscribe = true;
+
};
+
in
+
{
+
inbox = folder // {
+
name = "Inbox";
+
};
+
drafts = folder // {
+
name = "Drafts";
+
};
+
sent = folder // {
+
name = "Sent";
+
};
+
trash = folder // {
+
name = "Trash";
+
};
+
archive = folder // {
+
name = "Archive";
+
};
+
junk = folder // {
+
name = "Junk";
+
};
+
shared = {
+
name = "Shared Folders";
+
create = true;
+
subscribe = false;
+
};
+
};
+
timeout.authenticated = "30m";
+
timeout.anonymous = "1m";
+
timeout.idle = "30m";
+
}
+97
systems/x86_64-linux/prefect/services/mailserver/stalwart/queue.nix
···
···
+
{
+
d,
+
ifThen,
+
otherwise,
+
}:
+
# Queue Management
+
# https://stalw.art/docs/mta/outbound/overview
+
{
+
# Virtual Queues
+
# https://stalw.art/docs/mta/outbound/queue
+
virtual.default.threads-per-node = 100;
+
virtual.admin.threads-per-node = 10;
+
virtual.local.threads-per-node = 100;
+
# Schedules
+
# https://stalw.art/docs/mta/outbound/schedule
+
schedule =
+
let
+
queue = {
+
retry = [
+
"1m"
+
"2m"
+
"5m"
+
"10m"
+
"15m"
+
"30m"
+
"1h"
+
"2h"
+
];
+
notify = [
+
"1d"
+
"3d"
+
];
+
max-attempts = 15;
+
};
+
in
+
{
+
default = queue // {
+
queue-name = "default";
+
};
+
admin = queue // {
+
queue-name = "admin";
+
};
+
local = queue // {
+
queue-name = "local";
+
};
+
};
+
# Routes
+
# https://stalw.art/docs/mta/outbound/routing
+
route = {
+
local.type = "local";
+
remote = {
+
type = "mx";
+
ip-lookup = "ipv6_then_ipv4";
+
tls.implicit = false;
+
tls.allow-invalid-certs = false;
+
};
+
};
+
# Strategies
+
# https://stalw.art/docs/mta/outbound/strategy
+
strategy = {
+
schedule = [
+
(ifThen "is_local_domain('', rcpt_domain)" "'local'")
+
(ifThen "source = 'dsn'" "'admin'")
+
(ifThen "source = 'report'" "'admin'")
+
(ifThen "source = 'autogenerated'" "'admin'")
+
(otherwise "'default'")
+
];
+
route = [
+
(ifThen "is_local_domain('', rcpt_domain)" "'local'")
+
(otherwise "'remote'")
+
];
+
connection = "'default'";
+
tls = "'default'";
+
};
+
# Remote Connection
+
# https://stalw.art/docs/mta/outbound/connection
+
connection.default = {
+
ehlo-hostname = d.extUrl;
+
source-ips = d.extIPs;
+
timeout = {
+
connect = "3m";
+
greeting = "3m";
+
ehlo = "3m";
+
mail-from = "3m";
+
rcpt-to = "3m";
+
data = "10m";
+
};
+
};
+
tls.default = {
+
dane = "optional";
+
mta-sts = "optional";
+
starttls = "optional";
+
allow-invalid-certs = false;
+
timeout.tls = "3m";
+
timeout.mta-sts = "3m";
+
};
+
}
+64
systems/x86_64-linux/prefect/services/mailserver/stalwart/report.nix
···
···
+
{ d }:
+
# Reports
+
# https://stalw.art/docs/mta/reports/overview
+
{
+
domain = "pyrox.dev";
+
submitter = "'${d.extUrl}'";
+
analysis = {
+
addresses = [
+
"dmarc@"
+
"reports@"
+
"spf@"
+
"dkim@"
+
"abuse@"
+
];
+
forward = true;
+
store = "30d";
+
};
+
dsn = {
+
from-name = "'PyroNet Mail'";
+
from-address = "'mail@pyrox.dev'";
+
sign = "['rsa', 'ed25519']";
+
};
+
dkim = {
+
from-name = "'PyroNet Mail Reports'";
+
from-address = "'noreply-dkim@pyrox.dev'";
+
subject = "'DKIM Authentication Failure Report'";
+
sign = "['rsa', 'ed25519']";
+
send = "1/1d";
+
};
+
spf = {
+
from-name = "'PyroNet Mail Reports'";
+
from-address = "'noreply-spf@pyrox.dev'";
+
subject = "'SPF Authentication Failure Report'";
+
sign = "['rsa', 'ed25519']";
+
send = "1/1d";
+
};
+
dmarc = {
+
from-name = "'PyroNet Mail Reports'";
+
from-address = "'noreply-dmarc@pyrox.dev'";
+
subject = "'DMARC Authentication Failure Report'";
+
sign = "['rsa', 'ed25519']";
+
send = "1/1d";
+
aggregate = {
+
from-name = "'DMARC Report'";
+
from-address = "'noreply-dmarc@pyrox.dev'";
+
org-name = "'PyroNet Mail'";
+
contact-info = "'pyrox@pyrox.dev'";
+
send = "daily";
+
# 25 MiB
+
max-size = 26214400;
+
sign = "['rsa', 'ed25519']";
+
};
+
};
+
tls.aggregate = {
+
from-name = "'PyroNet Mail Reports'";
+
from-address = "'noreply-tls@pyrox.dev'";
+
org-name = "'PyroNet Mail'";
+
contact-info = "'pyrox@pyrox.dev'";
+
send = "daily";
+
# 25 MiB
+
max-size = 26214400;
+
sign = "['rsa', 'ed25519']";
+
};
+
}
+68
systems/x86_64-linux/prefect/services/mailserver/stalwart/server.nix
···
···
+
{ d }:
+
{
+
hostname = d.extUrl;
+
# TLS
+
# https://stalw.art/docs/server/tls/overview
+
tls = {
+
enable = true;
+
implicit = false;
+
ignore-client-order = true;
+
};
+
# Listeners
+
# https://stalw.art/docs/server/listener
+
listener = {
+
smtp = {
+
bind = [
+
"[::]:${toString d.intSMTP}"
+
"[::]:40025"
+
];
+
protocol = "smtp";
+
# Explicit TLS
+
tls.implicit = false;
+
};
+
smtps = {
+
bind = "[::]:${toString d.intSMTPS}";
+
protocol = "smtp";
+
# Implicit TLS
+
tls.implicit = true;
+
};
+
imap = {
+
bind = "[::]:${toString d.intIMAP}";
+
protocol = "imap";
+
# Explicit TLS
+
tls.implicit = false;
+
};
+
imaps = {
+
bind = "[::]:${toString d.intIMAPS}";
+
protocol = "imap";
+
# Implicit TLS
+
tls.implicit = true;
+
};
+
managesieve = {
+
bind = "[::]:${toString d.intManageSieve}";
+
protocol = "managesieve";
+
# Explicit TLS
+
tls.implicit = false;
+
};
+
https = {
+
bind = "[::]:${toString d.intHTTPS}";
+
protocol = "http";
+
# Implicit TLS
+
tls.implicit = true;
+
};
+
http = {
+
bind = "[::]:${toString d.intHTTP}";
+
protocol = "http";
+
# Implicit TLS
+
tls.implicit = false;
+
};
+
};
+
# Proxy Protocol from Caddy
+
# Only accepts proxy protocol from Tailscale IP Ranges
+
# https://tailscale.com/kb/1015/100.x-addresses
+
# https://tailscale.com/kb/1033/ip-and-dns-addresses
+
proxy.trusted-networks = [
+
"fd7a:115c:a1e0::/48"
+
"100.64.0.0/10"
+
];
+
}
+42
systems/x86_64-linux/prefect/services/mailserver/stalwart/session.nix
···
···
+
{ isAuthenticated, otherwise }:
+
# MTA Settings
+
# https://stalw.art/docs/mta/overview
+
{
+
# Inbound
+
# https://stalw.art/docs/mta/inbound/overview
+
# # EHLO Stage
+
# # https://stalw.art/docs/mta/inbound/ehlo
+
ehlo = {
+
require = true;
+
};
+
# # RCPT Stage
+
# # https://stalw.art/docs/mta/inbound/rcpt
+
rcpt = {
+
relay = [
+
(isAuthenticated true)
+
(otherwise false)
+
];
+
subaddressing = true;
+
};
+
extensions =
+
let
+
ifAuthed = [
+
(isAuthenticated true)
+
(otherwise false)
+
];
+
in
+
{
+
pipelining = true;
+
chunking = true;
+
requiretls = true;
+
no-soliciting = "";
+
dsn = ifAuthed;
+
deliver-by = [
+
(isAuthenticated "15d")
+
(otherwise false)
+
];
+
mt-priority = false;
+
vrfy = ifAuthed;
+
expn = ifAuthed;
+
};
+
}
+41
systems/x86_64-linux/prefect/services/mailserver/stalwart/signature.nix
···
···
+
let
+
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"
+
];
+
in
+
{
+
rsa = {
+
inherit headers;
+
private-key = "%{file:/run/credentials/stalwart-mail.service/rsa_private_key}";
+
domain = "pyrox.dev";
+
selector = "rsa-default";
+
algorithm = "rsa-sha256";
+
canonicalization = "relaxed/relaxed";
+
expire = "10d";
+
report = true;
+
};
+
ed25519 = {
+
inherit headers;
+
private-key = "%{file:/run/credentials/stalwart-mail.service/ed25519_private_key}";
+
domain = "pyrox.dev";
+
selector = "default";
+
algorithm = "ed25519-sha256";
+
canonicalization = "relaxed/relaxed";
+
expire = "10d";
+
report = true;
+
};
+
}