Leveraging age and agenix this project gives you the ability to inject variables containing secrets into your flakes' devShells
.
This simplify a lot the onboarding phase for new developers allowing them to share secrets (it's possible to set who can access which secrets) and make projects more self-contained eliminating the need of external tools.
Minimal knowledge of how agenix operates is required, indeed it relies on having the same setup as agenix
i.e. a secrets
directory containing all the encrypted secrets and a secrets.nix
file that lists them and specifies which keys can be used to decrypt each secret.
Example of secrets/secrets.nix
file:
{
"foo.age".publicKeys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPDpVA+jisOuuNDeCJ67M11qUP8YY29cipajWzTFAobi"
];
}
Notice how while this is the format expected by agenix
script you are not required to use the same since agenix-shell
will require you to set the paths for the .age
files (exactly as the agenix
modules do).
agenix-shell
will inject two environment variables for each secret, one containing the cleartext secret itself and the other one containing the path to the secret. Assuming the example above you will get:
FOO
containing the secretFOO_PATH
containing the path to the secret (agenix-shell
automatically appends _PATH
){
devShells.${system}.default = let
installationScript = inputs.agenix-shell.packages.${system}.installationScript.override {
agenixShellConfig.secrets = {
foo.file = ./secrets/foo.age;
};
};
in pkgs.mkShell {
shellHook = ''
source ${lib.getExe installationScript}
'';
};
}
Check the basic example for a working example (you will need to delete the encrypted secret and encrypt your own with your key). Otherwise you could copy the used key.
nix flake init -t github:aciceri/agenix-shell#basic
Notice that internally this approach uses flake-parts
for reading agenixShellConfig
, so you can browse the automatically generated documentation on flake.parts for understanding all the options available that you can pass in agenixShellConfig
.
flake-parts
{
imports = [
inputs.agenix-shell.flakeModules.default
];
agenix-shell = {
secrets = {
foo.file = ./secrets/foo.age;
};
};
perSystem = {pkgs, config, lib, ... }: {
devShells.default = pkgs.mkShell {
shellHook = ''
source ${lib.getExe config.agenix-shell.installationScript}
'';
};
};
}
Check the flake-parts template for a working example (you will need to delete the encrypted secret and encrypt your own with your key). Otherwise you could copy the used key.
nix flake init -t github:aciceri/agenix-shell#flake-parts
devenv
Here a working template.
nix flake init -t github:aciceri/agenix-shell#devenv
The functioning is quite simple, agenix-shell
exports a configurable script that will be sourced somewhere in the devShell
(like in an hook
). This script will:
$HOME/.ssh/id_rsa
or $HOME/.ssh/id_ed25519
)/run/user/$(id -u)/agenix-shell
(it creates a directory using the flake name and an UUID)That's it! Everything is as customizable as possible using the appropriate options. Check flake.parts for a complete list (and to know defaults).
flake-parts
module that integrates with devshellagenix-shell
in a real project and showcase it here