Documentation improvements
I find the current documentation to be a bit lacking. There is no link to further documentation in the README, so I am assuming that the README is the full documentation.
Here are some questions that are (imho) not fully addressed in the readme:
-
agenixhas a CLI and a NixOS module. What's the purpose of each component? It seems, that the CLI handles encryption and the NixOS module handles the decryption, but this is not clear.It is mentioned in the first line of the documentation, but later parts of the documentation just use "agenix" to refer both to the CLI and the NixOS module, so it can be confusing, which component is being described.
-
Assuming that my understanding of (1) is correct, what does the CLI provide over plain
age/rage? Can I just encrypt my secrets with plainage(or with something likegit-agecrypt) and then decrypt them at system startup with theagenix.nixosModule?Also, the first line of the documentation seems to imply, that the NixOS module is a secondary part of the project. This seems a bit backwards to me. If my understanding is correct, the CLI would be basically useless without the NixOS module.
-
The tutorial suggests, that you should "Make a directory to store secrets and secrets.nix", but it is not clear
-
Is creating this directory merely a suggestion, or does the NixOS module explicitly rely on this layout (even though
age.secrets.<name>.fileseems to accept arbitrary paths)? -
Where should you create this directory? In the system flake repository? On the development machine (that will encrypt the secrets)? On the deployment machine (that will decrypt the secrets)?
-
Is this directory used by the
agenixCLI or the NixOS module, or both?
-
-
The documentation states that SSH keys are supported as identity files, but GPG isn't.
agealso has its own key mechanism (AGE-SECRET-KEY-...andage1...). Is this type of key supported? -
At which point in the boot process are the secrets decrypted?
P.S. The Installation section takes up way too much space and for most users 75% of it won't be actually useful to them. Consider using <details><summary>blah</summary>contents</details> in some parts of the documentation. Something like this:
Install via niv
First add it to niv:
$ niv add ryantm/agenix
Install module via niv
Then add the following to your configuration.nix in the imports list:
{
imports = [ "${(import ./nix/sources.nix).agenix}/modules/age.nix" ];
}
Install CLI via niv
To install the agenix binary:
{
environment.systemPackages = [ (pkgs.callPackage "${(import ./nix/sources.nix).agenix}/pkgs/agenix.nix" {}) ];
}
Install via nix-channel
As root run:
$ sudo nix-channel --add https://github.com/ryantm/agenix/archive/main.tar.gz agenix
$ sudo nix-channel --update
Install module via nix-channel
Then add the following to your configuration.nix in the imports list:
{
imports = [ <agenix/modules/age.nix> ];
}
Install CLI via nix-channel
To install the agenix binary:
{
environment.systemPackages = [ (pkgs.callPackage <agenix/pkgs/agenix.nix> {}) ];
}
Install via fetchTarball
Install module via fetchTarball
Add the following to your configuration.nix:
{
imports = [ "${builtins.fetchTarball "https://github.com/ryantm/agenix/archive/main.tar.gz"}/modules/age.nix" ];
}
or with pinning:
{
imports = let
# replace this with an actual commit id or tag
commit = "298b235f664f925b433614dc33380f0662adfc3f";
in [
"${builtins.fetchTarball {
url = "https://github.com/ryantm/agenix/archive/${commit}.tar.gz";
# replace this with an actual hash
sha256 = "0000000000000000000000000000000000000000000000000000";
}}/modules/age.nix"
];
}
Install CLI via fetchTarball
To install the agenix binary:
{
environment.systemPackages = [ (pkgs.callPackage "${builtins.fetchTarball "https://github.com/ryantm/agenix/archive/main.tar.gz"}/pkgs/agenix.nix" {}) ];
}
Install via Flakes
Install module via Flakes
{
inputs.agenix.url = "github:ryantm/agenix";
# optional, not necessary for the module
#inputs.agenix.inputs.nixpkgs.follows = "nixpkgs";
outputs = { self, nixpkgs, agenix }: {
# change `yourhostname` to your actual hostname
nixosConfigurations.yourhostname = nixpkgs.lib.nixosSystem {
# change to your system:
system = "x86_64-linux";
modules = [
./configuration.nix
agenix.nixosModule
];
};
};
}
Install CLI via Flakes
You don't need to install it,
nix run github:ryantm/agenix -- --help
but, if you want to (change the system based on your system):
{
environment.systemPackages = [ agenix.defaultPackage.x86_64-linux ];
}
I'll answer your questions here, but I intend to incorporate this into the docs too eventually.
agenixhas a CLI and a NixOS module. What's the purpose of each component? It seems, that the CLI handles encryption and the NixOS module handles the decryption, but this is not clear.
This project provides a NixOS module age for adding age-encrypted secrets into the Nix store and decrypting them on NixOS at boot or rebuild time. It also provides a commandline tool agenix which can be used to edit and rekey the secrets and manage who can decrypt them. The documentation mostly assumes you are encrypting the secrets with your SSH keys, but all age methods can be used.
- Assuming that my understanding of (1) is correct, what does the CLI provide over plain
age/rage? Can I just encrypt my secrets with plainage(or with something likegit-agecrypt) and then decrypt them at system startup with theagenix.nixosModule?
Yes, you can encrypt your secrets with plain age. You need to make sure you encrypt it such that both your user and system age identities can decrypt them.
Also, the first line of the documentation seems to imply, that the NixOS module is a secondary part of the project. This seems a bit backwards to me. If my understanding is correct, the CLI would be basically useless without the NixOS module.
Correct, the module is critical and the cli is technically optional.
The tutorial suggests, that you should "Make a directory to store secrets and secrets.nix", but it is not clear
- Is creating this directory merely a suggestion, or does the NixOS module explicitly rely on this layout (even though
age.secrets.<name>.fileseems to accept arbitrary paths)?
Merely a suggestion. The module does not rely on this layout.
- Where should you create this directory? In the system flake repository? On the development machine (that will encrypt the secrets)? On the deployment machine (that will decrypt the secrets)?
I typically make the secrets directory next to my configuration. So I guess the default vanilla way would be to put it at /etc/nixos/secrets.
- Is this directory used by the
agenixCLI or the NixOS module, or both?
The agenix CLI uses it, in the sense that you must run the cli from a directory that contains a secrets.nix file, but the encrypted secrets don't need to be in that directory. The module doesn't care about the secrets.nix at all.
- The documentation states that SSH keys are supported as identity files, but GPG isn't.
agealso has its own key mechanism (AGE-SECRET-KEY-...andage1...). Is this type of key supported?
All default age identities are supported.
- At which point in the boot process are the secrets decrypted?
The age module's activation scripts run after the "specialfs" activation scripts, and don't finish until after the "users" and "groups" activation scripts have finished.
Add-on Q:
Currently the documentation emphasizes, re: secrets/secrets.nix:
This file is not imported into your NixOS configuration
The bold makes me think this is an important point -- is it? It's literally just a mapping of (non-secret) public keys to filenames, I wouldn't think it would matter if it were imported to the configuration (and hence the store).
I use a flake which defines several systems; for purity this requires all referenced files to be in the git repo, so my current setup is:
flake.nix
...
secrets/secrets.nix
secrets/secret1.age
All of which will be included in my repo, which is fine because none of it is ~"secret"~ secret and unencrypted. Is that correct?
If so, I might propose some clarification that makes it explicit:
The contents of
secrets/*.ageare encrypted andsecrets/secrets.nixare public, sosecretscan reasonably be checked into VCS if it contains no other private files.
You need to make sure you encrypt it such that both your user and system age identities can decrypt them.
By "system identity" I assume you mean the ones specified in age.identityPaths, but what is the "user identity" in this case and why is it needed?
Where should you create this directory? In the system flake repository? On the development machine (that will encrypt the secrets)? On the deployment machine (that will decrypt the secrets)?
I typically make the secrets directory next to my configuration. So I guess the default vanilla way would be to put it at /etc/nixos/secrets.
So, please correct me if I am wrong, the "minimal" usage with separate build/target systems would be
-
[on the build system] Encrypt some secret file
fooand store the resulting filebar.agesomewhere (inside the repo in case of flakes) -
[on the build system] Build the NixOS system derivation with
age.secrets.foo.file = ./path/to/bar.age;(optionally setage.identityPaths = [ /custom/path/to/identity ];) -
[on the target system] Ensure that
/etc/ssh/ssh_host_*_key(or/custom/path/to/identityifidentityPathswere set) exist and can decryptbar.age -
[on the target system] Push the derivation to target system and switch/reboot into it
Crucially, the build system doesn't need to be able to decrypt the secrets (no /etc/ssh/ssh_host_*_key or /custom/path/to/identity) and the target system doesn't need to have bar.age stored anywhere (no /etc/nixos/path/to/bar.age for example). Is this correct?