diff --git a/docs/secret-management.md b/docs/secret-management.md new file mode 100644 index 0000000..a44d429 --- /dev/null +++ b/docs/secret-management.md @@ -0,0 +1,160 @@ +# Secret management and `sops-nix` + +Nix config is love, nix config is life, and publishing said config to the +internet is not only a good deed and kinda cool, but also encourages properly +secured configuration as opposed to [security through obscurity](https://en.wikipedia.org/wiki/Security_through_obscurity). +That being said, there are some details of the config that we really shouldn't +share with the general public. In particular, there are so-called *secrets*, that is +API keys, passwords, tokens, cookie secrets, salts, peppers and jalapenos that we'd +rather keep to ourselves. However, it is not entirely trivial to do so in the NixOS config. +For one, we'd have to keep these secrets out of the public git repo somehow, and secondly +everything that is configured via nix ends up as world readable files (i.e. any user on the +system can read the file) in `/nix/store`. + +In order to solve this, we use a NixOS module called [`sops-nix`](https://github.com/Mic92/sops-nix) +which uses a technology called [`sops`](https://github.com/getsops/sops) behind the scenes. +The idea is simple: we encrypt these secrets with a bunch of different keys and store the +encrypted files in the git repo. First of all, we encrypt the secrets a bunch of time with +PVV maintenance member's keys, so that we can decrypt and edit the contents. Secondly, we +encrypt the secrets with the [host keys]() of the NixOS machines, so that they can decrypt +the secrets. The secrets will be decrypted and stored in a well-known location (usually `/run/secrets`) +so that they do not end up in the nix store, and are not world readable. + +This way, we can both keep the secrets in the git repository and let multiple people edit them, +but also ensure that they don't end up in the wrong hands. + +## Adding a new machine + +In order to add a new machine to the nix-sops setup, you should do the following: + +```console +# Create host keys (if they don't already exist) +ssh-keygen -A -b 4096 + +# Derive an age-key from the public host key +nix-shell -p ssh-to-age --run 'cat /etc/ssh/ssh_host_ed25519_key.pub | ssh-to-age' + +# Register the age key in .sops.yaml +vim .sops.yaml +``` + +The contents of `.sops.yaml` should look like this: + +```yaml +keys: + # Users + ... + + # Hosts + ... + - &host_ + +creation_rules: + ... + + - path_regex: secrets//[^/]+\.yaml$ + key_groups: + - age: + - *host_ + - ... user keys + - pgp: + - ... user keys +``` + +> [!NOTE] +> Take care that all the keys in the `age` and `pgp` sections are prefixed +> with a `-`, or else sops might try to encrypt the secrets in a way where +> you need both keys present to decrypt the content. Also, it tends to throw +> interesting errors when it fails to do so. + +```console +# While cd-ed into the repository, run this to get a shell with the `sops` tool present +nix-shell +``` + +Now you should also be able to edit secrets for this machine by running: + +``` +sops secrets//.yaml +``` + +## Adding a user + +Adding a user is quite similar to adding a new machine. +This guide assumes you have already set up SSH keys. + +``` +# Derive an age-key from your key +# (edit the path to the key if it is named something else) +nix-shell -p ssh-to-age --run 'cat ~/.ssh/id_ed25519.pub | ssh-to-age' + +# Register the age key in .sops.yaml +vim .sops.yaml +``` + +The contents of `.sops.yaml` should look like this: + +```yaml +keys: + # Users + ... + - &user_ + + # Hosts + ... + +creation_rules: + ... + + # Do this for all the machines you are planning to edit + # (or just do it for all machines) + - path_regex: secrets//[^/]+\.yaml$ + key_groups: + - age: + - *host_ + - ... user keys + - *host_ + - pgp: + - ... user keys +``` + +Now that sops is properly configured to recognize the key, you need someone +who already has access to decrypt all the secrets and re-encrypt them with your +key. At this point, you should probably [open a PR](https://docs.gitea.com/usage/issues-prs/pull-request) +and ask someone in PVV maintenance if they can checkout the PR branch, run the following +command and push the diff back into the PR (and maybe even ask them to merge if you're feeling +particularly needy). + +```console +sops updatekeys secrets/*/*.yaml +``` + +## Updating keys + +> [!NOTE] +> At some point, we found this flag called `sops -r` that seemed to be described to do what +> `sops updatekeys` does, do not be fooled. This only rotates the "inner key" for those who +> already have the secrets encrypted with their key. + +Updating keys is done with this command: + +```console +sops updatekeys secrets/*/*.yaml +``` + +However, there is a small catch. [oysteikt](https://git.pvv.ntnu.no/oysteikt) has kinda been +getting gray hairs lately, and refuses to use modern technology - he is still stuck using GPG. +This means that to be able to re-encrypt the sops secrets, you will need to have a gpg keychain +with his latest public key available. The key has an expiry date, so if he forgets to update it, +you should send him and angry email and tag him a bunch of times in a gitea issue. If the key +is up to date, you can do the following: + +```console +# Fetch gpg (unless you have it already) +nix-shell -p gpg + +# Import oysteikts key to the gpg keychain +gpg --import ./keys/oysteikt.pub +``` + +Now you should be able to run the `sops updatekeys` command again.