GNU bug report logs - #61740
[PATCH] services: Add rspamd-service-type.

Previous Next

Package: guix-patches;

Reported by: Thomas Ieong <th.ieong <at> free.fr>

Date: Thu, 23 Feb 2023 22:05:01 UTC

Severity: normal

Tags: moreinfo, patch

Done: Ludovic Courtès <ludo <at> gnu.org>

Bug is archived. No further changes may be made.

To add a comment to this bug, you must first unarchive it, by sending
a message to control AT debbugs.gnu.org, with unarchive 61740 in the body.
You can then email your comments to 61740 AT debbugs.gnu.org in the normal way.

Toggle the display of automated, internal messages from the tracker.

View this report as an mbox folder, status mbox, maintainer mbox


Report forwarded to guix-patches <at> gnu.org:
bug#61740; Package guix-patches. (Thu, 23 Feb 2023 22:05:01 GMT) Full text and rfc822 format available.

Acknowledgement sent to Thomas Ieong <th.ieong <at> free.fr>:
New bug report received and forwarded. Copy sent to guix-patches <at> gnu.org. (Thu, 23 Feb 2023 22:05:02 GMT) Full text and rfc822 format available.

Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):

From: Thomas Ieong <th.ieong <at> free.fr>
To: guix-patches <at> gnu.org
Cc: Thomas Ieong <th.ieong <at> free.fr>
Subject: [PATCH] services: Add rspamd-service-type.
Date: Thu, 23 Feb 2023 21:16:14 +0100
* gnu/services/mail.scm (rspamd-service-type): New variable.
* gnu/tests/mail.scm (%test-rspamd): New variable.
* doc/guix.texi: Document it.
---

Hey Guix!

First time contributor here, this patch
introduces some basic support for rspamd.

I do need guidance on some points.

How to handle the extra configs that a user can
provide to rspamd?

On your average linux distro rspamd does expects
you to not touch the rspamd.conf and instead put
your changes in the /etc/rspamd/{local.d,override.d} directories
(local is enough to redefine most settings, but if there are changes made via the web ui, the web ui changes takes precedence, you need to use override.d if you want to freeze a setting.)

For example to set the password of the web ui
you're supposed to create /etc/rspamd/local.d/worker-controller.inc
and then set password = "some_hash";

Then this will get merged with the config
as something like:

worker {
   type = "controller";
   password = "some_hash";
}

The point is we could ignore local.d/override.d
and write these blocks directly to rspamd.conf.

Of course it needs some additionals configuration records for the workers and the common options
between them.

And finally for the test I do plan to add integration test with opensmtpd when I get the time.

Are there examples of such integration test?

What do you think?

 doc/guix.texi         |  43 ++++++++++
 gnu/services/mail.scm | 191 +++++++++++++++++++++++++++++++++++++++++-
 gnu/tests/mail.scm    |  87 ++++++++++++++++++-
 3 files changed, 319 insertions(+), 2 deletions(-)

diff --git a/doc/guix.texi b/doc/guix.texi
index 05615b9549..c1070a5244 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -114,6 +114,7 @@
 Copyright @copyright{} 2023 Giacomo Leidi@*
 Copyright @copyright{} 2022 Antero Mejr@*
 Copyright @copyright{} 2023 Bruno Victal@*
+Copyright @copyright{} 2023 Thomas Ieong@*
 
 Permission is granted to copy, distribute and/or modify this document
 under the terms of the GNU Free Documentation License, Version 1.3 or
@@ -26365,6 +26366,48 @@ Mail Services
 @end table
 @end deftp
 
+@subsubheading Rspamd Service
+@cindex email
+@cindex spam
+
+@defvar rspamd-service-type
+This is the type of the @uref{https://rspamd.com/, Rspamd} filtering
+system whose value should be a @code{rspamd-configuration}.
+@end defvar
+
+@deftp {Data Type} rspamd-configuration
+Data type representing the configuration of @command{rspamd}.
+
+@table @asis
+@item @code{package} (default: @code{rspamd})
+The package that provides @command{rspamd}.
+
+@item @code{config-file} (default: @code{%default-rspamd-config-file})
+File-like object of the configuration file to use. By default
+all workers are enabled except fuzzy and they are binded
+to their usual ports, e.g localhost:11334, localhost:11333 and so on.
+
+@item @code{user} (default: @code{"rspamd"})
+The user to run rspamd as.
+
+@item @code{group} (default: @code{"rspamd"})
+The user to run rspamd as.
+
+@item @code{pid-file} (default: @code{"/var/run/rspamd/rspamd.pid"})
+Where to store the PID file.
+
+@item @code{debug?} (default: @code{#f})
+Force debug output.
+
+@item @code{insecure?} (default: @code{#f})
+Ignore running workers as privileged users (insecure).
+
+@item @code{skip-template?} (default: @code{#f})
+Do not apply Jinja templates.
+
+@end table
+@end deftp
+
 @node Messaging Services
 @subsection Messaging Services
 
diff --git a/gnu/services/mail.scm b/gnu/services/mail.scm
index 6f588679b1..8a4af26f66 100644
--- a/gnu/services/mail.scm
+++ b/gnu/services/mail.scm
@@ -5,6 +5,7 @@
 ;;; Copyright © 2017, 2020 Tobias Geerinckx-Rice <me <at> tobias.gr>
 ;;; Copyright © 2019 Kristofer Buffington <kristoferbuffington <at> gmail.com>
 ;;; Copyright © 2020 Jonathan Brielmaier <jonathan.brielmaier <at> web.de>
+;;; Copyright © 2023 Thomas Ieong <th.ieong <at> free.fr>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -79,7 +80,11 @@ (define-module (gnu services mail)
             radicale-configuration
             radicale-configuration?
             radicale-service-type
-            %default-radicale-config-file))
+            %default-radicale-config-file
+
+            rspamd-configuration
+	    rspamd-service-type
+	    %default-rspamd-config-file))
 
 ;;; Commentary:
 ;;;
@@ -1984,3 +1989,187 @@ (define radicale-service-type
           (service-extension account-service-type (const %radicale-accounts))
           (service-extension activation-service-type radicale-activation)))
    (default-value (radicale-configuration))))
+
+;;;
+;;; Rspamd.
+;;;
+
+(define-maybe boolean)
+
+(define-configuration rspamd-configuration
+  (package
+    (file-like rspamd)
+    "The package that provides rspamd."
+    empty-serializer)
+  (config-file
+   (file-like %default-rspamd-config-file)
+   "File-like object of the configuration file to use. By default
+all workers are enabled except fuzzy and they are binded
+to their usual ports, e.g localhost:11334, localhost:11333 and so on")
+  (user
+   (string "rspamd")
+   "The user to run rspamd as."
+   empty-serializer)
+  (group
+   (string "rspamd")
+   "The group to run rspamd as."
+   empty-serializer)
+  (pid-file
+   (string "/var/run/rspamd/rspamd.pid")
+   "Where to store the PID file."
+   empty-serializer)
+  (debug?
+   maybe-boolean
+   "Force debug output."
+   empty-serializer)
+  (insecure?
+   maybe-boolean
+   "Ignore running workers as privileged users (insecure)."
+   empty-serializer)
+  (skip-template?
+   maybe-boolean
+   "Do not apply Jinja templates."
+   empty-serializer))
+
+(define %default-rspamd-config-file
+  (plain-file "rspamd.conf" "
+.include \"$CONFDIR/common.conf\"
+
+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 = \"file\";
+    filename = \"$LOGDIR/rspamd.log\";
+    .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\"
+}
+
+worker \"normal\" {
+    bind_socket = \"localhost:11333\";
+    .include \"$CONFDIR/worker-normal.inc\"
+    .include(try=true; priority=1,duplicate=merge) \"$LOCAL_CONFDIR/local.d/worker-normal.inc\"
+    .include(try=true; priority=10) \"$LOCAL_CONFDIR/override.d/worker-normal.inc\"
+}
+
+worker \"controller\" {
+    bind_socket = \"localhost:11334\";
+    .include \"$CONFDIR/worker-controller.inc\"
+    .include(try=true; priority=1,duplicate=merge) \"$LOCAL_CONFDIR/local.d/worker-controller.inc\"
+    .include(try=true; priority=10) \"$LOCAL_CONFDIR/override.d/worker-controller.inc\"
+}
+
+worker \"rspamd_proxy\" {
+    bind_socket = \"localhost:11332\";
+    .include \"$CONFDIR/worker-proxy.inc\"
+    .include(try=true; priority=1,duplicate=merge) \"$LOCAL_CONFDIR/local.d/worker-proxy.inc\"
+    .include(try=true; priority=10) \"$LOCAL_CONFDIR/override.d/worker-proxy.inc\"
+}
+
+# Local fuzzy storage is disabled by default
+
+worker \"fuzzy\" {
+    bind_socket = \"localhost:11335\";
+    count = -1; # Disable by default
+    .include \"$CONFDIR/worker-fuzzy.inc\"
+    .include(try=true; priority=1,duplicate=merge) \"$LOCAL_CONFDIR/local.d/worker-fuzzy.inc\"
+    .include(try=true; priority=10) \"$LOCAL_CONFDIR/override.d/worker-fuzzy.inc\"
+}
+"))
+
+(define (rspamd-accounts config)
+  (match-record config <rspamd-configuration>
+    (user group)
+    (list (user-group
+	   (name group)
+	   (system? #t))
+	  (user-account
+	   (name user)
+	   (group group)
+	   (system? #t)
+	   (comment "Rspamd daemon")
+	   (home-directory "/var/empty")
+	   (shell (file-append shadow "/sbin/nologin"))))))
+
+(define (rspamd-shepherd-service config)
+  (match-record config <rspamd-configuration>
+    (package config-file user group pid-file debug? insecure? skip-template?)
+    (list (shepherd-service
+	   (provision '(rspamd))
+	   (documentation "Run the rspamd daemon.")
+	   (requirement '(networking))
+	   (start (let ((rspamd (file-append package "/bin/rspamd")))
+		    #~(make-forkexec-constructor
+		       (list #$rspamd "-c" #$config-file
+			     #$@(if debug?
+				    '("--debug")
+				    '())
+			     #$@(if insecure?
+				    '("--insecure")
+				    '())
+			     #$@(if skip-template?
+				    '("--skip-template")
+				    '()))
+		       #:user #$user
+		       #:group #$group
+		       #:pid-file #$pid-file)))
+	   (stop #~(make-kill-destructor))
+	   (actions
+	    (list (shepherd-configuration-action config-file)
+		  (shepherd-action
+		   (name 'reload)
+		   (documentation "Reload rspamd.")
+		   (procedure
+		    #~(lambda (pid)
+			(if pid
+			    (begin
+			      (kill pid SIGHUP)
+			      (display "Service rspamd has been reloaded"))
+			    (format #t "Service rspamd is not running.")))))
+		  (shepherd-action
+		   (name 'reopenlog)
+		   (documentation "Reopen log files.")
+		   (procedure
+		    #~(lambda (pid)
+			(if pid
+			    (begin
+			      (kill pid SIGUSR1)
+			      (display "Reopening the logs for rspamd"))
+			    (format #t "Service rspamd is not running.")))))))))))
+
+(define (rspamd-activation config)
+  (match-record config <rspamd-configuration>
+    (package config-file user)
+    #~(begin
+	(use-modules (guix build utils)
+		     (ice-9 match))
+	(let ((user (getpwnam #$user)))
+	  (mkdir-p/perms "/etc/rspamd" user #o755)
+	  (mkdir-p/perms "/etc/rspamd/local.d" user #o755)
+	  (mkdir-p/perms "/etc/rspamd/override.d" user #o755)
+	  (mkdir-p/perms "/var/run/rspamd" user #o755)
+	  (mkdir-p/perms "/var/log/rspamd" user #o755)
+	  (mkdir-p/perms "/var/lib/rspamd" user #o755))
+	;; Check configuration file syntax.
+	(system* (string-append #$package "/bin/rspamadm")
+		 "configtest"
+		 "-c" #$config-file))))
+
+(define rspamd-profile
+  (compose list rspamd-configuration-package))
+
+(define rspamd-service-type
+  (service-type
+   (name 'rspamd)
+   (description "Run the rapid spam filtering system")
+   (extensions
+    (list (service-extension shepherd-root-service-type rspamd-shepherd-service)
+	  (service-extension account-service-type rspamd-accounts)
+	  (service-extension activation-service-type rspamd-activation)
+	  (service-extension profile-service-type rspamd-profile)))
+   (default-value (rspamd-configuration))))
diff --git a/gnu/tests/mail.scm b/gnu/tests/mail.scm
index f13751b72f..f532d30805 100644
--- a/gnu/tests/mail.scm
+++ b/gnu/tests/mail.scm
@@ -6,6 +6,7 @@
 ;;; Copyright © 2018 Clément Lassieur <clement <at> lassieur.org>
 ;;; Copyright © 2019 Christopher Baines <mail <at> cbaines.net>
 ;;; Copyright © 2019, 2020 Tobias Geerinckx-Rice <me <at> tobias.gr>
+;;; Copyright © 2023 Thomas Ieong <th.ieong <at> free.fr>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -40,7 +41,8 @@ (define-module (gnu tests mail)
   #:export (%test-opensmtpd
             %test-exim
             %test-dovecot
-            %test-getmail))
+            %test-getmail
+            %test-rspamd))
 
 (define %opensmtpd-os
   (simple-operating-system
@@ -575,3 +577,86 @@ (define %test-getmail
    (name "getmail")
    (description "Connect to a running Getmail server.")
    (value (run-getmail-test))))
+
+(define %rspamd-os
+  (simple-operating-system
+   (service dhcp-client-service-type)
+   (service rspamd-service-type)))
+
+(define (run-rspamd-test)
+  "Return a test of an OS running Rspamd service."
+
+  (define rspamd-ports
+    '((22664 . 11332)    ;; proxy worker
+      (22666 . 11333)    ;; normal worker
+      (22668 . 11334)    ;; web controller
+      (22670 . 11335)))  ;; fuzzy worker
+
+  (define vm
+    (virtual-machine
+     (operating-system (marionette-operating-system
+                        %rspamd-os
+                        #:imported-modules '((gnu services herd))))
+     (port-forwardings rspamd-ports)))
+
+  (define test
+    (with-imported-modules '((gnu build marionette))
+      #~(begin
+          (use-modules (srfi srfi-64)
+                       (srfi srfi-11)
+                       (gnu build marionette)
+                       (web uri)
+                       (web client)
+                       (web response))
+
+          (define marionette
+            (make-marionette '(#$vm)))
+
+          (test-runner-current (system-test-runner #$output))
+          (test-begin "rspamd")
+
+          (test-assert "service is running"
+            (marionette-eval
+             '(begin
+                (use-modules (gnu services herd))
+                (start-service 'rspamd))
+             marionette))
+
+
+          ;; Check mympd-service-type commit for reference
+          ;; TODO: For this test we need to authorize the controller to
+          ;; listen on other interfaces, e.g *:11334 instead of localhost:11334
+
+          ;; Check that we can access the web ui
+          (test-equal "http-get"
+            200
+            (begin
+              (let-values (((response text)
+                            (http-get "http://localhost:22668/"
+                                      #:decode-body? #t)))
+                (response-code response))))
+
+          (test-assert "rspamd socket ready"
+            (wait-for-unix-socket
+             "/var/lib/rspamd/rspamd.sock"
+             marionette))
+
+          (test-assert "rspamd pid ready"
+            (marionette-eval
+             '(file-exists? "/var/run/rspamd/rspamd.pid")
+             marionette))
+
+          (test-assert "rspamd log file"
+            (marionette-eval
+             '(file-exists? "/var/log/rspamd/rspamd.log")
+             marionette))
+
+          (test-end))))
+
+  (gexp->derivation "rspamd-test" test))
+
+(define %test-rspamd
+  (system-test
+   (name "rspamd")
+   (description "Send an email to a running rspamd server.")
+   (value (run-rspamd-test))))

base-commit: 5e7b0a7735d9956ee8b8c3763e4ce05e2855606f
-- 
2.39.1





Information forwarded to guix-patches <at> gnu.org:
bug#61740; Package guix-patches. (Sat, 25 Feb 2023 21:35:01 GMT) Full text and rfc822 format available.

Message #8 received at 61740 <at> debbugs.gnu.org (full text, mbox):

From: Bruno Victal <mirai <at> makinata.eu>
To: Thomas Ieong <th.ieong <at> free.fr>
Cc: 61740 <at> debbugs.gnu.org
Subject: Re: [bug#61740] [PATCH] services: Add rspamd-service-type.
Date: Sat, 25 Feb 2023 21:33:57 +0000
Hi,

On 2023-02-23 20:16, Thomas Ieong wrote:
> * gnu/services/mail.scm (rspamd-service-type): New variable.
> * gnu/tests/mail.scm (%test-rspamd): New variable.
> * doc/guix.texi: Document it.
> ---
> 
> Hey Guix!
> 
> First time contributor here, this patch
> introduces some basic support for rspamd.
> 
> I do need guidance on some points.
> 
> How to handle the extra configs that a user can
> provide to rspamd?
> 
> On your average linux distro rspamd does expects
> you to not touch the rspamd.conf and instead put
> your changes in the /etc/rspamd/{local.d,override.d} directories
> (local is enough to redefine most settings, but if there are changes made via the web ui, the web ui changes takes precedence, you need to use override.d if you want to freeze a setting.)
> 
> For example to set the password of the web ui
> you're supposed to create /etc/rspamd/local.d/worker-controller.inc
> and then set password = "some_hash";
> 
> Then this will get merged with the config
> as something like:
> 
> worker {
>    type = "controller";
>    password = "some_hash";
> }
> 
> The point is we could ignore local.d/override.d
> and write these blocks directly to rspamd.conf.

For most services, the configuration is expected to be read-only (and generated & managed by guix)
though it is possible to have a mix of non guix-managed config files (but discouraged).

If you simply want to store the configuration in separate files, pulseaudio-service-type and mympd-service-type is an example that can do this.

> 
> Of course it needs some additionals configuration records for the workers and the common options
> between them.
> 
> And finally for the test I do plan to add integration test with opensmtpd when I get the time.
> 
> Are there examples of such integration test?

Specific examples no but gnu/tests/ contains many tests of varying complexity that could serve as inspiration.
See the NFS or web server tests.

> +
> +@deftp {Data Type} rspamd-configuration
> +Data type representing the configuration of @command{rspamd}.
> +
> +@table @asis
> +@item @code{package} (default: @code{rspamd})
> +The package that provides @command{rspamd}.
> +
> +@item @code{config-file} (default: @code{%default-rspamd-config-file})
> +File-like object of the configuration file to use. By default
> +all workers are enabled except fuzzy and they are binded
> +to their usual ports, e.g localhost:11334, localhost:11333 and so on.
> +
> +@item @code{user} (default: @code{"rspamd"})
> +The user to run rspamd as.
> +
> +@item @code{group} (default: @code{"rspamd"})
> +The user to run rspamd as.
> +
> +@item @code{pid-file} (default: @code{"/var/run/rspamd/rspamd.pid"})
> +Where to store the PID file.
> +
> +@item @code{debug?} (default: @code{#f})
> +Force debug output.
> +
> +@item @code{insecure?} (default: @code{#f})
> +Ignore running workers as privileged users (insecure).
> +
> +@item @code{skip-template?} (default: @code{#f})
> +Do not apply Jinja templates.
> +
> +@end table
> +@end deftp
> +

Was this manually typed? (It seems to be the case since it's missing the field type information)
You can generate the documentation automatically with configuration->documentation since you're using define-configuration.

> +;;;
> +;;; Rspamd.
> +;;;
> +
> +(define-maybe boolean)
> +
> +(define-configuration rspamd-configuration
> +  (package
> +    (file-like rspamd)
> +    "The package that provides rspamd."
> +    empty-serializer)
> +  (config-file
> +   (file-like %default-rspamd-config-file)
> +   "File-like object of the configuration file to use. By default
> +all workers are enabled except fuzzy and they are binded
> +to their usual ports, e.g localhost:11334, localhost:11333 and so on")
> +  (user
> +   (string "rspamd")
> +   "The user to run rspamd as."
> +   empty-serializer)
> +  (group
> +   (string "rspamd")
> +   "The group to run rspamd as."
> +   empty-serializer)
> +  (pid-file
> +   (string "/var/run/rspamd/rspamd.pid")
> +   "Where to store the PID file."
> +   empty-serializer)
> +  (debug?
> +   maybe-boolean
> +   "Force debug output."
> +   empty-serializer)
> +  (insecure?
> +   maybe-boolean
> +   "Ignore running workers as privileged users (insecure)."
> +   empty-serializer)
> +  (skip-template?
> +   maybe-boolean
> +   "Do not apply Jinja templates."
> +   empty-serializer))

If you're not going to use any serializer, you can use define-configuration/no-serialization instead.

> +
> +(define (rspamd-activation config)
> +  (match-record config <rspamd-configuration>
> +    (package config-file user)
> +    #~(begin
> +	(use-modules (guix build utils)
> +		     (ice-9 match))
> +	(let ((user (getpwnam #$user)))
> +	  (mkdir-p/perms "/etc/rspamd" user #o755)
> +	  (mkdir-p/perms "/etc/rspamd/local.d" user #o755)
> +	  (mkdir-p/perms "/etc/rspamd/override.d" user #o755)
> +	  (mkdir-p/perms "/var/run/rspamd" user #o755)
> +	  (mkdir-p/perms "/var/log/rspamd" user #o755)
> +	  (mkdir-p/perms "/var/lib/rspamd" user #o755))
> +	;; Check configuration file syntax.
> +	(system* (string-append #$package "/bin/rspamadm")
> +		 "configtest"
> +		 "-c" #$config-file))))

This should be moved into the service constructor. See how mpd-service-type does this.

To expand a bit here, activation-service-type service-extensions are often abused for "pre-service launch tasks"
but this is incorrect usage (see #60657 which covers the pitfalls on doing so).

> +
> +(define rspamd-profile
> +  (compose list rspamd-configuration-package))

How about: 
(service-extension profile-service-type
                   (compose list rspamd-configuration-package))


> diff --git a/gnu/tests/mail.scm b/gnu/tests/mail.scm
> index f13751b72f..f532d30805 100644

Do not forget to register this file in gnu/local.mk.


Cheers,
Bruno




Added tag(s) moreinfo. Request was from Ludovic Courtès <ludo <at> gnu.org> to control <at> debbugs.gnu.org. (Sun, 05 Mar 2023 22:07:02 GMT) Full text and rfc822 format available.

Information forwarded to guix-patches <at> gnu.org:
bug#61740; Package guix-patches. (Tue, 08 Aug 2023 15:35:02 GMT) Full text and rfc822 format available.

Message #13 received at 61740 <at> debbugs.gnu.org (full text, mbox):

From: Ludovic Courtès <ludo <at> gnu.org>
To: Thomas Ieong <th.ieong <at> free.fr>
Cc: Bruno Victal <mirai <at> makinata.eu>, 61740 <at> debbugs.gnu.org
Subject: Re: bug#61740: [PATCH] services: Add rspamd-service-type.
Date: Tue, 08 Aug 2023 17:34:45 +0200
Hi Thomas,

It’s been a while.  :-)  Did you have time to consider Bruno’s
suggestions to send an updated patch?

  https://issues.guix.gnu.org/61740

Thanks,
Ludo’.

Bruno Victal <mirai <at> makinata.eu> skribis:

> Hi,
>
> On 2023-02-23 20:16, Thomas Ieong wrote:
>> * gnu/services/mail.scm (rspamd-service-type): New variable.
>> * gnu/tests/mail.scm (%test-rspamd): New variable.
>> * doc/guix.texi: Document it.
>> ---
>> 
>> Hey Guix!
>> 
>> First time contributor here, this patch
>> introduces some basic support for rspamd.
>> 
>> I do need guidance on some points.
>> 
>> How to handle the extra configs that a user can
>> provide to rspamd?
>> 
>> On your average linux distro rspamd does expects
>> you to not touch the rspamd.conf and instead put
>> your changes in the /etc/rspamd/{local.d,override.d} directories
>> (local is enough to redefine most settings, but if there are changes made via the web ui, the web ui changes takes precedence, you need to use override.d if you want to freeze a setting.)
>> 
>> For example to set the password of the web ui
>> you're supposed to create /etc/rspamd/local.d/worker-controller.inc
>> and then set password = "some_hash";
>> 
>> Then this will get merged with the config
>> as something like:
>> 
>> worker {
>>    type = "controller";
>>    password = "some_hash";
>> }
>> 
>> The point is we could ignore local.d/override.d
>> and write these blocks directly to rspamd.conf.
>
> For most services, the configuration is expected to be read-only (and generated & managed by guix)
> though it is possible to have a mix of non guix-managed config files (but discouraged).
>
> If you simply want to store the configuration in separate files, pulseaudio-service-type and mympd-service-type is an example that can do this.
>
>> 
>> Of course it needs some additionals configuration records for the workers and the common options
>> between them.
>> 
>> And finally for the test I do plan to add integration test with opensmtpd when I get the time.
>> 
>> Are there examples of such integration test?
>
> Specific examples no but gnu/tests/ contains many tests of varying complexity that could serve as inspiration.
> See the NFS or web server tests.
>
>> +
>> +@deftp {Data Type} rspamd-configuration
>> +Data type representing the configuration of @command{rspamd}.
>> +
>> +@table @asis
>> +@item @code{package} (default: @code{rspamd})
>> +The package that provides @command{rspamd}.
>> +
>> +@item @code{config-file} (default: @code{%default-rspamd-config-file})
>> +File-like object of the configuration file to use. By default
>> +all workers are enabled except fuzzy and they are binded
>> +to their usual ports, e.g localhost:11334, localhost:11333 and so on.
>> +
>> +@item @code{user} (default: @code{"rspamd"})
>> +The user to run rspamd as.
>> +
>> +@item @code{group} (default: @code{"rspamd"})
>> +The user to run rspamd as.
>> +
>> +@item @code{pid-file} (default: @code{"/var/run/rspamd/rspamd.pid"})
>> +Where to store the PID file.
>> +
>> +@item @code{debug?} (default: @code{#f})
>> +Force debug output.
>> +
>> +@item @code{insecure?} (default: @code{#f})
>> +Ignore running workers as privileged users (insecure).
>> +
>> +@item @code{skip-template?} (default: @code{#f})
>> +Do not apply Jinja templates.
>> +
>> +@end table
>> +@end deftp
>> +
>
> Was this manually typed? (It seems to be the case since it's missing the field type information)
> You can generate the documentation automatically with configuration->documentation since you're using define-configuration.
>
>> +;;;
>> +;;; Rspamd.
>> +;;;
>> +
>> +(define-maybe boolean)
>> +
>> +(define-configuration rspamd-configuration
>> +  (package
>> +    (file-like rspamd)
>> +    "The package that provides rspamd."
>> +    empty-serializer)
>> +  (config-file
>> +   (file-like %default-rspamd-config-file)
>> +   "File-like object of the configuration file to use. By default
>> +all workers are enabled except fuzzy and they are binded
>> +to their usual ports, e.g localhost:11334, localhost:11333 and so on")
>> +  (user
>> +   (string "rspamd")
>> +   "The user to run rspamd as."
>> +   empty-serializer)
>> +  (group
>> +   (string "rspamd")
>> +   "The group to run rspamd as."
>> +   empty-serializer)
>> +  (pid-file
>> +   (string "/var/run/rspamd/rspamd.pid")
>> +   "Where to store the PID file."
>> +   empty-serializer)
>> +  (debug?
>> +   maybe-boolean
>> +   "Force debug output."
>> +   empty-serializer)
>> +  (insecure?
>> +   maybe-boolean
>> +   "Ignore running workers as privileged users (insecure)."
>> +   empty-serializer)
>> +  (skip-template?
>> +   maybe-boolean
>> +   "Do not apply Jinja templates."
>> +   empty-serializer))
>
> If you're not going to use any serializer, you can use define-configuration/no-serialization instead.
>
>> +
>> +(define (rspamd-activation config)
>> +  (match-record config <rspamd-configuration>
>> +    (package config-file user)
>> +    #~(begin
>> +	(use-modules (guix build utils)
>> +		     (ice-9 match))
>> +	(let ((user (getpwnam #$user)))
>> +	  (mkdir-p/perms "/etc/rspamd" user #o755)
>> +	  (mkdir-p/perms "/etc/rspamd/local.d" user #o755)
>> +	  (mkdir-p/perms "/etc/rspamd/override.d" user #o755)
>> +	  (mkdir-p/perms "/var/run/rspamd" user #o755)
>> +	  (mkdir-p/perms "/var/log/rspamd" user #o755)
>> +	  (mkdir-p/perms "/var/lib/rspamd" user #o755))
>> +	;; Check configuration file syntax.
>> +	(system* (string-append #$package "/bin/rspamadm")
>> +		 "configtest"
>> +		 "-c" #$config-file))))
>
> This should be moved into the service constructor. See how mpd-service-type does this.
>
> To expand a bit here, activation-service-type service-extensions are often abused for "pre-service launch tasks"
> but this is incorrect usage (see #60657 which covers the pitfalls on doing so).
>
>> +
>> +(define rspamd-profile
>> +  (compose list rspamd-configuration-package))
>
> How about: 
> (service-extension profile-service-type
>                    (compose list rspamd-configuration-package))
>
>
>> diff --git a/gnu/tests/mail.scm b/gnu/tests/mail.scm
>> index f13751b72f..f532d30805 100644
>
> Do not forget to register this file in gnu/local.mk.
>
>
> Cheers,
> Bruno




Information forwarded to guix-patches <at> gnu.org:
bug#61740; Package guix-patches. (Tue, 05 Sep 2023 19:07:02 GMT) Full text and rfc822 format available.

Message #16 received at 61740 <at> debbugs.gnu.org (full text, mbox):

From: Saku Laesvuori <saku <at> laesvuori.fi>
To: Ludovic Courtès <ludo <at> gnu.org>
Cc: Thomas Ieong <th.ieong <at> free.fr>, Bruno Victal <mirai <at> makinata.eu>,
 61740 <at> debbugs.gnu.org
Subject: Re: [bug#61740] [PATCH] services: Add rspamd-service-type.
Date: Tue, 5 Sep 2023 22:06:01 +0300
[Message part 1 (text/plain, inline)]
> Hi Thomas,
> 
> It’s been a while.  :-)  Did you have time to consider Bruno’s
> suggestions to send an updated patch?
> 
>   https://issues.guix.gnu.org/61740
> 
> Thanks,
> Ludo’.

I happened to need rspamd myself so I cleaned this patch a little and
thought it would be useful to submit a v2 of it. I don't really know how
co-authored patches should be sent (because I expect the From: in the
patch to interfere with email) so I added it as an attachment.
[v2-0001-services-Add-rspamd-service-type.patch (text/plain, attachment)]
[signature.asc (application/pgp-signature, inline)]

Information forwarded to guix-patches <at> gnu.org:
bug#61740; Package guix-patches. (Fri, 15 Sep 2023 20:48:02 GMT) Full text and rfc822 format available.

Message #19 received at 61740 <at> debbugs.gnu.org (full text, mbox):

From: Felix Lechner <felix.lechner <at> lease-up.com>
To: Saku Laesvuori <saku <at> laesvuori.fi>
Cc: Thomas Ieong <th.ieong <at> free.fr>, Bruno Victal <mirai <at> makinata.eu>,
 Ludovic Courtès <ludo <at> gnu.org>, 61740 <at> debbugs.gnu.org
Subject: Re: [PATCH] services: Add rspamd-service-type.
Date: Fri, 15 Sep 2023 13:47:14 -0700
Hi Saku,

> I happened to need rspamd myself

So do I but it does not seem to start locally. It created some folders
and, per the log, the configuration file passed the syntax check, but
then the boot stalls.

I used (service rspamd-service-type) and nothing else in my system
configuration. Should it be sufficient? Thanks!

Kind regards
Felix




Information forwarded to guix-patches <at> gnu.org:
bug#61740; Package guix-patches. (Sat, 16 Sep 2023 20:11:01 GMT) Full text and rfc822 format available.

Message #22 received at 61740 <at> debbugs.gnu.org (full text, mbox):

From: Saku Laesvuori <saku <at> laesvuori.fi>
To: Felix Lechner <felix.lechner <at> lease-up.com>
Cc: Thomas Ieong <th.ieong <at> free.fr>, Bruno Victal <mirai <at> makinata.eu>,
 Ludovic Courtès <ludo <at> gnu.org>, 61740 <at> debbugs.gnu.org
Subject: Re: [PATCH] services: Add rspamd-service-type.
Date: Sat, 16 Sep 2023 23:10:20 +0300
[Message part 1 (text/plain, inline)]
On Fri, Sep 15, 2023 at 01:47:14PM -0700, Felix Lechner wrote:
> Hi Saku,
> 
> > I happened to need rspamd myself
> 
> So do I but it does not seem to start locally. It created some folders
> and, per the log, the configuration file passed the syntax check, but
> then the boot stalls.
> 
> I used (service rspamd-service-type) and nothing else in my system
> configuration. Should it be sufficient? Thanks!

Oops, the version I tested (and am running right now) used
activation-service-type and I forgot to test that it was still working
after moving the code from there to the shepherd service. A fixed v3 is
attached.
[v3-0001-services-Add-rspamd-service-type.patch (text/plain, attachment)]
[signature.asc (application/pgp-signature, inline)]

Information forwarded to guix-patches <at> gnu.org:
bug#61740; Package guix-patches. (Fri, 01 Dec 2023 03:12:01 GMT) Full text and rfc822 format available.

Message #25 received at 61740 <at> debbugs.gnu.org (full text, mbox):

From: Felix Lechner <felix.lechner <at> lease-up.com>
To: 61740 <at> debbugs.gnu.org
Cc: Thomas Ieong <th.ieong <at> free.fr>, Bruno Victal <mirai <at> makinata.eu>,
 Ludovic Courtès <ludo <at> gnu.org>,
 Saku Laesvuori <saku <at> laesvuori.fi>
Subject: Re: [PATCH] services: Add rspamd-service-type.
Date: Thu, 30 Nov 2023 19:11:16 -0800
Hi,

This patch is ready to be merged.

I have been running the v3 patch of the rspamd-service-type, which was
posted here attached and not inline, on my production equipment since
the day Saku shared it.

I use it with OpenSMTPd like this. [1]

Kind regards
Felix

[1]
https://codeberg.org/lechner/system-config/src/commit/047830c4248076cec9e29ecd4f3c77d151afb102/host/wallace-server/operating-system.scm#L1226




Information forwarded to guix-patches <at> gnu.org:
bug#61740; Package guix-patches. (Wed, 06 Dec 2023 14:59:01 GMT) Full text and rfc822 format available.

Message #28 received at 61740 <at> debbugs.gnu.org (full text, mbox):

From: Bruno Victal <mirai <at> makinata.eu>
To: Saku Laesvuori <saku <at> laesvuori.fi>, Thomas Ieong <th.ieong <at> free.fr>
Cc: Ludovic Courtès <ludo <at> gnu.org>, 61740 <at> debbugs.gnu.org,
 Felix Lechner <felix.lechner <at> lease-up.com>
Subject: Re: [bug#61740] [PATCH v3] services: Add rspamd-service-type. (was
 [bug#61740] [PATCH] services: Add rspamd-service-type.)
Date: Wed, 6 Dec 2023 14:58:19 +0000
[Message part 1 (text/plain, inline)]
Hi Saku,

Some comments:

> +(define (directory-tree? xs)
> +  (match xs
> +    (((file-name file-like) ...)
> +     (and (every string? file-name)
> +          (every file-like? file-like)))
> +    (_ #f)))

You can express this more compactly as:

--8<---------------cut here---------------start------------->8---
(define directory-tree?
  (match-lambda
    ((((? string?) (? file-like?)) ...) #t)
    (_ #f)))
--8<---------------cut here---------------end--------------->8---

> +  (user
> +   (string "rspamd")
> +   "The user to run rspamd as.")
> +  (group
> +   (string "rspamd")
> +   "The group to run rspamd as.")

How about using user-account and user-group records instead? (see
vnstat-service-type for an example)

> +  (pid-file
> +   (string "/var/run/rspamd/rspamd.pid")
> +   "Where to store the PID file.")

Is it useful to expose this?


> +  (insecure?
> +   (boolean #f)
> +   "Ignore running workers as privileged users (insecure).")

To me it seems redundant to restate “(insecure)” in the description.

> +                     (make-forkexec-constructor
> +                      (list #$rspamd "-c" #$config-file

I'd prefer the long-name --config over the shorter ones here.

> +                            "--var" (string-append "LOCAL_CONFDIR=" #$local-confdir)

Curiously I don't see this listed in the 'rspamd' manpage although
it is on the 'rspamadm' one. Can you confirm whether this works
and if so, report to upstream that their docs are missing this?

> +     (service-extension profile-service-type
> +                        (compose list rspamd-configuration-package))

What's the motivation for adding the rspamd package to the profile?

> +(define %rspamd-os
> +  (simple-operating-system
> +   (service dhcp-client-service-type)
> +   (service rspamd-service-type)))

Is 'dhcp-client-service-type' needed for this system test?
I haven't tested it but it looks unnecessary to me.

> +          ;; Check that we can access the web ui
> +          (test-equal "http-get"
> +            200
> +            (begin
> +              (let-values (((response text)
> +                            (http-get "http://localhost:22668/"
> +                                      #:decode-body? #t)))
> +                (response-code response))))

IMO if you're only interested in the HTTP response code a http-head
is the better option, unless the program handles those requests
differently. Also, since 'text' isn't used you can simplify this to:

--8<---------------cut here---------------start------------->8---
;; Don't forget to remove the unused (srfi srfi-11) import.

(test-equal "Web UI is accessible"
  200
  (response-code (http-head "http://localhost:22668/")))
--8<---------------cut here---------------end--------------->8---

> +          (test-assert "rspamd pid ready"
> +            (marionette-eval
> +             '(file-exists? "/var/run/rspamd/rspamd.pid")
> +             marionette))

There's a procedure dedicated for this:

--8<---------------cut here---------------start------------->8---
(test-assert "rspamd pid ready"
  (wait-for-file #$(rspamd-configuration-pid-file (rspamd-configuration)) marionette)))
--8<---------------cut here---------------end--------------->8---

Note: I used (rspamd-configuration) since it you're using the default
rspamd-configuration value here.

> +(define %test-rspamd
> +  (system-test
> +   (name "rspamd")
> +   (description "Send an email to a running rspamd server.")
> +   (value (run-rspamd-test))))

I'd change the description to something like "Basic rspamd service test."
as the current one is misleading.

-- 
Furthermore, I consider that nonfree software must be eradicated.

Cheers,
Bruno.

[OpenPGP_signature.asc (application/pgp-signature, attachment)]

Information forwarded to guix-patches <at> gnu.org:
bug#61740; Package guix-patches. (Fri, 08 Dec 2023 08:18:01 GMT) Full text and rfc822 format available.

Message #31 received at 61740 <at> debbugs.gnu.org (full text, mbox):

From: Saku Laesvuori <saku <at> laesvuori.fi>
To: Bruno Victal <mirai <at> makinata.eu>
Cc: Thomas Ieong <th.ieong <at> free.fr>,
 Ludovic Courtès <ludo <at> gnu.org>,
 Felix Lechner <felix.lechner <at> lease-up.com>, 61740 <at> debbugs.gnu.org
Subject: Re: [bug#61740] [PATCH v3] services: Add rspamd-service-type. (was
 [bug#61740] [PATCH] services: Add rspamd-service-type.)
Date: Fri, 8 Dec 2023 10:17:21 +0200
[Message part 1 (text/plain, inline)]
On Wed, Dec 06, 2023 at 02:58:19PM +0000, Bruno Victal wrote:
> Hi Saku,
> 
> Some comments:
> 
> > +(define (directory-tree? xs)
> > +  (match xs
> > +    (((file-name file-like) ...)
> > +     (and (every string? file-name)
> > +          (every file-like? file-like)))
> > +    (_ #f)))
> 
> You can express this more compactly as:
> 
> --8<---------------cut here---------------start------------->8---
> (define directory-tree?
>   (match-lambda
>     ((((? string?) (? file-like?)) ...) #t)
>     (_ #f)))
> --8<---------------cut here---------------end--------------->8---

Done in v4.

> 
> > +  (user
> > +   (string "rspamd")
> > +   "The user to run rspamd as.")
> > +  (group
> > +   (string "rspamd")
> > +   "The group to run rspamd as.")
> 
> How about using user-account and user-group records instead? (see
> vnstat-service-type for an example)

Done in v4.

> 
> > +  (pid-file
> > +   (string "/var/run/rspamd/rspamd.pid")
> > +   "Where to store the PID file.")
> 
> Is it useful to expose this?

I don't know. It was there when I picked up this patch but I can't come
up with a case in which one would want to change it. Removed in v4.

> 
> 
> > +  (insecure?
> > +   (boolean #f)
> > +   "Ignore running workers as privileged users (insecure).")
> 
> To me it seems redundant to restate “(insecure)” in the description.

True. Removed in v4.

> 
> > +                     (make-forkexec-constructor
> > +                      (list #$rspamd "-c" #$config-file
> 
> I'd prefer the long-name --config over the shorter ones here.

Done in v4.

> > +                            "--var" (string-append "LOCAL_CONFDIR=" #$local-confdir)
> 
> Curiously I don't see this listed in the 'rspamd' manpage although
> it is on the 'rspamadm' one. Can you confirm whether this works
> and if so, report to upstream that their docs are missing this?

It does work; I've used it since before I submitted this patch. The
`--var` option is listed on `rspamd --help`. Unfortunately, Rspamd
tracks their issues on Github and I'd prefer not registering an account
there.

> > +     (service-extension profile-service-type
> > +                        (compose list rspamd-configuration-package))
> 
> What's the motivation for adding the rspamd package to the profile?

That was also there when I picked up this patch. I assume it is added to
the profile so that the `rspamadm` and `rspamc` programs are available
and compatible with the daemon. I don't have strong feelings about this
in either direction.

> > +(define %rspamd-os
> > +  (simple-operating-system
> > +   (service dhcp-client-service-type)
> > +   (service rspamd-service-type)))
> 
> Is 'dhcp-client-service-type' needed for this system test?
> I haven't tested it but it looks unnecessary to me.

It provides 'networking for the http test. Apparently the test wasn't
working yet anyway (I had no experience in Guix tests when I sent my
versions of the patch and just assumed that they were working in Thomas'
version). The tests are now fixed in v4.

> > +          ;; Check that we can access the web ui
> > +          (test-equal "http-get"
> > +            200
> > +            (begin
> > +              (let-values (((response text)
> > +                            (http-get "http://localhost:22668/"
> > +                                      #:decode-body? #t)))
> > +                (response-code response))))
> 
> IMO if you're only interested in the HTTP response code a http-head
> is the better option, unless the program handles those requests
> differently. Also, since 'text' isn't used you can simplify this to:
> 
> --8<---------------cut here---------------start------------->8---
> ;; Don't forget to remove the unused (srfi srfi-11) import.
> 
> (test-equal "Web UI is accessible"
>   200
>   (response-code (http-head "http://localhost:22668/")))
> --8<---------------cut here---------------end--------------->8---

Done in v4.

> > +          (test-assert "rspamd pid ready"
> > +            (marionette-eval
> > +             '(file-exists? "/var/run/rspamd/rspamd.pid")
> > +             marionette))
> 
> There's a procedure dedicated for this:
> 
> --8<---------------cut here---------------start------------->8---
> (test-assert "rspamd pid ready"
>   (wait-for-file #$(rspamd-configuration-pid-file (rspamd-configuration)) marionette)))
> --8<---------------cut here---------------end--------------->8---

Done in v4.

> > +(define %test-rspamd
> > +  (system-test
> > +   (name "rspamd")
> > +   (description "Send an email to a running rspamd server.")
> > +   (value (run-rspamd-test))))
> 
> I'd change the description to something like "Basic rspamd service test."
> as the current one is misleading.

Done in v4.
[Message part 2 (text/plain, inline)]
From 1a2a4378304e77ee6ac4823734b916c8810b0834 Mon Sep 17 00:00:00 2001
Message-ID: <1a2a4378304e77ee6ac4823734b916c8810b0834.1702023246.git.saku <at> laesvuori.fi>
From: Thomas Ieong <th.ieong <at> free.fr>
Date: Thu, 23 Feb 2023 21:16:14 +0100
Subject: [PATCH v4] services: Add rspamd-service-type.

* gnu/services/mail.scm (rspamd-service-type): New variable.
* gnu/tests/mail.scm (%test-rspamd): New variable.
* doc/guix.texi: Document it.

Co-authored-by: Saku Laesvuori <saku <at> laesvuori.fi>
Change-Id: I7196643f087ffe9fc91aab231b69d5ed8dc9d198
---
 doc/guix.texi         |  62 +++++++++++++
 gnu/services/mail.scm | 206 +++++++++++++++++++++++++++++++++++++++++-
 gnu/tests/mail.scm    |  74 ++++++++++++++-
 3 files changed, 340 insertions(+), 2 deletions(-)

diff --git a/doc/guix.texi b/doc/guix.texi
index f82bb99069..5875008ec3 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -119,6 +119,8 @@
 Copyright @copyright{} 2023 Zheng Junjie@*
 Copyright @copyright{} 2023 Brian Cully@*
 Copyright @copyright{} 2023 Felix Lechner@*
+Copyright @copyright{} 2023 Thomas Ieong@*
+Copyright @copyright{} 2023 Saku Laesvuori@*
 
 Permission is granted to copy, distribute and/or modify this document
 under the terms of the GNU Free Documentation License, Version 1.3 or
@@ -27393,6 +27395,66 @@ Mail Services
 @end table
 @end deftp
 
+@subsubheading Rspamd Service
+@cindex email
+@cindex spam
+
+@defvar rspamd-service-type
+This is the type of the @uref{https://rspamd.com/, Rspamd} filtering
+system whose value should be a @code{rspamd-configuration}.
+@end defvar
+
+@c %start of fragment
+
+@deftp {Data Type} rspamd-configuration
+Available @code{rspamd-configuration} fields are:
+
+@table @asis
+@item @code{package} (default: @code{rspamd}) (type: file-like)
+The package that provides rspamd.
+
+@item @code{config-file} (default: @code{%default-rspamd-config-file}) (type: file-like)
+File-like object of the configuration file to use.  By default all
+workers are enabled except fuzzy and they are binded to their usual
+ports, e.g localhost:11334, localhost:11333 and so on
+
+@item @code{local.d-files} (default: @code{()}) (type: directory-tree)
+Configuration files in local.d, provided as a list of two element lists
+where the first element is the filename and the second one is a
+file-like object.  Settings in these files will be merged with the
+defaults.
+
+@item @code{override.d-files} (default: @code{()}) (type: directory-tree)
+Configuration files in override.d, provided as a list of two element
+lists where the first element is the filename and the second one is a
+file-like object.  Settings in these files will override the defaults.
+
+@item @code{user} (default: @code{%default-rspamd-account}) (type: user-account)
+The user to run rspamd as.
+
+@item @code{group} (default: @code{%default-rspamd-group}) (type: user-group)
+The group to run rspamd as.
+
+@item @code{debug?} (default: @code{#f}) (type: boolean)
+Force debug output.
+
+@item @code{insecure?} (default: @code{#f}) (type: boolean)
+Ignore running workers as privileged users.
+
+@item @code{skip-template?} (default: @code{#f}) (type: boolean)
+Do not apply Jinja templates.
+
+@item @code{shepherd-requirements} (default: @code{(loopback)}) (type: list-of-symbols)
+This is a list of symbols naming Shepherd services that this service
+will depend on.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
 @node Messaging Services
 @subsection Messaging Services
 
diff --git a/gnu/services/mail.scm b/gnu/services/mail.scm
index 12dcc8e71d..0ec0c43a4d 100644
--- a/gnu/services/mail.scm
+++ b/gnu/services/mail.scm
@@ -5,6 +5,8 @@
 ;;; Copyright © 2017, 2020 Tobias Geerinckx-Rice <me <at> tobias.gr>
 ;;; Copyright © 2019 Kristofer Buffington <kristoferbuffington <at> gmail.com>
 ;;; Copyright © 2020 Jonathan Brielmaier <jonathan.brielmaier <at> web.de>
+;;; Copyright © 2023 Thomas Ieong <th.ieong <at> free.fr>
+;;; Copyright © 2023 Saku Laesvuori <saku <at> laesvuori.fi>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -80,7 +82,13 @@ (define-module (gnu services mail)
             radicale-configuration
             radicale-configuration?
             radicale-service-type
-            %default-radicale-config-file))
+            %default-radicale-config-file
+
+            rspamd-configuration
+            rspamd-service-type
+            %default-rspamd-account
+            %default-rspamd-config-file
+            %default-rspamd-group))
 
 ;;; Commentary:
 ;;;
@@ -1987,3 +1995,199 @@ (define radicale-service-type
           (service-extension account-service-type (const %radicale-accounts))
           (service-extension activation-service-type radicale-activation)))
    (default-value (radicale-configuration))))
+
+;;;
+;;; Rspamd.
+;;;
+
+(define (directory-tree? xs)
+  (match xs
+    ((((? string?) (? file-like?)) ...) #t)
+    (_ #f)))
+
+(define (list-of-symbols? x)
+  (and (list? x)
+       (every symbol? x)))
+
+(define-configuration/no-serialization rspamd-configuration
+  (package
+   (file-like rspamd)
+   "The package that provides rspamd.")
+  (config-file
+   (file-like %default-rspamd-config-file)
+   "File-like object of the configuration file to use.  By default
+all workers are enabled except fuzzy and they are binded
+to their usual ports, e.g localhost:11334, localhost:11333 and so on")
+  (local.d-files
+   (directory-tree '())
+   "Configuration files in local.d, provided as a list of two element lists where
+the first element is the filename and the second one is a file-like object.  Settings
+in these files will be merged with the defaults.")
+  (override.d-files
+   (directory-tree '())
+   "Configuration files in override.d, provided as a list of two element lists where
+the first element is the filename and the second one is a file-like object.  Settings
+in these files will override the defaults.")
+  (user
+   (user-account %default-rspamd-account)
+   "The user to run rspamd as.")
+  (group
+   (user-group %default-rspamd-group)
+   "The group to run rspamd as.")
+  (debug?
+   (boolean #f)
+   "Force debug output.")
+  (insecure?
+   (boolean #f)
+   "Ignore running workers as privileged users.")
+  (skip-template?
+   (boolean #f)
+   "Do not apply Jinja templates.")
+  (shepherd-requirements
+   (list-of-symbols '(loopback))
+   "This is a list of symbols naming Shepherd services that this service
+will depend on."))
+
+(define %default-rspamd-account
+  (user-account
+      (name "rspamd")
+      (group "rspamd")
+      (system? #t)
+      (comment "Rspamd daemon")
+      (home-directory "/var/empty")
+      (shell (file-append shadow "/sbin/nologin"))))
+
+(define %default-rspamd-group
+  (user-group
+    (name "rspamd")
+    (system? #t)))
+
+(define %default-rspamd-config-file
+  (plain-file "rspamd.conf" "
+.include \"$CONFDIR/common.conf\"
+
+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 = \"file\";
+    filename = \"$LOGDIR/rspamd.log\";
+    .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\"
+}
+
+worker \"normal\" {
+    bind_socket = \"localhost:11333\";
+    .include \"$CONFDIR/worker-normal.inc\"
+    .include(try=true; priority=1,duplicate=merge) \"$LOCAL_CONFDIR/local.d/worker-normal.inc\"
+    .include(try=true; priority=10) \"$LOCAL_CONFDIR/override.d/worker-normal.inc\"
+}
+
+worker \"controller\" {
+    bind_socket = \"localhost:11334\";
+    .include \"$CONFDIR/worker-controller.inc\"
+    .include(try=true; priority=1,duplicate=merge) \"$LOCAL_CONFDIR/local.d/worker-controller.inc\"
+    .include(try=true; priority=10) \"$LOCAL_CONFDIR/override.d/worker-controller.inc\"
+}
+
+worker \"rspamd_proxy\" {
+    bind_socket = \"localhost:11332\";
+    .include \"$CONFDIR/worker-proxy.inc\"
+    .include(try=true; priority=1,duplicate=merge) \"$LOCAL_CONFDIR/local.d/worker-proxy.inc\"
+    .include(try=true; priority=10) \"$LOCAL_CONFDIR/override.d/worker-proxy.inc\"
+}
+
+# Local fuzzy storage is disabled by default
+
+worker \"fuzzy\" {
+    bind_socket = \"localhost:11335\";
+    count = -1; # Disable by default
+    .include \"$CONFDIR/worker-fuzzy.inc\"
+    .include(try=true; priority=1,duplicate=merge) \"$LOCAL_CONFDIR/local.d/worker-fuzzy.inc\"
+    .include(try=true; priority=10) \"$LOCAL_CONFDIR/override.d/worker-fuzzy.inc\"
+}
+"))
+
+(define (rspamd-accounts config)
+  (match-record config <rspamd-configuration>
+    (user group)
+    (list group user)))
+
+(define (rspamd-shepherd-service config)
+  (match-record config <rspamd-configuration>
+    (package config-file user group debug? insecure? skip-template?
+     local.d-files override.d-files shepherd-requirements)
+    (list
+     (shepherd-service
+      (provision '(rspamd))
+      (documentation "Run the rspamd daemon.")
+      (requirement shepherd-requirements)
+      (start (let ((rspamd (file-append package "/bin/rspamd"))
+                   (local-confdir
+                     (file-union
+                      "rspamd-local-confdir"
+                      `(("local.d" ,(file-union "local.d" local.d-files))
+                        ("override.d" ,(file-union "override.d" override.d-files))))))
+               (with-imported-modules (source-module-closure '((gnu build activation)))
+                 #~(begin
+                     (use-modules (gnu build activation)) ; for mkdir-p/perms
+                     (let ((user (getpwnam #$(user-account-name user))))
+                       (mkdir-p/perms "/var/run/rspamd" user #o755)
+                       (mkdir-p/perms "/var/log/rspamd" user #o755)
+                       (mkdir-p/perms "/var/lib/rspamd" user #o755))
+                     (make-forkexec-constructor
+                      (list #$rspamd "--config" #$config-file
+                            "--var" (string-append "LOCAL_CONFDIR=" #$local-confdir)
+                            "--no-fork"
+                            #$@(if debug?
+                                 '("--debug")
+                                 '())
+                            #$@(if insecure?
+                                 '("--insecure")
+                                 '())
+                            #$@(if skip-template?
+                                 '("--skip-template")
+                                 '()))
+                      #:user #$(user-account-name user)
+                      #:group #$(user-group-name group))))))
+      (stop #~(make-kill-destructor))
+      (actions
+       (list
+        (shepherd-configuration-action config-file)
+        (shepherd-action
+         (name 'reload)
+         (documentation "Reload rspamd.")
+         (procedure
+          #~(lambda (pid)
+              (if pid
+                (begin
+                  (kill pid SIGHUP)
+                  (display "Service rspamd has been reloaded"))
+                (format #t "Service rspamd is not running.")))))
+        (shepherd-action
+         (name 'reopenlog)
+         (documentation "Reopen log files.")
+         (procedure
+          #~(lambda (pid)
+              (if pid
+                (begin
+                  (kill pid SIGUSR1)
+                  (display "Reopening the logs for rspamd"))
+                (format #t "Service rspamd is not running.")))))))))))
+
+(define rspamd-service-type
+  (service-type
+   (name 'rspamd)
+   (description "Run the rapid spam filtering system.")
+   (extensions
+    (list
+     (service-extension shepherd-root-service-type rspamd-shepherd-service)
+     (service-extension account-service-type rspamd-accounts)
+     (service-extension profile-service-type
+                        (compose list rspamd-configuration-package))))
+   (default-value (rspamd-configuration))))
diff --git a/gnu/tests/mail.scm b/gnu/tests/mail.scm
index dcb8f08ea8..fc1c69047b 100644
--- a/gnu/tests/mail.scm
+++ b/gnu/tests/mail.scm
@@ -6,6 +6,7 @@
 ;;; Copyright © 2018 Clément Lassieur <clement <at> lassieur.org>
 ;;; Copyright © 2019 Christopher Baines <mail <at> cbaines.net>
 ;;; Copyright © 2019, 2020 Tobias Geerinckx-Rice <me <at> tobias.gr>
+;;; Copyright © 2023 Thomas Ieong <th.ieong <at> free.fr>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -40,7 +41,8 @@ (define-module (gnu tests mail)
   #:export (%test-opensmtpd
             %test-exim
             %test-dovecot
-            %test-getmail))
+            %test-getmail
+            %test-rspamd))
 
 (define %opensmtpd-os
   (simple-operating-system
@@ -579,3 +581,73 @@ (define %test-getmail
    (name "getmail")
    (description "Connect to a running Getmail server.")
    (value (run-getmail-test))))
+
+(define %rspamd-os
+  (simple-operating-system
+   (service dhcp-client-service-type)
+   (service rspamd-service-type
+            (rspamd-configuration
+              (shepherd-requirements '(networking))
+              (local.d-files `(("worker-controller.inc"
+                                ,(plain-file
+                                   "rspamd-public-web-controller.conf"
+                                   "bind_socket = \"0.0.0.0:11334\";"))))))))
+
+(define (run-rspamd-test)
+  "Return a test of an OS running Rspamd service."
+
+  (define rspamd-ports
+    '((22668 . 11334)))    ;; web controller
+
+  (define vm
+    (virtual-machine
+     (operating-system (marionette-operating-system
+                        %rspamd-os
+                        #:imported-modules '((gnu services herd))))
+     (port-forwardings rspamd-ports)))
+
+  (define test
+    (with-imported-modules '((gnu build marionette))
+      #~(begin
+          (use-modules (srfi srfi-64)
+                       (gnu build marionette)
+                       (web uri)
+                       (web client)
+                       (web response))
+
+          (define marionette
+            (make-marionette '(#$vm)))
+
+          (test-runner-current (system-test-runner #$output))
+          (test-begin "rspamd")
+
+          (test-assert "service is running"
+            (marionette-eval
+             '(begin
+                (use-modules (gnu services herd))
+                (start-service 'rspamd))
+             marionette))
+
+          (test-assert "rspamd socket ready"
+            (wait-for-unix-socket
+             "/var/lib/rspamd/rspamd.sock"
+             marionette))
+
+          (test-assert "rspamd log file"
+            (wait-for-file "/var/log/rspamd/rspamd.log" marionette))
+
+          ;; Check that we can access the web ui
+
+          (test-equal "http-get"
+            200
+            (response-code (http-get "http://localhost:22668/"))) ; HEAD is unsupported
+
+          (test-end))))
+
+  (gexp->derivation "rspamd-test" test))
+
+(define %test-rspamd
+  (system-test
+   (name "rspamd")
+   (description "Basic rspamd service test.")
+   (value (run-rspamd-test))))

base-commit: ea88bef3e0579264b20fa8edbf059c02d9cbe104
prerequisite-patch-id: 6b143a0f0a9c696e5214b42bb7928cf2abd7fc52
-- 
2.41.0

[signature.asc (application/pgp-signature, inline)]

Information forwarded to guix-patches <at> gnu.org:
bug#61740; Package guix-patches. (Mon, 11 Dec 2023 19:21:02 GMT) Full text and rfc822 format available.

Message #34 received at 61740 <at> debbugs.gnu.org (full text, mbox):

From: Bruno Victal <mirai <at> makinata.eu>
To: Saku Laesvuori <saku <at> laesvuori.fi>
Cc: Thomas Ieong <th.ieong <at> free.fr>,
 Ludovic Courtès <ludo <at> gnu.org>,
 Felix Lechner <felix.lechner <at> lease-up.com>, 61740 <at> debbugs.gnu.org
Subject: Re: [bug#61740] [PATCH v4] services: Add rspamd-service-type. (was
 [bug#61740] [PATCH v3] services: Add rspamd-service-type.)
Date: Mon, 11 Dec 2023 19:19:38 +0000
[Message part 1 (text/plain, inline)]
Hi Saku,

On 2023-12-08 08:17, Saku Laesvuori wrote:
> On Wed, Dec 06, 2023 at 02:58:19PM +0000, Bruno Victal wrote:
>> On 2023-09-16 21:10, Saku Laesvuori wrote:
>>> +                            "--var" (string-append "LOCAL_CONFDIR=" #$local-confdir)
>>
>> Curiously I don't see this listed in the 'rspamd' manpage although
>> it is on the 'rspamadm' one. Can you confirm whether this works
>> and if so, report to upstream that their docs are missing this?
> 
> It does work; I've used it since before I submitted this patch. The
> `--var` option is listed on `rspamd --help`. Unfortunately, Rspamd
> tracks their issues on Github and I'd prefer not registering an account
> there.

Forwarded with [1].

>>> +     (service-extension profile-service-type
>>> +                        (compose list rspamd-configuration-package))
>>
>> What's the motivation for adding the rspamd package to the profile?
> 
> That was also there when I picked up this patch. I assume it is added to
> the profile so that the `rspamadm` and `rspamc` programs are available
> and compatible with the daemon. I don't have strong feelings about this
> in either direction.

I think it's better to omit this, users who are interested in the tools
can use 'guix shell rspamd'.

> +(define (list-of-symbols? x)
> +  (and (list? x)
> +       (every symbol? x)))

list-of-symbols? is already defined in (gnu services configuration),
you can omit this.

> +        (shepherd-action
> +         (name 'reopenlog)
> +         (documentation "Reopen log files.")

Missed this in my previous reply, I'd prefer naming this action as
'reopen instead.

> +(define %rspamd-os
> +  (simple-operating-system
> +   (service dhcp-client-service-type)

[…]

> +   (service rspamd-service-type
> +            (rspamd-configuration
> +              (shepherd-requirements '(networking))
> +              (local.d-files `(("worker-controller.inc"
> +                                ,(plain-file
> +                                   "rspamd-public-web-controller.conf"
> +                                   "bind_socket = \"0.0.0.0:11334\";"))))))))

I wonder if you could remove dhcp-client-service-type and use the
loopback device for this test instead, by binding to '[::1]' or '127.0.0.1'.
(You don't need to add %loopback-static-networking here since it is already
included in %base-services.)

> +(define (run-rspamd-test)
> +  "Return a test of an OS running Rspamd service."
> +
> +  (define rspamd-ports
> +    '((22668 . 11334)))    ;; web controller

[…]

> +
> +  (define vm
> +    (virtual-machine
> +     (operating-system (marionette-operating-system
> +                        %rspamd-os
> +                        #:imported-modules '((gnu services herd))))
> +     (port-forwardings rspamd-ports)))

[…]

> +          ;; Check that we can access the web ui
> +
> +          (test-equal "http-get"
> +            200
> +            (response-code (http-get "http://localhost:22668/"))) ; HEAD is unsupported

Actually I've realized that these port-forwards are unnecessary
and it would be better to instead do:

--8<---------------cut here---------------start------------->8---
;; Note: remove (web client) and (web response) in the imports above
;; i.e. after the #~(begin (use-modules …

(test-equal "http-get"
  200
  (marionette-eval
   '(begin
      (use-modules (web client)
                   (web response))
      (response-code (http-head "http://localhost:11334/")))
   marionette))
--8<---------------cut here---------------end--------------->8---


[1]: <https://github.com/rspamd/rspamd/issues/4736>

-- 
Furthermore, I consider that nonfree software must be eradicated.

Cheers,
Bruno.
[OpenPGP_signature.asc (application/pgp-signature, attachment)]

Information forwarded to guix-patches <at> gnu.org:
bug#61740; Package guix-patches. (Tue, 12 Dec 2023 07:00:02 GMT) Full text and rfc822 format available.

Message #37 received at 61740 <at> debbugs.gnu.org (full text, mbox):

From: Saku Laesvuori <saku <at> laesvuori.fi>
To: Bruno Victal <mirai <at> makinata.eu>
Cc: Thomas Ieong <th.ieong <at> free.fr>,
 Ludovic Courtès <ludo <at> gnu.org>,
 Felix Lechner <felix.lechner <at> lease-up.com>, 61740 <at> debbugs.gnu.org
Subject: [bug#61740] [PATCH v5] services: Add rspamd-service-type (was [PATCH
 v4] services: Add rspamd-service-type.)
Date: Tue, 12 Dec 2023 08:58:41 +0200
[Message part 1 (text/plain, inline)]
> >>> +     (service-extension profile-service-type
> >>> +                        (compose list rspamd-configuration-package))
> >>
> >> What's the motivation for adding the rspamd package to the profile?
> > 
> > That was also there when I picked up this patch. I assume it is added to
> > the profile so that the `rspamadm` and `rspamc` programs are available
> > and compatible with the daemon. I don't have strong feelings about this
> > in either direction.
> 
> I think it's better to omit this, users who are interested in the tools
> can use 'guix shell rspamd'.

Yes, except if the rspamd package in the configuration is incompatible
with the one in the user's Guix. Anyway, this is now removed in v5.

> > +(define (list-of-symbols? x)
> > +  (and (list? x)
> > +       (every symbol? x)))
> 
> list-of-symbols? is already defined in (gnu services configuration),
> you can omit this.

Done after a rebase in v5. Apparently it also defines some other useful
functions that I didn't know about.

> > +        (shepherd-action
> > +         (name 'reopenlog)
> > +         (documentation "Reopen log files.")
> 
> Missed this in my previous reply, I'd prefer naming this action as
> 'reopen instead.

Done in v5.

> > +(define %rspamd-os
> > +  (simple-operating-system
> > +   (service dhcp-client-service-type)
> 
> […]
> 
> > +   (service rspamd-service-type
> > +            (rspamd-configuration
> > +              (shepherd-requirements '(networking))
> > +              (local.d-files `(("worker-controller.inc"
> > +                                ,(plain-file
> > +                                   "rspamd-public-web-controller.conf"
> > +                                   "bind_socket = \"0.0.0.0:11334\";"))))))))
> 
> I wonder if you could remove dhcp-client-service-type and use the
> loopback device for this test instead, by binding to '[::1]' or '127.0.0.1'.
> (You don't need to add %loopback-static-networking here since it is already
> included in %base-services.)

I tried that but it didn't work because the vm does not have an IP route
configured to the host...

> 
> > +(define (run-rspamd-test)
> > +  "Return a test of an OS running Rspamd service."
> > +
> > +  (define rspamd-ports
> > +    '((22668 . 11334)))    ;; web controller
> 
> […]
> 
> > +
> > +  (define vm
> > +    (virtual-machine
> > +     (operating-system (marionette-operating-system
> > +                        %rspamd-os
> > +                        #:imported-modules '((gnu services herd))))
> > +     (port-forwardings rspamd-ports)))
> 
> […]
> 
> > +          ;; Check that we can access the web ui
> > +
> > +          (test-equal "http-get"
> > +            200
> > +            (response-code (http-get "http://localhost:22668/"))) ; HEAD is unsupported
> 
> Actually I've realized that these port-forwards are unnecessary
> and it would be better to instead do:
> 
> --8<---------------cut here---------------start------------->8---
> ;; Note: remove (web client) and (web response) in the imports above
> ;; i.e. after the #~(begin (use-modules …
> 
> (test-equal "http-get"
>   200
>   (marionette-eval
>    '(begin
>       (use-modules (web client)
>                    (web response))
>       (response-code (http-head "http://localhost:11334/")))
>    marionette))
> --8<---------------cut here---------------end--------------->8---

... However with changes like these it does work in v5.
[Message part 2 (text/plain, inline)]
From 7aee03ff1a7ada82436ff424fe7fdbac117fbf29 Mon Sep 17 00:00:00 2001
Message-ID: <7aee03ff1a7ada82436ff424fe7fdbac117fbf29.1702364088.git.saku <at> laesvuori.fi>
From: Thomas Ieong <th.ieong <at> free.fr>
Date: Thu, 23 Feb 2023 21:16:14 +0100
Subject: [PATCH v5] services: Add rspamd-service-type.

* gnu/services/mail.scm (rspamd-service-type): New variable.
* gnu/tests/mail.scm (%test-rspamd): New variable.
* doc/guix.texi: Document it.

Co-authored-by: Saku Laesvuori <saku <at> laesvuori.fi>
Change-Id: I7196643f087ffe9fc91aab231b69d5ed8dc9d198
---
 doc/guix.texi         |  62 +++++++++++++
 gnu/services/mail.scm | 200 +++++++++++++++++++++++++++++++++++++++++-
 gnu/tests/mail.scm    |  67 +++++++++++++-
 3 files changed, 327 insertions(+), 2 deletions(-)

diff --git a/doc/guix.texi b/doc/guix.texi
index 3ad8508a32..9dabd51f87 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -120,6 +120,8 @@
 Copyright @copyright{} 2023 Brian Cully@*
 Copyright @copyright{} 2023 Felix Lechner@*
 Copyright @copyright{} 2023 Foundation Devices, Inc.@*
+Copyright @copyright{} 2023 Thomas Ieong@*
+Copyright @copyright{} 2023 Saku Laesvuori@*
 
 Permission is granted to copy, distribute and/or modify this document
 under the terms of the GNU Free Documentation License, Version 1.3 or
@@ -27617,6 +27619,66 @@ Mail Services
 @end table
 @end deftp
 
+@subsubheading Rspamd Service
+@cindex email
+@cindex spam
+
+@defvar rspamd-service-type
+This is the type of the @uref{https://rspamd.com/, Rspamd} filtering
+system whose value should be a @code{rspamd-configuration}.
+@end defvar
+
+@c %start of fragment
+
+@deftp {Data Type} rspamd-configuration
+Available @code{rspamd-configuration} fields are:
+
+@table @asis
+@item @code{package} (default: @code{rspamd}) (type: file-like)
+The package that provides rspamd.
+
+@item @code{config-file} (default: @code{%default-rspamd-config-file}) (type: file-like)
+File-like object of the configuration file to use.  By default all
+workers are enabled except fuzzy and they are binded to their usual
+ports, e.g localhost:11334, localhost:11333 and so on
+
+@item @code{local.d-files} (default: @code{()}) (type: directory-tree)
+Configuration files in local.d, provided as a list of two element lists
+where the first element is the filename and the second one is a
+file-like object.  Settings in these files will be merged with the
+defaults.
+
+@item @code{override.d-files} (default: @code{()}) (type: directory-tree)
+Configuration files in override.d, provided as a list of two element
+lists where the first element is the filename and the second one is a
+file-like object.  Settings in these files will override the defaults.
+
+@item @code{user} (default: @code{%default-rspamd-account}) (type: user-account)
+The user to run rspamd as.
+
+@item @code{group} (default: @code{%default-rspamd-group}) (type: user-group)
+The group to run rspamd as.
+
+@item @code{debug?} (default: @code{#f}) (type: boolean)
+Force debug output.
+
+@item @code{insecure?} (default: @code{#f}) (type: boolean)
+Ignore running workers as privileged users.
+
+@item @code{skip-template?} (default: @code{#f}) (type: boolean)
+Do not apply Jinja templates.
+
+@item @code{shepherd-requirements} (default: @code{(loopback)}) (type: list-of-symbols)
+This is a list of symbols naming Shepherd services that this service
+will depend on.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
 @node Messaging Services
 @subsection Messaging Services
 
diff --git a/gnu/services/mail.scm b/gnu/services/mail.scm
index 12dcc8e71d..afe1bb6016 100644
--- a/gnu/services/mail.scm
+++ b/gnu/services/mail.scm
@@ -5,6 +5,8 @@
 ;;; Copyright © 2017, 2020 Tobias Geerinckx-Rice <me <at> tobias.gr>
 ;;; Copyright © 2019 Kristofer Buffington <kristoferbuffington <at> gmail.com>
 ;;; Copyright © 2020 Jonathan Brielmaier <jonathan.brielmaier <at> web.de>
+;;; Copyright © 2023 Thomas Ieong <th.ieong <at> free.fr>
+;;; Copyright © 2023 Saku Laesvuori <saku <at> laesvuori.fi>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -80,7 +82,13 @@ (define-module (gnu services mail)
             radicale-configuration
             radicale-configuration?
             radicale-service-type
-            %default-radicale-config-file))
+            %default-radicale-config-file
+
+            rspamd-configuration
+            rspamd-service-type
+            %default-rspamd-account
+            %default-rspamd-config-file
+            %default-rspamd-group))
 
 ;;; Commentary:
 ;;;
@@ -1987,3 +1995,193 @@ (define radicale-service-type
           (service-extension account-service-type (const %radicale-accounts))
           (service-extension activation-service-type radicale-activation)))
    (default-value (radicale-configuration))))
+
+;;;
+;;; Rspamd.
+;;;
+
+(define (directory-tree? xs)
+  (match xs
+    ((((? string?) (? file-like?)) ...) #t)
+    (_ #f)))
+
+(define-configuration/no-serialization rspamd-configuration
+  (package
+   (file-like rspamd)
+   "The package that provides rspamd.")
+  (config-file
+   (file-like %default-rspamd-config-file)
+   "File-like object of the configuration file to use.  By default
+all workers are enabled except fuzzy and they are binded
+to their usual ports, e.g localhost:11334, localhost:11333 and so on")
+  (local.d-files
+   (directory-tree '())
+   "Configuration files in local.d, provided as a list of two element lists where
+the first element is the filename and the second one is a file-like object.  Settings
+in these files will be merged with the defaults.")
+  (override.d-files
+   (directory-tree '())
+   "Configuration files in override.d, provided as a list of two element lists where
+the first element is the filename and the second one is a file-like object.  Settings
+in these files will override the defaults.")
+  (user
+   (user-account %default-rspamd-account)
+   "The user to run rspamd as.")
+  (group
+   (user-group %default-rspamd-group)
+   "The group to run rspamd as.")
+  (debug?
+   (boolean #f)
+   "Force debug output.")
+  (insecure?
+   (boolean #f)
+   "Ignore running workers as privileged users.")
+  (skip-template?
+   (boolean #f)
+   "Do not apply Jinja templates.")
+  (shepherd-requirements
+   (list-of-symbols '(loopback))
+   "This is a list of symbols naming Shepherd services that this service
+will depend on."))
+
+(define %default-rspamd-account
+  (user-account
+      (name "rspamd")
+      (group "rspamd")
+      (system? #t)
+      (comment "Rspamd daemon")
+      (home-directory "/var/empty")
+      (shell (file-append shadow "/sbin/nologin"))))
+
+(define %default-rspamd-group
+  (user-group
+    (name "rspamd")
+    (system? #t)))
+
+(define %default-rspamd-config-file
+  (plain-file "rspamd.conf" "
+.include \"$CONFDIR/common.conf\"
+
+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 = \"file\";
+    filename = \"$LOGDIR/rspamd.log\";
+    .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\"
+}
+
+worker \"normal\" {
+    bind_socket = \"localhost:11333\";
+    .include \"$CONFDIR/worker-normal.inc\"
+    .include(try=true; priority=1,duplicate=merge) \"$LOCAL_CONFDIR/local.d/worker-normal.inc\"
+    .include(try=true; priority=10) \"$LOCAL_CONFDIR/override.d/worker-normal.inc\"
+}
+
+worker \"controller\" {
+    bind_socket = \"localhost:11334\";
+    .include \"$CONFDIR/worker-controller.inc\"
+    .include(try=true; priority=1,duplicate=merge) \"$LOCAL_CONFDIR/local.d/worker-controller.inc\"
+    .include(try=true; priority=10) \"$LOCAL_CONFDIR/override.d/worker-controller.inc\"
+}
+
+worker \"rspamd_proxy\" {
+    bind_socket = \"localhost:11332\";
+    .include \"$CONFDIR/worker-proxy.inc\"
+    .include(try=true; priority=1,duplicate=merge) \"$LOCAL_CONFDIR/local.d/worker-proxy.inc\"
+    .include(try=true; priority=10) \"$LOCAL_CONFDIR/override.d/worker-proxy.inc\"
+}
+
+# Local fuzzy storage is disabled by default
+
+worker \"fuzzy\" {
+    bind_socket = \"localhost:11335\";
+    count = -1; # Disable by default
+    .include \"$CONFDIR/worker-fuzzy.inc\"
+    .include(try=true; priority=1,duplicate=merge) \"$LOCAL_CONFDIR/local.d/worker-fuzzy.inc\"
+    .include(try=true; priority=10) \"$LOCAL_CONFDIR/override.d/worker-fuzzy.inc\"
+}
+"))
+
+(define (rspamd-accounts config)
+  (match-record config <rspamd-configuration>
+    (user group)
+    (list group user)))
+
+(define (rspamd-shepherd-service config)
+  (match-record config <rspamd-configuration>
+    (package config-file user group debug? insecure? skip-template?
+     local.d-files override.d-files shepherd-requirements)
+    (list
+     (shepherd-service
+      (provision '(rspamd))
+      (documentation "Run the rspamd daemon.")
+      (requirement shepherd-requirements)
+      (start (let ((rspamd (file-append package "/bin/rspamd"))
+                   (local-confdir
+                     (file-union
+                      "rspamd-local-confdir"
+                      `(("local.d" ,(file-union "local.d" local.d-files))
+                        ("override.d" ,(file-union "override.d" override.d-files))))))
+               (with-imported-modules (source-module-closure '((gnu build activation)))
+                 #~(begin
+                     (use-modules (gnu build activation)) ; for mkdir-p/perms
+                     (let ((user (getpwnam #$(user-account-name user))))
+                       (mkdir-p/perms "/var/run/rspamd" user #o755)
+                       (mkdir-p/perms "/var/log/rspamd" user #o755)
+                       (mkdir-p/perms "/var/lib/rspamd" user #o755))
+                     (make-forkexec-constructor
+                      (list #$rspamd "--config" #$config-file
+                            "--var" (string-append "LOCAL_CONFDIR=" #$local-confdir)
+                            "--no-fork"
+                            #$@(if debug?
+                                 '("--debug")
+                                 '())
+                            #$@(if insecure?
+                                 '("--insecure")
+                                 '())
+                            #$@(if skip-template?
+                                 '("--skip-template")
+                                 '()))
+                      #:user #$(user-account-name user)
+                      #:group #$(user-group-name group))))))
+      (stop #~(make-kill-destructor))
+      (actions
+       (list
+        (shepherd-configuration-action config-file)
+        (shepherd-action
+         (name 'reload)
+         (documentation "Reload rspamd.")
+         (procedure
+          #~(lambda (pid)
+              (if pid
+                (begin
+                  (kill pid SIGHUP)
+                  (display "Service rspamd has been reloaded"))
+                (format #t "Service rspamd is not running.")))))
+        (shepherd-action
+         (name 'reopen)
+         (documentation "Reopen log files.")
+         (procedure
+          #~(lambda (pid)
+              (if pid
+                (begin
+                  (kill pid SIGUSR1)
+                  (display "Reopening the logs for rspamd"))
+                (format #t "Service rspamd is not running.")))))))))))
+
+(define rspamd-service-type
+  (service-type
+   (name 'rspamd)
+   (description "Run the rapid spam filtering system.")
+   (extensions
+    (list
+     (service-extension shepherd-root-service-type rspamd-shepherd-service)
+     (service-extension account-service-type rspamd-accounts)))
+   (default-value (rspamd-configuration))))
diff --git a/gnu/tests/mail.scm b/gnu/tests/mail.scm
index dcb8f08ea8..176e7c1d07 100644
--- a/gnu/tests/mail.scm
+++ b/gnu/tests/mail.scm
@@ -6,6 +6,7 @@
 ;;; Copyright © 2018 Clément Lassieur <clement <at> lassieur.org>
 ;;; Copyright © 2019 Christopher Baines <mail <at> cbaines.net>
 ;;; Copyright © 2019, 2020 Tobias Geerinckx-Rice <me <at> tobias.gr>
+;;; Copyright © 2023 Thomas Ieong <th.ieong <at> free.fr>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -40,7 +41,8 @@ (define-module (gnu tests mail)
   #:export (%test-opensmtpd
             %test-exim
             %test-dovecot
-            %test-getmail))
+            %test-getmail
+            %test-rspamd))
 
 (define %opensmtpd-os
   (simple-operating-system
@@ -579,3 +581,66 @@ (define %test-getmail
    (name "getmail")
    (description "Connect to a running Getmail server.")
    (value (run-getmail-test))))
+
+(define %rspamd-os
+  (simple-operating-system
+   (service rspamd-service-type)))
+
+(define (run-rspamd-test)
+  "Return a test of an OS running Rspamd service."
+
+  (define vm
+    (virtual-machine
+      (marionette-operating-system
+        %rspamd-os
+        #:imported-modules '((gnu services herd)))))
+
+  (define test
+    (with-imported-modules '((gnu build marionette))
+      #~(begin
+          (use-modules (srfi srfi-64)
+                       (gnu build marionette))
+
+          (define marionette
+            (make-marionette '(#$vm)))
+
+          (test-runner-current (system-test-runner #$output))
+          (test-begin "rspamd")
+
+          (test-assert "service is running"
+            (marionette-eval
+             '(begin
+                (use-modules (gnu services herd))
+                (start-service 'rspamd))
+             marionette))
+
+          (test-assert "rspamd socket ready"
+            (wait-for-unix-socket
+             "/var/lib/rspamd/rspamd.sock"
+             marionette))
+
+          (test-assert "rspamd log file"
+            (wait-for-file "/var/log/rspamd/rspamd.log" marionette))
+
+          ;; Check that we can access the web ui
+
+          (test-equal "http-get"
+            200
+            (marionette-eval
+              '(begin
+                 (use-modules (web client)
+                              (web response))
+                 ;; HEAD returns 500 internal server error, so use GET even though
+                 ;; only the headers are relevant
+                 (response-code (http-get "http://localhost:11334")))
+              marionette))
+
+          (test-end))))
+
+  (gexp->derivation "rspamd-test" test))
+
+(define %test-rspamd
+  (system-test
+   (name "rspamd")
+   (description "Basic rspamd service test.")
+   (value (run-rspamd-test))))

base-commit: 06587003b896755f876ecd57b848e1d663fafb87
-- 
2.41.0

[signature.asc (application/pgp-signature, inline)]

Reply sent to Ludovic Courtès <ludo <at> gnu.org>:
You have taken responsibility. (Thu, 14 Dec 2023 22:10:01 GMT) Full text and rfc822 format available.

Notification sent to Thomas Ieong <th.ieong <at> free.fr>:
bug acknowledged by developer. (Thu, 14 Dec 2023 22:10:01 GMT) Full text and rfc822 format available.

Message #42 received at 61740-done <at> debbugs.gnu.org (full text, mbox):

From: Ludovic Courtès <ludo <at> gnu.org>
To: Saku Laesvuori <saku <at> laesvuori.fi>
Cc: Thomas Ieong <th.ieong <at> free.fr>, Bruno Victal <mirai <at> makinata.eu>,
 Felix Lechner <felix.lechner <at> lease-up.com>, 61740-done <at> debbugs.gnu.org
Subject: Re: [bug#61740] [PATCH v5] services: Add rspamd-service-type (was
 [PATCH v4] services: Add rspamd-service-type.)
Date: Thu, 14 Dec 2023 23:09:00 +0100
Hello,

Saku Laesvuori <saku <at> laesvuori.fi> skribis:

> * gnu/services/mail.scm (rspamd-service-type): New variable.
> * gnu/tests/mail.scm (%test-rspamd): New variable.
> * doc/guix.texi: Document it.
>
> Co-authored-by: Saku Laesvuori <saku <at> laesvuori.fi>
> Change-Id: I7196643f087ffe9fc91aab231b69d5ed8dc9d198

Finally applied.  Thanks a lot to everyone involved, great team work!

Ludo’.




bug archived. Request was from Debbugs Internal Request <help-debbugs <at> gnu.org> to internal_control <at> debbugs.gnu.org. (Fri, 12 Jan 2024 12:24:06 GMT) Full text and rfc822 format available.

This bug report was last modified 97 days ago.

Previous Next


GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997,2003 nCipher Corporation Ltd, 1994-97 Ian Jackson.