Harden zot registry, pt 1 #231

Merged
eblume merged 7 commits from feature/harden-zot-registry into main 2026-02-20 22:50:02 -08:00
6 changed files with 278 additions and 2 deletions
Showing only changes of commit 0da5d8906c - Show all commits

Add version-infrastructure prereqs to harden-zot-registry Mikado chain

Analysis of adopt-commit-based-container-tags revealed three new prerequisites:
- pin-container-versions: add version ARGs to devpi, cv, quartz Dockerfiles
- add-dagger-nix-build: Dagger functions for nix container builds and version extraction
- add-container-version-sync-check: pre-commit hook enforcing version consistency
  across Dockerfile ARGs, service-versions.yaml, and nix derivations

Eliminated the need for separate VERSION files — existing sources (Dockerfile
ARGs, nix derivations, service-versions.yaml) are the source of truth, with a
sync check enforcing consistency.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Erich Blume 2026-02-20 19:08:15 -08:00

View file

@ -1 +1 @@
Create C2 Mikado cards for harden-zot-registry: root goal and three prerequisite cards (register-zot-oidc-client, wire-ci-registry-auth, enforce-tag-immutability).
Expand harden-zot-registry Mikado chain: add prereqs for container version sync check, pin container versions, and Dagger nix build function.

View file

@ -73,6 +73,9 @@ Mikado chain for hardening the zot registry. Track progress with `mise run docs-
- [[wire-ci-registry-auth]]
- [[enforce-tag-immutability]]
- [[adopt-commit-based-container-tags]]
- [[add-container-version-sync-check]]
- [[pin-container-versions]]
- [[add-dagger-nix-build]]
## Authentik

View file

@ -0,0 +1,84 @@
---
title: Add Container Version Sync Check
modified: 2026-02-20
status: active
requires:
- pin-container-versions
- add-dagger-nix-build
tags:
- how-to
- containers
- ci
- zot
---
# Add Container Version Sync Check
Add a pre-commit check that validates version consistency across the three places container versions are declared: Dockerfile ARGs, `service-versions.yaml`, and nix derivations. No VERSION files needed — the existing sources are the source of truth, and the check enforces they agree.
## Context
Discovered during analysis of [[adopt-commit-based-container-tags]]: the new commit-SHA-based image tags need a reliable version source (`vX.Y.Z-<sha>`). Versions are currently scattered across Dockerfile ARGs (varying naming conventions), `service-versions.yaml` entries (many still `null`), and nix derivations (implicit from nixpkgs). A sync check ensures these stay consistent without adding a redundant fourth source.
## What to Do
### 1. Create `mise run container-version-check` task
A uv-script mise task that validates version consistency across sources:
1. **Dockerfile ARG** — parse `ARG <NAME>_VERSION=<value>` (strip `v` prefix if present). This is the primary version declaration for Dockerfile containers.
2. **`service-versions.yaml`** — `current-version` field for the matching service name (strip `v` prefix). Must agree with the Dockerfile ARG.
3. **Nix derivation** — for nix-only containers (authentik), extract the version via `dagger call nix-version` (from [[add-dagger-nix-build]]). For dual containers (nettest, ntfy), the Dockerfile ARG is the primary check target; the nix version is informational.
The check also validates:
- Every `hybrid` service in `service-versions.yaml` has a non-null `current-version`
- Every container with a Dockerfile has a parseable `*_VERSION` ARG (after [[pin-container-versions]] is complete)
Report mismatches as errors. Exit non-zero if any are found.
### 2. Add pre-commit hook
Add a `container-version-check` entry to `.pre-commit-config.yaml` following the existing `docs-check-*` pattern:
```yaml
- id: container-version-check
name: container-version-check
entry: mise run container-version-check
language: system
files: ^(containers/|service-versions\.yaml)
pass_filenames: false
```
### 3. Populate `service-versions.yaml`
Fill in `current-version` for all hybrid services that currently have `null`. The sync check needs these values to validate against.
## Version extraction in CI
The CI workflow (from [[adopt-commit-based-container-tags]]) extracts the version at build time — no VERSION file needed:
- **Dockerfile builds**: `grep -oP 'ARG \w+_VERSION=\K\S+' containers/$CONTAINER/Dockerfile | head -1`
- **Nix builds**: `dagger call nix-version --src=. --package=<pkg>` or `nix eval --raw nixpkgs#<pkg>.version`
## Key Files
| File | Change |
|------|--------|
| `mise-tasks/container-version-check` | New: sync validation script |
| `.pre-commit-config.yaml` | Add `container-version-check` hook |
| `service-versions.yaml` | Fill in `current-version` for hybrid services |
## Verification
- [ ] `mise run container-version-check` passes with no errors
- [ ] Intentionally changing a Dockerfile ARG without updating `service-versions.yaml` fails the check
- [ ] Pre-commit hook catches mismatches on commit
- [ ] `service-versions.yaml` has `current-version` populated for all hybrid services
- [ ] Nix-only container versions are checked via Dagger (or gracefully skipped with a warning if Dagger unavailable)
## Related
- [[pin-container-versions]] — Prereq: containers need parseable version ARGs first
- [[add-dagger-nix-build]] — Prereq: nix version extraction
- [[adopt-commit-based-container-tags]] — Parent: CI uses the same version extraction at build time
- [[harden-zot-registry]] — Root goal

View file

@ -0,0 +1,97 @@
---
title: Add Dagger Nix Build Function
modified: 2026-02-20
status: active
tags:
- how-to
- containers
- ci
- dagger
- zot
---
# Add Dagger Nix Build Function
Add Dagger functions for building nix container images and extracting version info from nix derivations. This enables local nix container evaluation and provides the version extraction mechanism needed by [[add-container-version-sync-check]].
## Context
Discovered during analysis of [[adopt-commit-based-container-tags]]: nix containers (authentik, ntfy, nettest) derive their bundled app version from the nixpkgs pin, not from an explicit declaration. To validate that a VERSION file matches the actual nix-built version, we need a way to query the version from nix.
Currently, nix containers can only be built on ringtail (the `nix-container-builder` runner). There is no local build path for developers — the only option is to push and wait for CI. Adding a Dagger-based nix build gives both local evaluation and version extraction.
## What to Do
### 1. Add `build_nix` Dagger function
A new function in `.dagger/src/blumeops_ci/main.py` that builds a nix container inside a `nixos/nix` container:
```python
@function
async def build_nix(
self, src: dagger.Directory, container_name: str
) -> dagger.File:
"""Build a nix container from containers/<name>/default.nix. Returns the image tarball."""
# Uses NIX_IMAGE (nixos/nix:2.33.3) — already defined in the module
# Runs nix-build inside the container
# Returns the docker-archive tarball
```
This mirrors the existing `build` function (Dockerfile) but for nix. The result is a docker-archive tarball that can be loaded with `docker load` or pushed with `skopeo`.
### 2. Add `nix_version` Dagger function
A function that extracts the version of a specific nix package from the nixpkgs pin:
```python
@function
async def nix_version(
self, src: dagger.Directory, package: str
) -> str:
"""Extract the version of a nixpkgs package. Returns version string."""
# nix eval --raw nixpkgs#<package>.version
```
This lets the version sync check run `dagger call nix-version --src=. --package=authentik` to get the actual version that would be built.
### 3. Add `publish_nix` Dagger function (optional)
If useful, a combined build-and-push that mirrors `publish` but for nix images:
```python
@function
async def publish_nix(
self, src: dagger.Directory, container_name: str, version: str,
registry: str = "registry.ops.eblu.me",
) -> str:
"""Build nix container and push to registry via skopeo."""
```
This would give a `dagger call publish-nix` path parallel to the existing `dagger call publish`.
## Nix in Dagger
The `flake_lock` function already demonstrates running nix inside Dagger using `nixos/nix:2.33.3`. The nix build function follows the same pattern but needs:
- `NIX_PATH` set to resolved nixpkgs (same as the CI workflow does)
- `--extra-experimental-features "nix-command flakes"` for `nix eval`
- The full repo source mounted (nix files may reference other files like `test-connectivity.sh`)
## Key Files
| File | Change |
|------|--------|
| `.dagger/src/blumeops_ci/main.py` | Add `build_nix`, `nix_version`, optionally `publish_nix` |
## Verification
- [ ] `dagger call build-nix --src=. --container-name=nettest` produces a valid docker-archive tarball
- [ ] `dagger call nix-version --src=. --package=ntfy-sh` returns the correct version string
- [ ] `dagger call nix-version --src=. --package=authentik` returns the Authentik version
- [ ] Tarball from `build-nix` can be loaded with `docker load` and run locally
## Related
- [[add-container-version-sync-check]] — Parent: needs nix version extraction for sync check
- [[adopt-commit-based-container-tags]] — Grandparent goal
- [[dagger]] — Dagger reference

View file

@ -2,6 +2,8 @@
title: Adopt Commit-Based Container Tags
modified: 2026-02-20
status: active
requires:
- add-container-version-sync-check
tags:
- how-to
- containers
@ -35,7 +37,12 @@ Both the Dockerfile and Nix workflows fire for each trigger, each bailing out if
### Version Source
Each container declares the version of its primary bundled app. The mechanism for declaring this (e.g., a `VERSION` file, parsing a Dockerfile `ARG`, or a convention per container) should be determined during implementation.
Each container's version is extracted at build time from existing declarations — no separate VERSION file:
- **Dockerfile builds**: parsed from `ARG <NAME>_VERSION=<value>` in the Dockerfile
- **Nix builds**: extracted via `dagger call nix-version` or `nix eval`
The [[add-container-version-sync-check]] pre-commit check ensures these declarations stay in sync with `service-versions.yaml`. See [[pin-container-versions]] for the work to ensure every container has a parseable version.
### Image Tag Format

View file

@ -0,0 +1,85 @@
---
title: Pin Container Versions
modified: 2026-02-20
status: active
tags:
- how-to
- containers
- ci
- zot
---
# Pin Container Versions
Ensure every container has an explicit, parseable version declaration so that [[add-container-version-sync-check]] has something to validate against.
## Context
Discovered during analysis of [[adopt-commit-based-container-tags]]: most containers already have version ARGs (miniflux, navidrome, ntfy, etc.), but several do not. Without explicit versions in the build files, there is nothing for a VERSION file to sync against.
## Containers Needing Work
### devpi — Pin pip dependencies
Currently installs `devpi-server` and `devpi-web` without version pins:
```dockerfile
RUN pip install --no-cache-dir devpi-server devpi-web
```
Add version ARGs and pin:
```dockerfile
ARG DEVPI_SERVER_VERSION=6.12.1
ARG DEVPI_WEB_VERSION=4.2.2
RUN pip install --no-cache-dir \
devpi-server==${DEVPI_SERVER_VERSION} \
devpi-web==${DEVPI_WEB_VERSION}
```
The VERSION file will track `devpi-server` as the primary version.
### cv — Add internal version ARG
Thin nginx wrapper that downloads content at runtime. No upstream app to track — the version reflects the container definition itself:
```dockerfile
ARG CV_VERSION=0.1.0
```
Bump when the Dockerfile or scripts change.
### quartz — Add internal version ARG
Same pattern as cv:
```dockerfile
ARG QUARTZ_VERSION=0.1.0
```
### nettest — Already handled
Utility container with no upstream. Will use `0.1.0` in VERSION file. No Dockerfile ARG needed since there's nothing to pin — the Dockerfile just installs Alpine packages at whatever version Alpine ships. The sync check can skip the Dockerfile ARG validation for containers without a `*_VERSION` ARG.
### forgejo-runner — Already handled
Has `ARG DAGGER_VERSION=0.19.11` as its primary version. Good enough.
## Key Files
| File | Change |
|------|--------|
| `containers/devpi/Dockerfile` | Pin devpi-server and devpi-web versions |
| `containers/cv/Dockerfile` | Add `ARG CV_VERSION` |
| `containers/quartz/Dockerfile` | Add `ARG QUARTZ_VERSION` |
## Verification
- [ ] Every container Dockerfile either has a `*_VERSION` ARG or is documented as version-exempt (nettest)
- [ ] `devpi` container builds with pinned versions
- [ ] `cv` and `quartz` containers still build and serve correctly
## Related
- [[add-container-version-sync-check]] — Parent: needs parseable versions for sync check
- [[adopt-commit-based-container-tags]] — Grandparent goal