ryantm / agenix

age-encrypted secrets for NixOS and Home manager
https://matrix.to/#/#agenix:nixos.org
Creative Commons Zero v1.0 Universal
1.48k stars 117 forks source link

Documentation improvements #133

Open RuRo opened 1 year ago

RuRo commented 1 year ago

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:

1) agenix has 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.

2) Assuming that my understanding of (1) is correct, what does the CLI provide over plain age/rage? Can I just encrypt my secrets with plain age (or with something like git-agecrypt) and then decrypt them at system startup with the agenix.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.

3) 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>.file` seems 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 `agenix` CLI or the NixOS module, or both?

4) The documentation states that SSH keys are supported as identity files, but GPG isn't. age also has its own key mechanism (AGE-SECRET-KEY-... and age1...). Is this type of key supported?

5) 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](https://github.com/nmattia/niv) First add it to niv: ```ShellSession $ niv add ryantm/agenix ``` #### Install module via niv Then add the following to your `configuration.nix` in the `imports` list: ```nix { imports = [ "${(import ./nix/sources.nix).agenix}/modules/age.nix" ]; } ``` #### Install CLI via niv To install the `agenix` binary: ```nix { environment.systemPackages = [ (pkgs.callPackage "${(import ./nix/sources.nix).agenix}/pkgs/agenix.nix" {}) ]; } ```
### Install via nix-channel As root run: ```ShellSession $ 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: ```nix { imports = [ ]; } ``` #### Install CLI via nix-channel To install the `agenix` binary: ```nix { environment.systemPackages = [ (pkgs.callPackage {}) ]; } ```
### Install via fetchTarball #### Install module via fetchTarball Add the following to your configuration.nix: ```nix { imports = [ "${builtins.fetchTarball "https://github.com/ryantm/agenix/archive/main.tar.gz"}/modules/age.nix" ]; } ``` or with pinning: ```nix { 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: ```nix { 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 ```nix { 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, ```ShellSession nix run github:ryantm/agenix -- --help ``` but, if you want to (change the system based on your system): ```nix { environment.systemPackages = [ agenix.defaultPackage.x86_64-linux ]; } ```
ryantm commented 1 year ago

I'll answer your questions here, but I intend to incorporate this into the docs too eventually.

  • agenix has 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 plain age (or with something like git-agecrypt) and then decrypt them at system startup with the agenix.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>.file seems 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 agenix CLI 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. age also has its own key mechanism (AGE-SECRET-KEY-... and age1...). 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.

n8henrie commented 1 year ago

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/*.age are encrypted and secrets/secrets.nix are public, so secrets can reasonably be checked into VCS if it contains no other private files.

RuRo commented 1 year ago

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

1) [on the build system] Encrypt some secret file foo and store the resulting file bar.age somewhere (inside the repo in case of flakes)

2) [on the build system] Build the NixOS system derivation with age.secrets.foo.file = ./path/to/bar.age; (optionally set age.identityPaths = [ /custom/path/to/identity ];)

3) [on the target system] Ensure that /etc/ssh/ssh_host_*_key (or /custom/path/to/identity if identityPaths were set) exist and can decrypt bar.age

4) [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?