certbot.scm 8.8 KB

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