From d35763a8a2cc6ebf000cae2063986b7751626149 Mon Sep 17 00:00:00 2001 From: Thomas Preisner Date: Thu, 5 Dec 2024 14:10:04 +0100 Subject: [PATCH] mailsystem: Configure rspamd as spam filter --- mailsystem/common.nix | 3 ++ mailsystem/default.nix | 3 ++ mailsystem/kresd.nix | 11 ++++++ mailsystem/postfix.nix | 14 ++++++- mailsystem/redis.nix | 27 +++++++++++++ mailsystem/rspamd.nix | 88 ++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 144 insertions(+), 2 deletions(-) create mode 100644 mailsystem/kresd.nix create mode 100644 mailsystem/redis.nix create mode 100644 mailsystem/rspamd.nix diff --git a/mailsystem/common.nix b/mailsystem/common.nix index 2dd170c..f74539a 100644 --- a/mailsystem/common.nix +++ b/mailsystem/common.nix @@ -7,4 +7,7 @@ in rec { dovecotDynamicStateDir = "/var/lib/dovecot"; dovecotDynamicPasswdFile = "${dovecotDynamicStateDir}/passwd"; + + rspamdProxySocket = "/run/rspamd-proxy.sock"; + rspamdControllerSocket = "/run/rspamd-controller.sock"; } diff --git a/mailsystem/default.nix b/mailsystem/default.nix index dbc0e29..ae970a1 100644 --- a/mailsystem/default.nix +++ b/mailsystem/default.nix @@ -158,8 +158,11 @@ in { imports = [ ./dovecot.nix + ./kresd.nix ./nginx.nix ./postfix.nix + ./redis.nix + ./rspamd.nix ./user.nix ]; } diff --git a/mailsystem/kresd.nix b/mailsystem/kresd.nix new file mode 100644 index 0000000..448b644 --- /dev/null +++ b/mailsystem/kresd.nix @@ -0,0 +1,11 @@ +{ + config, + lib, + ... +}: let + cfg = config.mailsystem; +in { + config = lib.mkIf cfg.enable { + services.kresd.enable = true; + }; +} diff --git a/mailsystem/postfix.nix b/mailsystem/postfix.nix index ec42a1c..f0afb8d 100644 --- a/mailsystem/postfix.nix +++ b/mailsystem/postfix.nix @@ -154,6 +154,16 @@ in { # Configure a non-blocking source of randomness tls_random_source = "dev:/dev/urandom"; + smtpd_milters = [ + "unix:${rspamdProxySocket}" + ]; + # Also use milter for outgoing mails (for e.g., dkim) + non_smtpd_milters = [ + "unix:${rspamdProxySocket}" + ]; + milter_protocol = "6"; + milter_mail_macros = "i {mail_addr} {client_addr} {client_name} {auth_type} {auth_authen} {auth_author} {mail_addr} {mail_host} {mail_mailer}"; + # Fix for https://www.postfix.org/smtp-smuggling.html smtpd_forbid_bare_newline = "yes"; smtpd_forbid_bare_newline_exclusions = "$mynetworks"; @@ -179,9 +189,9 @@ in { systemd.services.postfix = { wants = sslCertService; after = - ["dovecot2.service"] + ["dovecot2.service" "rspamd.service"] ++ sslCertService; - requires = ["dovecot2.service"]; + requires = ["dovecot2.service" "rspamd.service"]; }; networking.firewall = lib.mkIf cfg.openFirewall { diff --git a/mailsystem/redis.nix b/mailsystem/redis.nix new file mode 100644 index 0000000..658093d --- /dev/null +++ b/mailsystem/redis.nix @@ -0,0 +1,27 @@ +{ + config, + lib, + pkgs, + ... +}: let + cfg = config.mailsystem; + redisCfg = config.services.redis.servers.rspamd; + rspamdCfg = config.services.rspamd; +in { + config = lib.mkIf cfg.enable { + services.redis.servers.rspamd = { + enable = true; + # Don't accept connections via tcp + port = 0; + unixSocketPerm = 600; + }; + + # TODO: Run commands as service user instead of as root? + systemd.services.redis-rspamd.serviceConfig.ExecStartPost = + "+" + + pkgs.writeShellScript "redis-rspamd-postStart" '' + ${pkgs.acl.bin}/bin/setfacl -m "u:${rspamdCfg.user}:x" "${builtins.dirOf redisCfg.unixSocket}" + ${pkgs.acl.bin}/bin/setfacl -m "u:${rspamdCfg.user}:rw" "${redisCfg.unixSocket}" + ''; + }; +} diff --git a/mailsystem/rspamd.nix b/mailsystem/rspamd.nix new file mode 100644 index 0000000..7211f2b --- /dev/null +++ b/mailsystem/rspamd.nix @@ -0,0 +1,88 @@ +{ + config, + lib, + pkgs, + ... +}: +with (import ./common.nix {inherit config;}); 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 { + config = lib.mkIf cfg.enable { + 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) + } + ''; + }; + "milter_headers.conf" = { + text = '' + # Add headers related to spam-detection + extended_spam_headers = true; + ''; + }; + "redis.conf" = { + text = '' + servers = "${redisCfg.unixSocket}"; + ''; + }; + }; + + 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 ""; + }; + + systemd.services.rspamd = { + requires = ["redis-rspamd.service"]; + after = ["redis-rspamd.service"]; + }; + }; +}