← Standards

container-publishing

Container images shipped from estate repos use the dunn.dev/pipeline container-image catalog component. The component handles buildah + cosign keyless signing via GitLab OIDC + (optionally) container-sbom

  • multi-arch manifests. Consumers don't reinvent the build script.

UBI is the runtime base mandate. Final images ship only the application binary or app payload on top of a Red Hat UBI 9 base (ci-runtime-go for Go static binaries, ci-runtime-node for Node SSR apps). No shells, no debuggers, no Alpine. Rationale matches the public-sector reference's least-functionality-container standard: minimal attack surface, RH CVE patching cadence, redistributable provenance.

What this standard checks

Generated from ./check.sh, in the order the check runs them.

assertion target pattern or value note
applies at all, or stops the run *Containerfile* or *Dockerfile* or .gitlab-ci.yml:gitlab\.com/dunn\.dev/pipeline/container-image@ a repo that builds no image has nothing this contract can be satisfied against, and buying a green run with a hand-written waiver per assertion says the wrong thing; governs repos that build and publish container images
satisfied elsewhere, stops the run .gitlab-ci.yml gitlab\.com/dunn\.dev/(cairn|immutable)/pipeline/[a-z][a-z0-9-]*@v?[0-9]+(\.[0-9]+){0,2}\> a bootc composer would have to buy its PASS with a false waiver, having no way to satisfy the two assertions below; bootc OS image composed via the dunn.dev/cairn or dunn.dev/immutable pipeline catalog, so the app container-image + UBI runtime contract does not govern this repo
file matches .gitlab-ci.yml ^[^#]*gitlab\.com/dunn\.dev/pipeline/container-image@[0-9]+(\.[0-9]+){0,2}([^0-9A-Za-z.-]|$) a hand-rolled buildah job skips the signing and provenance steps the component carries
some file matches Containerfile ^FROM[[:space:]]+registry\.gitlab\.com/dunn\.dev/pipeline/ci-runtime-(go|node):[0-9]+\.[0-9]+\.[0-9]+ the base image is the one layer nothing downstream patches, so an off-estate or floating base ships unowned CVEs; any file of that name in the repo, or matching *Containerfile* or *Dockerfile*; build dirs skipped

The bootc structural exemption (below) runs first and can stop the run before the container-image and runtime-base assertions evaluate.

How to satisfy

Consumer .gitlab-ci.yml:

include:
  - component: gitlab.com/dunn.dev/pipeline/container-image@X.Y.Z
    inputs:
      containerfile: Containerfile
      image_tag: app           # or: publish_at_root: true
      tag_pipeline: true
  - component: gitlab.com/dunn.dev/pipeline/container-sbom@X.Y.Z
    inputs:
      container_image: "${CI_REGISTRY_IMAGE}/app:${CI_COMMIT_TAG}"
      package_name: app

Consumer Containerfile (runtime stage):

FROM registry.gitlab.com/dunn.dev/pipeline/ci-runtime-go:X.Y.Z
COPY dist/app /app
ENTRYPOINT ["/app"]

Bootc OS-image composition (structural exemption)

This standard's mandate is APP containers. Repos that compose bootc OS images (dunn.dev/cairn/base, dunn.dev/immutable/basef, dunn.dev/immutable/carmine, dunn.dev/immutable/crimson) genuinely ship container images too, but through the dunn.dev/cairn/pipeline or dunn.dev/immutable/pipeline catalogs' own components (base-build-scratch, instance, supply-chain; the set varies per consumer), with Containerfiles FROM ${BASE_BOOTC_IMAGE}, ${REPOS_IMAGE}, or scratch. They cannot satisfy a buildah+UBI app contract by construction, and "doesn't ship containers" would be a false waiver against a repo that ships real, signed images every day.

check.sh recognizes this structurally, against a pinned catalog include (exact tag or major/major.minor range; a floating ref or a commit SHA does not qualify). No .deviations.yaml entry is needed or consulted for this. It is a computed fact, not a declared exception. See check_alternate_contract in ../_lib.sh (v1.6).

dunn.dev/pipeline's own container-publishing gap (its ci-runtime-go/ci-runtime-node Containerfiles are FROM raw UBI: it's the origin of those bases, not a bootc-catalog consumer) is a different shape: a single project's self-reference, same as the pipeline standard's own self-reference deviation. That belongs in dunn.dev/pipeline's own .deviations.yaml, not in this structural check.

Signing invariants

A bootc build under dunn.dev/cairn/pipeline carries invariants this standard names but does not check: every ref builds signed, using the canonical key on protected refs and an ephemeral per-pipeline key everywhere else, never skipped; the baked cert always matches the key that signed the image; and a green merge-lane pipeline means the image compiled, signed, booted, and loaded its kernel modules under that one consistent key.

The enforcement lives in the catalog, not in a reference check. cairn/pipeline's mok-fail-closed job is a negative control that runs the exact signing preamble every consumer includes and asserts it fails closed with the signing key absent and succeeds with it present.

Deviations

A deviation is for a repo that does build images and does not meet one; a repo that ships none reports N/A instead. Repos that ship a single image at the registry repo root use publish_at_root: true (e.g. dunn.dev/caddy-estate).

fetch as markdown
curl https://reference.dunn.dev/standards/container-publishing/index.md
check conformance
just -f ~/gitlab.com/dunn.dev/reference/justfile check