Skip to content

Converge InfraHouse APT repo + signing key from Puppet (development)#278

Merged
akuzminsky merged 1 commit into
mainfrom
infrahouse-repo-keyring-convergence
Jul 4, 2026
Merged

Converge InfraHouse APT repo + signing key from Puppet (development)#278
akuzminsky merged 1 commit into
mainfrom
infrahouse-repo-keyring-convergence

Conversation

@akuzminsky

Copy link
Copy Markdown
Member

Why

cloud-init seeds /etc/apt/keyrings/infrahouse.gpg and /etc/apt/sources.list.d/50-infrahouse.list only at first boot. A long-lived instance that isn't reprovisioned never receives a rotated signing key — so when the current noble signing key expires (2026-07-20), apt-get update would fail fleet-wide with EXPKEYSIG. This is the client-side "long pole" of the InfraHouse APT GPG key rotation.

What

New profile::infrahouse_repo (included from profile::base) converges both artifacts every Puppet run:

  • Keyringfiles/converge-infrahouse-keyring.sh fetches the published (possibly multi-key) bundle over TLS from release-<codename>.infrahouse.com, gpg --dearmors it, and installs atomically. A check verb guards the exec so steady state is a no-op; a fetch/dearmor failure leaves the prior keyring intact.
  • Source list — rendered byte-identical to cloud-init's seed (codename from os.distro.codename fills both host release-<codename> and suite), so Puppet and cloud-init never fight.
  • Single refresh — both notify one refreshonly apt-get update that fires only on an actual change.

Trust is anchored on the TLS channel (no fingerprint pinning), consistent with cloud-init 2.4.0.

Shared ownership with cloud-init is intentional

cloud-init must keep seeding both files at first boot — ih-secrets has to be installable before Puppet ever runs (secret names live in user-data, values don't). So this class does not replace the seed; it keeps it converged afterward. That's why the source line is rendered byte-for-byte identical.

Validation (real-time on jumphost-sincere-crawdad, development)

  • In-sync run is a clean no-op; second pass no-op (idempotent).
  • Source list byte-identical — md5 627951c5b24fde2de0feb9e93700c0b6 before and after (no churn).
  • keyring == published bundle verified (curl … | gpg --dearmor | cmp).
  • apt-get update clean against the InfraHouse repo (no EXPKEYSIG/NO_PUBKEY).
  • Rewrite path exercised: staled the keyring (truncate -s 0) → Exec[…::converge] ran, apt_update triggered once, reconverged byte-identical to backup. This also covers a drifted first-boot-only node.

Scope / rollout

Dev-first (environments/development/modules/profile); promotes to sandbox then global per the plan in .claude/plans/infrahouse-keyring-convergence.md.

Note: the live release-noble bundle is still single-key (only the old key, [expires: 2026-07-20]). Deploying this class is necessary but not sufficient — the upstream rotation must publish the new key into the bundle for the fleet to actually converge onto it.

🤖 Generated with Claude Code

Add profile::infrahouse_repo (wired into profile::base) so already-running
instances pick up a rotated GPG signing key without being reprovisioned.
Today cloud-init seeds /etc/apt/keyrings/infrahouse.gpg and
/etc/apt/sources.list.d/50-infrahouse.list only at first boot, so a long-lived
instance never receives a new key -- when the current noble signing key expires
(2026-07-20) apt-get update would fail fleet-wide with EXPKEYSIG.

The class converges both artifacts every Puppet run:
- Keyring: files/converge-infrahouse-keyring.sh fetches the published (possibly
  multi-key) bundle over TLS from release-<codename>.infrahouse.com, dearmors it,
  and installs atomically. A 'check' verb guards the exec so steady state is a
  no-op; a fetch/dearmor failure leaves the prior keyring intact.
- Source list: rendered byte-identical to cloud-init's seed (codename from
  os.distro.codename fills both host and suite), so Puppet and cloud-init never
  fight. cloud-init must keep seeding at first boot (ih-secrets has to be
  installable before Puppet runs), so shared ownership is intentional.
- Both notify a single refreshonly 'apt-get update' that fires only on change.

Trust is anchored on the TLS channel (no fingerprint pinning), consistent with
cloud-init 2.4.0.

Validated real-time on jumphost-sincere-crawdad (development): in-sync no-op,
source list byte-identical (md5 unchanged), keyring == published bundle, and the
rewrite path (staled keyring -> reconverged byte-identical, apt refreshed once).

Dev-first; promotes to sandbox then global. Note: the live release-noble bundle
is still single-key -- the upstream rotation must publish the new key into the
bundle for the fleet to actually converge onto it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@akuzminsky akuzminsky merged commit 9e6c45d into main Jul 4, 2026
2 checks passed
@akuzminsky akuzminsky deleted the infrahouse-repo-keyring-convergence branch July 4, 2026 00:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants