Merge remote-tracking branch master into core-updates
[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 knot-resolver-service-type
49 knot-resolver-configuration
50
51 dnsmasq-service-type
52 dnsmasq-configuration
53
54 ddclient-service-type
55 ddclient-configuration))
56
57 ;;;
58 ;;; Knot DNS.
59 ;;;
60
61 (define-record-type* <knot-key-configuration>
62 knot-key-configuration make-knot-key-configuration
63 knot-key-configuration?
64 (id knot-key-configuration-id
65 (default ""))
66 (algorithm knot-key-configuration-algorithm
67 (default #f)); one of #f, or an algorithm name
68 (secret knot-key-configuration-secret
69 (default "")))
70
71 (define-record-type* <knot-acl-configuration>
72 knot-acl-configuration make-knot-acl-configuration
73 knot-acl-configuration?
74 (id knot-acl-configuration-id
75 (default ""))
76 (address knot-acl-configuration-address
77 (default '()))
78 (key knot-acl-configuration-key
79 (default '()))
80 (action knot-acl-configuration-action
81 (default '()))
82 (deny? knot-acl-configuration-deny?
83 (default #f)))
84
85 (define-record-type* <zone-entry>
86 zone-entry make-zone-entry
87 zone-entry?
88 (name zone-entry-name
89 (default "@"))
90 (ttl zone-entry-ttl
91 (default ""))
92 (class zone-entry-class
93 (default "IN"))
94 (type zone-entry-type
95 (default "A"))
96 (data zone-entry-data
97 (default "")))
98
99 (define-record-type* <zone-file>
100 zone-file make-zone-file
101 zone-file?
102 (entries zone-file-entries
103 (default '()))
104 (origin zone-file-origin
105 (default ""))
106 (ns zone-file-ns
107 (default "ns"))
108 (mail zone-file-mail
109 (default "hostmaster"))
110 (serial zone-file-serial
111 (default 1))
112 (refresh zone-file-refresh
113 (default (* 2 24 3600)))
114 (retry zone-file-retry
115 (default (* 15 60)))
116 (expiry zone-file-expiry
117 (default (* 2 7 24 3600)))
118 (nx zone-file-nx
119 (default 3600)))
120 (define-record-type* <knot-keystore-configuration>
121 knot-keystore-configuration make-knot-keystore-configuration
122 knot-keystore-configuration?
123 (id knot-keystore-configuration-id
124 (default ""))
125 (backend knot-keystore-configuration-backend
126 (default 'pem))
127 (config knot-keystore-configuration-config
128 (default "/var/lib/knot/keys/keys")))
129
130 (define-record-type* <knot-policy-configuration>
131 knot-policy-configuration make-knot-policy-configuration
132 knot-policy-configuration?
133 (id knot-policy-configuration-id
134 (default ""))
135 (keystore knot-policy-configuration-keystore
136 (default "default"))
137 (manual? knot-policy-configuration-manual?
138 (default #f))
139 (single-type-signing? knot-policy-configuration-single-type-signing?
140 (default #f))
141 (algorithm knot-policy-configuration-algorithm
142 (default "ecdsap256sha256"))
143 (ksk-size knot-policy-configuration-ksk-size
144 (default 256))
145 (zsk-size knot-policy-configuration-zsk-size
146 (default 256))
147 (dnskey-ttl knot-policy-configuration-dnskey-ttl
148 (default 'default))
149 (zsk-lifetime knot-policy-configuration-zsk-lifetime
150 (default (* 30 24 3600)))
151 (propagation-delay knot-policy-configuration-propagation-delay
152 (default (* 24 3600)))
153 (rrsig-lifetime knot-policy-configuration-rrsig-lifetime
154 (default (* 14 24 3600)))
155 (rrsig-refresh knot-policy-configuration-rrsig-refresh
156 (default (* 7 24 3600)))
157 (nsec3? knot-policy-configuration-nsec3?
158 (default #f))
159 (nsec3-iterations knot-policy-configuration-nsec3-iterations
160 (default 5))
161 (nsec3-salt-length knot-policy-configuration-nsec3-salt-length
162 (default 8))
163 (nsec3-salt-lifetime knot-policy-configuration-nsec3-salt-lifetime
164 (default (* 30 24 3600))))
165
166 (define-record-type* <knot-zone-configuration>
167 knot-zone-configuration make-knot-zone-configuration
168 knot-zone-configuration?
169 (domain knot-zone-configuration-domain
170 (default ""))
171 (file knot-zone-configuration-file
172 (default "")) ; the file where this zone is saved.
173 (zone knot-zone-configuration-zone
174 (default (zone-file))) ; initial content of the zone file
175 (master knot-zone-configuration-master
176 (default '()))
177 (ddns-master knot-zone-configuration-ddns-master
178 (default #f))
179 (notify knot-zone-configuration-notify
180 (default '()))
181 (acl knot-zone-configuration-acl
182 (default '()))
183 (semantic-checks? knot-zone-configuration-semantic-checks?
184 (default #f))
185 (disable-any? knot-zone-configuration-disable-any?
186 (default #f))
187 (zonefile-sync knot-zone-configuration-zonefile-sync
188 (default 0))
189 (zonefile-load knot-zone-configuration-zonefile-load
190 (default #f))
191 (journal-content knot-zone-configuration-journal-content
192 (default #f))
193 (max-journal-usage knot-zone-configuration-max-journal-usage
194 (default #f))
195 (max-journal-depth knot-zone-configuration-max-journal-depth
196 (default #f))
197 (max-zone-size knot-zone-configuration-max-zone-size
198 (default #f))
199 (dnssec-policy knot-zone-configuration-dnssec-policy
200 (default #f))
201 (serial-policy knot-zone-configuration-serial-policy
202 (default 'increment)))
203
204 (define-record-type* <knot-remote-configuration>
205 knot-remote-configuration make-knot-remote-configuration
206 knot-remote-configuration?
207 (id knot-remote-configuration-id
208 (default ""))
209 (address knot-remote-configuration-address
210 (default '()))
211 (via knot-remote-configuration-via
212 (default '()))
213 (key knot-remote-configuration-key
214 (default #f)))
215
216 (define-record-type* <knot-configuration>
217 knot-configuration make-knot-configuration
218 knot-configuration?
219 (knot knot-configuration-knot
220 (default knot))
221 (run-directory knot-configuration-run-directory
222 (default "/var/run/knot"))
223 (includes knot-configuration-includes
224 (default '()))
225 (listen-v4 knot-configuration-listen-v4
226 (default "0.0.0.0"))
227 (listen-v6 knot-configuration-listen-v6
228 (default "::"))
229 (listen-port knot-configuration-listen-port
230 (default 53))
231 (keys knot-configuration-keys
232 (default '()))
233 (keystores knot-configuration-keystores
234 (default '()))
235 (acls knot-configuration-acls
236 (default '()))
237 (remotes knot-configuration-remotes
238 (default '()))
239 (policies knot-configuration-policies
240 (default '()))
241 (zones knot-configuration-zones
242 (default '())))
243
244 (define-syntax define-zone-entries
245 (syntax-rules ()
246 ((_ id (name ttl class type data) ...)
247 (define id (list (make-zone-entry name ttl class type data) ...)))))
248
249 (define (error-out msg)
250 (raise (condition (&message (message msg)))))
251
252 (define (verify-knot-key-configuration key)
253 (unless (knot-key-configuration? key)
254 (error-out "keys must be a list of only knot-key-configuration."))
255 (let ((id (knot-key-configuration-id key)))
256 (unless (and (string? id) (not (equal? id "")))
257 (error-out "key id must be a non empty string.")))
258 (unless (memq '(#f hmac-md5 hmac-sha1 hmac-sha224 hmac-sha256 hmac-sha384 hmac-sha512)
259 (knot-key-configuration-algorithm key))
260 (error-out "algorithm must be one of: #f, 'hmac-md5, 'hmac-sha1,
261 'hmac-sha224, 'hmac-sha256, 'hmac-sha384 or 'hmac-sha512")))
262
263 (define (verify-knot-keystore-configuration keystore)
264 (unless (knot-keystore-configuration? keystore)
265 (error-out "keystores must be a list of only knot-keystore-configuration."))
266 (let ((id (knot-keystore-configuration-id keystore)))
267 (unless (and (string? id) (not (equal? id "")))
268 (error-out "keystore id must be a non empty string.")))
269 (unless (memq '(pem pkcs11)
270 (knot-keystore-configuration-backend keystore))
271 (error-out "backend must be one of: 'pem or 'pkcs11")))
272
273 (define (verify-knot-policy-configuration policy)
274 (unless (knot-policy-configuration? policy)
275 (error-out "policies must be a list of only knot-policy-configuration."))
276 (let ((id (knot-policy-configuration-id policy)))
277 (unless (and (string? id) (not (equal? id "")))
278 (error-out "policy id must be a non empty string."))))
279
280 (define (verify-knot-acl-configuration acl)
281 (unless (knot-acl-configuration? acl)
282 (error-out "acls must be a list of only knot-acl-configuration."))
283 (let ((id (knot-acl-configuration-id acl))
284 (address (knot-acl-configuration-address acl))
285 (key (knot-acl-configuration-key acl))
286 (action (knot-acl-configuration-action acl)))
287 (unless (and (string? id) (not (equal? id "")))
288 (error-out "acl id must be a non empty string."))
289 (unless (and (list? address)
290 (fold (lambda (x1 x2) (and (string? x1) (string? x2))) "" address))
291 (error-out "acl address must be a list of strings.")))
292 (unless (boolean? (knot-acl-configuration-deny? acl))
293 (error-out "deny? must be #t or #f.")))
294
295 (define (verify-knot-zone-configuration zone)
296 (unless (knot-zone-configuration? zone)
297 (error-out "zones must be a list of only knot-zone-configuration."))
298 (let ((domain (knot-zone-configuration-domain zone)))
299 (unless (and (string? domain) (not (equal? domain "")))
300 (error-out "zone domain must be a non empty string."))))
301
302 (define (verify-knot-remote-configuration remote)
303 (unless (knot-remote-configuration? remote)
304 (error-out "remotes must be a list of only knot-remote-configuration."))
305 (let ((id (knot-remote-configuration-id remote)))
306 (unless (and (string? id) (not (equal? id "")))
307 (error-out "remote id must be a non empty string."))))
308
309 (define (verify-knot-configuration config)
310 (unless (package? (knot-configuration-knot config))
311 (error-out "knot configuration field must be a package."))
312 (unless (string? (knot-configuration-run-directory config))
313 (error-out "run-directory must be a string."))
314 (unless (list? (knot-configuration-includes config))
315 (error-out "includes must be a list of strings or file-like objects."))
316 (unless (list? (knot-configuration-keys config))
317 (error-out "keys must be a list of knot-key-configuration."))
318 (for-each (lambda (key) (verify-knot-key-configuration key))
319 (knot-configuration-keys config))
320 (unless (list? (knot-configuration-keystores config))
321 (error-out "keystores must be a list of knot-keystore-configuration."))
322 (for-each (lambda (keystore) (verify-knot-keystore-configuration keystore))
323 (knot-configuration-keystores config))
324 (unless (list? (knot-configuration-acls config))
325 (error-out "acls must be a list of knot-acl-configuration."))
326 (for-each (lambda (acl) (verify-knot-acl-configuration acl))
327 (knot-configuration-acls config))
328 (unless (list? (knot-configuration-zones config))
329 (error-out "zones must be a list of knot-zone-configuration."))
330 (for-each (lambda (zone) (verify-knot-zone-configuration zone))
331 (knot-configuration-zones config))
332 (unless (list? (knot-configuration-policies config))
333 (error-out "policies must be a list of knot-policy-configuration."))
334 (for-each (lambda (policy) (verify-knot-policy-configuration policy))
335 (knot-configuration-policies config))
336 (unless (list? (knot-configuration-remotes config))
337 (error-out "remotes must be a list of knot-remote-configuration."))
338 (for-each (lambda (remote) (verify-knot-remote-configuration remote))
339 (knot-configuration-remotes config))
340 #t)
341
342 (define (format-string-list l)
343 "Formats a list of string in YAML"
344 (if (eq? l '())
345 ""
346 (let ((l (reverse l)))
347 (string-append
348 "["
349 (fold (lambda (x1 x2)
350 (string-append (if (symbol? x1) (symbol->string x1) x1) ", "
351 (if (symbol? x2) (symbol->string x2) x2)))
352 (if (symbol? (car l)) (symbol->string (car l)) (car l)) (cdr l))
353 "]"))))
354
355 (define (knot-acl-config acls)
356 (with-output-to-string
357 (lambda ()
358 (for-each
359 (lambda (acl-config)
360 (let ((id (knot-acl-configuration-id acl-config))
361 (address (knot-acl-configuration-address acl-config))
362 (key (knot-acl-configuration-key acl-config))
363 (action (knot-acl-configuration-action acl-config))
364 (deny? (knot-acl-configuration-deny? acl-config)))
365 (format #t " - id: ~a\n" id)
366 (unless (eq? address '())
367 (format #t " address: ~a\n" (format-string-list address)))
368 (unless (eq? key '())
369 (format #t " key: ~a\n" (format-string-list key)))
370 (unless (eq? action '())
371 (format #t " action: ~a\n" (format-string-list action)))
372 (format #t " deny: ~a\n" (if deny? "on" "off"))))
373 acls))))
374
375 (define (knot-key-config keys)
376 (with-output-to-string
377 (lambda ()
378 (for-each
379 (lambda (key-config)
380 (let ((id (knot-key-configuration-id key-config))
381 (algorithm (knot-key-configuration-algorithm key-config))
382 (secret (knot-key-configuration-secret key-config)))
383 (format #t " - id: ~a\n" id)
384 (if algorithm
385 (format #t " algorithm: ~a\n" (symbol->string algorithm)))
386 (format #t " secret: ~a\n" secret)))
387 keys))))
388
389 (define (knot-keystore-config keystores)
390 (with-output-to-string
391 (lambda ()
392 (for-each
393 (lambda (keystore-config)
394 (let ((id (knot-keystore-configuration-id keystore-config))
395 (backend (knot-keystore-configuration-backend keystore-config))
396 (config (knot-keystore-configuration-config keystore-config)))
397 (format #t " - id: ~a\n" id)
398 (format #t " backend: ~a\n" (symbol->string backend))
399 (format #t " config: \"~a\"\n" config)))
400 keystores))))
401
402 (define (knot-policy-config policies)
403 (with-output-to-string
404 (lambda ()
405 (for-each
406 (lambda (policy-config)
407 (let ((id (knot-policy-configuration-id policy-config))
408 (keystore (knot-policy-configuration-keystore policy-config))
409 (manual? (knot-policy-configuration-manual? policy-config))
410 (single-type-signing? (knot-policy-configuration-single-type-signing?
411 policy-config))
412 (algorithm (knot-policy-configuration-algorithm policy-config))
413 (ksk-size (knot-policy-configuration-ksk-size policy-config))
414 (zsk-size (knot-policy-configuration-zsk-size policy-config))
415 (dnskey-ttl (knot-policy-configuration-dnskey-ttl policy-config))
416 (zsk-lifetime (knot-policy-configuration-zsk-lifetime policy-config))
417 (propagation-delay (knot-policy-configuration-propagation-delay
418 policy-config))
419 (rrsig-lifetime (knot-policy-configuration-rrsig-lifetime
420 policy-config))
421 (nsec3? (knot-policy-configuration-nsec3? policy-config))
422 (nsec3-iterations (knot-policy-configuration-nsec3-iterations
423 policy-config))
424 (nsec3-salt-length (knot-policy-configuration-nsec3-salt-length
425 policy-config))
426 (nsec3-salt-lifetime (knot-policy-configuration-nsec3-salt-lifetime
427 policy-config)))
428 (format #t " - id: ~a\n" id)
429 (format #t " keystore: ~a\n" keystore)
430 (format #t " manual: ~a\n" (if manual? "on" "off"))
431 (format #t " single-type-signing: ~a\n" (if single-type-signing?
432 "on" "off"))
433 (format #t " algorithm: ~a\n" algorithm)
434 (format #t " ksk-size: ~a\n" (number->string ksk-size))
435 (format #t " zsk-size: ~a\n" (number->string zsk-size))
436 (unless (eq? dnskey-ttl 'default)
437 (format #t " dnskey-ttl: ~a\n" dnskey-ttl))
438 (format #t " zsk-lifetime: ~a\n" zsk-lifetime)
439 (format #t " propagation-delay: ~a\n" propagation-delay)
440 (format #t " rrsig-lifetime: ~a\n" rrsig-lifetime)
441 (format #t " nsec3: ~a\n" (if nsec3? "on" "off"))
442 (format #t " nsec3-iterations: ~a\n"
443 (number->string nsec3-iterations))
444 (format #t " nsec3-salt-length: ~a\n"
445 (number->string nsec3-salt-length))
446 (format #t " nsec3-salt-lifetime: ~a\n" nsec3-salt-lifetime)))
447 policies))))
448
449 (define (knot-remote-config remotes)
450 (with-output-to-string
451 (lambda ()
452 (for-each
453 (lambda (remote-config)
454 (let ((id (knot-remote-configuration-id remote-config))
455 (address (knot-remote-configuration-address remote-config))
456 (via (knot-remote-configuration-via remote-config))
457 (key (knot-remote-configuration-key remote-config)))
458 (format #t " - id: ~a\n" id)
459 (unless (eq? address '())
460 (format #t " address: ~a\n" (format-string-list address)))
461 (unless (eq? via '())
462 (format #t " via: ~a\n" (format-string-list via)))
463 (if key
464 (format #t " key: ~a\n" key))))
465 remotes))))
466
467 (define (serialize-zone-entries entries)
468 (with-output-to-string
469 (lambda ()
470 (for-each
471 (lambda (entry)
472 (let ((name (zone-entry-name entry))
473 (ttl (zone-entry-ttl entry))
474 (class (zone-entry-class entry))
475 (type (zone-entry-type entry))
476 (data (zone-entry-data entry)))
477 (format #t "~a ~a ~a ~a ~a\n" name ttl class type data)))
478 entries))))
479
480 (define (serialize-zone-file zone domain)
481 (computed-file (string-append domain ".zone")
482 #~(begin
483 (call-with-output-file #$output
484 (lambda (port)
485 (format port "$ORIGIN ~a.\n"
486 #$(zone-file-origin zone))
487 (format port "@ IN SOA ~a ~a (~a ~a ~a ~a ~a)\n"
488 #$(zone-file-ns zone)
489 #$(zone-file-mail zone)
490 #$(zone-file-serial zone)
491 #$(zone-file-refresh zone)
492 #$(zone-file-retry zone)
493 #$(zone-file-expiry zone)
494 #$(zone-file-nx zone))
495 (format port "~a\n"
496 #$(serialize-zone-entries (zone-file-entries zone))))))))
497
498 (define (knot-zone-config zone)
499 (let ((content (knot-zone-configuration-zone zone)))
500 #~(with-output-to-string
501 (lambda ()
502 (let ((domain #$(knot-zone-configuration-domain zone))
503 (file #$(knot-zone-configuration-file zone))
504 (master (list #$@(knot-zone-configuration-master zone)))
505 (ddns-master #$(knot-zone-configuration-ddns-master zone))
506 (notify (list #$@(knot-zone-configuration-notify zone)))
507 (acl (list #$@(knot-zone-configuration-acl zone)))
508 (semantic-checks? #$(knot-zone-configuration-semantic-checks? zone))
509 (disable-any? #$(knot-zone-configuration-disable-any? zone))
510 (zonefile-sync #$(knot-zone-configuration-zonefile-sync zone))
511 (zonefile-load '#$(knot-zone-configuration-zonefile-load zone))
512 (journal-content #$(knot-zone-configuration-journal-content zone))
513 (max-journal-usage #$(knot-zone-configuration-max-journal-usage zone))
514 (max-journal-depth #$(knot-zone-configuration-max-journal-depth zone))
515 (max-zone-size #$(knot-zone-configuration-max-zone-size zone))
516 (dnssec-policy #$(knot-zone-configuration-dnssec-policy zone))
517 (serial-policy '#$(knot-zone-configuration-serial-policy zone)))
518 (format #t " - domain: ~a\n" domain)
519 (if (eq? master '())
520 ;; This server is a master
521 (if (equal? file "")
522 (format #t " file: ~a\n"
523 #$(serialize-zone-file content
524 (knot-zone-configuration-domain zone)))
525 (format #t " file: ~a\n" file))
526 ;; This server is a slave (has masters)
527 (begin
528 (format #t " master: ~a\n"
529 #$(format-string-list
530 (knot-zone-configuration-master zone)))
531 (if ddns-master (format #t " ddns-master ~a\n" ddns-master))))
532 (unless (eq? notify '())
533 (format #t " notify: ~a\n"
534 #$(format-string-list
535 (knot-zone-configuration-notify zone))))
536 (unless (eq? acl '())
537 (format #t " acl: ~a\n"
538 #$(format-string-list
539 (knot-zone-configuration-acl zone))))
540 (format #t " semantic-checks: ~a\n" (if semantic-checks? "on" "off"))
541 (format #t " disable-any: ~a\n" (if disable-any? "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 #~(begin
610 (use-modules (guix build utils))
611 (define (mkdir-p/perms directory owner perms)
612 (mkdir-p directory)
613 (chown directory (passwd:uid owner) (passwd:gid owner))
614 (chmod directory perms))
615 (mkdir-p/perms #$(knot-configuration-run-directory config)
616 (getpwnam "knot") #o755)
617 (mkdir-p/perms "/var/lib/knot" (getpwnam "knot") #o755)
618 (mkdir-p/perms "/var/lib/knot/keys" (getpwnam "knot") #o755)
619 (mkdir-p/perms "/var/lib/knot/keys/keys" (getpwnam "knot") #o755)))
620
621 (define (knot-shepherd-service config)
622 (let* ((config-file (knot-config-file config))
623 (knot (knot-configuration-knot config)))
624 (list (shepherd-service
625 (documentation "Run the Knot DNS daemon.")
626 (provision '(knot dns))
627 (requirement '(networking))
628 (start #~(make-forkexec-constructor
629 (list (string-append #$knot "/sbin/knotd")
630 "-c" #$config-file)))
631 (stop #~(make-kill-destructor))))))
632
633 (define knot-service-type
634 (service-type (name 'knot)
635 (extensions
636 (list (service-extension shepherd-root-service-type
637 knot-shepherd-service)
638 (service-extension activation-service-type
639 knot-activation)
640 (service-extension account-service-type
641 (const %knot-accounts))))))
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 net = { '127.0.0.1', '::1' }
662 user('knot-resolver', 'knot-resolver')
663 modules = { 'hints > iterate', 'stats', 'predict' }
664 cache.size = 100 * MB
665 "))
666
667 (define %knot-resolver-accounts
668 (list (user-group
669 (name "knot-resolver")
670 (system? #t))
671 (user-account
672 (name "knot-resolver")
673 (group "knot-resolver")
674 (system? #t)
675 (home-directory "/var/cache/knot-resolver")
676 (shell (file-append shadow "/sbin/nologin")))))
677
678 (define (knot-resolver-activation config)
679 #~(begin
680 (use-modules (guix build utils))
681 (let ((rundir "/var/cache/knot-resolver")
682 (owner (getpwnam "knot-resolver")))
683 (mkdir-p rundir)
684 (chown rundir (passwd:uid owner) (passwd:gid owner)))))
685
686 (define knot-resolver-shepherd-services
687 (match-lambda
688 (($ <knot-resolver-configuration> package
689 kresd-config-file
690 garbage-collection-interval)
691 (list
692 (shepherd-service
693 (provision '(kresd))
694 (requirement '(networking))
695 (documentation "Run the Knot Resolver daemon.")
696 (start #~(make-forkexec-constructor
697 '(#$(file-append package "/sbin/kresd")
698 "-c" #$kresd-config-file "-f" "1"
699 "/var/cache/knot-resolver")))
700 (stop #~(make-kill-destructor)))
701 (shepherd-service
702 (provision '(kres-cache-gc))
703 (requirement '(user-processes))
704 (documentation "Run the Knot Resolver Garbage Collector daemon.")
705 (start #~(make-forkexec-constructor
706 '(#$(file-append package "/sbin/kres-cache-gc")
707 "-d" #$(number->string garbage-collection-interval)
708 "-c" "/var/cache/knot-resolver")
709 #:user "knot-resolver"
710 #:group "knot-resolver"))
711 (stop #~(make-kill-destructor)))))))
712
713 (define knot-resolver-service-type
714 (service-type
715 (name 'knot-resolver)
716 (extensions
717 (list (service-extension shepherd-root-service-type
718 knot-resolver-shepherd-services)
719 (service-extension activation-service-type
720 knot-resolver-activation)
721 (service-extension account-service-type
722 (const %knot-resolver-accounts))))
723 (default-value (knot-resolver-configuration))
724 (description "Run the Knot DNS Resolver.")))
725
726 \f
727 ;;;
728 ;;; Dnsmasq.
729 ;;;
730
731 (define-record-type* <dnsmasq-configuration>
732 dnsmasq-configuration make-dnsmasq-configuration
733 dnsmasq-configuration?
734 (package dnsmasq-configuration-package
735 (default dnsmasq)) ;package
736 (no-hosts? dnsmasq-configuration-no-hosts?
737 (default #f)) ;boolean
738 (port dnsmasq-configuration-port
739 (default 53)) ;integer
740 (local-service? dnsmasq-configuration-local-service?
741 (default #t)) ;boolean
742 (listen-addresses dnsmasq-configuration-listen-address
743 (default '())) ;list of string
744 (resolv-file dnsmasq-configuration-resolv-file
745 (default "/etc/resolv.conf")) ;string
746 (no-resolv? dnsmasq-configuration-no-resolv?
747 (default #f)) ;boolean
748 (servers dnsmasq-configuration-servers
749 (default '())) ;list of string
750 (cache-size dnsmasq-configuration-cache-size
751 (default 150)) ;integer
752 (negative-cache? dnsmasq-configuration-negative-cache?
753 (default #t))) ;boolean
754
755 (define dnsmasq-shepherd-service
756 (match-lambda
757 (($ <dnsmasq-configuration> package
758 no-hosts?
759 port local-service? listen-addresses
760 resolv-file no-resolv? servers
761 cache-size negative-cache?)
762 (shepherd-service
763 (provision '(dnsmasq))
764 (requirement '(networking))
765 (documentation "Run the dnsmasq DNS server.")
766 (start #~(make-forkexec-constructor
767 '(#$(file-append package "/sbin/dnsmasq")
768 "--keep-in-foreground"
769 "--pid-file=/run/dnsmasq.pid"
770 #$@(if no-hosts?
771 '("--no-hosts")
772 '())
773 #$(format #f "--port=~a" port)
774 #$@(if local-service?
775 '("--local-service")
776 '())
777 #$@(map (cut format #f "--listen-address=~a" <>)
778 listen-addresses)
779 #$(format #f "--resolv-file=~a" resolv-file)
780 #$@(if no-resolv?
781 '("--no-resolv")
782 '())
783 #$@(map (cut format #f "--server=~a" <>)
784 servers)
785 #$(format #f "--cache-size=~a" cache-size)
786 #$@(if negative-cache?
787 '()
788 '("--no-negcache")))
789 #:pid-file "/run/dnsmasq.pid"))
790 (stop #~(make-kill-destructor))))))
791
792 (define dnsmasq-service-type
793 (service-type
794 (name 'dnsmasq)
795 (extensions
796 (list (service-extension shepherd-root-service-type
797 (compose list dnsmasq-shepherd-service))))
798 (default-value (dnsmasq-configuration))
799 (description "Run the dnsmasq DNS server.")))
800
801 \f
802 ;;;
803 ;;; ddclient
804 ;;;
805
806 (define (uglify-field-name field-name)
807 (string-delete #\? (symbol->string field-name)))
808
809 (define (serialize-field field-name val)
810 (when (not (member field-name '(group secret-file user)))
811 (format #t "~a=~a\n" (uglify-field-name field-name) val)))
812
813 (define (serialize-boolean field-name val)
814 (serialize-field field-name (if val "yes" "no")))
815
816 (define (serialize-integer field-name val)
817 (serialize-field field-name (number->string val)))
818
819 (define (serialize-string field-name val)
820 (if (and (string? val) (string=? val ""))
821 ""
822 (serialize-field field-name val)))
823
824 (define (serialize-list field-name val)
825 (if (null? val) "" (serialize-field field-name (string-join val))))
826
827 (define (serialize-extra-options extra-options)
828 (string-join extra-options "\n" 'suffix))
829
830 (define-configuration ddclient-configuration
831 (ddclient
832 (package ddclient)
833 "The ddclient package.")
834 (daemon
835 (integer 300)
836 "The period after which ddclient will retry to check IP and domain name.")
837 (syslog
838 (boolean #t)
839 "Use syslog for the output.")
840 (mail
841 (string "root")
842 "Mail to user.")
843 (mail-failure
844 (string "root")
845 "Mail failed update to user.")
846 (pid
847 (string "/var/run/ddclient/ddclient.pid")
848 "The ddclient PID file.")
849 (ssl
850 (boolean #t)
851 "Enable SSL support.")
852 (user
853 (string "ddclient")
854 "Specifies the user name or ID that is used when running ddclient
855 program.")
856 (group
857 (string "ddclient")
858 "Group of the user who will run the ddclient program.")
859 (secret-file
860 (string "/etc/ddclient/secrets.conf")
861 "Secret file which will be appended to @file{ddclient.conf} file. This
862 file contains credentials for use by ddclient. You are expected to create it
863 manually.")
864 (extra-options
865 (list '())
866 "Extra options will be appended to @file{ddclient.conf} file."))
867
868 (define (ddclient-account config)
869 "Return the user accounts and user groups for CONFIG."
870 (let ((ddclient-user (ddclient-configuration-user config))
871 (ddclient-group (ddclient-configuration-group config)))
872 (list (user-group
873 (name ddclient-group)
874 (system? #t))
875 (user-account
876 (name ddclient-user)
877 (system? #t)
878 (group ddclient-group)
879 (comment "ddclientd privilege separation user")
880 (home-directory (string-append "/var/run/" ddclient-user))))))
881
882 (define (ddclient-activation config)
883 "Return the activation GEXP for CONFIG."
884 (with-imported-modules '((guix build utils)
885 (ice-9 rdelim))
886 #~(begin
887 (use-modules (guix build utils)
888 (ice-9 rdelim))
889 (let ((ddclient-user
890 (passwd:uid (getpw #$(ddclient-configuration-user config))))
891 (ddclient-group
892 (passwd:gid (getpw #$(ddclient-configuration-group config))))
893 (ddclient-secret-file
894 #$(ddclient-configuration-secret-file config)))
895 ;; 'ddclient' complains about ddclient.conf file permissions, which
896 ;; rules out /gnu/store. Thus we copy the ddclient.conf to /etc.
897 (for-each (lambda (dir)
898 (mkdir-p dir)
899 (chmod dir #o700)
900 (chown dir ddclient-user ddclient-group))
901 '("/var/cache/ddclient" "/var/run/ddclient"
902 "/etc/ddclient"))
903 (with-output-to-file "/etc/ddclient/ddclient.conf"
904 (lambda ()
905 (display
906 (string-append
907 "# Generated by 'ddclient-service'.\n\n"
908 #$(with-output-to-string
909 (lambda ()
910 (serialize-configuration config
911 ddclient-configuration-fields)))
912 (if (string-null? ddclient-secret-file)
913 ""
914 (format #f "\n\n# Appended from '~a'.\n\n~a"
915 ddclient-secret-file
916 (with-input-from-file ddclient-secret-file
917 read-string)))))))
918 (chmod "/etc/ddclient/ddclient.conf" #o600)
919 (chown "/etc/ddclient/ddclient.conf"
920 ddclient-user ddclient-group)))))
921
922 (define (ddclient-shepherd-service config)
923 "Return a <shepherd-service> for ddclient with CONFIG."
924 (let ((ddclient (ddclient-configuration-ddclient config))
925 (ddclient-pid (ddclient-configuration-pid config))
926 (ddclient-user (ddclient-configuration-user config))
927 (ddclient-group (ddclient-configuration-group config)))
928 (list (shepherd-service
929 (provision '(ddclient))
930 (documentation "Run ddclient daemon.")
931 (start #~(make-forkexec-constructor
932 (list #$(file-append ddclient "/bin/ddclient")
933 "-foreground"
934 "-file" "/etc/ddclient/ddclient.conf")
935 #:pid-file #$ddclient-pid
936 #:environment-variables
937 (list "SSL_CERT_DIR=/run/current-system/profile\
938 /etc/ssl/certs"
939 "SSL_CERT_FILE=/run/current-system/profile\
940 /etc/ssl/certs/ca-certificates.crt")
941 #:user #$ddclient-user
942 #:group #$ddclient-group))
943 (stop #~(make-kill-destructor))))))
944
945 (define ddclient-service-type
946 (service-type
947 (name 'ddclient)
948 (extensions
949 (list (service-extension account-service-type
950 ddclient-account)
951 (service-extension shepherd-root-service-type
952 ddclient-shepherd-service)
953 (service-extension activation-service-type
954 ddclient-activation)))
955 (default-value (ddclient-configuration))
956 (description "Configure address updating utility for dynamic DNS services,
957 ddclient.")))
958
959 (define (generate-ddclient-documentation)
960 (generate-documentation
961 `((ddclient-configuration ,ddclient-configuration-fields))
962 'ddclient-configuration))