certbot.scm 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2016 Nikita <nikita@n0.is>
  3. ;;; Copyright © 2016 Sou Bunnbu <iyzsong@member.fsf.org>
  4. ;;; Copyright © 2017, 2018 Clément Lassieur <clement@lassieur.org>
  5. ;;; Copyright © 2019 Julien Lepiller <julien@lepiller.eu>
  6. ;;; Copyright © 2020 Jack Hill <jackhill@jackhill.us>
  7. ;;; Copyright © 2020 Tobias Geerinckx-Rice <me@tobias.gr>
  8. ;;; Copyright © 2021 Raghav Gururajan <rg@raghavgururajan.name>
  9. ;;;
  10. ;;; This file is part of GNU Guix.
  11. ;;;
  12. ;;; GNU Guix is free software; you can redistribute it and/or modify it
  13. ;;; under the terms of the GNU General Public License as published by
  14. ;;; the Free Software Foundation; either version 3 of the License, or (at
  15. ;;; your option) any later version.
  16. ;;;
  17. ;;; GNU Guix is distributed in the hope that it will be useful, but
  18. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  19. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  20. ;;; GNU General Public License for more details.
  21. ;;;
  22. ;;; You should have received a copy of the GNU General Public License
  23. ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
  24. (define-module (gnu services certbot)
  25. #:use-module (gnu services)
  26. #:use-module (gnu services base)
  27. #:use-module (gnu services shepherd)
  28. #:use-module (gnu services mcron)
  29. #:use-module (gnu services web)
  30. #:use-module (gnu system shadow)
  31. #:use-module (gnu packages tls)
  32. #:use-module (guix i18n)
  33. #:use-module (guix records)
  34. #:use-module (guix gexp)
  35. #:use-module (srfi srfi-1)
  36. #:use-module (ice-9 match)
  37. #:export (certbot-service-type
  38. certbot-configuration
  39. certbot-configuration?
  40. certificate-configuration))
  41. ;;; Commentary:
  42. ;;;
  43. ;;; Automatically obtaining TLS certificates from Let's Encrypt.
  44. ;;;
  45. ;;; Code:
  46. (define-record-type* <certificate-configuration>
  47. certificate-configuration make-certificate-configuration
  48. certificate-configuration?
  49. (name certificate-configuration-name
  50. (default #f))
  51. (domains certificate-configuration-domains
  52. (default '()))
  53. (challenge certificate-configuration-challenge
  54. (default #f))
  55. (csr certificate-configuration-csr
  56. (default #f))
  57. (authentication-hook certificate-authentication-hook
  58. (default #f))
  59. (cleanup-hook certificate-cleanup-hook
  60. (default #f))
  61. (deploy-hook certificate-configuration-deploy-hook
  62. (default #f)))
  63. (define-record-type* <certbot-configuration>
  64. certbot-configuration make-certbot-configuration
  65. certbot-configuration?
  66. (package certbot-configuration-package
  67. (default certbot))
  68. (webroot certbot-configuration-webroot
  69. (default "/var/www"))
  70. (certificates certbot-configuration-certificates
  71. (default '()))
  72. (email certbot-configuration-email
  73. (default #f))
  74. (server certbot-configuration-server
  75. (default #f))
  76. (rsa-key-size certbot-configuration-rsa-key-size
  77. (default #f))
  78. (default-location certbot-configuration-default-location
  79. (default
  80. (nginx-location-configuration
  81. (uri "/")
  82. (body
  83. (list "return 301 https://$host$request_uri;"))))))
  84. (define certbot-command
  85. (match-lambda
  86. (($ <certbot-configuration> package webroot certificates email
  87. server rsa-key-size default-location)
  88. (let* ((certbot (file-append package "/bin/certbot"))
  89. (rsa-key-size (and rsa-key-size (number->string rsa-key-size)))
  90. (commands
  91. (map
  92. (match-lambda
  93. (($ <certificate-configuration> custom-name domains challenge
  94. csr authentication-hook
  95. cleanup-hook deploy-hook)
  96. (let ((name (or custom-name (car domains))))
  97. (if challenge
  98. (append
  99. (list name certbot "certonly" "-n" "--agree-tos"
  100. "--manual"
  101. (string-append "--preferred-challenges=" challenge)
  102. "--cert-name" name
  103. "--manual-public-ip-logging-ok"
  104. "-d" (string-join domains ","))
  105. (if csr `("--csr" ,csr) '())
  106. (if email
  107. `("--email" ,email)
  108. '("--register-unsafely-without-email"))
  109. (if server `("--server" ,server) '())
  110. (if rsa-key-size `("--rsa-key-size" ,rsa-key-size) '())
  111. (if authentication-hook
  112. `("--manual-auth-hook" ,authentication-hook)
  113. '())
  114. (if cleanup-hook `("--manual-cleanup-hook" ,cleanup-hook) '())
  115. (if deploy-hook `("--deploy-hook" ,deploy-hook) '()))
  116. (append
  117. (list name certbot "certonly" "-n" "--agree-tos"
  118. "--webroot" "-w" webroot
  119. "--cert-name" name
  120. "-d" (string-join domains ","))
  121. (if csr `("--csr" ,csr) '())
  122. (if email
  123. `("--email" ,email)
  124. '("--register-unsafely-without-email"))
  125. (if server `("--server" ,server) '())
  126. (if rsa-key-size `("--rsa-key-size" ,rsa-key-size) '())
  127. (if deploy-hook `("--deploy-hook" ,deploy-hook) '()))))))
  128. certificates)))
  129. (program-file
  130. "certbot-command"
  131. #~(begin
  132. (use-modules (ice-9 match))
  133. (let ((code 0))
  134. (for-each
  135. (match-lambda
  136. ((name . command)
  137. (begin
  138. (format #t "Acquiring or renewing certificate: ~a~%" name)
  139. (set! code (or (apply system* command) code)))))
  140. '#$commands) code)))))))
  141. (define (certbot-renewal-jobs config)
  142. (list
  143. ;; Attempt to renew the certificates twice per day, at a random minute
  144. ;; within the hour. See https://eff-certbot.readthedocs.io/.
  145. #~(job '(next-minute-from (next-hour '(0 12)) (list (random 60)))
  146. #$(certbot-command config))))
  147. (define (certbot-activation config)
  148. (let* ((certbot-directory "/var/lib/certbot")
  149. (certbot-cert-directory "/etc/letsencrypt/live")
  150. (script (in-vicinity certbot-directory "renew-certificates"))
  151. (message (format #f (G_ "~a may need to be run~%") script)))
  152. (match config
  153. (($ <certbot-configuration> package webroot certificates email
  154. server rsa-key-size default-location)
  155. (with-imported-modules '((guix build utils))
  156. #~(begin
  157. (use-modules (guix build utils))
  158. (mkdir-p #$webroot)
  159. (mkdir-p #$certbot-directory)
  160. (mkdir-p #$certbot-cert-directory)
  161. (copy-file #$(certbot-command config) #$script)
  162. (display #$message)))))))
  163. (define certbot-nginx-server-configurations
  164. (match-lambda
  165. (($ <certbot-configuration> package webroot certificates email
  166. server rsa-key-size default-location)
  167. (define (certificate->nginx-server certificate-configuration)
  168. (match-record certificate-configuration <certificate-configuration>
  169. (domains challenge)
  170. (nginx-server-configuration
  171. (listen '("80" "[::]:80"))
  172. (ssl-certificate #f)
  173. (ssl-certificate-key #f)
  174. (server-name domains)
  175. (locations
  176. (filter identity
  177. (append
  178. (if challenge
  179. '()
  180. (list (nginx-location-configuration
  181. (uri "/.well-known")
  182. (body (list (list "root " webroot ";"))))))
  183. (list default-location)))))))
  184. (map certificate->nginx-server certificates))))
  185. (define certbot-service-type
  186. (service-type (name 'certbot)
  187. (extensions
  188. (list (service-extension nginx-service-type
  189. certbot-nginx-server-configurations)
  190. (service-extension activation-service-type
  191. certbot-activation)
  192. (service-extension mcron-service-type
  193. certbot-renewal-jobs)))
  194. (compose concatenate)
  195. (extend (lambda (config additional-certificates)
  196. (certbot-configuration
  197. (inherit config)
  198. (certificates
  199. (append
  200. (certbot-configuration-certificates config)
  201. additional-certificates)))))
  202. (description
  203. "Automatically renew @url{https://letsencrypt.org, Let's
  204. Encrypt} HTTPS certificates by adjusting the nginx web server configuration
  205. and periodically invoking @command{certbot}.")))