# Image Signing in Harbor: Supported Methods and Implementation Guide

> Explore Harbor's image signing methods including Docker Content Trust Notary v1 Cosign and Notation Notary v2 Learn how to implement secure image verification and policy enforcement.

- Repository: [Harbor/harbor](https://github.com/goharbor/harbor)
- Tags: how-to-guide
- Published: 2026-04-09

---

**Harbor supports three distinct image signing methods—Docker Content Trust (Notary v1), Cosign (Sigstore), and Notation (Notary v2)—each storing cryptographic signatures as accessories and enforcing deployment policies through dedicated middleware.**

Harbor provides enterprise-grade container provenance verification through multiple signing mechanisms that integrate directly with its artifact storage and policy engine. Whether you operate legacy Docker environments or modern cloud-native pipelines, Harbor's unified accessory model treats signatures as attached metadata, enabling fine-grained control over which images may be pulled or deployed.

## Docker Content Trust (Notary v1)

Docker Content Trust represents Harbor's original signing mechanism, leveraging the Notary v1 protocol to sign image digests via the `docker trust` CLI.

### Architecture and Policy Enforcement

When **content-trust** is enabled on a Harbor project through the `enable_content_trust` metadata flag, the platform intercepts pull requests to verify Notary signatures before allowing access. The Notary server stores signatures in a dedicated repository following the `notary-<project>` naming convention, which Harbor reads via the Notary client library according to the source code in [`src/server/middleware/contenttrust/contentrust.go`](https://github.com/goharbor/harbor/blob/main/src/server/middleware/contenttrust/contentrust.go).

If a requested image lacks a valid Notary signature while the policy is active, Harbor returns the error `The image is not signed by notary.` and blocks the pull operation.

### Source Code Implementation

The policy enforcement logic resides in [`src/server/middleware/contenttrust/contentrust.go`](https://github.com/goharbor/harbor/blob/main/src/server/middleware/contenttrust/contentrust.go), which validates signatures against the Notary server before permitting access. Project-level configuration is managed through metadata constants defined in [`src/pkg/project/models/pro_meta.go`](https://github.com/goharbor/harbor/blob/main/src/pkg/project/models/pro_meta.go), specifically `ProMetaEnableContentTrustCosign` (which covers both Notary and Cosign enablement flags).

### Signing Images with Docker Content Trust

Enable content trust for a project via the Harbor API, then use the Docker CLI to automatically sign images during push operations:

```bash

# Enable content-trust policy via API

curl -u admin:Harbor12345 -X PUT \
  "https://harbor.local/api/v2.0/projects/1" \
  -H "Content-Type: application/json" \
  -d '{"metadata":{"enable_content_trust":"true"}}'

# Sign and push with Docker Content Trust

export DOCKER_CONTENT_TRUST=1
docker push myrepo/myimage:1.0

# Pull requests will be rejected if unsigned

docker pull myrepo/myimage:1.0

```

## Cosign (Sigstore)

Cosign provides modern, keyless (or key-based) signing through the Sigstore project, storing signatures as OCI artifacts with the media type `application/vnd.dev.cosign.artifact.sig.v1+json`.

### Middleware and Signature Discovery

Harbor's Cosign support is implemented in [`src/server/middleware/cosign/cosign.go`](https://github.com/goharbor/harbor/blob/main/src/server/middleware/cosign/cosign.go), where incoming `GET /v2/.../manifests/...` requests are intercepted. The middleware uses a regular expression pattern (`cosignRe`) to identify signature URLs and retrieves the corresponding OCI artifact. When content-trust policies require Cosign verification (`enable_content_trust_cosign`), the system checks for the presence of these signatures and returns `The image is not signed by cosign.` if validation fails.

### Accessory Model and Storage

Cosign signatures are stored as accessories with the type `signature.cosign`, defined in [`src/pkg/accessory/model/cosign/cosign.go`](https://github.com/goharbor/harbor/blob/main/src/pkg/accessory/model/cosign/cosign.go). This accessory type links the cryptographic signature to the parent image artifact, allowing Harbor to display verification status in the web interface alongside other metadata.

### Keyless and Key-Based Signing

Generate a key pair and sign images directly against the Harbor registry:

```bash

# Generate key pair

cosign generate-key-pair

# Sign the image

cosign sign --key cosign.key myrepo/myimage:1.0

# Verify locally or through Harbor's enforcement

cosign verify --key cosign.pub myrepo/myimage:1.0

```

Harbor also exposes a UI button to "Sign with Cosign" when the Cosign deployment security policy is enabled, as referenced in the Robot-Case test suite.

## Notation (Notary v2)

Notation represents the OCI-standard signing format (Notary v2) that operates with the `notation` CLI, providing an evolution of the Docker Content Trust model.

### OCI-Native Signature Format

Unlike the legacy Notary v1 architecture, Notation implements the OCI image specification for signatures, making it interoperable with standards-compliant registries. Harbor treats Notation signatures as accessories of type `signature.notation`, distinguishing them from Cosign accessories while applying similar policy enforcement logic.

### Implementation in Harbor

The Notation accessory model is defined in [`src/pkg/accessory/model/notation/notation.go`](https://github.com/goharbor/harbor/blob/main/src/pkg/accessory/model/notation/notation.go), which specifies the `signature.notation` type constant. The content-trust middleware in [`src/server/middleware/contenttrust/contentrust.go`](https://github.com/goharbor/harbor/blob/main/src/server/middleware/contenttrust/contentrust.go) includes specific handling for Notation verification, checking for the existence of `signature.notation` accessories when policies require Notation validation (`enable_content_trust_notation`).

The Harbor portal displays Notation verification badges through constants defined in [`src/portal/src/app/base/project/repository/artifact/artifact.ts`](https://github.com/goharbor/harbor/blob/main/src/portal/src/app/base/project/repository/artifact/artifact.ts), where `NOTATION = 'signature.notation'` maps the backend type to UI indicators.

### Using the Notation CLI

Sign images using certificates and verify through Harbor's automated checks:

```bash

# Generate a test certificate

notation cert generate-test --default myca

# Sign the image with OCI referrers support

notation sign -d --allow-referrers-api myrepo/myimage:1.0

# Verify the signature

notation verify --key myca.pub myrepo/myimage:1.0

```

## Programmatic Access to Signature Metadata

External tools can query Harbor's REST API to determine which signing methods protect a specific artifact. The following Go SDK example lists accessories and identifies signature types:

```go
package main

import (
    "encoding/json"
    "fmt"
    "net/http"
)

type Accessory struct {
    Type string `json:"type"` // e.g. "signature.cosign" or "signature.notation"
}

func listAccessories(project, repo, digest, token string) ([]Accessory, error) {
    url := fmt.Sprintf("https://harbor.local/api/v2.0/projects/%s/repositories/%s/artifacts/%s/addons", project, repo, digest)
    req, _ := http.NewRequest("GET", url, nil)
    req.Header.Set("Authorization", "Bearer "+token)
    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()
    
    var result struct {
        Accessories []Accessory `json:"accessories"`
    }
    if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
        return nil, err
    }
    return result.Accessories, nil
}

```

This function returns accessory types such as `signature.cosign`, `signature.notation`, or legacy Notary signatures, enabling automated compliance checks.

## Summary

Harbor's image signing architecture accommodates diverse security requirements through three integrated mechanisms:

- **Docker Content Trust (Notary v1)** provides legacy compatibility for existing Docker workflows, storing signatures in dedicated Notary repositories and enforcing policies through [`src/server/middleware/contenttrust/contentrust.go`](https://github.com/goharbor/harbor/blob/main/src/server/middleware/contenttrust/contentrust.go).
- **Cosign** delivers modern Sigstore integration with keyless signing capabilities, storing OCI artifacts with media type `application/vnd.dev.cos.cosign.artifact.sig.v1+json` and type `signature.cosign`.
- **Notation** implements the OCI-native standard (Notary v2) using accessory type `signature.notation`, positioning Harbor for long-term interoperability with container signing standards.

Each method stores cryptographic proof as Harbor accessories, enabling project-level policies that block unsigned images before they reach your runtime environment.

## Frequently Asked Questions

### How do I enable image signing enforcement for a specific Harbor project?

Navigate to the project configuration or use the REST API to set the appropriate metadata flag. For Notary v1, set `enable_content_trust` to `"true"`; for Cosign, configure `enable_content_trust_cosign`. These settings are stored in the project metadata model ([`src/pkg/project/models/pro_meta.go`](https://github.com/goharbor/harbor/blob/main/src/pkg/project/models/pro_meta.go)) and enforced by the content-trust middleware during pull operations.

### Can I use multiple signing methods simultaneously for the same image?

Yes. Harbor stores each signature type as a distinct accessory attached to the parent artifact. An image can possess a Notary v1 signature, a Cosign signature, and a Notation signature simultaneously, with each stored under its respective accessory type (`signature.notary`, `signature.cosign`, `signature.notation`). You can configure policies to require any or all signature types depending on your compliance requirements.

### What error message appears when pulling an unsigned image with content-trust enabled?

When content-trust policies are active and an image lacks the required signature, Harbor returns specific error messages through the Docker client: `The image is not signed by notary.` for Docker Content Trust enforcement, or `The image is not signed by cosign.` for Cosign policies. These messages originate from [`src/server/middleware/contenttrust/contentrust.go`](https://github.com/goharbor/harbor/blob/main/src/server/middleware/contenttrust/contentrust.go) and indicate which signing mechanism validation failed.

### Where does Harbor store Cosign signature artifacts?

Cosign signatures are stored as OCI artifacts with the media type `application/vnd.dev.cosign.artifact.sig.v1+json` and linked to parent images through the accessory model defined in [`src/pkg/accessory/model/cosign/cosign.go`](https://github.com/goharbor/harbor/blob/main/src/pkg/accessory/model/cosign/cosign.go). The middleware in [`src/server/middleware/cosign/cosign.go`](https://github.com/goharbor/harbor/blob/main/src/server/middleware/cosign/cosign.go) identifies these signatures using a regular expression matcher before the content-trust interceptor validates policy compliance.