From 6cb389e193d6802a5ec541b8aad87ef74cc35854 Mon Sep 17 00:00:00 2001 From: "W. Kosior" Date: Fri, 3 May 2024 14:13:34 +0200 Subject: services: certbot: Facilitate granting key read access to groups. * gnu/services/certbot.scm (certificate-configuration)[key-read-group]: New field. (certbot-deploy-hook): Ensure requested group has the right access. (certbot-command): Pass the requested group to `certbot-deploy-hook'. (set-key-access-gexp): New procedure. (generate-certificate-gexp) Ensure the requested group has the right access. Change-Id: Ia46454a7d2b042cfb682d1d8a7e04aebbc9c19da --- gnu/services/certbot.scm | 104 +++++++++++++++++++++++++++++------------------ 1 file changed, 64 insertions(+), 40 deletions(-) (limited to 'gnu') diff --git a/gnu/services/certbot.scm b/gnu/services/certbot.scm index c79cf84391..27f40033d3 100644 --- a/gnu/services/certbot.scm +++ b/gnu/services/certbot.scm @@ -7,6 +7,9 @@ ;;; Copyright © 2020 Tobias Geerinckx-Rice ;;; Copyright © 2021 Raghav Gururajan ;;; Copyright © 2024 Carlo Zancanaro +;;; Copyright © 2024 W. Kosior +;;; Additions and modifications by W. Kosior are additionally +;;; dual-licensed under the Creative Commons Zero v1.0. ;;; ;;; This file is part of GNU Guix. ;;; @@ -68,7 +71,9 @@ (define-record-type* (deploy-hook certificate-configuration-deploy-hook (default #f)) (start-self-signed? certificate-configuration-start-self-signed? - (default #t))) + (default #t)) + (key-read-group certificate-configuration-key-read-group + (default #f))) (define-record-type* certbot-configuration make-certbot-configuration @@ -96,7 +101,8 @@ (define-record-type* (service-requirement certbot-configuration-service-requirement (default '(nginx)))) -(define (certbot-deploy-hook name deploy-hook-script reload-service-names) +(define (certbot-deploy-hook name deploy-hook-script reload-service-names + key-read-group) "Returns a gexp which creates symlinks for privkey.pem and fullchain.pem from /etc/certs/NAME to /etc/letsenctypt/live/NAME. If DEPLOY-HOOK-SCRIPT is not #f then it is run after the symlinks have been created. This wrapping is @@ -110,6 +116,10 @@ (define (certbot-deploy-hook name deploy-hook-script reload-service-names) #~(begin (use-modules (gnu services herd) (guix build utils)) + #$(set-key-access-gexp + (string-append "/etc/letsencrypt/live/" name "/privkey.pem") + key-read-group) + (mkdir-p #$(string-append "/etc/certs/" name)) (chmod #$(string-append "/etc/certs/" name) #o755) @@ -150,7 +160,9 @@ (define certbot-command (match-lambda (($ custom-name domains challenge csr authentication-hook - cleanup-hook deploy-hook) + cleanup-hook deploy-hook + start-self-signed? + key-read-group) (let ((name (or custom-name (car domains)))) (if challenge (append @@ -172,7 +184,8 @@ (define certbot-command (if cleanup-hook `("--manual-cleanup-hook" ,cleanup-hook) '()) (list "--deploy-hook" (certbot-deploy-hook name deploy-hook - service-reload))) + service-reload + key-read-group))) (append (list name certbot "certonly" "-n" "--agree-tos" "--webroot" "-w" webroot @@ -186,7 +199,8 @@ (define certbot-command (if rsa-key-size `("--rsa-key-size" ,rsa-key-size) '()) (list "--deploy-hook" (certbot-deploy-hook name deploy-hook - service-reload))))))) + service-reload + key-read-group))))))) certificates))) (program-file "certbot-command" @@ -272,46 +286,56 @@ (define (certbot-renewal-one-shot config) (documentation "Call certbot to renew certificates.") (actions (list (shepherd-configuration-action (certbot-command config))))))) +(define (set-key-access-gexp keyfile key-read-group) + #~(let ((gid (or (and=> #$key-read-group (compose group:gid getgr)) + 0))) + (chown #$keyfile -1 gid) + (chmod #$keyfile #$(if key-read-group #o640 #o600)))) + (define (generate-certificate-gexp certbot-cert-directory rsa-key-size) (match-lambda (($ name (primary-domain other-domains ...) challenge csr authentication-hook - cleanup-hook deploy-hook) - (let (;; Arbitrary default subject, with just the - ;; right domain filled in. These values don't - ;; have any real significance. - (subject (string-append - "/C=US/ST=Oregon/L=Portland/O=Company Name/OU=Org/CN=" - primary-domain)) - (alt-names (if (null? other-domains) - #f - (format #f "subjectAltName=~{DNS:~a~^,~}" - other-domains))) - (directory (string-append "/etc/certs/" (or name primary-domain)))) - #~(when (not (file-exists? #$directory)) - ;; We generate self-signed certificates in /etc/certs/{domain}, - ;; because certbot is very sensitive to its directory - ;; structure. It refuses to write over the top of existing files, - ;; so we need to use a directory outside of its control. - ;; - ;; These certificates are overwritten by the certbot deploy hook - ;; the first time it successfully obtains a letsencrypt-signed - ;; certificate. - (mkdir-p #$directory) - (chmod #$directory #o755) - (invoke #$(file-append openssl "/bin/openssl") - "req" "-x509" - "-newkey" #$(string-append "rsa:" (or rsa-key-size "4096")) - "-keyout" #$(string-append directory "/privkey.pem") - "-out" #$(string-append directory "/fullchain.pem") - "-sha256" - "-days" "1" ; Only one day, because we expect certbot to run - "-nodes" - "-subj" #$subject - #$@(if alt-names - (list "-addext" alt-names) - (list)))))))) + cleanup-hook deploy-hook + start-self-signed? key-read-group) + (let* (;; Arbitrary default subject, with just the + ;; right domain filled in. These values don't + ;; have any real significance. + (subject (string-append + "/C=US/ST=Oregon/L=Portland/O=Company Name/OU=Org/CN=" + primary-domain)) + (alt-names (if (null? other-domains) + #f + (format #f "subjectAltName=~{DNS:~a~^,~}" + other-domains))) + (directory (string-append "/etc/certs/" (or name primary-domain))) + (keyfile (string-append directory "/privkey.pem"))) + #~(begin + (when (not (file-exists? #$directory)) + ;; We generate self-signed certificates in /etc/certs/{domain}, + ;; because certbot is very sensitive to its directory + ;; structure. It refuses to write over the top of existing files, + ;; so we need to use a directory outside of its control. + ;; + ;; These certificates are overwritten by the certbot deploy hook + ;; the first time it successfully obtains a letsencrypt-signed + ;; certificate. + (mkdir-p #$directory) + (chmod #$directory #o755) + (invoke #$(file-append openssl "/bin/openssl") + "req" "-x509" + "-newkey" #$(format #f "rsa:~a" (or rsa-key-size "4096")) + "-keyout" #$keyfile + "-out" #$(string-append directory "/fullchain.pem") + "-sha256" + "-days" "1" ; Only one day, we expect certbot to run + "-nodes" + "-subj" #$subject + #$@(if alt-names + (list "-addext" alt-names) + (list)))) + #$(set-key-access-gexp keyfile key-read-group)))))) (define (certbot-activation config) (let* ((certbot-directory "/var/lib/certbot") -- cgit v1.2.3