docs/documentation/platform/pki/guides/code-signing/cosign.mdx
Sign OCI container images using cosign (from the Sigstore project) with the Infisical PKCS#11 module. Container image signing is a critical part of securing software supply chains — it lets you verify that images in your registry were built by trusted pipelines.
cosign v2.0 or later, built with PKCS#11 support (see below)Verify PKCS#11 support is available:
cosign pkcs11-tool --help
If you see This cosign was not built with pkcs11-tool support!, you need to rebuild with the pkcs11key tag.
First, list the available tokens and key URIs to find the correct PKCS#11 URI for your signer:
cosign pkcs11-tool list-tokens \
--module-path /usr/local/lib/libinfisical-pkcs11.so
cosign pkcs11-tool list-keys-uris \
--module-path /usr/local/lib/libinfisical-pkcs11.so \
--slot-id 0
Sign an image in your registry using the PKCS#11 key:
export COSIGN_PKCS11_MODULE_PATH="/usr/local/lib/libinfisical-pkcs11.so"
cosign sign \
--key "pkcs11:token=release-signer;object=release-signer;type=private" \
--tlog-upload=false \
myregistry.example.com/myapp@sha256:abc123...
Verify a signed image using the public key or certificate from the signer:
# Extract the certificate and public key from PKCS#11
pkcs11-tool --module /usr/local/lib/libinfisical-pkcs11.so \
--slot 0 --read-object --type cert --label release-signer \
--output-file signer-cert.der
openssl x509 -inform DER -in signer-cert.der -pubkey -noout > pubkey.pem
# Verify with cosign
cosign verify \
--key pubkey.pem \
--insecure-ignore-tlog \
myregistry.example.com/myapp:v1.0.0
Example for a container build pipeline:
export INFISICAL_UNIVERSAL_AUTH_CLIENT_ID="${INFISICAL_CLIENT_ID}"
export INFISICAL_UNIVERSAL_AUTH_CLIENT_SECRET="${INFISICAL_CLIENT_SECRET}"
export INFISICAL_PKCS11_CONFIG="/path/to/pkcs11.conf"
export COSIGN_PKCS11_MODULE_PATH="/usr/local/lib/libinfisical-pkcs11.so"
# Build and push the image
docker build -t myregistry.example.com/myapp:${CI_COMMIT_TAG} .
docker push myregistry.example.com/myapp:${CI_COMMIT_TAG}
# Get the image digest
DIGEST=$(docker inspect --format='{{index .RepoDigests 0}}' myregistry.example.com/myapp:${CI_COMMIT_TAG})
# Sign the image by digest
cosign sign \
--key "pkcs11:token=release-signer;object=release-signer;type=private" \
--tlog-upload=false \
"${DIGEST}"
Combine cosign verification with a Kubernetes admission controller (e.g., Kyverno or Connaisseur) to enforce that only signed images are deployed:
# Kyverno example policy
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: verify-image-signatures
spec:
validationFailureAction: Enforce
rules:
- name: check-image-signature
match:
any:
- resources:
kinds:
- Pod
verifyImages:
- imageReferences:
- "myregistry.example.com/myapp:*"
attestors:
- entries:
- keys:
publicKeys: |-
-----BEGIN PUBLIC KEY-----
<your-signer-public-key>
-----END PUBLIC KEY-----
For any issue, enable debug logging in your config file ("log_level": "debug", "log_file": "/tmp/infisical-pkcs11.log") to get detailed output.