services: rsync: Allow configuring several rsync "modules".

Until now the rsync service would export a single module, named
"files".  This allows users to specify as many modules as they want, in
line with rsyncd.conf(5).

* gnu/services/rsync.scm (warn-share-field-deprecation): New procedure.
(<rsync-configuration>)[modules]: New field.
[share-path, share-comment, read-only?, timeout]: Mark as deprecated.
(<rsync-module>): New record type.
(%default-modules): New variable.
(rsync-configuration-modules): New procedure.
(rsync-activation): Create the directory of each module.
(rsync-config-file): Generate configuration for each module.
(rsync-service-type)[description]: New field.
* doc/guix.texi (Networking Services): Adjust documentation.  Augment
example.
This commit is contained in:
Ludovic Courtès 2021-12-21 14:55:24 +01:00
parent 9834ff5d32
commit c9d92409d4
No known key found for this signature in database
GPG key ID: 090B11993D9AEBB5
2 changed files with 174 additions and 67 deletions

View file

@ -18059,7 +18059,17 @@ The value for this service type is a
@command{rsync-configuration} record as in this example: @command{rsync-configuration} record as in this example:
@lisp @lisp
(service rsync-service-type) ;; Export two directories over rsync. By default rsync listens on
;; all the network interfaces.
(service rsync-service-type
(rsync-configuration
(modules (list (rsync-module
(name "music")
(file-name "/srv/zik")
(read-only? #f))
(rsync-module
(name "movies")
(file-name "/home/charlie/movies"))))))
@end lisp @end lisp
See below for details about @code{rsync-configuration}. See below for details about @code{rsync-configuration}.
@ -18090,34 +18100,55 @@ Name of the file where @command{rsync} writes its lock file.
@item @code{log-file} (default: @code{"/var/log/rsyncd.log"}) @item @code{log-file} (default: @code{"/var/log/rsyncd.log"})
Name of the file where @command{rsync} writes its log file. Name of the file where @command{rsync} writes its log file.
@item @code{use-chroot?} (default: @var{#t}) @item @code{user} (default: @code{"root"})
Whether to use chroot for @command{rsync} shared directory.
@item @code{share-path} (default: @file{/srv/rsync})
Location of the @command{rsync} shared directory.
@item @code{share-comment} (default: @code{"Rsync share"})
Comment of the @command{rsync} shared directory.
@item @code{read-only?} (default: @var{#f})
Read-write permissions to shared directory.
@item @code{timeout} (default: @code{300})
I/O timeout in seconds.
@item @code{user} (default: @var{"root"})
Owner of the @code{rsync} process. Owner of the @code{rsync} process.
@item @code{group} (default: @var{"root"}) @item @code{group} (default: @code{"root"})
Group of the @code{rsync} process. Group of the @code{rsync} process.
@item @code{uid} (default: @var{"rsyncd"}) @item @code{uid} (default: @code{"rsyncd"})
User name or user ID that file transfers to and from that module should take User name or user ID that file transfers to and from that module should take
place as when the daemon was run as @code{root}. place as when the daemon was run as @code{root}.
@item @code{gid} (default: @var{"rsyncd"}) @item @code{gid} (default: @code{"rsyncd"})
Group name or group ID that will be used when accessing the module. Group name or group ID that will be used when accessing the module.
@item @code{modules} (default: @code{%default-modules})
List of ``modules''---i.e., directories exported over rsync. Each
element must be a @code{rsync-module} record, as described below.
@end table
@end deftp
@deftp {Data Type} rsync-module
This is the data type for rsync ``modules''. A module is a directory
exported over the rsync protocol. The available fields are as follows:
@table @asis
@item @code{name}
The module name. This is the name that shows up in URLs. For example,
if the module is called @code{music}, the corresponding URL will be
@code{rsync://host.example.org/music}.
@item @code{file-name}
Name of the directory being exported.
@item @code{comment} (default: @code{""})
Comment associated with the module. Client user interfaces may display
it when they obtain the list of available modules.
@item @code{read-only?} (default: @code{#t})
Whether or not client will be able to upload files. If this is false,
the uploads will be authorized if permissions on the daemon side permit
it.
@item @code{chroot?} (default: @code{#t})
When this is true, the rsync daemon changes root to the module's
directory before starting file transfers with the client. This improves
security, but requires rsync to run as root.
@item @code{timeout} (default: @code{300})
Idle time in seconds after which the daemon closes a connection with the
client.
@end table @end table
@end deftp @end deftp

View file

@ -1,5 +1,6 @@
;;; GNU Guix --- Functional package management for GNU ;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2017 Oleg Pykhalov <go.wigust@gmail.com> ;;; Copyright © 2017 Oleg Pykhalov <go.wigust@gmail.com>
;;; Copyright © 2021 Ludovic Courtès <ludo@gnu.org>
;;; ;;;
;;; This file is part of GNU Guix. ;;; This file is part of GNU Guix.
;;; ;;;
@ -25,11 +26,23 @@ (define-module (gnu services rsync)
#:use-module (gnu packages admin) #:use-module (gnu packages admin)
#:use-module (guix records) #:use-module (guix records)
#:use-module (guix gexp) #:use-module (guix gexp)
#:use-module (guix diagnostics)
#:use-module (guix i18n)
#:use-module (srfi srfi-1) #:use-module (srfi srfi-1)
#:use-module (srfi srfi-26) #:use-module (srfi srfi-26)
#:use-module (ice-9 match) #:use-module (ice-9 match)
#:export (rsync-configuration #:export (rsync-configuration
rsync-configuration? rsync-configuration?
rsync-configuration-modules
rsync-module
rsync-module?
rsync-module-name
rsync-module-file-name
rsync-module-comment
rsync-module-read-only
rsync-module-timeout
rsync-service-type)) rsync-service-type))
;;;; Commentary: ;;;; Commentary:
@ -39,6 +52,13 @@ (define-module (gnu services rsync)
;;; ;;;
;;;; Code: ;;;; Code:
(define-with-syntax-properties (warn-share-field-deprecation (value properties))
(unless (unspecified? value)
(warning (source-properties->location properties)
(G_ "the 'share-path' and 'share-comment' fields is deprecated, \
please use 'modules' instead~%")))
value)
(define-record-type* <rsync-configuration> (define-record-type* <rsync-configuration>
rsync-configuration rsync-configuration
make-rsync-configuration make-rsync-configuration
@ -56,15 +76,22 @@ (define-record-type* <rsync-configuration>
(log-file rsync-configuration-log-file ; string (log-file rsync-configuration-log-file ; string
(default "/var/log/rsyncd.log")) (default "/var/log/rsyncd.log"))
(use-chroot? rsync-configuration-use-chroot? ; boolean (use-chroot? rsync-configuration-use-chroot? ; boolean
(default #t)) (sanitize warn-share-field-deprecation)
(default *unspecified*))
(modules rsync-configuration-actual-modules ;list of <rsync-module>
(default %default-modules)) ;TODO: eventually remove default
(share-path rsync-configuration-share-path ; string (share-path rsync-configuration-share-path ; string
(default "/srv/rsyncd")) (sanitize warn-share-field-deprecation)
(default *unspecified*))
(share-comment rsync-configuration-share-comment ; string (share-comment rsync-configuration-share-comment ; string
(default "Rsync share")) (sanitize warn-share-field-deprecation)
(default *unspecified*))
(read-only? rsync-configuration-read-only? ; boolean (read-only? rsync-configuration-read-only? ; boolean
(default #f)) (sanitize warn-share-field-deprecation)
(default *unspecified*))
(timeout rsync-configuration-timeout ; integer (timeout rsync-configuration-timeout ; integer
(default 300)) (sanitize warn-share-field-deprecation)
(default *unspecified*))
(user rsync-configuration-user ; string (user rsync-configuration-user ; string
(default "root")) (default "root"))
(group rsync-configuration-group ; string (group rsync-configuration-group ; string
@ -74,6 +101,45 @@ (define-record-type* <rsync-configuration>
(gid rsync-configuration-gid ; string (gid rsync-configuration-gid ; string
(default "rsyncd"))) (default "rsyncd")))
;; Rsync "module": a directory exported the rsync protocol.
(define-record-type* <rsync-module>
rsync-module make-rsync-module
rsync-module?
(name rsync-module-name) ;string
(file-name rsync-module-file-name) ;string
(comment rsync-module-comment ;string
(default ""))
(read-only? rsync-module-read-only? ;boolean
(default #t))
(chroot? rsync-module-chroot? ;boolean
(default #t))
(timeout rsync-module-timeout ;integer
(default 300)))
(define %default-modules
;; Default modules, provided for backward compatibility.
(list (rsync-module (name "files")
(file-name "/srv/rsyncd")
(comment "Rsync share")
(read-only? #f)))) ;yes, that was the default
(define (rsync-configuration-modules config)
(match-record config <rsync-configuration>
(modules
share-path share-comment use-chroot? read-only? timeout) ;deprecated
(if (unspecified? share-path)
(rsync-configuration-actual-modules config)
(list (rsync-module ;backward compatibility
(name "files")
(file-name share-path)
(comment "Rsync share")
(chroot?
(if (unspecified? use-chroot?) #t use-chroot?))
(read-only?
(if (unspecified? read-only?) #f read-only?))
(timeout
(if (unspecified? timeout) 300 timeout)))))))
(define (rsync-account config) (define (rsync-account config)
"Return the user accounts and user groups for CONFIG." "Return the user accounts and user groups for CONFIG."
(let ((rsync-user (if (rsync-configuration-uid config) (let ((rsync-user (if (rsync-configuration-uid config)
@ -96,55 +162,62 @@ (define (rsync-activation config)
"Return the activation GEXP for CONFIG." "Return the activation GEXP for CONFIG."
(with-imported-modules '((guix build utils)) (with-imported-modules '((guix build utils))
#~(begin #~(begin
(let ((share-directory #$(rsync-configuration-share-path config)) (let ((user (getpw (if #$(rsync-configuration-uid config)
(user (getpw (if #$(rsync-configuration-uid config)
#$(rsync-configuration-uid config) #$(rsync-configuration-uid config)
#$(rsync-configuration-user config)))) #$(rsync-configuration-user config))))
(group (getpw (if #$(rsync-configuration-gid config) (group (getpw (if #$(rsync-configuration-gid config)
#$(rsync-configuration-gid config) #$(rsync-configuration-gid config)
#$(rsync-configuration-group config))))) #$(rsync-configuration-group config)))))
(mkdir-p (dirname #$(rsync-configuration-pid-file config))) (mkdir-p (dirname #$(rsync-configuration-pid-file config)))
(and=> share-directory mkdir-p) (for-each (lambda (directory)
(chown share-directory (mkdir-p directory)
(passwd:uid user) (chown directory (passwd:uid user) (group:gid group)))
(group:gid group)))))) '#$(map rsync-module-file-name
(rsync-configuration-modules config)))))))
(define rsync-config-file (define (rsync-config-file config)
;; Return the rsync configuration file corresponding to CONFIG. ;; Return the rsync configuration file corresponding to CONFIG.
(match-lambda (define (module-config module)
(($ <rsync-configuration> package address port-number pid-file lock-file log-file (match-record module <rsync-module>
use-chroot? share-path share-comment read-only? (name file-name comment chroot? read-only? timeout)
timeout user group uid gid) (list "[" name "]\n"
(if (not (string=? user "root")) " path = " file-name "\n"
(cond " use chroot = " (if chroot? "true" "false") "\n"
((<= port-number 1024) " comment = " comment "\n"
(error (string-append "rsync-service: to run on port " " read only = " (if read-only? "true" "false") "\n"
(number->string port-number) " timeout = " (number->string timeout) "\n")))
", user must be root.")))
(use-chroot? (define modules
(error (string-append "rsync-service: to run in a chroot" (rsync-configuration-modules config))
", user must be root.")))
(uid (match-record config <rsync-configuration>
(error "rsync-service: to use uid, user must be root.")) (package address port-number pid-file lock-file log-file
(gid user group uid gid)
(error "rsync-service: to use gid, user must be root.")))) (unless (string=? user "root")
(mixed-text-file (cond
"rsync.conf" ((<= port-number 1024)
"# Generated by 'rsync-service'.\n\n" (error (string-append "rsync-service: to run on port "
"pid file = " pid-file "\n" (number->string port-number)
"lock file = " lock-file "\n" ", user must be root.")))
"log file = " log-file "\n" ((find rsync-module-chroot? modules)
(if address (string-append "address = " address "\n") "") (error (string-append "rsync-service: to run in a chroot"
"port = " (number->string port-number) "\n" ", user must be root.")))
"use chroot = " (if use-chroot? "true" "false") "\n" (uid
(if uid (string-append "uid = " uid "\n") "") (error "rsync-service: to use uid, user must be root."))
"gid = " (if gid gid "nogroup") "\n" ; no group nobody (gid
"\n" (error "rsync-service: to use gid, user must be root."))))
"[files]\n"
"path = " share-path "\n" (apply mixed-text-file "rsync.conf"
"comment = " share-comment "\n" "# Generated by 'rsync-service'.\n\n"
"read only = " (if read-only? "true" "false") "\n" "pid file = " pid-file "\n"
"timeout = " (number->string timeout) "\n")))) "lock file = " lock-file "\n"
"log file = " log-file "\n"
(if address (string-append "address = " address "\n") "")
"port = " (number->string port-number) "\n"
(if uid (string-append "uid = " uid "\n") "")
"gid = " (if gid gid "nogroup") "\n" ; no group nobody
"\n\n"
(append-map module-config modules))))
(define (rsync-shepherd-service config) (define (rsync-shepherd-service config)
"Return a <shepherd-service> for rsync with CONFIG." "Return a <shepherd-service> for rsync with CONFIG."
@ -172,4 +245,7 @@ (define rsync-service-type
(list (service-extension shepherd-root-service-type rsync-shepherd-service) (list (service-extension shepherd-root-service-type rsync-shepherd-service)
(service-extension account-service-type rsync-account) (service-extension account-service-type rsync-account)
(service-extension activation-service-type rsync-activation))) (service-extension activation-service-type rsync-activation)))
(default-value (rsync-configuration)))) (default-value (rsync-configuration))
(description
"Run the rsync file copying tool in daemon mode. This allows remote hosts
to keep synchronized copies of the files exported by rsync.")))