about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--nixos/doc/manual/configuration/configuration.xml1
-rw-r--r--nixos/doc/manual/default.nix1
-rw-r--r--nixos/modules/security/acme.nix129
-rw-r--r--nixos/modules/security/acme.xml69
4 files changed, 138 insertions, 62 deletions
diff --git a/nixos/doc/manual/configuration/configuration.xml b/nixos/doc/manual/configuration/configuration.xml
index afffd60bc485f..1e488b59343e7 100644
--- a/nixos/doc/manual/configuration/configuration.xml
+++ b/nixos/doc/manual/configuration/configuration.xml
@@ -26,6 +26,7 @@ effect after you run <command>nixos-rebuild</command>.</para>
 
 <!-- FIXME: auto-include NixOS module docs -->
 <xi:include href="postgresql.xml" />
+<xi:include href="acme.xml" />
 <xi:include href="nixos.xml" />
 
 <!-- Apache; libvirtd virtualisation -->
diff --git a/nixos/doc/manual/default.nix b/nixos/doc/manual/default.nix
index 844cba57cd857..bd558dac971d9 100644
--- a/nixos/doc/manual/default.nix
+++ b/nixos/doc/manual/default.nix
@@ -55,6 +55,7 @@ let
       cp -prd $sources/* . # */
       chmod -R u+w .
       cp ${../../modules/services/databases/postgresql.xml} configuration/postgresql.xml
+      cp ${../../modules/security/acme.xml} configuration/acme.xml
       cp ${../../modules/misc/nixos.xml} configuration/nixos.xml
       ln -s ${optionsDocBook} options-db.xml
       echo "${version}" > version
diff --git a/nixos/modules/security/acme.nix b/nixos/modules/security/acme.nix
index 37de46cb1a53e..8f3a2ee073bba 100644
--- a/nixos/modules/security/acme.nix
+++ b/nixos/modules/security/acme.nix
@@ -131,67 +131,72 @@ in
   };
 
   ###### implementation
-  config = mkIf (cfg.certs != { }) {
-
-    systemd.services = flip mapAttrs' cfg.certs (cert: data:
-      let
-        cpath = "${cfg.directory}/${cert}";
-        cmdline = [ "-v" "-d" cert "--default_root" data.webroot "--valid_min" cfg.validMin ]
-                  ++ optionals (data.email != null) [ "--email" data.email ]
-                  ++ concatMap (p: [ "-f" p ]) data.plugins
-                  ++ concatLists (mapAttrsToList (name: root: [ "-d" (if root == null then name else "${name}:${root}")]) data.extraDomains);
-
-      in nameValuePair
-      ("acme-${cert}")
-      ({
-        description = "ACME cert renewal for ${cert} using simp_le";
-        after = [ "network.target" ];
-        serviceConfig = {
-          Type = "oneshot";
-          SuccessExitStatus = [ "0" "1" ];
-          PermissionsStartOnly = true;
-          User = data.user;
-          Group = data.group;
-          PrivateTmp = true;
-        };
-        path = [ pkgs.simp_le ];
-        preStart = ''
-          mkdir -p '${cfg.directory}'
-          if [ ! -d '${cpath}' ]; then
-            mkdir -m 700 '${cpath}'
-            chown '${data.user}:${data.group}' '${cpath}'
-          fi
-        '';
-        script = ''
-          cd '${cpath}'
-          set +e
-          simp_le ${concatMapStringsSep " " (arg: escapeShellArg (toString arg)) cmdline}
-          EXITCODE=$?
-          set -e
-          echo "$EXITCODE" > /tmp/lastExitCode
-          exit "$EXITCODE"
-        '';
-        postStop = ''
-          if [ -e /tmp/lastExitCode ] && [ "$(cat /tmp/lastExitCode)" = "0" ]; then
-            echo "Executing postRun hook..."
-            ${data.postRun}
-          fi
-        '';
-      })
-    );
-
-    systemd.timers = flip mapAttrs' cfg.certs (cert: data: nameValuePair
-      ("acme-${cert}")
-      ({
-        description = "timer for ACME cert renewal of ${cert}";
-        wantedBy = [ "timers.target" ];
-        timerConfig = {
-          OnCalendar = cfg.renewInterval;
-          Unit = "acme-simp_le-${cert}.service";
-        };
-      })
-    );
-
-  };
+  config = mkMerge [
+    (mkIf (cfg.certs != { }) {
+
+      systemd.services = flip mapAttrs' cfg.certs (cert: data:
+        let
+          cpath = "${cfg.directory}/${cert}";
+          cmdline = [ "-v" "-d" cert "--default_root" data.webroot "--valid_min" cfg.validMin ]
+                    ++ optionals (data.email != null) [ "--email" data.email ]
+                    ++ concatMap (p: [ "-f" p ]) data.plugins
+                    ++ concatLists (mapAttrsToList (name: root: [ "-d" (if root == null then name else "${name}:${root}")]) data.extraDomains);
+
+        in nameValuePair
+        ("acme-${cert}")
+        ({
+          description = "ACME cert renewal for ${cert} using simp_le";
+          after = [ "network.target" ];
+          serviceConfig = {
+            Type = "oneshot";
+            SuccessExitStatus = [ "0" "1" ];
+            PermissionsStartOnly = true;
+            User = data.user;
+            Group = data.group;
+            PrivateTmp = true;
+          };
+          path = [ pkgs.simp_le ];
+          preStart = ''
+            mkdir -p '${cfg.directory}'
+            if [ ! -d '${cpath}' ]; then
+              mkdir -m 700 '${cpath}'
+              chown '${data.user}:${data.group}' '${cpath}'
+            fi
+          '';
+          script = ''
+            cd '${cpath}'
+            set +e
+            simp_le ${concatMapStringsSep " " (arg: escapeShellArg (toString arg)) cmdline}
+            EXITCODE=$?
+            set -e
+            echo "$EXITCODE" > /tmp/lastExitCode
+            exit "$EXITCODE"
+          '';
+          postStop = ''
+            if [ -e /tmp/lastExitCode ] && [ "$(cat /tmp/lastExitCode)" = "0" ]; then
+              echo "Executing postRun hook..."
+              ${data.postRun}
+            fi
+          '';
+        })
+      );
+
+      systemd.timers = flip mapAttrs' cfg.certs (cert: data: nameValuePair
+        ("acme-${cert}")
+        ({
+          description = "timer for ACME cert renewal of ${cert}";
+          wantedBy = [ "timers.target" ];
+          timerConfig = {
+            OnCalendar = cfg.renewInterval;
+            Unit = "acme-simp_le-${cert}.service";
+          };
+        })
+      );
+    })
+
+    { meta.maintainers = with lib.maintainers; [ abbradar fpletz globin ];
+      meta.doc = ./acme.xml;
+    }
+  ];
 
 }
diff --git a/nixos/modules/security/acme.xml b/nixos/modules/security/acme.xml
new file mode 100644
index 0000000000000..e32fa72c93939
--- /dev/null
+++ b/nixos/modules/security/acme.xml
@@ -0,0 +1,69 @@
+<chapter xmlns="http://docbook.org/ns/docbook"
+         xmlns:xlink="http://www.w3.org/1999/xlink"
+         xmlns:xi="http://www.w3.org/2001/XInclude"
+         version="5.0"
+         xml:id="module-security-acme">
+
+<title>SSL/TLS Certificates with ACME</title>
+
+<para>NixOS supports automatic domain validation &amp; certificate
+retrieval and renewal using the ACME protocol. This is currently only
+implemented by and for Let's Encrypt. The alternative ACME client
+<literal>simp_le</literal> is used under the hood.</para>
+
+<section><title>Prerequisites</title>
+
+<para>You need to have a running HTTP server for verification. The server must
+have a webroot defined that can serve
+<filename>.well-known/acme-challenge</filename>. This directory must be
+writeable by the user that will run the ACME client.</para>
+
+<para>For instance, this generic snippet could be used for Nginx:
+
+<programlisting>
+http {
+  server {
+    server_name _;
+    listen 80;
+    listen [::]:80;
+
+    location /.well-known/acme-challenge {
+      root /var/www/challenges;
+    }
+
+    location / {
+      return 301 https://$host$request_uri;
+    }
+  }
+}
+</programlisting>
+</para>
+
+</section>
+
+<section><title>Configuring</title>
+
+<para>To enable ACME certificate retrieval &amp; renewal for a certificate for
+<literal>foo.example.com</literal>, add the following in your
+<filename>configuration.nix</filename>:
+
+<programlisting>
+security.acme.certs."foo.example.com" = {
+  webroot = "/var/www/challenges";
+  email = "foo@example.com";
+};
+</programlisting>
+</para>
+
+<para>The private key <filename>key.pem</filename> and certificate
+<filename>fullchain.pem</filename> will be put into
+<filename>/var/lib/acme/foo.example.com</filename>. The target directory can
+be configured with the option <literal>security.acme.directory</literal>.
+</para>
+
+<para>Refer to <xref linkend="ch-options" /> for all available configuration
+options for the <literal>security.acme</literal> module.</para>
+
+</section>
+
+</chapter>