gnu: Add knot-service-type.
* gnu/services/dns.scm: New file. * gnu/local.mk (GNU_SYSTEM_MODULES): Add it. * doc/guix.texi (DNS Services): New subsubsection.
This commit is contained in:
		
							parent
							
								
									d771ba62f8
								
							
						
					
					
						commit
						ba69e8f7ce
					
				
					 3 changed files with 1004 additions and 0 deletions
				
			
		
							
								
								
									
										410
									
								
								doc/guix.texi
									
										
									
									
									
								
							
							
						
						
									
										410
									
								
								doc/guix.texi
									
										
									
									
									
								
							|  | @ -218,6 +218,7 @@ Services | |||
| * Messaging Services::          Messaging services. | ||||
| * Kerberos Services::           Kerberos services. | ||||
| * Web Services::                Web servers. | ||||
| * DNS Services::                DNS daemons. | ||||
| * VPN Services::                VPN daemons. | ||||
| * Network File System::         NFS related services. | ||||
| * Continuous Integration::      The Cuirass service. | ||||
|  | @ -8737,6 +8738,7 @@ declaration. | |||
| * Messaging Services::          Messaging services. | ||||
| * Kerberos Services::           Kerberos services. | ||||
| * Web Services::                Web servers. | ||||
| * DNS Services::                DNS daemons. | ||||
| * VPN Services::                VPN daemons. | ||||
| * Network File System::         NFS related services. | ||||
| * Continuous Integration::      The Cuirass service. | ||||
|  | @ -13520,6 +13522,414 @@ Whether the server should add its configuration to response. | |||
| @end table | ||||
| @end deftp | ||||
| 
 | ||||
| @node DNS Services | ||||
| @subsubsection DNS Services | ||||
| @cindex DNS (domain name system) | ||||
| @cindex domain name system (DNS) | ||||
| 
 | ||||
| The @code{(gnu services dns)} module provides services related to the | ||||
| @dfn{domain name system} (DNS).  It provides a server service for hosting | ||||
| an @emph{authoritative} DNS server for multiple zones, slave or master. | ||||
| This service uses @uref{https://www.knot-dns.cz/, Knot DNS}. | ||||
| 
 | ||||
| An example configuration of an authoritative server for two zones, one master | ||||
| and one slave, is: | ||||
| 
 | ||||
| @lisp | ||||
| (define-zone-entries example.org.zone | ||||
| ;; Name TTL Class Type Data | ||||
|   ("@@"  ""  "IN"  "A"  "127.0.0.1") | ||||
|   ("@@"  ""  "IN"  "NS" "ns") | ||||
|   ("ns" ""  "IN"  "A"  "127.0.0.1")) | ||||
| 
 | ||||
| (define master-zone | ||||
|   (knot-zone-configuration | ||||
|     (domain "example.org") | ||||
|     (zone (zone-file | ||||
|             (origin "example.org") | ||||
|             (entries example.org.zone))))) | ||||
| 
 | ||||
| (define slave-zone | ||||
|   (knot-zone-configuration | ||||
|     (domain "plop.org") | ||||
|     (dnssec-policy "default") | ||||
|     (master (list "plop-master")))) | ||||
| 
 | ||||
| (define plop-master | ||||
|   (knot-remote-configuration | ||||
|     (id "plop-master") | ||||
|     (address (list "208.76.58.171")))) | ||||
| 
 | ||||
| (operating-system | ||||
|   ;; ... | ||||
|   (services (cons* (service knot-service-type | ||||
|                      (knot-confifguration | ||||
|                        (remotes (list plop-master)) | ||||
|                        (zones (list master-zone slave-zone)))) | ||||
|                    ;; ... | ||||
|                    %base-services))) | ||||
| @end lisp | ||||
| 
 | ||||
| @deffn {Scheme Variable} knot-service-type | ||||
| This is the type for the Knot DNS server. | ||||
| 
 | ||||
| Knot DNS is an authoritative DNS server, meaning that it can serve multiple | ||||
| zones, that is to say domain names you would buy from a registrar.  This server | ||||
| is not a resolver, meaning that it can only resolve names for which it is | ||||
| authoritative.  This server can be configured to serve zones as a master server | ||||
| or a slave server as a per-zone basis.  Slave zones will get their data from | ||||
| masters, and will serve it as an authoritative server.  From the point of view | ||||
| of a resolver, there is no difference between master and slave. | ||||
| 
 | ||||
| The following data types are used to configure the Knot DNS server: | ||||
| @end deffn | ||||
| 
 | ||||
| @deftp {Data Type} knot-key-configuration | ||||
| Data type representing a key. | ||||
| This type has the following parameters: | ||||
| 
 | ||||
| @table @asis | ||||
| @item @code{id} (default: @code{""}) | ||||
| An identifier for other configuration fields to refer to this key. IDs must | ||||
| be unique and must not be empty. | ||||
| 
 | ||||
| @item @code{algorithm} (default: @code{#f}) | ||||
| The algorithm to use.  Choose between @code{#f}, @code{'hmac-md5}, | ||||
| @code{'hmac-sha1}, @code{'hmac-sha224}, @code{'hmac-sha256}, @code{'hmac-sha384} | ||||
| and @code{'hmac-sha512}. | ||||
| 
 | ||||
| @item @code{secret} (default: @code{""}) | ||||
| The secret key itself. | ||||
| 
 | ||||
| @end table | ||||
| @end deftp | ||||
| 
 | ||||
| @deftp {Data Type} knot-acl-configuration | ||||
| Data type representing an Access Control List (ACL) configuration. | ||||
| This type has the following parameters: | ||||
| 
 | ||||
| @table @asis | ||||
| @item @code{id} (default: @code{""}) | ||||
| An identifier for ether configuration fields to refer to this key. IDs must be | ||||
| unique and must not be empty. | ||||
| 
 | ||||
| @item @code{address} (default: @code{'()}) | ||||
| An ordered list of IP addresses, network subnets, or network ranges represented | ||||
| with strings.  The query must match one of them.  Empty value means that | ||||
| address match is not required. | ||||
| 
 | ||||
| @item @code{key} (default: @code{'()}) | ||||
| An ordered list of references to keys represented with strings.  The string | ||||
| must match a key ID defined in a @code{knot-key-configuration}.  No key means | ||||
| that a key is not require to match that ACL. | ||||
| 
 | ||||
| @item @code{action} (default: @code{'()}) | ||||
| An ordered list of actions that are permitted or forbidden by this ACL.  Possible | ||||
| values are lists of zero or more elements from @code{'transfer}, @code{'notify} | ||||
| and @code{'update}. | ||||
| 
 | ||||
| @item @code{deny?} (default: @code{#f}) | ||||
| When true, the ACL defines restrictions.  Listed actions are forbidden.  When | ||||
| false, listed actions are allowed. | ||||
| 
 | ||||
| @end table | ||||
| @end deftp | ||||
| 
 | ||||
| @deftp {Data Type} zone-entry | ||||
| Data type represnting a record entry in a zone file. | ||||
| This type has the following parameters: | ||||
| 
 | ||||
| @table @asis | ||||
| @item @code{name} (default: @code{"@@"}) | ||||
| The name of the record.  @code{"@@"} refers to the origin of the zone.  Names | ||||
| are relative to the origin of the zone.  For example, in the @code{example.org} | ||||
| zone, @code{"ns.example.org"} actually refers to @code{ns.example.org.example.org}. | ||||
| Names ending with a dot are absolute, which means that @code{"ns.example.org."} | ||||
| refers to @code{ns.example.org}. | ||||
| 
 | ||||
| @item @code{ttl} (default: @code{""}) | ||||
| The Time-To-Live (TTL) of this record.  If not set, the default TTL is used. | ||||
| 
 | ||||
| @item @code{class} (default: @code{"IN"}) | ||||
| The class of the record.  Knot currently supports only @code{"IN"} and | ||||
| partially @code{"CH"}. | ||||
| 
 | ||||
| @item @code{type} (default: @code{"A"}) | ||||
| The type of the record.  Common types include A (IPv4 address), AAAA (IPv6 | ||||
| address), NS (Name Server) and MX (Mail eXchange).  Many other types are | ||||
| defined. | ||||
| 
 | ||||
| @item @code{data} (default: @code{""}) | ||||
| The data contained in the record.  For instance an IP address associated with | ||||
| an A record, or a domain name associated with an NS record.  Remember that | ||||
| domain names are relative to the origin unless they end with a dot. | ||||
| 
 | ||||
| @end table | ||||
| @end deftp | ||||
| 
 | ||||
| @deftp {Data Type} zone-file | ||||
| Data type representing the content of a zone file. | ||||
| This type has the following parameters: | ||||
| 
 | ||||
| @table @asis | ||||
| @item @code{entries} (default: @code{'()}) | ||||
| The list of entries.  The SOA record is taken care of, so you don't need to | ||||
| put it in the list of entries.  This list should probably contain an entry | ||||
| for your primary authoritative DNS server.  Other than using a list of entries | ||||
| directly, you can use @code{define-zone-entries} to define a object containing | ||||
| the list of entries more easily, that you can later pass to the @code{entries} | ||||
| field of the @code{zone-file}. | ||||
| 
 | ||||
| @item @code{origin} (default: @code{""}) | ||||
| The name of your zone.  This parameter cannot be empty. | ||||
| 
 | ||||
| @item @code{ns} (default: @code{"ns"}) | ||||
| The domain of your primary authoritative DNS server.  The name is relative to | ||||
| the origin, unless it ends with a dot.  It is mandatory that this primary | ||||
| DNS server corresponds to an NS record in the zone and that it is associated | ||||
| to an IP address in the list of entries. | ||||
| 
 | ||||
| @item @code{mail} (default: @code{"hostmaster"}) | ||||
| An email address people can contact you at, as the owner of the zone.  This | ||||
| is translated as @code{<mail>@@<origin>}. | ||||
| 
 | ||||
| @item @code{serial} (default: @code{1}) | ||||
| The serial number of the zone.  As this is used to keep track of changes by | ||||
| both slaves and resolvers, it is mandatory that it @emph{never} decreases. | ||||
| Always increment it when you make a change in your zone. | ||||
| 
 | ||||
| @item @code{refresh} (default: @code{"2d"}) | ||||
| The frequency at which slaves will do a zone transfer.  This value can be | ||||
| a number of seconds or a number of some unit between: | ||||
| @itemize | ||||
| @item m: minute | ||||
| @item h: hour | ||||
| @item d: day | ||||
| @item w: week | ||||
| @end itemize | ||||
| 
 | ||||
| @item @code{retry} (default: @code{"15m"}) | ||||
| The period after which a slave will retry to contact its master when it fails | ||||
| to do so a first time. | ||||
| 
 | ||||
| @item @code{expiry} (default: @code{"2w"}) | ||||
| Default TTL of records.  Existing records are considered correct for at most | ||||
| this amount of time.  After this period, resolvers will invalidate their cache | ||||
| and check again that it still exists. | ||||
| 
 | ||||
| @item @code{nx} (default: @code{"1h"}) | ||||
| Default TTL of inexistant records.  This delay is usually short because you want | ||||
| your new domains to reach everyone quickly. | ||||
| 
 | ||||
| @end table | ||||
| @end deftp | ||||
| 
 | ||||
| @deftp {Data Type} knot-remote-configuration | ||||
| Data type representing a remote configuration. | ||||
| This type has the following parameters: | ||||
| 
 | ||||
| @table @asis | ||||
| @item @code{id} (default: @code{""}) | ||||
| An identifier for other configuration fields to refer to this remote. IDs must | ||||
| be unique and must not be empty. | ||||
| 
 | ||||
| @item @code{address} (default: @code{'()}) | ||||
| An ordered list of destination IP addresses.  Addresses are tried in sequence. | ||||
| An optional port can be given with the @@ separator.  For instance: | ||||
| @code{(list "1.2.3.4" "2.3.4.5@@53")}.  Default port is 53. | ||||
| 
 | ||||
| @item @code{via} (default: @code{'()}) | ||||
| An ordered list of source IP addresses.  An empty list will have Knot choose | ||||
| an appropriate source IP.  An optional port can be given with the @@ separator. | ||||
| The default is to choose at random. | ||||
| 
 | ||||
| @item @code{key} (default: @code{#f}) | ||||
| A reference to a key, that is a string containing the identifier of a key | ||||
| defined in a @code{knot-key-configuration} field. | ||||
| 
 | ||||
| @end table | ||||
| @end deftp | ||||
| 
 | ||||
| @deftp {Data Type} knot-keystore-configuration | ||||
| Data type representing a keystore to hold dnssec keys. | ||||
| This type has the following parameters: | ||||
| 
 | ||||
| @table @asis | ||||
| @item @code{id} (default: @code{""}) | ||||
| The id of the keystore.  It must not be empty. | ||||
| 
 | ||||
| @item @code{backend} (default: @code{'pem}) | ||||
| The backend to store the keys in.  Can be @code{'pem} or @code{'pkcs11}. | ||||
| 
 | ||||
| @item @code{config} (default: @code{"/var/lib/knot/keys/keys"}) | ||||
| The configuration string of the backend.  An example for the PKCS#11 is: | ||||
| @code{"pkcs11:token=knot;pin-value=1234 /gnu/store/.../lib/pkcs11/libsofthsm2.so"}. | ||||
| For the pem backend, the string reprensents a path in the filesystem. | ||||
| 
 | ||||
| @end table | ||||
| @end deftp | ||||
| 
 | ||||
| @deftp {Data Type} knot-policy-configuration | ||||
| Data type representing a dnssec policy.  Knot DNS is able to automatically | ||||
| sign your zones.  It can either generate and manage your keys automatically or | ||||
| use keys that you generate. | ||||
| 
 | ||||
| Dnssec is usually implemented using two keys: a Key Signing Key (KSK) that is | ||||
| used to sign the second, and a Zone Signing Key (ZSK) that is used to sign the | ||||
| zone.  In order to be trusted, the KSK needs to be present in the parent zone | ||||
| (usually a top-level domain).  If your registrar supports dnssec, you will | ||||
| have to send them your KSK's hash so they can add a DS record in their zone. | ||||
| This is not automated and need to be done each time you change your KSK. | ||||
| 
 | ||||
| The policy also defines the lifetime of keys.  Usually, ZSK can be changed | ||||
| easily and use weaker cryptographic functions (they use lower parameters) in | ||||
| order to sign records quickly, so they are changed often.  The KSK however | ||||
| requires manual interaction with the registrar, so they are changed less often | ||||
| and use stronger parameters because they sign only one record. | ||||
| 
 | ||||
| This type has the following parameters: | ||||
| 
 | ||||
| @table @asis | ||||
| @item @code{id} (default: @code{""}) | ||||
| The id of the policy.  It must not be empty. | ||||
| 
 | ||||
| @item @code{keystore} (default: @code{"default"}) | ||||
| A reference to a keystore, that is a string containing the identifier of a | ||||
| keystore defined in a @code{knot-keystore-configuration} field.  The | ||||
| @code{"default"} identifier means the default keystore (a kasp database that | ||||
| was setup by this service). | ||||
| 
 | ||||
| @item @code{manual?} (default: @code{#f}) | ||||
| Whether the key management is manual or automatic. | ||||
| 
 | ||||
| @item @code{single-type-signing?} (default: @code{#f}) | ||||
| When @code{#t}, use the Single-Type Signing Scheme. | ||||
| 
 | ||||
| @item @code{algorithm} (default: @code{"ecdsap256sha256"}) | ||||
| An algorithm of signing keys and issued signatures. | ||||
| 
 | ||||
| @item @code{ksk-size} (default: @code{256}) | ||||
| The length of the KSK.  Note that this value is correct for the default | ||||
| algorithm, but would be unsecure for other algorithms. | ||||
| 
 | ||||
| @item @code{zsk-size} (default: @code{256}) | ||||
| The length of the ZSK.  Note that this value is correct for the default | ||||
| algorithm, but would be unsecure for other algorithms. | ||||
| 
 | ||||
| @item @code{dnskey-ttl} (default: @code{'default}) | ||||
| The TTL value for DNSKEY records added into zone apex.  The special | ||||
| @code{'default} value means same as the zone SOA TTL. | ||||
| 
 | ||||
| @item @code{zsk-lifetime} (default: @code{"30d"}) | ||||
| The period between ZSK publication and the next rollover initiation. | ||||
| 
 | ||||
| @item @code{propagation-delay} (default: @code{"1d"}) | ||||
| An extra delay added for each key rollover step.  This value should be high | ||||
| enough to cover propagation of data from the master server to all slaves. | ||||
| 
 | ||||
| @item @code{rrsig-lifetime} (default: @code{"14d"}) | ||||
| A validity period of newly issued signatures. | ||||
| 
 | ||||
| @item @code{rrsig-refresh} (default: @code{"7d"}) | ||||
| A period how long before a signature expiration the signature will be refreshed. | ||||
| 
 | ||||
| @item @code{nsec3?} (default: @code{#f}) | ||||
| When @code{#t}, NSEC3 will be used instead of NSEC. | ||||
| 
 | ||||
| @item @code{nsec3-iterations} (default: @code{5}) | ||||
| The number of additional times the hashing is performed. | ||||
| 
 | ||||
| @item @code{nsec3-salt-length} (default: @code{8}) | ||||
| The length of a salt field in octets, which is appended to the original owner | ||||
| name before hashing. | ||||
| 
 | ||||
| @item @code{nsec3-salt-lifetime} (default: @code{"30d"}) | ||||
| The validity period of newly issued salt field. | ||||
| 
 | ||||
| @end table | ||||
| @end deftp | ||||
| 
 | ||||
| @deftp {Data Type} knot-zone-configuration | ||||
| Data type representing a zone served by Knot. | ||||
| This type has the following parameters: | ||||
| 
 | ||||
| @table @asis | ||||
| @item @code{domain} (default: @code{""}) | ||||
| The domain served by this configuration.  It must not be empty. | ||||
| 
 | ||||
| @item @code{file} (default: @code{""}) | ||||
| The file where this zone is saved.  This parameter is ignored by master zones. | ||||
| Empty means default location that depends on the domain name. | ||||
| 
 | ||||
| @item @code{zone} (default: @code{(zone-file)}) | ||||
| The content of the zone file.  This parameter is ignored by slave zones.  It | ||||
| must contain a zone-file record. | ||||
| 
 | ||||
| @item @code{master} (default: @code{'()}) | ||||
| A list of master remotes.  When empty, this zone is a master.  When set, this | ||||
| zone is a slave.  This is a list of remotes identifiers. | ||||
| 
 | ||||
| @item @code{ddns-master} (default: @code{#f}) | ||||
| The main master.  When empty, it defaults to the first master in the list of | ||||
| masters. | ||||
| 
 | ||||
| @item @code{notify} (default: @code{'()}) | ||||
| A list of slave remote identifiers. | ||||
| 
 | ||||
| @item @code{acl} (default: @code{'()}) | ||||
| A list of acl identifiers. | ||||
| 
 | ||||
| @item @code{semantic-checks?} (default: @code{#f}) | ||||
| When set, this adds more semantic checks to the zone. | ||||
| 
 | ||||
| @item @code{disable-any?} (default: @code{#f}) | ||||
| When set, this forbids queries of the ANY type. | ||||
| 
 | ||||
| @item @code{zonefile-sync} (default: @code{0}) | ||||
| The delay between a modification in memory and on disk.  0 means immediate | ||||
| synchronization. | ||||
| 
 | ||||
| @item @code{serial-policy} (default: @code{'increment}) | ||||
| A policy between @code{'increment} and @code{'unixtime}. | ||||
| 
 | ||||
| @end table | ||||
| @end deftp | ||||
| 
 | ||||
| @deftp {Data Type} knot-configuration | ||||
| Data type representing the Knot configuration. | ||||
| This type has the following parameters: | ||||
| 
 | ||||
| @table @asis | ||||
| @item @code{knot} (default: @code{knot}) | ||||
| The Knot package. | ||||
| 
 | ||||
| @item @code{run-directory} (default: @code{"/var/run/knot"}) | ||||
| The run directory.  This directory will be used for pid file and sockets. | ||||
| 
 | ||||
| @item @code{listen-v4} (default: @code{"0.0.0.0"}) | ||||
| An ip address on which to listen. | ||||
| 
 | ||||
| @item @code{listen-v6} (default: @code{"::"}) | ||||
| An ip address on which to listen. | ||||
| 
 | ||||
| @item @code{listen-port} (default: @code{53}) | ||||
| A port on which to listen. | ||||
| 
 | ||||
| @item @code{keys} (default: @code{'()}) | ||||
| The list of knot-key-configuration used by this configuration. | ||||
| 
 | ||||
| @item @code{acls} (default: @code{'()}) | ||||
| The list of knot-acl-configuration used by this configuration. | ||||
| 
 | ||||
| @item @code{remotes} (default: @code{'()}) | ||||
| The list of knot-remote-configuration used by this configuration. | ||||
| 
 | ||||
| @item @code{zones} (default: @code{'()}) | ||||
| The list of knot-zone-configuration used by this configuration. | ||||
| 
 | ||||
| @end table | ||||
| @end deftp | ||||
| 
 | ||||
| @node VPN Services | ||||
| @subsubsection VPN Services | ||||
| @cindex VPN (virtual private network) | ||||
|  |  | |||
|  | @ -426,6 +426,7 @@ GNU_SYSTEM_MODULES =				\ | |||
|   %D%/services/dbus.scm				\
 | ||||
|   %D%/services/desktop.scm			\
 | ||||
|   %D%/services/dict.scm				\
 | ||||
|   %D%/services/dns.scm				\
 | ||||
|   %D%/services/kerberos.scm			\
 | ||||
|   %D%/services/lirc.scm				\
 | ||||
|   %D%/services/mail.scm				\
 | ||||
|  |  | |||
							
								
								
									
										593
									
								
								gnu/services/dns.scm
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										593
									
								
								gnu/services/dns.scm
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,593 @@ | |||
| ;;; GNU Guix --- Functional package management for GNU | ||||
| ;;; Copyright © 2017 Julien Lepiller <julien@lepiller.eu> | ||||
| ;;; | ||||
| ;;; This file is part of GNU Guix. | ||||
| ;;; | ||||
| ;;; GNU Guix is free software; you can redistribute it and/or modify it | ||||
| ;;; under the terms of the GNU General Public License as published by | ||||
| ;;; the Free Software Foundation; either version 3 of the License, or (at | ||||
| ;;; your option) any later version. | ||||
| ;;; | ||||
| ;;; GNU Guix is distributed in the hope that it will be useful, but | ||||
| ;;; WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| ;;; GNU General Public License for more details. | ||||
| ;;; | ||||
| ;;; You should have received a copy of the GNU General Public License | ||||
| ;;; along with GNU Guix.  If not, see <http://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| (define-module (gnu services dns) | ||||
|   #:use-module (gnu services) | ||||
|   #:use-module (gnu services configuration) | ||||
|   #:use-module (gnu services shepherd) | ||||
|   #:use-module (gnu system shadow) | ||||
|   #:use-module (gnu packages admin) | ||||
|   #:use-module (gnu packages dns) | ||||
|   #:use-module (guix packages) | ||||
|   #:use-module (guix records) | ||||
|   #:use-module (guix gexp) | ||||
|   #:use-module (srfi srfi-1) | ||||
|   #:use-module (srfi srfi-34) | ||||
|   #:use-module (srfi srfi-35) | ||||
|   #:use-module (ice-9 match) | ||||
|   #:use-module (ice-9 regex) | ||||
|   #:export (knot-service-type | ||||
|             knot-acl-configuration | ||||
|             knot-key-configuration | ||||
|             knot-keystore-configuration | ||||
|             knot-zone-configuration | ||||
|             knot-remote-configuration | ||||
|             knot-policy-configuration | ||||
|             knot-configuration | ||||
|             define-zone-entries | ||||
|             zone-file | ||||
|             zone-entry)) | ||||
| 
 | ||||
| ;;; | ||||
| ;;; Knot DNS. | ||||
| ;;; | ||||
| 
 | ||||
| (define-record-type* <knot-key-configuration> | ||||
|   knot-key-configuration make-knot-key-configuration | ||||
|   knot-key-configuration? | ||||
|   (id        knot-key-configuration-id | ||||
|              (default "")) | ||||
|   (algorithm knot-key-configuration-algorithm | ||||
|              (default #f)); one of #f, or an algorithm name | ||||
|   (secret    knot-key-configuration-secret | ||||
|              (default ""))) | ||||
| 
 | ||||
| (define-record-type* <knot-acl-configuration> | ||||
|   knot-acl-configuration make-knot-acl-configuration | ||||
|   knot-acl-configuration? | ||||
|   (id      knot-acl-configuration-id | ||||
|            (default "")) | ||||
|   (address knot-acl-configuration-address | ||||
|            (default '())) | ||||
|   (key     knot-acl-configuration-key | ||||
|            (default '())) | ||||
|   (action  knot-acl-configuration-action | ||||
|            (default '())) | ||||
|   (deny?   knot-acl-configuration-deny? | ||||
|            (default #f))) | ||||
| 
 | ||||
| (define-record-type* <zone-entry> | ||||
|   zone-entry make-zone-entry | ||||
|   zone-entry? | ||||
|   (name  zone-entry-name | ||||
|          (default "@")) | ||||
|   (ttl   zone-entry-ttl | ||||
|          (default "")) | ||||
|   (class zone-entry-class | ||||
|          (default "IN")) | ||||
|   (type  zone-entry-type | ||||
|          (default "A")) | ||||
|   (data  zone-entry-data | ||||
|          (default ""))) | ||||
| 
 | ||||
| (define-record-type* <zone-file> | ||||
|   zone-file make-zone-file | ||||
|   zone-file? | ||||
|   (entries zone-file-entries | ||||
|            (default '())) | ||||
|   (origin  zone-file-origin | ||||
|            (default "")) | ||||
|   (ns      zone-file-ns | ||||
|            (default "ns")) | ||||
|   (mail    zone-file-mail | ||||
|            (default "hostmaster")) | ||||
|   (serial  zone-file-serial | ||||
|            (default 1)) | ||||
|   (refresh zone-file-refresh | ||||
|            (default "2d")) | ||||
|   (retry   zone-file-retry | ||||
|            (default "15m")) | ||||
|   (expiry  zone-file-expiry | ||||
|            (default "2w")) | ||||
|   (nx      zone-file-nx | ||||
|            (default "1h"))) | ||||
| (define-record-type* <knot-keystore-configuration> | ||||
|   knot-keystore-configuration make-knot-keystore-configuration | ||||
|   knot-keystore-configuration? | ||||
|   (id knot-keystore-configuration-id | ||||
|       (default "")) | ||||
|   (backend knot-keystore-configuration-backend | ||||
|            (default 'pem)) | ||||
|   (config  knot-keystore-configuration-config | ||||
|            (default "/var/lib/knot/keys/keys"))) | ||||
| 
 | ||||
| (define-record-type* <knot-policy-configuration> | ||||
|   knot-policy-configuration make-knot-policy-configuration | ||||
|   knot-policy-configuration? | ||||
|   (id                   knot-policy-configuration-id | ||||
|                         (default "")) | ||||
|   (keystore             knot-policy-configuration-keystore | ||||
|                         (default "default")) | ||||
|   (manual?              knot-policy-configuration-manual? | ||||
|                         (default #f)) | ||||
|   (single-type-signing? knot-policy-configuration-single-type-signing? | ||||
|                         (default #f)) | ||||
|   (algorithm            knot-policy-configuration-algorithm | ||||
|                         (default "ecdsap256sha256")) | ||||
|   (ksk-size             knot-policy-configuration-ksk-size | ||||
|                         (default 256)) | ||||
|   (zsk-size             knot-policy-configuration-zsk-size | ||||
|                         (default 256)) | ||||
|   (dnskey-ttl           knot-policy-configuration-dnskey-ttl | ||||
|                         (default 'default)) | ||||
|   (zsk-lifetime         knot-policy-configuration-zsk-lifetime | ||||
|                         (default "30d")) | ||||
|   (propagation-delay    knot-policy-configuration-propagation-delay | ||||
|                         (default "1d")) | ||||
|   (rrsig-lifetime       knot-policy-configuration-rrsig-lifetime | ||||
|                         (default "14d")) | ||||
|   (rrsig-refresh        knot-policy-configuration-rrsig-refresh | ||||
|                         (default "7d")) | ||||
|   (nsec3?               knot-policy-configuration-nsec3? | ||||
|                         (default #f)) | ||||
|   (nsec3-iterations     knot-policy-configuration-nsec3-iterations | ||||
|                         (default 5)) | ||||
|   (nsec3-salt-length    knot-policy-configuration-nsec3-salt-length | ||||
|                         (default 8)) | ||||
|   (nsec3-salt-lifetime  knot-policy-configuration-nsec3-salt-lifetime | ||||
|                         (default "30d"))) | ||||
| 
 | ||||
| (define-record-type* <knot-zone-configuration> | ||||
|   knot-zone-configuration make-knot-zone-configuration | ||||
|   knot-zone-configuration? | ||||
|   (domain           knot-zone-configuration-domain | ||||
|                     (default "")) | ||||
|   (file             knot-zone-configuration-file | ||||
|                     (default "")) ; the file where this zone is saved. | ||||
|   (zone             knot-zone-configuration-zone | ||||
|                     (default (zone-file))) ; initial content of the zone file | ||||
|   (master           knot-zone-configuration-master | ||||
|                     (default '())) | ||||
|   (ddns-master      knot-zone-configuration-ddns-master | ||||
|                     (default #f)) | ||||
|   (notify           knot-zone-configuration-notify | ||||
|                     (default '())) | ||||
|   (acl              knot-zone-configuration-acl | ||||
|                     (default '())) | ||||
|   (semantic-checks? knot-zone-configuration-semantic-checks? | ||||
|                     (default #f)) | ||||
|   (disable-any?     knot-zone-configuration-disable-any? | ||||
|                     (default #f)) | ||||
|   (zonefile-sync    knot-zone-configuration-zonefile-sync | ||||
|                     (default 0)) | ||||
|   (dnssec-policy    knot-zone-configuration-dnssec-policy | ||||
|                     (default #f)) | ||||
|   (serial-policy    knot-zone-configuration-serial-policy | ||||
|                     (default 'increment))) | ||||
| 
 | ||||
| (define-record-type* <knot-remote-configuration> | ||||
|   knot-remote-configuration make-knot-remote-configuration | ||||
|   knot-remote-configuration? | ||||
|   (id  knot-remote-configuration-id | ||||
|        (default "")) | ||||
|   (address knot-remote-configuration-address | ||||
|            (default '())) | ||||
|   (via     knot-remote-configuration-via | ||||
|            (default '())) | ||||
|   (key     knot-remote-configuration-key | ||||
|            (default #f))) | ||||
| 
 | ||||
| (define-record-type* <knot-configuration> | ||||
|   knot-configuration make-knot-configuration | ||||
|   knot-configuration? | ||||
|   (knot          knot-configuration-knot | ||||
|                  (default knot)) | ||||
|   (run-directory knot-configuration-run-directory | ||||
|                  (default "/var/run/knot")) | ||||
|   (listen-v4     knot-configuration-listen-v4 | ||||
|                  (default "0.0.0.0")) | ||||
|   (listen-v6     knot-configuration-listen-v6 | ||||
|                  (default "::")) | ||||
|   (listen-port   knot-configuration-listen-port | ||||
|                  (default 53)) | ||||
|   (keys          knot-configuration-keys | ||||
|                  (default '())) | ||||
|   (keystores     knot-configuration-keystores | ||||
|                  (default '())) | ||||
|   (acls          knot-configuration-acls | ||||
|                  (default '())) | ||||
|   (remotes       knot-configuration-remotes | ||||
|                  (default '())) | ||||
|   (policies      knot-configuration-policies | ||||
|                  (default '())) | ||||
|   (zones         knot-configuration-zones | ||||
|                  (default '()))) | ||||
| 
 | ||||
| (define-syntax define-zone-entries | ||||
|   (syntax-rules () | ||||
|     ((_ id (name ttl class type data) ...) | ||||
|      (define id (list (make-zone-entry name ttl class type data) ...))))) | ||||
| 
 | ||||
| (define (error-out msg) | ||||
|   (raise (condition (&message (message msg))))) | ||||
| 
 | ||||
| (define (verify-knot-key-configuration key) | ||||
|   (unless (knot-key-configuration? key) | ||||
|     (error-out "keys must be a list of only knot-key-configuration.")) | ||||
|   (let ((id (knot-key-configuration-id key))) | ||||
|     (unless (and (string? id) (not (equal? id ""))) | ||||
|       (error-out "key id must be a non empty string."))) | ||||
|   (unless (memq '(#f hmac-md5 hmac-sha1 hmac-sha224 hmac-sha256 hmac-sha384 hmac-sha512) | ||||
|                 (knot-key-configuration-algorithm key)) | ||||
|           (error-out "algorithm must be one of: #f, 'hmac-md5, 'hmac-sha1, | ||||
| 'hmac-sha224, 'hmac-sha256, 'hmac-sha384 or 'hmac-sha512"))) | ||||
| 
 | ||||
| (define (verify-knot-keystore-configuration keystore) | ||||
|   (unless (knot-keystore-configuration? keystore) | ||||
|     (error-out "keystores must be a list of only knot-keystore-configuration.")) | ||||
|   (let ((id (knot-keystore-configuration-id keystore))) | ||||
|     (unless (and (string? id) (not (equal? id ""))) | ||||
|       (error-out "keystore id must be a non empty string."))) | ||||
|   (unless (memq '(pem pkcs11) | ||||
|                 (knot-keystore-configuration-backend keystore)) | ||||
|           (error-out "backend must be one of: 'pem or 'pkcs11"))) | ||||
| 
 | ||||
| (define (verify-knot-policy-configuration policy) | ||||
|   (unless (knot-keystore-configuration? policy) | ||||
|     (error-out "policies must be a list of only knot-policy-configuration.")) | ||||
|   (let ((id (knot-policy-configuration-id policy))) | ||||
|     (unless (and (string? id) (not (equal? id ""))) | ||||
|       (error-out "policy id must be a non empty string.")))) | ||||
| 
 | ||||
| (define (verify-knot-acl-configuration acl) | ||||
|   (unless (knot-acl-configuration? acl) | ||||
|     (error-out "acls must be a list of only knot-acl-configuration.")) | ||||
|   (let ((id (knot-acl-configuration-id acl)) | ||||
|         (address (knot-acl-configuration-address acl)) | ||||
|         (key (knot-acl-configuration-key acl)) | ||||
|         (action (knot-acl-configuration-action acl))) | ||||
|     (unless (and (string? id) (not (equal? id ""))) | ||||
|       (error-out "acl id must be a non empty string.")) | ||||
|     (unless (and (list? address) | ||||
|                  (fold (lambda (x1 x2) (and (string? x1) (string? x2))) "" address)) | ||||
|       (error-out "acl address must be a list of strings."))) | ||||
|   (unless (boolean? (knot-acl-configuration-deny? acl)) | ||||
|     (error-out "deny? must be #t or #f."))) | ||||
| 
 | ||||
| (define (verify-knot-zone-configuration zone) | ||||
|   (unless (knot-zone-configuration? zone) | ||||
|     (error-out "zones must be a list of only knot-zone-configuration.")) | ||||
|   (let ((domain (knot-zone-configuration-domain zone))) | ||||
|     (unless (and (string? domain) (not (equal? domain ""))) | ||||
|       (error-out "zone domain must be a non empty string.")))) | ||||
| 
 | ||||
| (define (verify-knot-remote-configuration remote) | ||||
|   (unless (knot-remote-configuration? remote) | ||||
|     (error-out "remotes must be a list of only knot-remote-configuration.")) | ||||
|   (let ((id (knot-remote-configuration-id remote))) | ||||
|     (unless (and (string? id) (not (equal? id ""))) | ||||
|       (error-out "remote id must be a non empty string.")))) | ||||
| 
 | ||||
| (define (verify-knot-configuration config) | ||||
|   (unless (package? (knot-configuration-knot config)) | ||||
|     (error-out "knot configuration field must be a package.")) | ||||
|   (unless (string? (knot-configuration-run-directory config)) | ||||
|     (error-out "run-directory must be a string.")) | ||||
|   (unless (list? (knot-configuration-keys config)) | ||||
|     (error-out "keys must be a list of knot-key-configuration.")) | ||||
|   (for-each (lambda (key) (verify-knot-key-configuration key)) | ||||
|             (knot-configuration-keys config)) | ||||
|   (unless (list? (knot-configuration-keystores config)) | ||||
|     (error-out "keystores must be a list of knot-keystore-configuration.")) | ||||
|   (for-each (lambda (keystore) (verify-knot-keystore-configuration keystore)) | ||||
|             (knot-configuration-keystores config)) | ||||
|   (unless (list? (knot-configuration-acls config)) | ||||
|     (error-out "acls must be a list of knot-acl-configuration.")) | ||||
|   (for-each (lambda (acl) (verify-knot-acl-configuration acl)) | ||||
|             (knot-configuration-acls config)) | ||||
|   (unless (list? (knot-configuration-zones config)) | ||||
|     (error-out "zones must be a list of knot-zone-configuration.")) | ||||
|   (for-each (lambda (zone) (verify-knot-zone-configuration zone)) | ||||
|             (knot-configuration-zones config)) | ||||
|   (unless (list? (knot-configuration-policies config)) | ||||
|     (error-out "policies must be a list of knot-policy-configuration.")) | ||||
|   (for-each (lambda (policy) (verify-knot-policy-configuration policy)) | ||||
|             (knot-configuration-policies config)) | ||||
|   (unless (list? (knot-configuration-remotes config)) | ||||
|     (error-out "remotes must be a list of knot-remote-configuration.")) | ||||
|   (for-each (lambda (remote) (verify-knot-remote-configuration remote)) | ||||
|             (knot-configuration-remotes config)) | ||||
|   #t) | ||||
| 
 | ||||
| (define (format-string-list l) | ||||
|   "Formats a list of string in YAML" | ||||
|   (if (eq? l '()) | ||||
|       "" | ||||
|       (let ((l (reverse l))) | ||||
|         (string-append | ||||
|           "[" | ||||
|           (fold (lambda (x1 x2) | ||||
|                   (string-append (if (symbol? x1) (symbol->string x1) x1) ", " | ||||
|                                  (if (symbol? x2) (symbol->string x2) x2))) | ||||
|                 (car l) (cdr l)) | ||||
|           "]")))) | ||||
| 
 | ||||
| (define (knot-acl-config acls) | ||||
|   (with-output-to-string | ||||
|     (lambda () | ||||
|       (for-each | ||||
|         (lambda (acl-config) | ||||
|           (let ((id (knot-acl-configuration-id acl-config)) | ||||
|                 (address (knot-acl-configuration-address acl-config)) | ||||
|                 (key (knot-acl-configuration-key acl-config)) | ||||
|                 (action (knot-acl-configuration-action acl-config)) | ||||
|                 (deny? (knot-acl-configuration-deny? acl-config))) | ||||
|             (format #t "    - id: ~a\n" id) | ||||
|             (unless (eq? address '()) | ||||
|               (format #t "      address: ~a\n" (format-string-list address))) | ||||
|             (unless (eq? key '()) | ||||
|               (format #t "      key: ~a\n" (format-string-list key))) | ||||
|             (unless (eq? action '()) | ||||
|               (format #t "      action: ~a\n" (format-string-list action))) | ||||
|             (format #t "      deny: ~a\n" (if deny? "on" "off")))) | ||||
|         acls)))) | ||||
| 
 | ||||
| (define (knot-key-config keys) | ||||
|   (with-output-to-string | ||||
|     (lambda () | ||||
|       (for-each | ||||
|         (lambda (key-config) | ||||
|           (let ((id (knot-key-configuration-id key-config)) | ||||
|                 (algorithm (knot-key-configuration-algorithm key-config)) | ||||
|                 (secret (knot-key-configuration-secret key-config))) | ||||
|             (format #t     "    - id: ~a\n" id) | ||||
|             (if algorithm | ||||
|                 (format #t "      algorithm: ~a\n" (symbol->string algorithm))) | ||||
|             (format #t     "      secret: ~a\n" secret))) | ||||
|         keys)))) | ||||
| 
 | ||||
| (define (knot-keystore-config keystores) | ||||
|   (with-output-to-string | ||||
|     (lambda () | ||||
|       (for-each | ||||
|         (lambda (keystore-config) | ||||
|           (let ((id (knot-keystore-configuration-id keystore-config)) | ||||
|                 (backend (knot-keystore-configuration-backend keystore-config)) | ||||
|                 (config (knot-keystore-configuration-config keystore-config))) | ||||
|             (format #t "    - id: ~a\n" id) | ||||
|             (format #t "      backend: ~a\n" (symbol->string backend)) | ||||
|             (format #t "      config: \"~a\"\n" config))) | ||||
|         keystores)))) | ||||
| 
 | ||||
| (define (knot-policy-config policies) | ||||
|   (with-output-to-string | ||||
|     (lambda () | ||||
|       (for-each | ||||
|         (lambda (policy-config) | ||||
|           (let ((id (knot-policy-configuration-id policy-config)) | ||||
|                 (keystore (knot-policy-configuration-keystore policy-config)) | ||||
|                 (manual? (knot-policy-configuration-manual? policy-config)) | ||||
|                 (single-type-signing? (knot-policy-configuration-single-type-signing? | ||||
|                                         policy-config)) | ||||
|                 (algorithm (knot-policy-configuration-algorithm policy-config)) | ||||
|                 (ksk-size (knot-policy-configuration-ksk-size policy-config)) | ||||
|                 (zsk-size (knot-policy-configuration-zsk-size policy-config)) | ||||
|                 (dnskey-ttl (knot-policy-configuration-dnskey-ttl policy-config)) | ||||
|                 (zsk-lifetime (knot-policy-configuration-zsk-lifetime policy-config)) | ||||
|                 (propagation-delay (knot-policy-configuration-propagation-delay | ||||
|                                      policy-config)) | ||||
|                 (rrsig-lifetime (knot-policy-configuration-rrsig-lifetime | ||||
|                                   policy-config)) | ||||
|                 (nsec3? (knot-policy-configuration-nsec3? policy-config)) | ||||
|                 (nsec3-iterations (knot-policy-configuration-nsec3-iterations | ||||
|                                     policy-config)) | ||||
|                 (nsec3-salt-length (knot-policy-configuration-nsec3-salt-length | ||||
|                                      policy-config)) | ||||
|                 (nsec3-salt-lifetime (knot-policy-configuration-nsec3-salt-lifetime | ||||
|                                        policy-config))) | ||||
|             (format #t "    - id: ~a\n" id) | ||||
|             (format #t "      keystore: ~a\n" keystore) | ||||
|             (format #t "      manual: ~a\n" (if manual? "on" "off")) | ||||
|             (format #t "      single-type-signing: ~a\n" (if single-type-signing? | ||||
|                                                              "on" "off")) | ||||
|             (format #t "      algorithm: ~a\n" algorithm) | ||||
|             (format #t "      ksk-size: ~a\n" (number->string ksk-size)) | ||||
|             (format #t "      zsk-size: ~a\n" (number->string zsk-size)) | ||||
|             (unless (eq? dnskey-ttl 'default) | ||||
|               (format #t "      dnskey-ttl: ~a\n" dnskey-ttl)) | ||||
|             (format #t "      zsk-lifetime: ~a\n" zsk-lifetime) | ||||
|             (format #t "      propagation-delay: ~a\n" propagation-delay) | ||||
|             (format #t "      rrsig-lifetime: ~a\n" rrsig-lifetime) | ||||
|             (format #t "      nsec3: ~a\n" (if nsec3? "on" "off")) | ||||
|             (format #t "      nsec3-iterations: ~a\n" | ||||
|                     (number->string nsec3-iterations)) | ||||
|             (format #t "      nsec3-salt-length: ~a\n" | ||||
|                     (number->string nsec3-salt-length)) | ||||
|             (format #t "      nsec3-salt-lifetime: ~a\n" nsec3-salt-lifetime))) | ||||
|         policies)))) | ||||
| 
 | ||||
| (define (knot-remote-config remotes) | ||||
|   (with-output-to-string | ||||
|     (lambda () | ||||
|       (for-each | ||||
|         (lambda (remote-config) | ||||
|           (let ((id (knot-remote-configuration-id remote-config)) | ||||
|                 (address (knot-remote-configuration-address remote-config)) | ||||
|                 (via (knot-remote-configuration-via remote-config)) | ||||
|                 (key (knot-remote-configuration-key remote-config))) | ||||
|             (format #t "    - id: ~a\n" id) | ||||
|             (unless (eq? address '()) | ||||
|               (format #t "      address: ~a\n" (format-string-list address))) | ||||
|             (unless (eq? via '()) | ||||
|               (format #t "      via: ~a\n" (format-string-list via))) | ||||
|             (if key | ||||
|               (format #t "      key: ~a\n" key)))) | ||||
|         remotes)))) | ||||
| 
 | ||||
| (define (serialize-zone-entries entries) | ||||
|   (with-output-to-string | ||||
|     (lambda () | ||||
|       (for-each | ||||
|         (lambda (entry) | ||||
|           (let ((name (zone-entry-name entry)) | ||||
|                 (ttl (zone-entry-ttl entry)) | ||||
|                 (class (zone-entry-class entry)) | ||||
|                 (type (zone-entry-type entry)) | ||||
|                 (data (zone-entry-data entry))) | ||||
|             (format #t "~a ~a ~a ~a ~a\n" name ttl class type data))) | ||||
|         entries)))) | ||||
| 
 | ||||
| (define (serialize-zone-file zone domain) | ||||
|   (computed-file (string-append domain ".zone") | ||||
|     #~(begin | ||||
|         (call-with-output-file #$output | ||||
|           (lambda (port) | ||||
|             (format port "$ORIGIN ~a.\n" | ||||
|                     #$(zone-file-origin zone)) | ||||
|             (format port "@ IN SOA ~a ~a (~a ~a ~a ~a ~a)\n" | ||||
|                     #$(zone-file-ns zone) | ||||
|                     #$(zone-file-mail zone) | ||||
|                     #$(zone-file-serial zone) | ||||
|                     #$(zone-file-refresh zone) | ||||
|                     #$(zone-file-retry zone) | ||||
|                     #$(zone-file-expiry zone) | ||||
|                     #$(zone-file-nx zone)) | ||||
|             (format port "~a\n" | ||||
|                     #$(serialize-zone-entries (zone-file-entries zone)))))))) | ||||
| 
 | ||||
| (define (knot-zone-config zone) | ||||
|   (let ((content (knot-zone-configuration-zone zone))) | ||||
|     #~(with-output-to-string | ||||
|         (lambda () | ||||
|           (let ((domain #$(knot-zone-configuration-domain zone)) | ||||
|                 (file #$(knot-zone-configuration-file zone)) | ||||
|                 (master (list #$@(knot-zone-configuration-master zone))) | ||||
|                 (ddns-master #$(knot-zone-configuration-ddns-master zone)) | ||||
|                 (notify (list #$@(knot-zone-configuration-notify zone))) | ||||
|                 (acl (list #$@(knot-zone-configuration-acl zone))) | ||||
|                 (semantic-checks? #$(knot-zone-configuration-semantic-checks? zone)) | ||||
|                 (disable-any? #$(knot-zone-configuration-disable-any? zone)) | ||||
|                 (dnssec-policy #$(knot-zone-configuration-dnssec-policy zone)) | ||||
|                 (serial-policy '#$(knot-zone-configuration-serial-policy zone))) | ||||
|             (format #t "    - domain: ~a\n" domain) | ||||
|             (if (eq? master '()) | ||||
|                 ;; This server is a master | ||||
|                 (if (equal? file "") | ||||
|                   (format #t "      file: ~a\n" | ||||
|                     #$(serialize-zone-file content | ||||
|                                            (knot-zone-configuration-domain zone))) | ||||
|                   (format #t "      file: ~a\n" file)) | ||||
|                 ;; This server is a slave (has masters) | ||||
|                 (begin | ||||
|                   (format #t "      master: ~a\n" | ||||
|                           #$(format-string-list | ||||
|                               (knot-zone-configuration-master zone))) | ||||
|                   (if ddns-master (format #t "      ddns-master ~a\n" ddns-master)))) | ||||
|             (unless (eq? notify '()) | ||||
|               (format #t "      notify: ~a\n" | ||||
|                       #$(format-string-list | ||||
|                           (knot-zone-configuration-notify zone)))) | ||||
|             (unless (eq? acl '()) | ||||
|               (format #t "      acl: ~a\n" | ||||
|                       #$(format-string-list | ||||
|                           (knot-zone-configuration-acl zone)))) | ||||
|             (format #t "      semantic-checks: ~a\n" (if semantic-checks? "on" "off")) | ||||
|             (format #t "      disable-any: ~a\n" (if disable-any? "on" "off")) | ||||
|             (if dnssec-policy | ||||
|                 (begin | ||||
|                   (format #t "      dnssec-signing: on\n") | ||||
|                   (format #t "      dnssec-policy: ~a\n" dnssec-policy))) | ||||
|             (format #t "      serial-policy: ~a\n" | ||||
|                     (symbol->string serial-policy))))))) | ||||
| 
 | ||||
| (define (knot-config-file config) | ||||
|   (verify-knot-configuration config) | ||||
|   (computed-file "knot.conf" | ||||
|     #~(begin | ||||
|         (call-with-output-file #$output | ||||
|           (lambda (port) | ||||
|             (format port "server:\n") | ||||
|             (format port "    rundir: ~a\n" #$(knot-configuration-run-directory config)) | ||||
|             (format port "    user: knot\n") | ||||
|             (format port "    listen: ~a@~a\n" | ||||
|                     #$(knot-configuration-listen-v4 config) | ||||
|                     #$(knot-configuration-listen-port config)) | ||||
|             (format port "    listen: ~a@~a\n" | ||||
|                     #$(knot-configuration-listen-v6 config) | ||||
|                     #$(knot-configuration-listen-port config)) | ||||
|             (format port "\nkey:\n") | ||||
|             (format port #$(knot-key-config (knot-configuration-keys config))) | ||||
|             (format port "\nkeystore:\n") | ||||
|             (format port #$(knot-keystore-config (knot-configuration-keystores config))) | ||||
|             (format port "\nacl:\n") | ||||
|             (format port #$(knot-acl-config (knot-configuration-acls config))) | ||||
|             (format port "\nremote:\n") | ||||
|             (format port #$(knot-remote-config (knot-configuration-remotes config))) | ||||
|             (format port "\npolicy:\n") | ||||
|             (format port #$(knot-policy-config (knot-configuration-policies config))) | ||||
|             (unless #$(eq? (knot-configuration-zones config) '()) | ||||
|               (format port "\nzone:\n") | ||||
|               (format port "~a\n" | ||||
|                       (string-concatenate | ||||
|                         (list #$@(map knot-zone-config | ||||
|                                       (knot-configuration-zones config))))))))))) | ||||
| 
 | ||||
| (define %knot-accounts | ||||
|   (list (user-group (name "knot") (system? #t)) | ||||
|         (user-account | ||||
|           (name "knot") | ||||
|           (group "knot") | ||||
|           (system? #t) | ||||
|           (comment "knot dns server user") | ||||
|           (home-directory "/var/empty") | ||||
|           (shell (file-append shadow "/sbin/nologin"))))) | ||||
| 
 | ||||
| (define (knot-activation config) | ||||
|   #~(begin | ||||
|       (use-modules (guix build utils)) | ||||
|       (define (mkdir-p/perms directory owner perms) | ||||
|         (mkdir-p directory) | ||||
|         (chown directory (passwd:uid owner) (passwd:gid owner)) | ||||
|         (chmod directory perms)) | ||||
|       (mkdir-p/perms #$(knot-configuration-run-directory config) | ||||
|                      (getpwnam "knot") #o755) | ||||
|       (mkdir-p/perms "/var/lib/knot" (getpwnam "knot") #o755) | ||||
|       (mkdir-p/perms "/var/lib/knot/keys" (getpwnam "knot") #o755) | ||||
|       (mkdir-p/perms "/var/lib/knot/keys/keys" (getpwnam "knot") #o755))) | ||||
| 
 | ||||
| (define (knot-shepherd-service config) | ||||
|   (let* ((config-file (knot-config-file config)) | ||||
|          (knot (knot-configuration-knot config))) | ||||
|     (list (shepherd-service | ||||
|             (documentation "Run the Knot DNS daemon.") | ||||
|             (provision '(knot dns)) | ||||
|             (requirement '(networking)) | ||||
|             (start #~(make-forkexec-constructor | ||||
|                        (list (string-append #$knot "/sbin/knotd") | ||||
|                              "-c" #$config-file))) | ||||
|             (stop #~(make-kill-destructor)))))) | ||||
| 
 | ||||
| (define knot-service-type | ||||
|   (service-type (name 'knot) | ||||
|                 (extensions | ||||
|                   (list (service-extension shepherd-root-service-type | ||||
|                                            knot-shepherd-service) | ||||
|                         (service-extension activation-service-type | ||||
|                                            knot-activation) | ||||
|                         (service-extension account-service-type | ||||
|                                            (const %knot-accounts)))))) | ||||
		Reference in a new issue