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