Agefs: agenix as a FUSE filesystem

Hi all,

Here is something I created recently, as a drop-in replacement module for agenix (uses the same options).

Instead of a shell script that decrypts all secrets during system activation, agefs is a small FUSE filesystem written in Golang that decrypts secrets on the fly as they are accessed. It creates filesystem entries for the secrets when mounted, but defers the actual decryption of the secrets until they’re opened. This solves many issues with agenix such as slow activation time, errors when an identity doesn’t exist/can’t decrypt a file, being swarmed by many decryption prompts on login (e.g. Touch ID prompt spam with age-plugin-se, this is why I created it initially), and when used with an interactive plugin can also serve as a method to authorize each secret use individually.

If there’s no identity to decrypt a secret, or decryption otherwise fails, the application will get back EIO when trying to access the file. The decryption will be tried again on next file access.

It works on macOS and NixOS, both for system and user secrets (using Home-Manager). On macOS, installing macFUSE is required.

Demo video

By default it will decrypt secrets every time they are opened. But this can get annoying with interactive plugins (such as Touch ID with age-plugin-se), so an option age.keepCached = true allows to keep the decrypted contents in memory after it’s been opened once.

On macOS it will by default wait for agefs to be mounted before proceeding with the activation. This ensures that by the time activation is finished, the secrets are available. It’s much quicker than agenix activation but if it’s not needed it can be disabled with age.wait = false. On NixOS this doesn’t happen.

On NixOS it uses an automount unit for system-wide secrets, so they are not even mounted until they’re needed. This requires root privileges so it’s not available for user secrets.

I thought others might find it useful, so that’s why I’m posting it here on the discourse. I would put it in the announcements category since it seems more appropriate but it seems I’m not allowed to post there… :frowning:

Here is the project link, if you also face issues with agenix then try it out and let me know how it goes!:

24 Likes

This is great! Finally a solution that avoids having to have plaintext age keys on my filesystem :slight_smile:

3 Likes

Could be just me, but I see big blank space where Demo Video is said to be.

Regardless, this sounds nice! I’ll probably give it a shot over next week or so. Thanks for making this!

3 Likes

Same. Try this link: https://cdn.andre4ik3.net/agefs.mp4

2 Likes

Yes, if the video isn’t loading you can try the link mentioned, or alternatively since it’s such a small video here it is as a GIF (maybe I should have done this from the beginning):

Demo video

4 Likes

This looks like a great addition! More and better secrets management is always good in my book.

2 Likes

Great job! I would like to try integrating this into agenix-shell.

1 Like

Great job, now integrate with modetc so we don’t have to think about mount point :wink:

Not entirely sure what you mean by this. Modetc is for managing the locations of files?, whereas this is a FUSE layer that takes your encrypted secrets and decrypts them (actually the initial implementation worked on top of a base directory, so you would give it a base directory of secrets, it would present that directory but with the files decrypted; now it works using a JSON file that provides some additional metadata that’s not possible to store in the Nix store, such as permissions).

If you mean managing of the mountpoint itself, it will be created automatically if it doesn’t exist. When agefs is unmounted the mountpoint becomes empty (but the mountpoint directory itself will remain).

Perhaps you meant making links to the decrypted secrets or moving their location? But you could just use a normal symlink for that, unless the software is really stubborn.

1 Like