services: root-file-system: Cleanly unmount upon shutdown.
[jackhill/guix/guix.git] / gnu / services / dns.scm
1 ;;; GNU Guix --- Functional package management for GNU
2 ;;; Copyright © 2017 Julien Lepiller <julien@lepiller.eu>
3 ;;; Copyright © 2018 Oleg Pykhalov <go.wigust@gmail.com>
4 ;;; Copyright © 2020 Pierre Langlois <pierre.langlois@gmx.com>
5 ;;; Copyright © 2021 Maxime Devos <maximedevos@telenet.be>
6 ;;; Copyright © 2022 Remco van 't Veer <remco@remworks.net>
7 ;;;
8 ;;; This file is part of GNU Guix.
9 ;;;
10 ;;; GNU Guix is free software; you can redistribute it and/or modify it
11 ;;; under the terms of the GNU General Public License as published by
12 ;;; the Free Software Foundation; either version 3 of the License, or (at
13 ;;; your option) any later version.
14 ;;;
15 ;;; GNU Guix is distributed in the hope that it will be useful, but
16 ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 ;;; GNU General Public License for more details.
19 ;;;
20 ;;; You should have received a copy of the GNU General Public License
21 ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
22
23 (define-module (gnu services dns)
24 #:use-module (gnu services)
25 #:use-module (gnu services configuration)
26 #:use-module (gnu services shepherd)
27 #:use-module (gnu system shadow)
28 #:use-module (gnu packages admin)
29 #:use-module (gnu packages dns)
30 #:use-module (guix packages)
31 #:use-module (guix records)
32 #:use-module (guix gexp)
33 #:use-module (guix modules)
34 #:use-module (srfi srfi-1)
35 #:use-module (srfi srfi-26)
36 #:use-module (srfi srfi-34)
37 #:use-module (srfi srfi-35)
38 #:use-module (ice-9 match)
39 #:use-module (ice-9 regex)
40 #:export (knot-service-type
41 knot-acl-configuration
42 knot-key-configuration
43 knot-keystore-configuration
44 knot-zone-configuration
45 knot-remote-configuration
46 knot-policy-configuration
47 knot-configuration
48 define-zone-entries
49 zone-file
50 zone-entry
51
52 knot-resolver-service-type
53 knot-resolver-configuration
54
55 dnsmasq-service-type
56 dnsmasq-configuration
57
58 ddclient-service-type
59 ddclient-configuration))
60
61 ;;;
62 ;;; Knot DNS.
63 ;;;
64
65 (define-record-type* <knot-key-configuration>
66 knot-key-configuration make-knot-key-configuration
67 knot-key-configuration?
68 (id knot-key-configuration-id
69 (default ""))
70 (algorithm knot-key-configuration-algorithm
71 (default #f)); one of #f, or an algorithm name
72 (secret knot-key-configuration-secret
73 (default "")))
74
75 (define-record-type* <knot-acl-configuration>
76 knot-acl-configuration make-knot-acl-configuration
77 knot-acl-configuration?
78 (id knot-acl-configuration-id
79 (default ""))
80 (address knot-acl-configuration-address
81 (default '()))
82 (key knot-acl-configuration-key
83 (default '()))
84 (action knot-acl-configuration-action
85 (default '()))
86 (deny? knot-acl-configuration-deny?
87 (default #f)))
88
89 (define-record-type* <zone-entry>
90 zone-entry make-zone-entry
91 zone-entry?
92 (name zone-entry-name
93 (default "@"))
94 (ttl zone-entry-ttl
95 (default ""))
96 (class zone-entry-class
97 (default "IN"))
98 (type zone-entry-type
99 (default "A"))
100 (data zone-entry-data
101 (default "")))
102
103 (define-record-type* <zone-file>
104 zone-file make-zone-file
105 zone-file?
106 (entries zone-file-entries
107 (default '()))
108 (origin zone-file-origin
109 (default ""))
110 (ns zone-file-ns
111 (default "ns"))
112 (mail zone-file-mail
113 (default "hostmaster"))
114 (serial zone-file-serial
115 (default 1))
116 (refresh zone-file-refresh
117 (default (* 2 24 3600)))
118 (retry zone-file-retry
119 (default (* 15 60)))
120 (expiry zone-file-expiry
121 (default (* 2 7 24 3600)))
122 (nx zone-file-nx
123 (default 3600)))
124 (define-record-type* <knot-keystore-configuration>
125 knot-keystore-configuration make-knot-keystore-configuration
126 knot-keystore-configuration?
127 (id knot-keystore-configuration-id
128 (default ""))
129 (backend knot-keystore-configuration-backend
130 (default 'pem))
131 (config knot-keystore-configuration-config
132 (default "/var/lib/knot/keys/keys")))
133
134 (define-record-type* <knot-policy-configuration>
135 knot-policy-configuration make-knot-policy-configuration
136 knot-policy-configuration?
137 (id knot-policy-configuration-id
138 (default ""))
139 (keystore knot-policy-configuration-keystore
140 (default "default"))
141 (manual? knot-policy-configuration-manual?
142 (default #f))
143 (single-type-signing? knot-policy-configuration-single-type-signing?
144 (default #f))
145 (algorithm knot-policy-configuration-algorithm
146 (default "ecdsap256sha256"))
147 (ksk-size knot-policy-configuration-ksk-size
148 (default 256))
149 (zsk-size knot-policy-configuration-zsk-size
150 (default 256))
151 (dnskey-ttl knot-policy-configuration-dnskey-ttl
152 (default 'default))
153 (zsk-lifetime knot-policy-configuration-zsk-lifetime
154 (default (* 30 24 3600)))
155 (propagation-delay knot-policy-configuration-propagation-delay
156 (default (* 24 3600)))
157 (rrsig-lifetime knot-policy-configuration-rrsig-lifetime
158 (default (* 14 24 3600)))
159 (rrsig-refresh knot-policy-configuration-rrsig-refresh
160 (default (* 7 24 3600)))
161 (nsec3? knot-policy-configuration-nsec3?
162 (default #f))
163 (nsec3-iterations knot-policy-configuration-nsec3-iterations
164 (default 5))
165 (nsec3-salt-length knot-policy-configuration-nsec3-salt-length
166 (default 8))
167 (nsec3-salt-lifetime knot-policy-configuration-nsec3-salt-lifetime
168 (default (* 30 24 3600))))
169
170 (define-record-type* <knot-zone-configuration>
171 knot-zone-configuration make-knot-zone-configuration
172 knot-zone-configuration?
173 (domain knot-zone-configuration-domain
174 (default ""))
175 (file knot-zone-configuration-file
176 (default "")) ; the file where this zone is saved.
177 (zone knot-zone-configuration-zone
178 (default (zone-file))) ; initial content of the zone file
179 (master knot-zone-configuration-master
180 (default '()))
181 (ddns-master knot-zone-configuration-ddns-master
182 (default #f))
183 (notify knot-zone-configuration-notify
184 (default '()))
185 (acl knot-zone-configuration-acl
186 (default '()))
187 (semantic-checks? knot-zone-configuration-semantic-checks?
188 (default #f))
189 (zonefile-sync knot-zone-configuration-zonefile-sync
190 (default 0))
191 (zonefile-load knot-zone-configuration-zonefile-load
192 (default #f))
193 (journal-content knot-zone-configuration-journal-content
194 (default #f))
195 (max-journal-usage knot-zone-configuration-max-journal-usage
196 (default #f))
197 (max-journal-depth knot-zone-configuration-max-journal-depth
198 (default #f))
199 (max-zone-size knot-zone-configuration-max-zone-size
200 (default #f))
201 (dnssec-policy knot-zone-configuration-dnssec-policy
202 (default #f))
203 (serial-policy knot-zone-configuration-serial-policy
204 (default 'increment)))
205
206 (define-record-type* <knot-remote-configuration>
207 knot-remote-configuration make-knot-remote-configuration
208 knot-remote-configuration?
209 (id knot-remote-configuration-id
210 (default ""))
211 (address knot-remote-configuration-address
212 (default '()))
213 (via knot-remote-configuration-via
214 (default '()))
215 (key knot-remote-configuration-key
216 (default #f)))
217
218 (define-record-type* <knot-configuration>
219 knot-configuration make-knot-configuration
220 knot-configuration?
221 (knot knot-configuration-knot
222 (default knot))
223 (run-directory knot-configuration-run-directory
224 (default "/var/run/knot"))
225 (includes knot-configuration-includes
226 (default '()))
227 (listen-v4 knot-configuration-listen-v4
228 (default "0.0.0.0"))
229 (listen-v6 knot-configuration-listen-v6
230 (default "::"))
231 (listen-port knot-configuration-listen-port
232 (default 53))
233 (keys knot-configuration-keys
234 (default '()))
235 (keystores knot-configuration-keystores
236 (default '()))
237 (acls knot-configuration-acls
238 (default '()))
239 (remotes knot-configuration-remotes
240 (default '()))
241 (policies knot-configuration-policies
242 (default '()))
243 (zones knot-configuration-zones
244 (default '())))
245
246 (define-syntax define-zone-entries
247 (syntax-rules ()
248 ((_ id (name ttl class type data) ...)
249 (define id (list (make-zone-entry name ttl class type data) ...)))))
250
251 (define (error-out msg)
252 (raise (condition (&message (message msg)))))
253
254 (define (verify-knot-key-configuration key)
255 (unless (knot-key-configuration? key)
256 (error-out "keys must be a list of only knot-key-configuration."))
257 (let ((id (knot-key-configuration-id key)))
258 (unless (and (string? id) (not (equal? id "")))
259 (error-out "key id must be a non empty string.")))
260 (unless (memq (knot-key-configuration-algorithm key)
261 '(#f hmac-md5 hmac-sha1 hmac-sha224 hmac-sha256 hmac-sha384 hmac-sha512))
262 (error-out "algorithm must be one of: #f, 'hmac-md5, 'hmac-sha1,
263 'hmac-sha224, 'hmac-sha256, 'hmac-sha384 or 'hmac-sha512")))
264
265 (define (verify-knot-keystore-configuration keystore)
266 (unless (knot-keystore-configuration? keystore)
267 (error-out "keystores must be a list of only knot-keystore-configuration."))
268 (let ((id (knot-keystore-configuration-id keystore)))
269 (unless (and (string? id) (not (equal? id "")))
270 (error-out "keystore id must be a non empty string.")))
271 (unless (memq (knot-keystore-configuration-backend keystore)
272 '(pem pkcs11))
273 (error-out "backend must be one of: 'pem or 'pkcs11")))
274
275 (define (verify-knot-policy-configuration policy)
276 (unless (knot-policy-configuration? policy)
277 (error-out "policies must be a list of only knot-policy-configuration."))
278 (let ((id (knot-policy-configuration-id policy)))
279 (unless (and (string? id) (not (equal? id "")))
280 (error-out "policy id must be a non empty string."))))
281
282 (define (verify-knot-acl-configuration acl)
283 (unless (knot-acl-configuration? acl)
284 (error-out "acls must be a list of only knot-acl-configuration."))
285 (let ((id (knot-acl-configuration-id acl))
286 (address (knot-acl-configuration-address acl))
287 (key (knot-acl-configuration-key acl))
288 (action (knot-acl-configuration-action acl)))
289 (unless (and (string? id) (not (equal? id "")))
290 (error-out "acl id must be a non empty string."))
291 (unless (and (list? address)
292 (every string? address))
293 (error-out "acl address must be a list of strings.")))
294 (unless (boolean? (knot-acl-configuration-deny? acl))
295 (error-out "deny? must be #t or #f.")))
296
297 (define (verify-knot-zone-configuration zone)
298 (unless (knot-zone-configuration? zone)
299 (error-out "zones must be a list of only knot-zone-configuration."))
300 (let ((domain (knot-zone-configuration-domain zone)))
301 (unless (and (string? domain) (not (equal? domain "")))
302 (error-out "zone domain must be a non empty string."))))
303
304 (define (verify-knot-remote-configuration remote)
305 (unless (knot-remote-configuration? remote)
306 (error-out "remotes must be a list of only knot-remote-configuration."))
307 (let ((id (knot-remote-configuration-id remote)))
308 (unless (and (string? id) (not (equal? id "")))
309 (error-out "remote id must be a non empty string."))))
310
311 (define (verify-knot-configuration config)
312 (unless (file-like? (knot-configuration-knot config))
313 (error-out "knot configuration field must be a file-like object."))
314 (unless (string? (knot-configuration-run-directory config))
315 (error-out "run-directory must be a string."))
316 (unless (list? (knot-configuration-includes config))
317 (error-out "includes must be a list of strings or file-like objects."))
318 (unless (list? (knot-configuration-keys config))
319 (error-out "keys must be a list of knot-key-configuration."))
320 (for-each (lambda (key) (verify-knot-key-configuration key))
321 (knot-configuration-keys config))
322 (unless (list? (knot-configuration-keystores config))
323 (error-out "keystores must be a list of knot-keystore-configuration."))
324 (for-each (lambda (keystore) (verify-knot-keystore-configuration keystore))
325 (knot-configuration-keystores config))
326 (unless (list? (knot-configuration-acls config))
327 (error-out "acls must be a list of knot-acl-configuration."))
328 (for-each (lambda (acl) (verify-knot-acl-configuration acl))
329 (knot-configuration-acls config))
330 (unless (list? (knot-configuration-zones config))
331 (error-out "zones must be a list of knot-zone-configuration."))
332 (for-each (lambda (zone) (verify-knot-zone-configuration zone))
333 (knot-configuration-zones config))
334 (unless (list? (knot-configuration-policies config))
335 (error-out "policies must be a list of knot-policy-configuration."))
336 (for-each (lambda (policy) (verify-knot-policy-configuration policy))
337 (knot-configuration-policies config))
338 (unless (list? (knot-configuration-remotes config))
339 (error-out "remotes must be a list of knot-remote-configuration."))
340 (for-each (lambda (remote) (verify-knot-remote-configuration remote))
341 (knot-configuration-remotes config))
342 #t)
343
344 (define (format-string-list l)
345 "Formats a list of string in YAML"
346 (if (eq? l '())
347 ""
348 (let ((l (reverse l)))
349 (string-append
350 "["
351 (fold (lambda (x1 x2)
352 (string-append (if (symbol? x1) (symbol->string x1) x1) ", "
353 (if (symbol? x2) (symbol->string x2) x2)))
354 (if (symbol? (car l)) (symbol->string (car l)) (car l)) (cdr l))
355 "]"))))
356
357 (define (knot-acl-config acls)
358 (with-output-to-string
359 (lambda ()
360 (for-each
361 (lambda (acl-config)
362 (let ((id (knot-acl-configuration-id acl-config))
363 (address (knot-acl-configuration-address acl-config))
364 (key (knot-acl-configuration-key acl-config))
365 (action (knot-acl-configuration-action acl-config))
366 (deny? (knot-acl-configuration-deny? acl-config)))
367 (format #t " - id: ~a\n" id)
368 (unless (eq? address '())
369 (format #t " address: ~a\n" (format-string-list address)))
370 (unless (eq? key '())
371 (format #t " key: ~a\n" (format-string-list key)))
372 (unless (eq? action '())
373 (format #t " action: ~a\n" (format-string-list action)))
374 (format #t " deny: ~a\n" (if deny? "on" "off"))))
375 acls))))
376
377 (define (knot-key-config keys)
378 (with-output-to-string
379 (lambda ()
380 (for-each
381 (lambda (key-config)
382 (let ((id (knot-key-configuration-id key-config))
383 (algorithm (knot-key-configuration-algorithm key-config))
384 (secret (knot-key-configuration-secret key-config)))
385 (format #t " - id: ~a\n" id)
386 (if algorithm
387 (format #t " algorithm: ~a\n" (symbol->string algorithm)))
388 (format #t " secret: ~a\n" secret)))
389 keys))))
390
391 (define (knot-keystore-config keystores)
392 (with-output-to-string
393 (lambda ()
394 (for-each
395 (lambda (keystore-config)
396 (let ((id (knot-keystore-configuration-id keystore-config))
397 (backend (knot-keystore-configuration-backend keystore-config))
398 (config (knot-keystore-configuration-config keystore-config)))
399 (format #t " - id: ~a\n" id)
400 (format #t " backend: ~a\n" (symbol->string backend))
401 (format #t " config: \"~a\"\n" config)))
402 keystores))))
403
404 (define (knot-policy-config policies)
405 (with-output-to-string
406 (lambda ()
407 (for-each
408 (lambda (policy-config)
409 (let ((id (knot-policy-configuration-id policy-config))
410 (keystore (knot-policy-configuration-keystore policy-config))
411 (manual? (knot-policy-configuration-manual? policy-config))
412 (single-type-signing? (knot-policy-configuration-single-type-signing?
413 policy-config))
414 (algorithm (knot-policy-configuration-algorithm policy-config))
415 (ksk-size (knot-policy-configuration-ksk-size policy-config))
416 (zsk-size (knot-policy-configuration-zsk-size policy-config))
417 (dnskey-ttl (knot-policy-configuration-dnskey-ttl policy-config))
418 (zsk-lifetime (knot-policy-configuration-zsk-lifetime policy-config))
419 (propagation-delay (knot-policy-configuration-propagation-delay
420 policy-config))
421 (rrsig-lifetime (knot-policy-configuration-rrsig-lifetime
422 policy-config))
423 (nsec3? (knot-policy-configuration-nsec3? policy-config))
424 (nsec3-iterations (knot-policy-configuration-nsec3-iterations
425 policy-config))
426 (nsec3-salt-length (knot-policy-configuration-nsec3-salt-length
427 policy-config))
428 (nsec3-salt-lifetime (knot-policy-configuration-nsec3-salt-lifetime
429 policy-config)))
430 (format #t " - id: ~a\n" id)
431 (format #t " keystore: ~a\n" keystore)
432 (format #t " manual: ~a\n" (if manual? "on" "off"))
433 (format #t " single-type-signing: ~a\n" (if single-type-signing?
434 "on" "off"))
435 (format #t " algorithm: ~a\n" algorithm)
436 (format #t " ksk-size: ~a\n" (number->string ksk-size))
437 (format #t " zsk-size: ~a\n" (number->string zsk-size))
438 (unless (eq? dnskey-ttl 'default)
439 (format #t " dnskey-ttl: ~a\n" dnskey-ttl))
440 (format #t " zsk-lifetime: ~a\n" zsk-lifetime)
441 (format #t " propagation-delay: ~a\n" propagation-delay)
442 (format #t " rrsig-lifetime: ~a\n" rrsig-lifetime)
443 (format #t " nsec3: ~a\n" (if nsec3? "on" "off"))
444 (format #t " nsec3-iterations: ~a\n"
445 (number->string nsec3-iterations))
446 (format #t " nsec3-salt-length: ~a\n"
447 (number->string nsec3-salt-length))
448 (format #t " nsec3-salt-lifetime: ~a\n" nsec3-salt-lifetime)))
449 policies))))
450
451 (define (knot-remote-config remotes)
452 (with-output-to-string
453 (lambda ()
454 (for-each
455 (lambda (remote-config)
456 (let ((id (knot-remote-configuration-id remote-config))
457 (address (knot-remote-configuration-address remote-config))
458 (via (knot-remote-configuration-via remote-config))
459 (key (knot-remote-configuration-key remote-config)))
460 (format #t " - id: ~a\n" id)
461 (unless (eq? address '())
462 (format #t " address: ~a\n" (format-string-list address)))
463 (unless (eq? via '())
464 (format #t " via: ~a\n" (format-string-list via)))
465 (if key
466 (format #t " key: ~a\n" key))))
467 remotes))))
468
469 (define (serialize-zone-entries entries)
470 (with-output-to-string
471 (lambda ()
472 (for-each
473 (lambda (entry)
474 (let ((name (zone-entry-name entry))
475 (ttl (zone-entry-ttl entry))
476 (class (zone-entry-class entry))
477 (type (zone-entry-type entry))
478 (data (zone-entry-data entry)))
479 (format #t "~a ~a ~a ~a ~a\n" name ttl class type data)))
480 entries))))
481
482 (define (serialize-zone-file zone domain)
483 (computed-file (string-append domain ".zone")
484 #~(begin
485 (call-with-output-file #$output
486 (lambda (port)
487 (format port "$ORIGIN ~a.\n"
488 #$(zone-file-origin zone))
489 (format port "@ IN SOA ~a ~a (~a ~a ~a ~a ~a)\n"
490 #$(zone-file-ns zone)
491 #$(zone-file-mail zone)
492 #$(zone-file-serial zone)
493 #$(zone-file-refresh zone)
494 #$(zone-file-retry zone)
495 #$(zone-file-expiry zone)
496 #$(zone-file-nx zone))
497 (format port "~a\n"
498 #$(serialize-zone-entries (zone-file-entries zone))))))))
499
500 (define (knot-zone-config zone)
501 (let ((content (knot-zone-configuration-zone zone)))
502 #~(with-output-to-string
503 (lambda ()
504 (let ((domain #$(knot-zone-configuration-domain zone))
505 (file #$(knot-zone-configuration-file zone))
506 (master (list #$@(knot-zone-configuration-master zone)))
507 (ddns-master #$(knot-zone-configuration-ddns-master zone))
508 (notify (list #$@(knot-zone-configuration-notify zone)))
509 (acl (list #$@(knot-zone-configuration-acl zone)))
510 (semantic-checks? #$(knot-zone-configuration-semantic-checks? zone))
511 (zonefile-sync #$(knot-zone-configuration-zonefile-sync zone))
512 (zonefile-load '#$(knot-zone-configuration-zonefile-load zone))
513 (journal-content #$(knot-zone-configuration-journal-content zone))
514 (max-journal-usage #$(knot-zone-configuration-max-journal-usage zone))
515 (max-journal-depth #$(knot-zone-configuration-max-journal-depth zone))
516 (max-zone-size #$(knot-zone-configuration-max-zone-size zone))
517 (dnssec-policy #$(knot-zone-configuration-dnssec-policy zone))
518 (serial-policy '#$(knot-zone-configuration-serial-policy zone)))
519 (format #t " - domain: ~a\n" domain)
520 (if (eq? master '())
521 ;; This server is a master
522 (if (equal? file "")
523 (format #t " file: ~a\n"
524 #$(serialize-zone-file content
525 (knot-zone-configuration-domain zone)))
526 (format #t " file: ~a\n" file))
527 ;; This server is a slave (has masters)
528 (begin
529 (format #t " master: ~a\n"
530 #$(format-string-list
531 (knot-zone-configuration-master zone)))
532 (if ddns-master (format #t " ddns-master ~a\n" ddns-master))))
533 (unless (eq? notify '())
534 (format #t " notify: ~a\n"
535 #$(format-string-list
536 (knot-zone-configuration-notify zone))))
537 (unless (eq? acl '())
538 (format #t " acl: ~a\n"
539 #$(format-string-list
540 (knot-zone-configuration-acl zone))))
541 (format #t " semantic-checks: ~a\n" (if semantic-checks? "on" "off"))
542 (if zonefile-sync
543 (format #t " zonefile-sync: ~a\n" zonefile-sync))
544 (if zonefile-load
545 (format #t " zonefile-load: ~a\n"
546 (symbol->string zonefile-load)))
547 (if journal-content
548 (format #t " journal-content: ~a\n"
549 (symbol->string journal-content)))
550 (if max-journal-usage
551 (format #t " max-journal-usage: ~a\n" max-journal-usage))
552 (if max-journal-depth
553 (format #t " max-journal-depth: ~a\n" max-journal-depth))
554 (if max-zone-size
555 (format #t " max-zone-size: ~a\n" max-zone-size))
556 (if dnssec-policy
557 (begin
558 (format #t " dnssec-signing: on\n")
559 (format #t " dnssec-policy: ~a\n" dnssec-policy)))
560 (format #t " serial-policy: ~a\n"
561 (symbol->string serial-policy)))))))
562
563 (define (knot-config-file config)
564 (verify-knot-configuration config)
565 (computed-file "knot.conf"
566 #~(begin
567 (call-with-output-file #$output
568 (lambda (port)
569 (for-each (lambda (inc)
570 (format port "include: ~a\n" inc))
571 '#$(knot-configuration-includes config))
572 (format port "server:\n")
573 (format port " rundir: ~a\n" #$(knot-configuration-run-directory config))
574 (format port " user: knot\n")
575 (format port " listen: ~a@~a\n"
576 #$(knot-configuration-listen-v4 config)
577 #$(knot-configuration-listen-port config))
578 (format port " listen: ~a@~a\n"
579 #$(knot-configuration-listen-v6 config)
580 #$(knot-configuration-listen-port config))
581 (format port "\nkey:\n")
582 (format port #$(knot-key-config (knot-configuration-keys config)))
583 (format port "\nkeystore:\n")
584 (format port #$(knot-keystore-config (knot-configuration-keystores config)))
585 (format port "\nacl:\n")
586 (format port #$(knot-acl-config (knot-configuration-acls config)))
587 (format port "\nremote:\n")
588 (format port #$(knot-remote-config (knot-configuration-remotes config)))
589 (format port "\npolicy:\n")
590 (format port #$(knot-policy-config (knot-configuration-policies config)))
591 (unless #$(eq? (knot-configuration-zones config) '())
592 (format port "\nzone:\n")
593 (format port "~a\n"
594 (string-concatenate
595 (list #$@(map knot-zone-config
596 (knot-configuration-zones config)))))))))))
597
598 (define %knot-accounts
599 (list (user-group (name "knot") (system? #t))
600 (user-account
601 (name "knot")
602 (group "knot")
603 (system? #t)
604 (comment "knot dns server user")
605 (home-directory "/var/empty")
606 (shell (file-append shadow "/sbin/nologin")))))
607
608 (define (knot-activation config)
609 (with-imported-modules (source-module-closure '((gnu build activation)))
610 #~(begin
611 (use-modules (gnu build activation))
612 (mkdir-p/perms #$(knot-configuration-run-directory config)
613 (getpwnam "knot") #o755)
614 (mkdir-p/perms "/var/lib/knot" (getpwnam "knot") #o755)
615 (mkdir-p/perms "/var/lib/knot/keys" (getpwnam "knot") #o755)
616 (mkdir-p/perms "/var/lib/knot/keys/keys" (getpwnam "knot") #o755))))
617
618 (define (knot-shepherd-service config)
619 (let* ((config-file (knot-config-file config))
620 (knot (knot-configuration-knot config)))
621 (list (shepherd-service
622 (documentation "Run the Knot DNS daemon.")
623 (provision '(knot dns))
624 (requirement '(networking))
625 (start #~(make-forkexec-constructor
626 (list (string-append #$knot "/sbin/knotd")
627 "-c" #$config-file)))
628 (stop #~(make-kill-destructor))))))
629
630 (define knot-service-type
631 (service-type (name 'knot)
632 (extensions
633 (list (service-extension shepherd-root-service-type
634 knot-shepherd-service)
635 (service-extension activation-service-type
636 knot-activation)
637 (service-extension account-service-type
638 (const %knot-accounts))))
639 (description
640 "Run @uref{https://www.knot-dns.cz/, Knot}, an authoritative
641 name server for the @acronym{DNS, Domain Name System}.")))
642
643 \f
644 ;;;
645 ;;; Knot Resolver.
646 ;;;
647
648 (define-record-type* <knot-resolver-configuration>
649 knot-resolver-configuration
650 make-knot-resolver-configuration
651 knot-resolver-configuration?
652 (package knot-resolver-configuration-package
653 (default knot-resolver))
654 (kresd-config-file knot-resolver-kresd-config-file
655 (default %kresd.conf))
656 (garbage-collection-interval knot-resolver-garbage-collection-interval
657 (default 1000)))
658
659 (define %kresd.conf
660 (plain-file "kresd.conf" "-- -*- mode: lua -*-
661 trust_anchors.add_file('/var/cache/knot-resolver/root.keys')
662 net = { '127.0.0.1', '::1' }
663 user('knot-resolver', 'knot-resolver')
664 modules = { 'hints > iterate', 'stats', 'predict' }
665 cache.size = 100 * MB
666 "))
667
668 (define %knot-resolver-accounts
669 (list (user-group
670 (name "knot-resolver")
671 (system? #t))
672 (user-account
673 (name "knot-resolver")
674 (group "knot-resolver")
675 (system? #t)
676 (home-directory "/var/cache/knot-resolver")
677 (shell (file-append shadow "/sbin/nologin")))))
678
679 (define (knot-resolver-activation config)
680 #~(begin
681 (use-modules (guix build utils))
682 (let ((rundir "/var/cache/knot-resolver")
683 (owner (getpwnam "knot-resolver")))
684 (mkdir-p rundir)
685 (chown rundir (passwd:uid owner) (passwd:gid owner)))))
686
687 (define knot-resolver-shepherd-services
688 (match-lambda
689 (($ <knot-resolver-configuration> package
690 kresd-config-file
691 garbage-collection-interval)
692 (list
693 (shepherd-service
694 (provision '(kresd))
695 (requirement '(networking))
696 (documentation "Run the Knot Resolver daemon.")
697 (start #~(make-forkexec-constructor
698 '(#$(file-append package "/sbin/kresd")
699 "-c" #$kresd-config-file "-n"
700 "/var/cache/knot-resolver")))
701 (stop #~(make-kill-destructor)))
702 (shepherd-service
703 (provision '(kres-cache-gc))
704 (requirement '(user-processes))
705 (documentation "Run the Knot Resolver Garbage Collector daemon.")
706 (start #~(make-forkexec-constructor
707 '(#$(file-append package "/sbin/kres-cache-gc")
708 "-d" #$(number->string garbage-collection-interval)
709 "-c" "/var/cache/knot-resolver")
710 #:user "knot-resolver"
711 #:group "knot-resolver"))
712 (stop #~(make-kill-destructor)))))))
713
714 (define knot-resolver-service-type
715 (service-type
716 (name 'knot-resolver)
717 (extensions
718 (list (service-extension shepherd-root-service-type
719 knot-resolver-shepherd-services)
720 (service-extension activation-service-type
721 knot-resolver-activation)
722 (service-extension account-service-type
723 (const %knot-resolver-accounts))))
724 (default-value (knot-resolver-configuration))
725 (description "Run the Knot DNS Resolver.")))
726
727 \f
728 ;;;
729 ;;; Dnsmasq.
730 ;;;
731
732 (define-record-type* <dnsmasq-configuration>
733 dnsmasq-configuration make-dnsmasq-configuration
734 dnsmasq-configuration?
735 (package dnsmasq-configuration-package
736 (default dnsmasq)) ;file-like
737 (no-hosts? dnsmasq-configuration-no-hosts?
738 (default #f)) ;boolean
739 (port dnsmasq-configuration-port
740 (default 53)) ;integer
741 (local-service? dnsmasq-configuration-local-service?
742 (default #t)) ;boolean
743 (listen-addresses dnsmasq-configuration-listen-address
744 (default '())) ;list of string
745 (resolv-file dnsmasq-configuration-resolv-file
746 (default "/etc/resolv.conf")) ;string
747 (no-resolv? dnsmasq-configuration-no-resolv?
748 (default #f)) ;boolean
749 (forward-private-reverse-lookup?
750 dnsmasq-configuration-forward-private-reverse-lookup?
751 (default #t)) ;boolean
752 (query-servers-in-order?
753 dnsmasq-configuration-query-servers-in-order?
754 (default #f)) ;boolean
755 (servers dnsmasq-configuration-servers
756 (default '())) ;list of string
757 (addresses dnsmasq-configuration-addresses
758 (default '())) ;list of string
759 (cache-size dnsmasq-configuration-cache-size
760 (default 150)) ;integer
761 (negative-cache? dnsmasq-configuration-negative-cache?
762 (default #t)) ;boolean
763 (cpe-id dnsmasq-configuration-cpe-id
764 (default #t)) ;string
765 (tftp-enable? dnsmasq-configuration-tftp-enable?
766 (default #f)) ;boolean
767 (tftp-no-fail? dnsmasq-configuration-tftp-no-fail?
768 (default #f)) ;boolean
769 (tftp-single-port? dnsmasq-configuration-tftp-single-port?
770 (default #f)) ;boolean
771 (tftp-secure? dnsmasq-tftp-secure?
772 (default #f)) ;boolean
773 (tftp-max dnsmasq-tftp-max
774 (default #f)) ;integer
775 (tftp-mtu dnsmasq-tftp-mtu
776 (default #f)) ;integer
777 (tftp-no-blocksize? dnsmasq-tftp-no-blocksize?
778 (default #f)) ;boolean
779 (tftp-lowercase? dnsmasq-tftp-lowercase?
780 (default #f)) ;boolean
781 (tftp-port-range dnsmasq-tftp-port-range
782 (default #f)) ;string
783 (tftp-root dnsmasq-tftp-root
784 (default "/var/empty,lo")) ;string
785 (tftp-unique-root dnsmasq-tftp-unique-root
786 (default #f))) ;"" or "ip" or "mac"
787
788 (define (dnsmasq-shepherd-service config)
789 (match-record config <dnsmasq-configuration>
790 (package
791 no-hosts?
792 port local-service? listen-addresses
793 resolv-file no-resolv?
794 forward-private-reverse-lookup? query-servers-in-order?
795 servers addresses
796 cache-size negative-cache?
797 cpe-id
798 tftp-enable? tftp-no-fail?
799 tftp-single-port? tftp-secure?
800 tftp-max tftp-mtu tftp-no-blocksize?
801 tftp-lowercase? tftp-port-range
802 tftp-root tftp-unique-root)
803 (shepherd-service
804 (provision '(dnsmasq))
805 (requirement '(networking))
806 (documentation "Run the dnsmasq DNS server.")
807 (start #~(make-forkexec-constructor
808 '(#$(file-append package "/sbin/dnsmasq")
809 "--keep-in-foreground"
810 "--pid-file=/run/dnsmasq.pid"
811 #$@(if no-hosts?
812 '("--no-hosts")
813 '())
814 #$(format #f "--port=~a" port)
815 #$@(if local-service?
816 '("--local-service")
817 '())
818 #$@(map (cut format #f "--listen-address=~a" <>)
819 listen-addresses)
820 #$(format #f "--resolv-file=~a" resolv-file)
821 #$@(if no-resolv?
822 '("--no-resolv")
823 '())
824 #$@(if forward-private-reverse-lookup?
825 '()
826 '("--bogus-priv"))
827 #$@(if query-servers-in-order?
828 '("--strict-order")
829 '())
830 #$@(map (cut format #f "--server=~a" <>)
831 servers)
832 #$@(map (cut format #f "--address=~a" <>)
833 addresses)
834 #$(format #f "--cache-size=~a" cache-size)
835 #$@(if negative-cache?
836 '()
837 '("--no-negcache"))
838 #$@(if cpe-id
839 (list (format #f "--add-cpe-id=~a" cpe-id))
840 '())
841 #$@(if tftp-enable?
842 '("--enable-tftp")
843 '())
844 #$@(if tftp-no-fail?
845 '("--tftp-no-fail")
846 '())
847 #$@(if tftp-single-port?
848 '("--tftp-single-port")
849 '())
850 #$@(if tftp-secure?
851 '("--tftp-secure?")
852 '())
853 #$@(if tftp-max
854 (list (format #f "--tftp-max=~a" tftp-max))
855 '())
856 #$@(if tftp-mtu
857 (list (format #f "--tftp-mtu=~a" tftp-mtu))
858 '())
859 #$@(if tftp-no-blocksize?
860 '("--tftp-no-blocksize")
861 '())
862 #$@(if tftp-lowercase?
863 '("--tftp-lowercase")
864 '())
865 #$@(if tftp-port-range
866 (list (format #f "--tftp-port-range=~a"
867 tftp-port-range))
868 '())
869 #$@(if tftp-root
870 (list (format #f "--tftp-root=~a" tftp-root))
871 '())
872 #$@(if tftp-unique-root
873 (list
874 (if (> (length tftp-unique-root) 0)
875 (format #f "--tftp-unique-root=~a" tftp-unique-root)
876 (format #f "--tftp-unique-root")))
877 '()))
878 #:pid-file "/run/dnsmasq.pid"))
879 (stop #~(make-kill-destructor)))))
880
881 (define (dnsmasq-activation config)
882 #~(begin
883 (use-modules (guix build utils))
884 ;; create directory to store dnsmasq lease file
885 (mkdir-p "/var/lib/misc")))
886
887 (define dnsmasq-service-type
888 (service-type
889 (name 'dnsmasq)
890 (extensions
891 (list (service-extension shepherd-root-service-type
892 (compose list dnsmasq-shepherd-service))
893 (service-extension activation-service-type
894 dnsmasq-activation)))
895 (default-value (dnsmasq-configuration))
896 (description "Run the dnsmasq DNS server.")))
897
898 \f
899 ;;;
900 ;;; ddclient
901 ;;;
902
903 (define (uglify-field-name field-name)
904 (string-delete #\? (symbol->string field-name)))
905
906 (define (serialize-field field-name val)
907 (when (not (member field-name '(group secret-file user)))
908 (format #t "~a=~a\n" (uglify-field-name field-name) val)))
909
910 (define (serialize-boolean field-name val)
911 (serialize-field field-name (if val "yes" "no")))
912
913 (define (serialize-integer field-name val)
914 (serialize-field field-name (number->string val)))
915
916 (define (serialize-string field-name val)
917 (if (and (string? val) (string=? val ""))
918 ""
919 (serialize-field field-name val)))
920
921 (define (serialize-list field-name val)
922 (if (null? val) "" (serialize-field field-name (string-join val))))
923
924 (define (serialize-extra-options extra-options)
925 (string-join extra-options "\n" 'suffix))
926
927 (define-configuration ddclient-configuration
928 (ddclient
929 (file-like ddclient)
930 "The ddclient package.")
931 (daemon
932 (integer 300)
933 "The period after which ddclient will retry to check IP and domain name.")
934 (syslog
935 (boolean #t)
936 "Use syslog for the output.")
937 (mail
938 (string "root")
939 "Mail to user.")
940 (mail-failure
941 (string "root")
942 "Mail failed update to user.")
943 (pid
944 (string "/var/run/ddclient/ddclient.pid")
945 "The ddclient PID file.")
946 (ssl
947 (boolean #t)
948 "Enable SSL support.")
949 (user
950 (string "ddclient")
951 "Specifies the user name or ID that is used when running ddclient
952 program.")
953 (group
954 (string "ddclient")
955 "Group of the user who will run the ddclient program.")
956 (secret-file
957 (string "/etc/ddclient/secrets.conf")
958 "Secret file which will be appended to @file{ddclient.conf} file. This
959 file contains credentials for use by ddclient. You are expected to create it
960 manually.")
961 (extra-options
962 (list '())
963 "Extra options will be appended to @file{ddclient.conf} file."))
964
965 (define (ddclient-account config)
966 "Return the user accounts and user groups for CONFIG."
967 (let ((ddclient-user (ddclient-configuration-user config))
968 (ddclient-group (ddclient-configuration-group config)))
969 (list (user-group
970 (name ddclient-group)
971 (system? #t))
972 (user-account
973 (name ddclient-user)
974 (system? #t)
975 (group ddclient-group)
976 (comment "ddclientd privilege separation user")
977 (home-directory (string-append "/var/run/" ddclient-user))))))
978
979 (define (ddclient-activation config)
980 "Return the activation GEXP for CONFIG."
981 (with-imported-modules '((guix build utils))
982 #~(begin
983 (use-modules (guix build utils)
984 (ice-9 rdelim))
985 (let ((ddclient-user
986 (passwd:uid (getpw #$(ddclient-configuration-user config))))
987 (ddclient-group
988 (passwd:gid (getpw #$(ddclient-configuration-group config))))
989 (ddclient-secret-file
990 #$(ddclient-configuration-secret-file config)))
991 ;; 'ddclient' complains about ddclient.conf file permissions, which
992 ;; rules out /gnu/store. Thus we copy the ddclient.conf to /etc.
993 (for-each (lambda (dir)
994 (mkdir-p dir)
995 (chmod dir #o700)
996 (chown dir ddclient-user ddclient-group))
997 '("/var/cache/ddclient" "/var/run/ddclient"
998 "/etc/ddclient"))
999 (with-output-to-file "/etc/ddclient/ddclient.conf"
1000 (lambda ()
1001 (display
1002 (string-append
1003 "# Generated by 'ddclient-service'.\n\n"
1004 #$(with-output-to-string
1005 (lambda ()
1006 (serialize-configuration config
1007 ddclient-configuration-fields)))
1008 (if (string-null? ddclient-secret-file)
1009 ""
1010 (format #f "\n\n# Appended from '~a'.\n\n~a"
1011 ddclient-secret-file
1012 (with-input-from-file ddclient-secret-file
1013 read-string)))))))
1014 (chmod "/etc/ddclient/ddclient.conf" #o600)
1015 (chown "/etc/ddclient/ddclient.conf"
1016 ddclient-user ddclient-group)))))
1017
1018 (define (ddclient-shepherd-service config)
1019 "Return a <shepherd-service> for ddclient with CONFIG."
1020 (let ((ddclient (ddclient-configuration-ddclient config))
1021 (ddclient-pid (ddclient-configuration-pid config))
1022 (ddclient-user (ddclient-configuration-user config))
1023 (ddclient-group (ddclient-configuration-group config)))
1024 (list (shepherd-service
1025 (provision '(ddclient))
1026 (documentation "Run ddclient daemon.")
1027 (start #~(make-forkexec-constructor
1028 (list #$(file-append ddclient "/bin/ddclient")
1029 "-foreground"
1030 "-file" "/etc/ddclient/ddclient.conf")
1031 #:pid-file #$ddclient-pid
1032 #:environment-variables
1033 (list "SSL_CERT_DIR=/run/current-system/profile\
1034 /etc/ssl/certs"
1035 "SSL_CERT_FILE=/run/current-system/profile\
1036 /etc/ssl/certs/ca-certificates.crt")
1037 #:user #$ddclient-user
1038 #:group #$ddclient-group))
1039 (stop #~(make-kill-destructor))))))
1040
1041 (define ddclient-service-type
1042 (service-type
1043 (name 'ddclient)
1044 (extensions
1045 (list (service-extension account-service-type
1046 ddclient-account)
1047 (service-extension shepherd-root-service-type
1048 ddclient-shepherd-service)
1049 (service-extension activation-service-type
1050 ddclient-activation)))
1051 (default-value (ddclient-configuration))
1052 (description "Configure address updating utility for dynamic DNS services,
1053 ddclient.")))
1054
1055 (define (generate-ddclient-documentation)
1056 (generate-documentation
1057 `((ddclient-configuration ,ddclient-configuration-fields))
1058 'ddclient-configuration))