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+jsonand typesignature.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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →