# 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 | Gitea repository Actions secret `GPG_PRIVATE_KEY` | | 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 ``` Provision the **private key** as the repository Actions secret `GPG_PRIVATE_KEY`. Run the export on the trusted key-generation machine, then enter its output in the Gitea repository's Actions secret settings. Do not save it in the checkout, logs, or a runner directory. The release workflow checks its fingerprint against the committed public key before signing. ```bash gpg --armor --export-secret-keys packaging@bongbetic.com ``` After provisioning the secret, delete the private key from the key-generation keyring: ```bash gpg --batch --yes --delete-secret-keys packaging@bongbetic.com gpg --batch --yes --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 tagged release performs: **import → verify → sign → delete** on the repository-scoped Gitea Actions runner. The Gitea secret remains configured; the runner's keyring copy is removed after the job. ### Step 1: Push the release tag After updating the version and dated changelog section, push the matching tag: ```bash git push origin v ``` ### Step 2: Build, verify, and sign packages The release workflow imports `GPG_PRIVATE_KEY`, checks it against `packaging/keys/fenris-packaging.asc`, builds packages, signs the RPM and clearsigned checksum manifest, validates both, and publishes the release. XBPS publication is separate and requires its signing key and host acceptance. ### Step 3: Verify runner cleanup The workflow's `always()` cleanup removes the imported key from the runner's keyring, including after a failed job. Confirm no packaging secret key remains on the runner after the release job. The Gitea Actions secret remains the approved signing source. Do not copy it to the runner or repository outside the workflow. ## 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 ```