HOMEPAGE

gpg, ssh & git

the ugh

i was having some issues with using multiple accounts in git while managing ssh and pgp keys with gpg-agent. gpg-agent picked ssh keys based on fixed priority, so the selected didn't always have access to my current repository. pinentry-curses, which i otherwise like, doesn't show the name or email of the identity when prompting for ssh key. pinentry frequently prompted for a password of a key that didn't have access to the remote repository.

i needed to ensure that ssh key always corresponds to the repository.

what's working.

a login script searches for gpg keys based on any identifier, enables ssh for those keys with gpg-agent and exports the public ssh keys for use in the ssh configuration.

$XDG_CONFIG_HOME/login/gpg
if command -v gpg-agent > /dev/null; then
	gpg-connect-agent updatestartuptty /bye > /dev/null 2>&1
    sshs_location="$GNUPGHOME/sshs"
    key_directory="$HOME/.ssh/public"
	if [[ -f "$sshs_location" ]]; then
		while IFS= read -r line; do
			keygrip=$(gpg --list-secret-keys --with-keygrip --with-colons "$line" 2>/dev/null | awk -F: '
				BEGIN { found_auth = 0 }
				($1 == "sub" || $1 == "ssb") {
					if (index($12, "a") > 0) {
						found_auth = 1
					} else {
						found_auth = 0
					}
				}
				$1 == "grp" && found_auth == 1 {
					print $10
					exit 0
				}
			')
			if [[ -n "$keygrip" ]]; then
				gpg-connect-agent "keyattr $keygrip Use-for-ssh: true" /bye > /dev/null
				if [[ ! -f "$key_directory/$line.ssh" ]]; then
					gpg --export-ssh-key "$line" > "$key_directory/$line.ssh"
				fi
			fi
		done < "$sshs_location"
	fi
fi

this improves on the deprecated but convenient sshcontrol file that requires a list of keygrips. instead sshs can use any identifier as long as it results in one key.

$GNUPGHOME/sshs
si@mmmeon
n@users.noreply.github.com
0x30FDAS89S1K50

finally, the ssh configuration needs to use these ssh keys. fake hosts specify a unique host & identity file combination. the configuration doesn't need this when each provider maps has one account.

$HOME/.ssh/config
+Host mmm.sr.ht
+   HostName git.sr.ht
+   User git
+   IdentityFile ~/.ssh/public/si@mmmeon.ssh
+   IdentitiesOnly yes
-Host git.sr.ht
-   User git
-   IdentityFile ~/.ssh/public/si@mmmeon.ssh
-   IdentitiesOnly yes

in the multi-account setup git clone git@git.sr.ht:~mmmeon/dot intuitively becomes git clone mmm.sr.ht:~/mmmeon/dot.

similarly, for already cloned repositories, git remote remove origin && git remote add origin mmm.sr.ht:~/mmmeon/dot does the same.