maxgoedjen / secretive

Store SSH keys in the Secure Enclave
MIT License
7.24k stars 159 forks source link

Support git signatures using SSH Keys #262

Closed maxgoedjen closed 2 years ago

maxgoedjen commented 3 years ago

This mostly works out of the box and is primarily documentation based + made easier by #261.

Config:

maxgoedjen commented 3 years ago

GitHub, notably, does not show theses as verified yet. The signature bit does verify locally though. See https://github.com/github/feedback/discussions/7744

142137872-c5cab9b9-108b-48bb-a674-6b866828cd4c 142137860-dd8f8262-0d57-4c4a-83f8-194adf36fdce
grncdr commented 2 years ago

I am trying to set gpg.ssh.defaultKeyCommand to ssh-add -L as suggested by the Git docs, but for some reason the output is not recognized as an SSH key:

warning: gpg.ssh.defaultKeyCommand succeeded but returned no keys:  ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBMW2LvEj+1peXoyOJUjH5azsd75mjeuSVZkUmT9lJG9hWCH6MEcc5daoya6Q2jHP2lBE+gqjOeTV4q/kYEq24VA= ecdsa-sha2-nistp256

however, if I copy-paste the same output into a file and set user.signingkey it seems to work fine. Any idea why this is?

maxgoedjen commented 2 years ago

@grncdr tbh not really, it's extremely finicky at the moment. Basically the config I posted is the only one I was able to get working nicely.

grncdr commented 2 years ago

I looked into this and the currently released version of git checks for a prefix of "ssh-" to determine if a key is present. I wrote a little patch for this to also accept "ecdsa-" and verified that it works.

However, a patch was already merged into the next branch that solves this in a slightly different way: https://github.com/git/git/commit/2d0c8cb8e6dac245b787dee23663d62e9b3d09fb

The next version of git will allow any key type that is prefixed with "key::". With a config like defaultKeyCommand = sh -c 'echo key::$(ssh-add -L)' signing works. :)

Not sure if this is just something that could be documented in the README, or if adding a helper program to output the "key::..." format would make sense. In any case, it should work well after the next release of git.

maxgoedjen commented 2 years ago

@grncdr very neat, thanks for digging into that!

I'm still trying to figure out what I'm going to do in terms of docs for this thing, but (depending on adoption of that patched git) I'll bear this all in mind.

CodeWithShreyans commented 2 years ago

But SSH keys can and usually do change with the machine (especially in the case of Secretive) but GPG keys usually stay the same unless lost so wouldn't that create a little messed up commit history if the signatures for the same person were to change relatively often. For example, Linus Torvalds has been using the same GPG key since 2011. Just wanted some thoughts on this.

Thanks!

cmoog commented 2 years ago

But SSH keys can and usually do change with the machine

Once Github adds support for displaying SSH commit signatures I plan on using my Yubikey with https://github.com/FiloSottile/yubikey-agent to sign commits. That should provide the benefits of hardware protection against key extraction without the downside of having to rotate keys with machines.

That said, Yubikeys have a monetary and convenience cost that the built-in secure enclave does not, so I still think Secretive can add value here by supporting this use case in a documented way.

CodeWithShreyans commented 2 years ago

Yes, that is a good use case but a yubikey is something you have to buy (and not very cheap at that) so 98-99% of git users won't have one which makes it a very niche userbase. Also, if you lose your yubikey or get a new one, you can't keep the ssh key safely backed up as it is not possible to extract it from the yubikey, just like the secure enclave.

dhess commented 2 years ago

I don’t think this will work any differently with SSH keys than it does with GPG keys. I have changed my GPG key once since I started signing my git commits, and so long as both public keys are available to GitHub in my GitHub profile, my git commits will show as “Verified” no matter which key I used for the commit. If I remove the older public key, then commits I made with that key will no longer be displayed as verified, until I upload that public key to GitHub again.

Conceivably it’ll work the same with SSH keys — just make sure all the public keys from SSH key pairs you’ve used to sign past git commits are known to GitHub in your profile, and it shouldn’t matter that you’ve changed keys.

(One difference is that GPG public keys are only used by GitHub to prove that you’ve signed a commit, but any and all SSH public keys listed in your profile can be used to authenticate with GitHub’s SSH-based API. Presumably GitHub will need to change that so that you can keep old SSH public keys around for signing verification but exclude them from the list of keys that can currently authenticate you, once you’ve stopped actively using that key.)

krubenok commented 2 years ago

IMO a better fix on the GitHub side would be something similar to what's proposed here: https://github.com/isaacs/github/issues/1099

Allow a key (ssh or gpg) to be "retired". Commits signed with with that key before the retirement date should be shown as valid with a sub-status that the key has since been retired, any commits made after the retirement would be invalid. This means the relatively ephemeral nature of secretive or other hardware backed keys is basically moot.

CodeWithShreyans commented 2 years ago

IMO a better fix on the GitHub side would be something similar to what's proposed here: isaacs/github#1099

Allow a key (ssh or gpg) to be "retired". Commits signed with with that key before the retirement date should be shown as valid with a sub-status that the key has since been retired, any commits made after the retirement would be invalid. This means the relatively ephemeral nature of secretive or other hardware backed keys is basically moot.

This post would be better in https://github.com/github/feedback/discussions/7744

CodeWithShreyans commented 2 years ago

I don’t think this will work any differently with SSH keys than it does with GPG keys. I have changed my GPG key once since I started signing my git commits, and so long as both public keys are available to GitHub in my GitHub profile, my git commits will show as “Verified” no matter which key I used for the commit. If I remove the older public key, then commits I made with that key will no longer be displayed as verified, until I upload that public key to GitHub again.

Conceivably it’ll work the same with SSH keys — just make sure all the public keys from SSH key pairs you’ve used to sign past git commits are known to GitHub in your profile, and it shouldn’t matter that you’ve changed keys.

I was discussing more about the general idea of signing git commits with an ssh key and not specifically about github's implementation.

If I remove the older public key, then commits I made with that key will no longer be displayed as verified, until I upload that public key to GitHub again.

This is done because the user's GPG private key may have been leaked. This actually happened to the Linux Kernel Maintainers in 2011 and they had to resign all their previous commits with a new key https://www.kernel.org/category/signatures.html#:~:text=Kernel%20releases%20prior%20to%20September%2C%202011.

(One difference is that GPG public keys are only used by GitHub to prove that you’ve signed a commit, but any and all SSH public keys listed in your profile can be used to authenticate with GitHub’s SSH-based API. Presumably GitHub will need to change that so that you can keep old SSH public keys around for signing verification but exclude them from the list of keys that can currently authenticate you, once you’ve stopped actively using that key.)

@maxgoedjen said something similar in https://github.com/github/feedback/discussions/7744#discussioncomment-1655786

dhess commented 2 years ago

I was discussing more about the general idea of signing git commits with an ssh key and not specifically about github's implementation.

Well in that case, personally I welcome the idea of relatively frequently rotating my signing key. In fact, I see this as an advantage of using SSH keys tied to my current hardware vs keeping the same GPG key around for years. I would follow this practice this already if it weren't such a PITA to do with GPG and a YubiKey.

CodeWithShreyans commented 2 years ago

I do get your point that it would become a good security practice to rotate keys every once in a while but then there is also the concept of webs of trust and if your key is signed by other people. Mine, yours (probably), and many other people's aren't but that still kind of dismisses the idea as a whole as it would make creating a new key cumbersome at best and impossible at worst.

krubenok commented 2 years ago

This is done because the user's GPG private key may have been leaked. This actually happened to the Linux Kernel Maintainers in 2011 and they had to resign all their previous commits with a new key

I feel like there are some reasonable ways around this. For example, when revoking a key present the following options:

Anyways, off topic from secretive. But for secretive to be useful for commit signing Github would not only need to support the signing, but do work around key management.

dhess commented 2 years ago

Anyways, off topic from secretive. But for secretive to be useful for commit signing Github would not only need to support the signing, but do work around key management.

Does GitHub do any of that for GPG signatures?

krubenok commented 2 years ago

No. If you delete a GPG key, any commits that were signed with that key are no longer verified. Since GPG keys are typically long lived it's less of an issue but since ssh keys (especially secretive ssh keys) are short lived it's a much bigger problem.

maxgoedjen commented 2 years ago

This thread got busy when I wasn't looking!

Once Github adds support for displaying SSH commit signatures I plan on using my Yubikey with https://github.com/FiloSottile/yubikey-agent to sign commits

@cmoog fwiw secretive also supports yubikeys (even if your Mac has a SEP!) so you can have the best of both worlds there.

Overall I still really wish that the GPG stuff I posted in those other threads was implemented (some way to retire keys gracefully) but part of that was me weighing the difficulty of implementing GPG support (pretty high) against the benefits and drawbacks of how it would work. That balance has shifted pretty significantly with SSH key signing.

Personally I'm planning on using SEP-backed keys and either renaming them as deprecated with a date or something if GitHub will let me, and if not just leaving them up. Wouldn't blame anyone who wanted to have their keys live on a YubiKey though for portability, especially since that flow should work great alongside secretive still.

solzard commented 2 years ago
  • Needs SSH_AUTH_SOCK in the shell rc (unfortunately doesn't work with ./ssh/config)

@maxgoedjen, would you please explain what this means in detail? Do I need to make ~/.ssh/rc and write something there?

artis3n commented 2 years ago

Going to +1 the above message. Given https://github.blog/changelog/2022-08-23-ssh-commit-verification-now-supported/ I see ed25519 keys are fine, but seems like git doesn't know how to use ecdsa-sha2-nistp256 keys? Do I need to do something around https://github.com/maxgoedjen/secretive/issues/262#issuecomment-997772711 ?

cmoog commented 2 years ago

I've been using a git signing workflow with Secretive for a few months now. The following git config works as expected https://github.com/cmoog/dotfiles/blob/b876d5483e0fbb065b16c20c34299edf9fb87af6/server/.gitconfig#L6. Of course you'll also need to set SSH_AUTH_SOCK to point towards the Secretive socket as usual.

dhess commented 2 years ago

I've been using a git signing workflow with Secretive for a few months now. The following git config works as expected https://github.com/cmoog/dotfiles/blob/b876d5483e0fbb065b16c20c34299edf9fb87af6/server/.gitconfig#L6. Of course you'll also need to set SSH_AUTH_SOCK to point towards the Secretive socket as usual.

Thanks. Does GitHub now show the commits you've signed with that P-256 key as verified in the GitHub UI?

cmoog commented 2 years ago

Thanks. Does GitHub now show the commits you've signed with that P-256 key as verified in the GitHub UI?

Yes:

Screen Shot 2022-08-24 at 11 54 25 AM
Manouchehri commented 2 years ago

I can confirm it works for me too, just like @cmoog. =)

https://github.com/Manouchehri/ssh-keysign-test/commit/323e5128174f607fe278f2e1b3ebcef21f6bf2f3

Here's my ~/.gitconfig:

[user]
    email = my.email@localhost
    name = YOUR NAME
    signingkey = /Users/XXXXX/Library/Containers/com.maxgoedjen.Secretive.SecretAgent/Data/PublicKeys/XXXXXX.pub
[color]
    ui = auto

[url "git@github.com:MYGITHUBUSERNAME"]
    insteadOf = https://github.com/MYGITHUBUSERNAME
[push]
    default = current
    gpgSign = if-asked
[commit]
    gpgSign = true
[gpg]
    format = ssh
[tag]
    gpgSign = true
    forceSignAnnotated = true
artis3n commented 2 years ago

Ahh hm looks like git tries to find the private key under PublicKeys, so maybe my ssh config isn't configured correctly? Regular ssh Secretive stuff works fine.

image

image

Using the format signingkey = key::ecdsa-sha2-nistp256 ... , I get a different error. Should I make a separate issue about this likely Secretive configuration issue? As it's related to git signatures with SSH keys I figure it may be helpful to others if it is in this thread.

image

dhess commented 2 years ago

Hmm, I can sign with my Secretive key, and I can convince git locally (via the gpg.ssh.allowedSignersFile) that my signature is valid, but despite the fact that I've uploaded my Secretive public key to GitHub (I use it exclusively for all of my pushes), GitHub claims that the signture is unverified.

edit: Weird, GitHub shows that the SSH key fingerprint for the commit I pushed is rATajE2l2IsyUJT5GKHs65AtH5lVP1oRr/5kxMWg4T4:

Screen Shot 2022-08-24 at 8 47 10 PM

which is exactly the same fingerprint as git log --show-signatures shows locally. Also, that is precisely the same fingerprint as what shows up as the 2nd of my 2 GitHub SSH keys:

Screen Shot 2022-08-24 at 8 48 11 PM

But the verification still fails. ~Seems like a GitHub bug to me.~ (It's not a bug, it's pilot error. See below.)

Manouchehri commented 2 years ago

@dhess It's because you have to add the ssh key again, but set Key type to Signing Key. This is so that your commits can still show up as valid even if you decide to later delete the key as an allowed method for pulls/pushes.

dhess commented 2 years ago

@dhess It's because you have to add the ssh key again, but set Key type to Signing Key. This is so that your commits can still show up as valid even if you decide to later delete the key as an allowed method for pulls/pushes.

Ahh yes, of course, that makes sense. It's working now, thank you!

sean-public commented 2 years ago

I'm on git version 2.37.2 and have the exact same two errors as @artis3n above with the key:: inline format or the file location. I noticed that, with the latter, the No such file or directory? error message has the complete and accurate path except without the .pub extension (is it looking for a matching private key?).

SSH_AUTH_SOCK is set correctly and everything else about Secretive works well.

artis3n commented 2 years ago

^ FWIW I'm on git 2.37.2 as well.

maxgoedjen commented 2 years ago

@dhess @Manouchehri yeah that distinction between key types caught me off guard as well! I think it's a good distinction to make on their end, but it did catch me off guard during setup.

maxgoedjen commented 2 years ago

@artis3n @sean-public I'm gonna split this this thread off into #404 since the ticket as initially described is working for me.

asmeurer commented 2 years ago

Would love to see something in Secretive to help set this up automatically.

maxgoedjen commented 2 years ago

@asmeurer Yeah I need to add some instructions to setup flow, tracking #405