From 47da7aafdfbe1df48cc25b056895d7dc1edf5e84 Mon Sep 17 00:00:00 2001 From: Lucas Savva Date: Wed, 29 Apr 2020 20:31:17 +0100 Subject: [PATCH 1/3] nixos/acme: update documentation --- nixos/modules/security/acme.nix | 6 +- nixos/modules/security/acme.xml | 250 +++++++++++++++++++++++++------- 2 files changed, 203 insertions(+), 53 deletions(-) diff --git a/nixos/modules/security/acme.nix b/nixos/modules/security/acme.nix index 39976380e3b4..6c3b5fc50b65 100644 --- a/nixos/modules/security/acme.nix +++ b/nixos/modules/security/acme.nix @@ -87,13 +87,13 @@ let default = {}; example = literalExample '' { - "example.org" = "/srv/http/nginx"; + "example.org" = null; "mydomain.org" = null; } ''; description = '' - A list of extra domain names, which are included in the one certificate to be issued, with their - own server roots if needed. + A list of extra domain names, which are included in the one certificate to be issued. + Setting a distinct server root is deprecated and not functional in 20.03+ ''; }; diff --git a/nixos/modules/security/acme.xml b/nixos/modules/security/acme.xml index 2b29c1174845..d8de26109bbd 100644 --- a/nixos/modules/security/acme.xml +++ b/nixos/modules/security/acme.xml @@ -10,61 +10,38 @@ for Let's Encrypt. The alternative ACME client lego is used under the hood. + + Automatic cert validation and configuration for Apache and Nginx virtual + hosts is included in NixOS, however if you would like to generate a wildcard + cert or you are not using a web server you will have to configure DNS + based validation. +
Prerequisites - You need to have a running HTTP server for verification. The server must - have a webroot defined that can serve + To use the ACME module, you must accept the provider's terms of service + by setting + to true. The Let's Encrypt ToS can be found + here. + + + + You must also set an email address to be used when creating accounts with + Let's Encrypt. You can set this for all certs with + + and/or on a per-cert basis with + . + This address is only used for registration and renewal reminders, + and cannot be used to administer the certificates in any way. + + + + You will need an HTTP server or DNS server for verification. For HTTP, + the server must have a webroot defined that can serve .well-known/acme-challenge. This directory must be - writeable by the user that will run the ACME client. - - - - For instance, this generic snippet could be used for Nginx: - -http { - server { - server_name _; - listen 80; - listen [::]:80; - - location /.well-known/acme-challenge { - root /var/www/challenges; - } - - location / { - return 301 https://$host$request_uri; - } - } -} - - -
-
- Configuring - - - To enable ACME certificate retrieval & renewal for a certificate for - foo.example.com, add the following in your - configuration.nix: - -."foo.example.com" = { - webroot = "/var/www/challenges"; - email = "foo@example.com"; -}; - - - - - The private key key.pem and certificate - fullchain.pem will be put into - /var/lib/acme/foo.example.com. - - - Refer to for all available configuration - options for the security.acme - module. + writeable by the user that will run the ACME client. For DNS, you must + set up credentials with your provider/server for use with lego.
@@ -80,12 +57,27 @@ http { + = true; + = "admin+acme@example.com"; services.nginx = { enable = true; virtualHosts = { "foo.example.com" = { forceSSL = true; enableACME = true; + # All serverAliases will be added as extra domains on the certificate. + serverAliases = [ "bar.example.com" ]; + locations."/" = { + root = "/var/www"; + }; + }; + + # We can also add a different vhost and reuse the same certificate + # but we have to append extraDomains manually. + security.acme.certs."foo.example.com".extraDomains."baz.example.com" = null; + "baz.example.com" = { + forceSSL = true; + useACMEHost = "foo.example.com"; locations."/" = { root = "/var/www"; }; @@ -94,4 +86,162 @@ services.nginx = { }
+
+ Using ACME certificates in Apache/httpd + + + Using ACME certificates with Apache virtual hosts is identical + to using them with Nginx. The attribute names are all the same, just replace + "nginx" with "httpd" where appropriate. + +
+
+ Manual configuration of HTTP-01 validation + + + First off you will need to set up a virtual host to serve the challenges. + This example uses a vhost called certs.example.com, with + the intent that you will generate certs for all your vhosts and redirect + everyone to HTTPS. + + + + = true; + = "admin+acme@example.com"; +services.nginx = { + enable = true; + virtualHosts = { + "acmechallenge.example.com" = { + # Catchall vhost, will redirect users to HTTPS for all vhosts + serverAliases = [ "*.example.com" ]; + # /var/lib/acme/.challenges must be writable by the ACME user + # and readable by the Nginx user. + # By default, this is the case. + locations."/.well-known/acme-challenge" = { + root = "/var/lib/acme/.challenges"; + }; + locations."/" = { + return = "301 https://$host$request_uri"; + }; + }; + }; +} +# Alternative config for Apache +services.httpd = { + enable = true; + virtualHosts = { + "acmechallenge.example.com" = { + # Catchall vhost, will redirect users to HTTPS for all vhosts + serverAliases = [ "*.example.com" ]; + # /var/lib/acme/.challenges must be writable by the ACME user and readable by the Apache user. + # By default, this is the case. + documentRoot = "/var/lib/acme/.challenges"; + extraConfig = '' + RewriteEngine On + RewriteCond %{HTTPS} off + RewriteCond %{REQUEST_URI} !^/\.well-known/acme-challenge [NC] + RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R=301] + ''; + }; + }; +} + + + + Now you need to configure ACME to generate a certificate. + + + +."foo.example.com" = { + webroot = "/var/lib/acme/.challenges"; + email = "foo@example.com"; + # Since we have a wildcard vhost to handle port 80, + # we can generate certs for anything! + # Just make sure your DNS resolves them. + extraDomains = [ "mail.example.com" ]; +}; + + + + The private key key.pem and certificate + fullchain.pem will be put into + /var/lib/acme/foo.example.com. + + + + Refer to for all available configuration + options for the security.acme + module. + +
+
+ Configuring ACME for DNS validation + + + This is useful if you want to generate a wildcard certificate, since + Let's Encrypt will only hand out wildcard certs over DNS validation. + There a number of supported DNS providers and servers you can utilise, + see the lego docs + for provider/server specific configuration values. For the sake of these + docs, we will provide a fully self-hosted example using bind. + + + +services.bind = { + enable = true; + extraConfig = '' + include "/var/secrets/dnskeys.conf"; + ''; + zones = [ + rec { + name = "example.com"; + file = "/var/db/bind/${name}"; + master = true; + extraConfig = "allow-update { key rfc2136key.example.com.; };"; + } + ]; +} + +# Now we can configure ACME + = true; + = "admin+acme@example.com"; +."example.com" = { + domain = "*.example.com"; + dnsProvider = "rfc2136"; + credentialsFile = "/var/secrets/certs.secret"; + # We don't need to wait for propagation since this is a local DNS server + dnsPropagationCheck = false; +}; + + + + The dnskeys.conf and certs.secret + must be kept secure and thus you should not keep their contents in your + Nix config. Instead, generate them one time with these commands: + + + +mkdir -p /var/secrets +tsig-keygen rfc2136key.example.com > /var/secrets/dnskeys.conf +chown named:root /var/secrets/dnskeys.conf +chmod 400 /var/secrets/dnskeys.conf + +# Copy the secret value from the dnskeys.conf, and put it in +# RFC2136_TSIG_SECRET below + +cat > /var/secrets/certs.secret << EOF +RFC2136_NAMESERVER='127.0.0.1:53' +RFC2136_TSIG_ALGORITHM='hmac-sha256.' +RFC2136_TSIG_KEY='rfc2136key.example.com' +RFC2136_TSIG_SECRET='your secret key' +EOF +chmod 400 /var/secrets/certs.secret + + + + Now you're all set to generate certs! You should monitor the first invokation + by running systemctl start acme-example.com.service & + journalctl -fu acme-example.com.service and watching for errors. + +
From c9f6e5f161f64701bce0527c80628eb637eae0ac Mon Sep 17 00:00:00 2001 From: Lucas Savva Date: Fri, 1 May 2020 18:23:16 +0100 Subject: [PATCH 2/3] nixos/acme: indicate support for other providers --- nixos/modules/security/acme.xml | 37 ++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/nixos/modules/security/acme.xml b/nixos/modules/security/acme.xml index d8de26109bbd..f802faee9749 100644 --- a/nixos/modules/security/acme.xml +++ b/nixos/modules/security/acme.xml @@ -6,9 +6,9 @@ SSL/TLS Certificates with ACME NixOS supports automatic domain validation & certificate retrieval and - renewal using the ACME protocol. This is currently only implemented by and - for Let's Encrypt. The alternative ACME client lego is - used under the hood. + renewal using the ACME protocol. Any provider can be used, but by default + NixOS uses Let's Encrypt. The alternative ACME client lego + is used under the hood. Automatic cert validation and configuration for Apache and Nginx virtual @@ -36,6 +36,13 @@ and cannot be used to administer the certificates in any way. + + Alternatively, you can use a different ACME server by changing the + option + to a provider of your choosing, or just change the server for one cert with + . + + You will need an HTTP server or DNS server for verification. For HTTP, the server must have a webroot defined that can serve @@ -60,7 +67,7 @@ = true; = "admin+acme@example.com"; services.nginx = { - enable = true; + enable = true; virtualHosts = { "foo.example.com" = { forceSSL = true; @@ -109,7 +116,7 @@ services.nginx = { = true; = "admin+acme@example.com"; services.nginx = { - enable = true; + enable = true; virtualHosts = { "acmechallenge.example.com" = { # Catchall vhost, will redirect users to HTTPS for all vhosts @@ -179,7 +186,7 @@ services.httpd = { This is useful if you want to generate a wildcard certificate, since - Let's Encrypt will only hand out wildcard certs over DNS validation. + ACME servers will only hand out wildcard certs over DNS validation. There a number of supported DNS providers and servers you can utilise, see the lego docs for provider/server specific configuration values. For the sake of these @@ -190,7 +197,7 @@ services.httpd = { services.bind = { enable = true; extraConfig = '' - include "/var/secrets/dnskeys.conf"; + include "/var/lib/secrets/dnskeys.conf"; ''; zones = [ rec { @@ -208,7 +215,7 @@ services.bind = { ."example.com" = { domain = "*.example.com"; dnsProvider = "rfc2136"; - credentialsFile = "/var/secrets/certs.secret"; + credentialsFile = "/var/lib/secrets/certs.secret"; # We don't need to wait for propagation since this is a local DNS server dnsPropagationCheck = false; }; @@ -221,27 +228,27 @@ services.bind = { -mkdir -p /var/secrets -tsig-keygen rfc2136key.example.com > /var/secrets/dnskeys.conf -chown named:root /var/secrets/dnskeys.conf -chmod 400 /var/secrets/dnskeys.conf +mkdir -p /var/lib/secrets +tsig-keygen rfc2136key.example.com > /var/lib/secrets/dnskeys.conf +chown named:root /var/lib/secrets/dnskeys.conf +chmod 400 /var/lib/secrets/dnskeys.conf # Copy the secret value from the dnskeys.conf, and put it in # RFC2136_TSIG_SECRET below -cat > /var/secrets/certs.secret << EOF +cat > /var/lib/secrets/certs.secret << EOF RFC2136_NAMESERVER='127.0.0.1:53' RFC2136_TSIG_ALGORITHM='hmac-sha256.' RFC2136_TSIG_KEY='rfc2136key.example.com' RFC2136_TSIG_SECRET='your secret key' EOF -chmod 400 /var/secrets/certs.secret +chmod 400 /var/lib/secrets/certs.secret Now you're all set to generate certs! You should monitor the first invokation by running systemctl start acme-example.com.service & - journalctl -fu acme-example.com.service and watching for errors. + journalctl -fu acme-example.com.service and watching its log output. From 037ef70d5cbacc9850f5c52d19b16cc41cd5efae Mon Sep 17 00:00:00 2001 From: Lucas Savva Date: Sat, 2 May 2020 00:07:50 +0100 Subject: [PATCH 3/3] nixos/acme: fix incorrect example --- nixos/modules/security/acme.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nixos/modules/security/acme.nix b/nixos/modules/security/acme.nix index 6c3b5fc50b65..a7411e5b184a 100644 --- a/nixos/modules/security/acme.nix +++ b/nixos/modules/security/acme.nix @@ -250,7 +250,7 @@ in "example.com" = { webroot = "/var/www/challenges/"; email = "foo@example.com"; - extraDomains = { "www.example.com" = null; "foo.example.com" = "/var/www/foo/"; }; + extraDomains = { "www.example.com" = null; "foo.example.com" = null; }; }; "bar.example.com" = { webroot = "/var/www/challenges/";