services: Add ddclient service.
[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 (dnssec-policy knot-zone-configuration-dnssec-policy
187 (default #f))
188 (serial-policy knot-zone-configuration-serial-policy
189 (default 'increment)))
190
191 (define-record-type* <knot-remote-configuration>
192 knot-remote-configuration make-knot-remote-configuration
193 knot-remote-configuration?
194 (id knot-remote-configuration-id
195 (default ""))
196 (address knot-remote-configuration-address
197 (default '()))
198 (via knot-remote-configuration-via
199 (default '()))
200 (key knot-remote-configuration-key
201 (default #f)))
202
203 (define-record-type* <knot-configuration>
204 knot-configuration make-knot-configuration
205 knot-configuration?
206 (knot knot-configuration-knot
207 (default knot))
208 (run-directory knot-configuration-run-directory
209 (default "/var/run/knot"))
210 (listen-v4 knot-configuration-listen-v4
211 (default "0.0.0.0"))
212 (listen-v6 knot-configuration-listen-v6
213 (default "::"))
214 (listen-port knot-configuration-listen-port
215 (default 53))
216 (keys knot-configuration-keys
217 (default '()))
218 (keystores knot-configuration-keystores
219 (default '()))
220 (acls knot-configuration-acls
221 (default '()))
222 (remotes knot-configuration-remotes
223 (default '()))
224 (policies knot-configuration-policies
225 (default '()))
226 (zones knot-configuration-zones
227 (default '())))
228
229 (define-syntax define-zone-entries
230 (syntax-rules ()
231 ((_ id (name ttl class type data) ...)
232 (define id (list (make-zone-entry name ttl class type data) ...)))))
233
234 (define (error-out msg)
235 (raise (condition (&message (message msg)))))
236
237 (define (verify-knot-key-configuration key)
238 (unless (knot-key-configuration? key)
239 (error-out "keys must be a list of only knot-key-configuration."))
240 (let ((id (knot-key-configuration-id key)))
241 (unless (and (string? id) (not (equal? id "")))
242 (error-out "key id must be a non empty string.")))
243 (unless (memq '(#f hmac-md5 hmac-sha1 hmac-sha224 hmac-sha256 hmac-sha384 hmac-sha512)
244 (knot-key-configuration-algorithm key))
245 (error-out "algorithm must be one of: #f, 'hmac-md5, 'hmac-sha1,
246 'hmac-sha224, 'hmac-sha256, 'hmac-sha384 or 'hmac-sha512")))
247
248 (define (verify-knot-keystore-configuration keystore)
249 (unless (knot-keystore-configuration? keystore)
250 (error-out "keystores must be a list of only knot-keystore-configuration."))
251 (let ((id (knot-keystore-configuration-id keystore)))
252 (unless (and (string? id) (not (equal? id "")))
253 (error-out "keystore id must be a non empty string.")))
254 (unless (memq '(pem pkcs11)
255 (knot-keystore-configuration-backend keystore))
256 (error-out "backend must be one of: 'pem or 'pkcs11")))
257
258 (define (verify-knot-policy-configuration policy)
259 (unless (knot-policy-configuration? policy)
260 (error-out "policies must be a list of only knot-policy-configuration."))
261 (let ((id (knot-policy-configuration-id policy)))
262 (unless (and (string? id) (not (equal? id "")))
263 (error-out "policy id must be a non empty string."))))
264
265 (define (verify-knot-acl-configuration acl)
266 (unless (knot-acl-configuration? acl)
267 (error-out "acls must be a list of only knot-acl-configuration."))
268 (let ((id (knot-acl-configuration-id acl))
269 (address (knot-acl-configuration-address acl))
270 (key (knot-acl-configuration-key acl))
271 (action (knot-acl-configuration-action acl)))
272 (unless (and (string? id) (not (equal? id "")))
273 (error-out "acl id must be a non empty string."))
274 (unless (and (list? address)
275 (fold (lambda (x1 x2) (and (string? x1) (string? x2))) "" address))
276 (error-out "acl address must be a list of strings.")))
277 (unless (boolean? (knot-acl-configuration-deny? acl))
278 (error-out "deny? must be #t or #f.")))
279
280 (define (verify-knot-zone-configuration zone)
281 (unless (knot-zone-configuration? zone)
282 (error-out "zones must be a list of only knot-zone-configuration."))
283 (let ((domain (knot-zone-configuration-domain zone)))
284 (unless (and (string? domain) (not (equal? domain "")))
285 (error-out "zone domain must be a non empty string."))))
286
287 (define (verify-knot-remote-configuration remote)
288 (unless (knot-remote-configuration? remote)
289 (error-out "remotes must be a list of only knot-remote-configuration."))
290 (let ((id (knot-remote-configuration-id remote)))
291 (unless (and (string? id) (not (equal? id "")))
292 (error-out "remote id must be a non empty string."))))
293
294 (define (verify-knot-configuration config)
295 (unless (package? (knot-configuration-knot config))
296 (error-out "knot configuration field must be a package."))
297 (unless (string? (knot-configuration-run-directory config))
298 (error-out "run-directory must be a string."))
299 (unless (list? (knot-configuration-keys config))
300 (error-out "keys must be a list of knot-key-configuration."))
301 (for-each (lambda (key) (verify-knot-key-configuration key))
302 (knot-configuration-keys config))
303 (unless (list? (knot-configuration-keystores config))
304 (error-out "keystores must be a list of knot-keystore-configuration."))
305 (for-each (lambda (keystore) (verify-knot-keystore-configuration keystore))
306 (knot-configuration-keystores config))
307 (unless (list? (knot-configuration-acls config))
308 (error-out "acls must be a list of knot-acl-configuration."))
309 (for-each (lambda (acl) (verify-knot-acl-configuration acl))
310 (knot-configuration-acls config))
311 (unless (list? (knot-configuration-zones config))
312 (error-out "zones must be a list of knot-zone-configuration."))
313 (for-each (lambda (zone) (verify-knot-zone-configuration zone))
314 (knot-configuration-zones config))
315 (unless (list? (knot-configuration-policies config))
316 (error-out "policies must be a list of knot-policy-configuration."))
317 (for-each (lambda (policy) (verify-knot-policy-configuration policy))
318 (knot-configuration-policies config))
319 (unless (list? (knot-configuration-remotes config))
320 (error-out "remotes must be a list of knot-remote-configuration."))
321 (for-each (lambda (remote) (verify-knot-remote-configuration remote))
322 (knot-configuration-remotes config))
323 #t)
324
325 (define (format-string-list l)
326 "Formats a list of string in YAML"
327 (if (eq? l '())
328 ""
329 (let ((l (reverse l)))
330 (string-append
331 "["
332 (fold (lambda (x1 x2)
333 (string-append (if (symbol? x1) (symbol->string x1) x1) ", "
334 (if (symbol? x2) (symbol->string x2) x2)))
335 (car l) (cdr l))
336 "]"))))
337
338 (define (knot-acl-config acls)
339 (with-output-to-string
340 (lambda ()
341 (for-each
342 (lambda (acl-config)
343 (let ((id (knot-acl-configuration-id acl-config))
344 (address (knot-acl-configuration-address acl-config))
345 (key (knot-acl-configuration-key acl-config))
346 (action (knot-acl-configuration-action acl-config))
347 (deny? (knot-acl-configuration-deny? acl-config)))
348 (format #t " - id: ~a\n" id)
349 (unless (eq? address '())
350 (format #t " address: ~a\n" (format-string-list address)))
351 (unless (eq? key '())
352 (format #t " key: ~a\n" (format-string-list key)))
353 (unless (eq? action '())
354 (format #t " action: ~a\n" (format-string-list action)))
355 (format #t " deny: ~a\n" (if deny? "on" "off"))))
356 acls))))
357
358 (define (knot-key-config keys)
359 (with-output-to-string
360 (lambda ()
361 (for-each
362 (lambda (key-config)
363 (let ((id (knot-key-configuration-id key-config))
364 (algorithm (knot-key-configuration-algorithm key-config))
365 (secret (knot-key-configuration-secret key-config)))
366 (format #t " - id: ~a\n" id)
367 (if algorithm
368 (format #t " algorithm: ~a\n" (symbol->string algorithm)))
369 (format #t " secret: ~a\n" secret)))
370 keys))))
371
372 (define (knot-keystore-config keystores)
373 (with-output-to-string
374 (lambda ()
375 (for-each
376 (lambda (keystore-config)
377 (let ((id (knot-keystore-configuration-id keystore-config))
378 (backend (knot-keystore-configuration-backend keystore-config))
379 (config (knot-keystore-configuration-config keystore-config)))
380 (format #t " - id: ~a\n" id)
381 (format #t " backend: ~a\n" (symbol->string backend))
382 (format #t " config: \"~a\"\n" config)))
383 keystores))))
384
385 (define (knot-policy-config policies)
386 (with-output-to-string
387 (lambda ()
388 (for-each
389 (lambda (policy-config)
390 (let ((id (knot-policy-configuration-id policy-config))
391 (keystore (knot-policy-configuration-keystore policy-config))
392 (manual? (knot-policy-configuration-manual? policy-config))
393 (single-type-signing? (knot-policy-configuration-single-type-signing?
394 policy-config))
395 (algorithm (knot-policy-configuration-algorithm policy-config))
396 (ksk-size (knot-policy-configuration-ksk-size policy-config))
397 (zsk-size (knot-policy-configuration-zsk-size policy-config))
398 (dnskey-ttl (knot-policy-configuration-dnskey-ttl policy-config))
399 (zsk-lifetime (knot-policy-configuration-zsk-lifetime policy-config))
400 (propagation-delay (knot-policy-configuration-propagation-delay
401 policy-config))
402 (rrsig-lifetime (knot-policy-configuration-rrsig-lifetime
403 policy-config))
404 (nsec3? (knot-policy-configuration-nsec3? policy-config))
405 (nsec3-iterations (knot-policy-configuration-nsec3-iterations
406 policy-config))
407 (nsec3-salt-length (knot-policy-configuration-nsec3-salt-length
408 policy-config))
409 (nsec3-salt-lifetime (knot-policy-configuration-nsec3-salt-lifetime
410 policy-config)))
411 (format #t " - id: ~a\n" id)
412 (format #t " keystore: ~a\n" keystore)
413 (format #t " manual: ~a\n" (if manual? "on" "off"))
414 (format #t " single-type-signing: ~a\n" (if single-type-signing?
415 "on" "off"))
416 (format #t " algorithm: ~a\n" algorithm)
417 (format #t " ksk-size: ~a\n" (number->string ksk-size))
418 (format #t " zsk-size: ~a\n" (number->string zsk-size))
419 (unless (eq? dnskey-ttl 'default)
420 (format #t " dnskey-ttl: ~a\n" dnskey-ttl))
421 (format #t " zsk-lifetime: ~a\n" zsk-lifetime)
422 (format #t " propagation-delay: ~a\n" propagation-delay)
423 (format #t " rrsig-lifetime: ~a\n" rrsig-lifetime)
424 (format #t " nsec3: ~a\n" (if nsec3? "on" "off"))
425 (format #t " nsec3-iterations: ~a\n"
426 (number->string nsec3-iterations))
427 (format #t " nsec3-salt-length: ~a\n"
428 (number->string nsec3-salt-length))
429 (format #t " nsec3-salt-lifetime: ~a\n" nsec3-salt-lifetime)))
430 policies))))
431
432 (define (knot-remote-config remotes)
433 (with-output-to-string
434 (lambda ()
435 (for-each
436 (lambda (remote-config)
437 (let ((id (knot-remote-configuration-id remote-config))
438 (address (knot-remote-configuration-address remote-config))
439 (via (knot-remote-configuration-via remote-config))
440 (key (knot-remote-configuration-key remote-config)))
441 (format #t " - id: ~a\n" id)
442 (unless (eq? address '())
443 (format #t " address: ~a\n" (format-string-list address)))
444 (unless (eq? via '())
445 (format #t " via: ~a\n" (format-string-list via)))
446 (if key
447 (format #t " key: ~a\n" key))))
448 remotes))))
449
450 (define (serialize-zone-entries entries)
451 (with-output-to-string
452 (lambda ()
453 (for-each
454 (lambda (entry)
455 (let ((name (zone-entry-name entry))
456 (ttl (zone-entry-ttl entry))
457 (class (zone-entry-class entry))
458 (type (zone-entry-type entry))
459 (data (zone-entry-data entry)))
460 (format #t "~a ~a ~a ~a ~a\n" name ttl class type data)))
461 entries))))
462
463 (define (serialize-zone-file zone domain)
464 (computed-file (string-append domain ".zone")
465 #~(begin
466 (call-with-output-file #$output
467 (lambda (port)
468 (format port "$ORIGIN ~a.\n"
469 #$(zone-file-origin zone))
470 (format port "@ IN SOA ~a ~a (~a ~a ~a ~a ~a)\n"
471 #$(zone-file-ns zone)
472 #$(zone-file-mail zone)
473 #$(zone-file-serial zone)
474 #$(zone-file-refresh zone)
475 #$(zone-file-retry zone)
476 #$(zone-file-expiry zone)
477 #$(zone-file-nx zone))
478 (format port "~a\n"
479 #$(serialize-zone-entries (zone-file-entries zone))))))))
480
481 (define (knot-zone-config zone)
482 (let ((content (knot-zone-configuration-zone zone)))
483 #~(with-output-to-string
484 (lambda ()
485 (let ((domain #$(knot-zone-configuration-domain zone))
486 (file #$(knot-zone-configuration-file zone))
487 (master (list #$@(knot-zone-configuration-master zone)))
488 (ddns-master #$(knot-zone-configuration-ddns-master zone))
489 (notify (list #$@(knot-zone-configuration-notify zone)))
490 (acl (list #$@(knot-zone-configuration-acl zone)))
491 (semantic-checks? #$(knot-zone-configuration-semantic-checks? zone))
492 (disable-any? #$(knot-zone-configuration-disable-any? zone))
493 (dnssec-policy #$(knot-zone-configuration-dnssec-policy zone))
494 (serial-policy '#$(knot-zone-configuration-serial-policy zone)))
495 (format #t " - domain: ~a\n" domain)
496 (if (eq? master '())
497 ;; This server is a master
498 (if (equal? file "")
499 (format #t " file: ~a\n"
500 #$(serialize-zone-file content
501 (knot-zone-configuration-domain zone)))
502 (format #t " file: ~a\n" file))
503 ;; This server is a slave (has masters)
504 (begin
505 (format #t " master: ~a\n"
506 #$(format-string-list
507 (knot-zone-configuration-master zone)))
508 (if ddns-master (format #t " ddns-master ~a\n" ddns-master))))
509 (unless (eq? notify '())
510 (format #t " notify: ~a\n"
511 #$(format-string-list
512 (knot-zone-configuration-notify zone))))
513 (unless (eq? acl '())
514 (format #t " acl: ~a\n"
515 #$(format-string-list
516 (knot-zone-configuration-acl zone))))
517 (format #t " semantic-checks: ~a\n" (if semantic-checks? "on" "off"))
518 (format #t " disable-any: ~a\n" (if disable-any? "on" "off"))
519 (if dnssec-policy
520 (begin
521 (format #t " dnssec-signing: on\n")
522 (format #t " dnssec-policy: ~a\n" dnssec-policy)))
523 (format #t " serial-policy: ~a\n"
524 (symbol->string serial-policy)))))))
525
526 (define (knot-config-file config)
527 (verify-knot-configuration config)
528 (computed-file "knot.conf"
529 #~(begin
530 (call-with-output-file #$output
531 (lambda (port)
532 (format port "server:\n")
533 (format port " rundir: ~a\n" #$(knot-configuration-run-directory config))
534 (format port " user: knot\n")
535 (format port " listen: ~a@~a\n"
536 #$(knot-configuration-listen-v4 config)
537 #$(knot-configuration-listen-port config))
538 (format port " listen: ~a@~a\n"
539 #$(knot-configuration-listen-v6 config)
540 #$(knot-configuration-listen-port config))
541 (format port "\nkey:\n")
542 (format port #$(knot-key-config (knot-configuration-keys config)))
543 (format port "\nkeystore:\n")
544 (format port #$(knot-keystore-config (knot-configuration-keystores config)))
545 (format port "\nacl:\n")
546 (format port #$(knot-acl-config (knot-configuration-acls config)))
547 (format port "\nremote:\n")
548 (format port #$(knot-remote-config (knot-configuration-remotes config)))
549 (format port "\npolicy:\n")
550 (format port #$(knot-policy-config (knot-configuration-policies config)))
551 (unless #$(eq? (knot-configuration-zones config) '())
552 (format port "\nzone:\n")
553 (format port "~a\n"
554 (string-concatenate
555 (list #$@(map knot-zone-config
556 (knot-configuration-zones config)))))))))))
557
558 (define %knot-accounts
559 (list (user-group (name "knot") (system? #t))
560 (user-account
561 (name "knot")
562 (group "knot")
563 (system? #t)
564 (comment "knot dns server user")
565 (home-directory "/var/empty")
566 (shell (file-append shadow "/sbin/nologin")))))
567
568 (define (knot-activation config)
569 #~(begin
570 (use-modules (guix build utils))
571 (define (mkdir-p/perms directory owner perms)
572 (mkdir-p directory)
573 (chown directory (passwd:uid owner) (passwd:gid owner))
574 (chmod directory perms))
575 (mkdir-p/perms #$(knot-configuration-run-directory config)
576 (getpwnam "knot") #o755)
577 (mkdir-p/perms "/var/lib/knot" (getpwnam "knot") #o755)
578 (mkdir-p/perms "/var/lib/knot/keys" (getpwnam "knot") #o755)
579 (mkdir-p/perms "/var/lib/knot/keys/keys" (getpwnam "knot") #o755)))
580
581 (define (knot-shepherd-service config)
582 (let* ((config-file (knot-config-file config))
583 (knot (knot-configuration-knot config)))
584 (list (shepherd-service
585 (documentation "Run the Knot DNS daemon.")
586 (provision '(knot dns))
587 (requirement '(networking))
588 (start #~(make-forkexec-constructor
589 (list (string-append #$knot "/sbin/knotd")
590 "-c" #$config-file)))
591 (stop #~(make-kill-destructor))))))
592
593 (define knot-service-type
594 (service-type (name 'knot)
595 (extensions
596 (list (service-extension shepherd-root-service-type
597 knot-shepherd-service)
598 (service-extension activation-service-type
599 knot-activation)
600 (service-extension account-service-type
601 (const %knot-accounts))))))
602
603 \f
604 ;;;
605 ;;; Dnsmasq.
606 ;;;
607
608 (define-record-type* <dnsmasq-configuration>
609 dnsmasq-configuration make-dnsmasq-configuration
610 dnsmasq-configuration?
611 (package dnsmasq-configuration-package
612 (default dnsmasq)) ;package
613 (no-hosts? dnsmasq-configuration-no-hosts?
614 (default #f)) ;boolean
615 (port dnsmasq-configuration-port
616 (default 53)) ;integer
617 (local-service? dnsmasq-configuration-local-service?
618 (default #t)) ;boolean
619 (listen-addresses dnsmasq-configuration-listen-address
620 (default '())) ;list of string
621 (resolv-file dnsmasq-configuration-resolv-file
622 (default "/etc/resolv.conf")) ;string
623 (no-resolv? dnsmasq-configuration-no-resolv?
624 (default #f)) ;boolean
625 (servers dnsmasq-configuration-servers
626 (default '())) ;list of string
627 (cache-size dnsmasq-configuration-cache-size
628 (default 150)) ;integer
629 (negative-cache? dnsmasq-configuration-negative-cache?
630 (default #t))) ;boolean
631
632 (define dnsmasq-shepherd-service
633 (match-lambda
634 (($ <dnsmasq-configuration> package
635 no-hosts?
636 port local-service? listen-addresses
637 resolv-file no-resolv? servers
638 cache-size negative-cache?)
639 (shepherd-service
640 (provision '(dnsmasq))
641 (requirement '(networking))
642 (documentation "Run the dnsmasq DNS server.")
643 (start #~(make-forkexec-constructor
644 '(#$(file-append package "/sbin/dnsmasq")
645 "--keep-in-foreground"
646 "--pid-file=/run/dnsmasq.pid"
647 #$@(if no-hosts?
648 '("--no-hosts")
649 '())
650 #$(format #f "--port=~a" port)
651 #$@(if local-service?
652 '("--local-service")
653 '())
654 #$@(map (cut format #f "--listen-address=~a" <>)
655 listen-addresses)
656 #$(format #f "--resolv-file=~a" resolv-file)
657 #$@(if no-resolv?
658 '("--no-resolv")
659 '())
660 #$@(map (cut format #f "--server=~a" <>)
661 servers)
662 #$(format #f "--cache-size=~a" cache-size)
663 #$@(if negative-cache?
664 '()
665 '("--no-negcache")))
666 #:pid-file "/run/dnsmasq.pid"))
667 (stop #~(make-kill-destructor))))))
668
669 (define dnsmasq-service-type
670 (service-type
671 (name 'dnsmasq)
672 (extensions
673 (list (service-extension shepherd-root-service-type
674 (compose list dnsmasq-shepherd-service))))
675 (default-value (dnsmasq-configuration))
676 (description "Run the dnsmasq DNS server.")))
677
678 \f
679 ;;;
680 ;;; ddclient
681 ;;;
682
683 (define (uglify-field-name field-name)
684 (string-delete #\? (symbol->string field-name)))
685
686 (define (serialize-field field-name val)
687 (format #t "~a=~a\n" (uglify-field-name field-name) val))
688
689 (define (serialize-boolean field-name val)
690 (serialize-field field-name (if val "yes" "no")))
691
692 (define (serialize-integer field-name val)
693 (serialize-field field-name (number->string val)))
694
695 (define (serialize-string field-name val)
696 (if (and (string? val) (string=? val ""))
697 ""
698 (serialize-field field-name val)))
699
700 (define (serialize-list field-name val)
701 (if (null? val) "" (serialize-field field-name (string-join val))))
702
703 (define (serialize-extra-options extra-options)
704 (string-join extra-options "\n" 'suffix))
705
706 (define-configuration ddclient-configuration
707 (ddclient
708 (package ddclient)
709 "The ddclient package.")
710 (daemon
711 (integer 300)
712 "The period after which ddclient will retry to check IP and domain name.")
713 (syslog
714 (boolean #t)
715 "Use syslog for the output.")
716 (mail
717 (string "root")
718 "Mail to user.")
719 (mail-failure
720 (string "root")
721 "Mail failed update to user.")
722 (pid
723 (string "/var/run/ddclient/ddclient.pid")
724 "The ddclient PID file.")
725 (ssl
726 (boolean #t)
727 "Enable SSL support.")
728 (user
729 (string "ddclient")
730 "Specifies the user name or ID that is used when running ddclient
731 program.")
732 (group
733 (string "ddclient")
734 "Group of the user who will run the ddclient program.")
735 (secret-file
736 (string "/etc/ddclient/secrets.conf")
737 "Secret file which will be appended to @file{ddclient.conf} file. This
738 file contains credentials for use by ddclient. You are expected to create it
739 manually.")
740 (extra-options
741 (list '())
742 "Extra options will be appended to @file{ddclient.conf} file."))
743
744 (define (ddclient-account config)
745 "Return the user accounts and user groups for CONFIG."
746 (let ((ddclient-user (ddclient-configuration-user config))
747 (ddclient-group (ddclient-configuration-group config)))
748 (list (user-group
749 (name ddclient-group)
750 (system? #t))
751 (user-account
752 (name ddclient-user)
753 (system? #t)
754 (group ddclient-group)
755 (comment "ddclientd privilege separation user")
756 (home-directory (string-append "/var/run/" ddclient-user))))))
757
758 (define (ddclient-activation config)
759 "Return the activation GEXP for CONFIG."
760 (with-imported-modules '((guix build utils)
761 (ice-9 rdelim))
762 #~(begin
763 (use-modules (guix build utils)
764 (ice-9 rdelim))
765 (let ((ddclient-user
766 #$(passwd:uid (getpw (ddclient-configuration-user config))))
767 (ddclient-group
768 #$(passwd:gid (getpw (ddclient-configuration-group config))))
769 (ddclient-secret-file
770 #$(ddclient-configuration-secret-file config)))
771 ;; 'ddclient' complains about ddclient.conf file permissions, which
772 ;; rules out /gnu/store. Thus we copy the ddclient.conf to /etc.
773 (for-each (lambda (dir)
774 (mkdir-p dir)
775 (chmod dir #o700)
776 (chown dir ddclient-user ddclient-group))
777 '("/var/cache/ddclient" "/var/run/ddclient"
778 "/etc/ddclient"))
779 (with-output-to-file "/etc/ddclient/ddclient.conf"
780 (lambda ()
781 (display
782 (string-append
783 "# Generated by 'ddclient-service'.\n\n"
784 #$(with-output-to-string
785 (lambda ()
786 (serialize-configuration config
787 ddclient-configuration-fields)))
788 (if (string-null? ddclient-secret-file)
789 ""
790 (format #f "\n\n# Appended from '~a'.\n\n~a"
791 ddclient-secret-file
792 (with-input-from-file ddclient-secret-file
793 read-string)))))))
794 (chmod "/etc/ddclient/ddclient.conf" #o600)
795 (chown "/etc/ddclient/ddclient.conf"
796 ddclient-user ddclient-group)))))
797
798 (define (ddclient-shepherd-service config)
799 "Return a <shepherd-service> for ddclient with CONFIG."
800 (let ((ddclient (ddclient-configuration-ddclient config))
801 (ddclient-pid (ddclient-configuration-pid config))
802 (ddclient-user (ddclient-configuration-user config))
803 (ddclient-group (ddclient-configuration-group config)))
804 (list (shepherd-service
805 (provision '(ddclient))
806 (documentation "Run ddclient daemon.")
807 (start #~(make-forkexec-constructor
808 (list #$(file-append ddclient "/bin/ddclient")
809 "-foreground"
810 "-file" "/etc/ddclient/ddclient.conf")
811 #:pid-file #$ddclient-pid
812 #:environment-variables
813 (list "SSL_CERT_DIR=/run/current-system/profile\
814 /etc/ssl/certs"
815 "SSL_CERT_FILE=/run/current-system/profile\
816 /etc/ssl/certs/ca-certificates.crt")
817 #:user #$ddclient-user
818 #:group #$ddclient-group))
819 (stop #~(make-kill-destructor))))))
820
821 (define ddclient-service-type
822 (service-type
823 (name 'ddclient)
824 (extensions
825 (list (service-extension account-service-type
826 ddclient-account)
827 (service-extension shepherd-root-service-type
828 ddclient-shepherd-service)
829 (service-extension activation-service-type
830 ddclient-activation)))
831 (default-value (ddclient-configuration))
832 (description "Configure address updating utility for dynamic DNS services,
833 ddclient.")))
834
835 (define (generate-ddclient-documentation)
836 (generate-documentation
837 `((ddclient-configuration ,ddclient-configuration-fields))
838 'ddclient-configuration))