- Shell 100%
| .gitignore | repo+src: first commit | |
| .shellcheckrc | repo+src: first commit | |
| _gpg_preset_passphrase.sh | src: improve comments | |
| CONFIG.example | src: improve comments | |
| COPYLEFT | repo+src: add COPYRIGHT and spdx headers | |
| install.sh | src: install.sh: fix test if append to pre-commit | |
| pre-commit.example | src: fix injection order if file exists; rename SCRIPT* to HOOKS* vars | |
| README.md | repo: readme: update | |
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 keysgpg-connect-agent reloadagent /byeflush all cached keys