docs/documentation/platform/pki/code-signing/guides/pkcs11-cosign.mdx
In the following steps, we explore how to 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.
Sign permission on the signercosign 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.