sops icon indicating copy to clipboard operation
sops copied to clipboard

feat: add age plugin support

Open brianmcgee opened this issue 1 year ago • 1 comments

Another attempt at #1465 without bringing in so much code from age.

Instead, I created https://github.com/FiloSottile/age/pull/591 upstream to expose PluginTerminalUI.

TODO

  • [ ] update go.mod once https://github.com/FiloSottile/age/pull/591 is merged

brianmcgee avatar Oct 02 '24 11:10 brianmcgee

Thank you very much for this! I've marked it as a draft (we can put it back to ready once the upstream change is merged).

felixfontein avatar Oct 02 '24 17:10 felixfontein

Using this, works fine for sops -d. However if I specify public keys for creation_rules in a .sops.yaml, sops will break when editing these files since parseRecipients doesn't know what to do with age-plugin-yubikey pubkeys.

I fixed this with the following patch on top:

From 64e77bd60f8ebc8a0a5c7f8602a6f5855c892fd3 Mon Sep 17 00:00:00 2001
From: Maximilian Bosch <[email protected]>
Date: Wed, 20 Nov 2024 22:44:49 +0100
Subject: [PATCH] age/keysource: parse recipients using plugin system

Otherwise I get

    failed to parse input as Bech32-encoded age public key: malformed recipient "age1yubikey1...": invalid type "age1yubikey"

for `sops secrets/.../secrets.sops.yaml` in a directory with a
`.sops.yaml` that has creation rules with age1yubikey1* keys in its
creation rules.
---
 age/keysource.go | 19 ++++++++++++++-----
 1 file changed, 14 insertions(+), 5 deletions(-)

diff --git a/age/keysource.go b/age/keysource.go
index f04e4aff8..a9051c926 100644
--- a/age/keysource.go
+++ b/age/keysource.go
@@ -304,12 +304,21 @@ func (key *MasterKey) loadIdentities() (ParsedIdentities, error) {
 
 // parseRecipient attempts to parse a string containing an encoded age public
 // key.
-func parseRecipient(recipient string) (*age.X25519Recipient, error) {
-	parsedRecipient, err := age.ParseX25519Recipient(recipient)
-	if err != nil {
-		return nil, fmt.Errorf("failed to parse input as Bech32-encoded age public key: %w", err)
+func parseRecipient(recipient string) (age.Recipient, error) {
+	switch {
+	case strings.HasPrefix(recipient, "age1") && strings.Count(recipient, "1") > 1:
+		parsedRecipient, err := plugin.NewRecipient(recipient, tui.PluginTerminalUI)
+		if err != nil {
+			return nil, fmt.Errorf("failed to parse input as age key from age plugin: %w", err)
+		}
+		return parsedRecipient, nil
+	default:
+		parsedRecipient, err := age.ParseX25519Recipient(recipient)
+		if err != nil {
+			return nil, fmt.Errorf("failed to parse input as Bech32-encoded age public key: %w", err)
+		}
+		return parsedRecipient, nil
 	}
-	return parsedRecipient, nil
 }
 
 // parseIdentities attempts to parse the string set of encoded age identities.
-- 
2.47.0

Feel free to pick this to your branch @brianmcgee :)

Ma27 avatar Nov 20 '24 21:11 Ma27

@Ma27 applied the patch, thanks :+1:

brianmcgee avatar Nov 21 '24 09:11 brianmcgee

+1 would be great to have this in sops

visualphoenix avatar Dec 11 '24 17:12 visualphoenix

Since https://github.com/FiloSottile/age/pull/591 doesn't seem to move forward at all (there isn't even any comment by the age maintainer, not even something like "this willl never happen" or "I still have to think about this"), so I think we have to proceed differently.

I just merged #1692, which actually vendors some of the code from https://github.com/FiloSottile/age/pull/591/files (basically ReadSecret with WithTerminal inlined). I think we should proceed similarly here, by modifying the tui.go added there to basically contain https://github.com/FiloSottile/age/blob/266c0940916da6bfa310fdd23156fbd70f9d90a0/tui/tui.go (resp. its original form).

I'd like to get #1400 resolved before merging this one, which can also use the readPassphrase function added by #1692. That way all three PRs (#1692, #1400, and this one) can share the same code vendored from age.

WDYT?

felixfontein avatar Feb 20 '25 20:02 felixfontein

I dream of a day I could use https://github.com/Foxboron/ssh-tpm-agent with sops…

visualphoenix avatar Feb 20 '25 23:02 visualphoenix

@felixfontein An alternative is for sops to use the filippo/plugin branch as it contains the required changes for this to work.

I've moved this over in age-plugin-tpm and considering the low velocity of the project I suspect it should be fine.

EDIT: Reading more of the code. I don't think it solves anything :/

Foxboron avatar Feb 21 '25 08:02 Foxboron

Since FiloSottile/age#591 doesn't seem to move forward at all (there isn't even any comment by the age maintainer, not even something like "this willl never happen" or "I still have to think about this"), so I think we have to proceed differently.

Agreed. I think the approach you laid out seems sensible.

brianmcgee avatar Feb 21 '25 10:02 brianmcgee

I've merged the other PR (as well as some dependabot PRs).

felixfontein avatar Feb 24 '25 18:02 felixfontein

Ok cool, I'll have a look at rebasing this one today.

brianmcgee avatar Feb 25 '25 07:02 brianmcgee

@felixfontein I manually tested this with the age-plugin-fido2-hmac plugin.

brianmcgee avatar Feb 25 '25 12:02 brianmcgee

I'd like to test this PR too! Is this feature added transparently to the user? Or are there flags I need to pass to make it work?

Ramblurr avatar Feb 25 '25 14:02 Ramblurr

I'd like to test this PR too! Is this feature added transparently to the user? Or are there flags I need to pass to make it work?

It should be relatively transparent, you just need to pass an appropriate age key per the plugin you're using.

Here's an example of how I'm using it with age-plugin-fido2-hmac:

❯ SOPS_AGE_KEY=$(age-plugin-fido2-hmac -m) sops test.yml

My .sops.yaml file contains a key entry of the form age1fido2-hmac1xxxxxxxxxxx.

brianmcgee avatar Feb 25 '25 14:02 brianmcgee

I'll take a closer look at this during the next days. The issues reported by CodeQL can likely be ignored (I think they're the same as in #1692, I'll have to take a cloesr look).

felixfontein avatar Feb 25 '25 21:02 felixfontein

@Kranzes @Mic92 @brianmcgee and everyone else involved - thank you very much for your contributions!

felixfontein avatar Feb 27 '25 20:02 felixfontein