I recently had an interesting discussion where we tried to explore some of the ways you can sign commits in Git and GitHub. If you’re not familiar with the functionality, Git provides mechanisms for signing commits and tags to ensure authorship. By default, Git trusts that the user name and email that you provide to
git config is legitimate. For many organizations, that may be perfectly acceptable. If only limited people have access to the repository, this is often enough.
In more secure environments, we may want to have some evidence of authorship. We handle this using public key/private key pairs. The public key is shared with the world, allowing validation of anything signed with the private key. The private key is closely managed by its owner, providing a way for them to assert their authorship. As a general practice, this key is carefully protected. Typically, it expires periodically to ensure that lost keys cannot be compromised.
GitHub supports three types of signatures (see the docs to learn how to configure each of these). To help make it easier to understand, let’s explore the approaches and their tradeoffs.
Each approach has its features and limitations, so you’ll need to consider the options. If you want maximum compatibility with Git implementations and approaches that are well-documented with Git, then consider GPG. If you want something developers are already familiar with using (and your hosting provider supports it), then SSH may be an option. For those that need something centralized (and are willing to use a recognized CA), S/MIME signatures may be something to consider.
GNU Private Guard is the open source implementation of OpenPGP ( RFC4880). It allows individuals to manage use cryptographic keys and relies on creating a “web of trust”. The approach is decentralized, so the user is responsible for managing the keys and choosing which keys to trust. For new users, this can have a short learning curve. That said, most developers can get comfortable with it quickly. It’s also a well-known and documented approach, natively supported by Git. An additional benefit of GPG keys is that they are the most supported format across Git hosting implementations.
One of the important things to remember is to create keys with an expiration date. The expiration date can be extended at any time. Having an expiration ensures that if the private key ever becomes inaccessible, it will eventually be unusable.
For centralized IT teams, GPG does have a drawback. Because GPG is decentralized, there is no easy way to manage ownership or control the distribution of keys. Instead, companies are normally forced to trust the developers and provide them best practices. While there are some tools and approaches which enable centralized key management, centralized management is usually the domain of X.509 infrastructures.
Working with GPG, you may need to get the public keys for other members of the same repository. Thankfully, retrieving a user’s key is not too challenging. GitHub provides the published GPG keys for users at this URL:
https://github.com/USER.gpg. GitLab provides a similar endpoint:
SSH keys are the go-to for developers that need to access remote Linux servers. Consequently, creating and generating key pairs is frequently something they are comfortable with handling. By registering the public key with GitHub, they can quickly and easily sign and verify commits. For those new to the process, other team members can often coach them. The process is often simpler than working with GPG (especially if you’re using tools like 1Password, which can handle the work for you). Similar to GPG, GitHub will need to have the public key registered (as an SSH key) in order to verify it.
Unfortunately, SSH keys do not have an expiration date. In addition, developers tend to re-use existing keys rather than creating keys for each separate system. This practice can make it more likely for an exploit to occur, especially if a private key slips into source control or get exposed. Both of these can be handled by ensuring developers understand how to best protect and use these keys.
To solve some of these issues, SSH keys offer an additional option: certificates. This extends the basic SSH key handling to add expiration dates and other metadata. The key management can also be centralized using a trusted certificate authority (CA). If the CA is trusted, the signing key is trusted inherently. Because this isn’t a full-feature public key infrastructure solution, you cannot use an intermediate CA to sign the keys. This system also lacks support for CSRs (certificate signing requests) and CRLs (certificate revocation lists).
When working with Git tags and commits, SSH certificates have a bigger limitation. The format (
firstname.lastname@example.org) is not supported by Git hosting providers for signing purposes. It can only be used as a credential for connectivity. While you can use the key to sign local commits, GitHub and other providers will not be able to verify it. For example, GitHub will display the commit as unverified with the message “GitHub supports GPG and S/MIME signatures. We don’t know what type of signature this is.” In other words, they only support authentication.
If you’re interested in using SSH certificates for authentication, that is more broadly supported. With GitHub, you must
register the CA. The user certificates that are created also must include the extension
email@example.com=USERNAME to identify the user. Full details can be found in the docs –
“Issuing certificates”. The GitHub blog also has
a great overview of the process.
S/MIME (Secure/Multipurpose Internet Mail Extensions) is a standard for public key encryption of MIME data. This approach relies on signing the commits using X.509 certificates issued by a well-known public CA. This allows large organizations to ensure identities using centralized authorities. The certificates are issued by a trusted CA, ensuring the identity of the person signing the commit. While this does require some configuration, it provides an easy way to guarantee the signing identity. The certificates are owned and managed through the CA, allowing for revocation if the key is compromised. This relies on using a well-known CA, so internal self-signed CAs are not supported. GitHub uses the
ca-certificates package — the same certificates used for the Mozilla browsers — to confirm trust. Because of this, organizations would need to use a vendor-provided approach to issue and manage the certificates across the organization. The only other thing to know about this approach is that it requires installing some tooling to manage the singing process. Two popular ones are
GitSMimeSign. The initial setup is well-documented, but not something most developers will typically have experience implementing.