Verify Component Versions in the Controller
On this page
- Goal
- You’ll end up with
- Prerequisites
- Steps
- How verification protects component references
- Troubleshooting
- Symptom: “signature verification failed for signature …”
- Symptom: “signature … not found in component”
- Symptom: “digest mismatch … for component version …:…”
- Symptom: “not safely digestible” event
- Symptom: “failed to get Secret” or “secret does not contain supported keys”
- Symptom: “missing public key, required for plain RSA signatures”
- Symptom: the
Componentbecomes ready without verifying anything
- Next Steps
- Related Documentation
Goal
Configure the OCM Kubernetes controller to automatically verify component version signatures before reconciling resources.
You’ll end up with
- A Secret holding an OCM configuration that requests signature verification
- A
Componentresource that reconciles only if the signature verifies
Estimated time: ~5 minutes
Prerequisites
- Controller environment set up
- A signed component version in a local CTF archive
- The public key file at
/tmp/keys/public-key.pem(from Generate Signing Keys) - Access to an OCI registry (e.g., ghcr.io)
Steps
Transfer the signed component version to the registry
Push your signed component version from the local CTF archive to a remote OCI registry:
ocm transfer cv /tmp/helloworld/transport-archive//github.com/acme.org/helloworld:1.0.0 ghcr.io/<your-namespace>Verify the upload:
ocm get cv ghcr.io/<your-namespace>//github.com/acme.org/helloworld:1.0.0Expected output
COMPONENT │ VERSION │ PROVIDER ───────────────────────────────────┼─────────┼────────────── github.com/acme.org/helloworld │ 1.0.0 │ acme.orgPrepare the verification configuration
The controller takes verification from the central OCM configuration, the same
signing.config.ocm.softwareandcredentials.config.ocm.softwareentries the CLI uses. Two entries are needed: one naming the signature to verify, and one supplying the public key to verify it with.cat > ocmconfig.yaml <<EOF type: generic.config.ocm.software/v1 configurations: - type: signing.config.ocm.software/v1alpha1 signature: default verifier: type: RSASigningConfiguration/v1alpha1 - type: credentials.config.ocm.software consumers: - identity: type: RSA/v1alpha1 algorithm: RSASSA-PSS signature: default credentials: - type: Credentials/v1 properties: public_key_pem: | $(sed 's/^/ /' /tmp/keys/public-key.pem) EOFThe public key is embedded as PEM, indented under
public_key_pem. No base64 encoding is needed.NoteOnly an entry that names a
signaturerequests verification of that signature. An entry without one merely supplies the verifier that named entries fall back to, so it does not turn verification on by itself.The
verifierfield is optional and defaults to RSASSA-PSS. Set it to select a different verification handler, for exampleSigstoreVerificationConfiguration/v1alpha1.Store the configuration in a Secret. The controller reads it from the
.ocmconfigkey:kubectl create secret generic signing-verification-secret --from-file=.ocmconfig=ocmconfig.yamlCreate the
RepositoryresourceCreate and apply a
Repositorythat points to your OCI registry:cat <<EOF > repository.yaml apiVersion: delivery.ocm.software/v1alpha1 kind: Repository metadata: name: helloworld-repository spec: repositorySpec: baseUrl: ghcr.io/<your-namespace> type: OCIRegistry interval: 10m EOFkubectl apply -f repository.yamlCreate the
Componentresource with verificationCreate and apply a
Componentthat references the repository and the configuration Secret:cat <<EOF > component.yaml apiVersion: delivery.ocm.software/v1alpha1 kind: Component metadata: name: helloworld-component spec: component: github.com/acme.org/helloworld repositoryRef: name: helloworld-repository semver: ">=1.0.0" interval: 10m ocmConfig: - apiVersion: v1 kind: Secret name: signing-verification-secret EOFkubectl apply -f component.yamlNoteThe Secret is looked up in the
Component’s own namespace unless the reference sets anamespacefield. The configuration also propagates: aResourceorDeployerthat references thisComponentinherits it and verifies the same signature.Verify the
Componentis readyCheck that the
Componentresource reconciles successfully with verification:kubectl get component helloworld-component -o wideExpected output
NAME READY AGE helloworld-component Applied version 1.0.0 98sTo confirm the signature was actually verified, check the controller logs:
kubectl logs -n ocm-k8s-toolkit-system deploy/ocm-k8s-toolkit-controller-manager | grep "verifying signature"Expected output
{"level":"info","ts":"2026-04-28T15:58:14Z","msg":"verifying signature","component":"github.com/acme.org/helloworld","version":"1.0.0"}If verification fails, the
Componentwill not become ready and an error condition will be set.Check for failure
kubectl get component helloworld-component -o wideNAME READY AGE helloworld-component signature verification failed for signature default: missing public key, required for plain RSA signatures 7s
How verification protects component references
Component references can carry digests. When the controller resolves a reference that includes a digest, it computes a fresh digest of the referenced component and compares it against the recorded value. If they do not match, reconciliation fails.
Reference digests are computed and added automatically by ocm add cv. The ocm sign cv
command checks that the component version is safely digestible and warns if any reference or
resource digests are missing.
Troubleshooting
When verification fails, the Component resource’s Ready condition is set to False with the
error message. Check it with:
kubectl get component <name> -o jsonpath='{.status.conditions[?(@.type=="Ready")].message}'Symptom: “signature verification failed for signature …”
Cause: The verification credential (public key or certificate) does not match the private key used to sign the component version.
Fix: Ensure you are using the correct verification credential that corresponds to the private key used during signing. Verify the signature name matches by inspecting the component version:
ocm get cv ghcr.io/<your-namespace>//github.com/acme.org/helloworld:1.0.0 -o yaml | grep -A 5 "signatures:"Symptom: “signature … not found in component”
Cause: The component version does not contain a signature with the name given in the
signing.config.ocm.software entry.
Fix: Check which signatures exist on the component version and ensure the signature field
of your signing configuration entry matches:
ocm get cv ghcr.io/<your-namespace>//github.com/acme.org/helloworld:1.0.0 -o yaml | grep -A 5 "signatures:"Symptom: “digest mismatch … for component version …:…”
Cause: A parent component version includes a reference to another component version with a recorded digest. When the controller resolves that reference, the actual content does not match the recorded digest. This typically means the referenced component was modified or re-published after the parent recorded its digest.
Fix: Inspect the parent component version’s references to identify the digest mismatch:
ocm get cv ghcr.io/<your-namespace>//github.com/acme.org/parent-component:1.0.0 -o yamlLook at the componentReferences: section and their digest fields. To resolve, rebuild the
parent component version with correct reference digests, re-sign it, and then publish it.
Symptom: “not safely digestible” event
The Component becomes Ready, but a Kubernetes event with severity error is emitted containing
“not safely digestible”.
Cause: The component version does not satisfy OCM’s digest consistency rules:
- Component references must have complete digests (hash algorithm, normalisation algorithm, value)
- Resources with access must have complete digests
- Resources without access must not carry a digest
Without consistent digests, signature verification is skipped because the normalised form cannot be reliably computed.
Fix: Rebuild the component version with consistent digests, re-sign it, and then publish it.
The ocm sign cv command warns when a component version is not safely digestible.
Symptom: “failed to get Secret” or “secret does not contain supported keys”
Cause: The Secret named in ocmConfig does not exist in the Component’s namespace, or it
does not carry the configuration under the .ocmconfig key.
Fix: Check that the Secret exists and holds an .ocmconfig entry:
kubectl get secret signing-verification-secret -n <component-namespace> -o jsonpath='{.data.\.ocmconfig}' | base64 -dSymptom: “missing public key, required for plain RSA signatures”
Cause: No credentials entry matched the consumer identity the verifier asked for, so
verification ran without a key. Consumer identities are matched exactly, and the identity is built
from the signature being verified: its name and its algorithm. An entry for signature: default
therefore does not serve a signature named prod, and an RSASSA-PSS entry does not serve an
RSASSA-PKCS1-V1_5 signature.
Fix: Check the signature’s name and algorithm on the component version, then make the consumer identity match exactly:
ocm get cv ghcr.io/<your-namespace>//github.com/acme.org/helloworld:1.0.0 -o yaml | grep -A 8 "signatures:"- type: credentials.config.ocm.software
consumers:
- identity:
type: RSA/v1alpha1
algorithm: RSASSA-PSS
signature: defaultSymptom: the Component becomes ready without verifying anything
Cause: The signing configuration contains no entry naming a signature. An entry without a
signature field only supplies the verifier, so nothing is requested.
Fix: Add the signature name to the entry:
- type: signing.config.ocm.software/v1alpha1
signature: defaultNext Steps
- Getting Started: Deploy Helm Charts - Deploy resources from verified component versions
Related Documentation
- Concept: Signing and Verification - Understand how OCM signing works
- How-To: Verify Component Versions (CLI) - Verify signatures using the CLI
- How-To: Configure Credentials for OCM Controllers - Set up registry credentials for the controller