Image Signing in Harbor: Supported Methods and Implementation Guide

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.

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, 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, 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:


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


# 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, which specifies the signature.notation type constant. The content-trust middleware in 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, 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:


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

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.
  • 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) 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 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. The middleware in src/server/middleware/cosign/cosign.go identifies these signatures using a regular expression matcher before the content-trust interceptor validates policy compliance.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →