From 1db74d1150827d09b9620457af673b2d9b6c2b07 Mon Sep 17 00:00:00 2001 From: Philipp Kern Date: Sun, 15 Nov 2020 11:02:28 +0100 Subject: [PATCH 1/6] nixos/spamassassin: Fix network requirement on boot sa-update currently runs as part of the pre-start script of spamd. The network is not guaranteed to be online at that point and even if we were to depend on that, it makes the bootup brittle, as there is a reliance on SpamAssassin's update server as a startup dependency on boot. Refactor the setup to move the pre-start script into its own unit. This allows to perform the setup task only once. Continuous updates are already done by sa-update.service triggered by sa-update.timer. Only run sa-update in case /var/lib/spamassassin is empty. While we are on it, let sa-update.service depend on the network being online. --- nixos/modules/services/mail/spamassassin.nix | 51 ++++++++++++-------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/nixos/modules/services/mail/spamassassin.nix b/nixos/modules/services/mail/spamassassin.nix index 4e642542ec66..0bbf2df48d40 100644 --- a/nixos/modules/services/mail/spamassassin.nix +++ b/nixos/modules/services/mail/spamassassin.nix @@ -126,6 +126,8 @@ in }; systemd.services.sa-update = { + wants = [ "network-online.target" ]; + after = [ "network-online.target" ]; script = '' set +e ${pkgs.su}/bin/su -s "${pkgs.bash}/bin/bash" -c "${pkgs.spamassassin}/bin/sa-update --gpghomedir=/var/lib/spamassassin/sa-update-keys/" spamd @@ -152,33 +154,44 @@ in }; }; + systemd.services.spamd-init = { + serviceConfig = { + Type = "oneshot"; + }; + script = '' + mkdir -p /var/lib/spamassassin + chown spamd:spamd /var/lib/spamassassin -R + if [ "$(ls -A /var/lib/spamassassin)" = "" ]; then + echo "'/var/lib/spamassassin' is empty, running sa-update..." + set +e + ${pkgs.su}/bin/su -s "${pkgs.bash}/bin/bash" -c "${pkgs.spamassassin}/bin/sa-update --gpghomedir=/var/lib/spamassassin/sa-update-keys/" spamd + v=$? + set -e + # 0 and 1 no error, exitcode > 1 means error: + # https://spamassassin.apache.org/full/3.1.x/doc/sa-update.html#exit_codes + if [ $v -gt 1 ]; then + echo "sa-update execution error" + exit $v + fi + echo "sa-update run successfully." + fi + ''; + }; + systemd.services.spamd = { - description = "Spam Assassin Server"; + description = "SpamAssassin Server"; wantedBy = [ "multi-user.target" ]; - after = [ "network.target" ]; + wants = [ "spamd-init.service" ]; + after = [ + "network.target" + "spamd-init.service" + ]; serviceConfig = { ExecStart = "${pkgs.spamassassin}/bin/spamd ${optionalString cfg.debug "-D"} --username=spamd --groupname=spamd --virtual-config-dir=/var/lib/spamassassin/user-%u --allow-tell --pidfile=/run/spamd.pid"; ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; }; - - # 0 and 1 no error, exitcode > 1 means error: - # https://spamassassin.apache.org/full/3.1.x/doc/sa-update.html#exit_codes - preStart = '' - echo "Recreating '/var/lib/spamasassin' with creating '3.004001' (or similar) and 'sa-update-keys'" - mkdir -p /var/lib/spamassassin - chown spamd:spamd /var/lib/spamassassin -R - set +e - ${pkgs.su}/bin/su -s "${pkgs.bash}/bin/bash" -c "${pkgs.spamassassin}/bin/sa-update --gpghomedir=/var/lib/spamassassin/sa-update-keys/" spamd - v=$? - set -e - if [ $v -gt 1 ]; then - echo "sa-update execution error" - exit $v - fi - chown spamd:spamd /var/lib/spamassassin -R - ''; }; }; } From 624cc51badb6b3b6aff2ebc7cf8a7243edcc0a0d Mon Sep 17 00:00:00 2001 From: Philipp Kern Date: Fri, 1 Jan 2021 19:56:52 +0100 Subject: [PATCH 2/6] nixos/spamassassin: Simplify services by using StateDirectory Let systemd create SpamAssassin's state directory and populate it using the regular updater service. Depend on the updater service on boot but do not propagate failure to the main service. spamd's commands to start and reload the service are still executed as root but user/group are set to properly chown the state directory to the target user. spamd drops privileges itself for its runner children but preserves root on the main daemon (to listen and re-exec). --- nixos/modules/services/mail/spamassassin.nix | 58 ++++++-------------- 1 file changed, 17 insertions(+), 41 deletions(-) diff --git a/nixos/modules/services/mail/spamassassin.nix b/nixos/modules/services/mail/spamassassin.nix index 0bbf2df48d40..98d9e925dcd7 100644 --- a/nixos/modules/services/mail/spamassassin.nix +++ b/nixos/modules/services/mail/spamassassin.nix @@ -126,22 +126,19 @@ in }; systemd.services.sa-update = { + # Needs to be able to contact the update server. wants = [ "network-online.target" ]; after = [ "network-online.target" ]; - script = '' - set +e - ${pkgs.su}/bin/su -s "${pkgs.bash}/bin/bash" -c "${pkgs.spamassassin}/bin/sa-update --gpghomedir=/var/lib/spamassassin/sa-update-keys/" spamd - v=$? - set -e - if [ $v -gt 1 ]; then - echo "sa-update execution error" - exit $v - fi - if [ $v -eq 0 ]; then - systemctl reload spamd.service - fi - ''; + serviceConfig = { + Type = "oneshot"; + User = "spamd"; + Group = "spamd"; + StateDirectory = "spamassassin"; + ExecStart = "${pkgs.spamassassin}/bin/sa-update --verbose --gpghomedir=%S/spamassassin/sa-update-keys/"; + ExecStartPost = "+${pkgs.systemd}/bin/systemctl -q --no-block try-reload-or-restart spamd.service"; + SuccessExitStatus = "1"; + }; }; systemd.timers.sa-update = { @@ -154,43 +151,22 @@ in }; }; - systemd.services.spamd-init = { - serviceConfig = { - Type = "oneshot"; - }; - script = '' - mkdir -p /var/lib/spamassassin - chown spamd:spamd /var/lib/spamassassin -R - if [ "$(ls -A /var/lib/spamassassin)" = "" ]; then - echo "'/var/lib/spamassassin' is empty, running sa-update..." - set +e - ${pkgs.su}/bin/su -s "${pkgs.bash}/bin/bash" -c "${pkgs.spamassassin}/bin/sa-update --gpghomedir=/var/lib/spamassassin/sa-update-keys/" spamd - v=$? - set -e - # 0 and 1 no error, exitcode > 1 means error: - # https://spamassassin.apache.org/full/3.1.x/doc/sa-update.html#exit_codes - if [ $v -gt 1 ]; then - echo "sa-update execution error" - exit $v - fi - echo "sa-update run successfully." - fi - ''; - }; - systemd.services.spamd = { description = "SpamAssassin Server"; wantedBy = [ "multi-user.target" ]; - wants = [ "spamd-init.service" ]; + wants = [ "sa-update.service" ]; after = [ "network.target" - "spamd-init.service" + "sa-update.service" ]; serviceConfig = { - ExecStart = "${pkgs.spamassassin}/bin/spamd ${optionalString cfg.debug "-D"} --username=spamd --groupname=spamd --virtual-config-dir=/var/lib/spamassassin/user-%u --allow-tell --pidfile=/run/spamd.pid"; - ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + User = "spamd"; + Group = "spamd"; + ExecStart = "+${pkgs.spamassassin}/bin/spamd ${optionalString cfg.debug "-D"} --username=spamd --groupname=spamd --virtual-config-dir=%S/spamassassin/user-%u --allow-tell --pidfile=/run/spamd.pid"; + ExecReload = "+${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + StateDirectory = "spamassassin"; }; }; }; From 4d4a0b7ccaad49f8ea790ecf6c1f15abd6099b05 Mon Sep 17 00:00:00 2001 From: Philipp Kern Date: Fri, 1 Jan 2021 20:45:06 +0100 Subject: [PATCH 3/6] spamassassin: include dependencies for sa-compile sa-compile requires re2c, gcc and gnumake to compile the expressions to performant C code. This compilation is done post-installation after every ruleset update and stored as local state. --- pkgs/servers/mail/spamassassin/default.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/servers/mail/spamassassin/default.nix b/pkgs/servers/mail/spamassassin/default.nix index ff96f0e7c827..f3843502ee30 100644 --- a/pkgs/servers/mail/spamassassin/default.nix +++ b/pkgs/servers/mail/spamassassin/default.nix @@ -1,4 +1,4 @@ -{ lib, fetchurl, perlPackages, makeWrapper, gnupg }: +{ lib, fetchurl, perlPackages, makeWrapper, gnupg, re2c, gcc, gnumake }: perlPackages.buildPerlPackage rec { pname = "SpamAssassin"; @@ -28,7 +28,7 @@ perlPackages.buildPerlPackage rec { mv "rules/"* $out/share/spamassassin/ for n in "$out/bin/"*; do - wrapProgram "$n" --prefix PERL5LIB : "$PERL5LIB" --prefix PATH : "${gnupg}/bin" + wrapProgram "$n" --prefix PERL5LIB : "$PERL5LIB" --prefix PATH : ${lib.makeBinPath [ gnupg re2c gcc gnumake ]} done ''; From cc625c968dfbc783d8f871da7e2b61f1372dd3f7 Mon Sep 17 00:00:00 2001 From: Philipp Kern Date: Sun, 3 Jan 2021 16:43:56 +0100 Subject: [PATCH 4/6] nixos/spamassassin: Run sa-compile after updating the rules sa-compile speeds up processing the rules by compiling them from Perl to C. This needs to be run after every update and is saved in the local state directory by Perl and SpamAssassin version. --- nixos/modules/services/mail/spamassassin.nix | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/nixos/modules/services/mail/spamassassin.nix b/nixos/modules/services/mail/spamassassin.nix index 98d9e925dcd7..036f1bfebd37 100644 --- a/nixos/modules/services/mail/spamassassin.nix +++ b/nixos/modules/services/mail/spamassassin.nix @@ -135,7 +135,10 @@ in User = "spamd"; Group = "spamd"; StateDirectory = "spamassassin"; - ExecStart = "${pkgs.spamassassin}/bin/sa-update --verbose --gpghomedir=%S/spamassassin/sa-update-keys/"; + ExecStart = [ + "${pkgs.spamassassin}/bin/sa-update --verbose --gpghomedir=%S/spamassassin/sa-update-keys/" + "${pkgs.spamassassin}/bin/sa-compile" + ]; ExecStartPost = "+${pkgs.systemd}/bin/systemctl -q --no-block try-reload-or-restart spamd.service"; SuccessExitStatus = "1"; }; From c86b339491d0c42540650d86e4ba840cdb686b50 Mon Sep 17 00:00:00 2001 From: Philipp Kern Date: Tue, 5 Jan 2021 14:53:14 +0100 Subject: [PATCH 5/6] nixos/spamassassin: Only run sa-compile when updates have been installed --- nixos/modules/services/mail/spamassassin.nix | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/nixos/modules/services/mail/spamassassin.nix b/nixos/modules/services/mail/spamassassin.nix index 036f1bfebd37..9bd415ef17ea 100644 --- a/nixos/modules/services/mail/spamassassin.nix +++ b/nixos/modules/services/mail/spamassassin.nix @@ -135,13 +135,21 @@ in User = "spamd"; Group = "spamd"; StateDirectory = "spamassassin"; - ExecStart = [ - "${pkgs.spamassassin}/bin/sa-update --verbose --gpghomedir=%S/spamassassin/sa-update-keys/" - "${pkgs.spamassassin}/bin/sa-compile" - ]; ExecStartPost = "+${pkgs.systemd}/bin/systemctl -q --no-block try-reload-or-restart spamd.service"; SuccessExitStatus = "1"; }; + + script = '' + set +e + ${pkgs.spamassassin}/bin/sa-update --verbose --gpghomedir=%S/spamassassin/sa-update-keys/ + rc=$? + set -e + + if [[ $rc -eq 0 ]]; then + # An update was available and installed. + ${pkgs.spamassassin}/bin/sa-compile + fi + ''; }; systemd.timers.sa-update = { From 8854b8251177e242d2869e3effe6ae47e5f229d1 Mon Sep 17 00:00:00 2001 From: Philipp Kern Date: Sun, 10 Jan 2021 12:15:38 +0100 Subject: [PATCH 6/6] nixos/spamassassin: Handle return codes correctly For sa-update we care about two successful codes: * 1 -> no updates available: exit successfully * 0 -> updates have been installed: run sa-compile and pass through its return code --- nixos/modules/services/mail/spamassassin.nix | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/nixos/modules/services/mail/spamassassin.nix b/nixos/modules/services/mail/spamassassin.nix index 9bd415ef17ea..ac878222b26a 100644 --- a/nixos/modules/services/mail/spamassassin.nix +++ b/nixos/modules/services/mail/spamassassin.nix @@ -136,19 +136,26 @@ in Group = "spamd"; StateDirectory = "spamassassin"; ExecStartPost = "+${pkgs.systemd}/bin/systemctl -q --no-block try-reload-or-restart spamd.service"; - SuccessExitStatus = "1"; }; script = '' set +e - ${pkgs.spamassassin}/bin/sa-update --verbose --gpghomedir=%S/spamassassin/sa-update-keys/ + ${pkgs.spamassassin}/bin/sa-update --verbose --gpghomedir=/var/lib/spamassassin/sa-update-keys/ rc=$? set -e - if [[ $rc -eq 0 ]]; then - # An update was available and installed. - ${pkgs.spamassassin}/bin/sa-compile + if [[ $rc -gt 1 ]]; then + # sa-update failed. + exit $rc fi + + if [[ $rc -eq 1 ]]; then + # No update was available, exit successfully. + exit 0 + fi + + # An update was available and installed. Compile the rules. + ${pkgs.spamassassin}/bin/sa-compile ''; };