Git Signing
Git Commit Signing: Methods & Evolution
This document tracks the evolution of Git commit signing in this repository, comparing different methods for security, convenience, and user experience.
📊 Comparison Matrix
| Method | Protocol | Storage | UX: Interaction | UX: Caching | Security |
|---|---|---|---|---|---|
| GnuPG (PGP) | OpenPGP | Disk (~/.gnupg) | Passphrase prompt | Via gpg-agent | High |
| YubiKey (SK) | SSH (-sk) | Hardware | Physical Touch | N/A (Hardware enforced) | Ultra High |
| SSH Key (Disk) | SSH | Disk (~/.ssh) | None (if no pass) | N/A | High |
| SSH Key (Agent) | SSH | Disk (Encrypted) | Passphrase (Once) | Via ssh-agent | Excellent Balance |
🕰️ History of Signing in this Repo
1. The GnuPG Era (gpgSign = true)
Initially, the repository used standard GnuPG.
- The Pain: Every commit triggered a
pinentrypopup. Whilegpg-agentcan cache passwords, it often feels “heavy” and separate from the SSH workflow. - The Config:
signingKeywas a hex ID like1A7FAEC8....
2. The YubiKey Era (ed25519-sk)
To maximize security, we moved to hardware-backed SSH keys.
- The Pain: The
-sk(Security Key) protocol is designed for User Presence. This means for every single signature (every commit), the hardware mandates a physical touch. While highly secure against remote attacks, it is exhausting for frequent commits. - The Config:
signingKeypointed to a.pubfile for a key with the-sksuffix.
3. The Modern SSH Era (Current)
We now use standard SSH signing (gpg.format = ssh).
- The Solution: A standard
ed25519key stored on disk. This removes the hardware touch requirement while maintaining strong cryptographic verification.
🔐 Passwords vs. Passphrases
There is often confusion between these terms in the context of Git signing:
- GnuPG Password/Passphrase:
- Used to unlock your PGP private key in the GPG keyring.
- Managed by
gpg-agent.
- SSH Passphrase:
- Used to encrypt the key file itself on your disk.
- If you didn’t set one during
ssh-keygen, the file is stored in plaintext (Solution 1). - If you did set one, you must “unlock” the file to use it.
- The Difference:
- Solution 1 (No Passphrase): Convenience at the cost of disk-level security.
- Solution 2 (With Passphrase + Agent): The key is encrypted on disk. You type the passphrase once at login, the
ssh-agentdecrypts it into memory, and Git uses it instantly for the rest of the session.
🏗️ Agent Architecture & Conflict Resolution
This repository is configured to allow GnuPG and SSH to co-exist perfectly without conflicts.
1. Agent Independence
In modules/shell/gnupg.nix, we set enableSSHSupport = false.
- Why?: By default,
gpg-agentcan try to act as an SSH agent. This often causes conflicts with the standard OpenSSH agent. By disabling this, we ensure the OpenSSH agent handles all SSH keys, while GnuPG only handles encryption/decryption.
2. Hardware Bridge (scdaemon)
We use disable-ccid in scdaemon.conf.
- Why?: This prevents GnuPG from trying to “monopolize” the YubiKey’s smartcard reader. It allows the system’s
pcscddaemon to manage the hardware, so that both SSH-SK (FIDO2) and GPG operations can share the device gracefully.
3. Git Protocol Selection
In modules/shell/git.nix, we explicitly set gpg.format = "ssh".
- Why?: This tells Git to ignore the GPG binary entirely for signing operations. It uses the SSH protocol and your SSH agent directly, bypassing any GPG-related delays or prompts.
🛠️ Configuration Details
To use SSH signing, the following must be set in .gitconfig:
[user]
signingKey = /home/user/.ssh/id_ed25519.pub
[gpg]
format = ssh
[commit]
gpgSign = true
Why is this better than GnuPG?
- Simplicity: You only manage one type of key (SSH) for both server access and Git signing.
- Performance: SSH agents are generally faster and more integrated into Linux shells than the GPG stack.
- Auditability: Verification uses a simple
allowed_signersfile instead of a complex PGP web of trust.
✅ Signature Verification (allowed_signers)
To ensure your commits (and others’) show up as “Verified” in your Git log or on platforms like GitHub, you must maintain an allowed_signers file.
- Role: While the
signingKeyis used to create a signature, theallowed_signersfile is used to verify it. Without it, Git won’t know if a signature belongs to a trusted user. - Config: In this repository, the file is managed at
~/.config/git/allowed_signersand enabled for all signing methods (SSH and YubiKey). - Format: Each line contains the user’s email, the key type, and the public key string:
[email protected] namespaces="git" ssh-ed25519 AAAAC3Nza... - Sync: Ensure your latest signing keys are added to
config/git/allowed_signersto maintain a “Verified” status across all your devices.