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
Signed-off-by: Ludovic Courtès <ludo@gnu.org>
This commit is contained in:
Thomas Ieong 2023-02-23 21:16:14 +01:00 committed by Ludovic Courtès
parent 7ce0c79e7a
commit b681e339fa
No known key found for this signature in database
GPG Key ID: 090B11993D9AEBB5
3 changed files with 327 additions and 2 deletions

View File

@ -120,6 +120,8 @@ Copyright @copyright{} 2023 Zheng Junjie@*
Copyright @copyright{} 2023 Brian Cully@* Copyright @copyright{} 2023 Brian Cully@*
Copyright @copyright{} 2023 Felix Lechner@* Copyright @copyright{} 2023 Felix Lechner@*
Copyright @copyright{} 2023 Foundation Devices, Inc.@* 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 Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3 or under the terms of the GNU Free Documentation License, Version 1.3 or
@ -27639,6 +27641,66 @@ on TCP port 5232 of @code{localhost} and use the @code{htpasswd} file at
@end table @end table
@end deftp @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 @node Messaging Services
@subsection Messaging Services @subsection Messaging Services

View File

@ -5,6 +5,8 @@
;;; Copyright © 2017, 2020 Tobias Geerinckx-Rice <me@tobias.gr> ;;; Copyright © 2017, 2020 Tobias Geerinckx-Rice <me@tobias.gr>
;;; Copyright © 2019 Kristofer Buffington <kristoferbuffington@gmail.com> ;;; Copyright © 2019 Kristofer Buffington <kristoferbuffington@gmail.com>
;;; Copyright © 2020 Jonathan Brielmaier <jonathan.brielmaier@web.de> ;;; 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. ;;; This file is part of GNU Guix.
;;; ;;;
@ -80,7 +82,13 @@
radicale-configuration radicale-configuration
radicale-configuration? radicale-configuration?
radicale-service-type 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: ;;; Commentary:
;;; ;;;
@ -1987,3 +1995,193 @@ hosts = localhost:5232"))
(service-extension account-service-type (const %radicale-accounts)) (service-extension account-service-type (const %radicale-accounts))
(service-extension activation-service-type radicale-activation))) (service-extension activation-service-type radicale-activation)))
(default-value (radicale-configuration)))) (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))))

View File

@ -6,6 +6,7 @@
;;; Copyright © 2018 Clément Lassieur <clement@lassieur.org> ;;; Copyright © 2018 Clément Lassieur <clement@lassieur.org>
;;; Copyright © 2019 Christopher Baines <mail@cbaines.net> ;;; Copyright © 2019 Christopher Baines <mail@cbaines.net>
;;; Copyright © 2019, 2020 Tobias Geerinckx-Rice <me@tobias.gr> ;;; Copyright © 2019, 2020 Tobias Geerinckx-Rice <me@tobias.gr>
;;; Copyright © 2023 Thomas Ieong <th.ieong@free.fr>
;;; ;;;
;;; This file is part of GNU Guix. ;;; This file is part of GNU Guix.
;;; ;;;
@ -40,7 +41,8 @@
#:export (%test-opensmtpd #:export (%test-opensmtpd
%test-exim %test-exim
%test-dovecot %test-dovecot
%test-getmail)) %test-getmail
%test-rspamd))
(define %opensmtpd-os (define %opensmtpd-os
(simple-operating-system (simple-operating-system
@ -579,3 +581,66 @@ Subject: Hello Nice to meet you!")
(name "getmail") (name "getmail")
(description "Connect to a running Getmail server.") (description "Connect to a running Getmail server.")
(value (run-getmail-test)))) (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))))