This repository has no description
hakobin docs key-rotation.md
4.5 kB
Markdown
at main

Key Rotation Guide #

Key rotation is the process of replacing an active GPG signing key with a new one. It is a critical security practice to perform regularly, and is necessary if a key expires or is compromised.


The Golden Rule of Key Rotation #

Important

Clients must trust the new public key BEFORE you begin signing packages with the new private key.

If you sign packages with a new private key before clients trust the new public key, their package managers (like apt or dnf) will reject the metadata as untrusted, and automated updates will fail.

To prevent breaking clients, Hakobin uses a two-phase key rotation pattern.


Key Rotation Workflow #

graph TD
    A[Start: Client trusts Key A] --> B[Phase 1: Publish Key B as trusted, keep signing with Key A]
    B --> C[Clients refresh keyring & now trust both Key A and Key B]
    C --> D[Phase 2: Switch active signer to Key B, publish Key A as trusted]
    D --> E[Clients refresh keyring & continue working seamlessly]
    E --> F[Retire: Remove Key A from trusted list, only Key B remains]

Detailed Step-by-Step Guide #

Step 1: Backup Current Public Keys #

Before making changes, download and save a copy of the currently published public key bundle.

For APT:

curl -fsSL https://packages.example.com/deb/pubkey.asc -o old-pubkey.asc

For RPM:

curl -fsSL https://packages.example.com/rpm/stable/x86_64/RPM-GPG-KEY-hakobin.asc -o old-rpm-pubkey.asc

Also, generate and export the public key of your new GPG key:

gpg --armor --export <new-key-id> > new-pubkey.asc

Step 2: Phase 1 — Distribute the New Key #

In this phase, we continue to sign metadata with our old private key (so existing clients do not break), but we bundle the new public key into the published trust list.

Run the rotate-key command:

APT:

hakobin \
  --signing-key ./old-signing-key.gpg \
  --trusted-key ./new-pubkey.asc \
  deb rotate-key

RPM:

hakobin \
  --signing-key ./old-signing-key.gpg \
  --trusted-key ./new-pubkey.asc \
  rpm rotate-key

Tip

Now, wait. You must allow enough time (e.g., 24 to 48 hours depending on your update schedules) for client systems to refresh their package databases. When they refresh, they will download the updated keyring containing both public keys.


Step 3: Refresh Clients (Manual Verification) #

To trigger client keyring updates manually (or via Configuration Management like Ansible):

APT clients:

curl -fsSL https://packages.example.com/deb/pubkey.gpg \
  | sudo tee /etc/apt/keyrings/hakobin.gpg >/dev/null
sudo chmod 0644 /etc/apt/keyrings/hakobin.gpg
sudo apt update

DNF/YUM clients:

sudo dnf clean metadata
sudo dnf makecache

Step 4: Phase 2 — Make the New Key the Signer #

Once you are confident clients have updated their keyrings and trust both keys, you can switch the active signing key to the new private key.

Keep the old public key as a trusted key in the bundle to support any slow-updating clients.

APT:

hakobin \
  --signing-key ./new-signing-key.gpg \
  --trusted-key ./old-pubkey.asc \
  deb rotate-key

RPM:

hakobin \
  --signing-key ./new-signing-key.gpg \
  --trusted-key ./old-rpm-pubkey.asc \
  rpm rotate-key

At this point, all new metadata and RPM packages are signed using the new key. Since clients already trust the new key from Phase 1, they will verify the signatures without any warnings or failures.


Step 5: Retire the Old Key #

After the transition window has closed (e.g., a week or two later), you can safely remove the old key entirely, publishing only the new active key in the public bundle:

APT:

hakobin --signing-key ./new-signing-key.gpg deb rotate-key

RPM:

hakobin --signing-key ./new-signing-key.gpg rpm rotate-key

Continuous Integration (CI/CD) Pattern #

In automated pipelines, it is best to manage the active private key using the GPG_PRIVATE_KEY environment variable, and specify trusted public keys using the --trusted-key flag:

# Phase 1: Sign with old, distribute new
export GPG_PRIVATE_KEY="$(cat old-signing-key.gpg)"
hakobin --trusted-key ./new-pubkey.asc deb rotate-key

# Phase 2: Sign with new, distribute old
export GPG_PRIVATE_KEY="$(cat new-signing-key.gpg)"
hakobin --trusted-key ./old-pubkey.asc deb rotate-key