Admission enforcement¶
Render a fail-closed Kyverno verifyImages policy from the gapsign policy source
and apply it via GitOps. Kyverno then verifies signatures at admission and blocks
unsigned images before a pod starts — fully offline.
Render¶
The rendered ClusterPolicy:
- sets
failurePolicy: Fail— a webhook error blocks admission (fail closed); - emits one
verifyImagesrule per gapsign policy rule, withrequired: true, tag→digest resolution (mutateDigest/verifyDigest), andfailureActionEnforce(block) for signed rules orAudit(report) for warn rules; - embeds each signer's public key (key mode) or CA roots + identity + issuer (X.509 mode) as the attestor;
- ignores the transparency log and SCT (
rekor.ignoreTlog/ctlog.ignoreSCT) — the airgap invariant.
Apply it the GitOps way (ArgoCD, Flux, kubectl apply); gapsign only renders.
Policy source¶
# policy.yaml — the single source of truth
policies:
- match: "registry.internal/*" # first-party
signer: ci-kms
namespaces: ["prod", "payments"] # protected → Enforce (fail closed)
require: signed
- match: "sandbox.internal/*"
signer: sandbox-key
namespaces: ["dev"]
require: warn # Audit-only (report, don't block)
matchis an image-reference glob (*or aprefix/*).namespacesscopes the rule; an empty list applies cluster-wide.require: signedblocks;require: warnreports. Awarnrule with no signer is skipped (nothing to verify).
Each signer named here must have trust material in the config trust: map, or the
render fails — the rendered policy must never fail open.
Trust distribution¶
The rendered policy carries only public material (public keys, CA roots), so it is safe to commit to the GitOps repository. A mis-pointed private key, an empty key, or an invalid CA bundle are all refused at render time — nothing is written.
Verified against real Kyverno¶
The render targets Kyverno v1.12+ and is regression-tested against the real
Kyverno CLI: a gapsign-signed image is admitted and an unsigned image is blocked,
offline. Kyverno matches the keyless subject/issuer as exact strings.