180 lines
5.4 KiB
Nix
180 lines
5.4 KiB
Nix
{
|
|
config,
|
|
lib,
|
|
pkgs,
|
|
...
|
|
}:
|
|
with (import ./common.nix {inherit config pkgs;}); let
|
|
cfg = config.mailsystem;
|
|
nginxCfg = config.services.nginx;
|
|
postfixCfg = config.services.postfix;
|
|
redisCfg = config.services.redis.servers.rspamd;
|
|
rspamdCfg = config.services.rspamd;
|
|
|
|
genSystemdSocketCfg = name: socketPath: additionalUser: {
|
|
description = "rspamd ${name} worker socket";
|
|
listenStreams = [socketPath];
|
|
requiredBy = ["rspamd.service"];
|
|
socketConfig = {
|
|
Service = "rspamd.service";
|
|
SocketUser = rspamdCfg.user;
|
|
SocketMode = 0600;
|
|
ExecStartPost =
|
|
lib.mkIf (additionalUser != "")
|
|
''${pkgs.acl.bin}/bin/setfacl -m "u:${additionalUser}:rw" "${socketPath}"'';
|
|
};
|
|
};
|
|
in {
|
|
options.mailsystem.rspamd.webUi = {
|
|
enable = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = false;
|
|
description = "Whether to enable the rspamd webui on `https://${config.mailsystem.fqdn}/rspamd`";
|
|
};
|
|
|
|
basicAuthFile = lib.mkOption {
|
|
type = lib.types.str;
|
|
description = "Path to basic auth file (entries can be generated using htpasswd)";
|
|
};
|
|
};
|
|
|
|
config = lib.mkIf cfg.enable {
|
|
assertions =
|
|
[
|
|
{
|
|
assertion = !cfg.rspamd.webUi.enable || cfg.rspamd.webUi.basicAuthFile != null;
|
|
message = "Setting basicAuthFile is required if rspamd's web interface is enabled";
|
|
}
|
|
]
|
|
++ lib.mapAttrsToList (
|
|
domain: dkimList: {
|
|
assertion = builtins.elem domain cfg.domains;
|
|
message = "Domain ${domain} as per `config.mailsystem.dkimSettings` needs to be managed by the mailserver.";
|
|
}
|
|
)
|
|
cfg.dkimSettings
|
|
++ lib.mapAttrsToList (
|
|
domain: dkimList: {
|
|
assertion = dkimList != [];
|
|
message = "Entry ${domain} as per `config.mailsystem.dkimSettings` must not be an empty list.";
|
|
}
|
|
)
|
|
cfg.dkimSettings;
|
|
|
|
services.rspamd = {
|
|
enable = true;
|
|
overrides = {
|
|
"classifier-bayes.conf" = {
|
|
text = ''
|
|
autolearn {
|
|
spam_threshold = 6.0 # When to learn spam (score >= threshold)
|
|
ham_threshold = -2.0 # When to learn ham (score <= threshold)
|
|
}
|
|
'';
|
|
};
|
|
"dkim_signing.conf" = let
|
|
genDkimSelectorList = entry: ''
|
|
{
|
|
path: "${entry.keyFile}";
|
|
selector: "${entry.selector}";
|
|
}
|
|
'';
|
|
genDkimDomainCfg = domain: domainSettings: ''
|
|
${domain} {
|
|
selectors [
|
|
${lib.concatStringsSep "\n" (map genDkimSelectorList domainSettings)}
|
|
]
|
|
}
|
|
'';
|
|
in {
|
|
text =
|
|
''
|
|
sign_authenticated = true;
|
|
use_esld = true;
|
|
use_domain = "header";
|
|
check_pubkey = true;
|
|
allow_username_mismatch = true;
|
|
allow_hdrfrom_mismatch = true;
|
|
allow_hdrfrom_mismatch_sign_networks = true;
|
|
|
|
''
|
|
+ lib.optionalString (cfg.dkimSettings != {}) ''
|
|
domain {
|
|
${lib.concatStringsSep "\n" (lib.mapAttrsToList genDkimDomainCfg cfg.dkimSettings)}
|
|
}
|
|
'';
|
|
};
|
|
"milter_headers.conf" = {
|
|
text = ''
|
|
# Add headers related to spam-detection
|
|
extended_spam_headers = true;
|
|
'';
|
|
};
|
|
"redis.conf" = {
|
|
text = ''
|
|
servers = "${redisCfg.unixSocket}";
|
|
'';
|
|
};
|
|
"worker-controller.inc" = lib.mkIf cfg.rspamd.webUi.enable {
|
|
text = ''
|
|
secure_ip = "0.0.0.0/0";
|
|
secure_ip = "::/0";
|
|
'';
|
|
};
|
|
};
|
|
|
|
workers = {
|
|
rspamd_proxy = {
|
|
bindSockets = ["systemd:rspamd-proxy.socket"];
|
|
count = 1; # Do not spawn too many processes of this type
|
|
extraConfig = ''
|
|
milter = yes; # Enable milter mode
|
|
timeout = 120s; # Needed for Milter usually
|
|
|
|
upstream "local" {
|
|
default = yes; # Self-scan upstreams are always default
|
|
self_scan = yes; # Enable self-scan
|
|
}
|
|
'';
|
|
};
|
|
|
|
controller = {
|
|
count = 1;
|
|
bindSockets = ["systemd:rspamd-controller.socket"];
|
|
extraConfig = ''
|
|
static_dir = "''${WWWDIR}"; # Serve the web UI static assets
|
|
'';
|
|
};
|
|
};
|
|
};
|
|
|
|
systemd.sockets = {
|
|
rspamd-proxy = genSystemdSocketCfg "proxy" rspamdProxySocket postfixCfg.user;
|
|
rspamd-controller = genSystemdSocketCfg "controller" rspamdControllerSocket (
|
|
lib.optionalString cfg.rspamd.webUi.enable nginxCfg.user
|
|
);
|
|
};
|
|
|
|
systemd.services.rspamd = {
|
|
requires = ["redis-rspamd.service"];
|
|
after = ["redis-rspamd.service"];
|
|
};
|
|
|
|
services.nginx = lib.mkIf cfg.rspamd.webUi.enable {
|
|
enable = true;
|
|
virtualHosts."${cfg.fqdn}" = {
|
|
forceSSL = true;
|
|
locations."/rspamd" = {
|
|
proxyPass = "http://unix:${rspamdControllerSocket}:/";
|
|
basicAuthFile = cfg.rspamd.webUi.basicAuthFile;
|
|
extraConfig = ''
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
'';
|
|
};
|
|
sslCertificate = lib.mkIf (cfg.certificateScheme == "selfsigned") sslCertPath;
|
|
sslCertificateKey = lib.mkIf (cfg.certificateScheme == "selfsigned") sslKeyPath;
|
|
};
|
|
};
|
|
};
|
|
}
|