GithubHelp home page GithubHelp logo

ryantm / agenix Goto Github PK

View Code? Open in Web Editor NEW
1.2K 19.0 100.0 202 KB

age-encrypted secrets for NixOS and Home manager

Home Page: https://matrix.to/#/#agenix:nixos.org

License: Creative Commons Zero v1.0 Universal

Nix 84.68% Shell 15.32%
nixos encryption

agenix's People

Contributors

aluisioasg avatar ambroisie avatar chuangzhu avatar cobaltcause avatar cole-h avatar edrex avatar eisfunke avatar felixscheinost avatar gabysbrain avatar jian-lin avatar jtojnar avatar kraem avatar malteneuss avatar montchr avatar n8henrie avatar nixinator avatar oluceps avatar pacman99 avatar rien avatar ryantm avatar samuelefacenda avatar scrumplex avatar shivak avatar sohalt avatar timhae avatar veehaitch avatar whentze avatar willpower3309 avatar winny- avatar ymarkus avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

agenix's Issues

Support for ssh-keys with passphrases

If I run agenix -e secret1.age and hit CTRL+C when it's asking for my passphrase. Then if I try to type stuff in my prompt, I don't see what I type.

[bbigras@desktop:~/nix-config]$ agenix -e secret1.age
Type passphrase for OpenSSH key '/home/bbigras/.ssh/id_ed25519':

Maybe it's a problem with the askpass thing or whatever.

Peek 18-12-2020 14-54

Feel free to rename the issue's title.

need to hard code user key paths

I have a few keys which are only encrypted with my user ssh keys to keep them consistent across systems. On a nixos-rebuild switch I was getting an error:

decrypting /nix/store/ddsxinl7ixlmfhlay848zy8qg916f1ks-google-vdirsyncer.age to /run/secrets/google-vdirsyncer...
Error: No matching keys found

[ Did rage not do what you expected? Could an error be more useful? ]
[ Tell us: https://str4d.xyz/rage/report                            ]
chmod: cannot access '/run/secrets/google-vdirsyncer.tmp': No such file or directory
chown: cannot access '/run/secrets/google-vdirsyncer.tmp': No such file or directory
mv: cannot stat '/run/secrets/google-vdirsyncer.tmp': No such file or directory

I traced this down to the fact that age,sshKeyPaths defaults to only the system rsa and ed25519 keys but does not try and look in user directories. Is this intentional? If so, I can update the documentation to reflect this but otherwise this will need some changes to the nixos module.

Thanks for an awesome package! This has made things so much easier!

Understanding secrets workflow

Hi. I'm currently converting my systems into nixos, and going with the flakes design already. I'm almost there, but I have a thing with agenix I cannot seem to get working.

I have a few things in my setup I do not want to push to the git repo. Secrets, that can easily stay unencrypted in my computer's memory, but that might not be such a great idea to push to the github.

So I have a nix file with my secrets, but what I can get from agenix is only the path to the unencrypted file, not the file content itself. Now let's say I have a file home.age, that has encrypted the following nix script:

{
  key = "secret";
}

Now, I'd import the data in my configuration with:

let
  home-secrets = import "${config.age.secrets.home.path}";
in {
  ...
}

But, this is not possible, and will lead to an error:

❯ sudo nixos-rebuild switch
warning: Git tree '/home/pimeys/.config/nixpkgs' is dirty
building the system configuration...
warning: Git tree '/home/pimeys/.config/nixpkgs' is dirty
error: --- RestrictedPathError ----------------------------------------------------------------------- nix
access to path '/run/secrets/home' is forbidden in restricted mode
(use '--show-trace' to show detailed location information)

I'm asking, is this even possible with agenix and flakes? What I want is to get the data out from the secrets, set them to system env vars and use them when my system boots up. If agenix is not the right tool for this, is there another way I could utilize a few tokens in nixos without needing to push them to a git repository?

Error: Header is invalid

I got this error but I have no idea how to resolve. I have a secrets.nix like this:

let
  macbook2017 =
    "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIESRdLYXkaqruy2cq7LFiwuYpkz+BIEGoaDz5pl58o+5 [email protected]";
in { "telegram-bot.age".publicKeys = [ macbook2017 ]; }

And I run it like this:

touch telegram-bot.age
nix run github:ryantm/agenix -- -e telegram-bot.age

And I got this:

warning: unknown setting 'experimental-features'
Error: Header is invalid

[ Did rage not do what you expected? Could an error be more useful? ]
[ Tell us: https://str4d.xyz/rage/report                            ]

Is this an upstream issue?

Activation of system should fail if secret file is not present

Hey, very cool project :)

I played around with it and noticed that the system will be activated even though the file configured as secret file is not present. Because other services depend on the decrypted file being present, it should fail the activation and print an error message to the user.

Secrets owned by non-root users are no longer readable by that user

Since 0.10 / #27

Relevant namei -l output:

drwxr-xr-x root root /
drwxr-xr-x root root run
lrwxrwxrwx root root agenix -> /run/agenix.d/2
drwxr-xr-x root root   /
drwxr-xr-x root root   run
drwxr-x--- root keys   agenix.d
                       2 - Permission denied

Even if I set symlink = false this doesn't work, since even if the secrets are decrypted to /run/agenix, they are actually decrypted to /run/agenix.d/2, since /run/agenix is already a symlink to /run/agenix.d/2 at that point. (Which kind of defeats the point of symlink = false IIUC. Maybe this should be a separate issue?)

I can add the relevant user(s) to the keys group to fix this behaviour, but this is not documented anywhere.

RFC: agenix-rs tool

Over the past few weeks, I've been working on a replacement for the shell script agenix tool, written in Rust. I've published it at https://github.com/cole-h/agenix-rs/.

I really like how sops-nix has the sops tool to edit binaries and then encrypt them to the proper recipients based on path globs specified in a config file, and I really dislike how the agenix script required a secrets.nix file for the identities.

I'm hoping for any and all feedback on it, and in the future it would be nice to replace the agenix shell script with agenix-rs.

One-way encryption, ad-hoc encryption for users, without private keys

The CLI instructions show an edit of an existing secret and having access to private keys necessary to do so. I may want to set up a situation where the ssh private key is unrecoverable by the user who needs to encrypt secrets. In such case, a one-way encryption workflow is necessary. The ad-hoc pathway to get encrypted secrets into the store, if there is one, is not documented on the README and needs to be pulled out of code. A user who can re-encrypt secrets can always control the contents of secrets without needing to access them, a blind edit.

I'm looking into the management of decryption keys still.

This issue is motivated by dotfiles use cases with multiple users, not the root user case.

Restrict folder permissions

Hello, I'm trying out agenix as a replacement for my current secret management scheme (krops + pass). Is there a reason why the /run/secrets/ directory has so open permissions of 755? I don't think others should be able to list the directory at all.

I think it would provide additional security at virtually no cost to set it to something like 0751 like sops-nix does or even to 440 so that only root can read them. If changing this in general is a problem, it would be nice to at least have the option to change them myself.

Can't get `agenix` into `nix shell`

I have the following shell.nix and nix shell fails because of a hash mismatch of rage-unstable. Am I doing something wrong?

I'm tracking unstable and nix-shell -p rage works great and rage --version prints 0.6.0 within that shell.

shell.nix

with (import <nixpkgs> {});
let
  imports =
    let agenixCommit = "204bd95";
    in {
        agenix = (callPackage "${builtins.fetchTarball {
                url = "https://github.com/ryantm/agenix/archive/${agenixCommit}.tar.gz";
                sha256 = "0xpfq3mnpnj7ygl4yphm8mvlhbx5a87p0nh3cjgnd2v1l4m0044g";
        }}" {}).agenix;
  };
in mkShell {
  buildInput = [
        imports.agenix
  ];
}

tail of nix-shell output

   Vendoring zeroize_derive v1.0.0 (/build/source/cargo-home.MaY/registry/src/github.com-1ecc6299db9ec823/zeroize_derive-1.0.0) to rage-unstable-2020-09-05-vendor.tar.gz/zeroize_derive
   Vendoring zip v0.5.6 (/build/source/cargo-home.MaY/registry/src/github.com-1ecc6299db9ec823/zip-0.5.6) to rage-unstable-2020-09-05-vendor.tar.gz/zip
To use vendored sources, add this to your .cargo/config.toml for this project:

installing
hash mismatch in fixed-output derivation '/nix/store/wbr2864sjaqfd9zgyj4hg71b7jzfwajj-rage-unstable-2020-09-05-vendor.tar.gz':
  wanted: sha256:0r3n5x89d4r5lmz3lxn8yrs52vkbdgag1qbnnffjdcpin6kns0s3
  got:    sha256:149wg4xnzn1fk8vwhgz4aqmqm2rqbl5yhpkjqlqsha6k9nqfmhw1
cannot build derivation '/nix/store/2wa4107rhbnaxpm8lndcrbfr42ir7lfz-rage-unstable-2020-09-05.drv': 1 dependencies couldn't be built
cannot build derivation '/nix/store/iqwbqlnqvmi6hqn6v46rbbx5nippjhzi-agenix.drv': 1 dependencies couldn't be built
error: build of '/nix/store/iqwbqlnqvmi6hqn6v46rbbx5nippjhzi-agenix.drv' failed

agenix has no meta description

so it doesn't explain itself, for example in a devshell:

[secrets]

  agenix
  ssh-show-ed25519     - ssh-show-ed25519 <user@hostName> | Show target host's SSH ed25519 key

I'll give it a shot

Support rules from flake output (nixosConfigurations)

Copy-pasting here from yaxitech/ragenix#52

Just an idea I wanted to propose:

Would it be possible to support reading rules from another flake's output instead of a separate secrets.nix file?
E.g. If I have a flake defining my systems like this:

{
  outputs = { self, nixpkgs }: {

    nixosConfigurations = {
      system1 = nixpkgs.lib.nixosSystem {
        system = "x86_64-linux";
        modules = [
          ({ pkgs, ... }:
            {
              # Config ...
              age.option-to-set-key = "ssh-ed25519 AAAAAAA...";
              age.secrets.secret1.file = ./secrets/secret1.age;
              age.secrets.secret2.file = ./secrets/secret2.age;
            })
        ];
      };

      system2 = nixpkgs.lib.nixosSystem {
        system = "x86_64-linux";
        modules = [
          ({ pkgs, ... }:
            {
              # Config
              age.option-to-set-key = "ssh-ed25519 AAAABBB...";
              age.secrets.secret2.file = ./secrets/secret2.age;
            })
        ];
      };
    };
  };
}

nixosConfigurations could be used directly resulting in a rule set equivalent to:

let
  system1 = "ssh-ed25519 AAAAAAA..";
  system2 = "ssh-ed25519 AAAABBB..";
in
{
  "secret1.age".publicKeys = [ system1 ];
  "secret2.age".publicKeys = [ system1 system2];
}

Since you plan on supporting flakes it seems like an extra step to have to write a secrets.nix file, as the information is already present in an organized form.

Confusing behaviour with age encryption/decryption

I've managed to set up agenix from unstable to a point where it appears to be working during the nixos-rebuild process. I've been using ssh host keys (located under /etc/ssh/) as outlined in:

map (e: e.path) (lib.filter (e: e.type == "rsa" || e.type == "ed25519") config.services.openssh.hostKeys)


This is my secrets.nix file I have:

let
  key =
    "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPWMxDpxfrlXhAyln0+MKZs7q3i1VimlHhGgUxVVaeYY Oxygen";
in {
  "ngrokConfig.age" = {
    publicKeys = [ key ];
  };
}

The issue I have arises when I use the agenix tool by command agenix -e ngrokConfig.age. When I first initialise the file it works, opening a blank file and jumping into the neovim process I have set as my editor. However the second time I try to do it, it gives back an error: No identity found to decrypt ngrokConfig.age. Try adding an SSH key at /home/padraic/.ssh/id_rsa or /home/padraic/.ssh/id_ed25519 or using the --identity flag to specify a file.

Using the -v option:

hosts/Oxygen/secrets on  main [⇡]
❯ agenix -e ngrokConfig.age -v
+ test 0 -gt 0
+ RULES=./secrets.nix
+ trap cleanup 0 2 3 15
+ '[' 0 -eq 1 ']'
+ edit ngrokConfig.age
+ FILE=ngrokConfig.age
++ /nix/store/iyw8r3bfd2jgvdp0hwsn97h7a6ps1zxx-nix-2.3.11/bin/nix-instantiate --eval -E '(let rules = import ./secrets.nix; in builtins.concatStringsSep "\n" rules."ngrokConfig.age".publicKeys)'
++ /nix/store/g34ldykl1cal5b9ir3xinnq70m52fcnq-gnused-4.8/bin/sed 's/\\n/\n/g'
++ /nix/store/g34ldykl1cal5b9ir3xinnq70m52fcnq-gnused-4.8/bin/sed 's/"//g'
warning: unknown setting 'experimental-features'
+ KEYS='ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPWMxDpxfrlXhAyln0+MKZs7q3i1VimlHhGgUxVVaeYY Oxygen'
+ '[' -z 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPWMxDpxfrlXhAyln0+MKZs7q3i1VimlHhGgUxVVaeYY Oxygen' ']'
++ /nix/store/761kp8ybw3yq5hiws56h0vxnjmyx9as0-mktemp-1.7/bin/mktemp -d
+ CLEARTEXT_DIR=/tmp/tmp.6J8hPwoNbz
++ basename ngrokConfig.age
+ CLEARTEXT_FILE=/tmp/tmp.6J8hPwoNbz/ngrokConfig.age
+ '[' -f ngrokConfig.age ']'
+ DECRYPT=("${DEFAULT_DECRYPT[@]}")
+ '[' -f /home/padraic/.ssh/id_rsa ']'
+ '[' -f /home/padraic/.ssh/id_ed25519 ']'
+ [[ --decrypt != *\-\-\i\d\e\n\t\i\t\y* ]]
+ echo 'No identity found to decrypt ngrokConfig.age. Try adding an SSH key at /home/padraic/.ssh/id_rsa or /home/padraic/.ssh/id_ed25519 or using the --identity flag to specify a file.'
No identity found to decrypt ngrokConfig.age. Try adding an SSH key at /home/padraic/.ssh/id_rsa or /home/padraic/.ssh/id_ed25519 or using the --identity flag to specify a file.
+ exit 1
+ cleanup
+ '[' '!' -z x ']'
+ rm -rf /tmp/tmp.6J8hPwoNbz
+ '[' '!' -z ']'

<br/>

If I try doing so directly with the host key using the `--identity` flag I get:

```bashhosts/Oxygen/secrets on  main [⇡]
❯ agenix -e ngrokConfig.age -i /etc/ssh/ssh_host_ed25519_key
Error: Permission denied (os error 13)

[ Did rage not do what you expected? Could an error be more useful? ]
[ Tell us: https://str4d.xyz/rage/report                            ]

And using running the same command as root user through sudo:

❯ EDITOR=nvim sudo agenix -e ngrokConfig.age -i /etc/ssh/ssh_host_ed25519_key -v
+ test 0 -gt 0
+ RULES=./secrets.nix
+ trap cleanup 0 2 3 15
+ '[' 0 -eq 1 ']'
+ edit ngrokConfig.age
+ FILE=ngrokConfig.age
++ /nix/store/iyw8r3bfd2jgvdp0hwsn97h7a6ps1zxx-nix-2.3.11/bin/nix-instantiate --eval -E '(let rules = import ./secrets.nix; in builtins.concatStringsSep "\n" rules."ngrokConfig.age".publicKeys)'
++ /nix/store/g34ldykl1cal5b9ir3xinnq70m52fcnq-gnused-4.8/bin/sed 's/"//g'
++ /nix/store/g34ldykl1cal5b9ir3xinnq70m52fcnq-gnused-4.8/bin/sed 's/\\n/\n/g'
warning: unknown setting 'experimental-features'
+ KEYS='ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPWMxDpxfrlXhAyln0+MKZs7q3i1VimlHhGgUxVVaeYY Oxygen'
+ '[' -z 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPWMxDpxfrlXhAyln0+MKZs7q3i1VimlHhGgUxVVaeYY Oxygen' ']'
++ /nix/store/761kp8ybw3yq5hiws56h0vxnjmyx9as0-mktemp-1.7/bin/mktemp -d
+ CLEARTEXT_DIR=/tmp/tmp.yL3LXJNTNO
++ basename ngrokConfig.age
+ CLEARTEXT_FILE=/tmp/tmp.yL3LXJNTNO/ngrokConfig.age
+ '[' -f ngrokConfig.age ']'
+ DECRYPT=("${DEFAULT_DECRYPT[@]}")
+ '[' -f /root/.ssh/id_rsa ']'
+ '[' -f /root/.ssh/id_ed25519 ']'
+ [[ --decrypt --identity /etc/ssh/ssh_host_ed25519_key != *\-\-\i\d\e\n\t\i\t\y* ]]
+ DECRYPT+=(-o "$CLEARTEXT_FILE" "$FILE")
+ /nix/store/rc7g2395vw74cd625drh32rsvglallhi-rage-0.6.0/bin/rage --decrypt --identity /etc/ssh/ssh_host_ed25519_key -o /tmp/tmp.yL3LXJNTNO/ngrokConfig.age ngrokConfig.age
+ cp /tmp/tmp.yL3LXJNTNO/ngrokConfig.age /tmp/tmp.yL3LXJNTNO/ngrokConfig.age.before
/run/current-system/sw/bin/agenix: line 123: EDITOR: unbound variable
++ cleanup
++ '[' '!' -z x ']'
++ rm -rf /tmp/tmp.yL3LXJNTNO
++ '[' '!' -z ']'

~I'm inclined to think that I may be going about the encryption process incorrectly and it was only intended to be a one-time edit of the file. I can get by well enough by just deleting the file and recreating it again, however it is a bit tedious and would be preferred if I can make incremental changes to secret configs and the such. ~

All above aside, great tool btw

Update:

On further reading of the agenix.nix file, I believe I understand the issue better. I have on my machine two ssh keys, one under /etc/ssh, the "host" key and then a personal ssh key under ~/.ssh. The script will automatically add the ~/.ssh key as a recipient if it exists but does not appear to search under /etc/hosts/ for the decryption, only ~/ssh.
I think it would be a good default to lookup host ssh keys in the event of running as root (as they could only be accessed by root).

aarch64-darwin support

Is it planned to support aarch64-darwin?
Currently we need to use nix-packages in Rosetta mode for this.

Asking me for my RSA key password

secrets.nix:

let
  rocky = "ssh-ed25519";
  elaine = "ssh-ed25519";
in
{
  "secret1.age".publicKeys = [ rocky elaine ];
}

Command that fails only after second entry:

agenix -e secret1.age

Thank you in advance!

no secrets on boot

There seems to be an issue after upgrading to latest nixos-unstable where the secrets don't get created properly on boot. Not sure what the root cause is yet, but I can tell you a simple nixos-rebuild switch does successfully make the secrets so it's only a boot time issue.

Strangely the files don't seem to exist on boot as the boot log is full of these message:

May 24 17:39:59 serval-ws stage-2-init: decrypting /nix/store/mxhq2zldxmbqabzhsayqjmnv7hm3r6mj-source/secrets/nrd.age to /run/secrets/nrd...
May 24 17:39:59 serval-ws stage-2-init: Error: No such file or directory (os error 2)
May 24 17:39:59 serval-ws stage-2-init: [ Did rage not do what you expected? Could an error be more useful? ]
May 24 17:39:59 serval-ws stage-2-init: [ Tell us: https://str4d.xyz/rage/report                            ]
May 24 17:39:59 serval-ws stage-2-init: chmod: cannot access '/run/secrets/nrd.tmp': No such file or directory
May 24 17:39:59 serval-ws stage-2-init: chown: cannot access '/run/secrets/nrd.tmp': No such file or directory
May 24 17:39:59 serval-ws stage-2-init: mv: cannot stat '/run/secrets/nrd.tmp': No such file or directory
May 24 17:39:59 serval-ws stage-2-init: decrypting /nix/store/mxhq2zldxmbqabzhsayqjmnv7hm3r6mj-source/secrets/root.age to /run/secrets/root...
May 24 17:39:59 serval-ws stage-2-init: Error: No such file or directory (os error 2)
May 24 17:39:59 serval-ws stage-2-init: [ Did rage not do what you expected? Could an error be more useful? ]
May 24 17:39:59 serval-ws stage-2-init: [ Tell us: https://str4d.xyz/rage/report                            ]
May 24 17:39:59 serval-ws stage-2-init: chmod: cannot access '/run/secrets/root.tmp': No such file or directory
May 24 17:39:59 serval-ws stage-2-init: chown: cannot access '/run/secrets/root.tmp': No such file or directory
May 24 17:39:59 serval-ws stage-2-init: mv: cannot stat '/run/secrets/root.tmp': No such file or directory
May 24 17:39:59 serval-ws stage-2-init: decrypting /nix/store/mxhq2zldxmbqabzhsayqjmnv7hm3r6mj-source/secrets/wireguard.age to /run/secrets/wireguard...
May 24 17:39:59 serval-ws stage-2-init: Error: No such file or directory (os error 2)
May 24 17:39:59 serval-ws stage-2-init: [ Did rage not do what you expected? Could an error be more useful? ]
May 24 17:39:59 serval-ws stage-2-init: [ Tell us: https://str4d.xyz/rage/report                            ]
May 24 17:39:59 serval-ws stage-2-init: chmod: cannot access '/run/secrets/wireguard.tmp': No such file or directory
May 24 17:39:59 serval-ws stage-2-init: chown: cannot access '/run/secrets/wireguard.tmp': No such file or directory
May 24 17:39:59 serval-ws stage-2-init: mv: cannot stat '/run/secrets/wireguard.tmp': No such file or directory
May 24 17:39:59 serval-ws stage-2-init: Activation script snippet 'agenixRoot' failed (1)
May 24 17:39:59 serval-ws stage-2-init: warning: password file ‘/run/secrets/nrd’ does not exist
May 24 17:39:59 serval-ws stage-2-init: warning: password file ‘/run/secrets/root’ does not exist
May 24 17:39:59 serval-ws stage-2-init: [agenix] decrypting non-root secrets...
May 24 17:39:59 serval-ws stage-2-init: decrypting /nix/store/mxhq2zldxmbqabzhsayqjmnv7hm3r6mj-source/secrets/aws.age to /run/secrets/aws...
May 24 17:39:59 serval-ws stage-2-init: Error: No such file or directory (os error 2)
May 24 17:39:59 serval-ws stage-2-init: [ Did rage not do what you expected? Could an error be more useful? ]
...

These files do in fact exist in the nix store and, as I said, all my secrets are properly created with a simple nixos-rebuild switch. My only guess so far is that stage-2-init doesn't have proper access to the nix/store somehow, but I'm not sure why.

home-manager integration?

Is it possible to use agenix from inside a home-manager module? I don't see anything talking about this, so I'm guessing it's not possible, but I figured I'd check.

I realize I can use an agenix secret from inside a home-manager module by just accessing the /run/secrets/blah file, but I'd like to keep the file generation (i.e. age.secrets.blah.file = "${self}/secrets/blah";) close to the point of use, rather than off in it's own NixOS module.

`secrets/` folder best practices

It seems that the standard way to do things is to put secrets.nix as well as any secrets in a special secrets/ folder. To me it seems more natural to have the secrets.nix file at top-level, and have the actual secrets spread out through the repository (close to the code that uses them).

Is there a reason the former style is preferred?

Bootstrap / recreate secrets

In a situation where an operator want's to bootstrap a prefabricated environment, or in a situation where an operator has to "break the glass" and cycle the root secrets,

it would be useful to store create instructions alongside the encryption definition in secrets.nix.


Example bootstrap/recreate scripts could be:

encrypt="$(nomad operator keygen)"
echo '{}' | jq --arg encrypt "$encrypt" '.server.encrypt = $encrypt'
export PATH="${lib.makeBinPath (with pkgs; [ xkcdpass ])}"
xkcdpass -n 24

Without further research, I would assume agenix cli contracting output on stdout for subsequent encryption would be good enough.

/cc @veehaitch if @ryantm is interested, I'd probably implement this for ragenix asap.

Symlink secrets like sops-nix does?

I think I would prefer if agenix would ln -sf /run/secrets/${name} ${path} files to their desired locations rather than mv -f them. When path is outside of /run/secrets, this means the files will not be cleaned up automatically (e.g. on reboot).

Is there a reason agenix decided not to go this route?

the cli is broken when using agenix via Flakes

In the readme is stated that you don't need to install agenix to use the cli via Flakes.
But that doesn't seem to work properly since the paths get messed up completely.

e.g when running nix run github:ryantm/agenix -- -e test.age you first get the message that there is no ./secrets.nix (even if its there). And if you workarround it by setting the absolute path via RULES env var. You get the editor to write down the secret, but the secret file is written to somewere!? (at least not to the directory where you would expect it).

when agenix is installed it works just fine as expected.

system.activationScripts.agenixRoot.deps is undefined

Using agenix at c27b633, nixpkgs at 773ef5ae, trying to build a NixOS configuration from flake.nix. Running into the following error:

$ nixos-rebuild build --flake '.#'                                                                                                                                                           
building the system configuration...                                                                                                                                                                                                 
error: --- EvalError --------------------------------------------------------------------------------------------------------------- nix
at: (61:28) in file: /nix/store/vkn2kvjvgy5dh93i0ra5f9828h0c2s79-source/lib/strings-with-deps.nix                                       
                                                                                                                                        
    60|           if isAttrs entry then                                                                                                 
    61|             let x = f done entry.deps;                                                                                          
      |                            ^                                                                                                    
    62|                 y = f x.done (tail todo);                                                                                       
                                                                                                                                        
attribute 'deps' missing                                                                                                                
(use '--show-trace' to show detailed location information)

Downgrading to 204bd95d makes the error go away.

I suspect the source is ecee2c7, as adding

system.activationScripts.agenixRoot.deps = []

to my NixOS configuration also makes the error go away.

Thank you!

Gitlab - permission denied on password

Hi, I have the following

# secrets.nix
let angelisland-root = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC/XI9D42X8djPVrmRblIa6RjcN9rMoIJn8Fw8UF08K5cbzeKRhLGwMw97kur7Onr13+3QtVimhkW732YyMwL7PwdEJVzLyOCoF5EW9sHxbPMj3dKNi5c/EOruCP2Q3TT+NR8nMPmWr2ByOXUqWYiP8DMbGAZqT1h/l/7owLT+ygxn24UMrEUbeKYyVHNk7Mind7H2TuCdGoN6LXwAlSNeXldGWtytbtK0UVaiw5cYsxCcNb84GLP4XKhJvfx3z7blQWzGFIcj+BAVb0ErTDaaocNZIV0thrdfDv9JuOgJSU+4GcaLeeq/9ePek08TWfsgaT4YXS7j7gm1w9SfZksESgy3nQQ1ub+TeQTEYhp8OoWBA5ohLpYv13z4aVDy9GJdGa1aQqCXSS3Lu+g6Gas5kEAMrQxFdlext7XQAbMYe3Je29x039k2iX4MuH8R8XuDf8RnmVB0CbRbIasSlxXZ/UOlxuPgyaGepxGB7b09PFjUr8kIqh8ZJqWH6NVhy5B1b8UZXDo/5P7jS1FpPP+tTIzdHDzz4XLQdLZsJQA5c9Y6qfy1a7W7JnRHDQuddBmHnEkmQa6dAAlQ7R6MtcY2NT+QlYlgqGYAHeW9hZAOkankc7Aae0vjy1yeRq6Kk5xLkv2CtzRGVdIEW0aEvKKI05mftp1rNlOfDllCoW3waSw==";
in
{
  "angelisland.secrets.db.age".publicKeys = [ angelisland-root ];
  "angelisland.secrets.jws.age".publicKeys = [ angelisland-root ];
  "angelisland.secrets.otp.age".publicKeys = [ angelisland-root ];
  "angelisland.secrets.secret.age".publicKeys = [ angelisland-root ];
  "angelisland.passwords.db.age".publicKeys = [ angelisland-root ];
  "angelisland.passwords.root.age".publicKeys = [ angelisland-root ];
}
#gitlab-module
{config, pkgs, lib, ...}: {
 
  age.secrets."secrets.db".file = ../secrets/angelisland.secrets.db.age;
  age.secrets."secrets.jws".file = ../secrets/angelisland.secrets.jws.age;
  age.secrets."secrets.otp".file = ../secrets/angelisland.secrets.otp.age;
  age.secrets."secrets.secret".file = ../secrets/angelisland.secrets.secret.age;
  age.secrets."passwords.db".file = ../secrets/angelisland.passwords.db.age;
  age.secrets."passwords.root".file = ../secrets/angelisland.passwords.root.age;

  services.gitlab = {
    enable = true;
    https = true;
    databasePasswordFile = config.age.secrets."passwords.db".path;
    initialRootPasswordFile = config.age.secrets."passwords.root".path;
    host = "<redacted>";
    port = 443;
    user = "git";
    databaseUsername = "git";
    group = "git";
    smtp = {
      enable = true;
      address = "localhost";
      port = 25;
    };
    secrets = {
      dbFile = config.age.secrets."secrets.db".path;
      jwsFile = config.age.secrets."secrets.jws".path;
      secretFile = config.age.secrets."secrets.secret".path;
      otpFile = config.age.secrets."secrets.otp".path;
    };

};

This results in:

Dec 06 15:11:51 angelisland cnwcsrz6kxcb4ax02dfswff1443f6yhj-gitlab-config[15746]: mkdir -p /var/gitlab/state/home/.ssh: OK
Dec 06 15:11:51 angelisland cnwcsrz6kxcb4ax02dfswff1443f6yhj-gitlab-config[15746]: chmod 700 /var/gitlab/state/home/.ssh: OK
Dec 06 15:11:51 angelisland cnwcsrz6kxcb4ax02dfswff1443f6yhj-gitlab-config[15753]: /nix/store/cnwcsrz6kxcb4ax02dfswff1443f6yhj-gitlab-config: line 27: /run/agenix/passwords.db: Permission denied
Dec 06 15:11:51 angelisland cnwcsrz6kxcb4ax02dfswff1443f6yhj-gitlab-config[15750]: Database password was an empty string!
Dec 06 15:11:51 angelisland systemd[1]: gitlab-config.service: Main process exited, code=exited, status=1/FAILURE
Dec 06 15:11:51 angelisland systemd[1]: gitlab-config.service: Failed with result 'exit-code'.
Dec 06 15:11:51 angelisland systemd[1]: Failed to start gitlab-config.service.

default secret path broken after a reboot

Not sure how I did not notice it sooner but the secret files no longer exist in the default path /run/agenix/${name} after reboot – it is just an empty directory.

$ sudo ls -la /run/agenix
total 0
drwxr-xr-x  2 root root  40 Jan  6 21:28 .
drwxr-xr-x 23 root root 560 Jan  6 21:29 ..
Log of activation after reboot
Jan 06 21:28:11 azazel unknown: booting system configuration /nix/store/d8ndfx49x56ldl73vl9zzr8iiwh58qz6-nixos-system-azazel-22.05.20220103.78cd22c
Jan 06 21:28:11 azazel stage-2-init: running activation script...
Jan 06 21:28:11 azazel stage-2-init: + ((  _localstatus > 0  ))
Jan 06 21:28:11 azazel stage-2-init: + _localstatus=0
Jan 06 21:28:11 azazel stage-2-init: +++ readlink /run/agenix
Jan 06 21:28:11 azazel stage-2-init: ++ basename /run/agenix.d/4
Jan 06 21:28:11 azazel stage-2-init: + _agenix_generation=4
Jan 06 21:28:11 azazel stage-2-init: + ((  ++_agenix_generation  ))
Jan 06 21:28:11 azazel stage-2-init: + echo '[agenix] symlinking new secrets to /run/agenix (generation 5)...'
Jan 06 21:28:11 azazel stage-2-init: [agenix] symlinking new secrets to /run/agenix (generation 5)...
Jan 06 21:28:11 azazel stage-2-init: + mkdir -p /run/agenix.d
Jan 06 21:28:11 azazel stage-2-init: + chmod 0751 /run/agenix.d
Jan 06 21:28:11 azazel stage-2-init: + grep -q '/run/agenix.d ramfs' /proc/mounts
Jan 06 21:28:11 azazel stage-2-init: + mount -t ramfs none /run/agenix.d -o nodev,nosuid,mode=0751
Jan 06 21:28:11 azazel stage-2-init: + mkdir -p /run/agenix.d/5
Jan 06 21:28:11 azazel stage-2-init: + chmod 0751 /run/agenix.d/5
Jan 06 21:28:11 azazel stage-2-init: + ln -sfn /run/agenix.d/5 /run/agenix
Jan 06 21:28:11 azazel stage-2-init: + ((  _agenix_generation > 1  ))
Jan 06 21:28:11 azazel stage-2-init: + echo '[agenix] removing old secrets (generation 4)...'
Jan 06 21:28:11 azazel stage-2-init: [agenix] removing old secrets (generation 4)...
Jan 06 21:28:11 azazel stage-2-init: + rm -rf /run/agenix.d/4
[…]
Jan 06 21:28:11 azazel stage-2-init: + echo '[agenix] decrypting root secrets...'
Jan 06 21:28:11 azazel stage-2-init: [agenix] decrypting root secrets...
Jan 06 21:28:11 azazel stage-2-init: + _truePath=/run/agenix.d/5/bag.ogion.cz-secret
Jan 06 21:28:11 azazel stage-2-init: + echo 'decrypting '\''/nix/store/ga41w0dn9dhk12w1w2zcv7pr4w9g36v2-bag.ogion.cz-secret.age'\'' to '\''/run/agenix.d/5/bag.ogion.cz-secret'\''...'
Jan 06 21:28:11 azazel stage-2-init: decrypting '/nix/store/ga41w0dn9dhk12w1w2zcv7pr4w9g36v2-bag.ogion.cz-secret.age' to '/run/agenix.d/5/bag.ogion.cz-secret'...
Jan 06 21:28:11 azazel stage-2-init: + TMP_FILE=/run/agenix.d/5/bag.ogion.cz-secret.tmp
Jan 06 21:28:11 azazel stage-2-init: ++ dirname /run/agenix.d/5/bag.ogion.cz-secret
Jan 06 21:28:11 azazel stage-2-init: + mkdir -p /run/agenix.d/5
Jan 06 21:28:11 azazel stage-2-init: ++ dirname /run/agenix/bag.ogion.cz-secret
Jan 06 21:28:11 azazel stage-2-init: + mkdir -p /run/agenix
Jan 06 21:28:11 azazel stage-2-init: + umask u=r,g=,o=
Jan 06 21:28:11 azazel stage-2-init: + LANG=en_US.UTF-8
Jan 06 21:28:11 azazel stage-2-init: + /nix/store/b7dz2sz5mh4h3xbav9q15j7547qjhdyl-rage-0.7.1/bin/rage --decrypt -i /etc/ssh/ssh_host_rsa_key -i /etc/ssh/ssh_host_ed25519_key -o /run/agenix.d/5/bag.ogion.cz-secret.tmp /nix/store/ga41w0dn9dhk12w1w2zcv7pr4w9g36v2-bag.ogion.cz-secret.age
Jan 06 21:28:11 azazel stage-2-init: + chmod 0400 /run/agenix.d/5/bag.ogion.cz-secret.tmp
Jan 06 21:28:11 azazel stage-2-init: + chown 0:0 /run/agenix.d/5/bag.ogion.cz-secret.tmp
Jan 06 21:28:11 azazel stage-2-init: + mv -f /run/agenix.d/5/bag.ogion.cz-secret.tmp /run/agenix.d/5/bag.ogion.cz-secret
Jan 06 21:28:11 azazel stage-2-init: + '[' /run/agenix/bag.ogion.cz-secret '!=' /run/agenix/bag.ogion.cz-secret ']'
Jan 06 21:28:11 azazel stage-2-init: + _truePath=/run/agenix.d/5/blackfire-agent-server-id
Jan 06 21:28:11 azazel stage-2-init: + echo 'decrypting '\''/nix/store/89qy5kr20hb5m88sqsii2q6v5xwpibg0-blackfire-agent-server-id.age'\'' to '\''/run/agenix.d/5/blackfire-agent-server-id'\''...'
Jan 06 21:28:11 azazel stage-2-init: decrypting '/nix/store/89qy5kr20hb5m88sqsii2q6v5xwpibg0-blackfire-agent-server-id.age' to '/run/agenix.d/5/blackfire-agent-server-id'...
Jan 06 21:28:11 azazel stage-2-init: + TMP_FILE=/run/agenix.d/5/blackfire-agent-server-id.tmp
Jan 06 21:28:11 azazel stage-2-init: ++ dirname /run/agenix.d/5/blackfire-agent-server-id
Jan 06 21:28:11 azazel stage-2-init: + mkdir -p /run/agenix.d/5
Jan 06 21:28:11 azazel stage-2-init: ++ dirname /run/agenix/blackfire-agent-server-id
Jan 06 21:28:11 azazel stage-2-init: + mkdir -p /run/agenix
Jan 06 21:28:11 azazel stage-2-init: + umask u=r,g=,o=
Jan 06 21:28:11 azazel stage-2-init: + LANG=en_US.UTF-8
Jan 06 21:28:11 azazel stage-2-init: + /nix/store/b7dz2sz5mh4h3xbav9q15j7547qjhdyl-rage-0.7.1/bin/rage --decrypt -i /etc/ssh/ssh_host_rsa_key -i /etc/ssh/ssh_host_ed25519_key -o /run/agenix.d/5/blackfire-agent-server-id.tmp /nix/store/89qy5kr20hb5m88sqsii2q6v5xwpibg0-blackfire-agent-server-id.age
Jan 06 21:28:11 azazel stage-2-init: + chmod 0400 /run/agenix.d/5/blackfire-agent-server-id.tmp
Jan 06 21:28:11 azazel stage-2-init: + chown 0:0 /run/agenix.d/5/blackfire-agent-server-id.tmp
Jan 06 21:28:11 azazel stage-2-init: + mv -f /run/agenix.d/5/blackfire-agent-server-id.tmp /run/agenix.d/5/blackfire-agent-server-id
Jan 06 21:28:11 azazel stage-2-init: + '[' /run/agenix/blackfire-agent-server-id '!=' /run/agenix/blackfire-agent-server-id ']'
Jan 06 21:28:11 azazel stage-2-init: + _truePath=/run/agenix.d/5/blackfire-agent-server-token
Jan 06 21:28:11 azazel stage-2-init: + echo 'decrypting '\''/nix/store/bxxyhwbw784f95vgxj5kn3bvg0dfd5xy-blackfire-agent-server-token.age'\'' to '\''/run/agenix.d/5/blackfire-agent-server-token'\''...'
Jan 06 21:28:11 azazel stage-2-init: decrypting '/nix/store/bxxyhwbw784f95vgxj5kn3bvg0dfd5xy-blackfire-agent-server-token.age' to '/run/agenix.d/5/blackfire-agent-server-token'...
Jan 06 21:28:11 azazel stage-2-init: + TMP_FILE=/run/agenix.d/5/blackfire-agent-server-token.tmp
Jan 06 21:28:11 azazel stage-2-init: ++ dirname /run/agenix.d/5/blackfire-agent-server-token
Jan 06 21:28:11 azazel stage-2-init: + mkdir -p /run/agenix.d/5
Jan 06 21:28:11 azazel stage-2-init: ++ dirname /run/agenix/blackfire-agent-server-token
Jan 06 21:28:11 azazel stage-2-init: + mkdir -p /run/agenix
Jan 06 21:28:11 azazel stage-2-init: + umask u=r,g=,o=
Jan 06 21:28:11 azazel stage-2-init: + LANG=en_US.UTF-8
Jan 06 21:28:11 azazel stage-2-init: + /nix/store/b7dz2sz5mh4h3xbav9q15j7547qjhdyl-rage-0.7.1/bin/rage --decrypt -i /etc/ssh/ssh_host_rsa_key -i /etc/ssh/ssh_host_ed25519_key -o /run/agenix.d/5/blackfire-agent-server-token.tmp /nix/store/bxxyhwbw784f95vgxj5kn3bvg0dfd5xy-blackfire-agent-server-token.age
Jan 06 21:28:11 azazel stage-2-init: + chmod 0400 /run/agenix.d/5/blackfire-agent-server-token.tmp
Jan 06 21:28:11 azazel stage-2-init: + chown 0:0 /run/agenix.d/5/blackfire-agent-server-token.tmp
Jan 06 21:28:11 azazel stage-2-init: + mv -f /run/agenix.d/5/blackfire-agent-server-token.tmp /run/agenix.d/5/blackfire-agent-server-token
Jan 06 21:28:11 azazel stage-2-init: + '[' /run/agenix/blackfire-agent-server-token '!=' /run/agenix/blackfire-agent-server-token ']'
Jan 06 21:28:11 azazel stage-2-init: + ((  _localstatus > 0  ))
Jan 06 21:28:11 azazel stage-2-init: + _localstatus=0
Jan 06 21:28:11 azazel stage-2-init: + install -m 0700 -d /root
Jan 06 21:28:11 azazel stage-2-init: + install -m 0755 -d /home
Jan 06 21:28:11 azazel stage-2-init: + /nix/store/i1hy2bjj3dmmn3qg61y87rci7xfv37dk-perl-5.34.0-env/bin/perl -w /nix/store/8smw5zaclai395bpr5gp5inzdgbkn43h-update-users-groups.pl /nix/store/s9nldar80mkgvnw760i2j2p2yy5nj95a-users-groups.json
Jan 06 21:28:11 azazel stage-2-init: + ((  _localstatus > 0  ))
Jan 06 21:28:11 azazel stage-2-init: + _localstatus=0
Jan 06 21:28:11 azazel stage-2-init: + ((  _localstatus > 0  ))
Jan 06 21:28:11 azazel stage-2-init: + _localstatus=0
Jan 06 21:28:11 azazel stage-2-init: + chown :keys /run/agenix.d /run/agenix.d/5
Jan 06 21:28:11 azazel stage-2-init: + ((  _localstatus > 0  ))
Jan 06 21:28:11 azazel stage-2-init: + _localstatus=0
Jan 06 21:28:11 azazel stage-2-init: + echo '[agenix] decrypting non-root secrets...'
Jan 06 21:28:11 azazel stage-2-init: [agenix] decrypting non-root secrets...
Jan 06 21:28:11 azazel stage-2-init: + ((  _localstatus > 0  ))
Jan 06 21:28:11 azazel stage-2-init: + _localstatus=0
Jan 06 21:28:11 azazel stage-2-init: + echo 'setting up /etc...'
Jan 06 21:28:11 azazel stage-2-init: setting up /etc...
Jan 06 21:28:11 azazel stage-2-init: + /nix/store/p36s6mbwqh4czbh107jyzc4d0hc345ck-perl-5.34.0-env/bin/perl /nix/store/cz6na7w751iv7z78fb9ms8hhvnsd0l8z-setup-etc.pl /nix/store/71ib0qisds7zvq58fr8qy3wn92ai6pcp-etc/etc
Jan 06 21:28:11 azazel stage-2-init: + ((  _localstatus > 0  ))
Jan 06 21:28:11 azazel stage-2-init: + _localstatus=0
Jan 06 21:28:11 azazel stage-2-init: ++ cat /run/agenix/bag.ogion.cz-secret
Jan 06 21:28:11 azazel stage-2-init: cat: /run/agenix/bag.ogion.cz-secret: No such file or directory
Jan 06 21:28:11 azazel stage-2-init: + secret=

If I delete the directory and switch, the symlink is created correctly:

$ sudo ls -la /run/agenix
lrwxrwxrwx 1 root root 15 Jan  6 21:34 /run/agenix -> /run/agenix.d/1
Log of first activation after deleting the directory
activating the configuration...
+ ((  _localstatus > 0  ))
+ _localstatus=0
+++ readlink /run/agenix
++ basename ''
+ _agenix_generation=
+ ((  ++_agenix_generation  ))
+ echo '[agenix] symlinking new secrets to /run/agenix (generation 1)...'
[agenix] symlinking new secrets to /run/agenix (generation 1)...
+ mkdir -p /run/agenix.d
+ chmod 0751 /run/agenix.d
+ grep -q '/run/agenix.d ramfs' /proc/mounts
+ mkdir -p /run/agenix.d/1
+ chmod 0751 /run/agenix.d/1
+ ln -sfn /run/agenix.d/1 /run/agenix
+ ((  _agenix_generation > 1  ))
[…]
[agenix] decrypting root secrets...
+ echo '[agenix] decrypting root secrets...'
+ _truePath=/run/agenix.d/1/bag.ogion.cz-secret
+ echo 'decrypting '\''/nix/store/ga41w0dn9dhk12w1w2zcv7pr4w9g36v2-bag.ogion.cz-secret.age'\'' to '\''/run/agenix.d/1/bag.ogion.cz-secret'\''...'
decrypting '/nix/store/ga41w0dn9dhk12w1w2zcv7pr4w9g36v2-bag.ogion.cz-secret.age' to '/run/agenix.d/1/bag.ogion.cz-secret'...
+ TMP_FILE=/run/agenix.d/1/bag.ogion.cz-secret.tmp
++ dirname /run/agenix.d/1/bag.ogion.cz-secret
+ mkdir -p /run/agenix.d/1
++ dirname /run/agenix/bag.ogion.cz-secret
+ mkdir -p /run/agenix
+ umask u=r,g=,o=
+ LANG=en_US.UTF-8
+ /nix/store/b7dz2sz5mh4h3xbav9q15j7547qjhdyl-rage-0.7.1/bin/rage --decrypt -i /etc/ssh/ssh_host_rsa_key -i /etc/ssh/ssh_host_ed25519_key -o /run/agenix.d/1/bag.ogion.cz-secret.tmp /nix/store/ga41w0dn9dhk12w1w2zcv7pr4w9g36v2-bag.ogion.cz-secret.age
+ chmod 0400 /run/agenix.d/1/bag.ogion.cz-secret.tmp
+ chown 0:0 /run/agenix.d/1/bag.ogion.cz-secret.tmp
+ mv -f /run/agenix.d/1/bag.ogion.cz-secret.tmp /run/agenix.d/1/bag.ogion.cz-secret
+ '[' /run/agenix/bag.ogion.cz-secret '!=' /run/agenix/bag.ogion.cz-secret ']'
decrypting '/nix/store/89qy5kr20hb5m88sqsii2q6v5xwpibg0-blackfire-agent-server-id.age' to '/run/agenix.d/1/blackfire-agent-server-id'...
+ _truePath=/run/agenix.d/1/blackfire-agent-server-id
+ echo 'decrypting '\''/nix/store/89qy5kr20hb5m88sqsii2q6v5xwpibg0-blackfire-agent-server-id.age'\'' to '\''/run/agenix.d/1/blackfire-agent-server-id'\''...'
+ TMP_FILE=/run/agenix.d/1/blackfire-agent-server-id.tmp
++ dirname /run/agenix.d/1/blackfire-agent-server-id
+ mkdir -p /run/agenix.d/1
++ dirname /run/agenix/blackfire-agent-server-id
+ mkdir -p /run/agenix
+ umask u=r,g=,o=
+ LANG=en_US.UTF-8
+ /nix/store/b7dz2sz5mh4h3xbav9q15j7547qjhdyl-rage-0.7.1/bin/rage --decrypt -i /etc/ssh/ssh_host_rsa_key -i /etc/ssh/ssh_host_ed25519_key -o /run/agenix.d/1/blackfire-agent-server-id.tmp /nix/store/89qy5kr20hb5m88sqsii2q6v5xwpibg0-blackfire-agent-server-id.age
+ chmod 0400 /run/agenix.d/1/blackfire-agent-server-id.tmp
+ chown 0:0 /run/agenix.d/1/blackfire-agent-server-id.tmp
+ mv -f /run/agenix.d/1/blackfire-agent-server-id.tmp /run/agenix.d/1/blackfire-agent-server-id
+ '[' /run/agenix/blackfire-agent-server-id '!=' /run/agenix/blackfire-agent-server-id ']'
+ _truePath=/run/agenix.d/1/blackfire-agent-server-token
+ echo 'decrypting '\''/nix/store/bxxyhwbw784f95vgxj5kn3bvg0dfd5xy-blackfire-agent-server-token.age'\'' to '\''/run/agenix.d/1/blackfire-agent-server-token'\''...'
decrypting '/nix/store/bxxyhwbw784f95vgxj5kn3bvg0dfd5xy-blackfire-agent-server-token.age' to '/run/agenix.d/1/blackfire-agent-server-token'...
+ TMP_FILE=/run/agenix.d/1/blackfire-agent-server-token.tmp
++ dirname /run/agenix.d/1/blackfire-agent-server-token
+ mkdir -p /run/agenix.d/1
++ dirname /run/agenix/blackfire-agent-server-token
+ mkdir -p /run/agenix
+ umask u=r,g=,o=
+ LANG=en_US.UTF-8
+ /nix/store/b7dz2sz5mh4h3xbav9q15j7547qjhdyl-rage-0.7.1/bin/rage --decrypt -i /etc/ssh/ssh_host_rsa_key -i /etc/ssh/ssh_host_ed25519_key -o /run/agenix.d/1/blackfire-agent-server-token.tmp /nix/store/bxxyhwbw784f95vgxj5kn3bvg0dfd5xy-blackfire-agent-server-token.age
+ chmod 0400 /run/agenix.d/1/blackfire-agent-server-token.tmp
+ chown 0:0 /run/agenix.d/1/blackfire-agent-server-token.tmp
+ mv -f /run/agenix.d/1/blackfire-agent-server-token.tmp /run/agenix.d/1/blackfire-agent-server-token
+ '[' /run/agenix/blackfire-agent-server-token '!=' /run/agenix/blackfire-agent-server-token ']'
+ ((  _localstatus > 0  ))
+ _localstatus=0
+ install -m 0700 -d /root
+ install -m 0755 -d /home
+ /nix/store/i1hy2bjj3dmmn3qg61y87rci7xfv37dk-perl-5.34.0-env/bin/perl -w /nix/store/8smw5zaclai395bpr5gp5inzdgbkn43h-update-users-groups.pl /nix/store/s9nldar80mkgvnw760i2j2p2yy5nj95a-users-groups.json
+ ((  _localstatus > 0  ))
+ _localstatus=0
+ ((  _localstatus > 0  ))
+ _localstatus=0
+ chown :keys /run/agenix.d /run/agenix.d/1
+ ((  _localstatus > 0  ))
+ _localstatus=0
[agenix] decrypting non-root secrets...
setting up /etc...
+ echo '[agenix] decrypting non-root secrets...'

and same on successive activations:

$ sudo ls -la /run/agenix
lrwxrwxrwx 1 root root 15 Jan  6 21:35 /run/agenix -> /run/agenix.d/2
Log of second activation after deleting the directory
+++ readlink /run/agenix
++ basename /run/agenix.d/1
+ _agenix_generation=1
+ ((  ++_agenix_generation  ))
[agenix] symlinking new secrets to /run/agenix (generation 2)...
+ echo '[agenix] symlinking new secrets to /run/agenix (generation 2)...'
+ mkdir -p /run/agenix.d
+ chmod 0751 /run/agenix.d
+ grep -q '/run/agenix.d ramfs' /proc/mounts
+ mkdir -p /run/agenix.d/2
+ chmod 0751 /run/agenix.d/2
+ ln -sfn /run/agenix.d/2 /run/agenix
+ ((  _agenix_generation > 1  ))
[agenix] removing old secrets (generation 1)...
+ echo '[agenix] removing old secrets (generation 1)...'
+ rm -rf /run/agenix.d/1
[…]
[agenix] decrypting root secrets...
decrypting '/nix/store/ga41w0dn9dhk12w1w2zcv7pr4w9g36v2-bag.ogion.cz-secret.age' to '/run/agenix.d/2/bag.ogion.cz-secret'...
+ echo '[agenix] decrypting root secrets...'
+ _truePath=/run/agenix.d/2/bag.ogion.cz-secret
+ echo 'decrypting '\''/nix/store/ga41w0dn9dhk12w1w2zcv7pr4w9g36v2-bag.ogion.cz-secret.age'\'' to '\''/run/agenix.d/2/bag.ogion.cz-secret'\''...'
+ TMP_FILE=/run/agenix.d/2/bag.ogion.cz-secret.tmp
++ dirname /run/agenix.d/2/bag.ogion.cz-secret
+ mkdir -p /run/agenix.d/2
++ dirname /run/agenix/bag.ogion.cz-secret
+ mkdir -p /run/agenix
+ umask u=r,g=,o=
+ LANG=en_US.UTF-8
+ /nix/store/b7dz2sz5mh4h3xbav9q15j7547qjhdyl-rage-0.7.1/bin/rage --decrypt -i /etc/ssh/ssh_host_rsa_key -i /etc/ssh/ssh_host_ed25519_key -o /run/agenix.d/2/bag.ogion.cz-secret.tmp /nix/store/ga41w0dn9dhk12w1w2zcv7pr4w9g36v2-bag.ogion.cz-secret.age
+ chmod 0400 /run/agenix.d/2/bag.ogion.cz-secret.tmp
+ chown 0:0 /run/agenix.d/2/bag.ogion.cz-secret.tmp
+ mv -f /run/agenix.d/2/bag.ogion.cz-secret.tmp /run/agenix.d/2/bag.ogion.cz-secret
+ '[' /run/agenix/bag.ogion.cz-secret '!=' /run/agenix/bag.ogion.cz-secret ']'
+ _truePath=/run/agenix.d/2/blackfire-agent-server-id
decrypting '/nix/store/89qy5kr20hb5m88sqsii2q6v5xwpibg0-blackfire-agent-server-id.age' to '/run/agenix.d/2/blackfire-agent-server-id'...
+ echo 'decrypting '\''/nix/store/89qy5kr20hb5m88sqsii2q6v5xwpibg0-blackfire-agent-server-id.age'\'' to '\''/run/agenix.d/2/blackfire-agent-server-id'\''...'
+ TMP_FILE=/run/agenix.d/2/blackfire-agent-server-id.tmp
++ dirname /run/agenix.d/2/blackfire-agent-server-id
+ mkdir -p /run/agenix.d/2
++ dirname /run/agenix/blackfire-agent-server-id
+ mkdir -p /run/agenix
+ umask u=r,g=,o=
+ LANG=en_US.UTF-8
+ /nix/store/b7dz2sz5mh4h3xbav9q15j7547qjhdyl-rage-0.7.1/bin/rage --decrypt -i /etc/ssh/ssh_host_rsa_key -i /etc/ssh/ssh_host_ed25519_key -o /run/agenix.d/2/blackfire-agent-server-id.tmp /nix/store/89qy5kr20hb5m88sqsii2q6v5xwpibg0-blackfire-agent-server-id.age
+ chmod 0400 /run/agenix.d/2/blackfire-agent-server-id.tmp
+ chown 0:0 /run/agenix.d/2/blackfire-agent-server-id.tmp
+ mv -f /run/agenix.d/2/blackfire-agent-server-id.tmp /run/agenix.d/2/blackfire-agent-server-id
+ '[' /run/agenix/blackfire-agent-server-id '!=' /run/agenix/blackfire-agent-server-id ']'
+ _truePath=/run/agenix.d/2/blackfire-agent-server-token
decrypting '/nix/store/bxxyhwbw784f95vgxj5kn3bvg0dfd5xy-blackfire-agent-server-token.age' to '/run/agenix.d/2/blackfire-agent-server-token'...
+ echo 'decrypting '\''/nix/store/bxxyhwbw784f95vgxj5kn3bvg0dfd5xy-blackfire-agent-server-token.age'\'' to '\''/run/agenix.d/2/blackfire-agent-server-token'\''...'
+ TMP_FILE=/run/agenix.d/2/blackfire-agent-server-token.tmp
++ dirname /run/agenix.d/2/blackfire-agent-server-token
+ mkdir -p /run/agenix.d/2
++ dirname /run/agenix/blackfire-agent-server-token
+ mkdir -p /run/agenix
+ umask u=r,g=,o=
+ LANG=en_US.UTF-8
+ /nix/store/b7dz2sz5mh4h3xbav9q15j7547qjhdyl-rage-0.7.1/bin/rage --decrypt -i /etc/ssh/ssh_host_rsa_key -i /etc/ssh/ssh_host_ed25519_key -o /run/agenix.d/2/blackfire-agent-server-token.tmp /nix/store/bxxyhwbw784f95vgxj5kn3bvg0dfd5xy-blackfire-agent-server-token.age
+ chmod 0400 /run/agenix.d/2/blackfire-agent-server-token.tmp
+ chown 0:0 /run/agenix.d/2/blackfire-agent-server-token.tmp
+ mv -f /run/agenix.d/2/blackfire-agent-server-token.tmp /run/agenix.d/2/blackfire-agent-server-token
+ '[' /run/agenix/blackfire-agent-server-token '!=' /run/agenix/blackfire-agent-server-token ']'
+ ((  _localstatus > 0  ))
+ _localstatus=0
+ install -m 0700 -d /root
+ install -m 0755 -d /home
+ /nix/store/i1hy2bjj3dmmn3qg61y87rci7xfv37dk-perl-5.34.0-env/bin/perl -w /nix/store/8smw5zaclai395bpr5gp5inzdgbkn43h-update-users-groups.pl /nix/store/s9nldar80mkgvnw760i2j2p2yy5nj95a-users-groups.json
+ ((  _localstatus > 0  ))
+ _localstatus=0
+ ((  _localstatus > 0  ))
+ _localstatus=0
+ chown :keys /run/agenix.d /run/agenix.d/2
[agenix] decrypting non-root secrets...
+ ((  _localstatus > 0  ))
+ _localstatus=0
+ echo '[agenix] decrypting non-root secrets...'

But it breaks again after a reboot.

If I run activation after reboot, basename /run/agenix returns empty string since it is a directory so the symlink will be crated in it:

$ sudo ls -la /run/agenix
total 0
drwxr-xr-x  2 root root  60 Jan  6 21:47 .
drwxr-xr-x 24 root root 580 Jan  6 21:47 ..
lrwxrwxrwx  1 root root  15 Jan  6 21:47 1 -> /run/agenix.d/1

I have no idea what is creating the directory.

mkdir -p "$(dirname "${secretType.path}")"

is superfluous but it only runs after agenixMountSecrets.

Darwin: Undefined variable `Foundation`

Hi!

I recently added Foundation to the buildInputs on darwin. (PR #20)

But unfortunately PR #19 broke support on my machine again.

The build complains about a missing attribute pkgs.darwin.Foundation while pkgs.darwin.apple_sdk.frameworks.Foundation does work for me.

My PR added darwin.apple_sdk.frameworks.Foundation while the other PR changed it to darwin.Foundation.

I am not sure if this is the result of merge gone wrong or whether somethings wrong with my setup.

Thanks!
Felix

Support for aarch64

I am trying to setup agenix for a raspberry pi 3 (aarch64) and unfortunately after several hours of compiling it errors because criterion-cycles-per-byte only supports x86 platforms.

Since this dependency is only used as part of the benchmarks, this dependency may actually not be needed.

It is marked as supported on aarch64-linux in nixpkgs, but I cannot find any build of it in hydra.

Until this issue is resolved, I would mention that this project does not support ARM in the README.

Needs better key detection logic

done <<<"$((find ~/.ssh -maxdepth 1 -type f -not -name "*pub" -not -name "config" -not -name "authorized_keys" -not -name "known_hosts") || exit 1)"

My ~/.ssh is a symbolic link into a dotfiles repository, and find will skip over it unless passed the -H or -L flags.

Even with that fixed, I see it's going to pass to rage files that are not SSH keys, such as known_hosts.old that is automatically generated by ssh-keygen.

I think it'd be better to be conservative here and only auto-detect the standard id_ed25519/id_rsa key files, and have the user rely on --identity for non-standard setups.

Decryption fails when no SSH key files are found

When no files match the find command used to auto-detect identity files (see #5), agenix ends up passing an empty filename identity to rage:

done <<<"$((find ~/.ssh -maxdepth 1 -type f -not -name "*pub" -not -name "config" -not -name "authorized_keys" -not -name "known_hosts") || exit 1)"

+ /nix/store/wqy46cyqqh9hkr542n6kyghqzh0s4v07-rage-unstable-2020-09-05/bin/rage --decrypt --identity /home/aasg/.ssh/id_rsa --identity '' -o /tmp/tmp.IlafYcjgOA/somesecret somesecret
Error: No such file or directory (os error 2)

[ERROR i18n_embed::requester] Unable to parse your locale: ParseError(InvalidLanguage)

Encountered on 20.09, resulting in secrets not being decrypted.

dmesg | grep stage-2-
# [ERROR i18n_embed::requester] Unable to parse your locale: ParseError(InvalidLanguage)

The fix is:

{config, pkgs, lib, ...}:
{
  system.activationScripts.agenixFixLocale = lib.stringAfter [ "stdio" ] ''
    export LANG=${config.i18n.defaultLocale} \
           LOCALE_ARCHIVE=${config.i18n.glibcLocales}/lib/locale/locale-archive
  '';
}

Likely, locale should be set for stage-2 in here, since other things might depend on it as well, but having experienced no side-effects without agenix, can't be certain. Maybe it's just a rust thing, no idea.

umask is inverted

pennae ryantm: since we see you, i think you inverted the umask in agenix by accident
ryantm pennae: Are you talking about

(umask 0400; LANG=${config.i18n.defaultLocale} ${ageBin} --decrypt ${identities} -o "$TMP_FILE" "${secretType.file}")
?
pennae yeah
ryantm pennae: Could you be more specific what you mean about "inverted"?
pennae as it stands it'll clear the owner-read bit and leave all others untouched either we can't brain right now or you probably meant 0177
ryantm pennae: I think you're right; thanks for letting me know. I'll look into fixing it.
pennae also there might be a bug with a root secret "something.tmp" and a non-root secret "something" that'll delete the root secret? not sure about that tho

Feature: templates based on secrets

Motivation

It has come up a couple of times that I've wanted a file to mostly not be encrypted, but have a few secrets in it (for instance, a config file that has a password in it). Of course, one can just encrypt the whole file, but it's less elegant, makes it awkward to share a common secret between files that depend on it, and makes it so only those with permissions to decrypt the secret can change other things in the file.

Proposal

The solution that I thought of is having another kind of secret that is specified by a template, and a list of secrets it depends on. Agenix would then splice in the secrets at activation time. I'm working on an implementation and will make a PR when done, but I wanted to see if people liked this idea in the meantime.

Problems

The downside is it adds some complexity to the code. We would need logic to say "if it's a regular secret, do X, if it's a template secret do Y". This is maybe okay in this case, but it worries me that this starts to be complexity creep and things become harder to maintain. I guess the question comes down to: is it worth it in this case? Is this a common enough use-case to include? It is for me, but I don't know whether other people use agenix in this way a lot.

Alternatives

In theory, this doesn't need to be managed by agenix. I could create an activationScript that runs after agenix and pieces the secrets together. However, that requires some amount of repetition of code, and I never need to do this except when managing secrets. Usually I would paste things together at build time.

Don't decrypt if secrets haven't changed from previous activation

Currently, agenix decrypts secrets every time the system is activated, even if the secrets haven't changed. Ideally, if the secrets haven't changed, the activation script would detect this and skip decrypting that secret. I can't really think of how this could be implemented though, hence the opening of this issue.

Removing old secrets

I noticed that the current logic, which relies on activationScripts, leaves old secrets around.

E.g., if I rename a secret in my secrets.nix, the new and the old secret will both be present on the machine.

Have you considered using systemd oneshot units? AFAICT, those are garbage-collected on system switch.

Incompatible with setting GRUB password(s)

Agenix should decrypt secrets before boot.loader.grub.users.«name».passwordFile is required, so that a password for GRUB can be set.

(Currently, a workaround is to run nixos-rebuild test before nixos-rebuild switch.)

Workflow for new builds - General Help - Question

I am relatively new to nix, so I am having a hard time determining what the workflow would be for a new build. Meaning we do not know the public key of the host, for we have yet to build the system.

attribute 'agenixRoot' missing

After a recent update of nixpkgs unstable, the system rebuild fails with this error message "attribute 'agenixRoot' missing".

I found this pull request NixOS/nixpkgs#136605 has changes to users/groups and system activation scripts, but sadly I can't quite understand how does the activation process of NixOS work.

Hope you can have a look of it :)

Feature: restart systemd service when relevant secret updated

If a systemd service depends on a secret, and agenix is managing that secret, it would be nice to restart the service on nixos-rebuild switch. Now, at the moment agenix has no way of knowing which service depends on what secret, so we could maybe add a field to specify that per-secret. Alternatively, a more general solution which is what morph does, is to just add a field for a command (or maybe script) to be run when the secret is deployed / changes.

The way I am currently managing this is via systemd paths:

{
    systemd.paths."secret-watcher" = {
      wantedBy = [ "multi-user.target" ];
      pathConfig = {
        PathModified = config.agenix."secret".path;
      };
    };
    systemd.services."secret-watcher" = {
      serviceConfig = {
        Type = "oneshot";
        ExecStart = "systemctl restart foo.service";
      };
    };
}

I'm not sure if there is a better nix-internal way to do this, but it shouldn't be too hard to translate this solution into an agenix-internal one. I'm happy to implement it if people like the idea.

pipe (generated) key to agenix

I just got agenix set up today. Yay!

I'd like to be able to generate machine keys without a lot of manual steps. It's a small, useful thing and I'd be happy to submit and impl if we can settle on a spec.

Rather than integrating a particular password gen how about adding support for taking the cleartext from stdin?

Either overload the existing -e mode:

cat /dev/urandom | head -c 128 | agenix -e mysecret.age

or add a new mode like -i

wdyt?

Option to use minisign to verify secrets

As called out in https://github.com/ryantm/agenix#threat-modelwarnings, age does not (currently?) support authentication of secrets. However, if it were paired with minisign, it may be possible to at least sidestep this limitation.

I imagine the API just being an additional 2 options: minisignFile and verifySignature. If verifySignature is true and minisignFile is unset, try ${file}.minisig; if verifySignature is true and minisignFile is set, use that file; if verifySignature is false, don't verify.

Could also change minisignFile to signatureFile / signaturePath. This would allow extensibility if we ever want to implement support for a different signing program, and could just add a signatureType enum.

If the secret is validated, proceed. Otherwise, error loudly that the secret may have been tampered with. (Provide escape hatch to deploy / switch / whatever in spite of this?)

Thoughts? Would this be accepted as a PR?

EDIT: Of course, the signing key would need to be accessible; probably as another option.

darwin build failure: `system.activationScripts.users.deps` does not exist

I'm using Nix (with nix-darwin and flakes support) on macOS 11.6 (though this happened on previous versions of macOS too). I've configured agenix as suggested in the README, but nix build fails with the following error:

error: The option `system.activationScripts.users.deps' does not exist. Definition values:
       - In `<unknown-file>':
           [
             "agenixRoot"
           ]

I think this is the relevant file in nix-darwin that configures system.activationScripts.users, and deps is not one of its attributes.

Bear with me, as I'm new-ish to Nix and haven't yet started working with NixOS: judging by the declaration of deps as a required(?) attribute for activation scripts in NixOS here, I'm guessing it would make more sense for nix-darwin to add support for this rather than have agenix accommodate nix-darwin? If so, I'm not sure about the level of effort there, so I'd welcome any advice about a temporary workaround for the time being.

TODO: add README with threat-model

People unfamiliar with age might be surprised that secrets are not authenticated.
This means that every attacker that has write access to the repository can modify secrets because public keys are exposed.
This seems not a problem on the first glance because changing configuration itself could
expose secrets easily. However it is easier to review configuration changes rather than random secrets i.e. 4096-bit rsa keys.
This would be solved by having a message authentication code (MAC) like other implementations like gpg or sops have, however this was left out for simplicity in age.

Incompatible with dry activation of users/groups

nixpkgs PR 136605 adds a feature to perform a dry-run to show which users/groups will be added or removed without switching to the configuration. The PR enables this feature for the system.activationScripts.users activation script by default which Agenix also uses.

This breaks nixos-rebuild because the agenixRoot attribute is not available during the dry-run. Disabling dryRun for the system.activationScripts.users script resolves the issue.

system.activationScripts.users.supportsDryActivation = lib.mkForce false;

It looks like agenix can check if the NIXOS_ACTION environment variable equals "dry-activate" to alter behavior during a dry-run. I'm not sure what all is required to make the agenixRoot attribute available.

Here's the full stack trace:

   error: attribute 'agenixRoot' missing

   at /nix/store/6dcqil119qr3sad2lp9ykkkc852ppmqm-source/lib/strings-with-deps.nix:72:71:

       71|           else if done ? ${entry} then f done (tail todo)
       72|           else f (done // listToAttrs [{name = entry; value = 1;}]) ([predefined.${entry}] ++ tail todo);
         |                                                                       ^
       73|     in (f {} arg).result;

   … while evaluating 'f'

   at /nix/store/6dcqil119qr3sad2lp9ykkkc852ppmqm-source/lib/strings-with-deps.nix:61:17:

       60|     let
       61|       f = done: todo:
         |                 ^
       62|         if todo == [] then {result = []; inherit done;}

   … from call site

   at /nix/store/6dcqil119qr3sad2lp9ykkkc852ppmqm-source/lib/strings-with-deps.nix:72:16:

       71|           else if done ? ${entry} then f done (tail todo)
       72|           else f (done // listToAttrs [{name = entry; value = 1;}]) ([predefined.${entry}] ++ tail todo);
         |                ^
       73|     in (f {} arg).result;

   … while evaluating 'f'

   at /nix/store/6dcqil119qr3sad2lp9ykkkc852ppmqm-source/lib/strings-with-deps.nix:61:17:

       60|     let
       61|       f = done: todo:
         |                 ^
       62|         if todo == [] then {result = []; inherit done;}

   … from call site

   at /nix/store/6dcqil119qr3sad2lp9ykkkc852ppmqm-source/lib/strings-with-deps.nix:66:21:

       65|           if isAttrs entry then
       66|             let x = f done entry.deps;
         |                     ^
       67|                 y = f x.done (tail todo);

   … while evaluating the attribute 'result'

   at /nix/store/6dcqil119qr3sad2lp9ykkkc852ppmqm-source/lib/strings-with-deps.nix:68:18:

       67|                 y = f x.done (tail todo);
       68|             in { result = x.result ++ [entry.text] ++ y.result;
         |                  ^
       69|                  done = y.done;

   … while evaluating 'textClosureList'

   at /nix/store/6dcqil119qr3sad2lp9ykkkc852ppmqm-source/lib/strings-with-deps.nix:59:33:

       58|
       59|   textClosureList = predefined: arg:
         |                                 ^
       60|     let

   … from call site

   at /nix/store/6dcqil119qr3sad2lp9ykkkc852ppmqm-source/lib/strings-with-deps.nix:76:35:

       75|   textClosureMap = f: predefined: names:
       76|     concatStringsSep "\n" (map f (textClosureList predefined names));
         |                                   ^
       77|

   … while evaluating 'textClosureMap'

   at /nix/store/6dcqil119qr3sad2lp9ykkkc852ppmqm-source/lib/strings-with-deps.nix:75:35:

       74|
       75|   textClosureMap = f: predefined: names:
         |                                   ^
       76|     concatStringsSep "\n" (map f (textClosureList predefined names));

   … from call site

   at /nix/store/6dcqil119qr3sad2lp9ykkkc852ppmqm-source/nixos/modules/system/activation/activation-script.nix:40:9:

       39|
       40|       ${textClosureMap id (withHeadlines) (attrNames withHeadlines)}
         |         ^
       41|

   … while evaluating 'systemActivationScript'

   at /nix/store/6dcqil119qr3sad2lp9ykkkc852ppmqm-source/nixos/modules/system/activation/activation-script.nix:20:33:

       19|
       20|   systemActivationScript = set: onlyDry: let
         |                                 ^
       21|     set' = filterAttrs (_: v: onlyDry -> v.supportsDryActivation) (mapAttrs (_: v: if isString v then (noDepEntry v) // { supportsDryActivation = false; } else v) set);

   … from call site

   at /nix/store/6dcqil119qr3sad2lp9ykkkc852ppmqm-source/nixos/modules/system/activation/activation-script.nix:135:17:

      134|       internal = true;
      135|       default = systemActivationScript (removeAttrs config.system.activationScripts [ "script" ]) true;
         |                 ^
      136|     };

   … while evaluating the attribute 'default'

   at /nix/store/6dcqil119qr3sad2lp9ykkkc852ppmqm-source/nixos/modules/system/activation/activation-script.nix:135:7:

      134|       internal = true;
      135|       default = systemActivationScript (removeAttrs config.system.activationScripts [ "script" ]) true;
         |       ^
      136|     };

   … while evaluating the attribute 'value.content'

   at /nix/store/6dcqil119qr3sad2lp9ykkkc852ppmqm-source/lib/modules.nix:708:14:

      707|     { _type = "override";
      708|       inherit priority content;
         |              ^
      709|     };

   … while evaluating the attribute 'value._type'

   at /nix/store/6dcqil119qr3sad2lp9ykkkc852ppmqm-source/lib/modules.nix:648:73:

      647|       highestPrio = foldl' (prio: def: min (getPrio def) prio) 9999 defs;
      648|       strip = def: if def.value._type or "" == "override" then def // { value = def.value.content; } else def;
         |                                                                         ^
      649|     in {

   … while evaluating anonymous lambda

   at /nix/store/6dcqil119qr3sad2lp9ykkkc852ppmqm-source/lib/modules.nix:547:19:

      546|           # Avoid sorting if we don't have to.
      547|           if any (def: def.value._type or "" == "order") defs''.values
         |                   ^
      548|           then sortProperties defs''.values

   … from call site

   at /nix/store/6dcqil119qr3sad2lp9ykkkc852ppmqm-source/lib/modules.nix:547:14:

      546|           # Avoid sorting if we don't have to.
      547|           if any (def: def.value._type or "" == "order") defs''.values
         |              ^
      548|           then sortProperties defs''.values

   … while evaluating the attribute 'values'

   at /nix/store/6dcqil119qr3sad2lp9ykkkc852ppmqm-source/lib/modules.nix:551:9:

      550|       in {
      551|         values = defs''';
         |         ^
      552|         inherit (defs'') highestPrio;

   … while evaluating the attribute 'mergedValue'

   at /nix/store/6dcqil119qr3sad2lp9ykkkc852ppmqm-source/lib/modules.nix:557:5:

      556|     # Type-check the remaining definitions, and merge them. Or throw if no definitions.
      557|     mergedValue =
         |     ^
      558|       if isDefined then

   … while evaluating the option `system.dryActivationScript':

   … while evaluating the attribute 'value'

   at /nix/store/6dcqil119qr3sad2lp9ykkkc852ppmqm-source/lib/modules.nix:525:9:

      524|     in warnDeprecation opt //
      525|       { value = builtins.addErrorContext "while evaluating the option `${showOption loc}':" value;
         |         ^
      526|         inherit (res.defsFinal') highestPrio;

   … while evaluating anonymous lambda

   at /nix/store/6dcqil119qr3sad2lp9ykkkc852ppmqm-source/lib/modules.nix:140:72:

      139|           # For definitions that have an associated option
      140|           declaredConfig = mapAttrsRecursiveCond (v: ! isOption v) (_: v: v.value) options;
         |                                                                        ^
      141|

   … from call site

   at /nix/store/6dcqil119qr3sad2lp9ykkkc852ppmqm-source/lib/attrsets.nix:304:20:

      303|               then recurse (path ++ [name]) value
      304|               else f (path ++ [name]) value;
         |                    ^
      305|         in mapAttrs g set;

   … while evaluating 'g'

   at /nix/store/6dcqil119qr3sad2lp9ykkkc852ppmqm-source/lib/attrsets.nix:301:19:

      300|           g =
      301|             name: value:
         |                   ^
      302|             if isAttrs value && cond value

   … from call site

   … while evaluating the attribute 'system.dryActivationScript'

   … while evaluating the attribute 'dryActivationScript' of the derivation 'nixos-system-infinitejest-21.11.20210908.09cd65b'

   at /nix/store/6dcqil119qr3sad2lp9ykkkc852ppmqm-source/pkgs/stdenv/generic/make-derivation.nix:205:7:

      204|     // (lib.optionalAttrs (attrs ? name || (attrs ? pname && attrs ? version)) {
      205|       name =
         |       ^
      206|         let

error unsupported ssh identity

Here is what I did
agenix -e postgresPassword.age -i ~/.ssh/hetzner
edit the file and then quit

when I try to edit again though I get the following error. It seems it's not trying to use the key I provided

❯ agenix -e postgresPassword.age -i ~/.ssh/hetzner
Error: Unsupported SSH identity: ⁨/Users/raphael/.ssh/id_rsa

Here are the contents of my secrets.nix

  raphael = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGyQSeQ0CV/qhZPre37+Nd0E9eW+soGs+up6a/bwggoP [email protected]";
  hetzner-AX41 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAi5EeQLa6AwhHI2QO4QGtOArRBzmz3q7/pGmiiR0pV5";
in
{
  "postgresPassword.age".publicKeys = [ raphael hetzner-AX41 ];
}

I have verified that the content of the ~/.ssh/hetzner.pub key match that of what I put in the raphael variable in my secrets.nix.

Maybe I am missed something somewhere?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.