From df03ab95649efe2e2b3ee9ad8e31518206eb6a68 Mon Sep 17 00:00:00 2001 From: Luis Guilherme Coelho 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 ;;; Copyright © 2020 Brice Waegeneire ;;; Copyright © 2023 Giacomo Leidi +;;; Copyright © 2023 Luis Guilherme Coelho ;;; ;;; 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 + (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 + (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