Security enhancement in the software development process: A detailed guide to integrating a YubiKey with Git, commit signing and GitLab

Tim Bastin
8 min readJul 4, 2023

German version of this article: https://l3montree.com/publications/sicherheitssteigung-im-software-entwicklungsprozess-anleitung

In today’s highly interconnected world, prioritizing security measures in software development is becoming increasingly important. With the increasing relevance of distributed version control systems like Git and continuous integration platforms like GitLab comes an increased risk of attacks on the software supply chain. According to the ENISA Report: “Threat Landscape for Supply Chain Attacks”, 62% of attackers in 2021 attempted to exploit companies’ faith in their own software supply chain and used it to carry out attacks such as the introduction of malware.

In this context, the implementation of a robust security infrastructure becomes an indispensable requirement. A key element in strengthening this infrastructure is commit signing. Commit signing is a security mechanism in Git that verifies the author of a commit and ensures the integrity of the commit. When commit signing is enabled, commits are signed with the author’s private key. Anyone who has the corresponding public key can check the signature and thus ensure that the commit really comes from the specified author and has not been manipulated. By verifying the author of a commit and ensuring the integrity of the commit, commit signing provides effective protection against unauthorized or manipulated code changes.

To further strengthen protection, the use of a YubiKey can be considered. The YubiKey is a physical authentication device that provides strong two-factor and multifactor authentication. This hardware device can not only securely store cryptographic keys, but also performs cryptographic operations on the device itself. This means that a private key never leaves the device and therefore cannot be copied or stolen.

Using a YubiKey for commit signing significantly increases the security of this process and improves the resilience of the software supply chain against attacks. In addition, the YubiKey can also be used to establish a connection through Git via SSH. When a commit is sent to a remote repository, Git requests SSH authentication. Instead of reading the private key from a file on the computer, SSH communicates with the YubiKey to perform the necessary cryptographic operations. Since the YubiKey must be physically attached to the computer to perform these operations, this provides a strong form of two-factor authentication: something to be known (password) and something to be owned (YubiKey).

With the increasing threats to the software supply chain, it is of utmost importance that all available tools and technologies are used to secure the software development process. The combination of Git, GitLab, Commit Signing and YubiKey provides an effective solution to meet these challenges. The next section describes the concrete configuration of all components.

Securing the SSH connection

To use the YubiKey with Git for SSH connectivity on macOS, additional steps are required, as the default OpenSSH version on macOS does not support the creation of secret keys. The relevant steps are:

(macOS only) Installing OpenSSH with Homebrew.

MacOS users must first install an updated version of OpenSSH that supports secret key creation (sk). This can be done with the package manager Homebrew. If Homebrew is not yet installed on your system, you can download it from the official website. Then install OpenSSH with the following terminal command: brew install openssh.

(macOS only) Make sure you are using the correct OpenSSH version.

Next, you need to make sure that your system is using the version of OpenSSH that you just installed and not the default version of macOS. You can check this by running the following command in the terminal: which ssh-keygen. The output should be: /opt/homebrew/bin/ssh-keygen.

If it does not, you will need to change your shell configuration file (.zshrc, .bashrc, etc.). Add the following line to add the path of the OpenSSH version you just installed to the PATH environment variable: export PATH=$(brew --prefix openssh/bin:$PATH).

You must reload the configuration file or open a new terminal window for this change to take effect. For example, by: source .zshrc.

Generate the SSH Key

After you have ensured that you are using the correct OpenSSH version, you can generate a new SSH key. To accomplish this, use the command ssh-keygen with a special type that can be placed on the YubiKey.

You can either generate a key of the type ed25519-sk or ecdsa-sk. Both key types provide strong security, but ed25519-sk is newer and often considered more secure.

ssh-keygen -t ed25519-sk -O resident -O application=ssh -O verify-required
When executing these commands, you will be prompted to plug in and touch your YubiKey device to confirm key generation. You may also be prompted to enter a password for the key to add an extra layer of security.

With these steps, you have generated a new SSH key on your YubiKey that is now ready to be used for secure SSH connections, including from Git.

Storing the public key in GitLab

After you have generated your new SSH key, you need to deposit your public key in GitLab. This will allow GitLab to authenticate you when you use SSH to connect to your repository.

Follow these steps to deposit your public key in GitLab:

  1. First, open the terminal and type the following command to display the contents of your public key (replace “id_ed25519_sk.pub” or “id_ecdsa_sk.pub” with the actual path to your public key file):
cat ~/.ssh/id_ed25519_sk.pub

2. Copy the entire output text that represents your public key

3. Now log into your GitLab account and navigate to your account settings (click on your profile picture in the top-right corner and select “Settings”).

4. On the left-hand side, click on “SSH Keys”.

5. Now add your public key in the “Key” field. You can optionally enter a title for the key to make it easier to identify. Click on “Add Key” to add the key to your account.

Now, your YubiKey is configured for use with SSH in GitLab. When you try to access GitLab via SSH, your system will prompt you to insert and touch your YubiKey to confirm the action.

Testing the connection

After you have deposited your SSH public key in GitLab, you should test the connection to make sure everything is set up properly.

You can do this by trying to connect to GitLab via SSH. To do this, type the following command into your terminal:

ssh -T git@gitlab.com

When prompted, paste your YubiKey and touch it to confirm.

If the setup was successful, you will receive a message that reads something like this:

Welcome to GitLab, @username!

Where @username stands for your GitLab username. If you see this message, it means that your YubiKey has been successfully set up for SSH connection with GitLab.

If you get an error message instead, please check the previous steps and make sure that you have entered your public key correctly in GitLab and that your YubiKey is working properly.

Securing the Git commits using YubiKey

Once your YubiKey is configured for use with SSH in GitLab, you can also use it to secure your Git commits. This is achieved by using your YubiKey to sign commits. Here are the steps to achieve this:

Installing the requirements

First, you need to install some tools that are required to use GPG and YubiKey. The exact steps depend on your operating system:

MacOS

brew install gnupg2
brew install pinentry-mac
brew install ykman
brew install yubikey-personalization

Linux

sudo add-apt-repository ppa:yubico/stable
sudo apt-get update
sudo apt-get install pcscd scdaemon pcsc-tools gnupg2 gnupg-agent
sudo apt-get install yubikey-manager yubikey-personalization-gui yubikey-personalization

Create a GPG key on your YubiKey

To use your YubiKey to sign Git commits, you need a GPG key on your device. If you don’t have one yet, you can create one with the following command:

gpg --card-edit

# > admin
# > generate

Enter admin to switch to admin mode. Then enter generate to start the key generation process. You will be prompted to enter the details for your key, including name, email address and password.

Determining the Key ID

After you have created your GPG key, you need to find out the ID of the key. You can do this with the following command:

gpg --card-status


Reader ...........: Yubico Yubikey XXX
Application ID ...: XXXXX
Version ..........: X.X
Manufacturer .....: Yubico
Serial number ....: XXXXX
Name of cardholder: [not set]
Language prefs ...: [not set]
Sex ..............: unspecified
URL of public key : [not set]
Login data .......: [not set]
Signature PIN ....: forced
Key attributes ...: rsa4096 rsa4096 rsa4096
Max. PIN lengths .: 127 127 127
PIN retry counter : 3 3 3
Signature counter : 0
Signature key ....: XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX
created ....: 2023-01-01 00:00:00
Encryption key....: XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX
created ....: 2023-01-01 00:00:00
Authentication key: XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX
created ....: 2023-01-01 00:00:00
General key info..: pub rsa4096/XXXXXXXXXXXXXXXX 2018-01-01 NAME <EMAIL>
sec> rsa4096/XXXXXXXXXXXXXXXX created: 2023-01-01 expires: never <=== HIER
card-no: 0000 00000000
ssb> rsa4096/XXXXXXXXXXXXXXXX created: 2023-01-01 expires: never
card-no: 0000 00000000
ssb> rsa4096/XXXXXXXXXXXXXXXX created: 2023-01-01 expires: never
card-no: 0000 00000000bash

Exporting the Public Key

To export the public part of your GPG key, you can use the command gpg — armor — exportfollowed by the ID of your key. This command outputs the public key in “ASCII armor” format, a text representation that can easily be inserted into emails or text files.

Here is an example of how to export the public part of a GPG key. In this command, replace your-key-id with the actual ID of your key:

gpg --armor --export your-key-id > key.pub

Adding the GPG key in GitLab

Then log in to your GitLab account, go to your account settings and click on “GPG Keys” on the left side. Add your public GPG key in the “Key” field and click on “Add key”. To do this, simply copy the content of the key.pub file you created.

Git configuration

Git is configured through a series of configuration files that can contain various settings. The main configuration file is .gitconfig, which is usually located in the user’s home directory (~/.gitconfig).

You can open and edit the configuration file with a text editor of your choice. Git reads this file and uses the settings defined in it every time it runs.

Modify your .gitconfig to include the following settings:

[user]
name = Your Name
email = your.email@example.com
signingkey = your-key-id
[commit]
gpgsign = true

Testing the signature process

After you have configured your GPG key in Git and GitLab, you should test the signing process to make sure everything is working properly. You can do this by creating a new commit and checking that it has been signed properly.

First, navigate to a local Git repository on your computer, perform a commit and check the signature using:

git log --show-signature

If everything is set up properly, the output of the command should include the following:

gpg: Signature made ...
gpg: using RSA key <your-key-id>bash

This commit should now also be marked as verified in GitLab.

Troubleshooting

The following GPG configuration can help with problems that indicate an “invalid format”:

export GPG_TTY=$(tty)

Securing the software development process is an ongoing and necessary task in our digital world. By implementing commit signing and using YubiKey in combination with Git and GitLab, we can protect the integrity of our code base and ensure the trustworthiness of our software supply chain. With the detailed guidance provided here, we hope that you will find a clear path to integrating these valuable security tools into your own software development process. The journey to strengthen software security is long and requires continuous effort, but with the right tools and knowledge, together we can create a more secure and trustworthy digital future.

--

--

Tim Bastin

I am a Full-Stack software engineer with a passion for high software quality. Chief Technical Officer & Co-Founder of l3montree