Im currently trying to migrate some of my secrets management to sops-nix
. To get a better feeling for it, I wanted to play around with it a bit on some toy examples, but this is already causing me to struggle with details.
Lets assume we have two machines:
A
: a macOS machine, configured throughnix-darwin
,B
: a NixOS VM that was just created for testing.
The secrets setup looks like this: three files, one for each machine and one with shared secrets.
# A.yaml
a: A
# B.yaml
b: B
# shared.yaml
shared: SHARED
with a .sops.yaml
file that looks like this
keys:
- &host_A age1XXXXXXXXXXXXXXXXXXXXXXXXX
- &host_B ssh-ed25519 XXXXXXXXXXXXXXXXXXX root@B
creation_rules:
- path_regex: A\.yaml$
key_groups:
- age:
- *host_A
- path_regex: B\.yaml$
key_groups:
- age:
- *host_B
- path_regex: shared\.yaml$
key_groups:
- age:
- *host_B
- *host_A
where:
host_A
is aage
key generate by hand onA
, andhost_B
is the public key of/etc/ssh/ssh_host_ed25519_key
onB
I run sops --encrypt ... > X.yaml
on the corresponding files (or encrypt them by directly calling sops X.yaml
). And the encrypted files look ok (for sample output of sops --encrypt ...
command, look at the end).
I my darwin configuration I have
let
# secrets are in separate repo, works fine,
# i.e. path is resolved correctly and files can be found at evaluation time
secretspath = builtins.toString inputs.nix-secrets;
in
{
sops = {
defaultSopsFile = "${secretspath}/secrets.yaml";
age = {
keyFile = "/User/${user}/Library/Application Support/sops/age/keys.txt";
};
};
}
and in my nixOS config I have
let
# secrets are in separate repo, works fine,
# i.e. path is resolved correctly and files can be found at evaluation time
secretspath = builtins.toString inputs.nix-secrets;
in
{
users.users = {
${user} = {
# ....
passwordFile = config.sops.secrets.b.path;
};
};
sops = {
defaultSopsFile = "${secretspath}/secrets/pilatus.yaml";
age = {
sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ];
};
secrets."b".neededForUsers = true;
};
}
When I now rebuild the next generation on my macOS machine, everything is fine and works as expected. On the NixOS side I run into trouble:
❯ sudo nixos-rebuild switch --flake .#pilatus --show-trace
[sudo] password for iilak:
warning: Git tree '/home/iilak/nix-config' is dirty
building the system configuration...
warning: Git tree '/home/iilak/nix-config' is dirty
activating the configuration...
sops-install-secrets: Imported /etc/ssh/ssh_host_rsa_key as GPG key with fingerprint ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZz
sops-install-secrets: Imported /etc/ssh/ssh_host_ed25519_key as age key with fingerprint age1YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY
/nix/store/0vf1wznjii73573f5rmh9jx8y5gvf37d-sops-install-secrets-0.0.1/bin/sops-install-secrets: failed to decrypt '/nix/store/42cnwyzvzdrwk6y6xkrkvl811y32vlgl-source/B.yaml': Error getting data key: 0 successful groups required, got 0
Activation script snippet 'setupSecretsForUsers' failed (1)
warning: password file ‘/run/secrets-for-users/b’ does not exist
setting up /etc...
Failed to run activate script
reloading user units for iilak...
restarting sysinit-reactivation.target
the following new units were started: NetworkManager-dispatcher.service
warning: error(s) occurred while switching to the new configuration
The problematic part seems to be this
Error getting data key: 0 successful groups required, got 0
I don’t understand this, since the encrypted file clearly contains a group for that key…
I tried to decrypt it manually, but there I run into
❯ sops -d /nix/store/42cnwyzvzdrwk6y6xkrkvl811y32vlgl-source/B.yaml
Failed to get the data key required to decrypt the SOPS file.
Group 0: FAILED
ssh-ed25519 XXXXXXXXXXXXXXXXXXXXXXXXXXXXX: FAILED
- | failed to create reader for decrypting sops data key with
| age: no identity matched any of the recipients
Recovery failed because no master key was able to decrypt the file. In
order for SOPS to recover the file, at least one key has to be successful,
but none were.
~/nix-config feature/sops_nix !3 ✘ 128 iilak@B ▼
❯ sudo sops -d /nix/store/42cnwyzvzdrwk6y6xkrkvl811y32vlgl-source/B.yaml
Failed to get the data key required to decrypt the SOPS file.
Group 0: FAILED
ssh-ed25519 XXXXXXXXXXXXXXXXXXXXXXXXXXXxx: FAILED
- | failed to load age identities: failed to open file: open
| /root/.config/sops/age/keys.txt: no such file or directory
Recovery failed because no master key was able to decrypt the file. In
order for SOPS to recover the file, at least one key has to be successful,
but none were.
If anybody has an idea what is going wrong here, I would really appreciate some input…
Notes
❯ sops --encrypt A.yaml
a: ENC[AES256_GCM,....]
sops:
age:
- recipient: age1XXXXXXXXXXXXXXXXXXXXXXX
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
........
-----END AGE ENCRYPTED FILE-----
lastmodified: "2025-06-09T20:16:13Z"
mac: ENC[....]
unencrypted_suffix: _unencrypted
version: 3.10.2
/tmp/test
❯ sops --encrypt B.yaml
b: ENC[AES256_GCM,.......]
sops:
age:
- recipient: ssh-ed25519 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXx
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
......
-----END AGE ENCRYPTED FILE-----
lastmodified: "2025-06-09T20:16:17Z"
mac: ENC[AES256_GCM,.....]
unencrypted_suffix: _unencrypted
version: 3.10.2
/tmp/test
❯ sops --encrypt shared.yaml
shared: ENC[AES256_GCM,......]
sops:
age:
- recipient: ssh-ed25519 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXxx
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
....
G56EZQ==
-----END AGE ENCRYPTED FILE-----
- recipient: age1XXXXXXXXXXXXXXXXXXXXX
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
....
-----END AGE ENCRYPTED FILE-----
lastmodified: "2025-06-09T20:16:19Z"
mac: ENC[AES256_GCM,......]
unencrypted_suffix: _unencrypted
version: 3.10.2