> ## 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.

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

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](/documentation/platform/pki/code-signing/signers) created by your Product Admin
* [Active signing access](/documentation/platform/pki/code-signing/approvals#access-lifecycle) (if an approval policy is attached)
* A [machine identity](/documentation/platform/identities/machine-identities) added to the Signer
* The Infisical [PKCS#11 module](/documentation/platform/pki/code-signing/pkcs11-module) 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.
</Note>

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

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

<Warning>
  Pre-built cosign binaries (including Homebrew) do **not** include PKCS#11 support. You must build cosign from source with the `pkcs11key` build tag enabled.
</Warning>

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

  <Tab title="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/
    ```
  </Tab>
</Tabs>

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:abc123...
```

<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 are not 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.
</Note>

## 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.
</Note>

## 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.

<AccordionGroup>
  <Accordion title="'unimplemented' or 'not built with pkcs11-tool support'">
    Your cosign binary was not built with PKCS#11 support. Pre-built binaries (including Homebrew) do not include it. Rebuild from source with `CGO_ENABLED=1 go install -tags pkcs11key github.com/sigstore/cosign/v2/cmd/cosign@latest`.
  </Accordion>

  <Accordion title="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.
  </Accordion>
</AccordionGroup>

## What's Next?

<CardGroup cols={2}>
  <Card title="Sign JARs" icon="java" href="/documentation/platform/pki/guides/code-signing/jarsigner">
    Sign Java artifacts with jarsigner
  </Card>

  <Card title="Sign with GPG" icon="key" href="/documentation/platform/pki/guides/code-signing/gpg">
    Sign git commits and packages with GPG
  </Card>
</CardGroup>
