diff --git a/nixos/modules/services/mail/rspamd.nix b/nixos/modules/services/mail/rspamd.nix
index ff01a5dee53d..d83d6f1f750c 100644
--- a/nixos/modules/services/mail/rspamd.nix
+++ b/nixos/modules/services/mail/rspamd.nix
@@ -127,11 +127,15 @@ let
options {
pidfile = "$RUNDIR/rspamd.pid";
.include "$CONFDIR/options.inc"
+ .include(try=true; priority=1,duplicate=merge) "$LOCAL_CONFDIR/local.d/options.inc"
+ .include(try=true; priority=10) "$LOCAL_CONFDIR/override.d/options.inc"
}
logging {
type = "syslog";
.include "$CONFDIR/logging.inc"
+ .include(try=true; priority=1,duplicate=merge) "$LOCAL_CONFDIR/local.d/logging.inc"
+ .include(try=true; priority=10) "$LOCAL_CONFDIR/override.d/logging.inc"
}
${concatStringsSep "\n" (mapAttrsToList (name: value: ''
@@ -149,6 +153,41 @@ let
${cfg.extraConfig}
'';
+ rspamdDir = pkgs.linkFarm "etc-rspamd-dir" (
+ (mapAttrsToList (name: file: { name = "local.d/${name}"; path = file.source; }) cfg.locals) ++
+ (mapAttrsToList (name: file: { name = "override.d/${name}"; path = file.source; }) cfg.overrides) ++
+ (optional (cfg.localLuaRules != null) { name = "rspamd.local.lua"; path = cfg.localLuaRules; }) ++
+ [ { name = "rspamd.conf"; path = rspamdConfFile; } ]
+ );
+
+ configFileModule = prefix: { name, config, ... }: {
+ options = {
+ enable = mkOption {
+ type = types.bool;
+ default = true;
+ description = ''
+ Whether this file ${prefix} should be generated. This
+ option allows specific ${prefix} files to be disabled.
+ '';
+ };
+
+ text = mkOption {
+ default = null;
+ type = types.nullOr types.lines;
+ description = "Text of the file.";
+ };
+
+ source = mkOption {
+ type = types.path;
+ description = "Path of the source file.";
+ };
+ };
+ config = {
+ source = mkIf (config.text != null) (
+ let name' = "rspamd-${prefix}-" + baseNameOf name;
+ in mkDefault (pkgs.writeText name' config.text));
+ };
+ };
in
{
@@ -167,6 +206,41 @@ in
description = "Whether to run the rspamd daemon in debug mode.";
};
+ locals = mkOption {
+ type = with types; loaOf (submodule (configFileModule "locals"));
+ default = {};
+ description = ''
+ Local configuration files, written into /etc/rspamd/local.d/{name}.
+ '';
+ example = literalExample ''
+ { "redis.conf".source = "/nix/store/.../etc/dir/redis.conf";
+ "arc.conf".text = "allow_envfrom_empty = true;";
+ }
+ '';
+ };
+
+ overrides = mkOption {
+ type = with types; loaOf (submodule (configFileModule "overrides"));
+ default = {};
+ description = ''
+ Overridden configuration files, written into /etc/rspamd/override.d/{name}.
+ '';
+ example = literalExample ''
+ { "redis.conf".source = "/nix/store/.../etc/dir/redis.conf";
+ "arc.conf".text = "allow_envfrom_empty = true;";
+ }
+ '';
+ };
+
+ localLuaRules = mkOption {
+ default = null;
+ type = types.nullOr types.path;
+ description = ''
+ Path of file to link to /etc/rspamd/rspamd.local.lua for local
+ rules written in Lua
+ '';
+ };
+
workers = mkOption {
type = with types; attrsOf (submodule workerOpts);
description = ''
@@ -242,16 +316,17 @@ in
gid = config.ids.gids.rspamd;
};
- environment.etc."rspamd.conf".source = rspamdConfFile;
+ environment.etc."rspamd".source = rspamdDir;
systemd.services.rspamd = {
description = "Rspamd Service";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
+ restartTriggers = [ rspamdDir ];
serviceConfig = {
- ExecStart = "${pkgs.rspamd}/bin/rspamd ${optionalString cfg.debug "-d"} --user=${cfg.user} --group=${cfg.group} --pid=/run/rspamd.pid -c ${rspamdConfFile} -f";
+ ExecStart = "${pkgs.rspamd}/bin/rspamd ${optionalString cfg.debug "-d"} --user=${cfg.user} --group=${cfg.group} --pid=/run/rspamd.pid -c /etc/rspamd/rspamd.conf -f";
Restart = "always";
RuntimeDirectory = "rspamd";
PrivateTmp = true;
diff --git a/nixos/tests/rspamd.nix b/nixos/tests/rspamd.nix
index a12622b6aa0b..af765f37b91b 100644
--- a/nixos/tests/rspamd.nix
+++ b/nixos/tests/rspamd.nix
@@ -27,7 +27,7 @@ let
$machine->succeed("id \"rspamd\" >/dev/null");
${checkSocket "/run/rspamd/rspamd.sock" "rspamd" "rspamd" "660" }
sleep 10;
- $machine->log($machine->succeed("cat /etc/rspamd.conf"));
+ $machine->log($machine->succeed("cat /etc/rspamd/rspamd.conf"));
$machine->log($machine->succeed("systemctl cat rspamd.service"));
$machine->log($machine->succeed("curl http://localhost:11334/auth"));
$machine->log($machine->succeed("curl http://127.0.0.1:11334/auth"));
@@ -55,7 +55,7 @@ in
$machine->waitForFile("/run/rspamd.sock");
${checkSocket "/run/rspamd.sock" "root" "root" "600" }
${checkSocket "/run/rspamd-worker.sock" "root" "root" "666" }
- $machine->log($machine->succeed("cat /etc/rspamd.conf"));
+ $machine->log($machine->succeed("cat /etc/rspamd/rspamd.conf"));
$machine->log($machine->succeed("rspamc -h /run/rspamd-worker.sock stat"));
$machine->log($machine->succeed("curl --unix-socket /run/rspamd-worker.sock http://localhost/ping"));
'';
@@ -86,9 +86,80 @@ in
$machine->waitForFile("/run/rspamd.sock");
${checkSocket "/run/rspamd.sock" "root" "root" "600" }
${checkSocket "/run/rspamd-worker.sock" "root" "root" "666" }
- $machine->log($machine->succeed("cat /etc/rspamd.conf"));
+ $machine->log($machine->succeed("cat /etc/rspamd/rspamd.conf"));
$machine->log($machine->succeed("rspamc -h /run/rspamd-worker.sock stat"));
$machine->log($machine->succeed("curl --unix-socket /run/rspamd-worker.sock http://localhost/ping"));
'';
};
+ customLuaRules = makeTest {
+ name = "rspamd-custom-lua-rules";
+ machine = {
+ environment.etc."tests/no-muh.eml".text = ''
+ From: Sheep1
+ To: Sheep2
+ Subject: Evil cows
+
+ I find cows to be evil don't you?
+ '';
+ environment.etc."tests/muh.eml".text = ''
+ From: Cow
+ To: Sheep2
+ Subject: Evil cows
+
+ Cows are majestic creatures don't Muh agree?
+ '';
+ services.rspamd = {
+ enable = true;
+ locals."groups.conf".text = ''
+ group "cows" {
+ symbol {
+ NO_MUH = {
+ weight = 1.0;
+ description = "Mails should not muh";
+ }
+ }
+ }
+ '';
+ localLuaRules = pkgs.writeText "rspamd.local.lua" ''
+ local rspamd_logger = require "rspamd_logger"
+ rspamd_config.NO_MUH = {
+ callback = function (task)
+ local parts = task:get_text_parts()
+ if parts then
+ for _,part in ipairs(parts) do
+ local content = tostring(part:get_content())
+ rspamd_logger.infox(rspamd_config, 'Found content %s', content)
+ local found = string.find(content, "Muh");
+ rspamd_logger.infox(rspamd_config, 'Found muh %s', tostring(found))
+ if found then
+ return true
+ end
+ end
+ end
+ return false
+ end,
+ score = 5.0,
+ description = 'Allow no cows',
+ group = "cows",
+ }
+ rspamd_logger.infox(rspamd_config, 'Work dammit!!!')
+ '';
+ };
+ };
+ testScript = ''
+ ${initMachine}
+ $machine->waitForOpenPort(11334);
+ $machine->log($machine->succeed("cat /etc/rspamd/rspamd.conf"));
+ $machine->log($machine->succeed("cat /etc/rspamd/rspamd.local.lua"));
+ $machine->log($machine->succeed("cat /etc/rspamd/local.d/groups.conf"));
+ ${checkSocket "/run/rspamd/rspamd.sock" "rspamd" "rspamd" "660" }
+ $machine->log($machine->succeed("curl --unix-socket /run/rspamd/rspamd.sock http://localhost/ping"));
+ $machine->log($machine->succeed("rspamc -h 127.0.0.1:11334 stat"));
+ $machine->log($machine->succeed("cat /etc/tests/no-muh.eml | rspamc -h 127.0.0.1:11334"));
+ $machine->log($machine->succeed("cat /etc/tests/muh.eml | rspamc -h 127.0.0.1:11334 symbols"));
+ $machine->waitUntilSucceeds("journalctl -u rspamd | grep -i muh >&2");
+ $machine->log($machine->fail("cat /etc/tests/no-muh.eml | rspamc -h 127.0.0.1:11334 symbols | grep NO_MUH"));
+ $machine->log($machine->succeed("cat /etc/tests/muh.eml | rspamc -h 127.0.0.1:11334 symbols | grep NO_MUH"));
+ '';
+ };
}