How to Detect Mirror Tampering with APT, Yum, and DNF Hash-Chain Provenance

Advanced 20 minutes Platform Engineers / Security Engineers Security & Policy

Enable end-to-end clearsigned InRelease + repomd.xml.asc verification for OS package mirrors by configuring the trust-root keyring.

Overview

OS-package mirrors (APT, Yum, DNF) have been a blind spot for most registry-proxy solutions — they’re not SemVer, they’re not on the OSV malware feed, and they don’t have a SLSA-style attestation standard. But they do have a hash-chain provenance model that pre-dates SLSA by a decade: a clearsigned InRelease file (APT) or a detached repomd.xml.asc (Yum / DNF) at the top of the repo, with each descendant file hash-chained back up to the signed root.

Chainsaw’s hasProvenance condition now walks that chain end-to-end for every OS package request:

  • APT — PGP-verify InRelease / Release.gpg, walk the hash of Packages.{gz,xz} → hash of the requested .deb file
  • Yum / DNF — PGP-verify repomd.xml.asc, walk the hash of primary.{xml,sqlite}.{gz,xz} → hash of the requested .rpm file

Result.Status reaches StatusVerified only when the chain validates top to bottom — no silent passes on mirror tampering.

Prerequisites

  • Chainsaw admin access to environment configuration
  • Access to your organisation’s trusted APT / RPM signing keys (.asc or .gpg files)

Step 1: Set the Trust Root

The trust root is a file or directory of public keys that Chainsaw treats as authoritative signers for the OS-package repos it proxies. Configure via two environment variables:

# File: a single keyring file (can be .asc-armored or .gpg-binary)
export CHAINSAW_APT_KEYRING=/etc/chainsaw/apt-keyring.asc
export CHAINSAW_RPM_KEYRING=/etc/chainsaw/rpm-keyring.gpg

# Directory: Chainsaw picks up every *.asc and *.gpg file inside
export CHAINSAW_APT_KEYRING=/etc/chainsaw/keys/apt/
export CHAINSAW_RPM_KEYRING=/etc/chainsaw/keys/rpm/

If unset, Chainsaw falls back to an embedded Debian / Fedora keyring snapshot shipped with the binary. The embedded fallback is a safety net for developer setups — production deployments should set the env vars explicitly so the trust root is auditable.

A missing or empty keyring does not silently pass provenance. The checker returns StatusUnavailable with a descriptive error (“inconclusive: keyring unavailable”) so the policy engine treats the check as failing rather than passing.

Step 2: Create the Provenance Policy

  1. Navigate to Policies → Create Policy
  2. Name: Require provenance — APT / Yum / DNF
  3. Condition: hasProvenance = true
  4. Scope: APT / Yum / DNF repositories
  5. Action: Block

Step 3: Verify the Walk

Trigger an install against your proxy from a client:

apt-get install curl   # will fetch via Chainsaw

The BOM will show the provenance result for the fetched .deb:

FieldValue
provenance.statusverified
provenance.signerPGP fingerprint from your trust root
provenance.chainInRelease → Packages.gz → curl_X.Y.Z.deb

For Yum / DNF:

FieldValue
provenance.statusverified
provenance.signerPGP fingerprint from RPM keyring
provenance.chainrepomd.xml.asc → primary.xml.gz → curl-X.Y.Z.rpm

Step 4: Handle the Known Gap — by-hash Layouts

Some repos serve exclusively via the APT by-hash layout (/by-hash/SHA256/... paths). Chainsaw currently walks the canonical-filename path rather than the by-hash path. If you proxy a by-hash-only repo, open an issue; meanwhile the provenance check will return StatusUnavailable with a “by-hash layout not supported” reason rather than silently passing.

Step 5: Rotate Keys Safely

When an upstream rolls its signing key:

  1. Add the new key to your keyring file / directory
  2. Wait until the new key has signed at least one InRelease / repomd.xml for each series you proxy
  3. Remove the old key

Running with both keys in the keyring during the overlap window keeps existing installs working without a provenance-failure outage.

Ecosystem Matrix Reference

For the full provenance matrix (which ecosystems are Sigstore, PGP, sum.golang.org, hash-chain, or absent), run chainsaw policy preflight against your server → the HasProvenance condition.

Next Steps