unofficial mirror of guix-patches@gnu.org 
 help / color / mirror / code / Atom feed
* [bug#61740] [PATCH] services: Add rspamd-service-type.
@ 2023-02-23 20:16 Thomas Ieong
  2023-02-25 21:33 ` Bruno Victal
                   ` (2 more replies)
  0 siblings, 3 replies; 12+ messages in thread
From: Thomas Ieong @ 2023-02-23 20:16 UTC (permalink / raw)
  To: 61740; +Cc: Thomas Ieong

* 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@tobias.gr>
 ;;; Copyright © 2019 Kristofer Buffington <kristoferbuffington@gmail.com>
 ;;; Copyright © 2020 Jonathan Brielmaier <jonathan.brielmaier@web.de>
+;;; Copyright © 2023 Thomas Ieong <th.ieong@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@lassieur.org>
 ;;; Copyright © 2019 Christopher Baines <mail@cbaines.net>
 ;;; Copyright © 2019, 2020 Tobias Geerinckx-Rice <me@tobias.gr>
+;;; Copyright © 2023 Thomas Ieong <th.ieong@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





^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [bug#61740] [PATCH] services: Add rspamd-service-type.
  2023-02-23 20:16 [bug#61740] [PATCH] services: Add rspamd-service-type Thomas Ieong
@ 2023-02-25 21:33 ` Bruno Victal
  2023-08-08 15:34   ` Ludovic Courtès
  2023-09-15 20:47 ` Felix Lechner via Guix-patches via
  2023-12-01  3:11 ` [bug#61740] [PATCH] services: Add rspamd-service-type Felix Lechner via Guix-patches via
  2 siblings, 1 reply; 12+ messages in thread
From: Bruno Victal @ 2023-02-25 21:33 UTC (permalink / raw)
  To: Thomas Ieong; +Cc: 61740

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




^ permalink raw reply	[flat|nested] 12+ messages in thread

* [bug#61740] [PATCH] services: Add rspamd-service-type.
  2023-02-25 21:33 ` Bruno Victal
@ 2023-08-08 15:34   ` Ludovic Courtès
  2023-09-05 19:06     ` Saku Laesvuori via Guix-patches via
  0 siblings, 1 reply; 12+ messages in thread
From: Ludovic Courtès @ 2023-08-08 15:34 UTC (permalink / raw)
  To: Thomas Ieong; +Cc: Bruno Victal, 61740

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@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




^ permalink raw reply	[flat|nested] 12+ messages in thread

* [bug#61740] [PATCH] services: Add rspamd-service-type.
  2023-08-08 15:34   ` Ludovic Courtès
@ 2023-09-05 19:06     ` Saku Laesvuori via Guix-patches via
  0 siblings, 0 replies; 12+ messages in thread
From: Saku Laesvuori via Guix-patches via @ 2023-09-05 19:06 UTC (permalink / raw)
  To: Ludovic Courtès; +Cc: Thomas Ieong, Bruno Victal, 61740


[-- Attachment #1.1: Type: text/plain, Size: 481 bytes --]

> 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.

[-- Attachment #1.2: v2-0001-services-Add-rspamd-service-type.patch --]
[-- Type: text/plain, Size: 15492 bytes --]

From 0de51c84aaccfa389276188cc617ddb6c05772f1 Mon Sep 17 00:00:00 2001
Message-ID: <0de51c84aaccfa389276188cc617ddb6c05772f1.1693939190.git.saku@laesvuori.fi>
From: Thomas Ieong <th.ieong@free.fr>
Date: Thu, 23 Feb 2023 21:16:14 +0100
Subject: [PATCH v2] 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@laesvuori.fi>
---
 doc/guix.texi         |  61 +++++++++++++
 gnu/services/mail.scm | 201 +++++++++++++++++++++++++++++++++++++++++-
 gnu/tests/mail.scm    |  87 +++++++++++++++++-
 3 files changed, 347 insertions(+), 2 deletions(-)

diff --git a/doc/guix.texi b/doc/guix.texi
index f82bb99069..04e4a60f97 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,65 @@ 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} (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{"rspamd"}) (type: string)
+The user to run rspamd as.
+
+@item @code{group} (default: @code{"rspamd"}) (type: string)
+The group to run rspamd as.
+
+@item @code{pid-file} (default: @code{"/var/run/rspamd/rspamd.pid"}) (type: string)
+Where to store the PID file.
+
+@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 (insecure).
+
+@item @code{skip-template?} (default: @code{#f}) (type: boolean)
+Do not apply Jinja templates.
+
+@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..43d39ecfe6 100644
--- a/gnu/services/mail.scm
+++ b/gnu/services/mail.scm
@@ -5,6 +5,8 @@
 ;;; Copyright © 2017, 2020 Tobias Geerinckx-Rice <me@tobias.gr>
 ;;; Copyright © 2019 Kristofer Buffington <kristoferbuffington@gmail.com>
 ;;; Copyright © 2020 Jonathan Brielmaier <jonathan.brielmaier@web.de>
+;;; Copyright © 2023 Thomas Ieong <th.ieong@free.fr>
+;;; Copyright © 2023 Saku Laesvuori <saku@laesvuori.fi>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -80,7 +82,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:
 ;;;
@@ -1987,3 +1993,196 @@ (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
+    (((file-name file-like) ...)
+     (and (every string? file-name)
+          (every file-like? file-like)))
+    (_ #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
+   (string "rspamd")
+   "The user to run rspamd as.")
+  (group
+   (string "rspamd")
+   "The group to run rspamd as.")
+  (pid-file
+   (string "/var/run/rspamd/rspamd.pid")
+   "Where to store the PID file.")
+  (debug?
+   (boolean #f)
+   "Force debug output.")
+  (insecure?
+   (boolean #f)
+   "Ignore running workers as privileged users (insecure).")
+  (skip-template?
+   (boolean #f)
+   "Do not apply Jinja templates."))
+
+(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?
+     local.d-files override.d-files)
+    (list
+     (shepherd-service
+      (provision '(rspamd))
+      (documentation "Run the rspamd daemon.")
+      (requirement '(networking))
+      (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))))))
+           #~(begin
+               (use-modules (guix build utils)
+                            (ice-9 match))
+               (let ((user (getpwnam #$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))
+                 ;; Check configuration file syntax.
+                 (system* (string-append #$package "/bin/rspamadm")
+                          "configtest"
+                          "-c" #$config-file)
+               (make-forkexec-constructor
+                (list #$rspamd "-c" #$config-file
+                      "--var" (string-append "LOCAL_CONFDIR=" #$local-confdir)
+                      "--no-fork"
+                      #$@(if debug?
+                           '("--debug")
+                           '())
+                      #$@(if insecure?
+                         '("--insecure")
+                         '())
+                      #$@(if skip-template?
+                           '("--skip-template")
+                           '()))
+              #:user #$user
+              #:group #$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..4dae6886b2 100644
--- a/gnu/tests/mail.scm
+++ b/gnu/tests/mail.scm
@@ -6,6 +6,7 @@
 ;;; Copyright © 2018 Clément Lassieur <clement@lassieur.org>
 ;;; Copyright © 2019 Christopher Baines <mail@cbaines.net>
 ;;; Copyright © 2019, 2020 Tobias Geerinckx-Rice <me@tobias.gr>
+;;; Copyright © 2023 Thomas Ieong <th.ieong@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,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: 2d4d147839b81ba8761c9e50cabe9b60025dc670
-- 
2.41.0


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [bug#61740] [PATCH] services: Add rspamd-service-type.
  2023-02-23 20:16 [bug#61740] [PATCH] services: Add rspamd-service-type Thomas Ieong
  2023-02-25 21:33 ` Bruno Victal
@ 2023-09-15 20:47 ` Felix Lechner via Guix-patches via
  2023-09-16 20:10   ` Saku Laesvuori via Guix-patches via
  2023-12-01  3:11 ` [bug#61740] [PATCH] services: Add rspamd-service-type Felix Lechner via Guix-patches via
  2 siblings, 1 reply; 12+ messages in thread
From: Felix Lechner via Guix-patches via @ 2023-09-15 20:47 UTC (permalink / raw)
  To: Saku Laesvuori; +Cc: Thomas Ieong, Bruno Victal, Ludovic Courtès, 61740

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




^ permalink raw reply	[flat|nested] 12+ messages in thread

* [bug#61740] [PATCH] services: Add rspamd-service-type.
  2023-09-15 20:47 ` Felix Lechner via Guix-patches via
@ 2023-09-16 20:10   ` Saku Laesvuori via Guix-patches via
  2023-12-06 14:58     ` [bug#61740] [PATCH v3] services: Add rspamd-service-type. (was [bug#61740] [PATCH] services: Add rspamd-service-type.) Bruno Victal
  0 siblings, 1 reply; 12+ messages in thread
From: Saku Laesvuori via Guix-patches via @ 2023-09-16 20:10 UTC (permalink / raw)
  To: Felix Lechner; +Cc: Thomas Ieong, Bruno Victal, Ludovic Courtès, 61740


[-- Attachment #1.1: Type: text/plain, Size: 637 bytes --]

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.

[-- Attachment #1.2: v3-0001-services-Add-rspamd-service-type.patch --]
[-- Type: text/plain, Size: 15745 bytes --]

From 886c32091bdc032c4180d490125a131df79b705c Mon Sep 17 00:00:00 2001
Message-ID: <886c32091bdc032c4180d490125a131df79b705c.1694894798.git.saku@laesvuori.fi>
From: Thomas Ieong <th.ieong@free.fr>
Date: Thu, 23 Feb 2023 21:16:14 +0100
Subject: [PATCH v3] 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@laesvuori.fi>
---
This version imports (gnu build activation) in the shepherd start action
and removes the syntax check, because I expect the service to fail
anyway if the configuration file syntax is invalid and the check seemed
to prevent booting (no idea why).

 doc/guix.texi         |  61 +++++++++++++
 gnu/services/mail.scm | 197 +++++++++++++++++++++++++++++++++++++++++-
 gnu/tests/mail.scm    |  87 ++++++++++++++++++-
 3 files changed, 343 insertions(+), 2 deletions(-)

diff --git a/doc/guix.texi b/doc/guix.texi
index f82bb99069..04e4a60f97 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,65 @@ 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} (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{"rspamd"}) (type: string)
+The user to run rspamd as.
+
+@item @code{group} (default: @code{"rspamd"}) (type: string)
+The group to run rspamd as.
+
+@item @code{pid-file} (default: @code{"/var/run/rspamd/rspamd.pid"}) (type: string)
+Where to store the PID file.
+
+@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 (insecure).
+
+@item @code{skip-template?} (default: @code{#f}) (type: boolean)
+Do not apply Jinja templates.
+
+@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..d8720907c8 100644
--- a/gnu/services/mail.scm
+++ b/gnu/services/mail.scm
@@ -5,6 +5,8 @@
 ;;; Copyright © 2017, 2020 Tobias Geerinckx-Rice <me@tobias.gr>
 ;;; Copyright © 2019 Kristofer Buffington <kristoferbuffington@gmail.com>
 ;;; Copyright © 2020 Jonathan Brielmaier <jonathan.brielmaier@web.de>
+;;; Copyright © 2023 Thomas Ieong <th.ieong@free.fr>
+;;; Copyright © 2023 Saku Laesvuori <saku@laesvuori.fi>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -80,7 +82,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:
 ;;;
@@ -1987,3 +1993,192 @@ (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
+    (((file-name file-like) ...)
+     (and (every string? file-name)
+          (every file-like? file-like)))
+    (_ #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
+   (string "rspamd")
+   "The user to run rspamd as.")
+  (group
+   (string "rspamd")
+   "The group to run rspamd as.")
+  (pid-file
+   (string "/var/run/rspamd/rspamd.pid")
+   "Where to store the PID file.")
+  (debug?
+   (boolean #f)
+   "Force debug output.")
+  (insecure?
+   (boolean #f)
+   "Ignore running workers as privileged users (insecure).")
+  (skip-template?
+   (boolean #f)
+   "Do not apply Jinja templates."))
+
+(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?
+     local.d-files override.d-files)
+    (list
+     (shepherd-service
+      (provision '(rspamd))
+      (documentation "Run the rspamd daemon.")
+      (requirement '(networking))
+      (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)))
+                       (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 "-c" #$config-file
+                            "--var" (string-append "LOCAL_CONFDIR=" #$local-confdir)
+                            "--no-fork"
+                            #$@(if debug?
+                                 '("--debug")
+                                 '())
+                            #$@(if insecure?
+                                 '("--insecure")
+                                 '())
+                            #$@(if skip-template?
+                                 '("--skip-template")
+                                 '()))
+                      #:user #$user
+                      #:group #$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..4dae6886b2 100644
--- a/gnu/tests/mail.scm
+++ b/gnu/tests/mail.scm
@@ -6,6 +6,7 @@
 ;;; Copyright © 2018 Clément Lassieur <clement@lassieur.org>
 ;;; Copyright © 2019 Christopher Baines <mail@cbaines.net>
 ;;; Copyright © 2019, 2020 Tobias Geerinckx-Rice <me@tobias.gr>
+;;; Copyright © 2023 Thomas Ieong <th.ieong@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,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: 2d4d147839b81ba8761c9e50cabe9b60025dc670
-- 
2.41.0


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [bug#61740] [PATCH] services: Add rspamd-service-type.
  2023-02-23 20:16 [bug#61740] [PATCH] services: Add rspamd-service-type Thomas Ieong
  2023-02-25 21:33 ` Bruno Victal
  2023-09-15 20:47 ` Felix Lechner via Guix-patches via
@ 2023-12-01  3:11 ` Felix Lechner via Guix-patches via
  2 siblings, 0 replies; 12+ messages in thread
From: Felix Lechner via Guix-patches via @ 2023-12-01  3:11 UTC (permalink / raw)
  To: 61740; +Cc: Thomas Ieong, Bruno Victal, Ludovic Courtès, Saku Laesvuori

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




^ permalink raw reply	[flat|nested] 12+ messages in thread

* [bug#61740] [PATCH v3] services: Add rspamd-service-type. (was [bug#61740] [PATCH] services: Add rspamd-service-type.)
  2023-09-16 20:10   ` Saku Laesvuori via Guix-patches via
@ 2023-12-06 14:58     ` Bruno Victal
  2023-12-08  8:17       ` Saku Laesvuori via Guix-patches via
  0 siblings, 1 reply; 12+ messages in thread
From: Bruno Victal @ 2023-12-06 14:58 UTC (permalink / raw)
  To: Saku Laesvuori, Thomas Ieong; +Cc: Ludovic Courtès, 61740, Felix Lechner


[-- Attachment #1.1: Type: text/plain, Size: 3814 bytes --]

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.


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [bug#61740] [PATCH v3] services: Add rspamd-service-type. (was [bug#61740] [PATCH] services: Add rspamd-service-type.)
  2023-12-06 14:58     ` [bug#61740] [PATCH v3] services: Add rspamd-service-type. (was [bug#61740] [PATCH] services: Add rspamd-service-type.) Bruno Victal
@ 2023-12-08  8:17       ` Saku Laesvuori via Guix-patches via
  2023-12-11 19:19         ` [bug#61740] [PATCH v4] services: Add rspamd-service-type. (was [bug#61740] [PATCH v3] " Bruno Victal
  0 siblings, 1 reply; 12+ messages in thread
From: Saku Laesvuori via Guix-patches via @ 2023-12-08  8:17 UTC (permalink / raw)
  To: Bruno Victal; +Cc: Thomas Ieong, Ludovic Courtès, Felix Lechner, 61740


[-- Attachment #1.1: Type: text/plain, Size: 4867 bytes --]

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.

[-- Attachment #1.2: Type: text/plain, Size: 15667 bytes --]

From 1a2a4378304e77ee6ac4823734b916c8810b0834 Mon Sep 17 00:00:00 2001
Message-ID: <1a2a4378304e77ee6ac4823734b916c8810b0834.1702023246.git.saku@laesvuori.fi>
From: Thomas Ieong <th.ieong@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@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@tobias.gr>
 ;;; Copyright © 2019 Kristofer Buffington <kristoferbuffington@gmail.com>
 ;;; Copyright © 2020 Jonathan Brielmaier <jonathan.brielmaier@web.de>
+;;; Copyright © 2023 Thomas Ieong <th.ieong@free.fr>
+;;; Copyright © 2023 Saku Laesvuori <saku@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@lassieur.org>
 ;;; Copyright © 2019 Christopher Baines <mail@cbaines.net>
 ;;; Copyright © 2019, 2020 Tobias Geerinckx-Rice <me@tobias.gr>
+;;; Copyright © 2023 Thomas Ieong <th.ieong@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


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [bug#61740] [PATCH v4] services: Add rspamd-service-type. (was [bug#61740] [PATCH v3] services: Add rspamd-service-type.)
  2023-12-08  8:17       ` Saku Laesvuori via Guix-patches via
@ 2023-12-11 19:19         ` Bruno Victal
  2023-12-12  6:58           ` [bug#61740] [PATCH v5] services: Add rspamd-service-type (was [PATCH v4] " Saku Laesvuori via Guix-patches via
  0 siblings, 1 reply; 12+ messages in thread
From: Bruno Victal @ 2023-12-11 19:19 UTC (permalink / raw)
  To: Saku Laesvuori; +Cc: Thomas Ieong, Ludovic Courtès, Felix Lechner, 61740


[-- Attachment #1.1: Type: text/plain, Size: 3796 bytes --]

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.

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [bug#61740] [PATCH v5] services: Add rspamd-service-type (was [PATCH v4] services: Add rspamd-service-type.)
  2023-12-11 19:19         ` [bug#61740] [PATCH v4] services: Add rspamd-service-type. (was [bug#61740] [PATCH v3] " Bruno Victal
@ 2023-12-12  6:58           ` Saku Laesvuori via Guix-patches via
  2023-12-14 22:09             ` bug#61740: " Ludovic Courtès
  0 siblings, 1 reply; 12+ messages in thread
From: Saku Laesvuori via Guix-patches via @ 2023-12-12  6:58 UTC (permalink / raw)
  To: Bruno Victal; +Cc: Thomas Ieong, Ludovic Courtès, Felix Lechner, 61740


[-- Attachment #1.1: Type: text/plain, Size: 3521 bytes --]

> >>> +     (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.

[-- Attachment #1.2: Type: text/plain, Size: 15044 bytes --]

From 7aee03ff1a7ada82436ff424fe7fdbac117fbf29 Mon Sep 17 00:00:00 2001
Message-ID: <7aee03ff1a7ada82436ff424fe7fdbac117fbf29.1702364088.git.saku@laesvuori.fi>
From: Thomas Ieong <th.ieong@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@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@tobias.gr>
 ;;; Copyright © 2019 Kristofer Buffington <kristoferbuffington@gmail.com>
 ;;; Copyright © 2020 Jonathan Brielmaier <jonathan.brielmaier@web.de>
+;;; Copyright © 2023 Thomas Ieong <th.ieong@free.fr>
+;;; Copyright © 2023 Saku Laesvuori <saku@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@lassieur.org>
 ;;; Copyright © 2019 Christopher Baines <mail@cbaines.net>
 ;;; Copyright © 2019, 2020 Tobias Geerinckx-Rice <me@tobias.gr>
+;;; Copyright © 2023 Thomas Ieong <th.ieong@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


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* bug#61740: [PATCH v5] services: Add rspamd-service-type (was [PATCH v4] services: Add rspamd-service-type.)
  2023-12-12  6:58           ` [bug#61740] [PATCH v5] services: Add rspamd-service-type (was [PATCH v4] " Saku Laesvuori via Guix-patches via
@ 2023-12-14 22:09             ` Ludovic Courtès
  0 siblings, 0 replies; 12+ messages in thread
From: Ludovic Courtès @ 2023-12-14 22:09 UTC (permalink / raw)
  To: Saku Laesvuori; +Cc: Thomas Ieong, Bruno Victal, Felix Lechner, 61740-done

Hello,

Saku Laesvuori <saku@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@laesvuori.fi>
> Change-Id: I7196643f087ffe9fc91aab231b69d5ed8dc9d198

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

Ludo’.




^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2023-12-14 22:10 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-02-23 20:16 [bug#61740] [PATCH] services: Add rspamd-service-type Thomas Ieong
2023-02-25 21:33 ` Bruno Victal
2023-08-08 15:34   ` Ludovic Courtès
2023-09-05 19:06     ` Saku Laesvuori via Guix-patches via
2023-09-15 20:47 ` Felix Lechner via Guix-patches via
2023-09-16 20:10   ` Saku Laesvuori via Guix-patches via
2023-12-06 14:58     ` [bug#61740] [PATCH v3] services: Add rspamd-service-type. (was [bug#61740] [PATCH] services: Add rspamd-service-type.) Bruno Victal
2023-12-08  8:17       ` Saku Laesvuori via Guix-patches via
2023-12-11 19:19         ` [bug#61740] [PATCH v4] services: Add rspamd-service-type. (was [bug#61740] [PATCH v3] " Bruno Victal
2023-12-12  6:58           ` [bug#61740] [PATCH v5] services: Add rspamd-service-type (was [PATCH v4] " Saku Laesvuori via Guix-patches via
2023-12-14 22:09             ` bug#61740: " Ludovic Courtès
2023-12-01  3:11 ` [bug#61740] [PATCH] services: Add rspamd-service-type Felix Lechner via Guix-patches via

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/guix.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).