-
Notifications
You must be signed in to change notification settings - Fork 196
feat: add age plugin support, take 2 #781
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
849b49f to
7678b2d
Compare
modules/home-manager/sops.nix
Outdated
| plugins = lib.mkOption { | ||
| type = lib.types.listOf lib.types.package; | ||
| default = [ | ||
| pkgs.age-plugin-fido2-hmac |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think, I don't want to set a default here for the first iteration. Especially this plugin might not work very well since we cannot enter a PIN easily - Maybe this works for activation scripts but with systemd services I am not sure how this is supposed to work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this pull request should at least describe how one can setup one age plugin of choice from start to end.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think, I don't want to set a default here for the first iteration. Especially this plugin might not work very well since we cannot enter a PIN easily - Maybe this works for activation scripts but with systemd services I am not sure how this is supposed to work.
Yeah, I can leave that option blank. 👍🏾
Speaking of systemd.. so I was trying userborn on my laptop to see if the plugins (specifically the yubikey one) works but with the sops-install-secrets-for-users service, I'm seeing warnings about the HOME variable isn't defined and it fails to interact with the key (the key flashes on like it got a request to do something but fails to do actually do the request)
● sops-install-secrets-for-users.service
Loaded: loaded (/etc/systemd/system/sops-install-secrets-for-users.service; enabled; preset: ignored)
Active: active (exited) since Fri 2025-05-16 10:47:05 CDT; 50min ago
Invocation: c65e2068806247bb8ed3193542af0afc
Process: 34406 ExecStart=/nix/store/yh0g1mxx2zcv4snnrgblxdss9mjbxbs5-sops-install-secrets-0.0.1/bin/sops-install-secrets -ignore-passwd /nix/store/b9swyn2kxpf98cdycg6yhshjm62131vw-manifest-for-users.json (code=exited, status=0/SUCCESS)
Main PID: 34406 (code=exited, status=0/SUCCESS)
IP: 0B in, 0B out
IO: 0B read, 0B written
Mem peak: 8.9M
CPU: 60ms
May 16 10:47:05 yoganova systemd[1]: Starting sops-install-secrets-for-users.service...
May 16 10:47:05 yoganova sops-install-secrets[34406]: sops-install-secrets: Imported /etc/ssh/ssh_host_ed25519_key as age key with fingerprint age1XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
May 16 10:47:05 yoganova sops-install-secrets[34406]: [AGE] time="2025-05-16T10:47:05-05:00" level=warning msg="could not determine the user home directory: $HOME is not defined"
May 16 10:47:05 yoganova sops-install-secrets[34406]: [AGE] time="2025-05-16T10:47:05-05:00" level=warning msg="could not determine the user home directory: $HOME is not defined"
May 16 10:47:05 yoganova sops-install-secrets[34406]: [AGE] time="2025-05-16T10:47:05-05:00" level=warning msg="could not determine the user home directory: $HOME is not defined"
May 16 10:47:05 yoganova sops-install-secrets[34406]: [AGE] time="2025-05-16T10:47:05-05:00" level=warning msg="could not determine the user home directory: $HOME is not defined"
May 16 10:47:05 yoganova systemd[1]: Finished sops-install-secrets-for-users.service.
Even stranger is the home-manager sops-nix service. It just completely skips the key, similarly flashing on like how I described with the install secrets service. But as soon as I turn off userborn, the home-manager service works fine for the home-manager service!
🤔 What's really odd is I've been able to get the plugin working over in agenix with home-manger but not with the user passwords (similarly had issues with the plugin not being executed during the boot process, always had to include an ssh key as a fallback key to decrypt the user passwords)
Additionally, the plugin activates normally when I switch the configurations for both Home-manager and NixOS (specifically with userborn off)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this pull request should at least describe how one can setup one age plugin of choice from start to end.
Will do! I intended to do one earlier but I forgot to do so 😭
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Even stranger is the home-manager sops-nix service. It just completely skips the key, similarly flashing on like how I described with the install secrets service. But as soon as I turn off userborn, the home-manager service works fine for the home-manager service!
Ah I realized. The age key I was using on my yubikey had a PIN policy on it, switched that one out for a pinless one and now the home-manager service doesn't break anymore (with either userborn and standard! 🥳 ); but I still can't figure out that $HOME is not defined warning with the NixOS service 🤔
| wantedBy = [ "systemd-sysusers.service" ]; | ||
| before = [ "systemd-sysusers.service" ]; | ||
| environment = cfg.environment; | ||
| environment = cfg.environment // {PATH = lib.mkForce "${cfg.environment.PATH}:${lib.makeSearchPathOutput "bin" "sbin" cfg.age.plugins}";}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cc @brianmcgee for review.
|
I also withdrew the darwin changes in case those caused breakages over on that side (I can't test that build at all since I don't have a Mac) |
|
And the write up is done! @Mic92 |
782693f to
162aec0
Compare
|
I completely missed the |
|
I am interested in TPM-based decryption at system boot. I got it working thanks to this pull request. I made a proof of concept here for a virtual machine. I also got it working on a physical machine, but not in a state to share. I found that it works the same whether I include the |
|
Updated to resolve some merge conflicts. Also I've noticed I can actually get the |
I had enabled userborn as well and it was working well in my virtual machine with a virtual TPM (for |
|
after having success with age-plugin-tpm, i'm now trying age-plugin-fido2-hmac and having less success. in particular, for nixos secrets loaded at boot, with or without
age-plugin-fido2-hmac always requires user presence (e.g. touching the key), while piv (age-plugin-yubikey) doesn't. when you used piv, were you able to test it with user presence? i'm not sure if my problem is the presence check (i don't see my key flashing to prompt me) or fido2 prerequisites not loading in time btw, in my view age-plugin-tpm has a nearly identical use case to the ssh host key route, but with the added security benefit of TPM for leaked material. the piv or ctap2 plugins may also have some use in a live-image, though i'm not sure how it can be done without exposing identity files. |
@sean-xyz
And yeah, all of my age keys on my yubikeys are set to use a touch confirmation. I found that having a PIN confirmation seemed to cause issues during boot but the touch based one works the most reliably. |
|
👀 Also looking I'm a good bit behind with the upstream repo, will rebase it shortly |
Now the identity files.. I particularly didn't use the fido2 plugin because of it exposes the identity files directly, vs the yubikey one only showing the public key. I have the identity files located under
🤔 This I'm not too sure if you can without in some way placing down the identity files somewhere. I notice without it, sops literally has no idea where the keys are, it always looks at |
|
I wasn't able to get this branch working, whether I used the overlay or not. It seems like the overlay may not actually be needed, since you set Sops doesn't decrypt any secrets using Is there anything obvious I may be missing? |
|
@rsdy Yeah you do need pcscd enabled! Also did you make sure to copy the identity files over into
The overlay is for the actual command that you access in the terminal when you are setting up the secrets. The service path is something different altogether. The path being set there is only applied to the environment the service will run in, not the entire system (basically anywhere that you'll run sops when you go to create the secrets as a user). |
|
It seems like in principle I needed to force systemd activation for sops-nix by enabling userborn, otherwise I could not control the execution order: This works now, thanks! 🎉 |
d3b6c36 to
33eea0a
Compare
|
No, it will use the activation script as normal (that's what I use on my system)
Sep 29, 2025 11:07:05 AM Casey Link ***@***.***>:
… Ramblurr left a comment (Mic92/sops-nix#781) #781 (comment)
I'd also like to give this a test, but is there some consequence to setting enabling and then potentially disabling userborn (if the test doesn't work out)? I don't want to lockmyself out the system heh.
—
Reply to this email directly, view it on GitHub #781 (comment), or unsubscribe https://github.com/notifications/unsubscribe-auth/ABW3US7N3IUOM7WLS5XGBDT3VFKJRAVCNFSM6AAAAAB32IPDG2VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZTGNBXHA2DOMBVGE.
You are receiving this because you were mentioned.Message ID: ***@***.***>
|
|
Ok, so I want to get this working with pinentry, but only for the home-manager sops-nix user service. This should be possible to do, after all the pinentry works just fine for my current PGP key setup which I'm trying to migrate away from.
To make this work I had to make the pinentry available to the sops-nix service.
|
|
My previous comment was all wrong, neither of those work. I thought that it worked because I happened to start Repro:
Result: sops-nix decrypts the file using the yubikey succesfully. Could this be related to #356, namely that because sops-install-secrets is using sops as a library it could be bypassing some important code for loading the plugins or pinentry? |
Do you have a secret that's accessible to the user setup for the home-manager version of sops-nix? I had to create a separate ssh key for home-manager sops-nix to use along with yubikey.. I haven't been able to make it where it strictly used the yubikey for the entire operating sadly. And in reguards for pinentry support.. that's outside the scope of what I was working on, I sadly have no idea where to begin with that one (since I don't use pinentry at all) |
This comment was marked as resolved.
This comment was marked as resolved.
|
... Ok something broke majorly with that, fixing it now |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
flake.nix(1 hunks)modules/home-manager/sops.nix(2 hunks)modules/sops/default.nix(3 hunks)modules/sops/secrets-for-users/default.nix(2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: tests
🔇 Additional comments (6)
modules/sops/secrets-for-users/default.nix (2)
20-20: LGTM: Plugin PATH correctly added to environment.The
lib.mkDefaultallows users to override if needed, andlib.makeBinPathcorrectly constructs the PATH from the plugin packages.
40-40: LGTM: Plugin path correctly added to systemd service.Setting
path = cfg.age.pluginsmakes the plugin binaries available in the service's execution environment. Systemd merges path entries by default, so this won't override other paths.modules/home-manager/sops.nix (1)
252-258: LGTM: Plugin option correctly defined.The option definition follows the standard pattern with appropriate type (
listOf package), sensible default (empty list), and clear description.modules/sops/default.nix (3)
333-339: LGTM: Plugin option correctly defined.The option definition follows the standard pattern with appropriate type (
listOf package), sensible default (empty list), and clear description. This is consistent with the same option defined in the home-manager module.
46-46: LGTM: Plugin PATH correctly added to environment.The
lib.mkDefaultallows users to override if needed, andlib.makeBinPathcorrectly constructs the PATH from the plugin packages. This is consistent with the implementation inmodules/sops/secrets-for-users/default.nix.
450-450: LGTM: Plugin path correctly added to systemd service.Setting
path = cfg.age.pluginsuses the correct systemd option (notpaths), and systemd merges path entries by default, so this won't override other paths. This addresses the past review comment about merging rather than overriding.Based on past review comments.
|
|
||
| sops = prev.sops.withAgePlugins (p: [ | ||
| p.age-plugin-fido2-hmac | ||
| ]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove the default age plugin from the overlay.
According to the maintainer's past review comments and the PR objectives, a default plugin should not be set in the first iteration. The age-plugin-fido2-hmac plugin requires user presence and may not work reliably in all contexts (boot-time, systemd services, etc.). Including it as a default:
- Creates an unnecessary dependency for users who don't need this specific plugin
- Goes against the maintainer's explicit request to not set a default
- May cause confusion about which plugin to use
Based on past review comments and PR objectives.
Apply this diff to remove the default plugin:
-
- sops = prev.sops.withAgePlugins (p: [
- p.age-plugin-fido2-hmac
- ]);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| sops = prev.sops.withAgePlugins (p: [ | |
| p.age-plugin-fido2-hmac | |
| ]); |
🤖 Prompt for AI Agents
In flake.nix around lines 64 to 67, the overlay currently sets a default age
plugin by adding age-plugin-fido2-hmac to sops.withAgePlugins; remove that
default so no plugin is preselected. Edit the sops.withAgePlugins invocation to
not include age-plugin-fido2-hmac (e.g., remove the entry or supply an empty
list), ensuring the overlay does not set any default age plugin.
modules/home-manager/sops.nix
Outdated
| )) | ||
| ]; | ||
|
|
||
| PATH = lib.mkDefault lib.makeBinPath cfg.age.plugins; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
🧩 Analysis chain
Verify PATH conflicts when users define custom PATH values.
A past review comment noted that this causes conflict errors when users try to define a PATH for sops-nix. While lib.mkDefault allows users to override this value, it may not handle cases where users want to extend the PATH rather than replace it.
Consider making this conditional on cfg.age.plugins != [] to avoid setting an empty PATH when no plugins are configured, or verify that the current approach doesn't cause issues for users who need custom PATH configurations.
Based on past review comments.
🏁 Script executed:
#!/bin/bash
# Search for user configurations that might set sops.environment.PATH
# to understand potential conflict patterns
rg -n -C3 'sops\.environment\.PATH' --type=nixLength of output: 0
🏁 Script executed:
#!/bin/bash
sed -n '340,390p' modules/home-manager/sops.nixLength of output: 1737
🏁 Script executed:
#!/bin/bash
rg -n --type=nix 'age.plugins' -n modules/home-manager/sops.nixLength of output: 63
🏁 Script executed:
sed -n '1,200p' modules/home-manager/sops.nixLength of output: 5549
Conditionally set PATH only when cfg.age.plugins is non-empty
Wrap the PATH assignment in lib.mkIf (cfg.age.plugins != []) to avoid setting an empty PATH when no plugins are configured, which can break executable lookups.
🤖 Prompt for AI Agents
In modules/home-manager/sops.nix around line 359, the PATH is unconditionally
set to lib.mkDefault lib.makeBinPath cfg.age.plugins which can produce an empty
PATH; wrap this assignment with lib.mkIf (cfg.age.plugins != []) so the PATH is
only set when cfg.age.plugins is non-empty, preventing an empty PATH from being
written and preserving normal executable lookup when no plugins are configured.
Co-authored-by: brianmcgee <[email protected]>
Description
Continuing directly off of #680 .
I went and updated the changes to work with the most recent commits of sops-nix as well as adding in the wrapper changes introduced into the sops and age packages with NixOS/nixpkgs#395189. I can confirm it does work when you rebuild the NixOS install (and home-manager). I'm using the
age-plugin-yubikeyplugin on my system but all other plugins should work fine.Additionally, I also went and added the plugin support over in the home-manager module too! I don't have a Mac to test the nix-darwin changes but I'm sure it's simply just adding the same changes into the darwin module aswell.
Some Notes
Some things oddities and things I've noticed while working on getting the PR together:
keys.txtfile is crucial otherwise sops can't figure out what keys to access. And I've noticed you can put multiple public keys in the same file so that's what I did for all of my age keys from my yubikeysneededForUserssecrets that only use the yubikeys (without some other key like ssh host derived age key) to decrypt when the system boots up.It seems to be because they're being executed before systemd even has a chance to start (because the service is using activationScripts when you're not using userborn)Actually, it maybe because the plugin can't determine the user?Write-up of new steps to configure age plugins
services.pcscd.enablesops.age.pluginsand make sure to install them withenvironment.systemPackagesso you can use the plugins to generate the keys.age-plugin-yubikey, runage-plugin-yubikeyand follow prompts (See guide for more options)age-plugin-fido2-hmac, runage-plugin-fido2-hmac -g(See the guide for more detailed information, I don't have this setup to give any further details)~/.config/sops/age/keys.txt. This will allow sops to use your newly created age keys to be used by sops (otherwise it can't figure out which hardware-based secret key to use). The age identities are generated as a text file after you generate the age keys with the pluginssops.age.keyFilefor both NixOS and Home-manager (which in my case is/var/lib/sops/keys.txtfor NixOS and/home/USERNAME/.config/sops/age/keys.txtfor Home-Manager). Sops-nix then will try to use the keys listed hereSummary by CodeRabbit