Skip to main content

Git Credential Manager on Headless Linux

Debian Derivatives

Install Prerequisite Packages

sudo apt update && sudo apt install -y libicu-dev gpg pass pinentry-tty curl
  • libicu-dev -- International Components for Unicode for string operations (Dotnet needs this)
  • gpg -- encrypts credentials
  • pass -- password store
  • pinentry-tty -- allows prompts for passwords in headless environments


Download and Install the Latest Tarball

Prefer the self-contained tarball, as this does not require installing Dotnet as a system library.

RELEASES_BASE_URL='https://github.com/git-ecosystem/git-credential-manager/releases'
LATEST_RELEASE_URL="${RELEASES_BASE_URL}/latest"
LATEST_VERSION_URL=$(curl -sI "$LATEST_RELEASE_URL" | grep -i location | cut -d ' ' -f 2 | tr -d '[:space:]')
LATEST_VERSION_NUMBER=$(echo "$LATEST_VERSION_URL" | rev | cut -d '/' -f 1 | rev | tr -d 'v')
DOWNLOAD_BASE_URL=$(echo "$LATEST_VERSION_URL" | sed 's/tag/download/g')
LATEST_TARBALL_NAME="gcm-linux-x64-${LATEST_VERSION_NUMBER}.tar.gz"
LATEST_TARBALL_DOWNLOAD_URL="${DOWNLOAD_BASE_URL}/${LATEST_TARBALL_NAME}"
curl -sLO "$LATEST_TARBALL_DOWNLOAD_URL"
ls -l "${PWD}/${LATEST_TARBALL_NAME}"

Download 64-bit .deb binary

[ -d /usr/local/bin/git-credential-manager ] || sudo mkdir /usr/local/bin/git-credential-manager

Create the install directory

sudo /usr/bin/tar -xvzf "$LATEST_TARBALL_NAME" -C /usr/local/bin/git-credential-manager

Extract the installation files

echo "$PATH" | tr ':' '\n' | grep '/usr/local/bin/git-credential-manager' > /dev/null || 
export PATH="$PATH:/usr/local/bin/git-credential-manager"

Add the install directory to the user PATH (if not already present)

grep -E 'PATH=".*:?/usr/local/bin/git-credential-manager:?.*"' "$HOME/.profile" || 
echo -e '\nexport PATH="$PATH:/usr/local/bin/git-credential-manager"' >> "$HOME/.profile"

Add the PATH updates to login profile to add at login (if not already added)

When upgrading to the latest version of git-credential-manager, simply run the same set of commands as above, since they will all gracefully handle existing requirements.


Setup Password Store

Configure GPG Agent for Headless Linux

[ -d "$HOME/.gnupg" ] || ( mkdir "$HOME/.gnupg" && chmod 700 "$HOME/.gnupg" )
cat > ~/.gnupg/gpg-agent.conf << 'EOF'
default-cache-ttl 3600
max-cache-ttl 86400
pinentry-program /usr/bin/pinentry-tty
EOF
export GPG_TTY=$(tty) && echo -e '\nexport GPG_TTY=$(tty)' >> "$HOME/.bashrc"
gpg-connect-agent reloadagent /bye

GPG Keys

Check for Existing GPG Key
gpg --list-secret-keys --keyid-format LONG

Determine if you've already generated a GPG key on the host, note the email set on the GPG key

If you've already generated a GPG key on the host, then you must know the password used to generate the original key.

If you know the password for the GPG key, then you can use the existing key to encrypt the Git Credential Manager secrets.

Generate New GPG Key

Only run this if you're sure you do not have an existing key or you want to start fresh

gpg --full-generate-key

Option 9, Option 1, Option 0, Note hexadecimal string for use with pass init

  • Enter your name
  • Enter your email
  • Enter a comment (e.g. Credential Encryption Key)

When prompted, generate a passphrase to protect the key. And, store the passphrase in a password vault.

Get the GPG Key Public Key Fingerprint
gpg --list-public-keys username@domain.tld | head -n 2 | tail -n 1 | tr -d ' '

Get the GPG key public key fingerprint using email ...

gpg --list-public-keys username | head -n 2 | tail -n 1 | tr -d ' '

... or, get the GPG public key fingerprint using username

Password Store

When you -- or applications -- save secrets using the pass backend, they will be encrypted with your GPG key. So, if you haven't already, store the GPG key passphrase in a password vault, as you will need then when pass goes to decrypt your secrets.

Check Password Store State
[ -f "$HOME/.password-store/.gpg-id" ] && echo "Password store initialized with GPG" || echo "Password store not yet initialized with GPG"

Determine if the password store has been initialized with GPG

If Already Initialized...
cat "$HOME/.password-store/.gpg-id"

Note the public key fingerprint and compare with the gpg command above

Check the output of .gpg-id. This should contain a hexadecimal string (fingerprint) that should equal the output of the gpg --list-public-keys command above.

If the fingerprints in .gpg-id and gpg --list-public-keys do not match, then any secrets in your password store were likely encrypted using another GPG key. You can pass init with a new GPG key fingerprint, but you will have to have access to the old key to re-encrypt.

If Not Yet Initialized...
pass init GPG_PUB_KEY_FINGERPRINT_HERE


Configure Git

Default to the Password Store

git config --global credential.credentialStore gpg
git config --global credential.guiPrompt false

Disable GUI prompt in headless environment

git-credential-manager configure

Writes the [credential] helper = ... line into your global ~/.gitconfig.

git config --global --get credential.helper

Use --get and confirm git is using /usr/local/bin/git-credential-manager/git-credential-manager

Test Authentication

git clone https://domain.tld/repo.git

Will prompt whether you want to use Personal Access Token or Username/Password.

Stating again, you must remember the passphrase for your GPG