Compare commits

...

3 commits

Author SHA1 Message Date
9af8f29b48
Revert "Attempt at using stalwart again"
Some checks failed
/ dev-shell (push) Successful in 41s
/ rust-packages (push) Successful in 47s
/ check (push) Failing after 58s
/ terraform-providers (push) Successful in 1m12s
/ systems (push) Successful in 4m0s
This reverts commit 2d3e02ad78.
2025-07-30 11:11:17 +02:00
ad84cfae7e
Revert "Final attempt at getting stalwart working before revert"
This reverts commit fbcd590bfe.
2025-07-30 11:11:14 +02:00
fbcd590bfe
Final attempt at getting stalwart working before revert
Non working parts:

1. OIDC login, stalwart assumes the entire token is base64 encoded,
   which it is not.
2. Apparently there's no support for mixed directories, allowing both
   logins from ldap and from internal database. I want this in order
   to support accounts for services as well as persons.
2025-07-30 11:08:00 +02:00
11 changed files with 162 additions and 649 deletions

View file

@ -1,40 +1,9 @@
{ lib, config, ... }:
let
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
{
config = lib.mkIf cfg.enable {
khscodes.services.nginx.virtualHosts."${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
];
khscodes.services.nginx.virtualHosts."${config.khscodes.networking.fqdn}" = { };
};
}

View file

@ -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) }
}
'';
};
}
];
};
}

View file

@ -1,7 +1,7 @@
{
config,
lib,
pkgs,
inputs,
...
}:
let
@ -17,8 +17,7 @@ in
};
};
imports = [
# inputs.simple-nixos-mailserver.nixosModules.mailserver
./admin_password.nix
inputs.simple-nixos-mailserver.nixosModules.mailserver
./acme.nix
./dmarc.nix
./dane.nix
@ -29,58 +28,8 @@ in
./prometheus.nix
./openid-connect.nix
./ldap.nix
./package/nixos-module.nix
];
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
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 = [
25
465

View file

@ -9,82 +9,28 @@ let
publicKeyEnd = ''"-----END PUBLIC KEY-----\n"'';
rsaKeyPath = domain: "/run/secret/dkim/${domain}.snm_rsa.key";
ed25519KeyPath = domain: "/run/secret/dkim/${domain}.snm_ed25519.key";
keyFiles = lib.lists.flatten (
domainKeyPaths = lib.lists.flatten (
lib.lists.map (domain: [
(rsaKeyPath domain)
(ed25519KeyPath domain)
]) cfg.domains
);
ifthen = condition: expr: {
"if" = condition;
"then" = expr;
};
otherwise = expr: { "else" = expr; };
authDkimForDomain = domain: [
(ifthen "sender_domain = '${domain}'" "['${domain}_rsa', '${domain}_ed25519']")
];
authDkim = lib.lists.flatten (lib.lists.map authDkimForDomain cfg.domains);
signatureForDomain = domain: [
# Currently (2025-07-25) I canot get rspamd to sign with ed25519 key,
# it appears it attempts to parse it as an RSA key
# {
# path: "${ed25519KeyPath domain}";
# selector: "snm_ed25519";
# }
dkimSigningForDomain = domain: ''
${domain} {
selectors [
{
name = "${domain}_rsa";
value = {
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;
};
path: "${rsaKeyPath domain}";
selector: "snm_rsa";
}
{
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 =
tls_key:
''''${ replace(trimprefix(trimsuffix(${tls_key}.public_key_pem, ${publicKeyEnd}), ${publicKeyBegin}), "\n", "") }'';
@ -190,10 +136,10 @@ in
'';
destination = rsaKeyPath domain;
perms = "0600";
owner = "stalwart-mail";
group = "stalwart-mail";
owner = "rspamd";
group = "rspamd";
restartUnits = [
"stalwart-mail.service"
"rspamd.service"
];
}
{
@ -204,25 +150,47 @@ in
'';
destination = ed25519KeyPath domain;
perms = "0600";
owner = "stalwart-mail";
group = "stalwart-mail";
owner = "rspamd";
group = "rspamd";
restartUnits = [
"stalwart-mail.service"
"rspamd.service"
];
}
]) cfg.domains
);
services.stalwart-mail.settings = {
auth.dkim = {
sign = authDkim ++ [
(otherwise false)
];
mailserver = {
dkimSigning = 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;
serviceConfig.ReadOnlyPaths = keyFiles;
'';
};
services.rspamd.locals."arc.conf" = lib.mkForce {
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;
};
};
};
}

View file

@ -16,43 +16,44 @@ in
};
config = lib.mkIf cfg.enable {
services.stalwart-mail.settings = {
storage = {
directory = "ldap";
};
directory.ldap = {
type = "ldap";
url = "ldaps://login.kaareskovgaard.net";
base-dn = "dc=login,dc=kaareskovgaard,dc=net";
mailserver.ldap = {
enable = true;
uris = [ "ldaps://login.kaareskovgaard.net" ];
searchBase = "dc=login,dc=kaareskovgaard,dc=net";
searchScope = "sub";
bind = {
dn = "dn=token";
secret = "%{env:STALWART_LDAP_SECRET}%";
auth = {
method = "lookup";
passwordFile = secretFile;
};
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 = {
name = "(&(class=account)(memberOf=mail_user)(uid=?))";
email = "(&(class=account)(memberOf=mail_user)(mail=?))";
};
attributes = {
name = "name";
class = "class";
description = "name";
groups = "memberOf";
email = "mail;primary";
email-alias = "mail;alternative";
quota = "diskQuota";
};
postfix = {
filter = "(&(class=account)(memberOf=mail_user)(mail=%s))";
mailAttribute = "uid";
uidAttribute = "uid";
};
};
systemd.services = {
stalwart-mail = {
dovecot2 = {
unitConfig = {
ConditionPathExists = [ secretFile ];
};
serviceConfig = {
EnvironmentFile = secretFile;
};
postfix = {
unitConfig = {
ConditionPathExists = [ secretFile ];
};
};
postfix-setup = {
unitConfig = {
ConditionPathExists = [ secretFile ];
};
};
};
@ -61,14 +62,15 @@ in
{
contents = ''
{{- with secret "${cfg.ldap.mount}/data/${cfg.ldap.path}" -}}
STALWART_LDAP_SECRET={{ .Data.data.apiToken }}
{{ .Data.data.apiToken }}
{{- end -}}
'';
destination = secretFile;
owner = "stalwart-mail";
group = "stalwart-mail";
owner = "dovecot";
group = "dovecot";
restartUnits = [
"stalwart-mail.service"
"dovecot2.service"
"postfix.service"
];
}
];

View file

@ -5,41 +5,41 @@ let
in
{
config = lib.mkIf cfg.enable {
# khscodes.services.vault-agent.templates = [
# {
# contents = ''
# {{- with secret "kanidm/data/apps/dovecot" -}}
# scope = email openid profile
# username_attribute = username
# debug = yes
# introspection_url = https://dovecot:{{ .Data.data.basic_secret }}@login.kaareskovgaard.net/oauth2/token/introspect
# introspection_mode = post
# {{- end -}}
# '';
# destination = oauthConfigFile;
# perms = "0600";
# owner = "root";
# group = "root";
# restartUnits = [ "dovecot2.service" ];
# }
# ];
# services.dovecot2.extraConfig = ''
# auth_mechanisms = $auth_mechanisms oauthbearer xoauth2
khscodes.services.vault-agent.templates = [
{
contents = ''
{{- with secret "kanidm/data/apps/dovecot" -}}
scope = email openid profile
username_attribute = username
debug = yes
introspection_url = https://dovecot:{{ .Data.data.basic_secret }}@login.kaareskovgaard.net/oauth2/token/introspect
introspection_mode = post
{{- end -}}
'';
destination = oauthConfigFile;
perms = "0600";
owner = "root";
group = "root";
restartUnits = [ "dovecot2.service" ];
}
];
services.dovecot2.extraConfig = ''
auth_mechanisms = $auth_mechanisms oauthbearer xoauth2
# passdb {
# driver = oauth2
# mechanisms = xoauth2 oauthbearer
# args = ${oauthConfigFile}
# }
# '';
# systemd.services.dovecot2 = {
# serviceConfig.ReadOnlyPaths = [
# oauthConfigFile
# ];
# unitConfig.ConditionPathExists = [
# oauthConfigFile
# ];
# };
passdb {
driver = oauth2
mechanisms = xoauth2 oauthbearer
args = ${oauthConfigFile}
}
'';
systemd.services.dovecot2 = {
serviceConfig.ReadOnlyPaths = [
oauthConfigFile
];
unitConfig.ConditionPathExists = [
oauthConfigFile
];
};
};
}

View file

@ -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}"
];
};
};
}

View file

@ -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
];
};
})

View file

@ -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;
};
})

View file

@ -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;
};
})

View file

@ -1,16 +1,13 @@
{ config, lib, ... }:
let
fqdn = config.khscodes.networking.fqdn;
cfg = config.khscodes.infrastructure.mailserver;
in
{
config = lib.mkIf cfg.enable {
services.stalwart-mail.settings.metrics.prometheus = {
services.prometheus.exporters.postfix = {
enable = true;
};
# Don't expose the endpoint
khscodes.services.nginx.virtualHosts."${fqdn}".locations."=/metrics/prometheus" = {
return = 404;
};
khscodes.infrastructure.vault-prometheus-sender.exporters.enabled = [ "postfix" ];
};
}