# Signing key ceremony The Fenris packaging key signs RPM payloads and clearsigns SHA256SUMS manifests. This document describes the key's lifecycle: creation, per-release use, rotation, and destruction. ## Key specification | Property | Value | |---|---| | Algorithm | RSA 3072 | | UID | `Fenris Packaging ` | | Expiry | 2 years from creation | | Hierarchy | Single key — no master/subkey split (single maintainer, manual builds) | | Private key storage | Password manager only | | Public key storage | `packaging/keys/fenris-packaging.asc` in-repo, release notes, docs | | Keyservers | Never — TOFU-over-TLS via raw URL | ## First release: key creation ```bash # Generate the dedicated RSA-3072 packaging key gpg --batch --gen-key < packaging/keys/fenris-packaging.asc # Print the fingerprint for docs and release notes gpg --fingerprint packaging@bongbetic.com ``` Save the **private key** to the password manager immediately: ```bash gpg --armor --export-secret-keys packaging@bongbetic.com ``` Then **delete the private key from the local keyring** — it must never persist on any build host: ```bash gpg --delete-secret-keys packaging@bongbetic.com gpg --delete-keys packaging@bongbetic.com ``` The committed `fenris-packaging.asc` must contain the real public key (replace the placeholder comments). ## Per-release signing flow Each release performs: **import → sign → delete**. The private key is never stored on disk longer than the release takes. ### Step 1: Import the private key Retrieve the private key from the password manager and import it: ```bash gpg --import /tmp/packaging-key-private.asc rm /f /tmp/packaging-key-private.asc # Shred if possible ``` ### Step 2: Build and sign packages The Makefile target `make release` handles signing automatically when the key is in the keyring: ```bash make release # builds, signs RPM, clearsigns SHA256SUMS, prints upload steps ``` Under the hood: 1. `rpmsign --addsign` signs the RPM payload with the packaging key (invoked by `make sign-rpm`). 2. `sha256sum` generates the checksum manifest. 3. `gpg --clearsign` produces `SHA256SUMS.asc` with the packaging key. ### Step 3: Delete the private key Immediately after signing: ```bash gpg --delete-secret-keys packaging@bongbetic.com gpg --delete-keys packaging@bongbetic.com ``` Verify the key is gone: ```bash gpg --list-keys packaging@bongbetic.com # Should produce: gpg: keyblock resource ...: No such file or directory ``` The entire import → sign → delete cycle should take minutes. The private key must never be left in any keyring between releases. ## Key rotation (outline) When the key approaches expiry, or if it is compromised: 1. **Generate a new key** using the same procedure as first release. 2. **Publish the new public key** alongside the old one in-repo: ```text packaging/keys/fenris-packaging.asc # new key (primary) packaging/keys/fenris-packaging-previous.asc # old key (one cycle) ``` 3. **Sign the next RPM** with the new key. 4. **Update `fenris.repo`** to list both `gpgkey` URLs (dnf accepts multiple): ```ini gpgkey=https://git.bongbetic.com/xavierk/Fenris/raw/branch/main/packaging/keys/fenris-packaging.asc https://git.bongbetic.com/xavierk/Fenris/raw/branch/main/packaging/keys/fenris-packaging-previous.asc ``` 5. **Drop the old key** from the repo after one release cycle. Delete `fenris-packaging-previous.asc` and revert `gpgkey` to the single URL. ## Verification Consumers verify the RPM payload signature via dnf (gpgcheck=1 in `fenris.repo` points at the published public key). The SHA256SUMS manifest verification is manual for downloaded assets: ```bash gpg --verify SHA256SUMS.asc SHA256SUMS sha256sum -c SHA256SUMS ``` ## One-time live probe Before the first real release, verify the full registry path end-to-end with a throwaway package. This confirms apt/dnf metadata generation, signature verification, and consumer setup work as a real consumer would experience them. ### Setup ```bash # Create a throwaway package name to avoid polluting fenris metadata PROBE_NAME="fenris-regtest" PROBE_VERSION="0.0.1" ``` ### Publish ```bash # Build a throwaway deb and rpm (use the existing nfpm config with a dummy name) # Or use a pre-built package — the probe tests the registry path, not the build # Upload deb to all codename pools for CODENAME in bookworm jammy noble; do curl --fail -X PUT \ -u "xavierk:${GITEA_TOKEN}" \ -T "dist/${PROBE_NAME}_${PROBE_VERSION}_amd64.deb" \ "https://git.bongbetic.com/api/packages/xavierk/debian/pool/${CODENAME}/main/upload" done # Upload rpm curl --fail -X PUT \ -u "xavierk:${GITEA_TOKEN}" \ -T "dist/${PROBE_NAME}-${PROBE_VERSION}-1.x86_64.rpm" \ "https://git.bongbetic.com/api/packages/xavierk/rpm/fenris/upload" ``` ### Verify apt metadata (Debian/Ubuntu consumer perspective) ```bash # On a Debian/Ubuntu machine: sudo mkdir -p /etc/apt/keyrings sudo curl -fsSL https://git.bongbetic.com/api/packages/xavierk/debian/repository.key \ | sudo gpg --dearmor -o /etc/apt/keyrings/gitea-xavierk.asc echo "deb [signed-by=/etc/apt/keyrings/gitea-xavierk.asc] https://git.bongbetic.com/api/packages/xavierk/debian bookworm main" \ | sudo tee /etc/apt/sources.list.d/fenris.list sudo apt update apt show ${PROBE_NAME} # metadata present, correct version apt install --dry-run ${PROBE_NAME} # dependency resolution works # Verify InRelease signature apt-key list 2>/dev/null || gpg --no-default-keyring --keyring /etc/apt/keyrings/gitea-xavierk.asc --list-keys ``` ### Verify dnf metadata (Fedora consumer perspective) ```bash # On a Fedora machine: sudo dnf config-manager --add-repo https://git.bongbetic.com/xavierk/Fenris/raw/branch/main/packaging/fenris.repo # Or use Gitea's auto-generated repo for the probe: sudo dnf config-manager --add-repo https://git.bongbetic.com/api/packages/xavierk/rpm/fenris.repo dnf info ${PROBE_NAME} # metadata present, correct version dnf install --assumeno ${PROBE_NAME} # dependency resolution works # Verify rpm signature rpm -q --scripts ${PROBE_NAME} # no scripts (throwaway) ``` ### Verify checksums and clearsign ```bash # Download from release assets or local build gpg --verify SHA256SUMS.asc SHA256SUMS sha256sum -c SHA256SUMS ``` ### Cleanup ```bash # Delete the throwaway packages from the registry for CODENAME in bookworm jammy noble; do curl --fail -X DELETE \ -u "xavierk:${GITEA_TOKEN}" \ "https://git.bongbetic.com/api/packages/xavierk/debian/pool/${CODENAME}/main/${PROBE_NAME}/${PROBE_VERSION}/amd64" done curl --fail -X DELETE \ -u "xavierk:${GITEA_TOKEN}" \ "https://git.bongbetic.com/api/packages/xavierk/rpm/fenris/${PROBE_NAME}/${PROBE_VERSION}/x86_64" # Remove test source list on consumer machines sudo rm /etc/apt/sources.list.d/fenris.list sudo apt update ```