1
0
Fork
You've already forked signhook
0
No description
  • Shell 100%
2026年06月12日 17:52:36 +02:00
.gitignore repo+src: first commit 2026年05月05日 23:33:42 +02:00
.shellcheckrc repo+src: first commit 2026年05月05日 23:33:42 +02:00
_gpg_preset_passphrase.sh src: improve comments 2026年05月06日 18:11:07 +02:00
CONFIG.example src: improve comments 2026年05月06日 18:11:07 +02:00
COPYLEFT repo+src: add COPYRIGHT and spdx headers 2026年05月06日 06:15:40 +02:00
install.sh src: install.sh: fix test if append to pre-commit 2026年06月12日 17:52:36 +02:00
pre-commit.example src: fix injection order if file exists; rename SCRIPT* to HOOKS* vars 2026年05月06日 15:38:43 +02:00
README.md repo: readme: update 2026年05月22日 16:41:20 +02:00

Quality of life (git) hook to preset a signing key into gpg-agent.

Rationale

If you have your signing key passphrase in a vault, writing commits messages becomes very soon very unpleasant. To sign a commit you have to unlock your password manager which then (hopefully) copies the passphrase into xclip. And now time is ticking, for example with pass you have 45s before the secret is removed from your x11 selection. At the same time while writing the commit message one has to be careful not to unintentional overwrite the passphrase by selecting or copying text into the wrong register or clipboard. If so, one must start over and even if things go smoothly, after closing the editor the pinentry prompt will still pop up and ask for a passphrase. Which we now can paste in, or can't we? This is why people in the internet suggesting to set very high ttl's to keep your keys unlocked for many hours. Others telling you to place a pre-commit script in .git/hooks and then write your passphrase in clear text in it. (Of course not using a password manager but a pleasant and very short passphrase (or none at all!) to unlock keys is also what people do. This is speculation, though. My guess is that no one is willing to confess or suggest such a crime in public. And I have to admit all of this seems kinda reasonable given how bad the workflow is.) My preference is setting the ttl's to hold keys for around 10m. And you guessed it, all my passphrases are auto-generated, so typing in my master password is the only way to unlock those keys. (A gnuk or some sort of usb device which holds encryption keys is a nice addition to such a setup, but at least i recommend using offline keys. This way you can rotate your signing keys without loosing your identity if you have to. By default gnupg uses a single key for signing and certification which makes rotation very tricky.) Usually i pipe secrets direct into the application which needs it and never have any in clear text lingering on my disk. And that's what _gpg_preset_passphrase.sh can do. It pipes your passphrase direct into gpg-preset-passphrase(1) not exposing sensitive information thru environment variables or worse. It's kinda comfy while being saver as most solutions I've seen.

Installation

# run from within $GITROOT. (only touches files in .git/hooks.)
# replace me@example.com with your gnupg KEYID
bash <(curl -sL https://codeberg.org/nzeo/signhook/raw/branch/main/install.sh) me@example.com

Manual installation

# configure gpg-agent; (watch out, this will overwrite your config)
cat >~/.gnupg/gpg-agent.conf <<EOF
# time in s the entry is available
default-cache-ttl 600
# max time in s the entry is cached
max-cache-ttl 1200
allow-preset-passphrase
pinentry-program /usr/bin/pinentry-gtk
EOF
# reload gpg-agent
gpg-connect-agent reloadagent /bye
# overwrite any existing $GITROOT/.git/hooks/pre-commit hook. (watch out)
cat >$GITROOT/.git/hooks/pre-commit <<EOF
#!/bin/bash
# shellcheck disable=SC2155
set -euo pipefail
declare -gr HOOKSPATH="\$(realpath "\${BASH_ARGV0}")"
declare -gr HOOKSDIR="\${HOOKSPATH%/*}"
source "\${HOOKSDIR}/CONFIG"
source "\${HOOKSDIR}/_gpg_preset_passphrase.sh"
EOF
# make pre-commit executable
chmod 755 $GITROOT/.git/hooks/pre-commit
# copy files to .git/hooks folder
cp _gpg_preset_passphrase.sh $GITROOT/.git/hooks/
cp CONFIG.example $GITROOT/.git/hooks/CONFIG
# edit CONFIG file
KEYGRIP="DEADBEEFXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" # signing key fpr (keygrip) [S]. `gpg -K --with-keygrip`
PASSCMD="pass me@example.com" # command to print $KEYGRIP passphrase to stdout
sed -i "s%^CONFIG_PASSCMD.*%CONFIG_PASSCMD=\"${PASSCMD}\"%" $GITROOT/.git/hooks/CONFIG
sed -i "s%^CONFIG_KEYGRIP.*%CONFIG_KEYGRIP=\"${KEYGRIP}\"%" $GITROOT/.git/hooks/CONFIG
# as an alternative one can set/export KEYGRIP and PASSCMD as enviorment variables.
# these variables will take precedence. NOTE: this how ever will make your
# passphrase visible to other users on your system!
KEYGRIP="DEADBEEFXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" PASSCMD="pass gpg/me@example.com" git commit -S
# if you set CONFIG_KEYGRIP and CONFIG_PASSCMD to an empty string and neither
# KEYGRIP and PASSCMD is set as enviorment variable _gpg_preset_passphrase.sh
# will do nothing. (you have to type in your signing key passphrase via gpg
# pinentry command)
  • gpg-connect-agent -q 'keyinfo --list' /bye | grep "1 P" view cached keys
  • gpg-connect-agent reloadagent /bye flush all cached keys