]> Git — Sourcephile - julm/julm-nix.git/blob - nixos/modules/security/systemd-creds.nix
creds: move creds-* scripts to a NixOS module
[julm/julm-nix.git] / nixos / modules / security / systemd-creds.nix
1 { pkgs, lib, config, ... }:
2 with builtins;
3 with lib;
4 let cfg = config.security.systemd-creds; in
5 {
6 options.security.systemd-creds = {
7 target = mkOption {
8 type = types.str;
9 description = ''
10 Destination address of the target host able to encrypt the credentials.
11 Used by the default {option}`security.systemd-creds.shell`.
12 '';
13 default = "root@${config.networking.hostName}.${config.networking.domain}";
14 defaultText = mdLiteral "root@\${config.networking.hostName}.\${config.networking.domain}";
15 example = mdLiteral "''${config.networking.hostName}.wg";
16 };
17 decrypt = mkOption {
18 type = with types; listOf str;
19 description = mdDoc ''
20 Command to get the cleartext of a credential.
21 `credBase` is derived from the path of the credential in `LoadCredentialEncrypted=`,
22 by removing the `storeDir` prefix and then one directory level if any,
23 and then the `.cred` suffix if any.
24 '';
25 apply = concatStringsSep " ";
26 default = [ "gpg" "--batch" "--decrypt" "\${credBase}.gpg" ];
27 example = [ "pass" "\${credBase}" ];
28 };
29 shell = mkOption {
30 type = with types; listOf str;
31 description = mdDoc ''
32 Command to get a shell on the target host.
33 '';
34 apply = concatStringsSep " ";
35 default = [
36 "ssh" "-o" "StrictHostKeyChecking=yes"
37 "-o" "ControlMaster=auto"
38 "-o" "ControlPersist=16s"
39 "\"\${SYSTEMD_CREDS_TARGET:-${cfg.target}}\""
40 ];
41 defaultText = mdLiteral ''
42 ssh -o StrictHostKeyChecking=yes \
43 -o ControlMaster=auto \
44 -o ControlPersist=16s \
45 \"''${SYSTEMD_CREDS_TARGET:-root@''${config.security.systemd-creds.target}}\"
46 '';
47 example = [ "sudo" ];
48 };
49 encrypt = mkOption {
50 type = with types; listOf str;
51 description = mdDoc ''
52 Command to run `systemd-creds encrypt` on the target host.
53 '';
54 apply = concatStringsSep " ";
55 default = [ "systemd-creds" "encrypt" "--name" "\"$credID\"" "--with-key=auto" ];
56 example = ["sudo" "systemd-creds" "encrypt" "--with-key=host"];
57 };
58 install = mkOption {
59 type = with types; listOf str;
60 apply = concatStringsSep " ";
61 description = mdDoc ''
62 Command to install the encrypted credential on the orchestrating host.
63 '';
64 default = [ "install" "-D" "-m" "640" "/dev/stdin" "\"$credBase\".cred" ];
65 };
66 script = mkOption {
67 type = types.lines;
68 apply = pkgs.writeShellScriptBin "systemd-creds-encrypt-${replaceStrings ["@"] ["-"] cfg.target}";
69 description = mdDoc ''
70 Encrypt credentials referenced in the `LoadCredentialEncrypted=`
71 of enabled systemd services, by running `systemd-creds` on the {option}`security.systemd-creds.target` host.
72 Only *existing* and *empty* credential files, are considered.
73 Recursively if the credential path is a directory.
74 Note that when using flakes, the sandboxing requires those empty files to be added to Git beforehand.
75
76 Example of use:
77 ```console
78 $ sudo wg genkey | gpg --encrypt --sign --recipient @$USER wireguard/wg-intra/privateKey.cred --output wireguard/wg-intra/privateKey.gpg
79 $ tee </dev/null >wireguard/wg-intra/privateKey.cred
80 $ git add wireguard/wg-intra/privateKey.cred
81 ```
82
83 ```nix
84 systemd.services."wireguard-wg-intra".serviceConfig.LoadCredentialEncrypted =
85 [ "privateKey:''${wireguard/wg-intra/privateKey.cred}" ];
86 ```
87
88 ```console
89 $ nix run .#nixosConfigurations.''${hostName}.config.security.systemd-creds.script
90 $ git add wireguard/wg-intra/privateKey.cred
91 ```
92 '';
93 };
94 };
95 config = {
96 security.systemd-creds.script = ''
97 shopt -s extglob globstar nullglob
98 set -o pipefail
99 set -eux
100 '' + concatMapStringsSep "\n"
101 (service:
102 concatMapStringsSep "\n"
103 (credential: let
104 cred = splitString ":" credential;
105 credID = elemAt cred 0;
106 credPath = elemAt cred 1;
107 in ''
108 credID=${escapeShellArg credID}
109 credPath=${escapeShellArg credPath}
110 if test -f "$credPath" -a ! -s "$credPath"; then
111 credBase=''${credPath#${storeDir}/}
112 credBase=''${credBase#*/}
113 credBase=''${credBase%.cred}
114 { ${cfg.decrypt}; } |
115 { ${cfg.shell} -- ${cfg.encrypt} - -; } |
116 { ${cfg.install}; }
117 elif test -d "$credPath"; then
118 for credPath in "$credPath"/**(.); do
119 if test ! -s "$credPath"; then
120 credBase=''${credPath#${storeDir}/}
121 credBase=''${credBase#*-}
122 credBase=''${credBase%.cred}
123 { ${cfg.decrypt}; } |
124 { ${cfg.shell} -- ${cfg.encrypt} - -; } |
125 { ${cfg.install}; }
126 fi
127 done
128 fi
129 ''
130 )
131 service.serviceConfig.LoadCredentialEncrypted)
132 (attrValues
133 (filterAttrs (serviceName: service:
134 service.enable &&
135 hasAttr "LoadCredentialEncrypted" service.serviceConfig
136 ) config.systemd.services
137 )
138 );
139 };
140 }