1
0
Fork 0
nixfiles/secrets.nix

97 lines
3.4 KiB
Nix

/*
Because I'm way too lazy I'm automatically generating the secret files config.
Secrets can be found below
hosts/${hostname}/secrets/*.age
Pubkeys can be found for the specific host below
hosts/${hostname}/ssh.pub
The users have their keys below
users/${username}/ssh.pub
Secrets get encrypted for the host they are in and the users specified.
Every host with a secrets directory has an entry for a secret called "new".
This exist to overcome the chicken and egg problem.
Create a secret with them name new in the specific secrets directory and rename it afterwards with the suffix .age.
*/
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;
in
builtins.listToAttrs (map (i: { name = i; value = builtins.readFile (directory + "/${i}/ssh.pub"); }) instancesWithPubkey);
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"));
/*
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
# 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
# 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))
)