Protect Your Github Commits

Did you know that anyone who knows the email you used in github could make a push as if they were you?

Securing Git Commits

No, this is NOT a security flaw in github. This is the very design of git. Git is meant to be decentralized with no central repository to speak of, but habits are hard to break and control is something that enterprises want over its assets. And centralizing a decentralized system was born on top of git. Git’s original design was such that we only pull from and push to trusted peers. And trusted peers are easy to get to agree to follow conventions.

TLDR;

The official git documentation provides a comprehensive sign your work article in using GPG.

Why Sign Your Work

Why Sign? Because anyone can make a commit as you if they know one or two of the emails you use to contribute work for either private or public git managed source codes.

A malicious individual can submit code under your name and email, contributing poor quality or malicious code.

Pre-Setup and Assumption

This article assumes you are on MacOS and is familiar with executing commands via Terminal.app

If you are on Linux, WSL, Android, or BusyBox, installation of:

  • Git Credential Manager (GCM)
  • GPG
  • pinentry

is out of scope of this article. It should however be really simple to find the apt, yum, rpm, dpkg, *pkg, dnf, synaptic, pacman, or portage equivalents.

Install Requirements

brew install git git-credential-manager gpg pinentry pinentry-mac 

GPG Key Generation & Github Setup

Signing a commit will require an SSH key or GPG key. We will use GPG key in this process.

gpg --full-generate-key

Choose the defaults or ECC (sign and encrypt), Curve 25519, and 1y.

I strongly suggest setting an expiry date. End of the year, if you will.

Fill in the needed information and make sure to use your github provided private email address. You can find that at https://github.com/settings/emails with the following pattern @users.noreply.github.com

Make sure that the checkbox Keep my email addresses private together with Block command line pushes that expose my email is checked.

This is also a good time to re-configure your Git to no longer use your business email. Repositories can be scanned and is a good source of valid emails for spamming and phishing.

git config user.email {github_provided_username}@users.noreply.github.com 

Now that that is configured, time to export the generated keys so we can tell GitHub which keys proves who we are. First step is to get the key id, specifically the long keyid.

gpg --list-keys --keyid-format long

The long keyid is usually after pub ed25519/, copy the long keyid and execute the command below:

gpg --export --armor {long-keyid}

Copy the text in the terminal or use pbcopy to send exported data to clipboard

gpg --export --armor {long-keyid} | pbcopy

Open https://github.com/settings/keys on a browser and click New GPG key button. Paste the key.

Configure git to use a signingkey.

git config user.signingkey {long-keyid}

Git Signing Application

Find the path of the gpg application using which

which gpg

Copy the return path and configure git to use that as the gpg.program. Assuming the value is /opt/homebrew/bin/gpg, execute the command below:

git config gpg.program /opt/homebrew/bin/gpg

or use backtick to use a command output as a command argument like:

git config gpg.program `which gpg`

Signing New Commits

To sign new commits, simply add -S to your git commit like so

git commit -S -m "This commit will be signed with the configured signing key"

Auto-Sign New Commits

Time to setup git to always sign commits.

git config commit.gpgsign true

With the commit.gpgsign configured to true, there will be no need to add -S to every commits made.

Tell our Shell about GPG

There is no doubt that we use different shells so to cater to two commonly used shells while in terminal, execute the command below.

current_shell=`ps -p $$ | awk 'FNR == 2 {print}' | awk '{print $4}'`; if [[ $current_shell == '-bash' ]]; then echo 'export GPG_TTY=$(tty)' >> ~/.bashrc; elif [[ $current_shell == '/bin/zsh' ]]; then echo 'export GPG_TTY=$(tty)' >> ~/.zshrc; fi

What the above command does is check whether you are using bash or zsh in your current terminal. It then sets up an environment variable GPG_TTY, so it knows that it can invoke gpg.

Multi-Project, Multi-Email, Multi-Keys/Machine

If you work with the above scenarios, you can use the --local option when executing git config for every local project repository you are working on.