vim: tweak ALE key bindings
[julm/julm-nix.git] / nixos / modules / security / systemd-creds.nix
index ca2065fabec6340870dbd7715cba99f299673db7..d1d0d50163f5c9ec84efccb27ad07cc28f7056a6 100644 (file)
@@ -1,5 +1,4 @@
 { pkgs, lib, config, ... }:
-with builtins;
 with lib;
 let cfg = config.security.systemd-creds; in
 {
@@ -19,12 +18,11 @@ let cfg = config.security.systemd-creds; in
       description = mdDoc ''
         Command to get the cleartext of a credential.
         `credBase` is derived from the path of the credential in `LoadCredentialEncrypted=`,
-        by removing the `storeDir` prefix and then one directory level if any,
-        and then the `.cred` suffix if any.
+        by removing the `builtins.storeDir` prefix and then one directory level.
       '';
       apply = concatStringsSep " ";
-      default = [ "gpg" "--batch" "--decrypt" "\${credBase}.gpg" ];
-      example = [ "pass" "\${credBase}" ];
+      default = [ "gpg" "--batch" "--decrypt" "\${credBase%.cred}.gpg" ];
+      example = [ "pass" "\${credBase%.cred}" ];
     };
     shell = mkOption {
       type = with types; listOf str;
@@ -33,9 +31,13 @@ let cfg = config.security.systemd-creds; in
       '';
       apply = concatStringsSep " ";
       default = [
-        "ssh" "-o" "StrictHostKeyChecking=yes"
-        "-o" "ControlMaster=auto"
-        "-o" "ControlPersist=16s"
+        "ssh"
+        "-o"
+        "StrictHostKeyChecking=yes"
+        "-o"
+        "ControlMaster=auto"
+        "-o"
+        "ControlPersist=16s"
         "\"\${SYSTEMD_CREDS_TARGET:-${cfg.target}}\""
       ];
       defaultText = mdLiteral ''
@@ -50,10 +52,19 @@ let cfg = config.security.systemd-creds; in
       type = with types; listOf str;
       description = mdDoc ''
         Command to run `systemd-creds encrypt` on the target host.
+
+        ::: {.warning}
+        Beware that the files `/etc/machine-id`
+        and `/var/lib/systemd/credential.secret` on the target host,
+        are both used to encrypt and decrypt when using the `host` key mechanism.
+        Meaning that reinstalling the system on a new drive
+        without restoring those two files
+        will require to reencrypt the credentials.
+        :::
       '';
       apply = concatStringsSep " ";
       default = [ "systemd-creds" "encrypt" "--name" "\"$credID\"" "--with-key=auto" ];
-      example = ["sudo" "systemd-creds" "encrypt" "--with-key=host"];
+      example = [ "sudo" "systemd-creds" "encrypt" "--with-key=host" ];
     };
     install = mkOption {
       type = with types; listOf str;
@@ -61,80 +72,84 @@ let cfg = config.security.systemd-creds; in
       description = mdDoc ''
         Command to install the encrypted credential on the orchestrating host.
       '';
-      default = [ "install" "-D" "-m" "640" "/dev/stdin" "\"$credBase\".cred" ];
+      default = [ "install" "-D" "-m" "640" "/dev/stdin" "\"$credBase\"" ];
     };
-    script = mkOption {
+    reencrypt = mkOption {
       type = types.lines;
       apply = pkgs.writeShellScriptBin "systemd-creds-encrypt-${replaceStrings ["@"] ["-"] cfg.target}";
       description = mdDoc ''
         Encrypt credentials referenced in the `LoadCredentialEncrypted=`
         of enabled systemd services, by running `systemd-creds` on the {option}`security.systemd-creds.target` host.
-        Only *existing* and *empty* credential files, are considered.
-        Recursively if the credential path is a directory.
-        Note that when using flakes, the sandboxing requires those empty files to be added to Git beforehand.
+        Only *non-existing* credential files are considered,
+        unless the `SYSTEMD_CREDS_FORCE_REENCRYPT` envvar is set to a non-empty value.
+        Credential directories are not supported.
 
         Example of use:
         ```console
-        $ sudo wg genkey | gpg --encrypt --sign --recipient @$USER wireguard/wg-intra/privateKey.cred --output wireguard/wg-intra/privateKey.gpg
-        $ tee </dev/null >wireguard/wg-intra/privateKey.cred
-        $ git add wireguard/wg-intra/privateKey.cred
+        $ sudo wg genkey | gpg --encrypt --sign --recipient @$USER --output wireguard/wg-intra/privateKey.gpg
+        $ git add wireguard/wg-intra/privateKey.gpg
         ```
 
         ```nix
+        { config, pkgs, lib, inputs, ... }:
+        {
         systemd.services."wireguard-wg-intra".serviceConfig.LoadCredentialEncrypted =
-          [ "privateKey:''${wireguard/wg-intra/privateKey.cred}" ];
+          [ "privateKey:''${inputs.self}/wireguard/wg-intra/privateKey.cred" ];
+        }
         ```
 
         ```console
-        $ nix run .#nixosConfigurations.''${hostName}.config.security.systemd-creds.script
+        $ nix run .#nixosConfigurations.''${hostName}.config.security.systemd-creds.reencrypt
         $ git add wireguard/wg-intra/privateKey.cred
         ```
+
+        ::: {.warning}
+        To be able to access the relative path of the `.cred` file,
+        `inputs.self` has to be used in `LoadCredentialEncrypted=`.
+        Note that `inputs` is a `config._module.args` or `specialArgs`
+        usually set in your `flake.nix`.
+        In other words, using `''${wireguard/wg-intra/privatekey}` here,
+        would not work, because it drops the `wireguard/wg-intra/` part.
+        :::
       '';
     };
   };
   config = {
-    security.systemd-creds.script = ''
+    security.systemd-creds.reencrypt = ''
       shopt -s extglob globstar nullglob
       set -o pipefail
       set -eux
-      '' + concatMapStringsSep "\n"
-        (service:
-          concatMapStringsSep "\n"
-            (credential: let
+    '' + concatMapStringsSep "\n"
+      (service:
+        concatMapStringsSep "\n"
+          (credential:
+            let
               cred = splitString ":" credential;
               credID = elemAt cred 0;
               credPath = elemAt cred 1;
-            in ''
+            in
+            ''
               credID=${escapeShellArg credID}
               credPath=${escapeShellArg credPath}
-              if test -f "$credPath" -a ! -s "$credPath"; then
-                credBase=''${credPath#${storeDir}/}
-                credBase=''${credBase#*/}
-                credBase=''${credBase%.cred}
+              credBase=''${credPath#${builtins.storeDir}/*/}
+              if test "''${SYSTEMD_CREDS_FORCE_REENCRYPT:+set}" \
+                      -o ! -s "$credBase" \
+                      -o -e "''${credBase%.cred}.gpg" -a "$credBase" -ot "''${credBase%.cred}.gpg"; then
                 { ${cfg.decrypt}; } |
                 { ${cfg.shell} -- ${cfg.encrypt} - -; } |
                 { ${cfg.install}; }
-              elif test -d "$credPath"; then
-                for credPath in "$credPath"/**(.); do
-                  if test ! -s "$credPath"; then
-                    credBase=''${credPath#${storeDir}/}
-                    credBase=''${credBase#*-}
-                    credBase=''${credBase%.cred}
-                    { ${cfg.decrypt}; } |
-                    { ${cfg.shell} -- ${cfg.encrypt} - -; } |
-                    { ${cfg.install}; }
-                  fi
-                done
               fi
             ''
-            )
-            service.serviceConfig.LoadCredentialEncrypted)
-        (attrValues
-          (filterAttrs (serviceName: service:
+          )
+          (toList service.serviceConfig.LoadCredentialEncrypted))
+      (attrValues
+        (filterAttrs
+          (_serviceName: service:
             service.enable &&
-            hasAttr "LoadCredentialEncrypted" service.serviceConfig
-            ) config.systemd.services
+              hasAttr "LoadCredentialEncrypted" service.serviceConfig
           )
-        );
+          config.systemd.services
+        )
+      );
   };
 }