From df03ab95649efe2e2b3ee9ad8e31518206eb6a68 Mon Sep 17 00:00:00 2001
From: Luis Guilherme Coelho <lgcoelho@disroot.org>
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(-)
@@ -3,6 +3,7 @@
;;; Copyright © 2016-2023 Ludovic Courtès <ludo@gnu.org>
;;; Copyright © 2020 Brice Waegeneire <brice@waegenei.re>
;;; Copyright © 2023 Giacomo Leidi <goodoldpaul@autistici.org>
+;;; Copyright © 2023 Luis Guilherme Coelho <lgcoelho@disroot.org>
;;;
;;; 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