[VS-NfD] Pin all container images by digest #203

Closed
opened 2026-07-30 01:43:38 +02:00 by fable-5 · 1 comment
Collaborator

Plan reference: 20-massnahmenplan.md -> P1-3 (moved here per the milestone plan)
ADR: ADR 0024
Effort: S (1 AT)
Depends on:

Context

A floating tag means the deployed artefact is not the reviewed artefact.
Digest pinning is the precondition for both the registry mirror and the
offline update path (M5), which is why it lands here first.

Current state

deploy/compose/docker-compose.yml pins tags, not digests:

  • postgres:17.5-alpine (:186)
  • pandoc/core:3.6 (:206)
  • gotenberg/gotenberg:8 (:221) — a floating major tag, the loosest
    of the four
  • caddy:2.10-alpine (:237)
    Own images are ${IMAGE_PREFIX:-dorfteich}-{web,api,collab,backup}:${TAG:-latest}
    (:20,36,105,140), pinned per release by the deploy workflow.

Acceptance criteria

  • Every third-party image is referenced as name:tag@sha256:…; the
    tag stays for readability, the digest decides.
  • A documented, repeatable procedure updates digests (which command,
    how the new digest is verified) and a CI check fails on any
    third-party image reference without a digest.
  • All stage composes (test/int/prod) are updated — note that CD does
    not sync stage composes, so the rollout step is part of this
    issue's definition of done.
  • deploy/stages.md and #229 document the update procedure.

Out of scope

Building our own base images, and mirroring them (#218).

**Plan reference:** `20-massnahmenplan.md` -> P1-3 (moved here per the milestone plan) **ADR:** ADR 0024 **Effort:** S (1 AT) **Depends on:** — ## Context A floating tag means the deployed artefact is not the reviewed artefact. Digest pinning is the precondition for both the registry mirror and the offline update path (M5), which is why it lands here first. ## Current state `deploy/compose/docker-compose.yml` pins tags, not digests: - `postgres:17.5-alpine` (`:186`) - `pandoc/core:3.6` (`:206`) - `gotenberg/gotenberg:8` (`:221`) — a floating **major** tag, the loosest of the four - `caddy:2.10-alpine` (`:237`) Own images are `${IMAGE_PREFIX:-dorfteich}-{web,api,collab,backup}:${TAG:-latest}` (`:20,36,105,140`), pinned per release by the deploy workflow. ## Acceptance criteria - [ ] Every third-party image is referenced as `name:tag@sha256:…`; the tag stays for readability, the digest decides. - [ ] A documented, repeatable procedure updates digests (which command, how the new digest is verified) and a CI check fails on any third-party image reference without a digest. - [ ] All stage composes (test/int/prod) are updated — note that CD does **not** sync stage composes, so the rollout step is part of this issue's definition of done. - [ ] `deploy/stages.md` and #229 document the update procedure. ## Out of scope Building our own base images, and mirroring them (#218).
fable-5 added this to the M25 — VS-NfD: hardening & supply chain milestone 2026-07-30 01:43:38 +02:00
fable-5 added the
effort:S
area:supply-chain
vs-nfd
labels 2026-07-30 01:43:38 +02:00
Author
Collaborator

Implemented in PR #262 (commit db4f517, CI run 535 green, fast-forward merged per Stefan's standing go-ahead for the M25 block).

Evidence against the acceptance criteria:

  • All four third-party deploy images pinned as name:tag@sha256 in deploy/compose/docker-compose.yml (postgres:17.5-alpine, pandoc/core:3.6, gotenberg/gotenberg:8 - previously a floating MAJOR tag -, caddy:2.10-alpine); tag kept for readability, digest decides.
  • Documented, repeatable update procedure in deploy/stages.md section 5a: docker buildx imagetools inspect name:tag resolves the manifest-list digest; verification after rollout via docker inspect (.Image / RepoDigests). New early CI step fails on any third-party compose image reference without a digest (positive and negative case tested).
  • Stage rollout DONE (CD does not sync composes): /srv/DOCKER/dorfteich-{test,int,prod}/docker-compose.yml on ONE updated with the same pins, each with a .bak-203 backup. The pinned digests are exactly what test/int already run (verified against the live containers' RepoDigests), so the next pull/up recreates byte-identical containers; the prod edit is file-only until the next release deploy.
  • compose.dev.yml deliberately exempt (local convenience; its node helpers follow the #236 pin).
  • Update procedure recorded on #229.

With this, milestone M25 is complete.

Implemented in PR #262 (commit db4f517, CI run 535 green, fast-forward merged per Stefan's standing go-ahead for the M25 block). Evidence against the acceptance criteria: - All four third-party deploy images pinned as name:tag@sha256 in deploy/compose/docker-compose.yml (postgres:17.5-alpine, pandoc/core:3.6, gotenberg/gotenberg:8 - previously a floating MAJOR tag -, caddy:2.10-alpine); tag kept for readability, digest decides. - Documented, repeatable update procedure in deploy/stages.md section 5a: docker buildx imagetools inspect <name:tag> resolves the manifest-list digest; verification after rollout via docker inspect (.Image / RepoDigests). New early CI step fails on any third-party compose image reference without a digest (positive and negative case tested). - Stage rollout DONE (CD does not sync composes): /srv/DOCKER/dorfteich-{test,int,prod}/docker-compose.yml on ONE updated with the same pins, each with a .bak-203 backup. The pinned digests are exactly what test/int already run (verified against the live containers' RepoDigests), so the next pull/up recreates byte-identical containers; the prod edit is file-only until the next release deploy. - compose.dev.yml deliberately exempt (local convenience; its node helpers follow the #236 pin). - Update procedure recorded on #229. With this, milestone M25 is complete.
Sign in to join this conversation.
No project
No Assignees
1 Participants
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: stwaidele/dorfteich#203
No description provided.