Subject: bug#68007: closed (closed in favor of a more feature complete service (opendoas-service-type) to be submitted)

From: [email-masked] (GNU bug Tracking System)
Date: Sat, 09 Nov 2024 10:49:02 +0000

Your bug report

#68007: [PATCH] services: Add doas service.

which was filed against the guix-patches package, has been closed.

The explanation is attached below, along with your original report.
If you require more details, please reply to [email-masked].

-- 
68007: https://debbugs.gnu.org/cgi/bugreport.cgi?bug=68007
GNU Bug Tracking System
Contact [email-masked] with problems

Subject: closed in favor of a more feature complete service (opendoas-service-type) to be submitted
From: [email-masked]
To: [email-masked]
Date: Sat, 09 Nov 2024 11:48:39 +0100
List-Id: <debbugs-submit.debbugs.gnu.org>

Subject: [PATCH] services: Add doas service.
From: [email-masked]
To: [email-masked]
Date: Sun, 24 Dec 2023 17:01:33 +0000
List-Id: <debbugs-submit.debbugs.gnu.org>

This service enables declarative description of doas.conf. A simple 
example would be

--8<---------------cut 
here-------------------------------------------------end--------------->8---
         (simple-service 'miscellaneous-permissions doas-service-type
                         (list (permit (identity ":wheel")
                                             (setenv `(("GUILE_LOAD_PATH" 
. #t))))
                                (permit (identity ":wheel")
                                             (nopass? #t)
                                             (command "guix")
                                             (args `("pull")))))

         (simple-service 'text-editors-permissions doas-service-type
                                  (map (lambda (cmd)
                                              (permit (identity ":wheel")
                                                          (keepenv? #t)
                                                          (command cmd)))
                                             `("kak" "emacsclient")))

         (simple-service 'power-management-permissions doas-service-type
                                  (map (lambda (cmd)
                                              (permit (identity ":wheel")
                                                           (nopass? #t)
                                                           (command cmd)
                                                           (args '())))
                                           `("zzz" "halt" "reboot")))

         (simple-service 'shepherd-status-permissions doas-service-type
                         (map (lambda (action)
                                    (permit (identity ":wheel")
                                                 (nopass? #t)
                                                 (command "herd")
                                                 (args (list action))))
                              `("status" "detailed-status")))

         (simple-service 'service-management-permissions 
doas-service-type
                         (flat-map (lambda (service action)
                                           (permit (identity ":wheel")
                                                        (nopass? #t)
                                                        (command "herd")
                                                        (args (map 
symbol->string
                                                                          
(list action service)))))
                                   '(tor networking wpa-supplicant)
                                   '(doc stop start enable status restart 
disable)))

--8<---------------cut 
here-------------------------------------------------end--------------->8---

This generates the following configuration file:

--8<---------------cut 
here-------------------------------------------------end--------------->8---

permit setenv { GUILE_LOAD_PATH }
permit nopass :wheel cmd guix args pull
permit keepenv :wheel cmd kak
permit keepenv :wheel cmd emacsclient
permit nopass :wheel cmd zzz args
permit nopass :wheel cmd halt args
permit nopass :wheel cmd reboot args
permit nopass :wheel cmd herd args status
permit nopass :wheel cmd herd args detailed-status
permit nopass :wheel cmd herd args doc tor
permit nopass :wheel cmd herd args stop tor
permit nopass :wheel cmd herd args start tor
permit nopass :wheel cmd herd args enable tor
permit nopass :wheel cmd herd args status tor
permit nopass :wheel cmd herd args restart tor
permit nopass :wheel cmd herd args disable tor
permit nopass :wheel cmd herd args doc networking
permit nopass :wheel cmd herd args stop networking
permit nopass :wheel cmd herd args start networking
permit nopass :wheel cmd herd args enable networking
permit nopass :wheel cmd herd args status networking
permit nopass :wheel cmd herd args restart networking
permit nopass :wheel cmd herd args disable networking
permit nopass :wheel cmd herd args doc wpa-supplicant
permit nopass :wheel cmd herd args stop wpa-supplicant
permit nopass :wheel cmd herd args start wpa-supplicant
permit nopass :wheel cmd herd args enable wpa-supplicant
permit nopass :wheel cmd herd args status wpa-supplicant
permit nopass :wheel cmd herd args restart wpa-supplicant
permit nopass :wheel cmd herd args disable wpa-supplicant

--8<---------------cut 
here-------------------------------------------------end--------------->8---

From df03ab95649efe2e2b3ee9ad8e31518206eb6a68 Mon Sep 17 00:00:00 2001
From: Luis Guilherme Coelho <[email-masked]>
Date: Sun, 24 Dec 2023 13:27:36 -0300
Subject: [PATCH] services: Add doas service.

---
 gnu/services/admin.scm | 174 ++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 173 insertions(+), 1 deletion(-)

diff --git a/gnu/services/admin.scm b/gnu/services/admin.scm
index 0b325fddb1..5bb598300e 100644
--- a/gnu/services/admin.scm
+++ b/gnu/services/admin.scm
@@ -3,6 +3,7 @@
 ;;; Copyright © 2016-2023 Ludovic Courtès <[email-masked]>
 ;;; Copyright © 2020 Brice Waegeneire <[email-masked]>
 ;;; Copyright © 2023 Giacomo Leidi <[email-masked]>
+;;; Copyright © 2023 Luis Guilherme Coelho <[email-masked]>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -37,6 +38,8 @@ (define-module (gnu services admin)
   #:use-module (guix packages)
   #:use-module (guix records)
   #:use-module (srfi srfi-1)
+  #:use-module (srfi srfi-26)
+  #:use-module (ice-9 format)
   #:use-module (ice-9 match)
   #:use-module (ice-9 vlist)
   #:export (%default-rotations
@@ -93,7 +96,29 @@ (define-module (gnu services admin)
             unattended-upgrade-configuration-services-to-restart
             unattended-upgrade-configuration-system-expiration
             unattended-upgrade-configuration-maximum-duration
-            unattended-upgrade-configuration-log-file))
+            unattended-upgrade-configuration-log-file
+
+            doas-service-type
+
+            permit
+            make-permit-statement
+            permit-statement?
+            permit-statement-args
+            permit-statement-as-user
+            permit-statement-command
+            permit-statement-identity
+            permit-statement-keepenv?
+            permit-statement-nolog?
+            permit-statement-nopass?
+            permit-statement-persist?
+            permit-statement-setenv
+
+            deny
+            make-deny-statement
+            deny-statement?
+            deny-statement-args
+            deny-statement-as-user
+            deny-statement-command))
 
 ;;; Commentary:
 ;;;
@@ -537,4 +562,151 @@ (define unattended-upgrade-service-type
     "Periodically upgrade the system from the current configuration.")
    (default-value (unattended-upgrade-configuration))))
 
+
+;;;
+;;; Doas configuration.
+;;;
+
+;; Dummy serializers, just to avoid warnings
+(define empty-serializer
+  (@@ (gnu services configuration) empty-serializer))
+(define serialize-string empty-serializer)
+(define serialize-list-of-strings empty-serializer)
+
+(define assoc-list? (list-of pair?))
+(define (serialize-assoc-list field-name val)
+  (map (match-lambda
+         ((var . #t) var)
+         ((var . #f) (string-append "-" var))
+         ((var . value) (format #f "~a=~a" var value)))
+       val))
+(define-maybe list-of-strings)
+(define-maybe assoc-list)
+(define-maybe string)
+
+(define-configuration/no-serialization permit-statement
+  (nopass?
+   (boolean #f)
+   "Whether the user should be permitted to run the command without a password.")
+  (nolog?
+   (boolean #f)
+   "Wheter sucessful command exection should be logged.")
+  (persist?
+   (boolean #f)
+   "After the user sucessfully authenticates, do not ask for a password again
+for some time.")
+  (keepenv?
+   (boolean #f)
+   "Wheter environment variables other than those listed in doas should be
+retained when creating the enviroment for the new process.")
+  (identity
+   string
+   "The username to match. Groups may be specified by prepending a colon ':'.")
+  (as-user
+   maybe-string
+   "The target user the running user is allowed to run the command as.  The
+default is all users.")
+  (command
+   maybe-string
+   "The command the user is allowed to run.  The default is all commands.
+It's preferable to have commands specifieds by absolute paths. If a relative
+path is specified, only a restricted PATH will be searched.")
+  (args
+   maybe-list-of-strings
+   "Arguments to command.  The command arguments provided by the user need to
+match those specified.  The keyword args alone means that command must be run
+without arguments.")
+  (setenv
+   maybe-assoc-list
+   "Set the specified variables.  Variables may also be removed by setting them
+to #f, or simply exported, by setting them to #t.  If the first character of the
+value is ‘$’ then the value to be set is taken from the existing environment
+variable with the given name."))
+(define-syntax-rule (permit entry ...)
+  (permit-statement entry ...))
+
+(define (unset? val)
+  "Tests if VAL is unset."
+  (equal? val (@@ (gnu services configuration)
+                  %unset-value)))
+
+(define* (if-set val #:optional (proc identity))
+  "Apply PROC to VAL if VAL is not unset, otherwise returns #f."
+  (if (not (unset? val)) (proc val) #f))
+
+(define serialize-permit-statement
+  (match-record-lambda <permit-statement>
+    (identity as-user command args setenv keepenv? nopass? nolog? persist?)
+    (format #f "permit ~:[~;keepenv ~]~
+                       ~:[~;nopass ~]~
+                       ~:[~;nolog ~]~
+                       ~:[~;persist ~]~
+                       ~@[setenv {~{ ~a~} } ~]~
+                       ~a~@[ as ~a~]~
+                         ~@[ cmd ~a~]~
+                         ~@[ args~{ ~a~}~]~%"
+            keepenv?
+            nopass?
+            nolog?
+            persist?
+            (if-set setenv (cut serialize-assoc-list #f <>))
+            identity
+            (if-set as-user)
+            (if-set command)
+            (if-set args))))
+
+(define-configuration/no-serialization deny-statement
+  (identity
+   string
+   "The username to match. Groups may be specified by prepending a colon ':'.")
+  (as-user
+   maybe-string
+   "The target user the running user is allowed to run the command as.  The
+default is all users.")
+  (command
+   maybe-string
+   "The command the user is allowed to run.  The default is all commands.
+It's preferable to have commands specifieds by absolute paths. If a relative
+path is specified, only a restricted PATH will be searched.")
+  (args
+   maybe-string
+   "Arguments to command.  The command arguments provided by the user need to
+match those specified.  The keyword args alone means that command must be run
+without arguments."))
+(define-syntax-rule (deny entry ...)
+  (deny-statement entry ...))
+
+(define serialize-deny-statement
+  (match-record-lambda <deny-statement>
+    (identity as-user command args)
+    (format #f "deny ~a~@[ as ~a~]~@[ cmd ~a~]~@[ args~{ ~a~}~]~%"
+            identity
+            (if-set as-user)
+            (if-set command)
+            (if-set args))))
+
+(define (doas-config-file config)
+  (plain-file "doas.conf"
+    (apply string-append
+           (map (lambda (s)
+                  (cond ((permit-statement? s)
+                         (serialize-permit-statement s))
+                        ((deny-statement?   s)
+                         (serialize-deny-statement s))))
+                config))))
+
+(define (doas-etc-service config)
+  `(("doas.conf" ,(doas-config-file config))))
+
+(define doas-service-type
+  (service-type (name 'doas-service)
+                (extensions
+                 (list (service-extension
+                         etc-service-type
+                         doas-etc-service)))
+                (compose (compose concatenate reverse))
+                (extend append)
+                (default-value '())
+                (description "Set /etc/doas.conf")))
+
 ;;; admin.scm ends here
-- 
2.41.0