Without a certificate nginx doesn’t start, and without nginx the standard service doesn’t work. To use this custom service, nginx should not listen on port 80.
100 lines
4.1 KiB
Scheme
100 lines
4.1 KiB
Scheme
(define-module (services certbot)
|
|
#:use-module (gnu services)
|
|
#:use-module (gnu services base)
|
|
#:use-module (gnu services shepherd)
|
|
#:use-module (gnu services mcron)
|
|
#:use-module (gnu packages tls)
|
|
#:use-module (guix i18n)
|
|
#:use-module (guix records)
|
|
#:use-module (guix gexp)
|
|
#:use-module (srfi srfi-1)
|
|
#:use-module (ice-9 match)
|
|
#:export (certbot-service-type
|
|
certbot-configuration
|
|
certbot-configuration?
|
|
certificate-configuration))
|
|
|
|
;; Standalone certbot service.
|
|
|
|
(define-record-type* <certificate-configuration>
|
|
certificate-configuration make-certificate-configuration
|
|
certificate-configuration?
|
|
(name certificate-configuration-name (default #f))
|
|
(domains certificate-configuration-domains (default '()))
|
|
(deploy-hook certificate-configuration-deploy-hook (default #f)))
|
|
|
|
(define-record-type* <certbot-configuration>
|
|
certbot-configuration make-certbot-configuration
|
|
certbot-configuration?
|
|
(package certbot-configuration-package (default certbot))
|
|
(certificates certbot-configuration-certificates (default '())))
|
|
|
|
(define certbot-command
|
|
(match-lambda
|
|
(($ <certbot-configuration> package certificates)
|
|
(let* ((certbot (file-append package "/bin/certbot"))
|
|
(commands
|
|
(map
|
|
(match-lambda
|
|
(($ <certificate-configuration> custom-name domains deploy-hook)
|
|
(let ((name (or custom-name (car domains))))
|
|
(append
|
|
(list name certbot "certonly" "-n" "--agree-tos"
|
|
"--register-unsafely-without-email"
|
|
"--standalone" "--cert-name" name
|
|
"-d" (string-join domains ","))
|
|
(if deploy-hook `("--deploy-hook" ,deploy-hook) '())))))
|
|
certificates)))
|
|
(program-file
|
|
"certbot-command"
|
|
#~(begin
|
|
(use-modules (ice-9 match))
|
|
(apply max 0
|
|
(map (match-lambda
|
|
((name . command)
|
|
(begin
|
|
(format #t "Acquiring or renewing certificate: ~a~%" name)
|
|
(apply system* command))))
|
|
'#$commands))))))))
|
|
|
|
(define (certbot-renewal-jobs config)
|
|
(list
|
|
;; Attempt to renew the certificates twice per day, at a random minute
|
|
;; within the hour. See https://certbot.eff.org/all-instructions/.
|
|
#~(job '(next-minute-from (next-hour '(0 12)) (list (random 60)))
|
|
#$(certbot-command config))))
|
|
|
|
(define (certbot-activation config)
|
|
(let* ((certbot-directory "/var/lib/certbot")
|
|
(certbot-cert-directory "/etc/letsencrypt/live")
|
|
(script (in-vicinity certbot-directory "renew-certificates"))
|
|
(message (format #f (G_ "~a may need to be run~%") script)))
|
|
(match config
|
|
(($ <certbot-configuration> package certificates)
|
|
(with-imported-modules '((guix build utils))
|
|
#~(begin
|
|
(use-modules (guix build utils))
|
|
(mkdir-p #$certbot-directory)
|
|
(mkdir-p #$certbot-cert-directory)
|
|
(copy-file #$(certbot-command config) #$script)
|
|
(display #$message)))))))
|
|
|
|
(define certbot-service-type
|
|
(service-type (name 'certbot)
|
|
(extensions
|
|
(list (service-extension activation-service-type
|
|
certbot-activation)
|
|
(service-extension mcron-service-type
|
|
certbot-renewal-jobs)))
|
|
(compose concatenate)
|
|
(extend (lambda (config additional-certificates)
|
|
(certbot-configuration
|
|
(inherit config)
|
|
(certificates
|
|
(append
|
|
(certbot-configuration-certificates config)
|
|
additional-certificates)))))
|
|
(description
|
|
"Automatically renew @url{https://letsencrypt.org, Let's
|
|
Encrypt} HTTPS certificates by adjusting the nginx web server configuration
|
|
and periodically invoking @command{certbot}.")))
|