Rendered from docs/how-to/rotate-or-revoke-the-apt-signing-subkey.md in the Headwater
corpus. Every document on this half of the site is typed by the taxonomy
the descriptor names: corpus.json.
Rotate or revoke the APT signing subkey
Audience: the owner of the Headwater primary key. An adopter does one step after a rotation, and step 4 tells them. HW-DR-0094 is the decision that this guide follows.
Before you start
You need the primary key, which you keep offline, and admin access to the repository settings. A signing subkey is in the APT_SIGNING_KEY Actions secret. The public keyring at site/apt/headwater-archive-keyring.asc is what adopters trust.
Rotate when a subkey comes near its expiry date. Revoke when a subkey leaves your control, for example when a person who could read the secret leaves.
apt does not fetch keys. It checks a signature only against the keyring file that the adopter downloaded and named in signed-by. A machine whose keyring does not hold a new subkey refuses metadata that the subkey signs, with NO_PUBKEY. This is true when the same primary key certifies the subkey. So a rotation puts the new subkey in the public keyring one release before it signs anything. It also tells adopters to download the keyring again.
Steps
- On the offline machine, add a new signing subkey to the primary key:
gpg --quick-add-key <primary-fingerprint> ed25519 sign 2y. - To revoke, also revoke the old subkey now:
gpg --edit-key <primary-fingerprint>, thenkey <n>,revkeyandsave. To rotate, keep the old subkey. It continues to sign until step 6. - Export the public keyring, which now holds both subkeys:
gpg --armor --export <primary-fingerprint> > site/apt/headwater-archive-keyring.asc. Commit it in a pull request and merge it. The merge starts thedeploy-site.ymljob, and that job serves the keyring athttps://headwater.tools/apt/headwater-archive-keyring.asc. - Tell adopters to download the keyring again to the path that their
signed-bynames. Put the line in the notes of the next release and on the tracker. To rotate, cut at least one release that the old subkey signs after this step. Adopters then have one release cycle to fetch the keyring. To revoke, there is no such cycle, because the old subkey must not sign again. An adopter who has not fetched the new keyring getsNO_PUBKEYat the next release. - Export the new subkey alone, with no passphrase:
gpg --armor --export-secret-subkeys <new-subkey-fingerprint>! > subkey.asc. The!exports that one subkey and no other. - Put the contents of
subkey.ascinto theAPT_SIGNING_KEYActions secret, then deletesubkey.ascwithshred -u subkey.asc. - Sign again. A release is immutable, so cut the next release per Cut a release. Its metadata carries the new signature. The
deploy-sitejob of the release runs afterpublishand serves it. An adopter whose keyring is older than step 3 getsNO_PUBKEYfrom this release until they do step 4. - To rotate, let the old subkey expire after step 7. It signs nothing after step 6.
How to know it worked
On a Debian or Ubuntu machine with the keyring from step 3, run apt-get update against the repository. It must fetch InRelease with no NO_PUBKEY and no EXPKEYSIG line.
Run gpg --verify InRelease on a copy of the file with the keyring from step 3 imported. The line Good signature must name the new subkey.
The publish job of the release prints no warning about APT_SIGNING_KEY.