Skip to content

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

NovaViper
Copy link

@NovaViper NovaViper commented Apr 24, 2025

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-yubikey plugin 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:

  • Using keys that doesn't require a pin seems to work more consistently (especially for the home-manager NixOS module) than those that do. From what I can tell, it's because the installer program's running in tty which doesn't have keyboard access
  • Putting the public keys generated from the plugins in a keys.txt file 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 yubikeys
  • Changing the keyFile path without rebuilding the configs first can cause you to get locked out of the secrets (the sops command itself will just refuse to decrypt the secrets). This I found out the hard way 😭
  • I haven't been able to get any neededForUsers secrets 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

  • Add a list of age plugins for the option sops.age.plugins and make sure to install them with environment.systemPackages so you can use the plugins to generate the keys.
    • You'll need to add in either the sops-nix overlay or override the sops package with the following in order to ensure the sops program itself reads the keys created by the plugins:
      (sops.withAgePlugins (p: [
        p.age-plugin-fido2-hmac
        p.age-plugin-yubikey
        p.age-plugin-tpm
        p.age-plugin-ledger
      ]))
    
  • Generate some age keys using the plugins
    • For age-plugin-yubikey, run age-plugin-yubikey and follow prompts (See guide for more options)
    • For age-plugin-fido2-hmac, run age-plugin-fido2-hmac -g (See the guide for more detailed information, I don't have this setup to give any further details)
    • For better reliability, make sure to generate keys that DO NOT require a PIN, as the plugins will not be able to get keyboard access in order to receive the pin when booting up a system.
  • In order to use the keys with sops itself, you must save the age identities of the keys in ~/.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).
  • Set sops.age.keyFile for both NixOS and Home-manager (which in my case is /var/lib/sops/keys.txt for NixOS and /home/USERNAME/.config/sops/age/keys.txt for Home-Manager). Sops-nix then will try to use the keys listed here
  • Create secrets as you normally would, sops should then begin to use the plugins and prompt you for the newly made hardware keys.

plugins = lib.mkOption {
type = lib.types.listOf lib.types.package;
default = [
pkgs.age-plugin-fido2-hmac
Copy link
Owner

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.

Copy link
Owner

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.

Copy link
Author

@NovaViper NovaViper May 16, 2025

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)

Copy link
Author

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 😭

Copy link
Author

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 🤔

@@ -34,8 +34,9 @@ in
{
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}";};
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cc @brianmcgee for review.

@NovaViper
Copy link
Author

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)

@NovaViper
Copy link
Author

NovaViper commented May 16, 2025

And the write up is done! @Mic92

@NovaViper NovaViper changed the title feat: add age plugin and fido2 hmac support, take 2 feat: add age plugin support, take 2 May 16, 2025
@NovaViper NovaViper force-pushed the age-plugin branch 2 times, most recently from 782693f to 162aec0 Compare May 26, 2025 17:11
@NovaViper
Copy link
Author

I completely missed the withEnvironment function that's used for the activation script in my last rebase, which caused the activation script not to use the plugins! 👀
Adding the plugins to the PATH environment variable there makes the plugins work again.

@sean-xyz
Copy link

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 sops = prev.sops.withAgePlugins overlay or not. I'm very new to nix and I didn't understand that bit, so I initially neglected it and found that it still worked.

Co-authored-by: brianmcgee <brian@41north.dev>
@NovaViper
Copy link
Author

Updated to resolve some merge conflicts. Also I've noticed I can actually get the neededForUser secrets to decrypt during boot, but only when I have services like userborn enabled. I still hadn't figured out how to get it working with the activation scripts but it does work with the systemd service.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants