projects/guix·blob python-team · refs

257 lines · 11.1 KiB

1;;; GNU Guix --- Functional package management for GNU
2;;; Copyright © 2017 Oleg Pykhalov <[email protected]>
3;;; Copyright © 2021, 2023, 2025 Ludovic Courtès <[email protected]>
4;;; Copyright © 2023 Maxim Cournoyer <[email protected]>
5;;;
6;;; This file is part of GNU Guix.
7;;;
8;;; GNU Guix is free software; you can redistribute it and/or modify it
9;;; under the terms of the GNU General Public License as published by
10;;; the Free Software Foundation; either version 3 of the License, or (at
11;;; your option) any later version.
12;;;
13;;; GNU Guix is distributed in the hope that it will be useful, but
14;;; WITHOUT ANY WARRANTY; without even the implied warranty of
15;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16;;; GNU General Public License for more details.
17;;;
18;;; You should have received a copy of the GNU General Public License
19;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
20
21;;;; Commentary:
22;;;
23;;; This module implements a service that to run instance of Rsync,
24;;; files synchronization tool.
25;;;
26;;;; Code:
27
28(define-module (gnu services rsync)
29 #:use-module ((gnu build linux-container) #:select (%namespaces))
30 #:use-module (gnu services)
31 #:use-module (gnu services base)
32 #:use-module (gnu services shepherd)
33 #:autoload (gnu system file-systems) (file-system-mapping)
34 #:use-module (gnu system shadow)
35 #:use-module (gnu packages admin)
36 #:use-module (gnu packages linux)
37 #:use-module (gnu packages rsync)
38 #:use-module (guix records)
39 #:use-module (guix gexp)
40 #:use-module (guix diagnostics)
41 #:use-module (guix i18n)
42 #:use-module (guix least-authority)
43 #:use-module (srfi srfi-1)
44 #:use-module (srfi srfi-26)
45 #:use-module (ice-9 match)
46 #:export (rsync-configuration
47 rsync-configuration?
48 rsync-configuration-modules
49
50 rsync-module
51 rsync-module?
52 rsync-module-name
53 rsync-module-file-name
54 rsync-module-comment
55 rsync-module-read-only
56 rsync-module-timeout
57
58 rsync-service-type))
59
60(define-record-type* <rsync-configuration>
61 rsync-configuration
62 make-rsync-configuration
63 rsync-configuration?
64 (package rsync-configuration-package ; file-like
65 (default rsync))
66 (address rsync-configuration-address ; string | #f
67 (default #f))
68 (port-number rsync-configuration-port-number ; integer
69 (default 873))
70 (pid-file rsync-configuration-pid-file ; string
71 (default "/var/run/rsyncd/rsyncd.pid"))
72 (lock-file rsync-configuration-lock-file ; string
73 (default "/var/run/rsyncd/rsyncd.lock"))
74 (log-file rsync-configuration-log-file ; string
75 (default "/var/log/rsyncd.log"))
76 (modules rsync-configuration-modules ;list of <rsync-module>
77 (default '()))
78 (user rsync-configuration-user ; string
79 (default "root"))
80 (group rsync-configuration-group ; string
81 (default "root"))
82 (uid rsync-configuration-uid ; string
83 (default "rsyncd"))
84 (gid rsync-configuration-gid ; string
85 (default "rsyncd")))
86
87;; Rsync "module": a directory exported the rsync protocol.
88(define-record-type* <rsync-module>
89 rsync-module make-rsync-module
90 rsync-module?
91 (name rsync-module-name) ;string
92 (file-name rsync-module-file-name) ;string
93 (comment rsync-module-comment ;string
94 (default ""))
95 (read-only? rsync-module-read-only? ;boolean
96 (default #t))
97 (chroot? rsync-module-chroot? ;boolean
98 (default #t))
99 (timeout rsync-module-timeout ;integer
100 (default 300)))
101
102(define (rsync-account config)
103 "Return the user accounts and user groups for CONFIG."
104 (let ((rsync-user (if (rsync-configuration-uid config)
105 (rsync-configuration-uid config)
106 (rsync-configuration-user config)))
107 (rsync-group (if (rsync-configuration-gid config)
108 (rsync-configuration-gid config)
109 (rsync-configuration-group config))))
110 (list (user-group (name rsync-group) (system? #t))
111 (user-account
112 (name rsync-user)
113 (system? #t)
114 (group rsync-group)
115 (comment "rsyncd privilege separation user")
116 (home-directory (string-append "/var/run/"
117 rsync-user))
118 (shell (file-append shadow "/sbin/nologin"))))))
119
120(define (rsync-activation config)
121 "Return the activation GEXP for CONFIG."
122 (with-imported-modules '((guix build utils))
123 #~(begin
124 (let ((user (getpw (if #$(rsync-configuration-uid config)
125 #$(rsync-configuration-uid config)
126 #$(rsync-configuration-user config))))
127 (group (getpw (if #$(rsync-configuration-gid config)
128 #$(rsync-configuration-gid config)
129 #$(rsync-configuration-group config)))))
130 (mkdir-p (dirname #$(rsync-configuration-pid-file config)))
131 (for-each (lambda (directory)
132 (mkdir-p directory)
133 (chown directory (passwd:uid user) (group:gid group)))
134 '#$(map rsync-module-file-name
135 (rsync-configuration-modules config)))))))
136
137(define (rsync-config-file config)
138 ;; Return the rsync configuration file corresponding to CONFIG.
139 (define (module-config module)
140 (match-record module <rsync-module>
141 (name file-name comment chroot? read-only? timeout)
142 (list "[" name "]\n"
143 " path = " file-name "\n"
144 " use chroot = " (if chroot? "true" "false") "\n"
145 " comment = " comment "\n"
146 " read only = " (if read-only? "true" "false") "\n"
147 " timeout = " (number->string timeout) "\n")))
148
149 (define modules
150 (rsync-configuration-modules config))
151
152 (match-record config <rsync-configuration>
153 (package address port-number pid-file lock-file log-file
154 user group uid gid)
155 (unless (string=? user "root")
156 (cond
157 ((<= port-number 1024)
158 (error (string-append "rsync-service: to run on port "
159 (number->string port-number)
160 ", user must be root.")))
161 ((find rsync-module-chroot? modules)
162 (error (string-append "rsync-service: to run in a chroot"
163 ", user must be root.")))
164 (uid
165 (error "rsync-service: to use uid, user must be root."))
166 (gid
167 (error "rsync-service: to use gid, user must be root."))))
168
169 (apply mixed-text-file "rsync.conf"
170 "# Generated by 'rsync-service'.\n\n"
171 "pid file = " pid-file "\n"
172 "lock file = " lock-file "\n"
173 "log file = " log-file "\n"
174 (if address (string-append "address = " address "\n") "")
175 "port = " (number->string port-number) "\n"
176 (if uid (string-append "uid = " uid "\n") "")
177 "gid = " (if gid gid "nogroup") "\n" ; no group nobody
178 "\n\n"
179 (append-map module-config modules))))
180
181(define (rsync-shepherd-service config)
182 "Return a <shepherd-service> for rsync with CONFIG."
183 (define ipv6-support?
184 #~(catch 'system-error
185 (lambda ()
186 (let ((sock (socket AF_INET6 SOCK_STREAM 0)))
187 (close-port sock)
188 #t))
189 (const #f)))
190
191 (define (module->file-system-mapping module)
192 "Return the <file-system-mapping> record corresponding to MODULE, an
193<rsync-module> object."
194 (match-record module <rsync-module>
195 (file-name read-only?)
196 (file-system-mapping
197 (source file-name)
198 (target source)
199 (writable? (not read-only?)))))
200
201 (match-record config <rsync-configuration>
202 (package log-file modules pid-file port-number user group)
203 ;; Run the rsync daemon in its own 'mnt' namespace, to guard against
204 ;; change to mount points it may be serving.
205 (let* ((config-file (rsync-config-file config))
206 (rsync-command #~(list #$(least-authority-wrapper
207 (file-append rsync "/bin/rsync")
208 #:name "rsync"
209 #:namespaces (fold delq %namespaces
210 '(net user))
211 #:mappings
212 (append (list (file-system-mapping
213 (source "/var/run/rsyncd")
214 (target source)
215 (writable? #t))
216 (file-system-mapping
217 (source (dirname log-file))
218 (target source)
219 (writable? #t))
220 (file-system-mapping
221 (source config-file)
222 (target source)))
223 (map module->file-system-mapping
224 modules)))
225 "--config" #$config-file "--daemon")))
226 (list (shepherd-service
227 (provision '(rsync))
228 (requirement '(user-processes))
229 (documentation "Run rsync daemon.")
230 (actions (list (shepherd-configuration-action config-file)))
231 (start #~(make-inetd-constructor
232 #$rsync-command
233 (cons (endpoint
234 (make-socket-address AF_INET INADDR_ANY
235 #$port-number))
236 (if #$ipv6-support?
237 (list
238 (endpoint
239 (make-socket-address AF_INET6 IN6ADDR_ANY
240 #$port-number)))
241 '()))
242 #:service-name-stem "rsync"
243 #:user #$user
244 #:group #$group))
245 (stop #~(make-inetd-destructor)))))))
246
247(define rsync-service-type
248 (service-type
249 (name 'rsync)
250 (extensions
251 (list (service-extension shepherd-root-service-type rsync-shepherd-service)
252 (service-extension account-service-type rsync-account)
253 (service-extension activation-service-type rsync-activation)))
254 (default-value (rsync-configuration))
255 (description
256 "Run the rsync file copying tool in daemon mode. This allows remote hosts
257to keep synchronized copies of the files exported by rsync.")))