feat: add GPG key setup script and generalize nginx GPG key serving
All checks were successful
poll-upstream / check (push) Successful in 2s
All checks were successful
poll-upstream / check (push) Successful in 2s
Add script/setup/gpg.sh to generate a dedicated lair keyring with a certify-only master key and a 1-year signing subkey, cross-signed by both personal keys. The public key is synced to oolon as <short-id>.gpg. Update nginx config to serve any .gpg file instead of a hardcoded RPM-GPG-KEY-mistralrs path, supporting multiple keys as the repo grows. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
99
script/setup/gpg.sh
Executable file
99
script/setup/gpg.sh
Executable file
@@ -0,0 +1,99 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
keyring_dir="${HOME}/.gnupg/lair"
|
||||
key_uid="rpm@lair.cafe"
|
||||
remote_host=oolon
|
||||
remote_key_dir="/var/www/rpm"
|
||||
signing_keys=(
|
||||
"1C09AC24C113C7F080DD4AA5B3C5A958508A43F2"
|
||||
"CF3E5AA5DAFD4A7FB69053E393977688ACF3510F"
|
||||
)
|
||||
|
||||
# ensure the lair keyring directory exists
|
||||
install --directory --mode 700 "${keyring_dir}"
|
||||
|
||||
# check for an existing valid key in the lair keyring
|
||||
existing_fpr=$(gpg --homedir "${keyring_dir}" --batch --with-colons --list-keys "${key_uid}" 2>/dev/null \
|
||||
| awk -F: '/^fpr:/ { print $10; exit }') || true
|
||||
|
||||
if [ -n "${existing_fpr}" ]; then
|
||||
echo "found existing key: ${existing_fpr}"
|
||||
else
|
||||
echo "no key found for ${key_uid} in ${keyring_dir}, generating..."
|
||||
|
||||
# create a certify-only master key
|
||||
gpg --homedir "${keyring_dir}" --batch --gen-key <<KEYEOF
|
||||
%no-protection
|
||||
Key-Type: eddsa
|
||||
Key-Curve: ed25519
|
||||
Key-Usage: cert
|
||||
Name-Real: lair.cafe RPM signing
|
||||
Name-Email: ${key_uid}
|
||||
Expire-Date: 0
|
||||
%commit
|
||||
KEYEOF
|
||||
existing_fpr=$(gpg --homedir "${keyring_dir}" --batch --with-colons --list-keys "${key_uid}" \
|
||||
| awk -F: '/^fpr:/ { print $10; exit }')
|
||||
echo "generated master key: ${existing_fpr}"
|
||||
|
||||
# add a dedicated signing subkey with 1-year expiry
|
||||
gpg --homedir "${keyring_dir}" --batch --passphrase '' --quick-add-key \
|
||||
"${existing_fpr}" ed25519 sign 1y
|
||||
echo "added signing subkey to ${existing_fpr}"
|
||||
|
||||
# sign the lair key with each personal key from the default keyring
|
||||
gpg --homedir "${keyring_dir}" --batch --armor --export "${existing_fpr}" | gpg --import
|
||||
for signer in "${signing_keys[@]}"; do
|
||||
gpg --batch --yes --local-user "${signer}" --sign-key "${existing_fpr}"
|
||||
echo "signed lair key with ${signer}"
|
||||
done
|
||||
gpg --armor --export "${existing_fpr}" | gpg --homedir "${keyring_dir}" --import
|
||||
echo "imported signatures back into lair keyring"
|
||||
fi
|
||||
|
||||
short_id="${existing_fpr: -8}"
|
||||
short_id_lower=$(echo "${short_id}" | tr '[:upper:]' '[:lower:]')
|
||||
public_key_file="${short_id_lower}.gpg"
|
||||
|
||||
echo ""
|
||||
echo "key fingerprint: ${existing_fpr}"
|
||||
echo "short id: ${short_id}"
|
||||
echo "public key file: ${public_key_file}"
|
||||
|
||||
# export the public key in ascii-armored format
|
||||
gpg --homedir "${keyring_dir}" --batch --armor --export "${existing_fpr}" > "/tmp/${public_key_file}"
|
||||
echo "exported public key to /tmp/${public_key_file}"
|
||||
|
||||
# sync public key to the remote rpm repo root (will not overwrite due to unique filename)
|
||||
if rsync \
|
||||
--archive \
|
||||
--verbose \
|
||||
--ignore-existing \
|
||||
--rsync-path 'sudo rsync' \
|
||||
--chown root:root \
|
||||
--chmod F644 \
|
||||
"/tmp/${public_key_file}" \
|
||||
"${remote_host}:${remote_key_dir}/${public_key_file}"; then
|
||||
echo "sync'd public key to ${remote_host}:${remote_key_dir}/${public_key_file}"
|
||||
else
|
||||
echo "failed to sync public key to ${remote_host}:${remote_key_dir}/${public_key_file}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
rm "/tmp/${public_key_file}"
|
||||
|
||||
echo ""
|
||||
signing_subkey_fpr=$(gpg --homedir "${keyring_dir}" --batch --with-colons --list-keys "${key_uid}" \
|
||||
| awk -F: '/^fpr:/ { fpr=$10 } /^sub:/ { getfpr=1; next } getfpr && /^fpr:/ { print $10; exit }')
|
||||
|
||||
echo "next steps:"
|
||||
echo " 1. add the following secrets to the gitea repo:"
|
||||
echo " RPM_SIGNING_KEY = output of: gpg --homedir ${keyring_dir} --armor --export-secret-subkeys ${signing_subkey_fpr}!"
|
||||
echo " RPM_SIGNING_KEY_ID = ${key_uid}"
|
||||
echo " 2. users can import the key with:"
|
||||
echo " sudo rpm --import https://rpm.lair.cafe/${public_key_file}"
|
||||
echo ""
|
||||
echo " the master key (certify-only, no expiry) stays on this workstation in ${keyring_dir}."
|
||||
echo " the signing subkey (1-year expiry) is what CI uses. rotate it with:"
|
||||
echo " gpg --homedir ${keyring_dir} --quick-add-key ${existing_fpr} ed25519 sign 1y"
|
||||
Reference in New Issue
Block a user