summary refs log tree commit diff
path: root/nixos/modules/security/acme.nix
diff options
context:
space:
mode:
authorLucas Savva <lucas@m1cr0man.com>2020-12-29 15:01:08 +0000
committerLucas Savva <lucas@m1cr0man.com>2020-12-29 15:01:08 +0000
commit92a3a37153b159951d027a77cbb7b1ee7f92bde6 (patch)
treee544f910906a75bacffb5658bf16ce3fbb7101d6 /nixos/modules/security/acme.nix
parentbfe07e21795685d023b0595d9305071f30e3d448 (diff)
downloadnixpkgs-92a3a37153b159951d027a77cbb7b1ee7f92bde6.tar
nixpkgs-92a3a37153b159951d027a77cbb7b1ee7f92bde6.tar.gz
nixpkgs-92a3a37153b159951d027a77cbb7b1ee7f92bde6.tar.bz2
nixpkgs-92a3a37153b159951d027a77cbb7b1ee7f92bde6.tar.lz
nixpkgs-92a3a37153b159951d027a77cbb7b1ee7f92bde6.tar.xz
nixpkgs-92a3a37153b159951d027a77cbb7b1ee7f92bde6.tar.zst
nixpkgs-92a3a37153b159951d027a77cbb7b1ee7f92bde6.zip
nixos/acme: Remove all systemd-tmpfiles usage
- Added an ExecPostStart to acme-$cert.service when webroot is defined to create the acme-challenge
directory and fix required permissions. Lego always tries to create .well-known and acme-challenge,
thus if any permissions in that tree are wrong it will crash and break cert renewal.
- acme-fixperms now configured with acme User and Group, however the script still runs as root. This
ensures the StateDirectories are owned by the acme user.
- Switched to list syntax for systemd options where multiple values are specified.
Diffstat (limited to 'nixos/modules/security/acme.nix')
-rw-r--r--nixos/modules/security/acme.nix64
1 files changed, 37 insertions, 27 deletions
diff --git a/nixos/modules/security/acme.nix b/nixos/modules/security/acme.nix
index 70c86d19680..d2d68eea9fd 100644
--- a/nixos/modules/security/acme.nix
+++ b/nixos/modules/security/acme.nix
@@ -62,24 +62,30 @@ let
   # Ensures that directories which are shared across all certs
   # exist and have the correct user and group, since group
   # is configurable on a per-cert basis.
-  userMigrationService = {
-    description = "Fix owner and group of all ACME certificates";
-
+  userMigrationService = let
     script = with builtins; concatStringsSep "\n" (mapAttrsToList (cert: data: ''
-      for fixpath in /var/lib/acme/${escapeShellArg cert} /var/lib/acme/.lego/${escapeShellArg cert}; do
+      chown -R acme .lego/accounts
+      for fixpath in ${escapeShellArg cert} .lego/${escapeShellArg cert}; do
         if [ -d "$fixpath" ]; then
           chmod -R u=rwX,g=rX,o= "$fixpath"
           chown -R acme:${data.group} "$fixpath"
         fi
       done
     '') certConfigs);
+  in {
+    description = "Fix owner and group of all ACME certificates";
 
-    serviceConfig = {
+    serviceConfig = commonServiceConfig // {
       # We don't want this to run every time a renewal happens
       RemainAfterExit = true;
 
       # These StateDirectory entries negate the need for tmpfiles
-      StateDirectory = "acme acme/.lego acme/.lego/accounts";
+      StateDirectory = [ "acme" "acme/.lego" "acme/.lego/accounts" ];
+      StateDirectoryMode = 755;
+      WorkingDirectory = "/var/lib/acme";
+
+      # Run the start script as root
+      ExecStart = "+" + (pkgs.writeShellScript "acme-fixperms" script);
     };
   };
 
@@ -153,7 +159,6 @@ let
   in {
     inherit accountHash cert selfsignedDeps;
 
-    webroot = data.webroot;
     group = data.group;
 
     renewTimer = {
@@ -193,7 +198,10 @@ let
 
         StateDirectory = "acme/${cert}";
 
-        BindPaths = "/var/lib/acme/.minica:/tmp/ca /var/lib/acme/${cert}:/tmp/${keyName}";
+        BindPaths = [
+          "/var/lib/acme/.minica:/tmp/ca"
+          "/var/lib/acme/${cert}:/tmp/${keyName}"
+        ];
       };
 
       # Working directory will be /tmp
@@ -234,17 +242,19 @@ let
         # Keep in mind that these directories will be deleted if the user runs
         # systemctl clean --what=state
         # acme/.lego/${cert} is listed for this reason.
-        StateDirectory =
-          "acme/${cert} " +
-          "acme/.lego/${cert} " +
-          "acme/.lego/${cert}/${certDir} " +
-          "acme/.lego/accounts/${accountHash} ";
+        StateDirectory = [
+          "acme/${cert}"
+          "acme/.lego/${cert}"
+          "acme/.lego/${cert}/${certDir}"
+          "acme/.lego/accounts/${accountHash}"
+        ];
 
         # Needs to be space separated, but can't use a multiline string because that'll include newlines
-        BindPaths =
-          "${accountDir}:/tmp/accounts " +
-          "/var/lib/acme/${cert}:/tmp/out " +
-          "/var/lib/acme/.lego/${cert}/${certDir}:/tmp/certificates ";
+        BindPaths = [
+          "${accountDir}:/tmp/accounts"
+          "/var/lib/acme/${cert}:/tmp/out"
+          "/var/lib/acme/.lego/${cert}/${certDir}:/tmp/certificates"
+        ];
 
         # Only try loading the credentialsFile if the dns challenge is enabled
         EnvironmentFile = mkIf useDns data.credentialsFile;
@@ -257,7 +267,16 @@ let
             ${data.postRun}
           fi
         '');
-      };
+
+      } // (optionalAttrs (data.webroot != null) {
+        # Lego always tries to create .well-known/acme-challenge, but if webroot is owned
+        # by the wrong user then it will crash and break cert renewal.
+        ExecStartPre = "+" + pkgs.writeShellScript "acme-${cert}-make-webroot" ''
+          mkdir -p '${data.webroot}/.well-known/acme-challenge'
+          cd '${data.webroot}'
+          chown 'acme:${data.group}' . .well-known .well-known/acme-challenge
+        '';
+      });
 
       # Working directory will be /tmp
       script = ''
@@ -676,15 +695,6 @@ in {
 
       systemd.timers = mapAttrs' (cert: conf: nameValuePair "acme-${cert}" conf.renewTimer) certConfigs;
 
-      systemd.tmpfiles.rules = unique (
-        flatten (
-          mapAttrsToList (
-            cert: conf:
-              optional (conf.webroot != null) "d ${conf.webroot}/.well-known/acme-challenge - acme ${conf.group}"
-          ) certConfigs
-        )
-      );
-
       systemd.targets = let
         # Create some targets which can be depended on to be "active" after cert renewals
         finishedTargets = mapAttrs' (cert: conf: nameValuePair "acme-finished-${cert}" {