all messages for Guix-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
* Nginx and certbot cervices don't play well togther
@ 2021-03-06  8:15 Brice Waegeneire
  2024-01-24 12:18 ` bug#46961: [PATCH 0/2] Allow nginx to start before certbot has run Carlo Zancanaro
  0 siblings, 1 reply; 33+ messages in thread
From: Brice Waegeneire @ 2021-03-06  8:15 UTC (permalink / raw)
  To: bug-guix; +Cc: guix-devel


Hello Guix,

After an suggestion from Tobias to give a try at forcing HTTPS for
Guix's websites on berlin, I had a go at it but it was more complex that
what I was expecting.  Looking deeper at nginx and certbot services it
appear both services don't play that well together, requering a inital
dance when deploying a new HTTPS virtual server. As explained in #36389¹
you need to:
« - run system configuration with just the certbot service
- use certbot to generate your initial certificates
- reconfigure with additional nginx server configuration, pointing to
  the SSL certificates created by certbot »

Indeed, with an operating-system continaing the following services it's
impossible to sart Nginx and Certbot at once as one would expect:

--8<---------------cut here---------------start------------->8---
(service nginx-service-type)
(service php-fpm-service-type)

(service certbot-service-type
         (certbot-configuration
           (certificates
             (list (certificate-configuration
                     (domains '("test.sama.re"))
                     (deploy-hook
                       (program-file
                         "nginx-deploy-hook"
                         #~(let ((pid (call-with-input-file "/var/run/nginx.pid"
                                                            read)))
                             (kill pid SIGHUP)))))))))

(cat-avatar-generator-service
  #:configuration
  (nginx-server-configuration
    (listen '("443 ssl"))
    (server-name '("test.sama.re"))
    (ssl-certificate
      "/etc/letsencrypt/live/test.sama.re/fullchain.pem")
    (ssl-certificate-key
      "/etc/letsencrypt/live/test.sama.re/privkey.pem")))
--8<---------------cut here---------------end--------------->8---

Here is the error from reconfiguring the system:

--8<---------------cut here---------------start------------->8---
# guix system reconfigure /etc/config.sm
[...]
building /gnu/store/55cq2ja4i5489s55viv9fh50032d1ziy-switch-to-system.scm.drv...
making '/gnu/store/p2rkcmrnpls5py7x2iappf2qcbxwlb95-system' the current system...
setting up setuid programs in '/run/setuid-programs'...
populating /etc from /gnu/store/k2kb8hsq3q0dhhad4a9pjh4kx32mn4g0-etc...
/var/lib/certbot/renew-certificates may need to be run
creating nginx log directory '/var/log/nginx'
creating nginx run directory '/var/run/nginx'
creating nginx temp directories '/var/run/nginx/{client_body,proxy,fastcgi,uwsgi,scgi}_temp'
nginx: [emerg] cannot load certificate "/etc/letsencrypt/live/test.sama.re/fullchain.pem": BIO_new_file() failed (SSL: error:02001002:system library:fopen:No such file or directory:fopen('/etc/letsencrypt/live/test.sama.re/fullchain.pem','r') error:2006D080:BIO routines:BIO_new_file:no such file)
nginx: configuration file /gnu/store/chpw631djay2w39x7agg8zz53iayy4zy-nginx.conf test failed
`/gnu/store/jyxc290q7jyhhpalski0h13h8z9zvnka-openssh-authorized-keys/bricewge' -> `/etc/ssh/authorized_keys.d/bricewge'
The following derivation will be built:
   /gnu/store/qlzbrmpx6wnhzqcpqi9yrbb6xva82kvr-install-bootloader.scm.drv

building /gnu/store/qlzbrmpx6wnhzqcpqi9yrbb6xva82kvr-install-bootloader.scm.drv...
guix system: bootloader successfully installed on '/dev/sda'
The following derivation will be built:
   /gnu/store/ikak44inrnz3b3dx8j8csdakgqafbijn-upgrade-shepherd-services.scm.drv

building /gnu/store/ikak44inrnz3b3dx8j8csdakgqafbijn-upgrade-shepherd-services.scm.drv...
shepherd: Removing service 'dbus-system'...
shepherd: Service dbus-system has been stopped.
shepherd: Done.
shepherd: Service host-name has been started.
shepherd: Service user-homes has been started.
shepherd: Service host-name has been started.
shepherd: Service term-auto could not be started.
shepherd: Service php-fpm has been started.
guix system: warning: exception caught while executing 'start' on service 'nginx':
Throw to key `%exception' with args `("#<&invoke-error program: \"/gnu/store/hn1mvgafkpf5knrnzvwpgpdlzmq553al-nginx-1.19.6/sbin/nginx\" arguments: (\"-c\" \"/gnu/store/chpw631djay2w39x7agg8zz53iayy4zy-nginx.conf\" \"-p\" \"/var/run/nginx\") exit-status: 1 term-signal: #f stop-signal: #f>")'.
guix system: warning: some services could not be upgraded
hint: To allow changes to all the system services to take effect, you will need to reboot.
--8<---------------cut here---------------end--------------->8---

What happen is Nginx won't start because the certficate related files
present in it's configuration doesn't exist and we can't get a Let's
Encrypt certificate from a HTTP-01 challenge without that web server
running.  NixOS broke that chicken and egg problem by generating a
self-signed certificate first, after that starting nginx, then
requesting a valid Lets' Encrypt certificate and finally reloading
Nginx.  That way we end up with a Nginx server using Let's Encrypt
certificate with no more that a simple system reconfiguration.  Note
that, the initial self-signed certificate will need to be at the path
were certbot will put it's own certificate.

WDYT?

¹ https://bugs.gnu.org/36389

Cheers,
- Brice


^ permalink raw reply	[flat|nested] 33+ messages in thread

* bug#46961: [PATCH 0/2] Allow nginx to start before certbot has run
  2021-03-06  8:15 Nginx and certbot cervices don't play well togther Brice Waegeneire
@ 2024-01-24 12:18 ` Carlo Zancanaro
  2024-01-24 12:18   ` bug#46961: [PATCH 1/2] services: certbot: Symlink certificates to /etc/certs Carlo Zancanaro
                     ` (6 more replies)
  0 siblings, 7 replies; 33+ messages in thread
From: Carlo Zancanaro @ 2024-01-24 12:18 UTC (permalink / raw)
  To: 46961

From time to time people have issues with setting up a new system with
certbot generating certificates for an nginx server. The issue is that
nginx won't start without being able to load certificates, but certbot
can't generate certificates (through the default HTTP challenge)
without a running nginx server.

Breaking this has generally required two reconfigures: one with nginx
configured without loading certificates, and then a second reconfigure
after running certbot to add the certificate configuration. This is a
bit of a pain, so I've made Guix generate a self-signed certificate to
allow nginx to start before certbot has run.

Unfortunately, I couldn't put the certificates in the same location as
certbot, because certbot is very particular about its directories not
existing when it requests a certificate for the first time. Rather
than try to convince it to do what I wanted, I opted to add another
level of indirection and move certificates to /etc/certs/. This is
backwards compatible, because the old /etc/letsenctypt/live/ is
maintained by certbot. The only real difference is for the initial
bootstrapping of a certificate.

Carlo Zancanaro (2):
  services: certbot: Symlink certificates to /etc/certs
  services: certbot: Create self-signed certificates before certbot runs

 doc/guix.texi            | 32 +++++++++------
 gnu/services/certbot.scm | 86 ++++++++++++++++++++++++++++++++++++++--
 2 files changed, 102 insertions(+), 16 deletions(-)


base-commit: ffc5fefce370f5fc01091869e13fdf525be1e0c0
-- 
2.41.0





^ permalink raw reply	[flat|nested] 33+ messages in thread

* bug#46961: [PATCH 1/2] services: certbot: Symlink certificates to /etc/certs
  2024-01-24 12:18 ` bug#46961: [PATCH 0/2] Allow nginx to start before certbot has run Carlo Zancanaro
@ 2024-01-24 12:18   ` Carlo Zancanaro
  2024-01-24 12:18   ` bug#46961: [PATCH 2/2] services: certbot: Create self-signed certificates before certbot runs Carlo Zancanaro
                     ` (5 subsequent siblings)
  6 siblings, 0 replies; 33+ messages in thread
From: Carlo Zancanaro @ 2024-01-24 12:18 UTC (permalink / raw)
  To: 46961

* gnu/services/certbot.scm (certbot-deploy-hook): New procedure.
(certbot-command): Pass new deploy hook to certbot.
* doc/guix.texi: Replace "letsencrypt/live" with "certs" throughout.
---
 doc/guix.texi            | 26 +++++++++++++-------------
 gnu/services/certbot.scm | 34 ++++++++++++++++++++++++++++++++--
 2 files changed, 45 insertions(+), 15 deletions(-)

diff --git a/doc/guix.texi b/doc/guix.texi
index a6187690bb..2d43ab9a65 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -43,7 +43,7 @@
 Copyright @copyright{} 2017, 2018, 2019 Clément Lassieur@*
 Copyright @copyright{} 2017, 2018, 2020, 2021, 2022 Mathieu Othacehe@*
 Copyright @copyright{} 2017 Federico Beffa@*
-Copyright @copyright{} 2017, 2018 Carlo Zancanaro@*
+Copyright @copyright{} 2017, 2018, 2024 Carlo Zancanaro@*
 Copyright @copyright{} 2017 Thomas Danckaert@*
 Copyright @copyright{} 2017 humanitiesNerd@*
 Copyright @copyright{} 2017, 2021 Christine Lemmer-Webber@*
@@ -28117,7 +28117,7 @@ Messaging Services
 them.  See @url{https://prosody.im/doc/letsencrypt}.
 
 @example
-prosodyctl --root cert import /etc/letsencrypt/live
+prosodyctl --root cert import /etc/certs
 @end example
 
 The available configuration parameters follow.  Each parameter
@@ -28820,8 +28820,8 @@ Telephony Services
           (welcome-text
             "Welcome to this Mumble server running on Guix!")
           (cert-required? #t) ;disallow text password logins
-          (ssl-cert "/etc/letsencrypt/live/mumble.example.com/fullchain.pem")
-          (ssl-key "/etc/letsencrypt/live/mumble.example.com/privkey.pem")))
+          (ssl-cert "/etc/certs/mumble.example.com/fullchain.pem")
+          (ssl-key "/etc/certs/mumble.example.com/privkey.pem")))
 @end lisp
 
 After reconfiguring your system, you can manually set the mumble-server
@@ -28939,12 +28939,12 @@ Telephony Services
 File name of the SSL/TLS certificate used for encrypted connections.
 
 @lisp
-(ssl-cert "/etc/letsencrypt/live/example.com/fullchain.pem")
+(ssl-cert "/etc/certs/example.com/fullchain.pem")
 @end lisp
 @item @code{ssl-key} (default: @code{#f})
 Filepath to the ssl private key used for encrypted connections.
 @lisp
-(ssl-key "/etc/letsencrypt/live/example.com/privkey.pem")
+(ssl-key "/etc/certs/example.com/privkey.pem")
 @end lisp
 
 @item @code{ssl-dh-params} (default: @code{#f})
@@ -32659,7 +32659,7 @@ Certificate Services
 Command to be run in a shell once for each successfully issued
 certificate.  For this command, the shell variable
 @code{$RENEWED_LINEAGE} will point to the config live subdirectory (for
-example, @samp{"/etc/letsencrypt/live/example.com"}) containing the new
+example, @samp{"/etc/certs/example.com"}) containing the new
 certificates and keys; the shell variable @code{$RENEWED_DOMAINS} will
 contain a space-delimited list of renewed certificate domains (for
 example, @samp{"example.com www.example.com"}.
@@ -32668,8 +32668,8 @@ Certificate Services
 @end deftp
 
 For each @code{certificate-configuration}, the certificate is saved to
-@code{/etc/letsencrypt/live/@var{name}/fullchain.pem} and the key is
-saved to @code{/etc/letsencrypt/live/@var{name}/privkey.pem}.
+@code{/etc/certs/@var{name}/fullchain.pem} and the key is
+saved to @code{/etc/certs/@var{name}/privkey.pem}.
 @node DNS Services
 @subsection DNS Services
 @cindex DNS (domain name system)
@@ -37355,9 +37355,9 @@ Version Control Services
              (listen '("443 ssl"))
              (server-name "git.my-host.org")
              (ssl-certificate
-              "/etc/letsencrypt/live/git.my-host.org/fullchain.pem")
+              "/etc/certs/git.my-host.org/fullchain.pem")
              (ssl-certificate-key
-              "/etc/letsencrypt/live/git.my-host.org/privkey.pem")
+              "/etc/certs/git.my-host.org/privkey.pem")
              (locations
               (list
                (git-http-nginx-location-configuration
@@ -38482,9 +38482,9 @@ Version Control Services
            (nginx-server-block
              (nginx-server-configuration
                (ssl-certificate
-                 "/etc/letsencrypt/live/myweb.site/fullchain.pem")
+                 "/etc/certs/myweb.site/fullchain.pem")
                (ssl-certificate-key
-                 "/etc/letsencrypt/live/myweb.site/privkey.pem")
+                 "/etc/certs/myweb.site/privkey.pem")
                (listen '("443 ssl http2" "[::]:443 ssl http2"))
                (locations
                  (list
diff --git a/gnu/services/certbot.scm b/gnu/services/certbot.scm
index 0c45471659..58e709f8a4 100644
--- a/gnu/services/certbot.scm
+++ b/gnu/services/certbot.scm
@@ -6,6 +6,7 @@
 ;;; Copyright © 2020 Jack Hill <jackhill@jackhill.us>
 ;;; Copyright © 2020 Tobias Geerinckx-Rice <me@tobias.gr>
 ;;; Copyright © 2021 Raghav Gururajan <rg@raghavgururajan.name>
+;;; Copyright © 2024 Carlo Zancanaro <carlo@zancanaro.id.au>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -87,6 +88,35 @@ (define-record-type* <certbot-configuration>
                           (body
                            (list "return 301 https://$host$request_uri;"))))))
 
+(define (certbot-deploy-hook name deploy-hook-script)
+  "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."
+  (program-file
+   (string-append name "-deploy-hook")
+   (with-imported-modules '((guix build utils))
+     #~(begin
+         (use-modules (guix build utils))
+         (mkdir-p #$(string-append "/etc/certs/" name))
+         (chmod #$(string-append "/etc/certs/" name) #o755)
+
+         ;; Create new symlinks
+         (symlink #$(string-append
+                     "/etc/letsencrypt/live/" name "/privkey.pem")
+                  #$(string-append "/etc/certs/" name "/privkey.pem.new"))
+         (symlink #$(string-append
+                     "/etc/letsencrypt/live/" name "/fullchain.pem")
+                  #$(string-append "/etc/certs/" name "/fullchain.pem.new"))
+
+         ;; Rename over the top of the old ones, if there are any.
+         (rename-file #$(string-append "/etc/certs/" name "/privkey.pem.new")
+                      #$(string-append "/etc/certs/" name "/privkey.pem"))
+         (rename-file #$(string-append "/etc/certs/" name "/fullchain.pem.new")
+                      #$(string-append "/etc/certs/" name "/fullchain.pem"))
+         #$@(if deploy-hook-script
+                (list #~(invoke #$deploy-hook-script))
+                '())))))
+
 (define certbot-command
   (match-lambda
     (($ <certbot-configuration> package webroot certificates email
@@ -118,7 +148,7 @@ (define certbot-command
                           `("--manual-auth-hook" ,authentication-hook)
                           '())
                       (if cleanup-hook `("--manual-cleanup-hook" ,cleanup-hook) '())
-                      (if deploy-hook `("--deploy-hook" ,deploy-hook) '()))
+                      (list "--deploy-hook" (certbot-deploy-hook name deploy-hook)))
                      (append
                       (list name certbot "certonly" "-n" "--agree-tos"
                             "--webroot" "-w" webroot
@@ -130,7 +160,7 @@ (define certbot-command
                           '("--register-unsafely-without-email"))
                       (if server `("--server" ,server) '())
                       (if rsa-key-size `("--rsa-key-size" ,rsa-key-size) '())
-                      (if deploy-hook `("--deploy-hook" ,deploy-hook) '()))))))
+                      (list "--deploy-hook" (certbot-deploy-hook name deploy-hook)))))))
               certificates)))
        (program-file
         "certbot-command"
-- 
2.41.0





^ permalink raw reply related	[flat|nested] 33+ messages in thread

* bug#46961: [PATCH 2/2] services: certbot: Create self-signed certificates before certbot runs
  2024-01-24 12:18 ` bug#46961: [PATCH 0/2] Allow nginx to start before certbot has run Carlo Zancanaro
  2024-01-24 12:18   ` bug#46961: [PATCH 1/2] services: certbot: Symlink certificates to /etc/certs Carlo Zancanaro
@ 2024-01-24 12:18   ` Carlo Zancanaro
  2024-01-24 13:01     ` Carlo Zancanaro
                       ` (2 more replies)
  2024-01-30 13:26     ` Carlo Zancanaro
                     ` (4 subsequent siblings)
  6 siblings, 3 replies; 33+ messages in thread
From: Carlo Zancanaro @ 2024-01-24 12:18 UTC (permalink / raw)
  To: 46961

* gnu/services/certbot.scm (<certificate-configuration>): Add
start-self-signed? field.
(generate-certificate-gexp): New procedure.
(certbot-activation): Generate self-signed certificates when
start-self-signed? is #t.
* doc/guix.texi (Certificate services): Document start-self-signed?.
---
 doc/guix.texi            |  6 +++++
 gnu/services/certbot.scm | 56 +++++++++++++++++++++++++++++++++++++---
 2 files changed, 59 insertions(+), 3 deletions(-)

diff --git a/doc/guix.texi b/doc/guix.texi
index 2d43ab9a65..15b256d0a3 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -32664,6 +32664,12 @@ Certificate Services
 contain a space-delimited list of renewed certificate domains (for
 example, @samp{"example.com www.example.com"}.
 
+@item @code{start-self-signed?} (default: @code{#t})
+Whether to generate an initial self-signed certificate during system
+activation.  This option is particularly useful to allow @code{nginx} to
+start before @code{certbot} has run, because @code{certbot} relies on
+@code{nginx} running to perform HTTP challenges.
+
 @end table
 @end deftp
 
diff --git a/gnu/services/certbot.scm b/gnu/services/certbot.scm
index 58e709f8a4..bb321a1b50 100644
--- a/gnu/services/certbot.scm
+++ b/gnu/services/certbot.scm
@@ -64,7 +64,9 @@ (define-record-type* <certificate-configuration>
   (cleanup-hook        certificate-cleanup-hook
                        (default #f))
   (deploy-hook         certificate-configuration-deploy-hook
-                       (default #f)))
+                       (default #f))
+  (start-self-signed?  certificate-configuration-start-self-signed?
+                       (default #t)))
 
 (define-record-type* <certbot-configuration>
   certbot-configuration make-certbot-configuration
@@ -91,7 +93,10 @@ (define-record-type* <certbot-configuration>
 (define (certbot-deploy-hook name deploy-hook-script)
   "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."
+not #f then it is run after the symlinks have been created.  This wrapping is
+necessary for certificates with start-self-signed? set to #t, as it will
+overwrite the initial self-signed certificates upon the first successful
+deploy."
   (program-file
    (string-append name "-deploy-hook")
    (with-imported-modules '((guix build utils))
@@ -108,7 +113,8 @@ (define (certbot-deploy-hook name deploy-hook-script)
                      "/etc/letsencrypt/live/" name "/fullchain.pem")
                   #$(string-append "/etc/certs/" name "/fullchain.pem.new"))
 
-         ;; Rename over the top of the old ones, if there are any.
+         ;; Rename over the top of the old ones, just in case they were the
+         ;; original self-signed certificates.
          (rename-file #$(string-append "/etc/certs/" name "/privkey.pem.new")
                       #$(string-append "/etc/certs/" name "/privkey.pem"))
          (rename-file #$(string-append "/etc/certs/" name "/fullchain.pem.new")
@@ -182,6 +188,44 @@ (define (certbot-renewal-jobs config)
    #~(job '(next-minute-from (next-hour '(0 12)) (list (random 60)))
           #$(certbot-command config))))
 
+(define (generate-certificate-gexp certbot-cert-directory rsa-key-size)
+  (match-lambda
+    (($ <certificate-configuration> 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))))
+       #~(begin
+           (use-modules (ice-9 format))
+           (when (not (file-exists? #$directory))
+             ;; Due to the way certbot runs, we need to
+             ;; create the self-signed certificates in the
+             ;; archive folder and symlink them into the live
+             ;; folder. This mimics what certbot does well
+             ;; enough to make acquiring new certificates
+             ;; work.
+             (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)))))))))
+
 (define (certbot-activation config)
   (let* ((certbot-directory "/var/lib/certbot")
          (certbot-cert-directory "/etc/letsencrypt/live")
@@ -196,6 +240,12 @@ (define (certbot-activation config)
              (mkdir-p #$webroot)
              (mkdir-p #$certbot-directory)
              (mkdir-p #$certbot-cert-directory)
+
+             #$@(map (generate-certificate-gexp certbot-cert-directory
+                                                (and rsa-key-size (number->string rsa-key-size)))
+                     (filter certificate-configuration-start-self-signed?
+                             certificates))
+
              (copy-file #$(certbot-command config) #$script)
              (display #$message)))))))
 
-- 
2.41.0





^ permalink raw reply related	[flat|nested] 33+ messages in thread

* bug#46961: [PATCH 2/2] services: certbot: Create self-signed certificates before certbot runs
  2024-01-24 12:18   ` bug#46961: [PATCH 2/2] services: certbot: Create self-signed certificates before certbot runs Carlo Zancanaro
@ 2024-01-24 13:01     ` Carlo Zancanaro
  2024-01-29 19:23     ` bug#46961: Nginx and certbot cervices don't play well togther Clément Lassieur
  2024-01-29 19:28     ` Clément Lassieur
  2 siblings, 0 replies; 33+ messages in thread
From: Carlo Zancanaro @ 2024-01-24 13:01 UTC (permalink / raw)
  Cc: 46961

On Wed, Jan 24 2024, Carlo Zancanaro wrote:
> +             ;; Due to the way certbot runs, we need to
> +             ;; create the self-signed certificates in the
> +             ;; archive folder and symlink them into the live
> +             ;; folder. This mimics what certbot does well
> +             ;; enough to make acquiring new certificates
> +             ;; work.

Gah, this comment is from a previous iteration. It turns out it 
didn't
work as well as I thought it did.

I'm happy to update this comment, but I won't do that until I've 
heard
back about the more substantive aspects of the change. I'm also 
happy
for whoever merges this to change this comment appropriately.




^ permalink raw reply	[flat|nested] 33+ messages in thread

* bug#46961: Nginx and certbot cervices don't play well togther
  2024-01-24 12:18   ` bug#46961: [PATCH 2/2] services: certbot: Create self-signed certificates before certbot runs Carlo Zancanaro
  2024-01-24 13:01     ` Carlo Zancanaro
@ 2024-01-29 19:23     ` Clément Lassieur
  2024-01-29 23:02       ` Carlo Zancanaro
  2024-01-29 19:28     ` Clément Lassieur
  2 siblings, 1 reply; 33+ messages in thread
From: Clément Lassieur @ 2024-01-29 19:23 UTC (permalink / raw)
  To: Carlo Zancanaro; +Cc: 46961

Hi Carlo,

On Wed, Jan 24 2024, Carlo Zancanaro wrote:

> * gnu/services/certbot.scm (<certificate-configuration>): Add
> start-self-signed? field.
> (generate-certificate-gexp): New procedure.
> (certbot-activation): Generate self-signed certificates when
> start-self-signed? is #t.
> * doc/guix.texi (Certificate services): Document start-self-signed?.
> ---
>  doc/guix.texi            |  6 +++++
>  gnu/services/certbot.scm | 56 +++++++++++++++++++++++++++++++++++++---
>  2 files changed, 59 insertions(+), 3 deletions(-)

This is great, thank you!  I tested it, it worked.  Could you please
just make sure lines fit within 80 columns?  And there is a warning
during compilation, pasted below.

Would it make sense now to run ‘update-certificates’ at end of the
activation stuff?

And would it make sense to reload nginx after ‘update-certificates’ is
run?

Clément

> diff --git a/doc/guix.texi b/doc/guix.texi
> index 2d43ab9a65..15b256d0a3 100644
> --- a/doc/guix.texi
> +++ b/doc/guix.texi
> @@ -32664,6 +32664,12 @@ Certificate Services
>  contain a space-delimited list of renewed certificate domains (for
>  example, @samp{"example.com www.example.com"}.
>  
> +@item @code{start-self-signed?} (default: @code{#t})
> +Whether to generate an initial self-signed certificate during system
> +activation.  This option is particularly useful to allow @code{nginx} to
> +start before @code{certbot} has run, because @code{certbot} relies on
> +@code{nginx} running to perform HTTP challenges.
> +
>  @end table
>  @end deftp
>  
> diff --git a/gnu/services/certbot.scm b/gnu/services/certbot.scm
> index 58e709f8a4..bb321a1b50 100644
> --- a/gnu/services/certbot.scm
> +++ b/gnu/services/certbot.scm
> @@ -64,7 +64,9 @@ (define-record-type* <certificate-configuration>
>    (cleanup-hook        certificate-cleanup-hook
>                         (default #f))
>    (deploy-hook         certificate-configuration-deploy-hook
> -                       (default #f)))
> +                       (default #f))
> +  (start-self-signed?  certificate-configuration-start-self-signed?
> +                       (default #t)))
>  
>  (define-record-type* <certbot-configuration>
>    certbot-configuration make-certbot-configuration
> @@ -91,7 +93,10 @@ (define-record-type* <certbot-configuration>
>  (define (certbot-deploy-hook name deploy-hook-script)
>    "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."
> +not #f then it is run after the symlinks have been created.  This wrapping is
> +necessary for certificates with start-self-signed? set to #t, as it will
> +overwrite the initial self-signed certificates upon the first successful
> +deploy."
>    (program-file
>     (string-append name "-deploy-hook")
>     (with-imported-modules '((guix build utils))
> @@ -108,7 +113,8 @@ (define (certbot-deploy-hook name deploy-hook-script)
>                       "/etc/letsencrypt/live/" name "/fullchain.pem")
>                    #$(string-append "/etc/certs/" name "/fullchain.pem.new"))
>  
> -         ;; Rename over the top of the old ones, if there are any.
> +         ;; Rename over the top of the old ones, just in case they were the
> +         ;; original self-signed certificates.
>           (rename-file #$(string-append "/etc/certs/" name "/privkey.pem.new")
>                        #$(string-append "/etc/certs/" name "/privkey.pem"))
>           (rename-file #$(string-append "/etc/certs/" name "/fullchain.pem.new")
> @@ -182,6 +188,44 @@ (define (certbot-renewal-jobs config)
>     #~(job '(next-minute-from (next-hour '(0 12)) (list (random 60)))
>            #$(certbot-command config))))
>  
> +(define (generate-certificate-gexp certbot-cert-directory rsa-key-size)
> +  (match-lambda
> +    (($ <certificate-configuration> 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)))

gnu/services/certbot.scm:203:26: warning: "subjectAltName=~{DNS:~a~^,~}": unsupported format option ~{, use (ice-9 format) instead

> +           (directory (string-append "/etc/certs/" (or name primary-domain))))
> +       #~(begin
> +           (use-modules (ice-9 format))
> +           (when (not (file-exists? #$directory))
> +             ;; Due to the way certbot runs, we need to
> +             ;; create the self-signed certificates in the
> +             ;; archive folder and symlink them into the live
> +             ;; folder. This mimics what certbot does well
> +             ;; enough to make acquiring new certificates
> +             ;; work.

In another mail you say it doesn't work as well as you thought it did?
What doesn't work?

> +             (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)))))))))
> +
>  (define (certbot-activation config)
>    (let* ((certbot-directory "/var/lib/certbot")
>           (certbot-cert-directory "/etc/letsencrypt/live")
> @@ -196,6 +240,12 @@ (define (certbot-activation config)
>               (mkdir-p #$webroot)
>               (mkdir-p #$certbot-directory)
>               (mkdir-p #$certbot-cert-directory)
> +
> +             #$@(map (generate-certificate-gexp certbot-cert-directory
> +                                                (and rsa-key-size (number->string rsa-key-size)))
> +                     (filter certificate-configuration-start-self-signed?
> +                             certificates))
> +
>               (copy-file #$(certbot-command config) #$script)
>               (display #$message)))))))




^ permalink raw reply	[flat|nested] 33+ messages in thread

* bug#46961: Nginx and certbot cervices don't play well togther
  2024-01-24 12:18   ` bug#46961: [PATCH 2/2] services: certbot: Create self-signed certificates before certbot runs Carlo Zancanaro
  2024-01-24 13:01     ` Carlo Zancanaro
  2024-01-29 19:23     ` bug#46961: Nginx and certbot cervices don't play well togther Clément Lassieur
@ 2024-01-29 19:28     ` Clément Lassieur
  2 siblings, 0 replies; 33+ messages in thread
From: Clément Lassieur @ 2024-01-29 19:28 UTC (permalink / raw)
  To: Carlo Zancanaro; +Cc: 46961

Also, I forgot, I think it would be great to have somewhere in the doc
an example of minimal config.scm that works.  I know we can't do proper
testing because we depend of certbot service but that would make it
easier for a lot of people to test it.  Maybe such example is already in
the docs and I haven't seen it though.

Here is the one I used:

--8<---------------cut here---------------start------------->8---
(use-modules (gnu) (gnu tests))
(use-package-modules web)
(use-service-modules certbot networking web)

(operating-system
  (inherit %simple-os)
  (services
   (cons*
    (service dhcp-client-service-type)
    (service nginx-service-type
             (nginx-configuration
              (server-blocks
               (list
                (nginx-server-configuration
                 (listen '("443 ssl"))
                 (server-name '("test.lassieur.org"))
                 (ssl-certificate
                  "/etc/certs/test.lassieur.org/fullchain.pem")
                 (ssl-certificate-key
                  "/etc/certs/test.lassieur.org/privkey.pem"))))))
    (service certbot-service-type
             (certbot-configuration
              (certificates
               (list
                (certificate-configuration
                 (domains '("test.lassieur.org")))))))
    %base-services)))
--8<---------------cut here---------------end--------------->8---




^ permalink raw reply	[flat|nested] 33+ messages in thread

* bug#46961: Nginx and certbot cervices don't play well togther
  2024-01-29 19:23     ` bug#46961: Nginx and certbot cervices don't play well togther Clément Lassieur
@ 2024-01-29 23:02       ` Carlo Zancanaro
  2024-01-29 23:19         ` Clément Lassieur
  0 siblings, 1 reply; 33+ messages in thread
From: Carlo Zancanaro @ 2024-01-29 23:02 UTC (permalink / raw)
  To: Clément Lassieur; +Cc: 46961

Hi Clément,

Thanks for taking the time to review my change. I've responded inline
below.

On Mon, Jan 29 2024, Clément Lassieur wrote:
> This is great, thank you!  I tested it, it worked.  Could you please
> just make sure lines fit within 80 columns?

Yep, no worries.

> Would it make sense now to run ‘update-certificates’ at end of the
> activation stuff?

We can't run it during activation, because nginx won't have started yet.
However, I am planning a follow-up to add a one-shot service to run
certbot after nginx starts. I'll see if I can add it to this series, but
if I run into any issues I'll leave it for later.

> And would it make sense to reload nginx after ‘update-certificates’ is
> run?

This would be a sensible default. There is an example in the manual of
configuring certbot to reload nginx, so this should be straightforward
to add.

> gnu/services/certbot.scm:203:26: warning: "subjectAltName=~{DNS:~a~^,~}": unsupported format option ~{, use (ice-9 format) instead

Ha! I import (ice-9 format), but within the gexp (and then I don't use
it, whoops!). Must be a leftover from a previous iteration. I'll fix
this up.

>> +             ;; Due to the way certbot runs, we need to
>> +             ;; create the self-signed certificates in the
>> +             ;; archive folder and symlink them into the live
>> +             ;; folder. This mimics what certbot does well
>> +             ;; enough to make acquiring new certificates
>> +             ;; work.
>
> In another mail you say it doesn't work as well as you thought it did?
> What doesn't work?

This comment doesn't describe the code any more. In my first attempt I
was trying to generate certificates in /etc/letsencrypt/live/ and get
certbot to write over them when it ran. Unfortunately, it refused to do
so. I then tried writing to /etc/letsencrypt/archive/ and symlinking
into /etc/letsencrypt/live/ (which is what this comment describes), but
that also failed. Certbot refuses to write over any existing files when
fetching a certificate.

It looks like other acme clients might be happier to overwrite existing
files, but changing away from certbot seemed like more work than adding
a deploy hook to do what we need.

I'll follow up with a v2 of this patch when I get a chance.

Carlo




^ permalink raw reply	[flat|nested] 33+ messages in thread

* bug#46961: Nginx and certbot cervices don't play well togther
  2024-01-29 23:02       ` Carlo Zancanaro
@ 2024-01-29 23:19         ` Clément Lassieur
  0 siblings, 0 replies; 33+ messages in thread
From: Clément Lassieur @ 2024-01-29 23:19 UTC (permalink / raw)
  To: Carlo Zancanaro; +Cc: 46961

On Tue, Jan 30 2024, Carlo Zancanaro wrote:

>>> +             ;; Due to the way certbot runs, we need to
>>> +             ;; create the self-signed certificates in the
>>> +             ;; archive folder and symlink them into the live
>>> +             ;; folder. This mimics what certbot does well
>>> +             ;; enough to make acquiring new certificates
>>> +             ;; work.
>>
>> In another mail you say it doesn't work as well as you thought it did?
>> What doesn't work?
>
> This comment doesn't describe the code any more. In my first attempt I
> was trying to generate certificates in /etc/letsencrypt/live/ and get
> certbot to write over them when it ran. Unfortunately, it refused to do
> so. I then tried writing to /etc/letsencrypt/archive/ and symlinking
> into /etc/letsencrypt/live/ (which is what this comment describes), but
> that also failed. Certbot refuses to write over any existing files when
> fetching a certificate.

Oh I read the comment too quickly, I thought it was describing the
/etc/certs moving.  I suppose you will update it so to reflect the
actual state?

What you did (using /etc/certs, and symlinking stuff in
/etc/letsencrypt) is a good idea I think, and it's excellent that it's
backward compatible!

> It looks like other acme clients might be happier to overwrite existing
> files, but changing away from certbot seemed like more work than adding
> a deploy hook to do what we need.

Indeed!

> I'll follow up with a v2 of this patch when I get a chance.

Thanks!

> Carlo




^ permalink raw reply	[flat|nested] 33+ messages in thread

* bug#46961: [PATCH v2 0/4] Make certbot play more nicely with nginx
  2024-01-24 12:18 ` bug#46961: [PATCH 0/2] Allow nginx to start before certbot has run Carlo Zancanaro
@ 2024-01-30 13:26     ` Carlo Zancanaro
  2024-01-24 12:18   ` bug#46961: [PATCH 2/2] services: certbot: Create self-signed certificates before certbot runs Carlo Zancanaro
                       ` (5 subsequent siblings)
  6 siblings, 0 replies; 33+ messages in thread
From: Carlo Zancanaro @ 2024-01-30 13:26 UTC (permalink / raw)
  To: 46961; +Cc: guix-devel, brice, clement

Hi Guix,

This patch series is a few changes to make certbot default to doing
"the right thing" in the common case of wanting certificates for an
nginx web server.

The initial change (in v1 of these patches) was to solve the certbot
bootstrapping problem. Nginx won't start without valid certificates,
but certbot can't produce certificates without a functional
nginx. This is solved by generating self-signed certificates to start
with, and then replacing them once certbot has run. Doing this
requires storing certificates in a different location (because certbot
is very particular). I've chosen /etc/certs/.

The other two changes (new to v2 of this series) make things a bit
easier to use: a one-shot shepherd service to renew certificates when
the machine starts up, and a default deploy-hook to reload the nginx
configuration (which picks up the new certificates). I think these
changes make certbot "do the right thing", at the expense of being
slightly more magical.

On IRC podiki suggested I should copy guix-devel and Brice (the
original bug reporter), so I've done that, too.

Carlo Zancanaro (4):
  services: certbot: Symlink certificates to /etc/certs.
  services: certbot: Create self-signed certificates before certbot
    runs.
  services: certbot: Add a default deploy hook to reload nginx.
  services: certbot: Add one-shot service to renew certificates.

 doc/guix.texi            |  38 ++++++---
 gnu/services/certbot.scm | 178 ++++++++++++++++++++++++++++++++++++---
 2 files changed, 188 insertions(+), 28 deletions(-)


base-commit: 144c95032e517bb8ce466b930fe91506bcc92b2b
-- 
2.41.0





^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH v2 0/4] Make certbot play more nicely with nginx
@ 2024-01-30 13:26     ` Carlo Zancanaro
  0 siblings, 0 replies; 33+ messages in thread
From: Carlo Zancanaro @ 2024-01-30 13:26 UTC (permalink / raw)
  To: 46961; +Cc: clement, brice, guix-devel

Hi Guix,

This patch series is a few changes to make certbot default to doing
"the right thing" in the common case of wanting certificates for an
nginx web server.

The initial change (in v1 of these patches) was to solve the certbot
bootstrapping problem. Nginx won't start without valid certificates,
but certbot can't produce certificates without a functional
nginx. This is solved by generating self-signed certificates to start
with, and then replacing them once certbot has run. Doing this
requires storing certificates in a different location (because certbot
is very particular). I've chosen /etc/certs/.

The other two changes (new to v2 of this series) make things a bit
easier to use: a one-shot shepherd service to renew certificates when
the machine starts up, and a default deploy-hook to reload the nginx
configuration (which picks up the new certificates). I think these
changes make certbot "do the right thing", at the expense of being
slightly more magical.

On IRC podiki suggested I should copy guix-devel and Brice (the
original bug reporter), so I've done that, too.

Carlo Zancanaro (4):
  services: certbot: Symlink certificates to /etc/certs.
  services: certbot: Create self-signed certificates before certbot
    runs.
  services: certbot: Add a default deploy hook to reload nginx.
  services: certbot: Add one-shot service to renew certificates.

 doc/guix.texi            |  38 ++++++---
 gnu/services/certbot.scm | 178 ++++++++++++++++++++++++++++++++++++---
 2 files changed, 188 insertions(+), 28 deletions(-)


base-commit: 144c95032e517bb8ce466b930fe91506bcc92b2b
-- 
2.41.0



^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH v2 1/4] services: certbot: Symlink certificates to /etc/certs.
  2024-01-24 12:18 ` bug#46961: [PATCH 0/2] Allow nginx to start before certbot has run Carlo Zancanaro
                     ` (2 preceding siblings ...)
  2024-01-30 13:26     ` Carlo Zancanaro
@ 2024-01-30 13:26   ` Carlo Zancanaro
  2024-01-30 13:26   ` [PATCH v2 2/4] services: certbot: Create self-signed certificates before certbot runs Carlo Zancanaro
                     ` (2 subsequent siblings)
  6 siblings, 0 replies; 33+ messages in thread
From: Carlo Zancanaro @ 2024-01-30 13:26 UTC (permalink / raw)
  To: 46961; +Cc: clement, brice, guix-devel

* gnu/services/certbot.scm (certbot-deploy-hook): New procedure.
(certbot-command): Pass new deploy hook to certbot.
* doc/guix.texi: Replace "letsencrypt/live" with "certs" throughout.

Change-Id: I2ba5e4903d1e293e566b732a84b07d5a134b697d
---
 doc/guix.texi            | 26 +++++++++++++-------------
 gnu/services/certbot.scm | 36 ++++++++++++++++++++++++++++++++++--
 2 files changed, 47 insertions(+), 15 deletions(-)

diff --git a/doc/guix.texi b/doc/guix.texi
index bb0af26d93..b134d45a16 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -43,7 +43,7 @@
 Copyright @copyright{} 2017, 2018, 2019 Clément Lassieur@*
 Copyright @copyright{} 2017, 2018, 2020, 2021, 2022 Mathieu Othacehe@*
 Copyright @copyright{} 2017 Federico Beffa@*
-Copyright @copyright{} 2017, 2018 Carlo Zancanaro@*
+Copyright @copyright{} 2017, 2018, 2024 Carlo Zancanaro@*
 Copyright @copyright{} 2017 Thomas Danckaert@*
 Copyright @copyright{} 2017 humanitiesNerd@*
 Copyright @copyright{} 2017, 2021 Christine Lemmer-Webber@*
@@ -28135,7 +28135,7 @@ Messaging Services
 them.  See @url{https://prosody.im/doc/letsencrypt}.
 
 @example
-prosodyctl --root cert import /etc/letsencrypt/live
+prosodyctl --root cert import /etc/certs
 @end example
 
 The available configuration parameters follow.  Each parameter
@@ -28846,8 +28846,8 @@ Telephony Services
           (welcome-text
             "Welcome to this Mumble server running on Guix!")
           (cert-required? #t) ;disallow text password logins
-          (ssl-cert "/etc/letsencrypt/live/mumble.example.com/fullchain.pem")
-          (ssl-key "/etc/letsencrypt/live/mumble.example.com/privkey.pem")))
+          (ssl-cert "/etc/certs/mumble.example.com/fullchain.pem")
+          (ssl-key "/etc/certs/mumble.example.com/privkey.pem")))
 @end lisp
 
 After reconfiguring your system, you can manually set the mumble-server
@@ -28965,12 +28965,12 @@ Telephony Services
 File name of the SSL/TLS certificate used for encrypted connections.
 
 @lisp
-(ssl-cert "/etc/letsencrypt/live/example.com/fullchain.pem")
+(ssl-cert "/etc/certs/example.com/fullchain.pem")
 @end lisp
 @item @code{ssl-key} (default: @code{#f})
 Filepath to the ssl private key used for encrypted connections.
 @lisp
-(ssl-key "/etc/letsencrypt/live/example.com/privkey.pem")
+(ssl-key "/etc/certs/example.com/privkey.pem")
 @end lisp
 
 @item @code{ssl-dh-params} (default: @code{#f})
@@ -32685,7 +32685,7 @@ Certificate Services
 Command to be run in a shell once for each successfully issued
 certificate.  For this command, the shell variable
 @code{$RENEWED_LINEAGE} will point to the config live subdirectory (for
-example, @samp{"/etc/letsencrypt/live/example.com"}) containing the new
+example, @samp{"/etc/certs/example.com"}) containing the new
 certificates and keys; the shell variable @code{$RENEWED_DOMAINS} will
 contain a space-delimited list of renewed certificate domains (for
 example, @samp{"example.com www.example.com"}.
@@ -32694,8 +32694,8 @@ Certificate Services
 @end deftp
 
 For each @code{certificate-configuration}, the certificate is saved to
-@code{/etc/letsencrypt/live/@var{name}/fullchain.pem} and the key is
-saved to @code{/etc/letsencrypt/live/@var{name}/privkey.pem}.
+@code{/etc/certs/@var{name}/fullchain.pem} and the key is
+saved to @code{/etc/certs/@var{name}/privkey.pem}.
 @node DNS Services
 @subsection DNS Services
 @cindex DNS (domain name system)
@@ -37381,9 +37381,9 @@ Version Control Services
              (listen '("443 ssl"))
              (server-name "git.my-host.org")
              (ssl-certificate
-              "/etc/letsencrypt/live/git.my-host.org/fullchain.pem")
+              "/etc/certs/git.my-host.org/fullchain.pem")
              (ssl-certificate-key
-              "/etc/letsencrypt/live/git.my-host.org/privkey.pem")
+              "/etc/certs/git.my-host.org/privkey.pem")
              (locations
               (list
                (git-http-nginx-location-configuration
@@ -38508,9 +38508,9 @@ Version Control Services
            (nginx-server-block
              (nginx-server-configuration
                (ssl-certificate
-                 "/etc/letsencrypt/live/myweb.site/fullchain.pem")
+                 "/etc/certs/myweb.site/fullchain.pem")
                (ssl-certificate-key
-                 "/etc/letsencrypt/live/myweb.site/privkey.pem")
+                 "/etc/certs/myweb.site/privkey.pem")
                (listen '("443 ssl http2" "[::]:443 ssl http2"))
                (locations
                  (list
diff --git a/gnu/services/certbot.scm b/gnu/services/certbot.scm
index 0c45471659..3926d0551a 100644
--- a/gnu/services/certbot.scm
+++ b/gnu/services/certbot.scm
@@ -6,6 +6,7 @@
 ;;; Copyright © 2020 Jack Hill <jackhill@jackhill.us>
 ;;; Copyright © 2020 Tobias Geerinckx-Rice <me@tobias.gr>
 ;;; Copyright © 2021 Raghav Gururajan <rg@raghavgururajan.name>
+;;; Copyright © 2024 Carlo Zancanaro <carlo@zancanaro.id.au>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -87,6 +88,35 @@ (define-record-type* <certbot-configuration>
                           (body
                            (list "return 301 https://$host$request_uri;"))))))
 
+(define (certbot-deploy-hook name deploy-hook-script)
+  "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."
+  (program-file
+   (string-append name "-deploy-hook")
+   (with-imported-modules '((guix build utils))
+     #~(begin
+         (use-modules (guix build utils))
+         (mkdir-p #$(string-append "/etc/certs/" name))
+         (chmod #$(string-append "/etc/certs/" name) #o755)
+
+         ;; Create new symlinks
+         (symlink #$(string-append
+                     "/etc/letsencrypt/live/" name "/privkey.pem")
+                  #$(string-append "/etc/certs/" name "/privkey.pem.new"))
+         (symlink #$(string-append
+                     "/etc/letsencrypt/live/" name "/fullchain.pem")
+                  #$(string-append "/etc/certs/" name "/fullchain.pem.new"))
+
+         ;; Rename over the top of the old ones, if there are any.
+         (rename-file #$(string-append "/etc/certs/" name "/privkey.pem.new")
+                      #$(string-append "/etc/certs/" name "/privkey.pem"))
+         (rename-file #$(string-append "/etc/certs/" name "/fullchain.pem.new")
+                      #$(string-append "/etc/certs/" name "/fullchain.pem"))
+         #$@(if deploy-hook-script
+                (list #~(invoke #$deploy-hook-script))
+                '())))))
+
 (define certbot-command
   (match-lambda
     (($ <certbot-configuration> package webroot certificates email
@@ -118,7 +148,8 @@ (define certbot-command
                           `("--manual-auth-hook" ,authentication-hook)
                           '())
                       (if cleanup-hook `("--manual-cleanup-hook" ,cleanup-hook) '())
-                      (if deploy-hook `("--deploy-hook" ,deploy-hook) '()))
+                      (list "--deploy-hook"
+                            (certbot-deploy-hook name deploy-hook)))
                      (append
                       (list name certbot "certonly" "-n" "--agree-tos"
                             "--webroot" "-w" webroot
@@ -130,7 +161,8 @@ (define certbot-command
                           '("--register-unsafely-without-email"))
                       (if server `("--server" ,server) '())
                       (if rsa-key-size `("--rsa-key-size" ,rsa-key-size) '())
-                      (if deploy-hook `("--deploy-hook" ,deploy-hook) '()))))))
+                      (list "--deploy-hook"
+                            (certbot-deploy-hook name deploy-hook)))))))
               certificates)))
        (program-file
         "certbot-command"
-- 
2.41.0



^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH v2 2/4] services: certbot: Create self-signed certificates before certbot runs.
  2024-01-24 12:18 ` bug#46961: [PATCH 0/2] Allow nginx to start before certbot has run Carlo Zancanaro
                     ` (3 preceding siblings ...)
  2024-01-30 13:26   ` [PATCH v2 1/4] services: certbot: Symlink certificates to /etc/certs Carlo Zancanaro
@ 2024-01-30 13:26   ` Carlo Zancanaro
  2024-01-30 13:26   ` [PATCH v2 3/4] services: certbot: Add a default deploy hook to reload nginx Carlo Zancanaro
  2024-01-30 13:26     ` Carlo Zancanaro
  6 siblings, 0 replies; 33+ messages in thread
From: Carlo Zancanaro @ 2024-01-30 13:26 UTC (permalink / raw)
  To: 46961; +Cc: clement, brice, guix-devel

* gnu/services/certbot.scm (<certificate-configuration>): Add
start-self-signed? field.
(generate-certificate-gexp): New procedure.
(certbot-activation): Generate self-signed certificates when
start-self-signed? is #t.
* doc/guix.texi (Certificate services): Document start-self-signed?.

Change-Id: Icfd85ae0c3e29324acbcde6ba283546cf0e27a1d
---
 doc/guix.texi            |  6 ++++
 gnu/services/certbot.scm | 62 ++++++++++++++++++++++++++++++++++++++--
 2 files changed, 65 insertions(+), 3 deletions(-)

diff --git a/doc/guix.texi b/doc/guix.texi
index b134d45a16..58a65fe0b7 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -32690,6 +32690,12 @@ Certificate Services
 contain a space-delimited list of renewed certificate domains (for
 example, @samp{"example.com www.example.com"}.
 
+@item @code{start-self-signed?} (default: @code{#t})
+Whether to generate an initial self-signed certificate during system
+activation.  This option is particularly useful to allow @code{nginx} to
+start before @code{certbot} has run, because @code{certbot} relies on
+@code{nginx} running to perform HTTP challenges.
+
 @end table
 @end deftp
 
diff --git a/gnu/services/certbot.scm b/gnu/services/certbot.scm
index 3926d0551a..10b99f5630 100644
--- a/gnu/services/certbot.scm
+++ b/gnu/services/certbot.scm
@@ -35,6 +35,7 @@ (define-module (gnu services certbot)
   #:use-module (guix records)
   #:use-module (guix gexp)
   #:use-module (srfi srfi-1)
+  #:use-module (ice-9 format)
   #:use-module (ice-9 match)
   #:export (certbot-service-type
             certbot-configuration
@@ -64,7 +65,9 @@ (define-record-type* <certificate-configuration>
   (cleanup-hook        certificate-cleanup-hook
                        (default #f))
   (deploy-hook         certificate-configuration-deploy-hook
-                       (default #f)))
+                       (default #f))
+  (start-self-signed?  certificate-configuration-start-self-signed?
+                       (default #t)))
 
 (define-record-type* <certbot-configuration>
   certbot-configuration make-certbot-configuration
@@ -91,7 +94,10 @@ (define-record-type* <certbot-configuration>
 (define (certbot-deploy-hook name deploy-hook-script)
   "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."
+not #f then it is run after the symlinks have been created.  This wrapping is
+necessary for certificates with start-self-signed? set to #t, as it will
+overwrite the initial self-signed certificates upon the first successful
+deploy."
   (program-file
    (string-append name "-deploy-hook")
    (with-imported-modules '((guix build utils))
@@ -108,7 +114,8 @@ (define (certbot-deploy-hook name deploy-hook-script)
                      "/etc/letsencrypt/live/" name "/fullchain.pem")
                   #$(string-append "/etc/certs/" name "/fullchain.pem.new"))
 
-         ;; Rename over the top of the old ones, if there are any.
+         ;; Rename over the top of the old ones, just in case they were the
+         ;; original self-signed certificates.
          (rename-file #$(string-append "/etc/certs/" name "/privkey.pem.new")
                       #$(string-append "/etc/certs/" name "/privkey.pem"))
          (rename-file #$(string-append "/etc/certs/" name "/fullchain.pem.new")
@@ -184,6 +191,47 @@ (define (certbot-renewal-jobs config)
    #~(job '(next-minute-from (next-hour '(0 12)) (list (random 60)))
           #$(certbot-command config))))
 
+(define (generate-certificate-gexp certbot-cert-directory rsa-key-size)
+  (match-lambda
+    (($ <certificate-configuration> 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))))))))
+
 (define (certbot-activation config)
   (let* ((certbot-directory "/var/lib/certbot")
          (certbot-cert-directory "/etc/letsencrypt/live")
@@ -198,6 +246,14 @@ (define (certbot-activation config)
              (mkdir-p #$webroot)
              (mkdir-p #$certbot-directory)
              (mkdir-p #$certbot-cert-directory)
+
+             #$@(let ((rsa-key-size (and rsa-key-size
+                                         (number->string rsa-key-size))))
+                  (map (generate-certificate-gexp certbot-cert-directory
+                                                  rsa-key-size)
+                       (filter certificate-configuration-start-self-signed?
+                               certificates)))
+
              (copy-file #$(certbot-command config) #$script)
              (display #$message)))))))
 
-- 
2.41.0



^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH v2 3/4] services: certbot: Add a default deploy hook to reload nginx.
  2024-01-24 12:18 ` bug#46961: [PATCH 0/2] Allow nginx to start before certbot has run Carlo Zancanaro
                     ` (4 preceding siblings ...)
  2024-01-30 13:26   ` [PATCH v2 2/4] services: certbot: Create self-signed certificates before certbot runs Carlo Zancanaro
@ 2024-01-30 13:26   ` Carlo Zancanaro
  2024-01-31  0:29     ` bug#46961: Nginx and certbot cervices don't play well togther Clément Lassieur
  2024-01-30 13:26     ` Carlo Zancanaro
  6 siblings, 1 reply; 33+ messages in thread
From: Carlo Zancanaro @ 2024-01-30 13:26 UTC (permalink / raw)
  To: 46961; +Cc: clement, brice, guix-devel

* gnu/services/certbot.scm (%default-deploy-hook): New variable.
(<certificate-configuration>)[deploy-hook]: Use it as default deploy hook.
* doc/guix.texi (Certificate services): Document new default deploy hook.

Change-Id: Ibb10481170a6fda7df72492072b939dd6a6ad176
---
 doc/guix.texi            |  6 +++++-
 gnu/services/certbot.scm | 13 +++++++++++--
 2 files changed, 16 insertions(+), 3 deletions(-)

diff --git a/doc/guix.texi b/doc/guix.texi
index 58a65fe0b7..0f372a460f 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -32681,7 +32681,7 @@ Certificate Services
 additionally @code{$CERTBOT_AUTH_OUTPUT} will contain the standard output
 of the @code{auth-hook} script.
 
-@item @code{deploy-hook} (default: @code{#f})
+@item @code{deploy-hook} (default: @code{%default-deploy-hook})
 Command to be run in a shell once for each successfully issued
 certificate.  For this command, the shell variable
 @code{$RENEWED_LINEAGE} will point to the config live subdirectory (for
@@ -32690,6 +32690,10 @@ Certificate Services
 contain a space-delimited list of renewed certificate domains (for
 example, @samp{"example.com www.example.com"}.
 
+The default deploy hook calls the @code{reload} action of the
+@code{nginx} Shepherd service, to reload the newly generated
+certificates.
+
 @item @code{start-self-signed?} (default: @code{#t})
 Whether to generate an initial self-signed certificate during system
 activation.  This option is particularly useful to allow @code{nginx} to
diff --git a/gnu/services/certbot.scm b/gnu/services/certbot.scm
index 10b99f5630..490b9e8d6d 100644
--- a/gnu/services/certbot.scm
+++ b/gnu/services/certbot.scm
@@ -37,7 +37,8 @@ (define-module (gnu services certbot)
   #:use-module (srfi srfi-1)
   #:use-module (ice-9 format)
   #:use-module (ice-9 match)
-  #:export (certbot-service-type
+  #:export (%default-deploy-hook
+            certbot-service-type
             certbot-configuration
             certbot-configuration?
             certificate-configuration))
@@ -49,6 +50,14 @@ (define-module (gnu services certbot)
 ;;; Code:
 
 \f
+(define %default-deploy-hook
+  (program-file
+   "reload-nginx.scm"
+   (with-imported-modules '((gnu services herd))
+     #~(begin
+         (use-modules (gnu services herd))
+         (with-shepherd-action 'nginx ('reload) result result)))))
+
 (define-record-type* <certificate-configuration>
   certificate-configuration make-certificate-configuration
   certificate-configuration?
@@ -65,7 +74,7 @@ (define-record-type* <certificate-configuration>
   (cleanup-hook        certificate-cleanup-hook
                        (default #f))
   (deploy-hook         certificate-configuration-deploy-hook
-                       (default #f))
+                       (default %default-deploy-hook))
   (start-self-signed?  certificate-configuration-start-self-signed?
                        (default #t)))
 
-- 
2.41.0



^ permalink raw reply related	[flat|nested] 33+ messages in thread

* bug#46961: [PATCH v2 4/4] services: certbot: Add one-shot service to renew certificates.
  2024-01-24 12:18 ` bug#46961: [PATCH 0/2] Allow nginx to start before certbot has run Carlo Zancanaro
@ 2024-01-30 13:26     ` Carlo Zancanaro
  2024-01-24 12:18   ` bug#46961: [PATCH 2/2] services: certbot: Create self-signed certificates before certbot runs Carlo Zancanaro
                       ` (5 subsequent siblings)
  6 siblings, 0 replies; 33+ messages in thread
From: Carlo Zancanaro @ 2024-01-30 13:26 UTC (permalink / raw)
  To: 46961; +Cc: guix-devel, brice, clement

* gnu/services/certbot.scm (certbot-renewal-one-shot): New procedure.
(certbot-service-type)[extensions]: Add it to shepherd-root extension.
(certbot-command): Make connection errors return a different exit code.
(certbot-activation): Remove message with certificate renewal instructions.

Change-Id: I614ac6214a753dba0396e2385a75926c8355caa1
---
 gnu/services/certbot.scm | 77 +++++++++++++++++++++++++++++++++-------
 1 file changed, 65 insertions(+), 12 deletions(-)

diff --git a/gnu/services/certbot.scm b/gnu/services/certbot.scm
index 490b9e8d6d..d6354c86d3 100644
--- a/gnu/services/certbot.scm
+++ b/gnu/services/certbot.scm
@@ -183,15 +183,37 @@ (define certbot-command
        (program-file
         "certbot-command"
         #~(begin
-            (use-modules (ice-9 match))
-            (let ((code 0))
+            (use-modules (ice-9 match)
+                         (ice-9 textual-ports))
+
+            (define (file-contains? file string)
+              (string-contains (call-with-input-file file
+                                 get-string-all)
+                               string))
+
+            (define (connection-error?)
+              (file-contains? "/var/log/letsencrypt/letsencrypt.log"
+                              "Failed to establish a new connection"))
+
+            (let ((script-code 0))
               (for-each
                (match-lambda
                  ((name . command)
                   (begin
                     (format #t "Acquiring or renewing certificate: ~a~%" name)
-                    (set! code (or (apply system* command) code)))))
-               '#$commands) code)))))))
+                    (unless (zero? (status:exit-val (apply system* command)))
+                      ;; Certbot errors are always exit code 1, but we'd like
+                      ;; to separate connection errors from other error types.
+                      (if (connection-error?)
+                          ;; If we have a connection error, then bail early
+                          ;; with exit code 2. We don't expect this to
+                          ;; resolve within the timespan of this script.
+                          (exit 2)
+                          ;; If we have any other type of error, then continue
+                          ;; but exit with a failing status code in the end.
+                          (set! script-code 1))))))
+               '#$commands)
+              (exit script-code))))))))
 
 (define (certbot-renewal-jobs config)
   (list
@@ -200,6 +222,40 @@ (define (certbot-renewal-jobs config)
    #~(job '(next-minute-from (next-hour '(0 12)) (list (random 60)))
           #$(certbot-command config))))
 
+(define (certbot-renewal-one-shot config)
+  (list
+   ;; Renew certificates when the system first starts. This is a one-shot
+   ;; service, because the mcron configuration will take care of running this
+   ;; periodically. This is most useful the very first time the system starts,
+   ;; to overwrite our self-signed certificates as soon as possible without
+   ;; user intervention.
+   (shepherd-service
+    (provision '(renew-certbot-certificates))
+    (requirement '(nginx))
+    (one-shot? #t)
+    (start #~(lambda _
+               ;; This needs the network, but there's no reliable way to know
+               ;; if the network is up other than trying. If we fail due to a
+               ;; connection error we retry a number of times in the hope that
+               ;; the network comes up soon.
+               (let loop ((attempt 0))
+                 (let ((code (status:exit-val
+                              (system* #$(certbot-command config)))))
+                   (cond
+                    ((and (= code 2)      ; Exit code 2 means connection error
+                          (< attempt 12)) ; 12 * 10 seconds = 2 minutes
+                     (sleep 10)
+                     (loop (1+ attempt)))
+                    ((zero? code)
+                     ;; Success!
+                     #t)
+                    (else
+                     ;; Failure.
+                     #f))))))
+    (auto-start? #t)
+    (documentation "Call certbot to renew certificates.")
+    (actions (list (shepherd-configuration-action (certbot-command config)))))))
+
 (define (generate-certificate-gexp certbot-cert-directory rsa-key-size)
   (match-lambda
     (($ <certificate-configuration> name (primary-domain other-domains ...)
@@ -243,9 +299,7 @@ (define (generate-certificate-gexp certbot-cert-directory rsa-key-size)
 
 (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)))
+         (certbot-cert-directory "/etc/letsencrypt/live"))
     (match config
       (($ <certbot-configuration> package webroot certificates email
                                   server rsa-key-size default-location)
@@ -261,10 +315,7 @@ (define (certbot-activation config)
                   (map (generate-certificate-gexp certbot-cert-directory
                                                   rsa-key-size)
                        (filter certificate-configuration-start-self-signed?
-                               certificates)))
-
-             (copy-file #$(certbot-command config) #$script)
-             (display #$message)))))))
+                               certificates)))))))))
 
 (define certbot-nginx-server-configurations
   (match-lambda
@@ -297,7 +348,9 @@ (define certbot-service-type
                        (service-extension activation-service-type
                                           certbot-activation)
                        (service-extension mcron-service-type
-                                          certbot-renewal-jobs)))
+                                          certbot-renewal-jobs)
+                       (service-extension shepherd-root-service-type
+                                          certbot-renewal-one-shot)))
                 (compose concatenate)
                 (extend (lambda (config additional-certificates)
                           (certbot-configuration
-- 
2.41.0





^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH v2 4/4] services: certbot: Add one-shot service to renew certificates.
@ 2024-01-30 13:26     ` Carlo Zancanaro
  0 siblings, 0 replies; 33+ messages in thread
From: Carlo Zancanaro @ 2024-01-30 13:26 UTC (permalink / raw)
  To: 46961; +Cc: clement, brice, guix-devel

* gnu/services/certbot.scm (certbot-renewal-one-shot): New procedure.
(certbot-service-type)[extensions]: Add it to shepherd-root extension.
(certbot-command): Make connection errors return a different exit code.
(certbot-activation): Remove message with certificate renewal instructions.

Change-Id: I614ac6214a753dba0396e2385a75926c8355caa1
---
 gnu/services/certbot.scm | 77 +++++++++++++++++++++++++++++++++-------
 1 file changed, 65 insertions(+), 12 deletions(-)

diff --git a/gnu/services/certbot.scm b/gnu/services/certbot.scm
index 490b9e8d6d..d6354c86d3 100644
--- a/gnu/services/certbot.scm
+++ b/gnu/services/certbot.scm
@@ -183,15 +183,37 @@ (define certbot-command
        (program-file
         "certbot-command"
         #~(begin
-            (use-modules (ice-9 match))
-            (let ((code 0))
+            (use-modules (ice-9 match)
+                         (ice-9 textual-ports))
+
+            (define (file-contains? file string)
+              (string-contains (call-with-input-file file
+                                 get-string-all)
+                               string))
+
+            (define (connection-error?)
+              (file-contains? "/var/log/letsencrypt/letsencrypt.log"
+                              "Failed to establish a new connection"))
+
+            (let ((script-code 0))
               (for-each
                (match-lambda
                  ((name . command)
                   (begin
                     (format #t "Acquiring or renewing certificate: ~a~%" name)
-                    (set! code (or (apply system* command) code)))))
-               '#$commands) code)))))))
+                    (unless (zero? (status:exit-val (apply system* command)))
+                      ;; Certbot errors are always exit code 1, but we'd like
+                      ;; to separate connection errors from other error types.
+                      (if (connection-error?)
+                          ;; If we have a connection error, then bail early
+                          ;; with exit code 2. We don't expect this to
+                          ;; resolve within the timespan of this script.
+                          (exit 2)
+                          ;; If we have any other type of error, then continue
+                          ;; but exit with a failing status code in the end.
+                          (set! script-code 1))))))
+               '#$commands)
+              (exit script-code))))))))
 
 (define (certbot-renewal-jobs config)
   (list
@@ -200,6 +222,40 @@ (define (certbot-renewal-jobs config)
    #~(job '(next-minute-from (next-hour '(0 12)) (list (random 60)))
           #$(certbot-command config))))
 
+(define (certbot-renewal-one-shot config)
+  (list
+   ;; Renew certificates when the system first starts. This is a one-shot
+   ;; service, because the mcron configuration will take care of running this
+   ;; periodically. This is most useful the very first time the system starts,
+   ;; to overwrite our self-signed certificates as soon as possible without
+   ;; user intervention.
+   (shepherd-service
+    (provision '(renew-certbot-certificates))
+    (requirement '(nginx))
+    (one-shot? #t)
+    (start #~(lambda _
+               ;; This needs the network, but there's no reliable way to know
+               ;; if the network is up other than trying. If we fail due to a
+               ;; connection error we retry a number of times in the hope that
+               ;; the network comes up soon.
+               (let loop ((attempt 0))
+                 (let ((code (status:exit-val
+                              (system* #$(certbot-command config)))))
+                   (cond
+                    ((and (= code 2)      ; Exit code 2 means connection error
+                          (< attempt 12)) ; 12 * 10 seconds = 2 minutes
+                     (sleep 10)
+                     (loop (1+ attempt)))
+                    ((zero? code)
+                     ;; Success!
+                     #t)
+                    (else
+                     ;; Failure.
+                     #f))))))
+    (auto-start? #t)
+    (documentation "Call certbot to renew certificates.")
+    (actions (list (shepherd-configuration-action (certbot-command config)))))))
+
 (define (generate-certificate-gexp certbot-cert-directory rsa-key-size)
   (match-lambda
     (($ <certificate-configuration> name (primary-domain other-domains ...)
@@ -243,9 +299,7 @@ (define (generate-certificate-gexp certbot-cert-directory rsa-key-size)
 
 (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)))
+         (certbot-cert-directory "/etc/letsencrypt/live"))
     (match config
       (($ <certbot-configuration> package webroot certificates email
                                   server rsa-key-size default-location)
@@ -261,10 +315,7 @@ (define (certbot-activation config)
                   (map (generate-certificate-gexp certbot-cert-directory
                                                   rsa-key-size)
                        (filter certificate-configuration-start-self-signed?
-                               certificates)))
-
-             (copy-file #$(certbot-command config) #$script)
-             (display #$message)))))))
+                               certificates)))))))))
 
 (define certbot-nginx-server-configurations
   (match-lambda
@@ -297,7 +348,9 @@ (define certbot-service-type
                        (service-extension activation-service-type
                                           certbot-activation)
                        (service-extension mcron-service-type
-                                          certbot-renewal-jobs)))
+                                          certbot-renewal-jobs)
+                       (service-extension shepherd-root-service-type
+                                          certbot-renewal-one-shot)))
                 (compose concatenate)
                 (extend (lambda (config additional-certificates)
                           (certbot-configuration
-- 
2.41.0



^ permalink raw reply related	[flat|nested] 33+ messages in thread

* Re: [PATCH v2 0/4] Make certbot play more nicely with nginx
  2024-01-30 13:26     ` Carlo Zancanaro
  (?)
@ 2024-01-30 14:49     ` Felix Lechner via Development of GNU Guix and the GNU System distribution.
  2024-01-30 21:48       ` Carlo Zancanaro
  -1 siblings, 1 reply; 33+ messages in thread
From: Felix Lechner via Development of GNU Guix and the GNU System distribution. @ 2024-01-30 14:49 UTC (permalink / raw)
  To: Carlo Zancanaro, 46961; +Cc: clement, brice, guix-devel

Hi Carlo,

On Tue, Jan 30 2024, Carlo Zancanaro wrote:

> certbot can't produce certificates without a functional nginx

Yes, it can. The option is called --standalone. [1]

Maybe another way to bootstrap the certificates would be to hold off on
starting Nginx or Apache until all certificates are obtained?

Anyway, that's what I do manually.

Kind regards
Felix

[1] https://eff-certbot.readthedocs.io/en/latest/using.html#standalone


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: bug#46961: [PATCH v2 0/4] Make certbot play more nicely with nginx
       [not found]     ` <875xzanaer.fsf__22488.5524179385$1706626282$gmane$org@lease-up.com>
@ 2024-01-30 19:39       ` Clément Lassieur
  2024-04-13  1:17         ` Felix Lechner via Development of GNU Guix and the GNU System distribution.
  0 siblings, 1 reply; 33+ messages in thread
From: Clément Lassieur @ 2024-01-30 19:39 UTC (permalink / raw)
  To: Felix Lechner via Bug reports for GNU Guix
  Cc: Carlo Zancanaro, 46961, Felix Lechner, guix-devel, brice

On Tue, Jan 30 2024, Felix Lechner via Bug reports for GNU Guix wrote:

> Hi Carlo,
>
> On Tue, Jan 30 2024, Carlo Zancanaro wrote:
>
>> certbot can't produce certificates without a functional nginx
>
> Yes, it can. The option is called --standalone. [1]
>
> Maybe another way to bootstrap the certificates would be to hold off on
> starting Nginx or Apache until all certificates are obtained?

Yes but if we do this and there is no internet, nginx won't start right?

Carlo's solution allows to have a working nginx even when certbot fails.

(If I understand well)

> Anyway, that's what I do manually.
>
> Kind regards
> Felix
>
> [1] https://eff-certbot.readthedocs.io/en/latest/using.html#standalone


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v2 0/4] Make certbot play more nicely with nginx
  2024-01-30 14:49     ` Felix Lechner via Development of GNU Guix and the GNU System distribution.
@ 2024-01-30 21:48       ` Carlo Zancanaro
  2024-01-31  0:04         ` Wojtek Kosior via Development of GNU Guix and the GNU System distribution.
  0 siblings, 1 reply; 33+ messages in thread
From: Carlo Zancanaro @ 2024-01-30 21:48 UTC (permalink / raw)
  To: Felix Lechner; +Cc: 46961, clement, brice, guix-devel

Hi Felix,

On Tue, Jan 30 2024, Felix Lechner wrote:
> On Tue, Jan 30 2024, Carlo Zancanaro wrote:
>> certbot can't produce certificates without a functional nginx
>
> Yes, it can. The option is called --standalone. [1]

You are correct, of course. If I had been more precise I would 
have said "with our current configuration, certbot can't produce 
certificates without a functional nginx".

> Maybe another way to bootstrap the certificates would be to hold 
> off on starting Nginx or Apache until all certificates are 
> obtained?

This could work, but I see a few downsides.

As Clément has already mentioned, this would make nginx dependent 
on certbot. This causes problems for servers disconnected from the 
general internet, but it also shifts complexity into the nginx 
service without much benefit over the patch series I'm proposing. 
We'd need to add more configuration on the nginx side to control 
whether to delay startup based on whether we actually want 
certificates. This would delay the startup of the whole nginx 
process, even if some server configurations don't require new 
certificates.

For renewal, we would also have two options: (1) use --standalone, 
and require a period of downtime for our web server; or (2) use 
--webroot, and maintain two code paths for the two cases. I think 
it's a bad idea for Guix to make a decision that requires downtime 
of user systems if there's an alternative, so I don't like (1). 
Maintaining two "similar but different" code paths for (2) doesn't 
seem like a clear advantage over the patch series I'm proposing.

> Anyway, that's what I do manually.

I use the DNS challenge type, with hooks which automatically 
create/remove DNS records. This solves all the problems I'm 
bringing up (i.e. doesn't require nginx, doesn't involve downtime, 
has a single code path), but I don't think Guix can assume that 
all users have the ability to do this. My aim with this patch 
series is to make the default certbot configuration work for the 
common case of a simple web server, without manual intervention.

Carlo


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v2 0/4] Make certbot play more nicely with nginx
  2024-01-30 21:48       ` Carlo Zancanaro
@ 2024-01-31  0:04         ` Wojtek Kosior via Development of GNU Guix and the GNU System distribution.
  0 siblings, 0 replies; 33+ messages in thread
From: Wojtek Kosior via Development of GNU Guix and the GNU System distribution. @ 2024-01-31  0:04 UTC (permalink / raw)
  To: Carlo Zancanaro; +Cc: Felix Lechner, 46961, clement, brice, guix-devel

[-- Attachment #1: Type: text/plain, Size: 3985 bytes --]

I sympathize with your approach (I, too, have been supplementing
Certbot with self-signed certs for some time).

What would also be cool is not to have `certbot-service-type` depend on
`nginx-service-type` in the first place.  So that one can more easily
use another HTTP server.  It can of course be achieved with
`remove-service-extensions` from `(gnu services)`, it's just less
elegant than having it supported directly.

Perhaps some variant of the "dependency inversion principle" would fit
here?  How about the following set of service types?

- certbot-tool-service-type — does what `certbot-service-type` used to
  do until now except it doesn't extend `nginx-service-type` and
  can itself be extended with not just `<certificate-configuration>`s
  but also `<certbot-configuration>`
- certbot/nginx-service-type — takes in `<certbot-configuration>`,
  extends both `certbot-tool-service-type` and `nginx-service-type`
- certbot/httpd-service-type — takes in `<certbot-configuration>`,
  extends both `certbot-tool-service-type` and `httpd-service-type`
- certbot-service-type — deprecated, functions as an alias for
  `certbot/nginx-service-type`

Your proposals are of course useful as well, regardless of this being
done

Best :)
Wojtek


-- (sig_start)
website: https://koszko.org/koszko.html
fingerprint: E972 7060 E3C5 637C 8A4F  4B42 4BC5 221C 5A79 FD1A
follow me on Fediverse: https://friendica.me/profile/koszko/profile

♥ R29kIGlzIHRoZXJlIGFuZCBsb3ZlcyBtZQ== | ÷ c2luIHNlcGFyYXRlZCBtZSBmcm9tIEhpbQ==
✝ YnV0IEplc3VzIGRpZWQgdG8gc2F2ZSBtZQ== | ? U2hhbGwgSSBiZWNvbWUgSGlzIGZyaWVuZD8=
-- (sig_end)


On Wed, 31 Jan 2024 08:48:54 +1100 Carlo Zancanaro <carlo@zancanaro.id.au> wrote:

> Hi Felix,
> 
> On Tue, Jan 30 2024, Felix Lechner wrote:
> > On Tue, Jan 30 2024, Carlo Zancanaro wrote:  
> >> certbot can't produce certificates without a functional nginx  
> >
> > Yes, it can. The option is called --standalone. [1]  
> 
> You are correct, of course. If I had been more precise I would 
> have said "with our current configuration, certbot can't produce 
> certificates without a functional nginx".
> 
> > Maybe another way to bootstrap the certificates would be to hold 
> > off on starting Nginx or Apache until all certificates are 
> > obtained?  
> 
> This could work, but I see a few downsides.
> 
> As Clément has already mentioned, this would make nginx dependent 
> on certbot. This causes problems for servers disconnected from the 
> general internet, but it also shifts complexity into the nginx 
> service without much benefit over the patch series I'm proposing. 
> We'd need to add more configuration on the nginx side to control 
> whether to delay startup based on whether we actually want 
> certificates. This would delay the startup of the whole nginx 
> process, even if some server configurations don't require new 
> certificates.
> 
> For renewal, we would also have two options: (1) use --standalone, 
> and require a period of downtime for our web server; or (2) use 
> --webroot, and maintain two code paths for the two cases. I think 
> it's a bad idea for Guix to make a decision that requires downtime 
> of user systems if there's an alternative, so I don't like (1). 
> Maintaining two "similar but different" code paths for (2) doesn't 
> seem like a clear advantage over the patch series I'm proposing.
> 
> > Anyway, that's what I do manually.  
> 
> I use the DNS challenge type, with hooks which automatically 
> create/remove DNS records. This solves all the problems I'm 
> bringing up (i.e. doesn't require nginx, doesn't involve downtime, 
> has a single code path), but I don't think Guix can assume that 
> all users have the ability to do this. My aim with this patch 
> series is to make the default certbot configuration work for the 
> common case of a simple web server, without manual intervention.
> 
> Carlo
> 

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 33+ messages in thread

* bug#46961: Nginx and certbot cervices don't play well togther
  2024-01-30 13:26   ` [PATCH v2 3/4] services: certbot: Add a default deploy hook to reload nginx Carlo Zancanaro
@ 2024-01-31  0:29     ` Clément Lassieur
  0 siblings, 0 replies; 33+ messages in thread
From: Clément Lassieur @ 2024-01-31  0:29 UTC (permalink / raw)
  To: Carlo Zancanaro; +Cc: brice, 46961

I removed guix-devel, not sure we need to spam it.

On Tue, Jan 30 2024, Carlo Zancanaro wrote:

> +(define %default-deploy-hook
> +  (program-file
> +   "reload-nginx.scm"
> +   (with-imported-modules '((gnu services herd))
> +     #~(begin
> +         (use-modules (gnu services herd))
> +         (with-shepherd-action 'nginx ('reload) result result)))))
> +
>  (define-record-type* <certificate-configuration>
>    certificate-configuration make-certificate-configuration
>    certificate-configuration?
> @@ -65,7 +74,7 @@ (define-record-type* <certificate-configuration>
>    (cleanup-hook        certificate-cleanup-hook
>                         (default #f))
>    (deploy-hook         certificate-configuration-deploy-hook
> -                       (default #f))
> +                       (default %default-deploy-hook))
>    (start-self-signed?  certificate-configuration-start-self-signed?
>                         (default #t)))

I'd reload within ‘certbot-deploy-hook’, between ‘rename-file’ and “(if
deploy-hook-script” so that people don't get surprised, when they use a
deploy-hook for unrelated reasons, that the nginx doesn't reload
anymore.

Plus, reloading nginx is harmless.




^ permalink raw reply	[flat|nested] 33+ messages in thread

* bug#46961: Nginx and certbot cervices don't play well togther
  2024-01-30 13:26     ` Carlo Zancanaro
  (?)
@ 2024-01-31  0:55     ` Clément Lassieur
  2024-01-31 11:50       ` Carlo Zancanaro
  -1 siblings, 1 reply; 33+ messages in thread
From: Clément Lassieur @ 2024-01-31  0:55 UTC (permalink / raw)
  To: Carlo Zancanaro; +Cc: brice, 46961

Removing guix-devel.

On Tue, Jan 30 2024, Carlo Zancanaro wrote:

> +            (define (file-contains? file string)
> +              (string-contains (call-with-input-file file
> +                                 get-string-all)
> +                               string))
> +
> +            (define (connection-error?)
> +              (file-contains? "/var/log/letsencrypt/letsencrypt.log"
> +                              "Failed to establish a new connection"))
> +
> +            (let ((script-code 0))
>                (for-each
>                 (match-lambda
>                   ((name . command)
>                    (begin
>                      (format #t "Acquiring or renewing certificate: ~a~%" name)

Here we could add ‘(force-output)’, because otherwise those logs arrive
after the certbot logs, and it's hard to understand anything.

> -                    (set! code (or (apply system* command) code)))))
> -               '#$commands) code)))))))
> +                    (unless (zero? (status:exit-val (apply system* command)))
> +                      ;; Certbot errors are always exit code 1, but we'd like
> +                      ;; to separate connection errors from other error types.
> +                      (if (connection-error?)
> +                          ;; If we have a connection error, then bail early
> +                          ;; with exit code 2. We don't expect this to
> +                          ;; resolve within the timespan of this script.

Could we have a (log + force-output) here too?  (I imagine within a
‘begin’)
> +                          (exit 2)
> +                          ;; If we have any other type of error, then continue
> +                          ;; but exit with a failing status code in the end.

and here?
> +                          (set! script-code 1))))))

And maybe a log also in case the command succeeds.  (So that would mean
to replace ‘unless’ with ‘if’).

> +               '#$commands)
> +              (exit script-code))))))))
>  
> +               (let loop ((attempt 0))
> +                 (let ((code (status:exit-val
> +                              (system* #$(certbot-command config)))))
> +                   (cond
> +                    ((and (= code 2)      ; Exit code 2 means connection error
> +                          (< attempt 12)) ; 12 * 10 seconds = 2 minutes
                                                                 ^------
This comment is not true because certbot takes time to execute (around
15s on my vm).  I don't think there is a need to be that precise.  Maybe
you can just add in in the let form, as in (let ((code ...)
                                                 (max-attempts 12)).

> +                     (sleep 10)
> +                     (loop (1+ attempt)))
> +                    ((zero? code)
> +                     ;; Success!
> +                     #t)
> +                    (else
> +                     ;; Failure.
> +                     #f))))))

Also could you update the example in the docs?

From the doc:
>> @defvar certbot-service-type
>> A service type for the @code{certbot} Let's Encrypt client.  Its value
>> must be a @code{certbot-configuration} record as in this example:
>> 
>> @lisp
>> (define %certbot-deploy-hook
>>   (program-file "certbot-deploy-hook.scm"
>>     (with-imported-modules '((gnu services herd))
>>       #~(begin
>>           (use-modules (gnu services herd))
>>           (with-shepherd-action 'nginx ('reload) result result)))))
                   ^
This part isn't useful anymore.  However, we could add a
nginx-service-type and a dhcp-client-service-type so that people have an
idea of what the minimal config is, maybe like I did in my first review:
https://debbugs.gnu.org/cgi/bugreport.cgi?bug=46961#23.

>> (service certbot-service-type
>>          (certbot-configuration
>>           (email "foo@@example.net")
>>           (certificates
>>            (list
>>             (certificate-configuration
>>              (domains '("example.net" "www.example.net"))
>>              (deploy-hook %certbot-deploy-hook))
>>             (certificate-configuration
>>              (domains '("bar.example.net")))))))
>> @end lisp

We are almost there, thanks!
Clément




^ permalink raw reply	[flat|nested] 33+ messages in thread

* bug#46961: [PATCH v3 0/4] Make certbot play more nicely with nginx
  2024-01-30 13:26     ` Carlo Zancanaro
                       ` (2 preceding siblings ...)
  (?)
@ 2024-01-31 11:46     ` Carlo Zancanaro
  -1 siblings, 0 replies; 33+ messages in thread
From: Carlo Zancanaro @ 2024-01-31 11:46 UTC (permalink / raw)
  To: 46961; +Cc: clement

Carlo Zancanaro (4):
  services: certbot: Symlink certificates to /etc/certs.
  services: certbot: Create self-signed certificates before certbot
    runs.
  services: certbot: Reload nginx in deploy hook.
  services: certbot: Add one-shot service to renew certificates.

 doc/guix.texi            |  40 ++++-----
 gnu/services/certbot.scm | 185 +++++++++++++++++++++++++++++++++++----
 2 files changed, 189 insertions(+), 36 deletions(-)


base-commit: 7a45f7b9e1b34912ee087daf4014aa4f67b11bf0
-- 
2.41.0





^ permalink raw reply	[flat|nested] 33+ messages in thread

* bug#46961: [PATCH v3 1/4] services: certbot: Symlink certificates to /etc/certs.
  2024-01-30 13:26     ` Carlo Zancanaro
                       ` (3 preceding siblings ...)
  (?)
@ 2024-01-31 11:46     ` Carlo Zancanaro
  -1 siblings, 0 replies; 33+ messages in thread
From: Carlo Zancanaro @ 2024-01-31 11:46 UTC (permalink / raw)
  To: 46961; +Cc: clement

* gnu/services/certbot.scm (certbot-deploy-hook): New procedure.
(certbot-command): Pass new deploy hook to certbot.
* doc/guix.texi: Replace "letsencrypt/live" with "certs" throughout, except in
the certbot deploy-hook description.

Change-Id: I2ba5e4903d1e293e566b732a84b07d5a134b697d
---
 doc/guix.texi            | 24 ++++++++++++------------
 gnu/services/certbot.scm | 36 ++++++++++++++++++++++++++++++++++--
 2 files changed, 46 insertions(+), 14 deletions(-)

diff --git a/doc/guix.texi b/doc/guix.texi
index bb0af26d93..97be37f9b5 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -43,7 +43,7 @@
 Copyright @copyright{} 2017, 2018, 2019 Clément Lassieur@*
 Copyright @copyright{} 2017, 2018, 2020, 2021, 2022 Mathieu Othacehe@*
 Copyright @copyright{} 2017 Federico Beffa@*
-Copyright @copyright{} 2017, 2018 Carlo Zancanaro@*
+Copyright @copyright{} 2017, 2018, 2024 Carlo Zancanaro@*
 Copyright @copyright{} 2017 Thomas Danckaert@*
 Copyright @copyright{} 2017 humanitiesNerd@*
 Copyright @copyright{} 2017, 2021 Christine Lemmer-Webber@*
@@ -28135,7 +28135,7 @@ Messaging Services
 them.  See @url{https://prosody.im/doc/letsencrypt}.
 
 @example
-prosodyctl --root cert import /etc/letsencrypt/live
+prosodyctl --root cert import /etc/certs
 @end example
 
 The available configuration parameters follow.  Each parameter
@@ -28846,8 +28846,8 @@ Telephony Services
           (welcome-text
             "Welcome to this Mumble server running on Guix!")
           (cert-required? #t) ;disallow text password logins
-          (ssl-cert "/etc/letsencrypt/live/mumble.example.com/fullchain.pem")
-          (ssl-key "/etc/letsencrypt/live/mumble.example.com/privkey.pem")))
+          (ssl-cert "/etc/certs/mumble.example.com/fullchain.pem")
+          (ssl-key "/etc/certs/mumble.example.com/privkey.pem")))
 @end lisp
 
 After reconfiguring your system, you can manually set the mumble-server
@@ -28965,12 +28965,12 @@ Telephony Services
 File name of the SSL/TLS certificate used for encrypted connections.
 
 @lisp
-(ssl-cert "/etc/letsencrypt/live/example.com/fullchain.pem")
+(ssl-cert "/etc/certs/example.com/fullchain.pem")
 @end lisp
 @item @code{ssl-key} (default: @code{#f})
 Filepath to the ssl private key used for encrypted connections.
 @lisp
-(ssl-key "/etc/letsencrypt/live/example.com/privkey.pem")
+(ssl-key "/etc/certs/example.com/privkey.pem")
 @end lisp
 
 @item @code{ssl-dh-params} (default: @code{#f})
@@ -32694,8 +32694,8 @@ Certificate Services
 @end deftp
 
 For each @code{certificate-configuration}, the certificate is saved to
-@code{/etc/letsencrypt/live/@var{name}/fullchain.pem} and the key is
-saved to @code{/etc/letsencrypt/live/@var{name}/privkey.pem}.
+@code{/etc/certs/@var{name}/fullchain.pem} and the key is
+saved to @code{/etc/certs/@var{name}/privkey.pem}.
 @node DNS Services
 @subsection DNS Services
 @cindex DNS (domain name system)
@@ -37381,9 +37381,9 @@ Version Control Services
              (listen '("443 ssl"))
              (server-name "git.my-host.org")
              (ssl-certificate
-              "/etc/letsencrypt/live/git.my-host.org/fullchain.pem")
+              "/etc/certs/git.my-host.org/fullchain.pem")
              (ssl-certificate-key
-              "/etc/letsencrypt/live/git.my-host.org/privkey.pem")
+              "/etc/certs/git.my-host.org/privkey.pem")
              (locations
               (list
                (git-http-nginx-location-configuration
@@ -38508,9 +38508,9 @@ Version Control Services
            (nginx-server-block
              (nginx-server-configuration
                (ssl-certificate
-                 "/etc/letsencrypt/live/myweb.site/fullchain.pem")
+                 "/etc/certs/myweb.site/fullchain.pem")
                (ssl-certificate-key
-                 "/etc/letsencrypt/live/myweb.site/privkey.pem")
+                 "/etc/certs/myweb.site/privkey.pem")
                (listen '("443 ssl http2" "[::]:443 ssl http2"))
                (locations
                  (list
diff --git a/gnu/services/certbot.scm b/gnu/services/certbot.scm
index 0c45471659..3926d0551a 100644
--- a/gnu/services/certbot.scm
+++ b/gnu/services/certbot.scm
@@ -6,6 +6,7 @@
 ;;; Copyright © 2020 Jack Hill <jackhill@jackhill.us>
 ;;; Copyright © 2020 Tobias Geerinckx-Rice <me@tobias.gr>
 ;;; Copyright © 2021 Raghav Gururajan <rg@raghavgururajan.name>
+;;; Copyright © 2024 Carlo Zancanaro <carlo@zancanaro.id.au>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -87,6 +88,35 @@ (define-record-type* <certbot-configuration>
                           (body
                            (list "return 301 https://$host$request_uri;"))))))
 
+(define (certbot-deploy-hook name deploy-hook-script)
+  "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."
+  (program-file
+   (string-append name "-deploy-hook")
+   (with-imported-modules '((guix build utils))
+     #~(begin
+         (use-modules (guix build utils))
+         (mkdir-p #$(string-append "/etc/certs/" name))
+         (chmod #$(string-append "/etc/certs/" name) #o755)
+
+         ;; Create new symlinks
+         (symlink #$(string-append
+                     "/etc/letsencrypt/live/" name "/privkey.pem")
+                  #$(string-append "/etc/certs/" name "/privkey.pem.new"))
+         (symlink #$(string-append
+                     "/etc/letsencrypt/live/" name "/fullchain.pem")
+                  #$(string-append "/etc/certs/" name "/fullchain.pem.new"))
+
+         ;; Rename over the top of the old ones, if there are any.
+         (rename-file #$(string-append "/etc/certs/" name "/privkey.pem.new")
+                      #$(string-append "/etc/certs/" name "/privkey.pem"))
+         (rename-file #$(string-append "/etc/certs/" name "/fullchain.pem.new")
+                      #$(string-append "/etc/certs/" name "/fullchain.pem"))
+         #$@(if deploy-hook-script
+                (list #~(invoke #$deploy-hook-script))
+                '())))))
+
 (define certbot-command
   (match-lambda
     (($ <certbot-configuration> package webroot certificates email
@@ -118,7 +148,8 @@ (define certbot-command
                           `("--manual-auth-hook" ,authentication-hook)
                           '())
                       (if cleanup-hook `("--manual-cleanup-hook" ,cleanup-hook) '())
-                      (if deploy-hook `("--deploy-hook" ,deploy-hook) '()))
+                      (list "--deploy-hook"
+                            (certbot-deploy-hook name deploy-hook)))
                      (append
                       (list name certbot "certonly" "-n" "--agree-tos"
                             "--webroot" "-w" webroot
@@ -130,7 +161,8 @@ (define certbot-command
                           '("--register-unsafely-without-email"))
                       (if server `("--server" ,server) '())
                       (if rsa-key-size `("--rsa-key-size" ,rsa-key-size) '())
-                      (if deploy-hook `("--deploy-hook" ,deploy-hook) '()))))))
+                      (list "--deploy-hook"
+                            (certbot-deploy-hook name deploy-hook)))))))
               certificates)))
        (program-file
         "certbot-command"
-- 
2.41.0





^ permalink raw reply related	[flat|nested] 33+ messages in thread

* bug#46961: [PATCH v3 2/4] services: certbot: Create self-signed certificates before certbot runs.
  2024-01-30 13:26     ` Carlo Zancanaro
                       ` (4 preceding siblings ...)
  (?)
@ 2024-01-31 11:46     ` Carlo Zancanaro
  -1 siblings, 0 replies; 33+ messages in thread
From: Carlo Zancanaro @ 2024-01-31 11:46 UTC (permalink / raw)
  To: 46961; +Cc: clement

* gnu/services/certbot.scm (<certificate-configuration>): Add
start-self-signed? field.
(generate-certificate-gexp): New procedure.
(certbot-activation): Generate self-signed certificates when
start-self-signed? is #t.
* doc/guix.texi (Certificate services): Document start-self-signed?.

Change-Id: Icfd85ae0c3e29324acbcde6ba283546cf0e27a1d
---
 doc/guix.texi            |  6 ++++
 gnu/services/certbot.scm | 62 ++++++++++++++++++++++++++++++++++++++--
 2 files changed, 65 insertions(+), 3 deletions(-)

diff --git a/doc/guix.texi b/doc/guix.texi
index 97be37f9b5..732abceb0f 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -32690,6 +32690,12 @@ Certificate Services
 contain a space-delimited list of renewed certificate domains (for
 example, @samp{"example.com www.example.com"}.
 
+@item @code{start-self-signed?} (default: @code{#t})
+Whether to generate an initial self-signed certificate during system
+activation.  This option is particularly useful to allow @code{nginx} to
+start before @code{certbot} has run, because @code{certbot} relies on
+@code{nginx} running to perform HTTP challenges.
+
 @end table
 @end deftp
 
diff --git a/gnu/services/certbot.scm b/gnu/services/certbot.scm
index 3926d0551a..10b99f5630 100644
--- a/gnu/services/certbot.scm
+++ b/gnu/services/certbot.scm
@@ -35,6 +35,7 @@ (define-module (gnu services certbot)
   #:use-module (guix records)
   #:use-module (guix gexp)
   #:use-module (srfi srfi-1)
+  #:use-module (ice-9 format)
   #:use-module (ice-9 match)
   #:export (certbot-service-type
             certbot-configuration
@@ -64,7 +65,9 @@ (define-record-type* <certificate-configuration>
   (cleanup-hook        certificate-cleanup-hook
                        (default #f))
   (deploy-hook         certificate-configuration-deploy-hook
-                       (default #f)))
+                       (default #f))
+  (start-self-signed?  certificate-configuration-start-self-signed?
+                       (default #t)))
 
 (define-record-type* <certbot-configuration>
   certbot-configuration make-certbot-configuration
@@ -91,7 +94,10 @@ (define-record-type* <certbot-configuration>
 (define (certbot-deploy-hook name deploy-hook-script)
   "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."
+not #f then it is run after the symlinks have been created.  This wrapping is
+necessary for certificates with start-self-signed? set to #t, as it will
+overwrite the initial self-signed certificates upon the first successful
+deploy."
   (program-file
    (string-append name "-deploy-hook")
    (with-imported-modules '((guix build utils))
@@ -108,7 +114,8 @@ (define (certbot-deploy-hook name deploy-hook-script)
                      "/etc/letsencrypt/live/" name "/fullchain.pem")
                   #$(string-append "/etc/certs/" name "/fullchain.pem.new"))
 
-         ;; Rename over the top of the old ones, if there are any.
+         ;; Rename over the top of the old ones, just in case they were the
+         ;; original self-signed certificates.
          (rename-file #$(string-append "/etc/certs/" name "/privkey.pem.new")
                       #$(string-append "/etc/certs/" name "/privkey.pem"))
          (rename-file #$(string-append "/etc/certs/" name "/fullchain.pem.new")
@@ -184,6 +191,47 @@ (define (certbot-renewal-jobs config)
    #~(job '(next-minute-from (next-hour '(0 12)) (list (random 60)))
           #$(certbot-command config))))
 
+(define (generate-certificate-gexp certbot-cert-directory rsa-key-size)
+  (match-lambda
+    (($ <certificate-configuration> 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))))))))
+
 (define (certbot-activation config)
   (let* ((certbot-directory "/var/lib/certbot")
          (certbot-cert-directory "/etc/letsencrypt/live")
@@ -198,6 +246,14 @@ (define (certbot-activation config)
              (mkdir-p #$webroot)
              (mkdir-p #$certbot-directory)
              (mkdir-p #$certbot-cert-directory)
+
+             #$@(let ((rsa-key-size (and rsa-key-size
+                                         (number->string rsa-key-size))))
+                  (map (generate-certificate-gexp certbot-cert-directory
+                                                  rsa-key-size)
+                       (filter certificate-configuration-start-self-signed?
+                               certificates)))
+
              (copy-file #$(certbot-command config) #$script)
              (display #$message)))))))
 
-- 
2.41.0





^ permalink raw reply related	[flat|nested] 33+ messages in thread

* bug#46961: [PATCH v3 3/4] services: certbot: Reload nginx in deploy hook.
  2024-01-30 13:26     ` Carlo Zancanaro
                       ` (5 preceding siblings ...)
  (?)
@ 2024-01-31 11:46     ` Carlo Zancanaro
  -1 siblings, 0 replies; 33+ messages in thread
From: Carlo Zancanaro @ 2024-01-31 11:46 UTC (permalink / raw)
  To: 46961; +Cc: clement

* gnu/services/certbot.scm (certbot-deploy-hook): Reload nginx.
* doc/guix.texi (Certificate services): Remove deploy-hook from example.

Change-Id: Ibb10481170a6fda7df72492072b939dd6a6ad176
---

I've pulled the nginx reloading into the regular deployment hook
here. I also removed the explicit deploy hook in the documentation,
because that is now the default behaviour.

 doc/guix.texi            | 10 +---------
 gnu/services/certbot.scm | 10 ++++++++--
 2 files changed, 9 insertions(+), 11 deletions(-)

diff --git a/doc/guix.texi b/doc/guix.texi
index 732abceb0f..c71d7e94cf 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -32562,21 +32562,13 @@ Certificate Services
 must be a @code{certbot-configuration} record as in this example:
 
 @lisp
-(define %certbot-deploy-hook
-  (program-file "certbot-deploy-hook.scm"
-    (with-imported-modules '((gnu services herd))
-      #~(begin
-          (use-modules (gnu services herd))
-          (with-shepherd-action 'nginx ('reload) result result)))))
-
 (service certbot-service-type
          (certbot-configuration
           (email "foo@@example.net")
           (certificates
            (list
             (certificate-configuration
-             (domains '("example.net" "www.example.net"))
-             (deploy-hook %certbot-deploy-hook))
+             (domains '("example.net" "www.example.net")))
             (certificate-configuration
              (domains '("bar.example.net")))))))
 @end lisp
diff --git a/gnu/services/certbot.scm b/gnu/services/certbot.scm
index 10b99f5630..cb1be0c0e9 100644
--- a/gnu/services/certbot.scm
+++ b/gnu/services/certbot.scm
@@ -100,9 +100,11 @@ (define (certbot-deploy-hook name deploy-hook-script)
 deploy."
   (program-file
    (string-append name "-deploy-hook")
-   (with-imported-modules '((guix build utils))
+   (with-imported-modules '((gnu services herd)
+                            (guix build utils))
      #~(begin
-         (use-modules (guix build utils))
+         (use-modules (gnu services herd)
+                      (guix build utils))
          (mkdir-p #$(string-append "/etc/certs/" name))
          (chmod #$(string-append "/etc/certs/" name) #o755)
 
@@ -120,6 +122,10 @@ (define (certbot-deploy-hook name deploy-hook-script)
                       #$(string-append "/etc/certs/" name "/privkey.pem"))
          (rename-file #$(string-append "/etc/certs/" name "/fullchain.pem.new")
                       #$(string-append "/etc/certs/" name "/fullchain.pem"))
+
+         ;; With the new certificates in place, tell nginx to reload them.
+         (with-shepherd-action 'nginx ('reload) result result)
+
          #$@(if deploy-hook-script
                 (list #~(invoke #$deploy-hook-script))
                 '())))))
-- 
2.41.0





^ permalink raw reply related	[flat|nested] 33+ messages in thread

* bug#46961: [PATCH v3 4/4] services: certbot: Add one-shot service to renew certificates.
  2024-01-30 13:26     ` Carlo Zancanaro
                       ` (6 preceding siblings ...)
  (?)
@ 2024-01-31 11:46     ` Carlo Zancanaro
  -1 siblings, 0 replies; 33+ messages in thread
From: Carlo Zancanaro @ 2024-01-31 11:46 UTC (permalink / raw)
  To: 46961; +Cc: clement

* gnu/services/certbot.scm (certbot-renewal-one-shot): New procedure.
(certbot-service-type)[extensions]: Add it to shepherd-root extension.
(certbot-command): Make connection errors return a different exit code.
(certbot-activation): Remove message with certificate renewal instructions.

Change-Id: I614ac6214a753dba0396e2385a75926c8355caa1
---

I've added some more logging here, and removed the comments that
implied that we expected the length of time for the retries to be
bounded.

 gnu/services/certbot.scm | 89 +++++++++++++++++++++++++++++++++-------
 1 file changed, 75 insertions(+), 14 deletions(-)

diff --git a/gnu/services/certbot.scm b/gnu/services/certbot.scm
index cb1be0c0e9..f287c8367f 100644
--- a/gnu/services/certbot.scm
+++ b/gnu/services/certbot.scm
@@ -180,15 +180,45 @@ (define certbot-command
        (program-file
         "certbot-command"
         #~(begin
-            (use-modules (ice-9 match))
-            (let ((code 0))
+            (use-modules (ice-9 match)
+                         (ice-9 textual-ports))
+
+            (define (log format-string . args)
+              (apply format #t format-string args)
+              (force-output))
+
+            (define (file-contains? file string)
+              (string-contains (call-with-input-file file
+                                 get-string-all)
+                               string))
+
+            (define (connection-error?)
+              ;; Certbot errors are always exit code 1, so we need to look at
+              ;; the log file to see if there was a connection error.
+              (file-contains? "/var/log/letsencrypt/letsencrypt.log"
+                              "Failed to establish a new connection"))
+
+            (let ((script-code 0))
               (for-each
                (match-lambda
                  ((name . command)
-                  (begin
-                    (format #t "Acquiring or renewing certificate: ~a~%" name)
-                    (set! code (or (apply system* command) code)))))
-               '#$commands) code)))))))
+                  (log "Acquiring or renewing certificate: ~a~%" name)
+                  (cond
+                   ((zero? (status:exit-val (apply system* command)))
+                    (log "Certificate successfully acquired: ~a~%" name))
+                   ((connection-error?)
+                    ;; If we have a connection error, then bail early with
+                    ;; exit code 2. We don't expect this to resolve within the
+                    ;; timespan of this script.
+                    (log "Connection error - bailing out~%")
+                    (exit 2))
+                   (else
+                    ;; If we have any other type of error, then continue but
+                    ;; exit with a failing status code in the end.
+                    (log "Error: ~a - continuing with other domains~%" name)
+                    (set! script-code 1)))))
+               '#$commands)
+              (exit script-code))))))))
 
 (define (certbot-renewal-jobs config)
   (list
@@ -197,6 +227,40 @@ (define (certbot-renewal-jobs config)
    #~(job '(next-minute-from (next-hour '(0 12)) (list (random 60)))
           #$(certbot-command config))))
 
+(define (certbot-renewal-one-shot config)
+  (list
+   ;; Renew certificates when the system first starts. This is a one-shot
+   ;; service, because the mcron configuration will take care of running this
+   ;; periodically. This is most useful the very first time the system starts,
+   ;; to overwrite our self-signed certificates as soon as possible without
+   ;; user intervention.
+   (shepherd-service
+    (provision '(renew-certbot-certificates))
+    (requirement '(nginx))
+    (one-shot? #t)
+    (start #~(lambda _
+               ;; This needs the network, but there's no reliable way to know
+               ;; if the network is up other than trying. If we fail due to a
+               ;; connection error we retry a number of times in the hope that
+               ;; the network comes up soon.
+               (let loop ((attempt 0))
+                 (let ((code (status:exit-val
+                              (system* #$(certbot-command config)))))
+                   (cond
+                    ((and (= code 2)      ; Exit code 2 means connection error
+                          (< attempt 12)) ; Arbitrarily chosen max attempts
+                     (sleep 10)           ; Arbitrarily chosen retry delay
+                     (loop (1+ attempt)))
+                    ((zero? code)
+                     ;; Success!
+                     #t)
+                    (else
+                     ;; Failure.
+                     #f))))))
+    (auto-start? #t)
+    (documentation "Call certbot to renew certificates.")
+    (actions (list (shepherd-configuration-action (certbot-command config)))))))
+
 (define (generate-certificate-gexp certbot-cert-directory rsa-key-size)
   (match-lambda
     (($ <certificate-configuration> name (primary-domain other-domains ...)
@@ -240,9 +304,7 @@ (define (generate-certificate-gexp certbot-cert-directory rsa-key-size)
 
 (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)))
+         (certbot-cert-directory "/etc/letsencrypt/live"))
     (match config
       (($ <certbot-configuration> package webroot certificates email
                                   server rsa-key-size default-location)
@@ -258,10 +320,7 @@ (define (certbot-activation config)
                   (map (generate-certificate-gexp certbot-cert-directory
                                                   rsa-key-size)
                        (filter certificate-configuration-start-self-signed?
-                               certificates)))
-
-             (copy-file #$(certbot-command config) #$script)
-             (display #$message)))))))
+                               certificates)))))))))
 
 (define certbot-nginx-server-configurations
   (match-lambda
@@ -294,7 +353,9 @@ (define certbot-service-type
                        (service-extension activation-service-type
                                           certbot-activation)
                        (service-extension mcron-service-type
-                                          certbot-renewal-jobs)))
+                                          certbot-renewal-jobs)
+                       (service-extension shepherd-root-service-type
+                                          certbot-renewal-one-shot)))
                 (compose concatenate)
                 (extend (lambda (config additional-certificates)
                           (certbot-configuration
-- 
2.41.0





^ permalink raw reply related	[flat|nested] 33+ messages in thread

* bug#46961: Nginx and certbot cervices don't play well togther
  2024-01-31  0:55     ` bug#46961: Nginx and certbot cervices don't play well togther Clément Lassieur
@ 2024-01-31 11:50       ` Carlo Zancanaro
  2024-01-31 15:58         ` Clément Lassieur
  0 siblings, 1 reply; 33+ messages in thread
From: Carlo Zancanaro @ 2024-01-31 11:50 UTC (permalink / raw)
  To: Clément Lassieur; +Cc: 46961

On Wed, Jan 31 2024, Clément Lassieur wrote:
> Removing guix-devel.

I've also removed Brice.

> On Tue, Jan 30 2024, Carlo Zancanaro wrote:
>>                      (format #t "Acquiring or renewing 
>>                      certificate: ~a~%" name)
>
> Here we could add ‘(force-output)’, because otherwise those logs 
> arrive
> after the certbot logs, and it's hard to understand anything.

Done.

>> +                          ;; If we have a connection error, 
>> then bail early
>> +                          ;; with exit code 2. We don't expect 
>> this to
>> +                          ;; resolve within the timespan of 
>> this script.
>
> Could we have a (log + force-output) here too?  (I imagine 
> within a
> ‘begin’)

Done.

>> +                          ;; If we have any other type of 
>> error, then continue
>> +                          ;; but exit with a failing status 
>> code in the end.
>
> and here?

Done.

> And maybe a log also in case the command succeeds.  (So that 
> would mean
> to replace ‘unless’ with ‘if’).

Done.

>> +                          (< attempt 12)) ; 12 * 10 seconds = 
>> 2 minutes
>                                                                  ^------
> This comment is not true because certbot takes time to execute 
> (around 15s on my vm).  I don't think there is a need to be that 
> precise.

I haven't extracted/named the max-attempts value, but I have 
removed the comments that imply that the time frame is bounded.

> Also could you update the example in the docs?

I have removed the %certbot-deploy-hook in the example in the 
manual.

> ... However, we could add a nginx-service-type and a 
> dhcp-client-service-type so that people have an idea of what the 
> minimal config is, maybe like I did in my first review: 
> https://debbugs.gnu.org/cgi/bugreport.cgi?bug=46961#23.

I have not added this. I understand the desire, but I'm wary of 
providing an example that's "too involved". The current example 
demonstrates a minimal config of certbot itself. I think you are 
looking to include an example of a minimal system that hosts a 
website using certbot provided certificates. I don't know where an 
example like that belongs, but I'm not yet convinced it belongs in 
the certbot service documentation.

Carlo




^ permalink raw reply	[flat|nested] 33+ messages in thread

* bug#46961: Nginx and certbot cervices don't play well togther
  2024-01-31 11:50       ` Carlo Zancanaro
@ 2024-01-31 15:58         ` Clément Lassieur
  0 siblings, 0 replies; 33+ messages in thread
From: Clément Lassieur @ 2024-01-31 15:58 UTC (permalink / raw)
  To: Carlo Zancanaro; +Cc: 46961-done

On Wed, Jan 31 2024, Carlo Zancanaro wrote:

>>> +                          (< attempt 12)) ; 12 * 10 seconds = 2 minutes
>>                                                                  ^------
>> This comment is not true because certbot takes time to execute (around 15s
>> on my vm).  I don't think there is a need to be that precise.
>
> I haven't extracted/named the max-attempts value, but I have removed the
> comments that imply that the time frame is bounded.

Ok

>> Also could you update the example in the docs?
>
> I have removed the %certbot-deploy-hook in the example in the manual.
>
>> ... However, we could add a nginx-service-type and a
>> dhcp-client-service-type so that people have an idea of what the minimal
>> config is, maybe like I did in my first review:
>> https://debbugs.gnu.org/cgi/bugreport.cgi?bug=46961#23.
>
> I have not added this. I understand the desire, but I'm wary of providing an
> example that's "too involved". The current example demonstrates a minimal
> config of certbot itself. I think you are looking to include an example of a
> minimal system that hosts a website using certbot provided certificates. I
> don't know where an example like that belongs, but I'm not yet convinced it
> belongs in the certbot service documentation.

Sounds good

Pushed, thank you!
Clément




^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: bug#46961: [PATCH v2 0/4] Make certbot play more nicely with nginx
  2024-01-30 19:39       ` bug#46961: " Clément Lassieur
@ 2024-04-13  1:17         ` Felix Lechner via Development of GNU Guix and the GNU System distribution.
  2024-04-14 11:42           ` Carlo Zancanaro
  2024-04-14 13:51           ` Carlo Zancanaro
  0 siblings, 2 replies; 33+ messages in thread
From: Felix Lechner via Development of GNU Guix and the GNU System distribution. @ 2024-04-13  1:17 UTC (permalink / raw)
  To: Clément Lassieur, Felix Lechner via Bug reports for GNU Guix
  Cc: Carlo Zancanaro, guix-devel, brice

Hi Clément,

On Tue, Jan 30 2024, Clément Lassieur wrote:

> Carlo's solution allows to have a working nginx even when certbot
> fails.

I just upgraded a server to the latest Guix version, which I think
includes a version of this patch.

To my surprise OpenSSL, which I saw in proced, generated a lot of
certificates in /etc/certs.  I am talking about pages and pages of
asterisk, plusses, and dots for a system with twenty or so certificates.
Is it possible that they were generated as a result of the patch?

It would be unfavorable to create such certificates when they are not
needed.  It reduces valuable server entropy.

Kind regards
Felix


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: bug#46961: [PATCH v2 0/4] Make certbot play more nicely with nginx
  2024-04-13  1:17         ` Felix Lechner via Development of GNU Guix and the GNU System distribution.
@ 2024-04-14 11:42           ` Carlo Zancanaro
  2024-04-14 13:51           ` Carlo Zancanaro
  1 sibling, 0 replies; 33+ messages in thread
From: Carlo Zancanaro @ 2024-04-14 11:42 UTC (permalink / raw)
  To: Felix Lechner; +Cc: Clément Lassieur, guix-devel

Hi Felix,
 
On Fri, Apr 12 2024, Felix Lechner wrote: 
> To my surprise OpenSSL, which I saw in proced, generated a lot 
> of certificates in /etc/certs.  I am talking about pages and 
> pages of asterisk, plusses, and dots for a system with twenty or 
> so certificates.  Is it possible that they were generated as a 
> result of the patch?

I expect the first reconfiguration after this change to create one 
self signed certificate in /etc/certs for each 
<certificate-configuration> object in your certbot configuration. 
These self-signed certificates will then be replaced by symlinks 
to the certificates that cerbot generates after your next renewal 
(i.e. when the deploy hook runs).   We could avoid generating 
unnecessary self-signed certificates by first checking if we 
already have certificates from certbot, and creating the symlink 
straight away if we can.   About the "pages and pages" of output: 
it might be sensible to change the size of the self keys used in 
the self signed certificates. The current code uses the 
rsa-key-size from the <cerbot-configuration>, or 4096 if that is 
unset (the default). This is probably overkill given we don't 
actually need, or want, to use the initial certificates.   We 
could instead use the smallest key size that openssl supports 
(512?).   I'm not sure when I'll have time to make those changes, 
but they should be pretty straightforward if someone else has time 
before I do.
 
> It would be unfavorable to create such certificates when they 
> are not needed.  It reduces valuable server entropy.

If you don't want the initial self signed certificate you can tell 
Guix not to generate it by setting start-self-signed? to #f on the 
<certificate-configuration> object.

Carlo


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: bug#46961: [PATCH v2 0/4] Make certbot play more nicely with nginx
  2024-04-13  1:17         ` Felix Lechner via Development of GNU Guix and the GNU System distribution.
  2024-04-14 11:42           ` Carlo Zancanaro
@ 2024-04-14 13:51           ` Carlo Zancanaro
  2024-04-14 16:25             ` Felix Lechner via Development of GNU Guix and the GNU System distribution.
  1 sibling, 1 reply; 33+ messages in thread
From: Carlo Zancanaro @ 2024-04-14 13:51 UTC (permalink / raw)
  To: Felix Lechner; +Cc: Clément Lassieur, guix-devel

Apologies for the line breaks my earlier email. I'm not entirely sure
what happened, but hopefully they'll come through properly this time.

On Fri, Apr 12 2024, Felix Lechner wrote:
> To my surprise OpenSSL, which I saw in proced, generated a lot of
> certificates in /etc/certs.  I am talking about pages and pages of
> asterisk, plusses, and dots for a system with twenty or so certificates.
> Is it possible that they were generated as a result of the patch?

I expect the first reconfiguration after this change to create one self
signed certificate in /etc/certs for each <certificate-configuration>
object in your certbot configuration. These self-signed certificates
will then be replaced by symlinks to the certificates that cerbot
generates after your next renewal (i.e. when the deploy hook runs).

We could avoid generating unnecessary self-signed certificates by first
checking if we already have certificates from certbot, and creating the
symlink straight away if we can.

About the "pages and pages" of output: it might be sensible to change
the size of the self keys used in the self signed certificates. The
current code uses the rsa-key-size from the <cerbot-configuration>, or
4096 if that is unset (the default). This is probably overkill given we
don't actually need, or want, to use the initial certificates.

We could instead use the smallest key size that openssl supports (512?).

I'm not sure when I'll have time to make those changes, but they should
be pretty straightforward if someone else has time before I do.

> It would be unfavorable to create such certificates when they are not
> needed.  It reduces valuable server entropy.

If you don't want the initial self signed certificate you can tell Guix
not to generate it by setting start-self-signed? to #f on the
<certificate-configuration> object.

Carlo


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: bug#46961: [PATCH v2 0/4] Make certbot play more nicely with nginx
  2024-04-14 13:51           ` Carlo Zancanaro
@ 2024-04-14 16:25             ` Felix Lechner via Development of GNU Guix and the GNU System distribution.
  0 siblings, 0 replies; 33+ messages in thread
From: Felix Lechner via Development of GNU Guix and the GNU System distribution. @ 2024-04-14 16:25 UTC (permalink / raw)
  To: Carlo Zancanaro; +Cc: Clément Lassieur, guix-devel

Hi Carlo,

Thanks for fixing the Cc: addresses.  I should not have included the bug
filing address in my reply.

On Sun, Apr 14 2024, Carlo Zancanaro wrote:

> We could avoid generating unnecessary self-signed certificates by first
> checking if we already have certificates from certbot, and creating the
> symlink straight away if we can.

That would seem wise to me.

> The current code uses the rsa-key-size from the
> <cerbot-configuration>, or 4096 if that is unset (the default). This
> is probably [excessive] given we don't actually need, or want, to use
> the initial certificates.
>
> We could instead use the smallest key size that openssl supports (512?).

Like you, I asked myself why those self-signed certificates, which are
mostly useless, would be 4096-bit strong.  I am more offended, however,
that they were generated with a lifetime of just one day.

What happens if certbot is not yet configured, and the sysadmin forgot
to do so for a few days?  A long time span, such as ten years (3650
days) might be more appropriate for the fallback certificates.

By the way, in Debian we call them "snake oil".  I believe they are
present on all systems.

Kind regards
Felix


^ permalink raw reply	[flat|nested] 33+ messages in thread

end of thread, other threads:[~2024-04-14 16:26 UTC | newest]

Thread overview: 33+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-03-06  8:15 Nginx and certbot cervices don't play well togther Brice Waegeneire
2024-01-24 12:18 ` bug#46961: [PATCH 0/2] Allow nginx to start before certbot has run Carlo Zancanaro
2024-01-24 12:18   ` bug#46961: [PATCH 1/2] services: certbot: Symlink certificates to /etc/certs Carlo Zancanaro
2024-01-24 12:18   ` bug#46961: [PATCH 2/2] services: certbot: Create self-signed certificates before certbot runs Carlo Zancanaro
2024-01-24 13:01     ` Carlo Zancanaro
2024-01-29 19:23     ` bug#46961: Nginx and certbot cervices don't play well togther Clément Lassieur
2024-01-29 23:02       ` Carlo Zancanaro
2024-01-29 23:19         ` Clément Lassieur
2024-01-29 19:28     ` Clément Lassieur
2024-01-30 13:26   ` bug#46961: [PATCH v2 0/4] Make certbot play more nicely with nginx Carlo Zancanaro
2024-01-30 13:26     ` Carlo Zancanaro
2024-01-30 14:49     ` Felix Lechner via Development of GNU Guix and the GNU System distribution.
2024-01-30 21:48       ` Carlo Zancanaro
2024-01-31  0:04         ` Wojtek Kosior via Development of GNU Guix and the GNU System distribution.
     [not found]     ` <875xzanaer.fsf__22488.5524179385$1706626282$gmane$org@lease-up.com>
2024-01-30 19:39       ` bug#46961: " Clément Lassieur
2024-04-13  1:17         ` Felix Lechner via Development of GNU Guix and the GNU System distribution.
2024-04-14 11:42           ` Carlo Zancanaro
2024-04-14 13:51           ` Carlo Zancanaro
2024-04-14 16:25             ` Felix Lechner via Development of GNU Guix and the GNU System distribution.
2024-01-31 11:46     ` bug#46961: [PATCH v3 " Carlo Zancanaro
2024-01-31 11:46     ` bug#46961: [PATCH v3 1/4] services: certbot: Symlink certificates to /etc/certs Carlo Zancanaro
2024-01-31 11:46     ` bug#46961: [PATCH v3 2/4] services: certbot: Create self-signed certificates before certbot runs Carlo Zancanaro
2024-01-31 11:46     ` bug#46961: [PATCH v3 3/4] services: certbot: Reload nginx in deploy hook Carlo Zancanaro
2024-01-31 11:46     ` bug#46961: [PATCH v3 4/4] services: certbot: Add one-shot service to renew certificates Carlo Zancanaro
2024-01-30 13:26   ` [PATCH v2 1/4] services: certbot: Symlink certificates to /etc/certs Carlo Zancanaro
2024-01-30 13:26   ` [PATCH v2 2/4] services: certbot: Create self-signed certificates before certbot runs Carlo Zancanaro
2024-01-30 13:26   ` [PATCH v2 3/4] services: certbot: Add a default deploy hook to reload nginx Carlo Zancanaro
2024-01-31  0:29     ` bug#46961: Nginx and certbot cervices don't play well togther Clément Lassieur
2024-01-30 13:26   ` bug#46961: [PATCH v2 4/4] services: certbot: Add one-shot service to renew certificates Carlo Zancanaro
2024-01-30 13:26     ` Carlo Zancanaro
2024-01-31  0:55     ` bug#46961: Nginx and certbot cervices don't play well togther Clément Lassieur
2024-01-31 11:50       ` Carlo Zancanaro
2024-01-31 15:58         ` Clément Lassieur

Code repositories for project(s) associated with this external index

	https://git.savannah.gnu.org/cgit/guix.git

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.