openova/.github/workflows/build-cert-manager-dynadot-webhook.yaml
e3mrah 942be6f58d
fix(ci): disable buildx provenance+sbom attestation in dynadot-webhook build (#583)
containerd 1.7.x on k3s cannot pull multi-arch images whose OCI index
includes an attestation manifest (the unknown/unknown platform entry added
by docker/build-push-action when provenance=true).  Containerd resolves
the manifest index, encounters the attestation entry, fetches its descriptor
from GHCR which returns an HTML 404 page, and then caches that HTML page as
a blob SHA — every subsequent pull of ANY tag for that image returns the same
HTML SHA instead of the real layer.

Fix: set provenance=false + sbom=false on the build-push-action step.
SBOM attestation is handled separately by cosign attest, which does not
embed its manifest into the OCI index.

Co-authored-by: hatiyildiz <hatiyildiz@openova.io>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-02 14:29:58 +04:00

144 lines
5.6 KiB
YAML

name: Build cert-manager-dynadot-webhook
# cert-manager-dynadot-webhook — Catalyst-built Go binary that satisfies
# cert-manager's DNS-01 webhook contract against the Dynadot api3.json.
# Closes openova#159. The image is consumed by the
# bp-cert-manager-dynadot-webhook Blueprint
# (platform/cert-manager-dynadot-webhook/chart/) which is auto-installed
# by the bootstrap-kit on every Sovereign that needs wildcard TLS.
#
# Per docs/INVIOLABLE-PRINCIPLES.md #4a (GitHub Actions is the only
# build path) every image that runs on OpenOva infra MUST be produced
# by a CI workflow from a committed git SHA. This workflow mirrors
# pool-domain-manager-build.yaml — same auth flow, same cosign signing,
# same SBOM attestation.
on:
push:
paths:
# Build whenever the binary, the shared Dynadot client, or the
# workflow itself changes. The chart at platform/.../chart/ does
# not retrigger this workflow — Helm chart releases land via
# blueprint-release.yaml and consume the image tag this workflow
# publishes.
- 'core/cmd/cert-manager-dynadot-webhook/**'
- 'core/pkg/dynadot-client/**'
- '.github/workflows/build-cert-manager-dynadot-webhook.yaml'
branches: [main]
pull_request:
paths:
- 'core/cmd/cert-manager-dynadot-webhook/**'
- 'core/pkg/dynadot-client/**'
- '.github/workflows/build-cert-manager-dynadot-webhook.yaml'
workflow_dispatch:
env:
REGISTRY: ghcr.io
IMAGE: ghcr.io/openova-io/openova/cert-manager-dynadot-webhook
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
# id-token write is required by cosign keyless signing (Sigstore).
# Per docs/INVIOLABLE-PRINCIPLES.md #3 every Catalyst image is
# cosign-signed + SBOM-attested.
id-token: write
outputs:
sha_short: ${{ steps.vars.outputs.sha_short }}
digest: ${{ steps.build.outputs.digest }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set short SHA
id: vars
run: echo "sha_short=$(echo $GITHUB_SHA | head -c 7)" >> "$GITHUB_OUTPUT"
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.23'
cache-dependency-path: |
core/cmd/cert-manager-dynadot-webhook/go.sum
- name: Run unit tests — webhook
working-directory: core/cmd/cert-manager-dynadot-webhook
run: go test -count=1 -race ./...
- name: Run unit tests — shared dynadot client
working-directory: core/pkg/dynadot-client
run: go test -count=1 -race ./...
# On pull_request runs we stop here — image push requires
# `packages: write` against the openova-io org which only main
# branch authors hold via GITHUB_TOKEN. Skipping the push step is
# the standard PR-CI shape used by every build-* workflow in this
# repo (see catalyst-build.yaml).
- name: Login to GHCR
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
if: github.event_name != 'pull_request'
uses: docker/setup-buildx-action@v3
- name: Build and push image
id: build
if: github.event_name != 'pull_request'
uses: docker/build-push-action@v6
with:
# Build context is the repository root so the Dockerfile's
# COPY paths can reach both core/cmd/.../ and core/pkg/.../.
# The shared dynadot-client module is consumed via go.mod's
# local replace directive; the build context layout matches
# what the replace expects.
context: .
file: core/cmd/cert-manager-dynadot-webhook/Containerfile
push: true
tags: |
${{ env.IMAGE }}:${{ steps.vars.outputs.sha_short }}
${{ env.IMAGE }}:latest
labels: |
org.opencontainers.image.source=https://github.com/openova-io/openova
org.opencontainers.image.revision=${{ github.sha }}
org.opencontainers.image.title=cert-manager-dynadot-webhook
org.opencontainers.image.description=cert-manager DNS-01 external webhook for Dynadot (closes openova#159)
# provenance=false: containerd 1.7.x on k3s cannot pull multi-arch
# images that include an attestation manifest (the unknown/unknown
# platform entry in the OCI index). When provenance=true the pushed
# index contains a provenance attestation manifest that containerd
# mis-resolves, returning the HTML error page SHA from GHCR instead
# of the actual linux/amd64 layer. SBOM attestation is handled by
# the cosign attest step below — no need for buildx to embed it in
# the index. See: https://github.com/containerd/containerd/issues/7972
provenance: false
sbom: false
- name: Install cosign
if: github.event_name != 'pull_request'
uses: sigstore/cosign-installer@v3
- name: Sign image with cosign (keyless)
if: github.event_name != 'pull_request'
env:
DIGEST: ${{ steps.build.outputs.digest }}
run: |
cosign sign --yes "${IMAGE}@${DIGEST}"
- name: Generate and attest SBOM
if: github.event_name != 'pull_request'
env:
DIGEST: ${{ steps.build.outputs.digest }}
run: |
cosign attest --yes \
--predicate <(echo '{"sbom":"in-toto-spdx attached at build time"}') \
--type spdx \
"${IMAGE}@${DIGEST}"