gopasspw / gopass

The slightly more awesome standard unix password manager for teams
https://www.gopass.pw/
MIT License
5.83k stars 485 forks source link

gopass recipients rm causes inconsistent state when git commit fails #2180

Open s3lph opened 2 years ago

s3lph commented 2 years ago

Summary

I'm currently rolling over to a new PGP key, and one of the steps there is to add the new key as a gopass recipient, and then remove the old key. However, I've also configured git to sign my commits with the old keys, and had forgotten to update my ~/.gitconfig with the new key after deleting the new secret key. This causes git commits to fail, one of them in gopass recipients rm:

$ gopass recipients remove --store root OLDKEYFPR

Error: failed to remove recipient "OLDKEYFPR": failed to save recipients: failed to commit changes to git: exit status 128: error: gpg failed to sign the data
fatal: failed to write commit object

This leaves the repo in an inconsistent state, however gopass believes it has removed the recipient. After fixing my ~/.gitconfig, i retried:

$ gopass recipients remove --store root OLDKEYFPR

Error: failed to remove recipient "OLDKEYFPR": recipient not in store

This is what the root store repo looked like:

root $ git status
On branch main
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
    modified:   .gpg-id

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
    modified:   a/single/modified/secret

.gpg-id now contained ONLY the new key id, hoever, every secret, including the modified one was still encrypted with BOTH keys:

root $ gpg --list-packets <a/single/modified/secret
gpg: encrypted with 4096-bit RSA key, ID OLDKEYFPR, created 2019-04-01
gpg: encrypted with 4096-bit RSA key, ID NEWKEYFPR, created 2022-04-01
# off=0 ctb=85 tag=1 hlen=3 plen=524
:pubkey enc packet: version 3, algo 1, keyid OLDKEYFPR
    data: [4095 bits]
# off=527 ctb=85 tag=1 hlen=3 plen=524
:pubkey enc packet: version 3, algo 1, keyid NEWKEYFPR
    data: [4096 bits]
# off=1054 ctb=d2 tag=18 hlen=2 plen=61 new-ctb
:encrypted data packet:
    length: 61
    mdc_method: 2
# off=1075 ctb=cb tag=11 hlen=2 plen=18 new-ctb
:literal data packet:
    mode b (62), created 1648820902, name="",
    raw data: 12 bytes

After resetting the uncommitted changes, gopass was happy again:

root $ git reset --hard HEAD
root $ gopass recipients remove --store root OLDKEYFPR
Starting reencrypt
[WARNING snipped]
Removed 1 recipients
You need to run 'gopass sync' to push these changes

Steps To Reproduce

  1. Configure git to sign commits using a specific keyid:
    1. git config --global commit.gpgsign true
    2. git config --global user.signingkey OLDKEYFPR
  2. Create a new PGP key
  3. Add new recipient: gopass recipients add --store root NEWKEYFPR
  4. Remove old secret key (AFTER MAKING A BACKUP): gpg --delete-secret-keys OLDKEYFPR
  5. Remove the old key from gopass: gopass recipients remove --store root OLDKEYFPR

Expected behavior

The gopass repo should not end up in an inconsistent state

Environment

dominikschulz commented 2 years ago

Yeah, we had similar issues before.

I wonder how much gopass fsck would have helped. It would be nice if it could help recover such situations.

Would be nice if we could gracefully abort after a failed commit.