ryantm / agenix Goto Github PK
View Code? Open in Web Editor NEWage-encrypted secrets for NixOS and Home manager
Home Page: https://matrix.to/#/#agenix:nixos.org
License: Creative Commons Zero v1.0 Universal
age-encrypted secrets for NixOS and Home manager
Home Page: https://matrix.to/#/#agenix:nixos.org
License: Creative Commons Zero v1.0 Universal
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.
Feel free to rename the issue's title.
For a more unified, devops-dream, future-proof secrets management.
There is the advent of (cheap, as in here-implementation / here-maintenance) FIDO2 integration on the horizon: https://github.com/str4d/age-plugin-yubikey — specifically: str4d/age-plugin-yubikey#1
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!
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?
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?
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.
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.
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
.
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.
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.
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
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
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.
I would suggest renaming sshKeyPaths
to something more general like identityPaths
because besides ssh keys you can also pass in age keys what does not look that obvious based on the name.
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:
Line 99 in 53aa91b
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).
Is it planned to support aarch64-darwin?
Currently we need to use nix-packages in Rosetta mode for this.
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!
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.
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.
DRY → divnix/digga#37
So that a similar functionality to git crypt
can be achieved.
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?
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.
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?
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.
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!
enable CSIu support (good for https://discuss.kakoune.com/t/key-maps-as-key-locations/1414)
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.
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 ..
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
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
+++ 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.
Line 27 in c5558c8
is superfluous but it only runs after agenixMountSecrets
.
The permission update is done after writing the file:
https://github.com/ryantm/age-nix/blob/202ea075cf9e75a57543478ae7efb0985b77a3df/modules/age.nix#L65
$ age -d -i ./key.txt -o foo ./data.tar.gz.age
$ ls -la foo
.rw-r--r-- 1,5k joerg 1 Sep 6:52 foo
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
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.
Line 111 in 8af9714
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.
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
:
Line 111 in 8af9714
+ /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)
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.
pennae ryantm: since we see you, i think you inverted the umask in agenix by accident
ryantm pennae: Are you talking about
Line 22 in 8652eb6
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.
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.
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.
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.
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.
Is it planned to support aarch64-macos?
Currently we need to use nix-packages in Rosetta mode for this.
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.
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
.)
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.
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 :)
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.
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?
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.
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.
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.
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
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?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.