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).
curl https://reference.dunn.dev/standards/container-publishing/index.md
check conformance
just -f ~/gitlab.com/dunn.dev/reference/justfile check