GNU bug report logs - #68757
[PATCH] services: dns: Add unbound service

Please note: This is a static page, with minimal formatting, updated once a day.
Click here to see this page with the latest information and nicer formatting.

Package: guix-patches; Reported by: soeren@HIDDEN; Keywords: patch; Done: Ludovic Courtès <ludo@HIDDEN>; Maintainer for guix-patches is guix-patches@HIDDEN.

Message received at 68757-done <at> debbugs.gnu.org:


Received: (at 68757-done) by debbugs.gnu.org; 11 Jan 2025 22:09:50 +0000
From debbugs-submit-bounces <at> debbugs.gnu.org Sat Jan 11 17:09:50 2025
Received: from localhost ([127.0.0.1]:45207 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1tWjfy-0007lj-1X
	for submit <at> debbugs.gnu.org; Sat, 11 Jan 2025 17:09:50 -0500
Received: from eggs.gnu.org ([2001:470:142:3::10]:38608)
 by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.84_2) (envelope-from <ludo@HIDDEN>) id 1tWjfv-0007lU-IU
 for 68757-done <at> debbugs.gnu.org; Sat, 11 Jan 2025 17:09:48 -0500
Received: from fencepost.gnu.org ([2001:470:142:3::e])
 by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.90_1) (envelope-from <ludo@HIDDEN>)
 id 1tWjfp-0004yI-Dc; Sat, 11 Jan 2025 17:09:41 -0500
DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnu.org;
 s=fencepost-gnu-org; h=MIME-Version:Date:References:In-Reply-To:Subject:To:
 From; bh=fAJTbi8yvcJtWL+4t4omo0Bo81ptHsuJUbfHGXm6BOY=; b=BfypFoVESYaCrKuH69lS
 PPsBAzYrVzePmnfVXXLtOJXnMa0PbLwxYMaen1LSxD5uD3NnQXMTV6n8N328H+VFAYGo5I4rvHjPA
 kVpI5cbPsMB7c1yvomyFqC/ZDXX3mMKhdtXvNNOZPj/s4yGitKkUw89gde3CBZouvge7K0PltboKi
 RhsveA2hbEr5CxQ8rONxqrXRAHWmYUpo+QA66w/U5F5+hPDigNLyE/p4HvCYf330QbF3+Rme9Xm8/
 fsgusiP1OpUSdeYQA1aLQBdWieUzCTSE0CLXn4kpTOwpgjp+KtHkkGVSU0wCUu/Rmoxwmg2mqBtYJ
 Aqsbu6Xng8QmJA==;
From: =?utf-8?Q?Ludovic_Court=C3=A8s?= <ludo@HIDDEN>
To: soeren@HIDDEN
Subject: Re: [bug#68757] [PATCH v3 1/1] services: dns: Add unbound service
In-Reply-To: <20250108211416.27602-1-soeren@HIDDEN>
 (soeren@HIDDEN's message of "Wed, 8 Jan 2025 22:13:54
 +0100")
References: <20240127121040.7156-2-soeren@HIDDEN>
 <20250108211416.27602-1-soeren@HIDDEN>
Date: Sat, 11 Jan 2025 23:09:36 +0100
Message-ID: <87wmf1m28v.fsf@HIDDEN>
User-Agent: Gnus/5.13 (Gnus v5.13)
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="=-=-="
X-Spam-Score: -2.3 (--)
X-Debbugs-Envelope-To: 68757-done
Cc: 68757-done <at> debbugs.gnu.org
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: -3.3 (---)

--=-=-=
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable

Hello,

soeren@HIDDEN skribis:

> From: S=C3=B6ren Tempel <soeren@HIDDEN>
>
> This allows using Unbound as a local DNSSEC-enabled resolver. This
> commit also allows configuration of the Unbound DNS resolver via a
> Scheme API. The API currently provides very common options and
> includes an escape hatch to enable less common configurations.
>
> * gnu/service/dns.scm (unbound-serialize-field): New procedure.
> * gnu/service/dns.scm (unbound-serialize-alist): New procedure.
> * gnu/service/dns.scm (unbound-serialize-section): New procedure.
> * gnu/service/dns.scm (unbound-serialize-string): New procedure.
> * gnu/service/dns.scm (unbound-serialize-boolean): New procedure.
> * gnu/service/dns.scm (unbound-serialize-list-of-strings): New procedure.
> * gnu/service/dns.scm (unbound-zone): New record.
> * gnu/service/dns.scm (unbound-serialize-unbound-zone): New procedure.
> * gnu/service/dns.scm (unbound-serialize-list-of-unbound-zone): New proce=
dure.
> * gnu/service/dns.scm (unbound-remote): New record.
> * gnu/service/dns.scm (unbound-serialize-unbound-remote): New procedure.
> * gnu/service/dns.scm (unbound-server): New record.
> * gnu/service/dns.scm (unbound-serialize-unbound-server): New procedure.
> * gnu/service/dns.scm (unbound-configuration): New record.
> * gnu/service/dns.scm (unbound-config-file): New procedure.
> * gnu/service/dns.scm (unbound-shepherd-service): New procedure.
> * gnu/service/dns.scm (unbound-account-service): New constant.
> * gnu/service/dns.scm (unbound-service-type): New services.
> * gnu/tests/dns.scm: New file.
> * gnu/local.mk: Add new files.
> * doc/guix.texi: Add documentation.
>
> Signed-off-by: S=C3=B6ren Tempel <soeren@HIDDEN>

Applied with the cosmetic changes below and tweaks to the commit log,
such as remove repetitions of the file name.

Thanks!

Ludo=E2=80=99.


--=-=-=
Content-Type: text/x-patch; charset=utf-8
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable

diff --git a/doc/guix.texi b/doc/guix.texi
index a9b548cd45..3a64fede2d 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -135,6 +135,7 @@
 Copyright @copyright{} 2024 Troy Figiel@*
 Copyright @copyright{} 2024 Sharlatan Hellseher@*
 Copyright @copyright{} 2024 45mg@*
+Copyright @copyright{} 2025 S=C3=B6ren Tempel@*
=20
 Permission is granted to copy, distribute and/or modify this document
 under the terms of the GNU Free Documentation License, Version 1.3 or
@@ -34303,8 +34304,9 @@ DNS Services
 @subsubheading Unbound Service
=20
 @defvar unbound-service-type
-This is the type of the unbound service, whose value should be a
-@code{unbound-configuration} object as in this example:
+This is the type of the service to run @uref{https://www.unbound.net,
+Unbound}, a validating, recursive, and caching DNS resolver.  Its value
+must be a @code{unbound-configuration} object as in this example:
=20
 @lisp
 (service unbound-service-type

--=-=-=--




Notification sent to soeren@HIDDEN:
bug acknowledged by developer. Full text available.
Reply sent to Ludovic Courtès <ludo@HIDDEN>:
You have taken responsibility. Full text available.

Message received at 68757 <at> debbugs.gnu.org:


Received: (at 68757) by debbugs.gnu.org; 11 Jan 2025 19:14:06 +0000
From debbugs-submit-bounces <at> debbugs.gnu.org Sat Jan 11 14:14:06 2025
Received: from localhost ([127.0.0.1]:44946 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1tWgvq-00074S-Ek
	for submit <at> debbugs.gnu.org; Sat, 11 Jan 2025 14:14:06 -0500
Received: from magnesium.8pit.net ([45.76.88.171]:39597)
 by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.84_2) (envelope-from <soeren@HIDDEN>)
 id 1tWgvm-00073t-TG; Sat, 11 Jan 2025 14:14:01 -0500
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; s=opensmtpd; bh=Mv7/IO9T
 N/X3toLBpqZ0gR7XfDTe5nAioj29ymRJKJM=; h=date:subject:to:from; 
 d=soeren-tempel.net;
 b=LQHX0e6gYguDU8ViwPlano5V8Qn6oFvDoMNvQ1SXU2oSSKS
 1kS4zVXypEWzINZnSiGoFKP9Q3ywhYiN1xdFUkJpihNob0BsQErv1oBhgkHMR83c3mpIJc
 u4y9Mb6/Klv0a+WxzQd8vlyalf4JQwcbOLq34j2ez52tmn0PsaMCWE=
Received: from localhost (<unknown> [2a02:560:4d3d:df00:dc2b:c47d:2594:21b8])
 by magnesium.8pit.net (OpenSMTPD) with ESMTPSA id d6384b72
 (TLSv1.3:TLS_AES_256_GCM_SHA384:256:YES); 
 Sat, 11 Jan 2025 20:13:56 +0100 (CET)
From: soeren@HIDDEN
To: 68757 <at> debbugs.gnu.org
Subject: [PATCH v4 1/1] services: dns: Add unbound service
Date: Sat, 11 Jan 2025 20:12:32 +0100
Message-ID: <20250111191341.32416-1-soeren@HIDDEN>
X-Mailer: git-send-email 2.47.1
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
X-Spam-Score: 0.0 (/)
X-Debbugs-Envelope-To: 68757
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: -1.0 (-)

From: Sören Tempel <soeren@HIDDEN>

This allows using Unbound as a local DNSSEC-enabled resolver. This
commit also allows configuration of the Unbound DNS resolver via a
Scheme API. The API currently provides very common options and
includes an escape hatch to enable less common configurations.

* gnu/service/dns.scm (unbound-serialize-field): New procedure.
* gnu/service/dns.scm (unbound-serialize-alist): New procedure.
* gnu/service/dns.scm (unbound-serialize-section): New procedure.
* gnu/service/dns.scm (unbound-serialize-string): New procedure.
* gnu/service/dns.scm (unbound-serialize-boolean): New procedure.
* gnu/service/dns.scm (unbound-serialize-list-of-strings): New procedure.
* gnu/service/dns.scm (unbound-zone): New record.
* gnu/service/dns.scm (unbound-serialize-unbound-zone): New procedure.
* gnu/service/dns.scm (unbound-serialize-list-of-unbound-zone): New procedure.
* gnu/service/dns.scm (unbound-remote): New record.
* gnu/service/dns.scm (unbound-serialize-unbound-remote): New procedure.
* gnu/service/dns.scm (unbound-server): New record.
* gnu/service/dns.scm (unbound-serialize-unbound-server): New procedure.
* gnu/service/dns.scm (unbound-configuration): New record.
* gnu/service/dns.scm (unbound-config-file): New procedure.
* gnu/service/dns.scm (unbound-shepherd-service): New procedure.
* gnu/service/dns.scm (unbound-account-service): New constant.
* gnu/service/dns.scm (unbound-service-type): New services.
* gnu/tests/dns.scm: New file.
* gnu/local.mk: Add new files.
* doc/guix.texi: Add documentation.

---
Changes since v3: Removed service dependency on networking, depend on
user-processes instead. Remove some removed variables from the module's
export declaration and fix some typos.

 doc/guix.texi        |  95 ++++++++++++++++++++++
 gnu/local.mk         |   1 +
 gnu/services/dns.scm | 185 ++++++++++++++++++++++++++++++++++++++++++-
 gnu/tests/dns.scm    | 108 +++++++++++++++++++++++++
 4 files changed, 388 insertions(+), 1 deletion(-)
 create mode 100644 gnu/tests/dns.scm

diff --git a/doc/guix.texi b/doc/guix.texi
index caebe3b03c..d9ed112494 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -34300,6 +34300,101 @@ command-line arguments to @command{dnsmasq} as a list of strings.
 @end table
 @end deftp
 
+@subsubheading Unbound Service
+
+@defvar unbound-service-type
+This is the type of the unbound service, whose value should be a
+@code{unbound-configuration} object as in this example:
+
+@lisp
+(service unbound-service-type
+         (unbound-configuration
+          (forward-zone
+           (list
+            (unbound-zone
+             (name ".")
+             (forward-addr '("149.112.112.112#dns.quad9.net"
+                             "2620:fe::9#dns.quad9.net"))
+             (forward-tls-upstream #t))))))
+@end lisp
+@end defvar
+
+@deftp {Data Type} unbound-configuration
+Available @code{unbound-configuration} fields are:
+
+@table @asis
+@item @code{server} (type: unbound-server)
+General options for the Unbound server.
+
+@item @code{remote-control} (type: unbound-remote)
+Remote control options for the daemon.
+
+@item @code{forward-zone} (default: @code{()}) (type: list-of-unbound-zone)
+A zone for which queries should be forwarded to another resolver.
+
+@item @code{extra-content} (type: maybe-string)
+Raw content to add to the configuration file.
+
+@end table
+@end deftp
+
+@deftp {Data Type} unbound-server
+Available @code{unbound-server} fields are:
+
+@table @asis
+@item @code{interface} (type: maybe-list-of-strings)
+Interfaces listened on for queries from clients.
+
+@item @code{hide-version} (type: maybe-boolean)
+Refuse the version.server and version.bind queries.
+
+@item @code{hide-identity} (type: maybe-boolean)
+Refuse the id.server and hostname.bind queries.
+
+@item @code{tls-cert-bundle} (type: maybe-string)
+Certificate bundle file, used for DNS over TLS.
+
+@item @code{extra-options} (default: @code{()}) (type: alist)
+An association list of options to append.
+
+@end table
+@end deftp
+
+@deftp {Data Type} unbound-remote
+Available @code{unbound-remote} fields are:
+
+@table @asis
+@item @code{control-enable} (type: maybe-boolean)
+Enable remote control.
+
+@item @code{control-interface} (type: maybe-string)
+IP address or local socket path to listen on for remote control.
+
+@item @code{extra-options} (default: @code{()}) (type: alist)
+An association list of options to append.
+
+@end table
+@end deftp
+
+@deftp {Data Type} unbound-zone
+Available @code{unbound-zone} fields are:
+
+@table @asis
+@item @code{name} (type: string)
+Zone name.
+
+@item @code{forward-addr} (type: maybe-list-of-strings)
+IP address of server to forward to.
+
+@item @code{forward-tls-upstream} (type: maybe-boolean)
+Whether the queries to this forwarder use TLS for transport.
+
+@item @code{extra-options} (default: @code{()}) (type: alist)
+An association list of options to append.
+
+@end table
+@end deftp
+
 @node VNC Services
 @subsection VNC Services
 @cindex VNC (virtual network computing)
diff --git a/gnu/local.mk b/gnu/local.mk
index 1d15be886d..9201230f35 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -838,6 +838,7 @@ GNU_SYSTEM_MODULES =				\
   %D%/tests/cups.scm				\
   %D%/tests/databases.scm			\
   %D%/tests/desktop.scm				\
+  %D%/tests/dns.scm				\
   %D%/tests/dict.scm				\
   %D%/tests/docker.scm				\
   %D%/tests/emacs.scm				\
diff --git a/gnu/services/dns.scm b/gnu/services/dns.scm
index 532e20e38a..a237c12883 100644
--- a/gnu/services/dns.scm
+++ b/gnu/services/dns.scm
@@ -3,6 +3,7 @@
 ;;; Copyright © 2020 Pierre Langlois <pierre.langlois@HIDDEN>
 ;;; Copyright © 2021 Maxime Devos <maximedevos@HIDDEN>
 ;;; Copyright © 2022 Remco van 't Veer <remco@HIDDEN>
+;;; Copyright © 2024 Sören Tempel <soeren@HIDDEN>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -52,7 +53,14 @@ (define-module (gnu services dns)
             knot-resolver-configuration
 
             dnsmasq-service-type
-            dnsmasq-configuration))
+            dnsmasq-configuration
+
+            unbound-service-type
+            unbound-configuration
+            unbound-configuration
+            unbound-server
+            unbound-zone
+            unbound-remote))
 
 ;;;
 ;;; Knot DNS.
@@ -902,3 +910,178 @@ (define dnsmasq-service-type
                              dnsmasq-activation)))
    (default-value (dnsmasq-configuration))
    (description "Run the dnsmasq DNS server.")))
+
+
+;;;
+;;; Unbound.
+;;;
+
+(define (unbound-serialize-field field-name value)
+  (let ((field (object->string field-name))
+        (value (cond
+                 ((boolean? value) (if value "yes" "no"))
+                 ((string? value) value)
+                 (else (object->string value)))))
+    (if (string=? field "extra-content")
+      #~(string-append #$value "\n")
+      #~(format #f "	~a: ~s~%" #$field #$value))))
+
+(define (unbound-serialize-alist field-name value)
+  #~(string-append #$@(generic-serialize-alist list
+                                               unbound-serialize-field
+                                               value)))
+
+(define (unbound-serialize-section section-name value fields)
+  #~(format #f "~a:~%~a"
+            #$(object->string section-name)
+            #$(serialize-configuration value fields)))
+
+(define unbound-serialize-string unbound-serialize-field)
+(define unbound-serialize-boolean unbound-serialize-field)
+
+(define-maybe string (prefix unbound-))
+(define-maybe list-of-strings (prefix unbound-))
+(define-maybe boolean (prefix unbound-))
+
+(define (unbound-serialize-list-of-strings field-name value)
+  #~(string-append #$@(map (cut unbound-serialize-string field-name <>) value)))
+
+(define-configuration unbound-zone
+  (name
+    string
+    "Zone name.")
+
+  (forward-addr
+    maybe-list-of-strings
+    "IP address of server to forward to.")
+
+  (forward-tls-upstream
+    maybe-boolean
+    "Whether the queries to this forwarder use TLS for transport.")
+
+  (extra-options
+   (alist '())
+   "An association list of options to append.")
+
+  (prefix unbound-))
+
+(define (unbound-serialize-unbound-zone field-name value)
+  (unbound-serialize-section field-name value unbound-zone-fields))
+
+(define (unbound-serialize-list-of-unbound-zone field-name value)
+  #~(string-append #$@(map (cut unbound-serialize-unbound-zone field-name <>)
+                           value)))
+
+(define list-of-unbound-zone? (list-of unbound-zone?))
+
+(define-configuration unbound-remote
+  (control-enable
+    maybe-boolean
+    "Enable remote control.")
+
+  (control-interface
+    maybe-string
+    "IP address or local socket path to listen on for remote control.")
+
+  (extra-options
+   (alist '())
+   "An association list of options to append.")
+
+  (prefix unbound-))
+
+(define (unbound-serialize-unbound-remote field-name value)
+  (unbound-serialize-section field-name value unbound-remote-fields))
+
+(define-configuration unbound-server
+  (interface
+    maybe-list-of-strings
+    "Interfaces listened on for queries from clients.")
+
+  (hide-version
+    maybe-boolean
+    "Refuse the version.server and version.bind queries.")
+
+  (hide-identity
+    maybe-boolean
+    "Refuse the id.server and hostname.bind queries.")
+
+  (tls-cert-bundle
+    maybe-string
+    "Certificate bundle file, used for DNS over TLS.")
+
+  (extra-options
+   (alist '())
+   "An association list of options to append.")
+
+  (prefix unbound-))
+
+(define (unbound-serialize-unbound-server field-name value)
+  (unbound-serialize-section field-name value unbound-server-fields))
+
+(define-configuration unbound-configuration
+  (server
+    (unbound-server
+      (unbound-server
+        (interface '("127.0.0.1" "::1"))
+
+        (hide-version #t)
+        (hide-identity #t)
+
+        (tls-cert-bundle "/etc/ssl/certs/ca-certificates.crt")))
+    "General options for the Unbound server.")
+
+  (remote-control
+    (unbound-remote
+      (unbound-remote
+        (control-enable #t)
+        (control-interface "/run/unbound.sock")))
+    "Remote control options for the daemon.")
+
+  (forward-zone
+    (list-of-unbound-zone '())
+    "A zone for which queries should be forwarded to another resolver.")
+
+  (extra-content
+    maybe-string
+    "Raw content to add to the configuration file.")
+
+  (prefix unbound-))
+
+(define (unbound-config-file config)
+  (mixed-text-file "unbound.conf"
+    (serialize-configuration
+      config
+      unbound-configuration-fields)))
+
+(define (unbound-shepherd-service config)
+  (let ((config-file (unbound-config-file config)))
+    (list (shepherd-service
+            (documentation "Unbound daemon.")
+            (provision '(unbound dns))
+            (requirement '(user-processes))
+            (actions (list (shepherd-configuration-action config-file)))
+            (start #~(make-forkexec-constructor
+                       (list (string-append #$unbound "/sbin/unbound")
+                             "-d" "-p" "-c" #$config-file)))
+            (stop #~(make-kill-destructor))))))
+
+(define unbound-account-service
+  (list (user-group (name "unbound") (system? #t))
+        (user-account
+         (name "unbound")
+         (group "unbound")
+         (system? #t)
+         (comment "Unbound daemon user")
+         (home-directory "/var/empty")
+         (shell (file-append shadow "/sbin/nologin")))))
+
+(define unbound-service-type
+  (service-type (name 'unbound)
+                (description "Run the Unbound DNS resolver.")
+                (extensions
+                  (list (service-extension account-service-type
+                                           (const unbound-account-service))
+                        (service-extension shepherd-root-service-type
+                                           unbound-shepherd-service)))
+                (compose concatenate)
+                (default-value (unbound-configuration))))
diff --git a/gnu/tests/dns.scm b/gnu/tests/dns.scm
new file mode 100644
index 0000000000..057ce63484
--- /dev/null
+++ b/gnu/tests/dns.scm
@@ -0,0 +1,108 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2025 Sören Tempel <soeren@HIDDEN>
+;;;
+;;; 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 tests dns)
+  #:use-module (gnu tests)
+  #:use-module (gnu system)
+  #:use-module (gnu system vm)
+  #:use-module (gnu services)
+  #:use-module (gnu services dns)
+  #:use-module (gnu packages dns)
+  #:use-module (guix gexp)
+  #:export (%test-unbound))
+
+(define %unbound-os
+  ;; TODO: Unbound config
+  (let ((base-os
+          (simple-operating-system
+            (service unbound-service-type
+                     (unbound-configuration
+                       (server
+                         (unbound-server
+                           (interface '("127.0.0.1" "::1"))
+                           (extra-options
+                             '((local-data . "example.local A 192.0.2.1"))))))))))
+    (operating-system
+      (inherit base-os)
+      (packages
+        (append (list
+                  `(,isc-bind "utils")
+                  unbound)
+                (operating-system-packages base-os))))))
+
+(define (run-unbound-test)
+  "Run tests in %unbound-os with a running unbound daemon on localhost."
+  (define os
+    (marionette-operating-system
+     %unbound-os
+     #:imported-modules '((gnu services herd))))
+
+  (define vm
+    (virtual-machine os))
+
+  (define test
+    (with-imported-modules '((gnu build marionette))
+      #~(begin
+          (use-modules (srfi srfi-64)
+                       (gnu build marionette))
+          (define marionette
+            (make-marionette (list #$vm)))
+
+          (test-runner-current (system-test-runner #$output))
+          (test-begin "unbound")
+
+          (test-assert "service is running"
+            (marionette-eval
+             '(begin
+                (use-modules (gnu services herd))
+
+                ;; Make sure the 'unbound-control' and 'host' command is found.
+                (setenv "PATH" "/run/current-system/profile/bin:/run/current-system/profile/sbin")
+
+                (start-service 'unbound))
+             marionette))
+
+          (test-equal "unbound remote control works"
+            0
+            (marionette-eval
+              '(status:exit-val
+                 (system* "unbound-control" "-s" "/run/unbound.sock" "status"))
+              marionette))
+
+          ;; We use a custom local-data A record here to avoid depending
+          ;; on network access and being able to contact the root servers.
+          (test-equal "resolves local-data domain"
+            "192.0.2.1"
+            (marionette-eval
+              '(begin
+                 (use-modules (ice-9 popen) (rnrs io ports))
+
+                 (let* ((port (open-input-pipe "dig @127.0.0.1 example.local +short"))
+                        (out  (get-string-all port)))
+                   (close-port port)
+                   (string-drop-right out 1))) ;; drop newline
+              marionette))
+
+          (test-end))))
+  (gexp->derivation "unbound-test" test))
+
+(define %test-unbound
+  (system-test
+   (name "unbound")
+   (description "Test that the unbound can respond to queries.")
+   (value (run-unbound-test))))




Information forwarded to guix-patches@HIDDEN:
bug#68757; Package guix-patches. Full text available.

Message received at 68757 <at> debbugs.gnu.org:


Received: (at 68757) by debbugs.gnu.org; 8 Jan 2025 21:14:36 +0000
From debbugs-submit-bounces <at> debbugs.gnu.org Wed Jan 08 16:14:36 2025
Received: from localhost ([127.0.0.1]:49231 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1tVdNq-0003kk-TV
	for submit <at> debbugs.gnu.org; Wed, 08 Jan 2025 16:14:35 -0500
Received: from magnesium.8pit.net ([2001:19f0:6c01:4ae:5400:ff:fe66:af9d]:7644)
 by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.84_2) (envelope-from <soeren@HIDDEN>)
 id 1tVdNn-0003kW-8J; Wed, 08 Jan 2025 16:14:33 -0500
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; s=opensmtpd; bh=iKrPZUKE
 az93D5/EmbRVHzNGFsuG+XEutgdHidWvG+Q=; h=date:subject:cc:to:from;
 d=soeren-tempel.net; b=WAXQG3RKRzwB71xB0NXM1OLLNbXFb9c9F+0BDKehLfaOzvn
 RXsQrr7XH7RygkbJCC60iaumwKllw9IttKDK/RuSss5KmoDcs3OjINk65ew1H1b/cC0LM4
 fAykYMniGPeta9XFkHHD/8GANnppFToS21gGsoSs8KUauiErwXkLP8=
Received: from localhost (<unknown> [2a02:560:4d3d:df00:27ca:6ad:4496:a67a])
 by magnesium.8pit.net (OpenSMTPD) with ESMTPSA id 142249df
 (TLSv1.3:TLS_AES_256_GCM_SHA384:256:YES); 
 Wed, 8 Jan 2025 22:14:25 +0100 (CET)
From: soeren@HIDDEN
To: 68757 <at> debbugs.gnu.org
Subject: [PATCH v3 1/1] services: dns: Add unbound service
Date: Wed,  8 Jan 2025 22:13:54 +0100
Message-ID: <20250108211416.27602-1-soeren@HIDDEN>
X-Mailer: git-send-email 2.47.1
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
X-Spam-Score: 0.0 (/)
X-Debbugs-Envelope-To: 68757
Cc: ludo@HIDDEN
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: -1.0 (-)

From: Sören Tempel <soeren@HIDDEN>

This allows using Unbound as a local DNSSEC-enabled resolver. This
commit also allows configuration of the Unbound DNS resolver via a
Scheme API. The API currently provides very common options and
includes an escape hatch to enable less common configurations.

* gnu/service/dns.scm (unbound-serialize-field): New procedure.
* gnu/service/dns.scm (unbound-serialize-alist): New procedure.
* gnu/service/dns.scm (unbound-serialize-section): New procedure.
* gnu/service/dns.scm (unbound-serialize-string): New procedure.
* gnu/service/dns.scm (unbound-serialize-boolean): New procedure.
* gnu/service/dns.scm (unbound-serialize-list-of-strings): New procedure.
* gnu/service/dns.scm (unbound-zone): New record.
* gnu/service/dns.scm (unbound-serialize-unbound-zone): New procedure.
* gnu/service/dns.scm (unbound-serialize-list-of-unbound-zone): New procedure.
* gnu/service/dns.scm (unbound-remote): New record.
* gnu/service/dns.scm (unbound-serialize-unbound-remote): New procedure.
* gnu/service/dns.scm (unbound-server): New record.
* gnu/service/dns.scm (unbound-serialize-unbound-server): New procedure.
* gnu/service/dns.scm (unbound-configuration): New record.
* gnu/service/dns.scm (unbound-config-file): New procedure.
* gnu/service/dns.scm (unbound-shepherd-service): New procedure.
* gnu/service/dns.scm (unbound-account-service): New constant.
* gnu/service/dns.scm (unbound-service-type): New services.
* gnu/tests/dns.scm: New file.
* gnu/local.mk: Add new files.
* doc/guix.texi: Add documentation.

Signed-off-by: Sören Tempel <soeren@HIDDEN>
---
Changes since v2: Added a system test and documentation.

 doc/guix.texi        |  95 +++++++++++++++++++++
 gnu/local.mk         |   1 +
 gnu/services/dns.scm | 192 ++++++++++++++++++++++++++++++++++++++++++-
 gnu/tests/dns.scm    | 110 +++++++++++++++++++++++++
 4 files changed, 397 insertions(+), 1 deletion(-)
 create mode 100644 gnu/tests/dns.scm

diff --git a/doc/guix.texi b/doc/guix.texi
index caebe3b03c..d9ed112494 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -34300,6 +34300,101 @@ command-line arguments to @command{dnsmasq} as a list of strings.
 @end table
 @end deftp
 
+@subsubheading Unbound Service
+
+@defvar unbound-service-type
+This is the type of the unbound service, whose value should be a
+@code{unbound-configuration} object as in this example:
+
+@lisp
+(service unbound-service-type
+         (unbound-configuration
+          (forward-zone
+           (list
+            (unbound-zone
+             (name ".")
+             (forward-addr '("149.112.112.112#dns.quad9.net"
+                             "2620:fe::9#dns.quad9.net"))
+             (forward-tls-upstream #t))))))
+@end lisp
+@end defvar
+
+@deftp {Data Type} unbound-configuration
+Available @code{unbound-configuration} fields are:
+
+@table @asis
+@item @code{server} (type: unbound-server)
+General options for the Unbound server.
+
+@item @code{remote-control} (type: unbound-remote)
+Remote control options for the daemon.
+
+@item @code{forward-zone} (default: @code{()}) (type: list-of-unbound-zone)
+A zone for which queries should be forwarded to another resolver.
+
+@item @code{extra-content} (type: maybe-string)
+Raw content to add to the configuration file.
+
+@end table
+@end deftp
+
+@deftp {Data Type} unbound-server
+Available @code{unbound-server} fields are:
+
+@table @asis
+@item @code{interface} (type: maybe-list-of-strings)
+Interfaces listened on for queries from clients.
+
+@item @code{hide-version} (type: maybe-boolean)
+Refuse the version.server and version.bind queries.
+
+@item @code{hide-identity} (type: maybe-boolean)
+Refuse the id.server and hostname.bind queries.
+
+@item @code{tls-cert-bundle} (type: maybe-string)
+Certificate bundle file, used for DNS over TLS.
+
+@item @code{extra-options} (default: @code{()}) (type: alist)
+An association list of options to append.
+
+@end table
+@end deftp
+
+@deftp {Data Type} unbound-remote
+Available @code{unbound-remote} fields are:
+
+@table @asis
+@item @code{control-enable} (type: maybe-boolean)
+Enable remote control.
+
+@item @code{control-interface} (type: maybe-string)
+IP address or local socket path to listen on for remote control.
+
+@item @code{extra-options} (default: @code{()}) (type: alist)
+An association list of options to append.
+
+@end table
+@end deftp
+
+@deftp {Data Type} unbound-zone
+Available @code{unbound-zone} fields are:
+
+@table @asis
+@item @code{name} (type: string)
+Zone name.
+
+@item @code{forward-addr} (type: maybe-list-of-strings)
+IP address of server to forward to.
+
+@item @code{forward-tls-upstream} (type: maybe-boolean)
+Whether the queries to this forwarder use TLS for transport.
+
+@item @code{extra-options} (default: @code{()}) (type: alist)
+An association list of options to append.
+
+@end table
+@end deftp
+
 @node VNC Services
 @subsection VNC Services
 @cindex VNC (virtual network computing)
diff --git a/gnu/local.mk b/gnu/local.mk
index f118fe4442..5d550b0639 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -832,6 +832,7 @@ GNU_SYSTEM_MODULES =				\
   %D%/tests/cups.scm				\
   %D%/tests/databases.scm			\
   %D%/tests/desktop.scm				\
+  %D%/tests/dns.scm				\
   %D%/tests/dict.scm				\
   %D%/tests/docker.scm				\
   %D%/tests/emacs.scm				\
diff --git a/gnu/services/dns.scm b/gnu/services/dns.scm
index 532e20e38a..c74001fac2 100644
--- a/gnu/services/dns.scm
+++ b/gnu/services/dns.scm
@@ -3,6 +3,7 @@
 ;;; Copyright © 2020 Pierre Langlois <pierre.langlois@HIDDEN>
 ;;; Copyright © 2021 Maxime Devos <maximedevos@HIDDEN>
 ;;; Copyright © 2022 Remco van 't Veer <remco@HIDDEN>
+;;; Copyright © 2024 Sören Tempel <soeren@HIDDEN>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -52,7 +53,21 @@ (define-module (gnu services dns)
             knot-resolver-configuration
 
             dnsmasq-service-type
-            dnsmasq-configuration))
+            dnsmasq-configuration
+
+            unbound-service-type
+            unbound-zone
+            unbound-server
+            unbound-configuration
+            unbound-configuration?
+            unbound-configuration-server
+            unbound-configuration-remote-control
+            unbound-configuration-forward-zone
+            unbound-configuration-stub-zone
+            unbound-configuration-auth-zone
+            unbound-configuration-view
+            unbound-configuration-python
+            unbound-configuration-dynlib))
 
 ;;;
 ;;; Knot DNS.
@@ -902,3 +917,178 @@ (define dnsmasq-service-type
                              dnsmasq-activation)))
    (default-value (dnsmasq-configuration))
    (description "Run the dnsmasq DNS server.")))
+
+
+;;;
+;;; Unbound.
+;;;
+
+(define (unbound-serialize-field field-name value)
+  (let ((field (object->string field-name))
+        (value (cond
+                 ((boolean? value) (if value "yes" "no"))
+                 ((string? value) value)
+                 (else (object->string value)))))
+    (if (string=? field "extra-content")
+      #~(string-append #$value "\n")
+      #~(format #f "	~a: ~s~%" #$field #$value))))
+
+(define (unbound-serialize-alist field-name value)
+  #~(string-append #$@(generic-serialize-alist list
+                                               unbound-serialize-field
+                                               value)))
+
+(define (unbound-serialize-section section-name value fields)
+  #~(format #f "~a:~%~a"
+            #$(object->string section-name)
+            #$(serialize-configuration value fields)))
+
+(define unbound-serialize-string unbound-serialize-field)
+(define unbound-serialize-boolean unbound-serialize-field)
+
+(define-maybe string (prefix unbound-))
+(define-maybe list-of-strings (prefix unbound-))
+(define-maybe boolean (prefix unbound-))
+
+(define (unbound-serialize-list-of-strings field-name value)
+  #~(string-append #$@(map (cut unbound-serialize-string field-name <>) value)))
+
+(define-configuration unbound-zone
+  (name
+    string
+    "Zone name.")
+
+  (forward-addr
+    maybe-list-of-strings
+    "IP address of server to forward to.")
+
+  (forward-tls-upstream
+    maybe-boolean
+    "Whether the queries to this forwarder use TLS for transport.")
+
+  (extra-options
+   (alist '())
+   "An association list of options to append.")
+
+  (prefix unbound-))
+
+(define (unbound-serialize-unbound-zone field-name value)
+  (unbound-serialize-section field-name value unbound-zone-fields))
+
+(define (unbound-serialize-list-of-unbound-zone field-name value)
+  #~(string-append #$@(map (cut unbound-serialize-unbound-zone field-name <>)
+                           value)))
+
+(define list-of-unbound-zone? (list-of unbound-zone?))
+
+(define-configuration unbound-remote
+  (control-enable
+    maybe-boolean
+    "Enable remote control.")
+
+  (control-interface
+    maybe-string
+    "IP address or local socket path to listen on for remote control.")
+
+  (extra-options
+   (alist '())
+   "An association list of options to append.")
+
+  (prefix unbound-))
+
+(define (unbound-serialize-unbound-remote field-name value)
+  (unbound-serialize-section field-name value unbound-remote-fields))
+
+(define-configuration unbound-server
+  (interface
+    maybe-list-of-strings
+    "Interfaces listened on for queries from clients.")
+
+  (hide-version
+    maybe-boolean
+    "Refuse the version.server and version.bind queries.")
+
+  (hide-identity
+    maybe-boolean
+    "Refuse the id.server and hostname.bind queries.")
+
+  (tls-cert-bundle
+    maybe-string
+    "Certificate bundle file, used for DNS over TLS.")
+
+  (extra-options
+   (alist '())
+   "An association list of options to append.")
+
+  (prefix unbound-))
+
+(define (unbound-serialize-unbound-server field-name value)
+  (unbound-serialize-section field-name value unbound-server-fields))
+
+(define-configuration unbound-configuration
+  (server
+    (unbound-server
+      (unbound-server
+        (interface '("127.0.0.1" "::1"))
+
+        (hide-version #t)
+        (hide-identity #t)
+
+        (tls-cert-bundle "/etc/ssl/certs/ca-certificates.crt")))
+    "General options for the Unbound server.")
+
+  (remote-control
+    (unbound-remote
+      (unbound-remote
+        (control-enable #t)
+        (control-interface "/run/unbound.sock")))
+    "Remote control options for the daemon.")
+
+  (forward-zone
+    (list-of-unbound-zone '())
+    "A zone for which queries should be forwarded to another resolver.")
+
+  (extra-content
+    maybe-string
+    "Raw content to add to the configuration file.")
+
+  (prefix unbound-))
+
+(define (unbound-config-file config)
+  (mixed-text-file "unbound.conf"
+    (serialize-configuration
+      config
+      unbound-configuration-fields)))
+
+(define (unbound-shepherd-service config)
+  (let ((config-file (unbound-config-file config)))
+    (list (shepherd-service
+            (documentation "Unbound daemon.")
+            (provision '(unbound dns))
+            (requirement '(networking))
+            (actions (list (shepherd-configuration-action config-file)))
+            (start #~(make-forkexec-constructor
+                       (list (string-append #$unbound "/sbin/unbound")
+                             "-d" "-p" "-c" #$config-file)))
+            (stop #~(make-kill-destructor))))))
+
+(define unbound-account-service
+  (list (user-group (name "unbound") (system? #t))
+        (user-account
+         (name "unbound")
+         (group "unbound")
+         (system? #t)
+         (comment "Unbound daemon user")
+         (home-directory "/var/empty")
+         (shell "/run/current-system/profile/sbin/nologin"))))
+
+(define unbound-service-type
+  (service-type (name 'unbound)
+                (description "Run the unbound DNS resolver.")
+                (extensions
+                  (list (service-extension account-service-type
+                                           (const unbound-account-service))
+                        (service-extension shepherd-root-service-type
+                                           unbound-shepherd-service)))
+                (compose concatenate)
+                (default-value (unbound-configuration))))
diff --git a/gnu/tests/dns.scm b/gnu/tests/dns.scm
new file mode 100644
index 0000000000..ff42456760
--- /dev/null
+++ b/gnu/tests/dns.scm
@@ -0,0 +1,110 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2025 Sören Tempel <soeren@HIDDEN>
+;;;
+;;; 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 tests dns)
+  #:use-module (gnu tests)
+  #:use-module (gnu system)
+  #:use-module (gnu system vm)
+  #:use-module (gnu services)
+  #:use-module (gnu services dns)
+  #:use-module (gnu services networking)
+  #:use-module (gnu packages dns)
+  #:use-module (guix gexp)
+  #:export (%test-unbound))
+
+(define %unbound-os
+  ;; TODO: Unbound config
+  (let ((base-os
+          (simple-operating-system
+            (service dhcp-client-service-type)
+            (service unbound-service-type
+                     (unbound-configuration
+                       (server
+                         (unbound-server
+                           (interface '("127.0.0.1" "::1"))
+                           (extra-options
+                             '((local-data . "example.local A 192.0.2.1"))))))))))
+    (operating-system
+      (inherit base-os)
+      (packages
+        (append (list
+                  `(,isc-bind "utils")
+                  unbound)
+                (operating-system-packages base-os))))))
+
+(define (run-unbound-test)
+  "Run tests in %unbound-os with a running unbound daemon on localhost."
+  (define os
+    (marionette-operating-system
+     %unbound-os
+     #:imported-modules '((gnu services herd))))
+
+  (define vm
+    (virtual-machine os))
+
+  (define test
+    (with-imported-modules '((gnu build marionette))
+      #~(begin
+          (use-modules (srfi srfi-64)
+                       (gnu build marionette))
+          (define marionette
+            (make-marionette (list #$vm)))
+
+          (test-runner-current (system-test-runner #$output))
+          (test-begin "unbound")
+
+          (test-assert "service is running"
+            (marionette-eval
+             '(begin
+                (use-modules (gnu services herd))
+
+                ;; Make sure the 'unbound-control' and 'host' command is found.
+                (setenv "PATH" "/run/current-system/profile/bin:/run/current-system/profile/sbin")
+
+                (start-service 'unbound))
+             marionette))
+
+          (test-equal "unbound remote control works"
+            0
+            (marionette-eval
+              '(status:exit-val
+                 (system* "unbound-control" "-s" "/run/unbound.sock" "status"))
+              marionette))
+
+          ;; We use a custom local-data A record here to avoid depending
+          ;; on network access and being able to contact the root servers.
+          (test-equal "resolves local-data domain"
+            "192.0.2.1"
+            (marionette-eval
+              '(begin
+                 (use-modules (ice-9 popen) (rnrs io ports))
+
+                 (let* ((port (open-input-pipe "dig @127.0.0.1 example.local +short"))
+                        (out  (get-string-all port)))
+                   (close-port port)
+                   (string-drop-right out 1))) ;; drop newline
+              marionette))
+
+          (test-end))))
+  (gexp->derivation "unbound-test" test))
+
+(define %test-unbound
+  (system-test
+   (name "unbound")
+   (description "Test that the unbound can respond to queries.")
+   (value (run-unbound-test))))




Information forwarded to guix-patches@HIDDEN:
bug#68757; Package guix-patches. Full text available.

Message received at 68757 <at> debbugs.gnu.org:


Received: (at 68757) by debbugs.gnu.org; 7 Jan 2025 18:22:32 +0000
From debbugs-submit-bounces <at> debbugs.gnu.org Tue Jan 07 13:22:32 2025
Received: from localhost ([127.0.0.1]:44607 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1tVEDk-0005zs-0v
	for submit <at> debbugs.gnu.org; Tue, 07 Jan 2025 13:22:32 -0500
Received: from magnesium.8pit.net ([45.76.88.171]:48683)
 by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.84_2) (envelope-from <soeren@HIDDEN>)
 id 1tVEDe-0005zY-Q4
 for 68757 <at> debbugs.gnu.org; Tue, 07 Jan 2025 13:22:27 -0500
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; s=opensmtpd; bh=hQIgdUHT
 E4blLvgSr7JA1si6I1UdSNdN229p6TMf6iw=;
 h=in-reply-to:references:from:
 subject:cc:to:date; d=soeren-tempel.net; b=DKtSXO/jdSRgcljaLr/WeIHGl/+
 h+hCarPYMVI161xgchaSaRKfYrGlcMqj9YiRlnNVhEfcvO2y2Tk17FGE6o+yBDIPJp7ZYG
 8Msd6syLn7rj91xKMeSiz+kGTEO7+swUTA5z3fYx45QQQrzQ0c5MAtuVQhmJ3Xmg9dvXKa
 TFeU=
Received: from localhost (<unknown> [2a02:560:4d3d:df00:553e:15:d1b3:1cf7])
 by magnesium.8pit.net (OpenSMTPD) with ESMTPSA id a8bf33a3
 (TLSv1.3:TLS_AES_256_GCM_SHA384:256:YES); 
 Tue, 7 Jan 2025 19:22:21 +0100 (CET)
Date: Tue, 07 Jan 2025 19:22:21 +0100
To: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
Subject: Re: [bug#68757] [PATCH] services: dns: Add unbound service
From: =?UTF-8?Q?S=C3=B6ren?= Tempel <soeren@HIDDEN>
References: <20240127121040.7156-2-soeren@HIDDEN>
 <87sf1pls1y.fsf@HIDDEN> <2O0HFY6AW6QUG.320OU5YPLJHHZ@HIDDEN>
 <87frxei57o.fsf@HIDDEN>
In-Reply-To: <87frxei57o.fsf@HIDDEN>
Message-Id: <2G8GU5IMBTU4M.20MNA2G0LKMK3@HIDDEN>
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
X-Spam-Score: 0.0 (/)
X-Debbugs-Envelope-To: 68757
Cc: 68757 <at> debbugs.gnu.org
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: -1.0 (-)

Ludovic Court=C3=A8s <ludo@HIDDEN> wrote:
> Hi,

Hello,

> The usual approach for services in Guix is to have a record for the most
> common options (or for all the options if that doing so can be
> automated, as was done with Dovecot) and an =E2=80=9Cescape hatch=E2=80=9D=
 that lets
> users insert raw config text.  Key/value alists are not a common idiom.

I finally got around to revising the patch accordingly, see the v2 that
I just send. As requested in the other mail, I will look into adding a
system test as well ASAP.

Greetings,
S=C3=B6ren




Information forwarded to guix-patches@HIDDEN:
bug#68757; Package guix-patches. Full text available.

Message received at 68757 <at> debbugs.gnu.org:


Received: (at 68757) by debbugs.gnu.org; 7 Jan 2025 18:19:46 +0000
From debbugs-submit-bounces <at> debbugs.gnu.org Tue Jan 07 13:19:46 2025
Received: from localhost ([127.0.0.1]:44599 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1tVEB7-0005mp-AB
	for submit <at> debbugs.gnu.org; Tue, 07 Jan 2025 13:19:46 -0500
Received: from magnesium.8pit.net ([45.76.88.171]:4835)
 by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.84_2) (envelope-from <soeren@HIDDEN>)
 id 1tVEB3-0005mX-Uq; Tue, 07 Jan 2025 13:19:43 -0500
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; s=opensmtpd; bh=c3Ot32by
 1+tSeE9T5ZYxeXc5yCJW3CvhUaRBI//bRN0=; h=date:subject:cc:to:from;
 d=soeren-tempel.net; b=Td9UT2JPIKzvVGjNclufRnzXWPANiah+lsedwjs3UVquX9v
 hYviTHfVslJT+Tn/eMIfhRTjWPTAWUInuFZWW8aOBavmtMLNBMGGE344/JsDbsFzxPLZiD
 0QYCI0l4V2rfaN+fQXC6V3ncCU8Wm6grfueHI/q12HKmrrWpkEQ0LQ=
Received: from localhost (<unknown> [2a02:560:4d3d:df00:553e:15:d1b3:1cf7])
 by magnesium.8pit.net (OpenSMTPD) with ESMTPSA id 64b3f0d3
 (TLSv1.3:TLS_AES_256_GCM_SHA384:256:YES); 
 Tue, 7 Jan 2025 19:19:38 +0100 (CET)
From: soeren@HIDDEN
To: 68757 <at> debbugs.gnu.org
Subject: [PATCH v2 1/1] services: dns: Add unbound service
Date: Tue,  7 Jan 2025 19:17:30 +0100
Message-ID: <20250107181902.3982-1-soeren@HIDDEN>
X-Mailer: git-send-email 2.47.1
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
X-Spam-Score: 0.0 (/)
X-Debbugs-Envelope-To: 68757
Cc: ludo@HIDDEN
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: -1.0 (-)

From: Sören Tempel <soeren@HIDDEN>

This allows using Unbound as a local DNSSEC-enabled resolver. This
commit also allows configuration of the Unbound DNS resolver via a
Scheme API. The API currently provides very common options and
includes an escape hatch to enable less common configurations.

A sample configuration, which uses a DoT forwarder, looks as follows:

	(service unbound-service-type
	 (unbound-configuration
	  (forward-zone
	   (list
	    (unbound-zone
	     (name ".")
	     (forward-addr '("149.112.112.112#dns.quad9.net"
	                     "2620:fe::9#dns.quad9.net"))
	     (forward-tls-upstream #t))))))

* gnu/service/dns.scm (unbound-serialize-field): New procedure.
* gnu/service/dns.scm (unbound-serialize-alist): New procedure.
* gnu/service/dns.scm (unbound-serialize-section): New procedure.
* gnu/service/dns.scm (unbound-serialize-string): New procedure.
* gnu/service/dns.scm (unbound-serialize-boolean): New procedure.
* gnu/service/dns.scm (unbound-serialize-list-of-strings): New procedure.
* gnu/service/dns.scm (unbound-zone): New record.
* gnu/service/dns.scm (unbound-serialize-unbound-zone): New procedure.
* gnu/service/dns.scm (unbound-serialize-list-of-unbound-zone): New procedure.
* gnu/service/dns.scm (unbound-remote): New record.
* gnu/service/dns.scm (unbound-serialize-unbound-remote): New procedure.
* gnu/service/dns.scm (unbound-server): New record.
* gnu/service/dns.scm (unbound-serialize-unbound-server): New procedure.
* gnu/service/dns.scm (unbound-configuration): New record.
* gnu/service/dns.scm (unbound-config-file): New procedure.
* gnu/service/dns.scm (unbound-shepherd-service): New procedure.
* gnu/service/dns.scm (unbound-account-service): New constant.
* gnu/service/dns.scm (unbound-service-type): New services.

Signed-off-by: Sören Tempel <soeren@HIDDEN>
---
Changes since v1: This revision revises unbound-configuration to use
record types with the most common options as record fields instead of
association-list, as requested by ludo@.

 gnu/services/dns.scm | 192 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 191 insertions(+), 1 deletion(-)

diff --git a/gnu/services/dns.scm b/gnu/services/dns.scm
index 532e20e38a..efef5f0fed 100644
--- a/gnu/services/dns.scm
+++ b/gnu/services/dns.scm
@@ -3,6 +3,7 @@
 ;;; Copyright © 2020 Pierre Langlois <pierre.langlois@HIDDEN>
 ;;; Copyright © 2021 Maxime Devos <maximedevos@HIDDEN>
 ;;; Copyright © 2022 Remco van 't Veer <remco@HIDDEN>
+;;; Copyright © 2024 Sören Tempel <soeren@HIDDEN>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -52,7 +53,21 @@ (define-module (gnu services dns)
             knot-resolver-configuration
 
             dnsmasq-service-type
-            dnsmasq-configuration))
+            dnsmasq-configuration
+
+            unbound-service-type
+            unbound-zone
+            unbound-server
+            unbound-configuration
+            unbound-configuration?
+            unbound-configuration-server
+            unbound-configuration-remote-control
+            unbound-configuration-forward-zone
+            unbound-configuration-stub-zone
+            unbound-configuration-auth-zone
+            unbound-configuration-view
+            unbound-configuration-python
+            unbound-configuration-dynlib))
 
 ;;;
 ;;; Knot DNS.
@@ -902,3 +917,178 @@ (define dnsmasq-service-type
                              dnsmasq-activation)))
    (default-value (dnsmasq-configuration))
    (description "Run the dnsmasq DNS server.")))
+
+
+;;;
+;;; Unbound.
+;;;
+
+(define (unbound-serialize-field field-name value)
+  (let ((field (object->string field-name))
+        (value (cond
+                 ((boolean? value) (if value "yes" "no"))
+                 ((string? value) value)
+                 (else (object->string value)))))
+    (if (string=? field "extra-content")
+      #~(string-append #$value "\n")
+      #~(format #f "	~a: ~a~%" #$field #$value))))
+
+(define (unbound-serialize-alist field-name value)
+  #~(string-append #$@(generic-serialize-alist list
+                                               unbound-serialize-field
+                                               value)))
+
+(define (unbound-serialize-section section-name value fields)
+  #~(format #f "~a:~%~a"
+            #$(object->string section-name)
+            #$(serialize-configuration value fields)))
+
+(define unbound-serialize-string unbound-serialize-field)
+(define unbound-serialize-boolean unbound-serialize-field)
+
+(define-maybe string (prefix unbound-))
+(define-maybe list-of-strings (prefix unbound-))
+(define-maybe boolean (prefix unbound-))
+
+(define (unbound-serialize-list-of-strings field-name value)
+  #~(string-append #$@(map (cut unbound-serialize-string field-name <>) value)))
+
+(define-configuration unbound-zone
+  (name
+    string
+    "Zone name.")
+
+  (forward-addr
+    maybe-list-of-strings
+    "IP address of server to forward to.")
+
+  (forward-tls-upstream
+    maybe-boolean
+    "Whether the queries to this forwarder use TLS for transport.")
+
+  (extra-options
+   (alist '())
+   "An association list of options to append.")
+
+  (prefix unbound-))
+
+(define (unbound-serialize-unbound-zone field-name value)
+  (unbound-serialize-section field-name value unbound-zone-fields))
+
+(define (unbound-serialize-list-of-unbound-zone field-name value)
+  #~(string-append #$@(map (cut unbound-serialize-unbound-zone field-name <>)
+                           value)))
+
+(define list-of-unbound-zone? (list-of unbound-zone?))
+
+(define-configuration unbound-remote
+  (control-enable
+    maybe-boolean
+    "Enable remote control.")
+
+  (control-interface
+    maybe-string
+    "IP address or local socket path to listen on for remote control.")
+
+  (extra-options
+   (alist '())
+   "An association list of options to append.")
+
+  (prefix unbound-))
+
+(define (unbound-serialize-unbound-remote field-name value)
+  (unbound-serialize-section field-name value unbound-remote-fields))
+
+(define-configuration unbound-server
+  (interface
+    maybe-list-of-strings
+    "Interfaces listened on for queries from clients.")
+
+  (hide-version
+    maybe-boolean
+    "Refuse the version.server and version.bind queries.")
+
+  (hide-identity
+    maybe-boolean
+    "Refuse the id.server and hostname.bind queries.")
+
+  (tls-cert-bundle
+    maybe-string
+    "Certificate bundle file, used for DNS over TLS.")
+
+  (extra-options
+   (alist '())
+   "An association list of options to append.")
+
+  (prefix unbound-))
+
+(define (unbound-serialize-unbound-server field-name value)
+  (unbound-serialize-section field-name value unbound-server-fields))
+
+(define-configuration unbound-configuration
+  (server
+    (unbound-server
+      (unbound-server
+        (interface '("127.0.0.1" "::1"))
+
+        (hide-version #t)
+        (hide-identity #t)
+
+        (tls-cert-bundle "/etc/ssl/certs/ca-certificates.crt")))
+    "General options for the Unbound server.")
+
+  (remote-control
+    (unbound-remote
+      (unbound-remote
+        (control-enable #t)
+        (control-interface "/run/unbound.sock")))
+    "Remote control options for the daemon.")
+
+  (forward-zone
+    (list-of-unbound-zone '())
+    "A zone for which queries should be forwarded to another resolver.")
+
+  (extra-content
+    maybe-string
+    "Raw content to add to the configuration file.")
+
+  (prefix unbound-))
+
+(define (unbound-config-file config)
+  (mixed-text-file "unbound.conf"
+    (serialize-configuration
+      config
+      unbound-configuration-fields)))
+
+(define (unbound-shepherd-service config)
+  (let ((config-file (unbound-config-file config)))
+    (list (shepherd-service
+            (documentation "Unbound daemon.")
+            (provision '(unbound dns))
+            (requirement '(networking))
+            (actions (list (shepherd-configuration-action config-file)))
+            (start #~(make-forkexec-constructor
+                       (list (string-append #$unbound "/sbin/unbound")
+                             "-d" "-p" "-c" #$config-file)))
+            (stop #~(make-kill-destructor))))))
+
+(define unbound-account-service
+  (list (user-group (name "unbound") (system? #t))
+        (user-account
+         (name "unbound")
+         (group "unbound")
+         (system? #t)
+         (comment "Unbound daemon user")
+         (home-directory "/var/empty")
+         (shell "/run/current-system/profile/sbin/nologin"))))
+
+(define unbound-service-type
+  (service-type (name 'unbound)
+                (description "Run the unbound DNS resolver.")
+                (extensions
+                  (list (service-extension account-service-type
+                                           (const unbound-account-service))
+                        (service-extension shepherd-root-service-type
+                                           unbound-shepherd-service)))
+                (compose concatenate)
+                (default-value (unbound-configuration))))




Information forwarded to guix-patches@HIDDEN:
bug#68757; Package guix-patches. Full text available.

Message received at 68757 <at> debbugs.gnu.org:


Received: (at 68757) by debbugs.gnu.org; 27 Feb 2024 10:20:23 +0000
From debbugs-submit-bounces <at> debbugs.gnu.org Tue Feb 27 05:20:23 2024
Received: from localhost ([127.0.0.1]:40869 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1reuZS-0008Eo-Ma
	for submit <at> debbugs.gnu.org; Tue, 27 Feb 2024 05:20:23 -0500
Received: from eggs.gnu.org ([209.51.188.92]:38074)
 by debbugs.gnu.org with esmtp (Exim 4.84_2)
 (envelope-from <ludo@HIDDEN>) id 1reuZP-0008E3-PW
 for 68757 <at> debbugs.gnu.org; Tue, 27 Feb 2024 05:20:20 -0500
Received: from fencepost.gnu.org ([2001:470:142:3::e])
 by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.90_1) (envelope-from <ludo@HIDDEN>)
 id 1reuUB-000216-07; Tue, 27 Feb 2024 05:14:55 -0500
DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnu.org;
 s=fencepost-gnu-org; h=MIME-Version:Date:References:In-Reply-To:Subject:To:
 From; bh=QUmG8fBcK6BapkPkESbMG8VA09Rm4GB7JmoI/tztwcE=; b=NHviorbSYSPxfGDVksf0
 mfGlBcXehZWsQbgsabobvm+QQj+R0rQ8MmpfoSE1fLZmqbf56Y/2Vu4aTuQ5RmRRCNT/iwK5BI7Z9
 zxThStBcAJLFUyqdxni9fgEdcbKAUWl3cd+EBhDqKFdZdtLHFbr7gjDhN0p2L9rb7pOFa4c52l/OM
 a1190XsoUDknYwJLGb9PbjRtoifMOO60VcuWA1seCc5ahkXsMgjYL5xZt8RHCAn+O8Nr++0pH+sme
 a0yHFYLRU5oSz5NyswvP7WyFXqbTSiF+cJI/854t35yiTYyPSXloBG5PeuLzzTFmQDMmEVJFLVm8G
 azBV8ATIRGHeBw==;
From: =?utf-8?Q?Ludovic_Court=C3=A8s?= <ludo@HIDDEN>
To: =?utf-8?Q?S=C3=B6ren?= Tempel <soeren@HIDDEN>
Subject: Re: [bug#68757] [PATCH] services: dns: Add unbound service
In-Reply-To: <2O0HFY6AW6QUG.320OU5YPLJHHZ@HIDDEN> (=?utf-8?Q?=22S=C3=B6r?=
 =?utf-8?Q?en?= Tempel"'s message
 of "Sat, 24 Feb 2024 19:45:44 +0100")
References: <20240127121040.7156-2-soeren@HIDDEN>
 <87sf1pls1y.fsf@HIDDEN> <2O0HFY6AW6QUG.320OU5YPLJHHZ@HIDDEN>
Date: Tue, 27 Feb 2024 11:14:51 +0100
Message-ID: <87frxei57o.fsf@HIDDEN>
User-Agent: Gnus/5.13 (Gnus v5.13)
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable
X-Spam-Score: -2.3 (--)
X-Debbugs-Envelope-To: 68757
Cc: 68757 <at> debbugs.gnu.org
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: -3.3 (---)

Hi,

S=C3=B6ren Tempel <soeren@HIDDEN> skribis:

> Prior to submitting this patch I was experimenting with both records and
> alists for the Unbound configuration abstraction. Unbound has **a lot**
> of configuration options and new options are constantly getting added by
> upstream, see unbound.conf(5). Therefore, supporting them through a
> record type with fields for each configuration option requires a lot of
> code. Furthermore, it will require constant maintenance to keep up with
> new upstream options.

Right.

> I looked at prior art and noticed that the Nix service configuration for
> unbound just uses a plain hash with string keys [1]. This seemed like a
> good way to deal with the complexity of unbound.conf, hence I opted for
> a similar approach here. I don't think it's feasible to model the
> configuration using a record type with several hundred fields and, as rde
> uses an alist-based approach for services with similar complexity, I
> don't think its unheard of in the Guix world either. While it is not as
> =E2=80=9Ctype safe=E2=80=9D as a record-based approach (e.g. you can crea=
te semantically
> invalid unbound configurations), it offers good forwards compatibility
> and requires less Scheme code.
>
> In theory, it would be possible to model sections with less options
> (e.g. the =E2=80=98remote-control=E2=80=99 or =E2=80=98server=E2=80=99 op=
tion) using records. However,
> using alists for some sections and records for others seems inconsistent
> to me.
>
> Please let me know what you think so I can revise this accordingly.

The usual approach for services in Guix is to have a record for the most
common options (or for all the options if that doing so can be
automated, as was done with Dovecot) and an =E2=80=9Cescape hatch=E2=80=9D =
that lets
users insert raw config text.  Key/value alists are not a common idiom.

I would suggest sticking to this model as much as possible.  Perhaps
key/value alists would be preferable as an escape hatch than raw
strings?

Now, I don=E2=80=99t use Unbound, so I can only give general advice based on
what=E2=80=99s usually done in Guix.  Maybe =E2=80=98knot-service-type=E2=
=80=99 is a useful
source of inspiration.

HTH!

Ludo=E2=80=99.




Information forwarded to guix-patches@HIDDEN:
bug#68757; Package guix-patches. Full text available.

Message received at 68757 <at> debbugs.gnu.org:


Received: (at 68757) by debbugs.gnu.org; 24 Feb 2024 18:55:21 +0000
From debbugs-submit-bounces <at> debbugs.gnu.org Sat Feb 24 13:55:21 2024
Received: from localhost ([127.0.0.1]:49410 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1rdxBA-00063O-Gc
	for submit <at> debbugs.gnu.org; Sat, 24 Feb 2024 13:55:21 -0500
Received: from magnesium.8pit.net ([45.76.88.171]:29853)
 by debbugs.gnu.org with esmtp (Exim 4.84_2)
 (envelope-from <soeren@HIDDEN>) id 1rdx3k-0005b2-9s
 for 68757 <at> debbugs.gnu.org; Sat, 24 Feb 2024 13:47:41 -0500
DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; s=opensmtpd; bh=dQTLg7AEcG
 IE+/5QD0SE0dLNR6G6HA+tLofetNuF/8s=;
 h=in-reply-to:references:from:
 subject:cc:to:date; d=soeren-tempel.net; b=V+R0CbJ4mOACpqyy+KBrpl0RHW/
 CDJK+18tVB3ItD8jH18LrhcoqfJvYoEyL/s3eXP5Hhh1USq2UOH8S/7zO3pFxFTjYha3s9
 HytUtJwrxe28H301AKyu+BEWnGJ1A+Abp4Oiav+WjB/hN8/gV1s411G6n2l1fNffBwkXsn
 JmEs=
Received: from localhost
 (dynamic-2a02-3102-49da-001b-acdb-b735-16a2-ee83.310.pool.telefonica.de
 [2a02:3102:49da:1b:acdb:b735:16a2:ee83])
 by magnesium.8pit.net (OpenSMTPD) with ESMTPSA id 430aaae5
 (TLSv1.3:TLS_AES_256_GCM_SHA384:256:YES); 
 Sat, 24 Feb 2024 19:47:14 +0100 (CET)
Date: Sat, 24 Feb 2024 19:45:44 +0100
To: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
Subject: Re: [bug#68757] [PATCH] services: dns: Add unbound service
From: =?UTF-8?Q?S=C3=B6ren?= Tempel <soeren@HIDDEN>
References: <20240127121040.7156-2-soeren@HIDDEN>
 <87sf1pls1y.fsf@HIDDEN>
In-Reply-To: <87sf1pls1y.fsf@HIDDEN>
Message-Id: <2O0HFY6AW6QUG.320OU5YPLJHHZ@HIDDEN>
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
X-Spam-Score: -0.0 (/)
X-Debbugs-Envelope-To: 68757
Cc: 68757 <at> debbugs.gnu.org
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: -1.0 (-)

Ludovic Court=C3=A8s <ludo@HIDDEN> wrote:
> Hi S=C3=B6ren,

Hi Ludovic,

> For =E2=80=98remote-control=E2=80=99 and =E2=80=98server=E2=80=99, it=
=E2=80=99s not clear to me why we resort to
> alists instead of records (or fields within this record type); it looks
> inconsistent.
>=20
> Could you consider turning them into records or fields?

Prior to submitting this patch I was experimenting with both records and
alists for the Unbound configuration abstraction. Unbound has **a lot**
of configuration options and new options are constantly getting added by
upstream, see unbound.conf(5). Therefore, supporting them through a
record type with fields for each configuration option requires a lot of
code. Furthermore, it will require constant maintenance to keep up with
new upstream options.

I looked at prior art and noticed that the Nix service configuration for
unbound just uses a plain hash with string keys [1]. This seemed like a
good way to deal with the complexity of unbound.conf, hence I opted for
a similar approach here. I don't think it's feasible to model the
configuration using a record type with several hundred fields and, as rde
uses an alist-based approach for services with similar complexity, I
don't think its unheard of in the Guix world either. While it is not as
=E2=80=9Ctype safe=E2=80=9D as a record-based approach (e.g. you can create=
 semantically
invalid unbound configurations), it offers good forwards compatibility
and requires less Scheme code.

In theory, it would be possible to model sections with less options
(e.g. the =E2=80=98remote-control=E2=80=99 or =E2=80=98server=E2=80=99 opti=
on) using records. However,
using alists for some sections and records for others seems inconsistent
to me.

Please let me know what you think so I can revise this accordingly.

> I recommend adding an =E2=80=9Cescape hatch=E2=80=9D by which users may p=
rovide raw
> strings (or a file-like object) that gets inserted into the config file.

I think at the moment, it should be possible to express all possible
unbound configurations using the alist-based approach. If not, I would
consider it this a bug in the Scheme abstraction. As such, I don't think
there is a need for an =E2=80=9Cescape hatch=E2=80=9D right now (see also: =
my comment on
records and forwards compatibility above). However, if this is a common
idiom then I can add such an escape hatch.

The other things you mentioned seem obvious to me and I will just
implement them as suggested in a v2 revision of the patch. Thanks for
the feedback!

Greetings,
S=C3=B6ren

[1]: https://github.com/NixOS/nixpkgs/blob/0a37316d6cfea44280f4470b6867a711=
a24606bd/nixos/modules/services/networking/unbound.nix#L102-L126




Information forwarded to guix-patches@HIDDEN:
bug#68757; Package guix-patches. Full text available.

Message received at 68757 <at> debbugs.gnu.org:


Received: (at 68757) by debbugs.gnu.org; 18 Feb 2024 15:18:48 +0000
From debbugs-submit-bounces <at> debbugs.gnu.org Sun Feb 18 10:18:48 2024
Received: from localhost ([127.0.0.1]:36360 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1rbiwJ-00028d-Ir
	for submit <at> debbugs.gnu.org; Sun, 18 Feb 2024 10:18:47 -0500
Received: from eggs.gnu.org ([209.51.188.92]:48346)
 by debbugs.gnu.org with esmtp (Exim 4.84_2)
 (envelope-from <ludo@HIDDEN>) id 1rbiwH-00028Q-Ii
 for 68757 <at> debbugs.gnu.org; Sun, 18 Feb 2024 10:18:46 -0500
Received: from fencepost.gnu.org ([2001:470:142:3::e])
 by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.90_1) (envelope-from <ludo@HIDDEN>)
 id 1rbivr-0005gs-JW; Sun, 18 Feb 2024 10:18:19 -0500
DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnu.org;
 s=fencepost-gnu-org; h=MIME-Version:Date:References:In-Reply-To:Subject:To:
 From; bh=gAULCc+gN+gKNtugM8g01yYmdYqcBbPVLt7h6mavyeE=; b=Xr4ajFcbm+Il1ZHTwc9k
 vI+a7wk8Da7rXbgLOYQvU/IdZhndeB791k7p6o2S+xn3dO61IQrkIyI8uNo/lnZYzpK8ew/McHT8O
 Ugnl8W24NkwwDBbqq3DgxGZ0LIx/UakjItRi3U/S1Tzy3jlKPYK3xtkb7NHCwOyMnTWwm4Ijo+VE3
 CNbJj2wa5Fht7rlS4TLRQ5s9fwuKHypxm1osuy4osQDuGpNQH4QB3H4NQ1IUUS+WzSnkxpfqpFDsv
 bd7BjLRX3kZXm9itovqE3E6AMTYOK3xkJD0ocQze//S+Y1ZtT1u8rrNIMlw/XOY8cnOnYdZCxOtPK
 HhRuNcxkSVYPhw==;
From: =?utf-8?Q?Ludovic_Court=C3=A8s?= <ludo@HIDDEN>
To: soeren@HIDDEN
Subject: Re: [bug#68757] [PATCH] services: dns: Add unbound service
In-Reply-To: <20240127121040.7156-2-soeren@HIDDEN>
 (soeren@HIDDEN's message of "Sat, 27 Jan 2024 13:10:41
 +0100")
References: <20240127121040.7156-2-soeren@HIDDEN>
Date: Sun, 18 Feb 2024 16:18:17 +0100
Message-ID: <87sf1pls1y.fsf@HIDDEN>
User-Agent: Gnus/5.13 (Gnus v5.13)
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable
X-Spam-Score: -4.2 (----)
X-Debbugs-Envelope-To: 68757
Cc: 68757 <at> debbugs.gnu.org
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: -5.2 (-----)

Hi S=C3=B6ren,

soeren@HIDDEN skribis:

> From: S=C3=B6ren Tempel <soeren@HIDDEN>
>
> This allows using Unbound as a local DNSSEC-enabled resolver. This
> commit also allows configuration of the Unbound DNS resolver via a
> Scheme API. Conceptually, the Unbound configuration consists of several
> "sections" that contain key-value pairs (see unbound.conf(5)). The
> configuration sections are modeled in Scheme using record-type fields,
> where each field expects a list of pairs.
>
> A sample configuration, which uses a DoT forwarder, looks as follows:
>
> 	(service unbound-service-type
> 	  (unbound-configuration
> 	    (forward-zone
> 	      '((name . ".")
> 	        (forward-addr . "149.112.112.112#dns.quad9.net")
> 	        (forward-addr . "2620:fe::9#dns.quad9.net")
> 	        (forward-tls-upstream . yes)))))
>
> * gnu/service/dns.scm (serialize-list): New procedure.
> * gnu/service/dns.scm (unbound-configuration): New record.
> * gnu/service/dns.scm (unbound-config-file): New procedure.
> * gnu/service/dns.scm (unbound-shepherd-service): New procedure.
> * gnu/service/dns.scm (unbound-account-service): New constant.
> * gnu/service/dns.scm (unbound-service-type): New services.
>
> Signed-off-by: S=C3=B6ren Tempel <soeren@HIDDEN>

Nice!

Some comments:

  =E2=80=A2 Please document the service in doc/guix.texi.  Make sure to inc=
lude
    an example like the one above in the introduction, with
    explanations (you take remove the example from the commit log
    though).

  =E2=80=A2 Unless it=E2=80=99s too hard, please provide a system test (the=
 service for
    knot lacks one for some reason, so there=E2=80=99s a precedent, but the
    general rule is that system services should always have associated
    tests.)

> +(define-configuration unbound-configuration

I recommend adding an =E2=80=9Cescape hatch=E2=80=9D by which users may pro=
vide raw
strings (or a file-like object) that gets inserted into the config file.

> +  (server
> +    (maybe-list '((interface . "127.0.0.1")
> +                  (interface . "::1")
> +
> +                  ;; TLS certificate bundle for DNS over TLS.
> +                  (tls-cert-bundle . "/etc/ssl/certs/ca-certificates.crt=
")
> +
> +                  (hide-identity . yes)
> +                  (hide-version . yes)))

Please use Scheme booleans #t and #f instead of 'yes and 'no.

> +    "The server section of the configuration.")
> +  (remote-control
> +    (maybe-list '((control-enable . yes)
> +                  (control-interface . "/run/unbound.sock")))
> +    "Configuration of the remote control facility.")

For =E2=80=98remote-control=E2=80=99 and =E2=80=98server=E2=80=99, it=E2=80=
=99s not clear to me why we resort to
alists instead of records (or fields within this record type); it looks
inconsistent.

Could you consider turning them into records or fields?

> +            (documentation "Unbound daemon.")

=E2=80=9CRun the Unbound DNS resolver=E2=80=9D maybe?

> +            (provision '(unbound dns))
> +            (requirement '(networking))

Add 'user-processes.  However, does it really need =E2=80=98networking=E2=
=80=99?  (See
<https://issues.guix.gnu.org/66306>.)

> +         (shell "/run/current-system/profile/sbin/nologin"))))

Rather (file-append =E2=80=A6) as is done in other services.

> +(define unbound-service-type
> +  (service-type (name 'unbound)
> +                (description "Run the unbound DNS resolver.")

s/unbound/Unbound/

TIA,
Ludo=E2=80=99.




Information forwarded to guix-patches@HIDDEN:
bug#68757; Package guix-patches. Full text available.

Message received at submit <at> debbugs.gnu.org:


Received: (at submit) by debbugs.gnu.org; 27 Jan 2024 12:12:06 +0000
From debbugs-submit-bounces <at> debbugs.gnu.org Sat Jan 27 07:12:06 2024
Received: from localhost ([127.0.0.1]:53592 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1rThXZ-0002uN-Md
	for submit <at> debbugs.gnu.org; Sat, 27 Jan 2024 07:12:06 -0500
Received: from lists.gnu.org ([2001:470:142::17]:59416)
 by debbugs.gnu.org with esmtp (Exim 4.84_2)
 (envelope-from <soeren@HIDDEN>) id 1rThXW-0002tk-5O
 for submit <at> debbugs.gnu.org; Sat, 27 Jan 2024 07:12:05 -0500
Received: from eggs.gnu.org ([2001:470:142:3::10])
 by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.90_1) (envelope-from <soeren@HIDDEN>)
 id 1rThXJ-0004tE-2b
 for guix-patches@HIDDEN; Sat, 27 Jan 2024 07:11:49 -0500
Received: from magnesium.8pit.net ([45.76.88.171])
 by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.90_1) (envelope-from <soeren@HIDDEN>)
 id 1rThXD-0007Wh-Jg; Sat, 27 Jan 2024 07:11:47 -0500
DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; s=opensmtpd; bh=bTZJRnalDt
 WWJSD9rIdKlZZ85mZ+7U9Fkn3soXPBmrE=; h=date:subject:to:from;
 d=soeren-tempel.net; b=OV1aSZDDpwKsjmfVmyEPYrDDAgbhebycDjCFrWZzS/kPG57
 QrKmIS+hEHW2NhNrK8qF5WgW7LlJ6cBP0SjZIAKxlxgCsf3A0l4ffRDZ56UU+rON6bvF5P
 R7mb4HespRO06k0QyhdjVtsevUmks8H3rLii7OwPstq54exKV4cUWc=
Received: from localhost
 (dynamic-2a02-3102-49da-001b-ba57-b46b-a3ed-689f.310.pool.telefonica.de
 [2a02:3102:49da:1b:ba57:b46b:a3ed:689f])
 by magnesium.8pit.net (OpenSMTPD) with ESMTPSA id b4d280ed
 (TLSv1.3:TLS_AES_256_GCM_SHA384:256:YES); 
 Sat, 27 Jan 2024 13:11:38 +0100 (CET)
From: soeren@HIDDEN
To: guix-patches@HIDDEN
Subject: [PATCH] services: dns: Add unbound service
Date: Sat, 27 Jan 2024 13:10:41 +0100
Message-ID: <20240127121040.7156-2-soeren@HIDDEN>
X-Mailer: git-send-email 2.43.0
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Received-SPF: pass client-ip=45.76.88.171;
 envelope-from=soeren@HIDDEN; helo=magnesium.8pit.net
X-Spam_score_int: -20
X-Spam_score: -2.1
X-Spam_bar: --
X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1,
 DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, SPF_HELO_NONE=0.001,
 SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no
X-Spam_action: no action
X-Spam-Score: 0.9 (/)
X-Debbugs-Envelope-To: submit
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: -0.1 (/)

From: Sören Tempel <soeren@HIDDEN>

This allows using Unbound as a local DNSSEC-enabled resolver. This
commit also allows configuration of the Unbound DNS resolver via a
Scheme API. Conceptually, the Unbound configuration consists of several
"sections" that contain key-value pairs (see unbound.conf(5)). The
configuration sections are modeled in Scheme using record-type fields,
where each field expects a list of pairs.

A sample configuration, which uses a DoT forwarder, looks as follows:

	(service unbound-service-type
	  (unbound-configuration
	    (forward-zone
	      '((name . ".")
	        (forward-addr . "149.112.112.112#dns.quad9.net")
	        (forward-addr . "2620:fe::9#dns.quad9.net")
	        (forward-tls-upstream . yes)))))

* gnu/service/dns.scm (serialize-list): New procedure.
* gnu/service/dns.scm (unbound-configuration): New record.
* gnu/service/dns.scm (unbound-config-file): New procedure.
* gnu/service/dns.scm (unbound-shepherd-service): New procedure.
* gnu/service/dns.scm (unbound-account-service): New constant.
* gnu/service/dns.scm (unbound-service-type): New services.

Signed-off-by: Sören Tempel <soeren@HIDDEN>
---
 gnu/services/dns.scm | 115 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 114 insertions(+), 1 deletion(-)

diff --git a/gnu/services/dns.scm b/gnu/services/dns.scm
index 6608046909..224a4d4c32 100644
--- a/gnu/services/dns.scm
+++ b/gnu/services/dns.scm
@@ -3,6 +3,7 @@
 ;;; Copyright © 2020 Pierre Langlois <pierre.langlois@HIDDEN>
 ;;; Copyright © 2021 Maxime Devos <maximedevos@HIDDEN>
 ;;; Copyright © 2022 Remco van 't Veer <remco@HIDDEN>
+;;; Copyright © 2024 Sören Tempel <soeren@HIDDEN>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -52,7 +53,19 @@ (define-module (gnu services dns)
             knot-resolver-configuration
 
             dnsmasq-service-type
-            dnsmasq-configuration))
+            dnsmasq-configuration
+
+            unbound-service-type
+            unbound-configuration
+            unbound-configuration?
+            unbound-configuration-server
+            unbound-configuration-remote-control
+            unbound-configuration-forward-zone
+            unbound-configuration-stub-zone
+            unbound-configuration-auth-zone
+            unbound-configuration-view
+            unbound-configuration-python
+            unbound-configuration-dynlib))
 
 ;;;
 ;;; Knot DNS.
@@ -897,3 +910,103 @@ (define dnsmasq-service-type
                              dnsmasq-activation)))
    (default-value (dnsmasq-configuration))
    (description "Run the dnsmasq DNS server.")))
+
+
+;;;
+;;; Unbound.
+;;;
+
+(define-maybe list)
+
+(define (serialize-list field-name lst)
+  ;; Ensure that strings within the unbound configuration
+  ;; are not enclosed in double quotes by the serialization.
+  (define (->string obj)
+    (if (string? obj)
+      obj
+      (object->string obj)))
+
+  #~(string-append
+      #$(string-append (symbol->string field-name) ":\n")
+      #$(apply string-append
+          (map
+            (lambda (pair)
+              (string-append "\t"
+                             (symbol->string (car pair))
+                             ": "
+                             (->string (cdr pair))
+                             "\n"))
+            lst))))
+
+(define-configuration unbound-configuration
+  (server
+    (maybe-list '((interface . "127.0.0.1")
+                  (interface . "::1")
+
+                  ;; TLS certificate bundle for DNS over TLS.
+                  (tls-cert-bundle . "/etc/ssl/certs/ca-certificates.crt")
+
+                  (hide-identity . yes)
+                  (hide-version . yes)))
+    "The server section of the configuration.")
+  (remote-control
+    (maybe-list '((control-enable . yes)
+                  (control-interface . "/run/unbound.sock")))
+    "Configuration of the remote control facility.")
+  (forward-zone
+    maybe-list
+    "Configuration of nameservers to forward queries to.")
+  (stub-zone
+    maybe-list
+    "Configuration of stub zones.")
+  (auth-zone
+    maybe-list
+    "Zones for which unbound should response as an authority server.")
+  (view
+    maybe-list
+    "Configuration of view clauses.")
+  (python
+    maybe-list
+    "Configuration of the Python module.")
+  (dynlib
+    maybe-list
+    "Dynamic library module configuration."))
+
+(define (unbound-config-file config)
+  (mixed-text-file "unbound.conf"
+    (serialize-configuration
+      config
+      unbound-configuration-fields)))
+
+(define (unbound-shepherd-service config)
+  (let ((config-file (unbound-config-file config)))
+    (list (shepherd-service
+            (documentation "Unbound daemon.")
+            (provision '(unbound dns))
+            (requirement '(networking))
+            (actions (list (shepherd-configuration-action config-file)))
+            (start #~(make-forkexec-constructor
+                       (list (string-append #$unbound "/sbin/unbound")
+                             "-d" "-p" "-c" #$config-file)))
+            (stop #~(make-kill-destructor))))))
+
+(define unbound-account-service
+  (list (user-group (name "unbound") (system? #t))
+        (user-account
+         (name "unbound")
+         (group "unbound")
+         (system? #t)
+         (comment "Unbound daemon user")
+         (home-directory "/var/empty")
+         (shell "/run/current-system/profile/sbin/nologin"))))
+
+(define unbound-service-type
+  (service-type (name 'unbound)
+                (description "Run the unbound DNS resolver.")
+                (extensions
+                  (list (service-extension account-service-type
+                                           (const unbound-account-service))
+                        (service-extension shepherd-root-service-type
+                                           unbound-shepherd-service)))
+                (compose concatenate)
+                (default-value (unbound-configuration))))




Acknowledgement sent to soeren@HIDDEN:
New bug report received and forwarded. Copy sent to guix-patches@HIDDEN. Full text available.
Report forwarded to guix-patches@HIDDEN:
bug#68757; Package guix-patches. Full text available.
Please note: This is a static page, with minimal formatting, updated once a day.
Click here to see this page with the latest information and nicer formatting.
Last modified: Sun, 12 Jan 2025 05:45:02 UTC

GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997 nCipher Corporation Ltd, 1994-97 Ian Jackson.