-
Notifications
You must be signed in to change notification settings - Fork 377
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
261 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
See [specification](spec/README.md) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
# OCI Image Signature Specification | ||
|
||
**Version 0.1** | ||
|
||
## Introduction | ||
|
||
This document specifies an out-of-band cryptographic verification model for | ||
container images. Detached signatures are associated with images by manifest digest hash value. | ||
|
||
## Signature Format | ||
|
||
### Fields | ||
|
||
Field Name | Type | Description | ||
---|:---:|--- | ||
<a name="critical"></a>critical | [ [CriticalObject](#criticalObject) ] | **Required.** | ||
<a name="optional"></a>optional | [ [OptionalObject](#optionalObject) ] | **Optional.** | ||
|
||
#### <a name="criticalObject"></a>Critical Object | ||
|
||
Field Name | Type | Description | ||
---|:---:|--- | ||
<a name="identity"></a>identity | [ [IdentityObject](#identityObject) ] | **Required.** | ||
<a name="image"></a>image | [ [ImageObject](#imageObject) ] | **Required.** | ||
<a name="type"></a>type | `string` | **Required.** Valid values: "atomic container signature" | ||
|
||
#### <a name="identityObject"></a>Identity Object | ||
|
||
Field Name | Type | Description | ||
---|:---:|--- | ||
<a name="docker-reference"></a>docker-reference | `string` | **Required.** Image name | ||
|
||
#### <a name="imageObject"></a>Image Object | ||
|
||
Field Name | Type | Description | ||
---|:---:|--- | ||
<a name="docker-manifest-digest"></a>docker-manifest-digest | `digest` | **Required.** Manifest digest in the form of `<algorithm>:<hashValue>` | ||
|
||
#### <a name="optionalObject"></a>Optional Object | ||
|
||
Field Name | Type | Description | ||
---|:---:|--- | ||
<a name="creator"></a>creator | `string` | **Optional.** Creator ID | ||
<a name="timestamp"></a>timestamp | `int64` | **Optional.** Timestamp epoch | ||
|
||
### Example | ||
|
||
See [example signature file](signature.json). | ||
|
||
### Encryption and Decryption | ||
|
||
The signature data is written to a file that is encrypted with a private key. The file may be decrypted using the corresponding public key. | ||
|
||
**Example GPG encrypt command** | ||
|
||
TODO | ||
|
||
**Example GPG decrypt command** | ||
|
||
``` | ||
$ gpg --decrypt busybox.sig | python -m json.tool | ||
``` | ||
|
||
## Static File Layout | ||
|
||
When encrypted signature files are stored as static files the following directory layout shall be used: | ||
|
||
[/<pathPrefix>]/<registryUri>/<repository>/<image@sha256:hash>/<pubkeyID> | ||
|
||
## Validation | ||
|
||
1. Decrypt signature file. | ||
1. Compare image manifest digest with signature digest | ||
|
||
## Policy | ||
|
||
Policy provides a way to describe trust by mapping registries, repositories and images to a list of required public keys. It also describes default fallback behavior when conditions are not met. | ||
|
||
Policy answers the following questions: | ||
|
||
* What should be done when an image does not have specific policy? | ||
* What list of public keys do you trust for a given registry, repository or image? | ||
|
||
See [policy specification file](policy.go). | ||
|
||
**Example Policy File** | ||
|
||
https://github.com/projectatomic/skopeo/blob/master/integration/fixtures/policy.json | ||
|
||
## Signature Server Discovery | ||
|
||
To enable a reasonable user experience, a mechanism is needed to map a given registry with a signature server and associated public keys. Without such a mechanism each user would have to look up this information out-of-band. While this can lead to insecure workflows, Discovery allows tooling to prompt users to make a trust evaluation: Do you want to trust this public key for this registry? | ||
|
||
### Discovery Container Format | ||
|
||
A container image may be used to provide registry metadata. The image uses LABELs with signature server metadata. Four LABELs are specified. | ||
|
||
#### Discovery Container Naming | ||
|
||
Discovery container images shall have the following characteristics: | ||
|
||
* The image shall be named **sigstore**. | ||
* The primary image shall be tagged **:latest** | ||
* When more than one image is served arbitrary tags may be used, such as **:auxilliary** or **:backup**. | ||
* The scope of the signature metadata shall apply to the image being served. In other words, an image served at *registry.example.com/acme/sigstore:latest* provides information about images in the "acme" repository namespace. An image served at *registry.example.com/sigstore:latest* provides information about all images in the registry.example.com registry. | ||
|
||
**Example Dockerfile** | ||
|
||
Consider the following Dockerfile built as registry.example.com/acme/sigstore:latest and pushed to the registry. | ||
|
||
``` | ||
FROM scratch | ||
LABEL sigstore-url="https://sigstore.example.com:8443" \ | ||
pubkey-id="ef442d51: Example, Inc. <security@example.com>" \ | ||
pubkey-fingerprint="657F 347A D004 4ADE 55BA 8A5F 199E 2F91 FD43 1D51" \ | ||
pubkey-download-url="https://www.example.com/security/ef431d51.txt" | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
type Policy struct { | ||
// Default applies to any image which does not have a matching policy in Specific. | ||
Default PolicyRequirements `json:"default"` | ||
// Specific applies to images matching scope, the map key. | ||
// Scope is registry/server, namespace in a registry, single repository. | ||
// FIXME: Scope syntax - should it be namespaced docker:something ? Or, in the worst case, a composite object (we couldn't use a JSON map) | ||
// Most specific scope wins, duplication is prohibited (hard failure). | ||
// Defaults to an empty map if not specified. | ||
Specific map[string]PolicyRequirements `json:"specific"` | ||
} | ||
|
||
// PolicyRequirements is a set of requirements applying to a set of images; each of them must be satisfied (though perhaps each by a different signature). | ||
// Must not be empty, frequently will only contain a single element. | ||
type PolicyRequirements []PolicyRequirement | ||
|
||
// PolicyRequirement is a rule which must be satisfied by at least one of the signatures of an image. See prCommon and the various pr* structures below. | ||
type PolicyRequirement interface { | ||
} | ||
|
||
// prCommon is the common type field in a JSON encoding of PolicyRequirement. | ||
type prCommon struct { | ||
Type prTypeIdentifier `json:"type"` | ||
} | ||
|
||
// prTypeIdentifier is string designating a kind of a PolicyRequirement. | ||
type prTypeIdentifier string | ||
|
||
const ( | ||
prTypeInsecureAcceptAnything prTypeIdentifier = "insecureAcceptAnything" | ||
prTypeReject prTypeIdentifier = "reject" | ||
prTypeSignedBy prTypeIdentifier = "signedBy" | ||
prTypeSignedBaseLayer prTypeIdentifier = "signedBaseLayer" | ||
) | ||
|
||
// prInsecureAcceptAnything is a PolicyRequirement with type = prTypeInsecureAcceptAnything: every image is accepted. | ||
// Note that because PolicyRequirements are implicitly ANDed, this is necessary only if it is the only rule (to make the list non-empty and the policy explicit). | ||
type prInsecureAcceptAnything struct { | ||
prCommon | ||
} | ||
|
||
// prReject is a PolicyRequirement with type = prTypeReject: every image is rejected. | ||
type prReject struct { | ||
prCommon | ||
} | ||
|
||
// prSignedBy is a PolicyRequirement with type = prTypeSignedBy: the image is signed by trusted keys for a specified identity. | ||
type prSignedBy struct { | ||
prCommon | ||
|
||
// KeyType specifies what kind of key reference KeyPath/KeyData is. | ||
// Acceptable values are “GPGKeys” | “signedByGPGKeys” “X.509Certificates” | “signedByX.509CAs” | ||
// FIXME: eventually also support GPGTOFU, X.509TOFU, with KeyPath only | ||
KeyType sbKeyType `json:"keyType"` | ||
|
||
// KeyPath is a pathname to a local file containing the trusted key(s). Exactly one of KeyPath and KeyData must be specified. | ||
KeyPath string `json:"keyPath,omitempty"` | ||
// KeyData contains the trusted key(s), base64-encoded. Exactly one of KeyPath and KeyData must be specified. | ||
KeyData []byte `json:"keyData,omitempty"` | ||
|
||
// SignedIdentity specifies what image identity the signature must be claiming about the image. | ||
// Defaults to "match-exact" if not specified. | ||
SignedIdentity PolicyReferenceMatch `json:"signedIdentity"` | ||
} | ||
|
||
// sbKeyType are the allowed values for prSignedBy.KeyType | ||
type sbKeyType string | ||
|
||
const ( | ||
// SBKeyTypeGPGKeys refers to keys contained in a GPG keyring | ||
SBKeyTypeGPGKeys sbKeyType = "GPGKeys" | ||
// SBKeyTypeSignedByGPGKeys refers to keys signed by keys in a GPG keyring | ||
SBKeyTypeSignedByGPGKeys sbKeyType = "signedByGPGKeys" | ||
// SBKeyTypeX509Certificates refers to keys in a set of X.509 certificates | ||
// FIXME: PEM, DER? | ||
SBKeyTypeX509Certificates sbKeyType = "X509Certificates" | ||
// SBKeyTypeSignedByX509CAs refers to keys signed by one of the X.509 CAs | ||
// FIXME: PEM, DER? | ||
SBKeyTypeSignedByX509CAs sbKeyType = "signedByX509CAs" | ||
) | ||
|
||
// prSignedBaseLayer is a PolicyRequirement with type = prSignedBaseLayer: the image has a specified, correctly signed, base image. | ||
type prSignedBaseLayer struct { | ||
prCommon | ||
// BaseLayerIdentity specifies the base image to look for. "match-exact" is rejected, "match-repository" is unlikely to be useful. | ||
BaseLayerIdentity PolicyReferenceMatch `json:"baseLayerIdentity"` | ||
} | ||
|
||
// PolicyReferenceMatch specifies a set of image identities accepted in PolicyRequirement. See prmCommon and the various prm* structures below. | ||
type PolicyReferenceMatch interface { | ||
} | ||
|
||
// prmCommon is the common type field in a JSON encoding of PolicyReferenceMatch. | ||
type prmCommon struct { | ||
Type prmTypeIdentifier `json:"type"` | ||
} | ||
|
||
// prmTypeIdentifier is string designating a kind of a PolicyReferenceMatch. | ||
type prmTypeIdentifier string | ||
|
||
const ( | ||
prmTypeMatchExact prmTypeIdentifier = "matchExact" | ||
prmTypeMatchRepository prmTypeIdentifier = "matchRepository" | ||
prmTypeExactReference prmTypeIdentifier = "exactReference" | ||
prmTypeExactRepository prmTypeIdentifier = "exactRepository" | ||
) | ||
|
||
// prmMatchExact is a PolicyReferenceMatch with type = prmMatchExact: the two references must match exactly. | ||
type prmMatchExact struct { | ||
prmCommon | ||
} | ||
|
||
// prmMatchRepository is a PolicyReferenceMatch with type = prmMatchRepository: the two references must use the same repository, may differ in the tag. | ||
type prmMatchRepository struct { | ||
prmCommon | ||
} | ||
|
||
// prmExactReference is a PolicyReferenceMatch with type = prmExactReference: matches a specified reference exactly. | ||
type prmExactReference struct { | ||
prmCommon | ||
DockerReference string `json:"dockerReference"` | ||
} | ||
|
||
// prmExactRepository is a PolicyReferenceMatch with type = prmExactRepository: matches a specified repository, with any tag. | ||
type prmExactRepository struct { | ||
prmCommon | ||
DockerRepository string `json:"dockerRepository"` | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
{ | ||
"critical": { | ||
"identity": { | ||
"docker-reference": "busybox" | ||
}, | ||
"image": { | ||
"docker-manifest-digest": "sha256:a59906e33509d14c036c8678d687bd4eec81ed7c4b8ce907b888c607f6a1e0e6" | ||
}, | ||
"type": "atomic container signature" | ||
}, | ||
"optional": { | ||
"creator": "atomic 0.1.0-dev", | ||
"timestamp": 1471035347 | ||
} | ||
} |