You clone a Git repository, then pull from it. How can you tell itscontents are “authentic”—i.e., coming from the “genuine” project youthink you’re pulling from, written by the fine human beings you’ve beenworking with? With commit signatures and “verified” badges ✅flourishing, you’d think this has long been solved—but nope!

Four years after Guix deployed its owntool to allowusers to authenticate updates fetched with guix pull (which uses Gitunder the hood), the situation hasn’t changed all that much: the vastmajority of developers using Git simply do not authenticate the codethey pull. That’s pretty bad. It’s the modern-day equivalent ofsharing unsigned tarballs and packages like we’d blissfully do in thepast century.

The authentication mechanism Guix uses forchannelsis available to any Git user through the guix git authenticatecommand. This post is a guide for Git users who are not necessarilyGuix users but are interested in using this command for their ownrepositories. Before looking into the command-line interface and how weimproved it to make it more convenient, let’s dispel anymisunderstandings or misconceptions.

Why you should careWhen you run git pull, you’re fetching a bunch of commits from aserver. If it’s over HTTPS, you’re authenticating the server itself,which is nice, but that does not tell you who the code actually comesfrom—the server might be compromised and an attacker pushed code to therepository. Not helpful. At all.

But hey, maybe you think you’re good because everyone on your project issigning commits and tags, and because you’re disciplined, you routinelyrun git log --show-signature and check those “Good signature” GPGmessages. Maybe you even have those fancy “✅ verified” badges as foundonGitLaband onGitHub.

Signing commits is part of the solution, but it’s not enough toauthenticate a set of commits that you pull; all it shows is that,well, those commits are signed. Badges aren’t much better: the presenceof a “verified” badge only shows that the commit is signed by theOpenPGP key currently registered for the corresponding GitLab/GitHubaccount. It’s another source of lock-in and makes the hosting platforma trusted third-party. Worse, there’s no notion of authorization (whichkeys are authorized), let alone tracking of the history of authorizationchanges (which keys were authorized at the time a given commit wasmade). Not helpful either.

Being able to ensure that when you run git pull, you’re getting codethat genuinely comes from authorized developers of the project isbasic security hygiene. Obviously it cannot protect against efforts toinfiltrate a project to eventually get commit access and insertmalicious code—the kind of multi-year plot that led to the xzbackdoor—but if you don’t evenprotect against unauthorized commits, then all bets are off.

Authentication is something we naturally expect from apt update,pip, guix pull, and similar tools; why not treat git pull to thesame standard?

Initial setupThe guix git authenticatecommand authenticates Git checkouts, unsurprisingly. It’s currentlypart of Guix because that’s where it was brought to life, but it can beused on any Git repository. This section focuses on how to use it; youcan learn about the motivation, its design, and its implementation inthe 2020 blogpost, in the 2022peer-reviewed academic paper entitled Building a Secure SoftwareSupply Chain withGNU Guix,or in this 20mnpresentation.

To support authentication of your repository with guix git authenticate, you need to follow these steps:

  1. Enable commit signing on your repo: git config commit.gpgSign true. (Git now supports other signing methods but here we needOpenPGP signatures.)
  2. Create a keyring branch containing all the OpenPGP keys of allthe committers, along these lines:

git checkout --orphan keyringgit reset --hardgpg --export alice@example.org > alice.keygpg --export bob@example.org > bob.key…git add *.keygit commit -m "Add committer keys." All the files must end in .key. You must never remove keys fromthat branch: keys of users who left the project are necessary toauthenticate past commits. 3. Back to the main branch, add a .guix-authorizations file, listingthe OpenPGP keys of authorized committers—we’ll get back to itsformat below. 4. Commit! This becomes the introductory commit from whichauthentication can proceed. The introduction of your repositoryis the ID of this commit and the OpenPGP fingerprint of the keyused to sign it.

That’s it. From now on, anyone who clones the repository canauthenticate it. The first time, run:

guix git authenticate COMMIT SIGNER … where COMMIT is the commit ID of the introductory commit, andSIGNER is the OpenPGP fingerprint of the key used to sign that commit(make sure to enclose it in double quotes if there are spaces!). As arepo maintainer, you must advertise this introductory commit ID andfingerprint on a web page or in a README file so others know what topass to guix git authenticate.

The commit and signer are now recorded on the first run in.git/config; next time, you can run it without any arguments:

guix git authenticate The other new feature is that the first time you run it, the commandinstalls pre-push and pre-merge hooks (unless preexisting hooks arefound) such that your repository is automatically authenticated fromthere on every time you run git pull or git push.

guix git authenticate exits with a non-zero code and an error messagewhen it stumbles upon a commit that lacks a signature, that is signed bya key not in the keyring branch, or that is signed by a key not listedin .guix-authorizations.

Maintaining the list of authorized committersThe .guix-authorizations file in the repository is central: it liststhe OpenPGP fingerprints of authorized committers. Any commit that isnot signed by a key listed in the .guix-authorizations file of itsparent commit(s) is considered inauthentic—and an error is reported.The format of.guix-authorizationsis based on S-expressionsand looks like this:

;; Example ‘.guix-authorizations’ file.(authorizations (version 0) ;current file format version (("AD17 A21E F8AE D8F1 CC02 DBD9 F8AE D8F1 765C 61E3" (name "alice")) ("2A39 3FFF 68F4 EF7A 3D29 12AF 68F4 EF7A 22FB B2D5" (name "bob")) ("CABB A931 C0FF EEC6 900D 0CFB 090B 1199 3D9A EBB5" (name "charlie")))) The name bits are hints and do not have any effect; what matters isthe fingerprints that are listed. You can obtain them with GnuPG byrunning commands like:

gpg --fingerprint charlie@example.org At any time you can add or remove keys from .guix-authorizations andcommit the changes; those changes take effect for child commits. Forexample, if we add Billie’s fingerprint to the file in commit A, thenBillie becomes an authorized committer in descendants of commit A(we must make sure to add Billie’s key as a file in the keyringbranch, too, as we saw above); Billie is still unauthorized in branchesthat lack A. If we remove Charlie’s key from the file in commit B,then Charlie is no longer an authorized committer, except in branchesthat start before B. This should feel rather natural.

That’s pretty much all you need to know to get started! Check themanualfor more info.

All the information needed to authenticate the repository is containedin the repository itself—it does not depend on a forge or key server.That’s a good property to allow anyone to authenticate it, to ensuredeterminism and transparency, and to avoid lock-in.

Interested? You can help!guix git authenticate is a great tool that you can start using todayso you and fellow co-workers can be sure you’re getting the right code!It solves an important problem that, to my knowledge, hasn’t really beenaddressed by any other tool.

Maybe you’re interested but don’t feel like installing Guix “just” forthis tool. Maybe you’re not into Scheme and Lisp and would rather use atool written in your favorite language. Or maybe you think—andrightfully so—that such a tool ought to be part of Git proper.

That’s OK, we can talk! We’re open to discussing with folks who’d liketo come up with alternative implementations—check out the articlesmentioned above if you’d like to take that route. And we’re open tocontributing to a standardization effort. Let’s get intouch!

AcknowledgmentsThanks to Florian Pelz and Simon Tournier for their insightful commentson an earlier draft of this post.