diff options
Diffstat (limited to 'flakes/secrets')
-rw-r--r-- | flakes/secrets/flake.nix | 60 |
1 files changed, 52 insertions, 8 deletions
diff --git a/flakes/secrets/flake.nix b/flakes/secrets/flake.nix index ef74a30..7bf04a4 100644 --- a/flakes/secrets/flake.nix +++ b/flakes/secrets/flake.nix | |||
@@ -3,6 +3,8 @@ | |||
3 | 3 | ||
4 | outputs = { self }: { | 4 | outputs = { self }: { |
5 | nixosModule = { config, lib, pkgs, ... }: { | 5 | nixosModule = { config, lib, pkgs, ... }: { |
6 | # Necessary for situations where flake gets included multiple times | ||
7 | key = builtins.hashString "sha256" (builtins.path { path = self.sourceInfo.outPath; name = "source"; }); | ||
6 | options.secrets = with lib; { | 8 | options.secrets = with lib; { |
7 | keys = mkOption { | 9 | keys = mkOption { |
8 | type = types.attrsOf (types.submodule { | 10 | type = types.attrsOf (types.submodule { |
@@ -36,11 +38,38 @@ | |||
36 | type = types.str; | 38 | type = types.str; |
37 | description = "Content of the entry"; | 39 | description = "Content of the entry"; |
38 | }; | 40 | }; |
41 | keyDependencies = mkOption { | ||
42 | default = []; | ||
43 | type = types.listOf (types.either types.path types.package); | ||
44 | description = '' | ||
45 | (public) system dependencies that needs to be | ||
46 | uploaded with the key. | ||
47 | |||
48 | keyDependencies + ignoredKeyDependencies should | ||
49 | contain the exhaustive list of the text context. | ||
50 | |||
51 | A warning will be thrown if there are remaning | ||
52 | dependencies from the text. | ||
53 | ''; | ||
54 | }; | ||
55 | ignoredKeyDependencies = mkOption { | ||
56 | default = []; | ||
57 | type = types.listOf (types.either types.path types.package); | ||
58 | description = '' | ||
59 | dependencies that must not be sent along with the key. | ||
60 | |||
61 | keyDependencies + ignoredKeyDependencies should | ||
62 | contain the exhaustive list of the text context. | ||
63 | |||
64 | A warning will be thrown if there are remaning | ||
65 | dependencies from the text. | ||
66 | ''; | ||
67 | }; | ||
39 | }; | 68 | }; |
40 | }); | 69 | }); |
41 | default = {}; | 70 | default = {}; |
42 | description = "Keys attrs to upload to the server"; | 71 | description = "Keys attrs to upload to the server"; |
43 | apply = lib.mapAttrsToList (dest: v: v // { inherit dest; }); | 72 | apply = builtins.mapAttrs (dest: v: v // { inherit dest; }); |
44 | }; | 73 | }; |
45 | gpgKeys = mkOption { | 74 | gpgKeys = mkOption { |
46 | type = types.listOf types.path; | 75 | type = types.listOf types.path; |
@@ -74,8 +103,8 @@ | |||
74 | # Read-only variables | 103 | # Read-only variables |
75 | fullPaths = mkOption { | 104 | fullPaths = mkOption { |
76 | type = types.attrsOf types.path; | 105 | type = types.attrsOf types.path; |
77 | default = builtins.listToAttrs | 106 | default = builtins.mapAttrs |
78 | (map (v: { name = v.dest; value = "${config.secrets.location}/${v.dest}"; }) config.secrets.keys); | 107 | (n: v: "${config.secrets.location}/${n}") config.secrets.keys; |
79 | readOnly = true; | 108 | readOnly = true; |
80 | description = "set of full paths to secrets"; | 109 | description = "set of full paths to secrets"; |
81 | }; | 110 | }; |
@@ -83,7 +112,7 @@ | |||
83 | 112 | ||
84 | config = let | 113 | config = let |
85 | location = config.secrets.location; | 114 | location = config.secrets.location; |
86 | keys = config.secrets.keys; | 115 | keys = builtins.attrValues config.secrets.keys; |
87 | empty = pkgs.runCommand "empty" { preferLocalBuild = true; } "mkdir -p $out && touch $out/done"; | 116 | empty = pkgs.runCommand "empty" { preferLocalBuild = true; } "mkdir -p $out && touch $out/done"; |
88 | fpath = v: "secrets/${v.dest}${lib.optionalString v.isTemplated ".gucci.tpl"}"; | 117 | fpath = v: "secrets/${v.dest}${lib.optionalString v.isTemplated ".gucci.tpl"}"; |
89 | dumpKey = v: | 118 | dumpKey = v: |
@@ -125,7 +154,24 @@ | |||
125 | exclPath = builtins.concatStringsSep " -o " (map (d: " -path $TMP/${d.dest}") dirs); | 154 | exclPath = builtins.concatStringsSep " -o " (map (d: " -path $TMP/${d.dest}") dirs); |
126 | in | 155 | in |
127 | lib.optionalString (builtins.length dirs > 0) " -not \\( ${exclPath} \\) "; | 156 | lib.optionalString (builtins.length dirs > 0) " -not \\( ${exclPath} \\) "; |
157 | |||
158 | checkKeyDependencies = key: | ||
159 | let | ||
160 | allDeps = builtins.map (n: if builtins.isPath n then "${n}" else n.drvPath) (key.keyDependencies ++ key.ignoredKeyDependencies); | ||
161 | context = builtins.attrNames (builtins.getContext key.text); | ||
162 | missing = builtins.foldl' (o: n: lib.remove n o) context allDeps; | ||
163 | in | ||
164 | lib.optional (!key.isDir && builtins.length missing > 0) | ||
165 | '' | ||
166 | Key ${key.dest} has non declared dependencies in its context: ${builtins.concatStringsSep " " missing} | ||
167 | Add them to ignoredKeyDependencies to ignore | ||
168 | ''; | ||
128 | in lib.mkIf (builtins.length keys > 0) { | 169 | in lib.mkIf (builtins.length keys > 0) { |
170 | warnings = lib.concatMap checkKeyDependencies keys; | ||
171 | # FIXME: Use lib.concatMap (k: k.keyDependencies) keys in latest nixpkgs | ||
172 | system.extraDependencies = lib.concatMap (k: builtins.map (dep: | ||
173 | if builtins.isPath dep then pkgs.writeText "extra-dep" "${dep}" else dep | ||
174 | ) k.keyDependencies) keys; | ||
129 | system.activationScripts.secrets = { | 175 | system.activationScripts.secrets = { |
130 | deps = [ "users" "wrappers" ]; | 176 | deps = [ "users" "wrappers" ]; |
131 | text = '' | 177 | text = '' |
@@ -133,13 +179,11 @@ | |||
133 | TMP=$(${pkgs.coreutils}/bin/mktemp -d) | 179 | TMP=$(${pkgs.coreutils}/bin/mktemp -d) |
134 | TMPWORK=$(${pkgs.coreutils}/bin/mktemp -d) | 180 | TMPWORK=$(${pkgs.coreutils}/bin/mktemp -d) |
135 | chmod go-rwx $TMPWORK | 181 | chmod go-rwx $TMPWORK |
136 | if [ -n "$TMP" -a -n "$TMPWORK" ]; then | 182 | if [ -n "$TMP" -a -n "$TMPWORK" -a -f ${config.secrets.secretsVars} ]; then |
137 | install -m0750 -o root -g keys -d $TMP | 183 | install -m0750 -o root -g keys -d $TMP |
138 | ${pkgs.ssh-to-age}/bin/ssh-to-age -private-key -i ${config.secrets.decryptKey} -o $TMPWORK/keys.txt | 184 | ${pkgs.ssh-to-age}/bin/ssh-to-age -private-key -i ${config.secrets.decryptKey} -o $TMPWORK/keys.txt |
139 | SOPS_AGE_KEY_FILE=$TMPWORK/keys.txt ${pkgs.sops}/bin/sops -d ${secrets} | ${pkgs.gnutar}/bin/tar --strip-components 1 -C $TMP -x | 185 | SOPS_AGE_KEY_FILE=$TMPWORK/keys.txt ${pkgs.sops}/bin/sops -d ${secrets} | ${pkgs.gnutar}/bin/tar --strip-components 1 -C $TMP -x |
140 | if [ -f ${config.secrets.secretsVars} ]; then | 186 | SOPS_AGE_KEY_FILE=$TMPWORK/keys.txt ${pkgs.sops}/bin/sops -d ${config.secrets.secretsVars} > $TMPWORK/vars.yml |
141 | SOPS_AGE_KEY_FILE=$TMPWORK/keys.txt ${pkgs.sops}/bin/sops -d ${config.secrets.secretsVars} > $TMPWORK/vars.yml | ||
142 | fi | ||
143 | if [ -f $TMPWORK/vars.yml ]; then | 187 | if [ -f $TMPWORK/vars.yml ]; then |
144 | find $TMP -name "*.gucci.tpl" -exec \ | 188 | find $TMP -name "*.gucci.tpl" -exec \ |
145 | /bin/sh -c 'f="{}"; ${pkgs.gucci}/bin/gucci -f '$TMPWORK'/vars.yml "$f" > "''${f%.gucci.tpl}"; touch --reference "$f" ''${f%.gucci.tpl} ; chmod --reference="$f" ''${f%.gucci.tpl} ; chown --reference="$f" ''${f%.gucci.tpl}' \; | 189 | /bin/sh -c 'f="{}"; ${pkgs.gucci}/bin/gucci -f '$TMPWORK'/vars.yml "$f" > "''${f%.gucci.tpl}"; touch --reference "$f" ''${f%.gucci.tpl} ; chmod --reference="$f" ''${f%.gucci.tpl} ; chown --reference="$f" ''${f%.gucci.tpl}' \; |