From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([2001:4830:134:3::10]:47334) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1d3pnl-0003w4-1J for guix-patches@gnu.org; Thu, 27 Apr 2017 16:14:11 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1d3pne-0003jH-Iv for guix-patches@gnu.org; Thu, 27 Apr 2017 16:14:09 -0400 Received: from debbugs.gnu.org ([208.118.235.43]:45091) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1d3pne-0003jB-EU for guix-patches@gnu.org; Thu, 27 Apr 2017 16:14:02 -0400 Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1d3pne-0003wV-5v for guix-patches@gnu.org; Thu, 27 Apr 2017 16:14:02 -0400 Subject: bug#26685: certbot service Resent-Message-ID: Received: from eggs.gnu.org ([2001:4830:134:3::10]:47208) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1d3pmn-0003vJ-HR for guix-patches@gnu.org; Thu, 27 Apr 2017 16:13:11 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1d3pmk-0003Jb-1N for guix-patches@gnu.org; Thu, 27 Apr 2017 16:13:09 -0400 Received: from pb-sasl2.pobox.com ([64.147.108.67]:52464 helo=sasl.smtp.pobox.com) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1d3pmj-0003JX-S6 for guix-patches@gnu.org; Thu, 27 Apr 2017 16:13:05 -0400 Received: from sasl.smtp.pobox.com (unknown [127.0.0.1]) by pb-sasl2.pobox.com (Postfix) with ESMTP id 76CE47F12A for ; Thu, 27 Apr 2017 16:13:05 -0400 (EDT) Received: from pb-sasl2.nyi.icgroup.com (unknown [127.0.0.1]) by pb-sasl2.pobox.com (Postfix) with ESMTP id 6EC2D7F129 for ; Thu, 27 Apr 2017 16:13:05 -0400 (EDT) Received: from clucks (unknown [88.160.190.192]) (using TLSv1 with cipher ECDHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by pb-sasl2.pobox.com (Postfix) with ESMTPSA id 412EF7F128 for ; Thu, 27 Apr 2017 16:13:03 -0400 (EDT) From: Andy Wingo Date: Thu, 27 Apr 2017 22:12:56 +0200 Message-ID: MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: guix-patches-bounces+kyle=kyleam.com@gnu.org Sender: "Guix-patches" To: 26685@debbugs.gnu.org --=-=-= Content-Type: text/plain Attached patch adds a certbot service extending nginx. Only question is, how to ensure that nginx is runing when the certbot activation runs? In practice I bet this races so it's not a big issue but if there's a way to require nginx before activation, that would be nice. --=-=-= Content-Type: text/x-patch; charset=utf-8 Content-Disposition: inline; filename=0002-gnu-Add-certbot-service.patch Content-Transfer-Encoding: quoted-printable >From 641607bb3ea2b46e80f43216783833c1fc8d70d6 Mon Sep 17 00:00:00 2001 From: Andy Wingo Date: Thu, 27 Apr 2017 14:09:16 +0200 Subject: [PATCH 2/5] gnu: Add certbot service. * gnu/services/certbot.scm: New file. * gnu/local.mk (GNU_SYSTEM_MODULES): Add new file. * doc/guix.texi (Certificate Services): New section. --- doc/guix.texi | 80 +++++++++++++++++++++++++++++ gnu/local.mk | 1 + gnu/services/certbot.scm | 128 +++++++++++++++++++++++++++++++++++++++++++= ++++ 3 files changed, 209 insertions(+) create mode 100644 gnu/services/certbot.scm diff --git a/doc/guix.texi b/doc/guix.texi index 0d334e302..21b6d7d88 100644 --- a/doc/guix.texi +++ b/doc/guix.texi @@ -215,6 +215,7 @@ Services * Messaging Services:: Messaging services. * Kerberos Services:: Kerberos services. * Web Services:: Web servers. +* Certificate Services:: TLS certificates via Let's Encrypt. * VPN Services:: VPN daemons. * Network File System:: NFS related services. * Continuous Integration:: The Cuirass service. @@ -8605,6 +8606,7 @@ declaration. * Messaging Services:: Messaging services. * Kerberos Services:: Kerberos services. * Web Services:: Web servers. +* Certificate Services:: TLS certificates via Let's Encrypt. * VPN Services:: VPN daemons. * Network File System:: NFS related services. * Continuous Integration:: The Cuirass service. @@ -13379,6 +13381,84 @@ Whether the server should add its configuration to= response. @end table @end deftp =20 + +@node Certificate Services +@subsubsection Certificate Services + +@cindex web +@cindex www +@cindex HTTP +The @code{(gnu services certbot)} module allows Guix systems to +automatically obtain a valid TLS certificate from the Let's Encrypt +certificate authority. These certificates can then be used to serve +content securely over HTTPS or other TLS-based protocols, with the +knowledge that the client will be able to verify the server's +authenticity. + +Let's Encrypt (@url{https://letsencrypt.org/}) provides the +@code{certbot} tool to automate the certification process. This tool +first securely generates a key on the server. It then makes a request +to the Let's Encrypt certificate authority (CA) to sign the key. The CA +checks that the request originates from the host in question by using a +challenge-response protocol, requiring the server to provide its +response over HTTP. If that protocol completes successfully, the CA +signs the key, resulting in a certificate. That certificate is valid +for a limited period of time, and therefore to continue to provide TLS +services, the server needs to periodically ask the CA to renew its +signature. + +The Guix certbot service automates this process: the initial key +generation, the initial certification request to the Let's Encrypt +service, the web server challenge/response integration, writing the +certificate to disk, and the automated periodic renewals. + +@defvr {Scheme Variable} certbot-service-type +A service type for the @code{certbot} Let's Encrypt client. +@end defvr + +@deftp {Data Type} certbot-configuration +Data type representing the configuration of the @code{certbot} serice. +This type has the following parameters: +@table @asis +@item @code{package} (default: @code{certbot}) +The certbot package to use. + +@item @code{webroot} (default: @code{/var/www}) +The directory from which to serve the Let's Encrypt challenge/response +files. + +@item @code{hosts} (default: @code{()}) +A list of hosts for which to generate certificates and request +signatures. + +@item @code{default-location} (default: @i{see below}) +The default @code{nginx-location-configuration}. Because @code{certbot} +needs to be able to serve challenges and responses, it needs to be able +to run a web server. It does so by extending the @code{nginx} web +service with an @code{nginx-server-configuration} listening on the +@var{hosts} on port 80, and which has a +@code{nginx-location-configuration} for the @code{/.well-known/} URI +path subspace used by Let's Encrypt. @xref{Web Services}, for more on +these nginx configuration data types. + +Requests to other URL paths will be matched by the +@code{default-location}, which if present is added to all +@code{nginx-server-configuration}s. + +By default, the @code{default-location} will issue a redirect from +@code{http://@var{host}/...} to @code{https://@var{host}/...}, leaving +you to define what to serve on your site via @code{https}. + +Pass @code{#f} to not issue a default location. +@end table +@end deftp + +The public key and its signatures will be written to +@code{/etc/letsencrypt/live/@var{host}/fullchain.pem}, for each +@var{host} in the configuration. The private key is written to +@code{/etc/letsencrypt/live/@var{host}/privkey.pem}. + + @node VPN Services @subsubsection VPN Services @cindex VPN (virtual private network) diff --git a/gnu/local.mk b/gnu/local.mk index 31239933c..ac0c9d510 100644 --- a/gnu/local.mk +++ b/gnu/local.mk @@ -414,6 +414,7 @@ GNU_SYSTEM_MODULES =3D \ %D%/services/admin.scm \ %D%/services/avahi.scm \ %D%/services/base.scm \ + %D%/services/certbot.scm \ %D%/services/configuration.scm \ %D%/services/cuirass.scm \ %D%/services/cups.scm \ diff --git a/gnu/services/certbot.scm b/gnu/services/certbot.scm new file mode 100644 index 000000000..c11c9a66b --- /dev/null +++ b/gnu/services/certbot.scm @@ -0,0 +1,128 @@ +;;; GNU Guix --- Functional package management for GNU +;;; Copyright =C2=A9 2016 ng0 +;;; Copyright =C2=A9 2016 Sou Bunnbu +;;; +;;; This file is part of GNU Guix. +;;; +;;; GNU Guix is free software; you can redistribute it and/or modify it +;;; under the terms of the GNU General Public License as published by +;;; the Free Software Foundation; either version 3 of the License, or (at +;;; your option) any later version. +;;; +;;; GNU Guix is distributed in the hope that it will be useful, but +;;; WITHOUT ANY WARRANTY; without even the implied warranty of +;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;;; GNU General Public License for more details. +;;; +;;; You should have received a copy of the GNU General Public License +;;; along with GNU Guix. If not, see . + +(define-module (gnu services certbot) + #:use-module (gnu services) + #:use-module (gnu services base) + #:use-module (gnu services shepherd) + #:use-module (gnu services mcron) + #:use-module (gnu services web) + #:use-module (gnu system shadow) + #:use-module (gnu packages tls) + #:use-module (guix records) + #:use-module (guix gexp) + #:use-module (srfi srfi-1) + #:use-module (ice-9 match) + #:export (certbot-service-type + certbot-configuration + certbot-configuration?)) + +;;; Commentary: +;;; +;;; Automatically obtaining TLS certificates from Let's Encrypt. +;;; +;;; Code: + + +(define-record-type* + certbot-configuration make-certbot-configuration + certbot-configuration? + (package certbot-configuration-package + (default certbot)) + (webroot certbot-configuration-webroot + (default "/var/www")) + (hosts certbot-configuration-hosts + (default '())) + (default-location certbot-configuration-default-location + (default + (nginx-location-configuration + (uri "/") + (body + (list "return 301 https://$host$request_uri;"))= )))) + +(define certbot-renewal-jobs + (match-lambda + (($ package webroot hosts default-location) + (match hosts + ;; Avoid pinging certbot if we have no hosts. + (() '()) + (_ + (list + ;; Attempt to renew the certificates twice a week. + #~(job (lambda (now) + (next-day-from (next-hour-from now '(3)) + '(2 5))) + (string-append #$package "/bin/certbot renew" + (string-concatenate + (map (lambda (host) + (string-append " -d " host)) + #$hosts)))))))))) + +(define certbot-activation + (match-lambda + (($ package webroot hosts default-location) + (with-imported-modules '((guix build utils)) + #~(begin + (use-modules (guix build utils)) + (mkdir-p #$webroot) + (for-each + (lambda (host) + (unless (file-exists? (in-vicinity "/etc/letsencrypt/live" h= ost)) + (unless (zero? (system* + (string-append #$certbot "/bin/certbot") + "certonly" "--webroot" "-w" #$webroot + "-d" host)) + (error "failed to acquire cert for host" host)))) + '#$hosts)))))) + +(define certbot-nginx-server-configurations + (match-lambda + (($ package webroot hosts default-location) + (map + (lambda (host) + (nginx-server-configuration + (http-port 80) + (https-port #f) + (ssl-certificate #f) + (ssl-certificate-key #f) + (server-name (list host)) + (locations + (filter identity + (list + (nginx-location-configuration + (uri "/.well-known") + (body (list (list "root " webroot ";")))) + default-location))))) + hosts)))) + +(define certbot-service-type + (service-type (name 'certbot) + (extensions + (list (service-extension nginx-service-type + certbot-nginx-server-configurati= ons) + (service-extension activation-service-type + certbot-activation) + (service-extension mcron-service-type + certbot-renewal-jobs))) + (compose concatenate) + (extend (lambda (config additional-hosts) + (certbot-configuration + (inherit config) + (hosts (append (certbot-configuration-hosts con= fig) + additional-hosts))))))) --=20 2.12.2 --=-=-=--