Enable zot registry auth + wire CI credentials #237

Merged
eblume merged 7 commits from wire-ci-registry-auth into main 2026-02-21 12:20:29 -08:00
Owner

Summary

  • Enable OIDC + API key authentication on zot registry with three-tier accessControl
    • anonymousPolicy: ["read"] — anyone can pull
    • artifact-workloads group: ["read", "create"] — CI push, no overwrite/delete
    • admins group: ["read", "create", "update", "delete"] — break-glass
  • Wire both CI push paths (Dagger and Nix/skopeo) with ZOT_CI_API_KEY credentials
  • Add artifact-workloads PolicyBinding in Authentik blueprint for zot app access
  • Add ZOT_CI_API_KEY to Forgejo Actions secrets via existing ansible role

Completes the wire-ci-registry-auth and harden-zot-registry Mikado cards.

Manual Deployment Steps (after merge)

  1. Deploy Authentik blueprint: argocd app sync authentik
  2. In Authentik admin UI: set a password for the zot-ci service account
  3. Deploy zot config: mise run provision-indri -- --tags zot
  4. Log in to https://registry.ops.eblu.me as zot-ci via OIDC → generate API key
  5. Store API key in 1Password as zot-ci-apikey in blumeops vault
  6. Sync Forgejo secrets: mise run provision-indri -- --tags forgejo_actions_secrets
  7. Trigger a test container build to verify CI push
  8. Verify anonymous pull: curl -sf https://registry.ops.eblu.me/v2/_catalog

Uncertainties

  • Zot accessControl group matching with OIDC: Groups from Authentik's profile scope claim should map to zot policy groups, but the exact claim-to-group matching needs runtime verification
  • http.auth.apikey: true: This config key is documented but needs verification against the specific zot version built from source on indri
  • API key permissions: Need to confirm zot API keys inherit the generating user's group for accessControl evaluation

Test Plan

  • mise run provision-indri -- --check --diff --tags zot shows expected config changes
  • Anonymous pull works after deploy
  • Unauthenticated push fails (401)
  • OIDC browser login redirects to Authentik and back
  • API key push works after key generation
  • CI push succeeds with both Dagger and skopeo paths
  • mise run services-check passes

🤖 Generated with Claude Code

## Summary - Enable OIDC + API key authentication on zot registry with three-tier accessControl - `anonymousPolicy: ["read"]` — anyone can pull - `artifact-workloads` group: `["read", "create"]` — CI push, no overwrite/delete - `admins` group: `["read", "create", "update", "delete"]` — break-glass - Wire both CI push paths (Dagger and Nix/skopeo) with `ZOT_CI_API_KEY` credentials - Add `artifact-workloads` PolicyBinding in Authentik blueprint for zot app access - Add `ZOT_CI_API_KEY` to Forgejo Actions secrets via existing ansible role Completes the `wire-ci-registry-auth` and `harden-zot-registry` Mikado cards. ## Manual Deployment Steps (after merge) 1. Deploy Authentik blueprint: `argocd app sync authentik` 2. In Authentik admin UI: set a password for the `zot-ci` service account 3. Deploy zot config: `mise run provision-indri -- --tags zot` 4. Log in to `https://registry.ops.eblu.me` as `zot-ci` via OIDC → generate API key 5. Store API key in 1Password as `zot-ci-apikey` in blumeops vault 6. Sync Forgejo secrets: `mise run provision-indri -- --tags forgejo_actions_secrets` 7. Trigger a test container build to verify CI push 8. Verify anonymous pull: `curl -sf https://registry.ops.eblu.me/v2/_catalog` ## Uncertainties - **Zot `accessControl` group matching with OIDC:** Groups from Authentik's `profile` scope claim should map to zot policy groups, but the exact claim-to-group matching needs runtime verification - **`http.auth.apikey: true`:** This config key is documented but needs verification against the specific zot version built from source on indri - **API key permissions:** Need to confirm zot API keys inherit the generating user's group for accessControl evaluation ## Test Plan - [ ] `mise run provision-indri -- --check --diff --tags zot` shows expected config changes - [ ] Anonymous pull works after deploy - [ ] Unauthenticated push fails (401) - [ ] OIDC browser login redirects to Authentik and back - [ ] API key push works after key generation - [ ] CI push succeeds with both Dagger and skopeo paths - [ ] `mise run services-check` passes 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Enable authentication on the zot registry with OIDC (via Authentik) and
API key support. Add three-tier accessControl: anonymous read, CI create
(artifact-workloads group), admin full access.

Wire both CI push paths with registry credentials:
- Dagger publish() gains optional registry_password/username params
- Nix/skopeo path adds --dest-creds to skopeo copy

The ZOT_CI_API_KEY secret flows from 1Password through the existing
forgejo_actions_secrets ansible role to both runners.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The auth.apikey flag enables *using* API keys but the extensions.apikey
section is needed for the UI to show login and key generation options.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Zot defaults to using the email claim as username, but service accounts
in Authentik have no email set. Map to preferred_username instead, which
contains the actual username (e.g. "zot-ci").

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The apikey extension was deprecated in zot v2.1.13 — API key
management is now configured under http.auth.apikey, which was
already set.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix op read path to use Forgejo Secrets item field zot-ci-api
  (was zot-ci-apikey/credential)
- Rewrite zot reference card security model for OIDC + API key auth
- Add API key rotation procedure with impersonation steps and op
  oneliner
- Document 90-day key expiry in wire-ci-registry-auth how-to

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
eblume merged commit ff63679efb into main 2026-02-21 12:20:29 -08:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
eblume/blumeops!237
No description provided.