projects/guix·blob python-team · refs

1333 lines · 54.4 KiB

1;;; GNU Guix --- Functional package management for GNU
2;;; Copyright © 2017 Julien Lepiller <[email protected]>
3;;; Copyright © 2020 Pierre Langlois <[email protected]>
4;;; Copyright © 2021 Maxime Devos <[email protected]>
5;;; Copyright © 2022 Remco van 't Veer <[email protected]>
6;;; Copyright © 2024 Sören Tempel <[email protected]>
7;;; Copyright © 2026 Maxim Cournoyer <[email protected]>
8;;;
9;;; This file is part of GNU Guix.
10;;;
11;;; GNU Guix is free software; you can redistribute it and/or modify it
12;;; under the terms of the GNU General Public License as published by
13;;; the Free Software Foundation; either version 3 of the License, or (at
14;;; your option) any later version.
15;;;
16;;; GNU Guix is distributed in the hope that it will be useful, but
17;;; WITHOUT ANY WARRANTY; without even the implied warranty of
18;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19;;; GNU General Public License for more details.
20;;;
21;;; You should have received a copy of the GNU General Public License
22;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
23
24(define-module (gnu services dns)
25 #:use-module (gnu services)
26 #:use-module (gnu services configuration)
27 #:use-module (gnu services dbus)
28 #:use-module (gnu services shepherd)
29 #:use-module (gnu system shadow)
30 #:use-module (gnu packages admin)
31 #:use-module (gnu packages dns)
32 #:autoload (guix least-authority)
33 (least-authority-wrapper
34 %default-preserved-environment-variables)
35 #:autoload (gnu system file-systems) (file-system-mapping)
36 #:autoload (gnu build linux-container) (%namespaces)
37 #:use-module (guix deprecation)
38 #:use-module (guix packages)
39 #:use-module (guix records)
40 #:use-module (guix gexp)
41 #:use-module (guix modules)
42 #:use-module (srfi srfi-1)
43 #:use-module (srfi srfi-26)
44 #:use-module (srfi srfi-34)
45 #:use-module (srfi srfi-35)
46 #:use-module (ice-9 match)
47 #:use-module (ice-9 regex)
48 #:export (knot-service-type
49 knot-acl-configuration
50 knot-key-configuration
51 knot-keystore-configuration
52 knot-zone-configuration
53 knot-remote-configuration
54 knot-policy-configuration
55 knot-configuration
56 define-zone-entries
57 zone-file
58 zone-entry
59
60 knot-resolver-service-type
61 knot-resolver-configuration
62
63 dnsmasq-service-type
64 dnsmasq-configuration
65 dnsmasq-configuration-package
66 dnsmasq-configuration-shepherd-provision
67 dnsmasq-configuration-shepherd-requirement
68 dnsmasq-configuration-pid-file
69 dnsmasq-configuration-no-hosts?
70 dnsmasq-configuration-port
71 dnsmasq-configuration-local-service?
72 dnsmasq-configuration-listen-address
73 dnsmasq-configuration-ipv4?
74 dnsmasq-configuration-ipv6?
75 dnsmasq-configuration-resolv-file
76 dnsmasq-configuration-no-resolv?
77 dnsmasq-configuration-authoritative-zones
78 dnsmasq-configuration-forward-private-reverse-lookup?
79 dnsmasq-configuration-query-servers-in-order?
80 dnsmasq-configuration-locals
81 dnsmasq-configuration-servers
82 dnsmasq-configuration-servers-file
83 dnsmasq-configuration-addresses
84 dnsmasq-configuration-cache-size
85 dnsmasq-configuration-negative-cache?
86 dnsmasq-configuration-cpe-id
87 dnsmasq-configuration-tftp-enable?
88 dnsmasq-configuration-tftp-no-fail?
89 dnsmasq-configuration-tftp-single-port?
90 dnsmasq-tftp-secure?
91 dnsmasq-tftp-max
92 dnsmasq-tftp-mtu
93 dnsmasq-tftp-no-blocksize?
94 dnsmasq-tftp-lowercase?
95 dnsmasq-tftp-port-range
96 dnsmasq-tftp-root
97 dnsmasq-tftp-unique-root
98 dnsmasq-configuration-conf-file
99 dnsmasq-configuration-conf-dir
100 dnsmasq-configuration-extra-options
101
102 unbound-service-type
103 unbound-configuration
104 unbound-configuration?
105 unbound-server
106 unbound-server?
107 unbound-zone
108 unbound-zone?
109 unbound-remote
110 unbound-remote?
111
112 ddclient-service-type
113 ddclient-configuration
114 ddclient-configuration?))
115
116;;;
117;;; Knot DNS.
118;;;
119
120(define-record-type* <knot-key-configuration>
121 knot-key-configuration make-knot-key-configuration
122 knot-key-configuration?
123 (id knot-key-configuration-id
124 (default ""))
125 (algorithm knot-key-configuration-algorithm
126 (default #f)); one of #f, or an algorithm name
127 (secret knot-key-configuration-secret
128 (default "")))
129
130(define-record-type* <knot-acl-configuration>
131 knot-acl-configuration make-knot-acl-configuration
132 knot-acl-configuration?
133 (id knot-acl-configuration-id
134 (default ""))
135 (address knot-acl-configuration-address
136 (default '()))
137 (key knot-acl-configuration-key
138 (default '()))
139 (action knot-acl-configuration-action
140 (default '()))
141 (deny? knot-acl-configuration-deny?
142 (default #f)))
143
144(define-record-type* <zone-entry>
145 zone-entry make-zone-entry
146 zone-entry?
147 (name zone-entry-name
148 (default "@"))
149 (ttl zone-entry-ttl
150 (default ""))
151 (class zone-entry-class
152 (default "IN"))
153 (type zone-entry-type
154 (default "A"))
155 (data zone-entry-data
156 (default "")))
157
158(define-record-type* <zone-file>
159 zone-file make-zone-file
160 zone-file?
161 (entries zone-file-entries
162 (default '()))
163 (origin zone-file-origin
164 (default ""))
165 (ns zone-file-ns
166 (default "ns"))
167 (mail zone-file-mail
168 (default "hostmaster"))
169 (serial zone-file-serial
170 (default 1))
171 (refresh zone-file-refresh
172 (default (* 12 3600)))
173 (retry zone-file-retry
174 (default (* 15 60)))
175 (expiry zone-file-expiry
176 (default (* 2 7 24 3600)))
177 (nx zone-file-nx
178 (default 3600)))
179(define-record-type* <knot-keystore-configuration>
180 knot-keystore-configuration make-knot-keystore-configuration
181 knot-keystore-configuration?
182 (id knot-keystore-configuration-id
183 (default ""))
184 (backend knot-keystore-configuration-backend
185 (default 'pem))
186 (config knot-keystore-configuration-config
187 (default "/var/lib/knot/keys/keys")))
188
189(define-record-type* <knot-policy-configuration>
190 knot-policy-configuration make-knot-policy-configuration
191 knot-policy-configuration?
192 (id knot-policy-configuration-id
193 (default ""))
194 (keystore knot-policy-configuration-keystore
195 (default "default"))
196 (manual? knot-policy-configuration-manual?
197 (default #f))
198 (single-type-signing? knot-policy-configuration-single-type-signing?
199 (default #f))
200 (algorithm knot-policy-configuration-algorithm
201 (default "ecdsap256sha256"))
202 (ksk-size knot-policy-configuration-ksk-size
203 (default 256))
204 (zsk-size knot-policy-configuration-zsk-size
205 (default 256))
206 (dnskey-ttl knot-policy-configuration-dnskey-ttl
207 (default 'default))
208 (zsk-lifetime knot-policy-configuration-zsk-lifetime
209 (default (* 30 24 3600)))
210 (propagation-delay knot-policy-configuration-propagation-delay
211 (default (* 24 3600)))
212 (rrsig-lifetime knot-policy-configuration-rrsig-lifetime
213 (default (* 14 24 3600)))
214 (rrsig-refresh knot-policy-configuration-rrsig-refresh
215 (default (* 7 24 3600)))
216 (nsec3? knot-policy-configuration-nsec3?
217 (default #f))
218 (nsec3-iterations knot-policy-configuration-nsec3-iterations
219 (default 5))
220 (nsec3-salt-length knot-policy-configuration-nsec3-salt-length
221 (default 8))
222 (nsec3-salt-lifetime knot-policy-configuration-nsec3-salt-lifetime
223 (default (* 30 24 3600))))
224
225(define-record-type* <knot-zone-configuration>
226 knot-zone-configuration make-knot-zone-configuration
227 knot-zone-configuration?
228 (domain knot-zone-configuration-domain
229 (default ""))
230 (file knot-zone-configuration-file
231 (default "")) ; the file where this zone is saved.
232 (zone knot-zone-configuration-zone
233 (default (zone-file))) ; initial content of the zone file
234 (master knot-zone-configuration-master
235 (default '()))
236 (ddns-master knot-zone-configuration-ddns-master
237 (default #f))
238 (notify knot-zone-configuration-notify
239 (default '()))
240 (acl knot-zone-configuration-acl
241 (default '()))
242 (semantic-checks? knot-zone-configuration-semantic-checks?
243 (default #f))
244 (zonefile-sync knot-zone-configuration-zonefile-sync
245 (default 0))
246 (zonefile-load knot-zone-configuration-zonefile-load
247 (default #f))
248 (journal-content knot-zone-configuration-journal-content
249 (default #f))
250 (max-journal-usage knot-zone-configuration-max-journal-usage
251 (default #f))
252 (max-journal-depth knot-zone-configuration-max-journal-depth
253 (default #f))
254 (max-zone-size knot-zone-configuration-max-zone-size
255 (default #f))
256 (dnssec-policy knot-zone-configuration-dnssec-policy
257 (default #f))
258 (serial-policy knot-zone-configuration-serial-policy
259 (default 'increment)))
260
261(define-record-type* <knot-remote-configuration>
262 knot-remote-configuration make-knot-remote-configuration
263 knot-remote-configuration?
264 (id knot-remote-configuration-id
265 (default ""))
266 (address knot-remote-configuration-address
267 (default '()))
268 (via knot-remote-configuration-via
269 (default '()))
270 (key knot-remote-configuration-key
271 (default #f)))
272
273(define-record-type* <knot-configuration>
274 knot-configuration make-knot-configuration
275 knot-configuration?
276 (knot knot-configuration-knot
277 (default knot))
278 (run-directory knot-configuration-run-directory
279 (default "/var/run/knot"))
280 (includes knot-configuration-includes
281 (default '()))
282 (listen-v4 knot-configuration-listen-v4
283 (default "0.0.0.0"))
284 (listen-v6 knot-configuration-listen-v6
285 (default "::"))
286 (listen-port knot-configuration-listen-port
287 (default 53))
288 (keys knot-configuration-keys
289 (default '()))
290 (keystores knot-configuration-keystores
291 (default '()))
292 (acls knot-configuration-acls
293 (default '()))
294 (remotes knot-configuration-remotes
295 (default '()))
296 (policies knot-configuration-policies
297 (default '()))
298 (zones knot-configuration-zones
299 (default '())))
300
301(define-syntax define-zone-entries
302 (syntax-rules ()
303 ((_ id (name ttl class type data) ...)
304 (define id (list (make-zone-entry name ttl class type data) ...)))))
305
306(define (error-out msg)
307 (raise (condition (&message (message msg)))))
308
309(define (verify-knot-key-configuration key)
310 (unless (knot-key-configuration? key)
311 (error-out "keys must be a list of only knot-key-configuration."))
312 (let ((id (knot-key-configuration-id key)))
313 (unless (and (string? id) (not (equal? id "")))
314 (error-out "key id must be a non empty string.")))
315 (unless (memq (knot-key-configuration-algorithm key)
316 '(#f hmac-md5 hmac-sha1 hmac-sha224 hmac-sha256 hmac-sha384 hmac-sha512))
317 (error-out "algorithm must be one of: #f, 'hmac-md5, 'hmac-sha1,
318'hmac-sha224, 'hmac-sha256, 'hmac-sha384 or 'hmac-sha512")))
319
320(define (verify-knot-keystore-configuration keystore)
321 (unless (knot-keystore-configuration? keystore)
322 (error-out "keystores must be a list of only knot-keystore-configuration."))
323 (let ((id (knot-keystore-configuration-id keystore)))
324 (unless (and (string? id) (not (equal? id "")))
325 (error-out "keystore id must be a non empty string.")))
326 (unless (memq (knot-keystore-configuration-backend keystore)
327 '(pem pkcs11))
328 (error-out "backend must be one of: 'pem or 'pkcs11")))
329
330(define (verify-knot-policy-configuration policy)
331 (unless (knot-policy-configuration? policy)
332 (error-out "policies must be a list of only knot-policy-configuration."))
333 (let ((id (knot-policy-configuration-id policy)))
334 (unless (and (string? id) (not (equal? id "")))
335 (error-out "policy id must be a non empty string."))))
336
337(define (verify-knot-acl-configuration acl)
338 (unless (knot-acl-configuration? acl)
339 (error-out "acls must be a list of only knot-acl-configuration."))
340 (let ((id (knot-acl-configuration-id acl))
341 (address (knot-acl-configuration-address acl))
342 (key (knot-acl-configuration-key acl))
343 (action (knot-acl-configuration-action acl)))
344 (unless (and (string? id) (not (equal? id "")))
345 (error-out "acl id must be a non empty string."))
346 (unless (and (list? address)
347 (every string? address))
348 (error-out "acl address must be a list of strings.")))
349 (unless (boolean? (knot-acl-configuration-deny? acl))
350 (error-out "deny? must be #t or #f.")))
351
352(define (verify-knot-zone-configuration zone)
353 (unless (knot-zone-configuration? zone)
354 (error-out "zones must be a list of only knot-zone-configuration."))
355 (let ((domain (knot-zone-configuration-domain zone)))
356 (unless (and (string? domain) (not (equal? domain "")))
357 (error-out "zone domain must be a non empty string."))))
358
359(define (verify-knot-remote-configuration remote)
360 (unless (knot-remote-configuration? remote)
361 (error-out "remotes must be a list of only knot-remote-configuration."))
362 (let ((id (knot-remote-configuration-id remote)))
363 (unless (and (string? id) (not (equal? id "")))
364 (error-out "remote id must be a non empty string."))))
365
366(define (verify-knot-configuration config)
367 (unless (file-like? (knot-configuration-knot config))
368 (error-out "knot configuration field must be a file-like object."))
369 (unless (string? (knot-configuration-run-directory config))
370 (error-out "run-directory must be a string."))
371 (unless (list? (knot-configuration-includes config))
372 (error-out "includes must be a list of strings or file-like objects."))
373 (unless (list? (knot-configuration-keys config))
374 (error-out "keys must be a list of knot-key-configuration."))
375 (for-each (lambda (key) (verify-knot-key-configuration key))
376 (knot-configuration-keys config))
377 (unless (list? (knot-configuration-keystores config))
378 (error-out "keystores must be a list of knot-keystore-configuration."))
379 (for-each (lambda (keystore) (verify-knot-keystore-configuration keystore))
380 (knot-configuration-keystores config))
381 (unless (list? (knot-configuration-acls config))
382 (error-out "acls must be a list of knot-acl-configuration."))
383 (for-each (lambda (acl) (verify-knot-acl-configuration acl))
384 (knot-configuration-acls config))
385 (unless (list? (knot-configuration-zones config))
386 (error-out "zones must be a list of knot-zone-configuration."))
387 (for-each (lambda (zone) (verify-knot-zone-configuration zone))
388 (knot-configuration-zones config))
389 (unless (list? (knot-configuration-policies config))
390 (error-out "policies must be a list of knot-policy-configuration."))
391 (for-each (lambda (policy) (verify-knot-policy-configuration policy))
392 (knot-configuration-policies config))
393 (unless (list? (knot-configuration-remotes config))
394 (error-out "remotes must be a list of knot-remote-configuration."))
395 (for-each (lambda (remote) (verify-knot-remote-configuration remote))
396 (knot-configuration-remotes config))
397 #t)
398
399(define (format-string-list l)
400 "Formats a list of string in YAML"
401 (if (eq? l '())
402 ""
403 (let ((l (reverse l)))
404 (string-append
405 "["
406 (fold (lambda (x1 x2)
407 (string-append (if (symbol? x1) (symbol->string x1) x1) ", "
408 (if (symbol? x2) (symbol->string x2) x2)))
409 (if (symbol? (car l)) (symbol->string (car l)) (car l)) (cdr l))
410 "]"))))
411
412(define (knot-acl-config acls)
413 (with-output-to-string
414 (lambda ()
415 (for-each
416 (lambda (acl-config)
417 (let ((id (knot-acl-configuration-id acl-config))
418 (address (knot-acl-configuration-address acl-config))
419 (key (knot-acl-configuration-key acl-config))
420 (action (knot-acl-configuration-action acl-config))
421 (deny? (knot-acl-configuration-deny? acl-config)))
422 (format #t " - id: ~a\n" id)
423 (unless (eq? address '())
424 (format #t " address: ~a\n" (format-string-list address)))
425 (unless (eq? key '())
426 (format #t " key: ~a\n" (format-string-list key)))
427 (unless (eq? action '())
428 (format #t " action: ~a\n" (format-string-list action)))
429 (format #t " deny: ~a\n" (if deny? "on" "off"))))
430 acls))))
431
432(define (knot-key-config keys)
433 (with-output-to-string
434 (lambda ()
435 (for-each
436 (lambda (key-config)
437 (let ((id (knot-key-configuration-id key-config))
438 (algorithm (knot-key-configuration-algorithm key-config))
439 (secret (knot-key-configuration-secret key-config)))
440 (format #t " - id: ~a\n" id)
441 (if algorithm
442 (format #t " algorithm: ~a\n" (symbol->string algorithm)))
443 (format #t " secret: ~a\n" secret)))
444 keys))))
445
446(define (knot-keystore-config keystores)
447 (with-output-to-string
448 (lambda ()
449 (for-each
450 (lambda (keystore-config)
451 (let ((id (knot-keystore-configuration-id keystore-config))
452 (backend (knot-keystore-configuration-backend keystore-config))
453 (config (knot-keystore-configuration-config keystore-config)))
454 (format #t " - id: ~a\n" id)
455 (format #t " backend: ~a\n" (symbol->string backend))
456 (format #t " config: \"~a\"\n" config)))
457 keystores))))
458
459(define (knot-policy-config policies)
460 (with-output-to-string
461 (lambda ()
462 (for-each
463 (lambda (policy-config)
464 (let ((id (knot-policy-configuration-id policy-config))
465 (keystore (knot-policy-configuration-keystore policy-config))
466 (manual? (knot-policy-configuration-manual? policy-config))
467 (single-type-signing? (knot-policy-configuration-single-type-signing?
468 policy-config))
469 (algorithm (knot-policy-configuration-algorithm policy-config))
470 (ksk-size (knot-policy-configuration-ksk-size policy-config))
471 (zsk-size (knot-policy-configuration-zsk-size policy-config))
472 (dnskey-ttl (knot-policy-configuration-dnskey-ttl policy-config))
473 (zsk-lifetime (knot-policy-configuration-zsk-lifetime policy-config))
474 (propagation-delay (knot-policy-configuration-propagation-delay
475 policy-config))
476 (rrsig-lifetime (knot-policy-configuration-rrsig-lifetime
477 policy-config))
478 (nsec3? (knot-policy-configuration-nsec3? policy-config))
479 (nsec3-iterations (knot-policy-configuration-nsec3-iterations
480 policy-config))
481 (nsec3-salt-length (knot-policy-configuration-nsec3-salt-length
482 policy-config))
483 (nsec3-salt-lifetime (knot-policy-configuration-nsec3-salt-lifetime
484 policy-config)))
485 (format #t " - id: ~a\n" id)
486 (format #t " keystore: ~a\n" keystore)
487 (format #t " manual: ~a\n" (if manual? "on" "off"))
488 (format #t " single-type-signing: ~a\n" (if single-type-signing?
489 "on" "off"))
490 (format #t " algorithm: ~a\n" algorithm)
491 (format #t " ksk-size: ~a\n" (number->string ksk-size))
492 (format #t " zsk-size: ~a\n" (number->string zsk-size))
493 (unless (eq? dnskey-ttl 'default)
494 (format #t " dnskey-ttl: ~a\n" dnskey-ttl))
495 (format #t " zsk-lifetime: ~a\n" zsk-lifetime)
496 (format #t " propagation-delay: ~a\n" propagation-delay)
497 (format #t " rrsig-lifetime: ~a\n" rrsig-lifetime)
498 (format #t " nsec3: ~a\n" (if nsec3? "on" "off"))
499 (format #t " nsec3-iterations: ~a\n"
500 (number->string nsec3-iterations))
501 (format #t " nsec3-salt-length: ~a\n"
502 (number->string nsec3-salt-length))
503 (format #t " nsec3-salt-lifetime: ~a\n" nsec3-salt-lifetime)))
504 policies))))
505
506(define (knot-remote-config remotes)
507 (with-output-to-string
508 (lambda ()
509 (for-each
510 (lambda (remote-config)
511 (let ((id (knot-remote-configuration-id remote-config))
512 (address (knot-remote-configuration-address remote-config))
513 (via (knot-remote-configuration-via remote-config))
514 (key (knot-remote-configuration-key remote-config)))
515 (format #t " - id: ~a\n" id)
516 (unless (eq? address '())
517 (format #t " address: ~a\n" (format-string-list address)))
518 (unless (eq? via '())
519 (format #t " via: ~a\n" (format-string-list via)))
520 (if key
521 (format #t " key: ~a\n" key))))
522 remotes))))
523
524(define (serialize-zone-entries entries)
525 (with-output-to-string
526 (lambda ()
527 (for-each
528 (lambda (entry)
529 (let ((name (zone-entry-name entry))
530 (ttl (zone-entry-ttl entry))
531 (class (zone-entry-class entry))
532 (type (zone-entry-type entry))
533 (data (zone-entry-data entry)))
534 (format #t "~a ~a ~a ~a ~a\n" name ttl class type data)))
535 entries))))
536
537(define (serialize-zone-file zone domain)
538 (computed-file (string-append domain ".zone")
539 #~(begin
540 (call-with-output-file #$output
541 (lambda (port)
542 (format port "$ORIGIN ~a.\n"
543 #$(zone-file-origin zone))
544 (format port "@ IN SOA ~a ~a (~a ~a ~a ~a ~a)\n"
545 #$(zone-file-ns zone)
546 #$(zone-file-mail zone)
547 #$(zone-file-serial zone)
548 #$(zone-file-refresh zone)
549 #$(zone-file-retry zone)
550 #$(zone-file-expiry zone)
551 #$(zone-file-nx zone))
552 (format port "~a\n"
553 #$(serialize-zone-entries (zone-file-entries zone))))))))
554
555(define (knot-zone-config zone)
556 (let ((content (knot-zone-configuration-zone zone)))
557 #~(with-output-to-string
558 (lambda ()
559 (let ((domain #$(knot-zone-configuration-domain zone))
560 (file #$(knot-zone-configuration-file zone))
561 (master (list #$@(knot-zone-configuration-master zone)))
562 (ddns-master #$(knot-zone-configuration-ddns-master zone))
563 (notify (list #$@(knot-zone-configuration-notify zone)))
564 (acl (list #$@(knot-zone-configuration-acl zone)))
565 (semantic-checks? #$(knot-zone-configuration-semantic-checks? zone))
566 (zonefile-sync #$(knot-zone-configuration-zonefile-sync zone))
567 (zonefile-load '#$(knot-zone-configuration-zonefile-load zone))
568 (journal-content #$(knot-zone-configuration-journal-content zone))
569 (max-journal-usage #$(knot-zone-configuration-max-journal-usage zone))
570 (max-journal-depth #$(knot-zone-configuration-max-journal-depth zone))
571 (max-zone-size #$(knot-zone-configuration-max-zone-size zone))
572 (dnssec-policy #$(knot-zone-configuration-dnssec-policy zone))
573 (serial-policy '#$(knot-zone-configuration-serial-policy zone)))
574 (format #t " - domain: ~a\n" domain)
575 (if (eq? master '())
576 ;; This server is a master
577 (if (equal? file "")
578 (format #t " file: ~a\n"
579 #$(serialize-zone-file content
580 (knot-zone-configuration-domain zone)))
581 (format #t " file: ~a\n" file))
582 ;; This server is a slave (has masters)
583 (begin
584 (format #t " master: ~a\n"
585 #$(format-string-list
586 (knot-zone-configuration-master zone)))
587 (if ddns-master (format #t " ddns-master ~a\n" ddns-master))))
588 (unless (eq? notify '())
589 (format #t " notify: ~a\n"
590 #$(format-string-list
591 (knot-zone-configuration-notify zone))))
592 (unless (eq? acl '())
593 (format #t " acl: ~a\n"
594 #$(format-string-list
595 (knot-zone-configuration-acl zone))))
596 (format #t " semantic-checks: ~a\n" (if semantic-checks? "on" "off"))
597 (if zonefile-sync
598 (format #t " zonefile-sync: ~a\n" zonefile-sync))
599 (if zonefile-load
600 (format #t " zonefile-load: ~a\n"
601 (symbol->string zonefile-load)))
602 (if journal-content
603 (format #t " journal-content: ~a\n"
604 (symbol->string journal-content)))
605 (if max-journal-usage
606 (format #t " max-journal-usage: ~a\n" max-journal-usage))
607 (if max-journal-depth
608 (format #t " max-journal-depth: ~a\n" max-journal-depth))
609 (if max-zone-size
610 (format #t " max-zone-size: ~a\n" max-zone-size))
611 (if dnssec-policy
612 (begin
613 (format #t " dnssec-signing: on\n")
614 (format #t " dnssec-policy: ~a\n" dnssec-policy)))
615 (format #t " serial-policy: ~a\n"
616 (symbol->string serial-policy)))))))
617
618(define (knot-config-file config)
619 (verify-knot-configuration config)
620 (computed-file "knot.conf"
621 #~(begin
622 (call-with-output-file #$output
623 (lambda (port)
624 (for-each (lambda (inc)
625 (format port "include: ~a\n" inc))
626 '#$(knot-configuration-includes config))
627 (format port "server:\n")
628 (format port " rundir: ~a\n" #$(knot-configuration-run-directory config))
629 (format port " user: knot\n")
630 (format port " listen: ~a@~a\n"
631 #$(knot-configuration-listen-v4 config)
632 #$(knot-configuration-listen-port config))
633 (format port " listen: ~a@~a\n"
634 #$(knot-configuration-listen-v6 config)
635 #$(knot-configuration-listen-port config))
636 (format port "\nkey:\n")
637 (format port #$(knot-key-config (knot-configuration-keys config)))
638 (format port "\nkeystore:\n")
639 (format port #$(knot-keystore-config (knot-configuration-keystores config)))
640 (format port "\nacl:\n")
641 (format port #$(knot-acl-config (knot-configuration-acls config)))
642 (format port "\nremote:\n")
643 (format port #$(knot-remote-config (knot-configuration-remotes config)))
644 (format port "\npolicy:\n")
645 (format port #$(knot-policy-config (knot-configuration-policies config)))
646 (unless #$(eq? (knot-configuration-zones config) '())
647 (format port "\nzone:\n")
648 (format port "~a\n"
649 (string-concatenate
650 (list #$@(map knot-zone-config
651 (knot-configuration-zones config)))))))))))
652
653(define %knot-accounts
654 (list (user-group (name "knot") (system? #t))
655 (user-account
656 (name "knot")
657 (group "knot")
658 (system? #t)
659 (comment "knot dns server user")
660 (home-directory "/var/empty")
661 (shell (file-append shadow "/sbin/nologin")))))
662
663(define (knot-activation config)
664 (with-imported-modules (source-module-closure '((gnu build activation)))
665 #~(begin
666 (use-modules (gnu build activation))
667 (mkdir-p/perms #$(knot-configuration-run-directory config)
668 (getpwnam "knot") #o755)
669 (mkdir-p/perms "/var/lib/knot" (getpwnam "knot") #o755)
670 (mkdir-p/perms "/var/lib/knot/keys" (getpwnam "knot") #o755)
671 (mkdir-p/perms "/var/lib/knot/keys/keys" (getpwnam "knot") #o755))))
672
673(define (knot-shepherd-service config)
674 (let* ((config-file (knot-config-file config))
675 (knot (knot-configuration-knot config)))
676 (list (shepherd-service
677 (documentation "Run the Knot DNS daemon.")
678 (provision '(knot dns))
679 (requirement '(networking user-processes))
680 (actions (list (shepherd-configuration-action config-file)))
681 (start #~(make-forkexec-constructor
682 (list (string-append #$knot "/sbin/knotd")
683 "-c" #$config-file)))
684 (stop #~(make-kill-destructor))))))
685
686(define knot-service-type
687 (service-type (name 'knot)
688 (extensions
689 (list (service-extension shepherd-root-service-type
690 knot-shepherd-service)
691 (service-extension activation-service-type
692 knot-activation)
693 (service-extension account-service-type
694 (const %knot-accounts))))
695 (description
696 "Run @uref{https://www.knot-dns.cz/, Knot}, an authoritative
697name server for the @acronym{DNS, Domain Name System}.")))
698
699
700;;;
701;;; Knot Resolver.
702;;;
703
704(define-record-type* <knot-resolver-configuration>
705 knot-resolver-configuration
706 make-knot-resolver-configuration
707 knot-resolver-configuration?
708 (package knot-resolver-configuration-package
709 (default knot-resolver))
710 (kresd-config-file knot-resolver-kresd-config-file
711 (default %kresd.conf))
712 (garbage-collection-interval knot-resolver-garbage-collection-interval
713 (default 1000)))
714
715(define %kresd.conf
716 (plain-file "kresd.conf" "-- -*- mode: lua -*-
717trust_anchors.add_file('/var/cache/knot-resolver/root.keys')
718net = { '127.0.0.1', '::1' }
719user('knot-resolver', 'knot-resolver')
720modules = { 'hints > iterate', 'stats', 'predict' }
721cache.size = 100 * MB
722"))
723
724(define %knot-resolver-accounts
725 (list (user-group
726 (name "knot-resolver")
727 (system? #t))
728 (user-account
729 (name "knot-resolver")
730 (group "knot-resolver")
731 (system? #t)
732 (home-directory "/var/cache/knot-resolver")
733 (shell (file-append shadow "/sbin/nologin")))))
734
735(define (knot-resolver-activation config)
736 #~(begin
737 (use-modules (guix build utils))
738 (let ((rundir "/var/cache/knot-resolver")
739 (owner (getpwnam "knot-resolver")))
740 (mkdir-p rundir)
741 (chown rundir (passwd:uid owner) (passwd:gid owner)))))
742
743(define knot-resolver-shepherd-services
744 (match-lambda
745 (($ <knot-resolver-configuration> package
746 kresd-config-file
747 garbage-collection-interval)
748 (list
749 (shepherd-service
750 (provision '(kresd))
751 (requirement '(user-processes networking))
752 (documentation "Run the Knot Resolver daemon.")
753 (start #~(make-forkexec-constructor
754 '(#$(file-append package "/sbin/kresd")
755 "-c" #$kresd-config-file "-n"
756 "/var/cache/knot-resolver")))
757 (stop #~(make-kill-destructor)))
758 (shepherd-service
759 (provision '(kres-cache-gc))
760 (requirement '(user-processes))
761 (documentation "Run the Knot Resolver Garbage Collector daemon.")
762 (start #~(make-forkexec-constructor
763 '(#$(file-append package "/sbin/kres-cache-gc")
764 "-d" #$(number->string garbage-collection-interval)
765 "-c" "/var/cache/knot-resolver")
766 #:user "knot-resolver"
767 #:group "knot-resolver"))
768 (stop #~(make-kill-destructor)))))))
769
770(define knot-resolver-service-type
771 (service-type
772 (name 'knot-resolver)
773 (extensions
774 (list (service-extension shepherd-root-service-type
775 knot-resolver-shepherd-services)
776 (service-extension activation-service-type
777 knot-resolver-activation)
778 (service-extension account-service-type
779 (const %knot-resolver-accounts))))
780 (default-value (knot-resolver-configuration))
781 (description "Run the Knot DNS Resolver.")))
782
783
784;;;
785;;; Dnsmasq.
786;;;
787
788(define-record-type* <dnsmasq-configuration>
789 dnsmasq-configuration make-dnsmasq-configuration
790 dnsmasq-configuration?
791 (package dnsmasq-configuration-package
792 (default dnsmasq)) ;file-like
793 (provision dnsmasq-configuration-provision ; deprecated
794 (default #f)
795 (sanitize warn-deprecated-dnsmasq-configuration-provision))
796 (shepherd-provision dnsmasq-configuration-shepherd-provision
797 (default '(dnsmasq)))
798 (shepherd-requirement dnsmasq-configuration-shepherd-requirement
799 (default '(user-processes networking)))
800 (no-hosts? dnsmasq-configuration-no-hosts?
801 (default #f)) ;boolean
802 (pid-file dnsmasq-configuration-pid-file
803 (default "/run/dnsmasq.pid")) ;string
804 (port dnsmasq-configuration-port
805 (default 53)) ;integer
806 (local-service? dnsmasq-configuration-local-service?
807 (default #t)) ;boolean
808 (listen-addresses dnsmasq-configuration-listen-address
809 (default '())) ;list of string
810 (ipv4? dnsmasq-configuration-ipv4?
811 (default #t))
812 (ipv6? dnsmasq-configuration-ipv6?
813 (default #t))
814 (resolv-file dnsmasq-configuration-resolv-file
815 (default "/etc/resolv.conf")) ;string
816 (no-resolv? dnsmasq-configuration-no-resolv?
817 (default #f)) ;boolean
818 (authoritative-servers dnsmasq-configuration-authoritative-servers
819 (default '())) ;list of strings
820 (authoritative-zones dnsmasq-configuration-authoritative-zones
821 (default '())) ;list of strings
822 (host-records dnsmasq-configuration-host-records
823 (default '())) ;list of strings
824 (forward-private-reverse-lookup?
825 dnsmasq-configuration-forward-private-reverse-lookup?
826 (default #t)) ;boolean
827 (query-servers-in-order?
828 dnsmasq-configuration-query-servers-in-order?
829 (default #f)) ;boolean
830 (locals dnsmasq-configuration-locals
831 (default '()))
832 (servers dnsmasq-configuration-servers
833 (default '())) ;list of string
834 (servers-file dnsmasq-configuration-servers-file
835 (default #f)) ;string|file-like
836 (addresses dnsmasq-configuration-addresses
837 (default '())) ;list of string
838 (cache-size dnsmasq-configuration-cache-size
839 (default 150)) ;integer
840 (negative-cache? dnsmasq-configuration-negative-cache?
841 (default #t)) ;boolean
842 (cpe-id dnsmasq-configuration-cpe-id
843 (default #f)) ;string
844 (tftp-enable? dnsmasq-configuration-tftp-enable?
845 (default #f)) ;boolean
846 (tftp-no-fail? dnsmasq-configuration-tftp-no-fail?
847 (default #f)) ;boolean
848 (tftp-single-port? dnsmasq-configuration-tftp-single-port?
849 (default #f)) ;boolean
850 (tftp-secure? dnsmasq-tftp-secure?
851 (default #f)) ;boolean
852 (tftp-max dnsmasq-tftp-max
853 (default #f)) ;integer
854 (tftp-mtu dnsmasq-tftp-mtu
855 (default #f)) ;integer
856 (tftp-no-blocksize? dnsmasq-tftp-no-blocksize?
857 (default #f)) ;boolean
858 (tftp-lowercase? dnsmasq-tftp-lowercase?
859 (default #f)) ;boolean
860 (tftp-port-range dnsmasq-tftp-port-range
861 (default #f)) ;string
862 (tftp-root dnsmasq-tftp-root
863 (default "/var/empty,lo")) ;string
864 (tftp-unique-root dnsmasq-tftp-unique-root
865 (default #f)) ;"" or "ip" or "mac"
866 (conf-file dnsmasq-configuration-conf-file
867 (default '())) ;list of string|file-like
868 (conf-dir dnsmasq-configuration-conf-dir
869 (default #f)) ;string|file-like
870 (extra-options dnsmasq-configuration-extra-options
871 (default '()))) ;list of string
872
873(define (warn-deprecated-dnsmasq-configuration-provision value)
874 (when (pair? value)
875 (warn-about-deprecation
876 'provision #f
877 #:replacement 'shepherd-provision))
878 value)
879
880(define (dnsmasq-shepherd-service config)
881 (match-record config <dnsmasq-configuration>
882 (package
883 provision
884 shepherd-provision
885 shepherd-requirement
886 pid-file
887 no-hosts?
888 port local-service? listen-addresses ipv4? ipv6?
889 resolv-file no-resolv?
890 authoritative-servers authoritative-zones host-records
891 forward-private-reverse-lookup? query-servers-in-order?
892 locals servers addresses servers-file
893 cache-size negative-cache?
894 cpe-id
895 tftp-enable? tftp-no-fail?
896 tftp-single-port? tftp-secure?
897 tftp-max tftp-mtu tftp-no-blocksize?
898 tftp-lowercase? tftp-port-range
899 tftp-root tftp-unique-root
900 conf-file conf-dir extra-options)
901 (shepherd-service
902 (provision (or provision shepherd-provision))
903 (requirement shepherd-requirement)
904 (documentation "Run the dnsmasq DNS server.")
905 (actions (list (dnsmasq-service-reload-action config)
906 (dnsmasq-service-stats-action config)))
907 (start #~(make-forkexec-constructor
908 (list
909 #$(file-append package "/sbin/dnsmasq")
910 "--keep-in-foreground"
911 (string-append "--pid-file=" #$pid-file)
912 #$@(if no-hosts?
913 '("--no-hosts")
914 '())
915 #$(format #f "--port=~a" port)
916 #$@(if local-service?
917 '("--local-service")
918 '())
919 #$@(map (cut format #f "--listen-address=~a" <>)
920 listen-addresses)
921 #$@(if (not ipv4?)
922 '("--filter-A")
923 '())
924 #$@(if (not ipv6?)
925 '("--filter-AAAA")
926 '())
927 #$(format #f "--resolv-file=~a" resolv-file)
928 #$@(if no-resolv?
929 '("--no-resolv")
930 '())
931 #$@(map (cut format #f "--auth-server=~a" <>)
932 authoritative-servers)
933 #$@(map (cut format #f "--auth-zone=~a" <>)
934 authoritative-zones)
935 #$@(map (cut format #f "--host-record=~a" <>)
936 host-records)
937 #$@(if forward-private-reverse-lookup?
938 '()
939 '("--bogus-priv"))
940 #$@(if query-servers-in-order?
941 '("--strict-order")
942 '())
943 #$@(if servers-file
944 (list #~(string-append "--servers-file=" #$servers-file))
945 '())
946 #$@(map (cut format #f "--local=~a" <>)
947 locals)
948 #$@(map (cut format #f "--server=~a" <>)
949 servers)
950 #$@(map (cut format #f "--address=~a" <>)
951 addresses)
952 #$(format #f "--cache-size=~a" cache-size)
953 #$@(if negative-cache?
954 '()
955 '("--no-negcache"))
956 #$@(if cpe-id
957 (list (format #f "--add-cpe-id=~a" cpe-id))
958 '())
959 #$@(if tftp-enable?
960 '("--enable-tftp")
961 '())
962 #$@(if tftp-no-fail?
963 '("--tftp-no-fail")
964 '())
965 #$@(if tftp-single-port?
966 '("--tftp-single-port")
967 '())
968 #$@(if tftp-secure?
969 '("--tftp-secure")
970 '())
971 #$@(if tftp-max
972 (list (format #f "--tftp-max=~a" tftp-max))
973 '())
974 #$@(if tftp-mtu
975 (list (format #f "--tftp-mtu=~a" tftp-mtu))
976 '())
977 #$@(if tftp-no-blocksize?
978 '("--tftp-no-blocksize")
979 '())
980 #$@(if tftp-lowercase?
981 '("--tftp-lowercase")
982 '())
983 #$@(if tftp-port-range
984 (list (format #f "--tftp-port-range=~a"
985 tftp-port-range))
986 '())
987 #$@(if tftp-root
988 (list (format #f "--tftp-root=~a" tftp-root))
989 '())
990 #$@(if tftp-unique-root
991 (list
992 (if (> (length tftp-unique-root) 0)
993 (format #f "--tftp-unique-root=~a" tftp-unique-root)
994 (format #f "--tftp-unique-root")))
995 '())
996 #$@(map (lambda (conf-file)
997 #~(string-append "--conf-file=" #$conf-file))
998 conf-file)
999 #$@(if conf-dir
1000 (list #~(string-append "--conf-dir=" #$conf-dir))
1001 '())
1002 #$@extra-options)
1003 #:pid-file #$pid-file))
1004 (stop #~(make-kill-destructor)))))
1005
1006(define (dnsmasq-activation config)
1007 #~(begin
1008 (use-modules (guix build utils))
1009 ;; create directory to store dnsmasq lease file
1010 (mkdir-p "/var/lib/misc")))
1011
1012(define (dnsmasq-service-reload-action config)
1013 (shepherd-signal-action
1014 'reload SIGHUP
1015 #:documentation "Send a @code{SIGHUP} signal to @command{dnsmasq} to clear
1016cache and reload hosts files."))
1017
1018(define (dnsmasq-service-stats-action config)
1019 (shepherd-signal-action
1020 'stats SIGUSR1
1021 #:documentation "Send a @code{SIGUSR1} to write statistics to the system
1022log."))
1023
1024(define dnsmasq-service-type
1025 (service-type
1026 (name 'dnsmasq)
1027 (extensions
1028 (list (service-extension shepherd-root-service-type
1029 (compose list dnsmasq-shepherd-service))
1030 (service-extension dbus-root-service-type
1031 (compose list dnsmasq-configuration-package))
1032 (service-extension activation-service-type
1033 dnsmasq-activation)))
1034 (default-value (dnsmasq-configuration))
1035 (description "Run the dnsmasq DNS server.")))
1036
1037
1038;;;
1039;;; Unbound.
1040;;;
1041
1042(define (unbound-serialize-field field-name value)
1043 (let ((field (object->string field-name))
1044 (value (cond
1045 ((boolean? value) (if value "yes" "no"))
1046 ((string? value) value)
1047 (else (object->string value)))))
1048 (if (string=? field "extra-content")
1049 #~(string-append #$value "\n")
1050 #~(format #f " ~a: ~s~%" #$field #$value))))
1051
1052(define (unbound-serialize-alist field-name value)
1053 #~(string-append #$@(generic-serialize-alist list
1054 unbound-serialize-field
1055 value)))
1056
1057(define (unbound-serialize-section section-name value fields)
1058 #~(format #f "~a:~%~a"
1059 #$(object->string section-name)
1060 #$(serialize-configuration value fields)))
1061
1062(define unbound-serialize-string unbound-serialize-field)
1063(define unbound-serialize-boolean unbound-serialize-field)
1064
1065(define-maybe string (prefix unbound-))
1066(define-maybe list-of-strings (prefix unbound-))
1067(define-maybe boolean (prefix unbound-))
1068
1069(define (unbound-serialize-list-of-strings field-name value)
1070 #~(string-append #$@(map (cut unbound-serialize-string field-name <>) value)))
1071
1072(define-configuration unbound-zone
1073 (name
1074 string
1075 "Zone name.")
1076
1077 (forward-addr
1078 maybe-list-of-strings
1079 "IP address of server to forward to.")
1080
1081 (forward-tls-upstream
1082 maybe-boolean
1083 "Whether the queries to this forwarder use TLS for transport.")
1084
1085 (extra-options
1086 (alist '())
1087 "An association list of options to append.")
1088
1089 (prefix unbound-))
1090
1091(define (unbound-serialize-unbound-zone field-name value)
1092 (unbound-serialize-section field-name value unbound-zone-fields))
1093
1094(define (unbound-serialize-list-of-unbound-zone field-name value)
1095 #~(string-append #$@(map (cut unbound-serialize-unbound-zone field-name <>)
1096 value)))
1097
1098(define list-of-unbound-zone? (list-of unbound-zone?))
1099
1100(define-configuration unbound-remote
1101 (control-enable
1102 maybe-boolean
1103 "Enable remote control.")
1104
1105 (control-interface
1106 maybe-string
1107 "IP address or local socket path to listen on for remote control.")
1108
1109 (extra-options
1110 (alist '())
1111 "An association list of options to append.")
1112
1113 (prefix unbound-))
1114
1115(define (unbound-serialize-unbound-remote field-name value)
1116 (unbound-serialize-section field-name value unbound-remote-fields))
1117
1118(define-configuration unbound-server
1119 (interface
1120 maybe-list-of-strings
1121 "Interfaces listened on for queries from clients.")
1122
1123 (hide-version
1124 maybe-boolean
1125 "Refuse the version.server and version.bind queries.")
1126
1127 (hide-identity
1128 maybe-boolean
1129 "Refuse the id.server and hostname.bind queries.")
1130
1131 (tls-cert-bundle
1132 maybe-string
1133 "Certificate bundle file, used for DNS over TLS.")
1134
1135 (extra-options
1136 (alist '())
1137 "An association list of options to append.")
1138
1139 (prefix unbound-))
1140
1141(define (unbound-serialize-unbound-server field-name value)
1142 (unbound-serialize-section field-name value unbound-server-fields))
1143
1144(define-configuration unbound-configuration
1145 (server
1146 (unbound-server
1147 (unbound-server
1148 (interface '("127.0.0.1" "::1"))
1149
1150 (hide-version #t)
1151 (hide-identity #t)
1152
1153 (tls-cert-bundle "/etc/ssl/certs/ca-certificates.crt")))
1154 "General options for the Unbound server.")
1155
1156 (remote-control
1157 (unbound-remote
1158 (unbound-remote
1159 (control-enable #t)
1160 (control-interface "/run/unbound.sock")))
1161 "Remote control options for the daemon.")
1162
1163 (forward-zone
1164 (list-of-unbound-zone '())
1165 "A zone for which queries should be forwarded to another resolver.")
1166
1167 (extra-content
1168 maybe-string
1169 "Raw content to add to the configuration file.")
1170
1171 (prefix unbound-))
1172
1173(define (unbound-config-file config)
1174 (mixed-text-file "unbound.conf"
1175 (serialize-configuration
1176 config
1177 unbound-configuration-fields)))
1178
1179(define (unbound-shepherd-service config)
1180 (let ((config-file (unbound-config-file config)))
1181 (list (shepherd-service
1182 (documentation "Unbound daemon.")
1183 (provision '(unbound))
1184 ;; unbound may be bound to a particular IP address, hence
1185 ;; only start it after the networking service has started.
1186 (requirement '(user-processes networking))
1187 (actions (list (shepherd-configuration-action config-file)))
1188 (start #~(make-forkexec-constructor
1189 (list (string-append #$unbound "/sbin/unbound")
1190 "-d" "-p" "-c" #$config-file)))
1191 (stop #~(make-kill-destructor))))))
1192
1193(define unbound-account-service
1194 (list (user-group (name "unbound") (system? #t))
1195 (user-account
1196 (name "unbound")
1197 (group "unbound")
1198 (system? #t)
1199 (comment "Unbound daemon user")
1200 (home-directory "/var/empty")
1201 (shell (file-append shadow "/sbin/nologin")))))
1202
1203(define unbound-service-type
1204 (service-type (name 'unbound)
1205 (description "Run the Unbound DNS resolver.")
1206 (extensions
1207 (list (service-extension account-service-type
1208 (const unbound-account-service))
1209 (service-extension shepherd-root-service-type
1210 unbound-shepherd-service)))
1211 (compose concatenate)
1212 (default-value (unbound-configuration))))
1213
1214
1215;;;
1216;;; ddclient.
1217;;;
1218
1219(define (string-or-file-like? value)
1220 (or (string? value) (file-like? value)))
1221
1222(define-configuration/no-serialization ddclient-configuration
1223 (package
1224 (file-like ddclient)
1225 "The ddclient package to use.")
1226 (config-file
1227 (string-or-file-like "/etc/ddclient/ddclient.conf")
1228 "Configuration file for @command{ddclient}. Must be readable by the
1229@code{ddclient} user.")
1230 (interval
1231 (integer 300)
1232 "Number of seconds to wait between two update checks.")
1233 (cache-file
1234 (string "/var/cache/ddclient/ddclient.cache")
1235 "File where @command{ddclient} records the addresses it last sent. Its
1236parent directory is created at activation time.")
1237 (extra-options
1238 (list-of-strings '())
1239 "Extra command-line options passed to @command{ddclient}."))
1240
1241(define (ddclient-shepherd-service config)
1242 (match-record config <ddclient-configuration>
1243 (package config-file interval cache-file extra-options)
1244 (list
1245 (shepherd-service
1246 (provision '(ddclient))
1247 (documentation "Update dynamic DNS entries.")
1248 (requirement '(user-processes networking))
1249 (start (let ((ddclient*
1250 (least-authority-wrapper
1251 (file-append package "/bin/ddclient")
1252 #:name "ddclient"
1253 #:user "ddclient"
1254 #:group "ddclient"
1255 #:mappings (list
1256 (file-system-mapping
1257 (source (dirname cache-file))
1258 (target source)
1259 (writable? #t))
1260 ;; Map the certificate bundle rather than the
1261 ;; whole of /etc/ssl/certs: the latter is a
1262 ;; directory of symbolic links to store items
1263 ;; that are not mounted in the container,
1264 ;; whereas bind-mounting the bundle resolves
1265 ;; the symbolic link on the host side.
1266 (file-system-mapping
1267 (source "/etc/ssl/certs/ca-certificates.crt")
1268 (target source))
1269 (file-system-mapping
1270 (source config-file)
1271 (target config-file)))
1272 ;; 'ddclient' accesses files owned by the "ddclient" user
1273 ;; so it needs to change UIDs, which in turn requires
1274 ;; running in the global user namespace.
1275 #:namespaces (fold delq %namespaces '(net user))
1276 #:preserved-environment-variables
1277 (cons* "SSL_CERT_DIR"
1278 "SSL_CERT_FILE"
1279 %default-preserved-environment-variables))))
1280 #~(make-forkexec-constructor
1281 (list #$ddclient*
1282 "--foreground"
1283 #$(string-append "--daemon=" (number->string interval))
1284 "--file" #$config-file
1285 "--cache" #$cache-file
1286 #$@extra-options)
1287 #:log-file "/var/log/ddclient.log"
1288 ;; ddclient talks to its provider by running 'curl', which
1289 ;; needs to be told where X.509 certificates live.
1290 #:environment-variables
1291 (list "SSL_CERT_DIR=/etc/ssl/certs"
1292 "SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt"))))
1293 (stop #~(make-kill-destructor))
1294 (respawn? #t)))))
1295
1296(define ddclient-activation
1297 (match-record-lambda <ddclient-configuration>
1298 (cache-file)
1299 (with-imported-modules (source-module-closure '((gnu build activation)))
1300 #~(begin
1301 (use-modules (gnu build activation))
1302 (mkdir-p/perms #$(dirname cache-file)
1303 (getpwnam "ddclient")
1304 #o700)))))
1305
1306(define %ddclient-accounts
1307 (list (user-group (name "ddclient") (system? #t))
1308 (user-account
1309 (name "ddclient")
1310 (group "ddclient")
1311 (system? #t)
1312 (comment "ddclient daemon user")
1313 (home-directory "/var/empty")
1314 (shell (file-append shadow "/sbin/nologin")))))
1315
1316(define ddclient-profile-service
1317 (compose list ddclient-configuration-package))
1318
1319(define ddclient-service-type
1320 (service-type (name 'ddclient)
1321 (extensions
1322 (list (service-extension shepherd-root-service-type
1323 ddclient-shepherd-service)
1324 (service-extension activation-service-type
1325 ddclient-activation)
1326 (service-extension profile-service-type
1327 ddclient-profile-service)
1328 (service-extension account-service-type
1329 (const %ddclient-accounts))))
1330 (default-value (ddclient-configuration))
1331 (description
1332 "Run @command{ddclient}, a client that keeps the entries of a
1333dynamic @acronym{DNS, Domain Name System} account up to date.")))