Signed Releases¶
Starting with v1.2.0, every release artifact — the container image, the Helm
chart OCI artifact, and a checksums.txt bundle attached to the GitHub Release —
is cryptographically signed. Anyone can verify authenticity without contacting the
maintainer, and Kubernetes cluster operators can enforce it automatically with
Kyverno.
Releases before v1.2.0 are not signed
Signing starts at v1.2.0. There is no retroactive signing of earlier releases — do not assume coverage for tags published before it.
What is signed¶
| Artifact | What it is |
|---|---|
| Container image | The multi-architecture (amd64/arm64) manifest list pushed to ghcr.io/przemekhys/homeassistant-operator |
| Helm chart | The chart packaged and pushed as an OCI artifact to oci://ghcr.io/przemekhys/charts/homeassistant-operator |
checksums.txt |
A text file listing the image and chart digests for the release, attached to the GitHub Release along with its own signature |
All three are signed keyless, using Sigstore/cosign,
bound to this repository's own GitHub Actions release workflow identity. There is
no long-lived signing key anywhere — the maintainer never generates, stores, or
rotates one. Each signature is backed by a short-lived certificate from Sigstore's
Fulcio and a public transparency-log entry in Rekor, which is what the verification
commands below check against.
Verify the container image¶
IMAGE=ghcr.io/przemekhys/homeassistant-operator
DIGEST=$(crane digest "$IMAGE:v1.2.0") # or read it from checksums.txt
cosign verify \
--certificate-identity-regexp \
'https://github.com/przemekhys/homeassistant-operator/\.github/workflows/release\.yml@refs/tags/.*' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
"$IMAGE@$DIGEST"
To verify the whole checksums.txt bundle instead (covers the image and chart
digests transitively in one check):
gh release download v1.2.0 -p 'checksums.txt*'
cosign verify-blob \
--bundle checksums.txt.sigstore.json \
--certificate-identity-regexp \
'https://github.com/przemekhys/homeassistant-operator/\.github/workflows/release\.yml@refs/tags/.*' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
checksums.txt
Local reproduction
hack/verify-signatures.sh
runs these same checks (image, chart, checksums bundle) in one command:
hack/verify-signatures.sh v1.2.0.
Verify with Kyverno¶
Cluster operators can enforce this automatically at admission time with Kyverno, so an unsigned or tampered image is rejected before it ever runs.
Minimum Kyverno version
This sample was validated against the classic kyverno.io/v1 ClusterPolicy API,
supported since Kyverno 1.9. It intentionally does not use the newer
CEL-based ImageValidatingPolicy (Kyverno 1.14+) so it works on a broader range
of clusters.
The full sample lives at
hack/kyverno/verify-homeassistant-operator-image.yaml:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: verify-homeassistant-operator-image
annotations:
policies.kyverno.io/title: Verify homeassistant-operator image signature
policies.kyverno.io/category: Software Supply Chain
policies.kyverno.io/severity: high
policies.kyverno.io/description: >-
Requires that any ghcr.io/przemekhys/homeassistant-operator image was signed
by the project's own GitHub Actions release workflow (keyless Sigstore
signing), rejecting unsigned or tampered images at admission time.
spec:
validationFailureAction: Enforce
webhookTimeoutSeconds: 30
rules:
- name: verify-signature
match:
any:
- resources:
kinds:
- Pod
verifyImages:
- imageReferences:
- "ghcr.io/przemekhys/homeassistant-operator*"
attestors:
- count: 1
entries:
- keyless:
issuer: "https://token.actions.githubusercontent.com"
subject: "https://github.com/przemekhys/homeassistant-operator/.github/workflows/release.yml@refs/tags/*"
rekor:
url: https://rekor.sigstore.dev
Apply it, then try both a genuine and a tampered image:
kubectl apply -f hack/kyverno/verify-homeassistant-operator-image.yaml
# Genuine image: admitted.
kubectl run ha-genuine --image=ghcr.io/przemekhys/homeassistant-operator:v1.2.0 --restart=Never
# Tampered/re-tagged image: rejected by the admission webhook.
kubectl run ha-tampered --image=<attacker-controlled-retag> --restart=Never
Roll out with Audit first
The sample defaults to validationFailureAction: Enforce. For a first adoption
in an existing cluster, consider switching it to Audit (log-only) to confirm
it matches as expected before enforcing rejections.
Keep this policy in sync if the signing identity changes
The issuer/subject values above are pinned to this repository's release
workflow file path and tag-ref pattern. If that workflow is ever renamed or
moved, both this doc page and the policy file must be updated together in the
same change — otherwise the policy would silently stop matching new releases
instead of failing loudly.
Verify the Helm chart¶
cosign verify \
--certificate-identity-regexp \
'https://github.com/przemekhys/homeassistant-operator/\.github/workflows/release\.yml@refs/tags/.*' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
ghcr.io/przemekhys/charts/homeassistant-operator@<chart-digest>
This check is independent of Kyverno and of any cluster — it works anywhere
cosign can reach the OCI registry, for example as a preflight step in a GitOps
pipeline before helm install/upgrade ever runs.