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 credentialspass-- password storepinentry-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