2020-12-18 18:49:50 +01:00
# agenix - [age](https://github.com/FiloSottile/age)-encrypted secrets for NixOS
2020-09-03 21:03:01 +02:00
2020-12-18 18:49:50 +01:00
`agenix` is a commandline tool for managing secrets encrypted with your existing SSH keys. This project also includes the NixOS module `age` for adding encrypted secrets into the Nix store and decrypting them.
2020-09-03 21:03:01 +02:00
2020-12-18 18:49:50 +01:00
## Problem and solution
All files in the Nix store are readable by any system user, so it is not a suitable place for including cleartext secrets. Many existing tools (like NixOps deployment.keys) deploy secrets separately from `nixos-rebuild` , making deployment, caching, and auditing more difficult. Out-of-band secret management is also less reproducible.
`agenix` solves these issues by using your pre-existing SSH key infrastructure and `age` to encrypt secrets into the Nix store. Secrets are decrypted using an SSH host private key during NixOS system activation.
2020-09-03 22:16:44 +02:00
## Features
2020-09-03 21:03:01 +02:00
* Secrets are encrypted with SSH keys
2020-09-03 22:18:21 +02:00
* system public keys via `ssh-keyscan`
* can use public keys available on GitHub for users (for example, https://github.com/ryantm.keys)
2020-09-03 21:03:01 +02:00
* No GPG
* Very little code, so it should be easy for you to audit
2020-09-04 06:12:02 +02:00
* Encrypted secrets are stored in the Nix store, so a separate distribution mechanism is not necessary
2020-09-03 21:03:01 +02:00
2020-12-18 19:09:17 +01:00
## Notices
2020-12-19 00:40:34 +01:00
* Password-protected ssh keys: since the underlying tool age/rage do not support ssh-agent, password-protected ssh keys do not work well. For example, if you need to rekey 20 secrets you will have to enter your password 20 times.
2021-05-03 03:27:44 +02:00
* If you want to manage user's hashed passwords, you must use a version of NixOS with [commit e6b8587 ](https://github.com/NixOS/nixpkgs/commit/e6b8587b25a19528695c5c270e6ff1c209705c31 ), so the root-owned secrets can be decrypted before the user activation script runs. Currently available on up to date `20.09` and `unstable` .
2020-12-18 19:09:17 +01:00
2020-09-03 22:16:44 +02:00
## Installation
2020-09-03 21:03:01 +02:00
Choose one of the following methods:
2020-09-03 22:16:44 +02:00
### [niv](https://github.com/nmattia/niv) (Current recommendation)
2020-09-03 21:03:01 +02:00
First add it to niv:
2020-12-18 18:49:50 +01:00
```ShellSession
2020-09-03 21:03:01 +02:00
$ niv add ryantm/agenix
```
2020-09-03 22:16:44 +02:00
#### Module
Then add the following to your configuration.nix in the `imports` list:
2020-09-03 21:03:01 +02:00
```nix
{
imports = [ "${(import ./nix/sources.nix).agenix}/modules/age" ];
}
```
2020-09-03 22:16:44 +02:00
### nix-channel
2020-09-03 21:03:01 +02:00
As root run:
2020-12-18 18:49:50 +01:00
```ShellSession
2020-09-03 21:03:01 +02:00
$ nix-channel --add https://github.com/ryantm/agenix/archive/master.tar.gz agenix
$ nix-channel --update
```
Than add the following to your configuration.nix in the `imports` list:
```nix
{
imports = [ < agenix / modules / age > ];
}
```
2020-09-03 22:16:44 +02:00
### fetchTarball
2020-09-03 21:03:01 +02:00
Add the following to your configuration.nix:
2020-09-03 22:16:44 +02:00
```nix
2020-09-03 21:03:01 +02:00
{
imports = [ "${builtins.fetchTarball "https://github.com/ryantm/agenix/archive/master.tar.gz"}/modules/age" ];
}
```
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"
];
}
```
2020-09-03 22:16:44 +02:00
### Flakes
#### Module
2020-09-03 21:03:01 +02:00
2020-09-03 22:16:44 +02:00
```nix
2020-09-03 21:03:01 +02:00
{
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.nixosModules.age
];
};
};
}
```
2020-09-03 22:16:44 +02:00
#### CLI
2020-09-04 16:13:03 +02:00
You don't need to install it,
2020-09-03 22:16:44 +02:00
2020-12-18 18:49:50 +01:00
```ShellSession
2020-09-03 22:16:44 +02:00
nix run github:ryantm/agenix -- --help
```
2020-09-04 16:13:03 +02:00
but, if you want to (change the system based on your system):
2020-09-04 06:12:02 +02:00
```nix
{
environment.systemPackages = [ agenix.defaultPackage.x86_64-linux ];
}
```
2020-09-03 22:16:44 +02:00
## Tutorial
2020-09-04 00:18:20 +02:00
1. Make a directory to store secrets and `secrets.nix` file for listing secrets and their public keys:
2020-09-03 22:16:44 +02:00
2020-12-18 18:49:50 +01:00
```ShellSession
2020-09-03 22:16:44 +02:00
$ mkdir secrets
2020-12-18 20:37:23 +01:00
$ cd secrets
2020-09-04 00:18:20 +02:00
$ touch secrets.nix
2020-09-03 22:16:44 +02:00
```
2020-09-04 00:18:20 +02:00
2. Add public keys to `secrets.nix` file (hint: use `ssh-keyscan` or GitHub (for example, https://github.com/ryantm.keys)):
```nix
let
user1 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIL0idNvgGiucWgup/mP78zyC23uFjYq0evcWdjGQUaBH";
2020-12-18 18:49:50 +01:00
user2 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILI6jSq53F/3hEmSs+oq9L4TwOo1PrDMAgcA1uo1CCV/";
users = [ user1 user2 ];
2020-09-04 00:18:20 +02:00
system1 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPJDyIr/FSz1cJdcoW69R+NrWzwGK/+3gJpqD1t8L2zE";
2020-12-18 18:49:50 +01:00
system2 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKzxQgondgEYcLpcPdJLrTdNgZ2gznOHCAxMdaceTUT1";
systems = [ system1 system2 ];
2020-09-04 00:18:20 +02:00
in
{
2020-09-04 06:13:10 +02:00
"secret1.age".publicKeys = [ user1 system1 ];
2020-12-18 18:49:50 +01:00
"secret2.age".publicKeys = users ++ systems;
2020-09-04 00:18:20 +02:00
}
2020-09-03 22:16:44 +02:00
```
2020-12-18 18:49:50 +01:00
3. Edit secret files (these instructions assume your SSH private key is in ~/.ssh/):
```ShellSession
2020-09-03 22:16:44 +02:00
$ agenix -e secret1.age
```
2020-12-18 18:49:50 +01:00
4. Add secret to a NixOS module config:
2020-09-03 22:16:44 +02:00
```nix
2020-09-04 06:12:02 +02:00
age.secrets.secret1.file = ../secrets/secret1.age;
2020-09-03 22:16:44 +02:00
```
2021-04-08 20:47:48 +02:00
2020-12-18 18:49:50 +01:00
5. NixOS rebuild or use your deployment tool like usual.
2020-09-03 22:16:44 +02:00
2021-04-09 02:03:08 +02:00
The secret will be decrypted to the value of `age.secrets.secret1.path` (`/run/secrets/secret1` by default). For per-secret options controlling ownership etc, see [modules/age.nix ](modules/age.nix ).
2021-04-08 20:47:48 +02:00
2020-09-03 22:16:44 +02:00
## Rekeying
2020-09-04 00:18:20 +02:00
If you change the public keys in `secrets.nix` , you should rekey your
2020-09-03 22:16:44 +02:00
secrets:
2020-12-18 18:49:50 +01:00
```ShellSession
2020-09-03 22:16:44 +02:00
$ agenix --rekey
```
To rekey a secret, you have to be able to decrypt it. Because of
randomness in `age` 's encryption algorithms, the files always change
2020-12-18 18:49:50 +01:00
when rekeyed, even if the identities do not. (This eventually could be
improved upon by reading the identities from the age file.)
2020-09-03 22:16:44 +02:00
## Threat model/Warnings
2020-09-03 22:35:15 +02:00
This project has not be audited by a security professional.
2020-09-03 22:16:44 +02:00
People unfamiliar with `age` might be surprised that secrets are not
authenticated. This means that every attacker that has write access to
2020-12-18 18:49:50 +01:00
the secret files can modify secrets because public keys are exposed.
2020-09-03 22:16:44 +02:00
This seems like not a problem on the first glance because changing the
2020-12-18 18:49:50 +01:00
configuration itself could expose secrets easily. However, reviewing
configuration changes is easier than reviewing random secrets (for
example, 4096-bit rsa keys). This would be solved by having a message
2020-09-03 22:16:44 +02:00
authentication code (MAC) like other implementations like GPG or
[sops ](https://github.com/Mic92/sops-nix ) have, however this was left
out for simplicity in `age` .
## Acknowledgements
2020-09-03 21:03:01 +02:00
2020-12-18 18:49:50 +01:00
This project is based off of [sops-nix ](https://github.com/Mic92/sops-nix ) created Mic92. Thank you to Mic92 for inspiration and advice.