From a00c276c5c8383b4c4c532ed07561453a9bd29a7 Mon Sep 17 00:00:00 2001 From: clerie Date: Thu, 7 Dec 2023 20:17:31 +0100 Subject: [PATCH] secrets.nix: Document the magic --- secrets.nix | 67 +++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 63 insertions(+), 4 deletions(-) diff --git a/secrets.nix b/secrets.nix index 098713f..a9ee68d 100644 --- a/secrets.nix +++ b/secrets.nix @@ -16,6 +16,15 @@ */ let + /* + Returns an attrset for a given directory, + having the name of a subdirectory as its attribute names + and the contents of the containing ssh.pub file as their value + + { + clerie = "ssh-ed25519 AAAA..."; + } + */ pubkeysFor = directory: let instances = builtins.attrNames (builtins.readDir directory); instancesWithPubkey = builtins.filter (i: builtins.pathExists (directory + "/${i}/ssh.pub")) instances; @@ -25,13 +34,63 @@ let users = pubkeysFor ./users; hosts = pubkeysFor ./hosts; + /* + Returns secret configuration for a given hostname + */ secretsForHost = hostname: let + /* + Returns a list of all file names in the secrets directory of the specified host + */ secretsFiles = builtins.attrNames (builtins.readDir (./hosts + "/${hostname}/secrets")); - listOfSecrets = builtins.filter (i: (builtins.stringLength i) > 4 && builtins.substring ((builtins.stringLength i) - 4) (builtins.stringLength i) i == ".age") secretsFiles; + + /* + Returns all file names that end with .age + */ + listOfSecrets = builtins.filter (i: + # Make sure the file name is longer than the file extension + (builtins.stringLength i) > 4 + # Take the last four letters of the file name and check if it is .age + && builtins.substring ((builtins.stringLength i) - 4) (builtins.stringLength i) i == ".age" + ) secretsFiles; + in - if builtins.pathExists (./hosts + "/${hostname}/secrets") && builtins.pathExists (./hosts + "/${hostname}/ssh.pub") then - map (secret: { name = "hosts/${hostname}/secrets/${secret}"; value = { publicKeys = [ users.clerie hosts."${hostname}" ]; }; }) (listOfSecrets ++ [ "new" ]) + if + # Make sure the host has a secrets directory + builtins.pathExists (./hosts + "/${hostname}/secrets") + # Make sure the host has a public ssh key provided + && builtins.pathExists (./hosts + "/${hostname}/ssh.pub") + then + /* + This map specifies all public keys for which a given secret file should be encrypted + It returns a list of name value pairs + The name is the path to the secret file + The value is an attribute set containing a list of public keys as a string + */ + map + (secret: { + name = "hosts/${hostname}/secrets/${secret}"; + value = { + publicKeys = [ + # Hardcode clerie's public key here + users.clerie + # No other user should have access to any secrets + + # A host should only have access to their own secrets + hosts."${hostname}" + ]; + }; + }) + # All file names of already existing secrets plus the magic "new" secret + (listOfSecrets ++ [ "new" ]) else + # Answer with an empty list, if no secrets are provided for a host []; in - builtins.listToAttrs (builtins.concatMap (hostname: secretsForHost hostname) (builtins.attrNames (builtins.readDir ./hosts))) + # We just have a list of name value pairs that need to get transformed into an attribute set + builtins.listToAttrs ( + builtins.concatMap + # Provide a list of secrets for a given hostname + (hostname: secretsForHost hostname) + # Names of all hosts + (builtins.attrNames (builtins.readDir ./hosts)) + )