> ## Documentation Index
> Fetch the complete documentation index at: https://infisical.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Sign container images with cosign

> Use the Infisical PKCS#11 module to sign OCI container images with Sigstore cosign.

> [!NOTE]
>
> This guide assumes your Product Admin has already created a [Signer](https://infisical.com/docs/documentation/platform/pki/code-signing/signers) and assigned you to it. If the signer has an approval policy, you'll also need [active access](https://infisical.com/docs/documentation/platform/pki/code-signing/approvals.md#access-lifecycle) before signing.

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.

## Prerequisites

* A [Signer](https://infisical.com/docs/documentation/platform/pki/code-signing/signers) created by your Product Admin
* [Active signing access](https://infisical.com/docs/documentation/platform/pki/code-signing/approvals.md#access-lifecycle) (if an approval policy is attached)
* A [machine identity](https://infisical.com/docs/documentation/platform/identities/machine-identities.md) added to the Signer
* The Infisical [PKCS#11 module](https://infisical.com/docs/documentation/platform/pki/code-signing/pkcs11-module.md) installed and configured
* `cosign` v2.0 or later, **built with PKCS#11 support** (see below)
* Write access to an OCI-compatible container registry

## Step 1: Set up authentication

Configure the Infisical PKCS#11 module with your machine identity credentials. Without this, the signing commands below fail with an auth error.

Create `/etc/infisical/pkcs11.conf` (or set `INFISICAL_CONFIG` to point elsewhere):

```yaml theme={"dark"}
auth:
  method: universal-auth
  universal_auth:
    client_id: "<machine-identity-client-id>"
    client_secret: "<machine-identity-client-secret>"

signer:
  id: "<signer-id>"
```

You can also pass the credentials via environment variables:

```bash theme={"dark"}
export INFISICAL_UNIVERSAL_AUTH_CLIENT_ID="<machine-identity-client-id>"
export INFISICAL_UNIVERSAL_AUTH_CLIENT_SECRET="<machine-identity-client-secret>"
```

> [!NOTE]
>
> Environment variables override values from the config file.

For the full set of options, see the [PKCS#11 module configuration reference](https://infisical.com/docs/documentation/platform/pki/code-signing/pkcs11-module.md#configuration).

## Step 2: Install cosign with PKCS#11 support

> [!WARNING]
>
> Pre-built cosign binaries (including Homebrew) don't include PKCS#11 support. You must build cosign from source with the `pkcs11key` build tag enabled.

> [tab] Go install (recommended)
>
> ```bash theme={"dark"}
> CGO_ENABLED=1 go install -tags pkcs11key github.com/sigstore/cosign/v2/cmd/cosign@latest
> ```

> [tab] Build from source
>
> ```bash theme={"dark"}
> git clone https://github.com/sigstore/cosign.git
> cd cosign
> CGO_ENABLED=1 go build -tags pkcs11key -o cosign ./cmd/cosign
> sudo mv cosign /usr/local/bin/
> ```

Verify PKCS#11 support is available:

```bash theme={"dark"}
cosign pkcs11-tool --help
```

If you see `This cosign was not built with pkcs11-tool support!`, you need to rebuild with the `pkcs11key` tag.

## Step 3: Sign a container image

First, list the available tokens and key URIs to find the correct PKCS#11 URI for your signer:

```bash theme={"dark"}
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:

```bash theme={"dark"}
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:<digest>
```

> [!NOTE]
>
> * The `--key` flag uses a PKCS#11 URI. The `token` must match your signer's token label and `object` must match the key label. Use `cosign pkcs11-tool list-keys-uris` to find the exact URI.
> * Set `COSIGN_PKCS11_MODULE_PATH` to the absolute path of the Infisical PKCS#11 shared library.
> * Use `--tlog-upload=false` if you aren't using a Sigstore transparency log.
> * cosign pushes the signature to the same registry alongside the image.
> * You may need to authenticate to your container registry first (e.g., `docker login` or `cosign login`).
> * Prefer signing by digest (`@sha256:...`) rather than tag for reproducible builds.

## Step 4: Verify the signature

Verify a signed image using the public key or certificate from the signer:

```bash theme={"dark"}
# 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
```

> [!NOTE]
>
> Use `--insecure-ignore-tlog` if you signed without uploading to a transparency log. In production, configure a transparency log for auditability.

## CI/CD integration

Example for a container build pipeline:

```bash theme={"dark"}
export INFISICAL_UNIVERSAL_AUTH_CLIENT_ID="${INFISICAL_CLIENT_ID}"
export INFISICAL_UNIVERSAL_AUTH_CLIENT_SECRET="${INFISICAL_CLIENT_SECRET}"
export INFISICAL_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}"
```

### Kubernetes admission control

Combine cosign verification with a Kubernetes admission controller (e.g., [Kyverno](https://kyverno.io/) or [Connaisseur](https://github.com/sse-secure-systems/connaisseur)) to enforce that only signed images are deployed:

```yaml theme={"dark"}
# 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-----
```

## Troubleshooting

For any issue, enable debug logging in your config file (`"log_level": "debug"`, `"log_file": "/tmp/infisical-pkcs11.log"`) to get detailed output.

> [accordion] 'unimplemented' or 'not built with pkcs11-tool support'
>
> Your cosign binary wasn't built with PKCS#11 support. Pre-built binaries (including Homebrew) don't include it. Rebuild from source with `CGO_ENABLED=1 go install -tags pkcs11key github.com/sigstore/cosign/v2/cmd/cosign@latest`.

> [accordion] Error getting signer or key not found
>
> Ensure the PKCS#11 URI includes both `token` and `object` fields matching your signer name, and that `COSIGN_PKCS11_MODULE_PATH` is set to the absolute path of the module. Use `cosign pkcs11-tool list-keys-uris` to find the correct URI.

## What's next?

> [card] [Sign JARs](https://infisical.com/docs/documentation/platform/pki/guides/code-signing/jarsigner.md)
>
> Sign Java artifacts with jarsigner

> [card] [Sign with GPG](https://infisical.com/docs/documentation/platform/pki/guides/code-signing/gpg.md)
>
> Sign git commits and packages with GPG


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.