~miguelbernadi

Git configuration and signing commits with SSH keys

I was reading the other day a nice blogpost by Matthew Garret (mjg59) about being able to use SSH keys to sign Git commits. While trying to make that set up in my machine I realized there are some tricks I use to configure the Git repository identities I had not spoken about before, so I’m going to merge both topics.

If you are only interested in the SSH signing, you can check mjg59’s post above and Caleb Hearth’s. If you want my usual ramblings, keep reading.

I’m a minor, occasional, Free Software contributor and I also write code for a living. My employers graciously offer me an email address associated to the projects I work on for them that I can use with GitHub and other related services my employer requires. When I change employers, my identity thus changes and I lose access to what I worked on before. That makes sense for company-owned code, but not that much for Free Software.

My personal contributions (extremely disregardable) predate my employments and continue happening occasionally. Most of those projects are personal itches I’ve had that I share openly, even if I’m the only actual user of them.

This situation requires that I have several public identities depending on the project I work on. In the specific case of Github I’ve used the same account for personal and work (my time at Devex) with different identities depending on the owner of the repository.

In general, you could have your work identity in your work computer and your personal identity in your personal computer. This breaks down fast if you start building Free Software projects that can be used at work, as you may want to continue working on them in the open later on. Now you need both identities in the same computer. An alternative would be to set the configuration per repository, but that can be messy, tedious and error-prone. I have created many commits with the wrong identity when changing laptops, employers or projects.

Git Conditional Configuration #

The best solution is to use Git’s conditional configuration. Unfortunately, the Git Book chapter on configuration does not mention this but there are several blogposts on the topic. With it, you define some Git configuration values to apply to all repos and define/overwrite others in certain paths.

I set up my repositories directory as this (obviously redacted):

$ tree -d -L 2 ~/git
~/git
├── Foo
│   ├── AWSCredentialExporter
│   ├── bisecting-helpers
│   └── metrics-api
├── arch-zfs-image
├── blog
│── crisol
│   └── crisolAdmin
├── emacs
├── guix-config
├── synthetic
└── zfs-backup

Each entry is a repository. First level repositories are personal projects, some published and some not. Second level repositories belong to an organization with specific settings.

In my ~/.config/git/config I set the following:

#~/.config/git/config
[user]
      email = miguel.bernabeu@lobber.eu
      name = Miguel Bernabeu
[includeIf "gitdir:~/git/Foo/"]
      path = config_foo
[includeIf "gitdir:~/git/crisol/"]
      path = config_crisol

This instructs to load the configuration file called config_foo and merge it with this global configuration when operating under the path ~/git/Foo/. That configuration file is next to the main configuration file and looks something like:

#~/.config/git/config_foo
[user]
      email = miguel.bernabeu@foo.tld

Which overrides my email address for the repositories in that path.

Adding SSH signatures to commits #

Signing Git commits is a good idea to avoid malicious commit rewriting and the usually supported way of signing anything is with GPG. There are many discussions about the details of the problem and a huge effort around it. If you are interested, look up Source Bill Of Materials and Web of Trust and keep reading from there.

Any signing process has the problem of distributing the keys for verification. GPG has its own distribution channel, but for those not already using GPG, the entry barrier is quite high. On the other hand, SSH keys can be used for the same purpose and we already need SSH keys to upload our commits to most forges, so most Git users already use them. Many of those forges publish user’s public keys as well, so distribution of the keys could be pretty easy.

mjg59’s post gets us to the signing point pretty fast, but only details manual verification of the signatures due to some bugs/limitations he encountered. Thankfully I found a related post by Caleb Hearth that included the verification setup in Git itself.

To summarize both posts, the final configuration would look like this:

#~/.config/git/config
[user]
      email = miguel.bernabeu@lobber.eu
      name = Miguel Bernabeu
[gpg]
      format = ssh
[includeIf "gitdir:~/git/Foo/"]
      path = config_foo
[includeIf "gitdir:~/git/crisol/"]
      path = config_crisol

The signing options are contained under group gpg for historical reasons, so we have to specify there that we want to use SSH as signing method.

#~/.config/git/config_foo
[user]
      email = miguel.bernabeu@foo.tld
      signingkey = ~/.ssh/foo.pub
[commit]
      gpgsign = true
[gpg "ssh"]
      allowedSignersFile = ~/git/Foo/allowed_signers

For the Foo organisation, we add the requirement to sign all commits with a specific public key (~/.ssh/foo.pub) and will keept the list of verification signatures in ~/git/Foo/allowed_signers. The format of this file can be found in the man page ssh-keygen(1) ALLOWED SIGNERS.

If you are missing any details you can check the posts linked above.

Conclusion #

Proper key management is never easy and not being an expert in security means I have no idea if this is actually a bad idea. On top of that, there is a missing block to gather the list of keys to trust. Git forges make it easy to get the keys of a specific user, but how easy it is for you to discover which forge user signed this commit?

This setup benefits small groups of developers or organizations as they can share the keys beforehand. Another option would be to add the list of trusted keys to the repository itself, but this sounds so easy and nice that it’s likely to actually be a very bad idea security-wise.

Anyway, it is nice to have signed commits and using SSH for it is likely to be much easier and widespread than with GPG due to its complexity and usability issues. The missing pieces will eventually appear from people that do understand how to do it properly. And in the worst case scenario, signed commits are just as reliable as unsigned ones.

COMMENTS

Have a comment on this article? Start a discussion in my public inbox by sending an email to ~miguelbernadi/public-inbox@lists.sr.ht [mailing list etiquette] , or see existing discussions.