pkgs/sops-install-secrets/main.go in decryptSecret (current master, also present in earlier tags) has a broken decrypt cache:
func decryptSecret(s *secret, sourceFiles map[string]plainData) error {
sourceFile := sourceFiles[s.SopsFile]
if sourceFile.data == nil || sourceFile.binary == nil {
plain, err := decrypt.File(s.SopsFile, string(s.Format))
...
The intent is "decrypt the file once, reuse for all subsequent secrets from the same sopsFile". The actual condition fires every time: for YAML/JSON secrets with a non-empty key, only sourceFile.data is populated and sourceFile.binary stays nil. For binary/dotenv/INI secrets it's the opposite. So data == nil || binary == nil is always true for a single source file, and decrypt.File is invoked once per secret entry.
For age-plugin-based hardware tokens (age-plugin-fido2-hmac, age-plugin-yubikey) each decrypt.File triggers a fresh PIN/touch prompt. A host with 5 secrets in one secrets.yaml therefore has to PIN+touch the YubiKey/Security Key 5 times per darwin-rebuild switch / home-manager switch, even though the manifest references a single sopsFile.
Repro
sops.defaultSopsFile = ./secrets.yaml;
sops.secrets = {
a = { };
b = { };
c = { };
};
with a FIDO2 age recipient. Observe 3× PIN+touch instead of 1.
Fix proposal
Use a real existence check, e.g.
if _, cached := sourceFiles[s.SopsFile]; !cached {
plain, err := decrypt.File(...)
...
sourceFiles[s.SopsFile] = sourceFile
}
A minimal one-character fix (|| → &&) also works for the common cases where each sopsFile is accessed uniformly via key or as a whole file, but does not cover mixing the two access modes against the same file.
Workaround
Override sops.package with a postPatch build that flips || to &&. Drops 5× FIDO2 prompts to 1× on a Darwin/home-manager host with a single consolidated secrets.yaml.
Environment
- Platform:
aarch64-darwin, home-manager module
- sops-nix:
c591bf665727040c6cc5cb409079acb22dcce33c
- age plugins:
age-plugin-fido2-hmac 0.5.0, age-plugin-yubikey 0.5.1
pkgs/sops-install-secrets/main.goindecryptSecret(currentmaster, also present in earlier tags) has a broken decrypt cache:The intent is "decrypt the file once, reuse for all subsequent secrets from the same
sopsFile". The actual condition fires every time: for YAML/JSON secrets with a non-emptykey, onlysourceFile.datais populated andsourceFile.binarystaysnil. For binary/dotenv/INI secrets it's the opposite. Sodata == nil || binary == nilis always true for a single source file, anddecrypt.Fileis invoked once per secret entry.For age-plugin-based hardware tokens (
age-plugin-fido2-hmac,age-plugin-yubikey) eachdecrypt.Filetriggers a fresh PIN/touch prompt. A host with 5 secrets in onesecrets.yamltherefore has to PIN+touch the YubiKey/Security Key 5 times perdarwin-rebuild switch/home-manager switch, even though the manifest references a singlesopsFile.Repro
with a FIDO2 age recipient. Observe 3× PIN+touch instead of 1.
Fix proposal
Use a real existence check, e.g.
A minimal one-character fix (
||→&&) also works for the common cases where eachsopsFileis accessed uniformly via key or as a whole file, but does not cover mixing the two access modes against the same file.Workaround
Override
sops.packagewith apostPatchbuild that flips||to&&. Drops 5× FIDO2 prompts to 1× on a Darwin/home-manager host with a single consolidatedsecrets.yaml.Environment
aarch64-darwin,home-managermodulec591bf665727040c6cc5cb409079acb22dcce33cage-plugin-fido2-hmac0.5.0,age-plugin-yubikey0.5.1