Merge pull request #599 from stevvooe/clarify-deletion-by-digest-constraint
Clarify digest in API specification
This commit is contained in:
commit
f63313de1f
@ -116,6 +116,12 @@ indicating what is different. Optionally, we may start marking parts of the
|
|||||||
specification to correspond with the versions enumerated here.
|
specification to correspond with the versions enumerated here.
|
||||||
|
|
||||||
<dl>
|
<dl>
|
||||||
|
<dt>2.0.2</dt>
|
||||||
|
<dd>
|
||||||
|
<li>Added section covering digest format.</li>
|
||||||
|
<li>Added more clarification that manifest cannot be deleted by tag.</li>
|
||||||
|
</dd>
|
||||||
|
|
||||||
<dt>2.0.1</dt>
|
<dt>2.0.1</dt>
|
||||||
<dd>
|
<dd>
|
||||||
<ul>
|
<ul>
|
||||||
@ -238,6 +244,84 @@ When a `200 OK` or `401 Unauthorized` response is returned, the
|
|||||||
Clients may require this header value to determine if the endpoint serves this
|
Clients may require this header value to determine if the endpoint serves this
|
||||||
API. When this header is omitted, clients may fallback to an older API version.
|
API. When this header is omitted, clients may fallback to an older API version.
|
||||||
|
|
||||||
|
### Content Digests
|
||||||
|
|
||||||
|
This API design is driven heavily by [content addressability](http://en.wikipedia.org/wiki/Content-addressable_storage).
|
||||||
|
The core of this design is the concept of a content addressable identifier. It
|
||||||
|
uniquely identifies content by taking a collision-resistent hash of the bytes.
|
||||||
|
Such an identifier can be independently calculated and verified by selection
|
||||||
|
of a common _algorithm_. If such an identifier can be communicated in a secure
|
||||||
|
manner, one can retrieve the content from an insecure source, calculate it
|
||||||
|
independently and be certain that the correct content was obtained. Put simply,
|
||||||
|
the identifier is a property of the content.
|
||||||
|
|
||||||
|
To disambiguate from other concepts, we call this identifier a _digest_. A
|
||||||
|
_digest_ is a serialized hash result, consisting of a _algorithm_ and _hex_
|
||||||
|
portion. The _algorithm_ identifies the methodology used to calculate the
|
||||||
|
digest. The _hex_ portion is the hex-encoded result of the hash.
|
||||||
|
|
||||||
|
We define a _digest_ string to match the following grammar:
|
||||||
|
|
||||||
|
digest := algorithm ":" hex
|
||||||
|
algorithm := /[A-Fa-f0-9_+.-]+/
|
||||||
|
hex := /[A-Fa-f0-9]+/
|
||||||
|
|
||||||
|
Some examples of _digests_ include the following:
|
||||||
|
|
||||||
|
digest | description |
|
||||||
|
----------------------------------------------------------------------------------|------------------------------------------------
|
||||||
|
sha256:6c3c624b58dbbcd3c0dd82b4c53f04194d1247c6eebdaab7c610cf7d66709b3b | Common sha256 based digest |
|
||||||
|
tarsum.v1+sha256:6c3c624b58dbbcd3c0dd82b4c53f04194d1247c6eebdaab7c610cf7d66709b3b | Tarsum digest, used for legacy layer digests. |
|
||||||
|
|
||||||
|
> __NOTE:__ While we show an example of using a `tarsum` digest, the security
|
||||||
|
> of tarsum has not been verified. It is recommended that most implementations
|
||||||
|
> use sha256 for interoperability.
|
||||||
|
|
||||||
|
While the _algorithm_ does allow one to implement a wide variety of
|
||||||
|
algorithms, compliant implementations should use sha256. Heavy processing of
|
||||||
|
input before calculating a hash is discouraged to avoid degrading the
|
||||||
|
uniqueness of the _digest_ but some canonicalization may be performed to
|
||||||
|
ensure consistent identifiers.
|
||||||
|
|
||||||
|
Let's use a simple example in pseudo-code to demonstrate a digest calculation:
|
||||||
|
|
||||||
|
```
|
||||||
|
let C = 'a small string'
|
||||||
|
let B = sha256(C)
|
||||||
|
let D = 'sha256:' + EncodeHex(B)
|
||||||
|
let ID(C) = D
|
||||||
|
```
|
||||||
|
|
||||||
|
Above, we have bytestring _C_ passed into a function, _SHA256_, that returns a
|
||||||
|
bytestring B, which is the hash of _C_. _D_ gets the algorithm concatenated
|
||||||
|
with the hex encoding of _B_. We then define the identifier of _C_ to _ID(C)_
|
||||||
|
as equal to _D_. A digest can be verified by independently calculating _D_ and
|
||||||
|
comparing it with identifier _ID(C)_
|
||||||
|
|
||||||
|
#### Digest Header
|
||||||
|
|
||||||
|
To provide verification of http content, any response may include a `Docker-
|
||||||
|
Content-Digest` header. This will include the digest of the target entity
|
||||||
|
returned in the response. For blobs, this is the entire blob content. For
|
||||||
|
manifests, this is the manifest body without the signature content, also known
|
||||||
|
as the JWS payload. Note that the commonly used canonicalization for digest
|
||||||
|
calculation may be dependent on the mediatype of the content, such as with
|
||||||
|
manifests.
|
||||||
|
|
||||||
|
The client may choose to ignore the header or may verify it to ensure content
|
||||||
|
integrity and transport security. This is most important when fetching by a
|
||||||
|
digest. To ensure security, the content should be verified against the digest
|
||||||
|
used to fetch the content. At times, the returned digest may differ from that
|
||||||
|
used to initiate a request. Such digests are considered to be from different
|
||||||
|
_domains_, meaning they have different values for _algorithm_. In such a case,
|
||||||
|
the client may choose to verify the digests in both domains or ignore the
|
||||||
|
server's digest. To maintain security, the client _must_ always verify the
|
||||||
|
content against the _digest_ used to fetch the content.
|
||||||
|
|
||||||
|
> __IMPORTANT:__ If a _digest_ is used to fetch content, the client should use
|
||||||
|
> the same digest used to fetch the content to verify it. The header `Docker-
|
||||||
|
> Content-Digest` should not be trusted over the "local" digest.
|
||||||
|
|
||||||
### Pulling An Image
|
### Pulling An Image
|
||||||
|
|
||||||
An "image" is a combination of a JSON manifest and individual layer files. The
|
An "image" is a combination of a JSON manifest and individual layer files. The
|
||||||
@ -717,7 +801,7 @@ A list of methods and URIs are covered in the table below:
|
|||||||
| GET | `/v2/<name>/tags/list` | Tags | Fetch the tags under the repository identified by `name`. |
|
| GET | `/v2/<name>/tags/list` | Tags | Fetch the tags under the repository identified by `name`. |
|
||||||
| GET | `/v2/<name>/manifests/<reference>` | Manifest | Fetch the manifest identified by `name` and `reference` where `reference` can be a tag or digest. |
|
| GET | `/v2/<name>/manifests/<reference>` | Manifest | Fetch the manifest identified by `name` and `reference` where `reference` can be a tag or digest. |
|
||||||
| PUT | `/v2/<name>/manifests/<reference>` | Manifest | Put the manifest identified by `name` and `reference` where `reference` can be a tag or digest. |
|
| PUT | `/v2/<name>/manifests/<reference>` | Manifest | Put the manifest identified by `name` and `reference` where `reference` can be a tag or digest. |
|
||||||
| DELETE | `/v2/<name>/manifests/<reference>` | Manifest | Delete the manifest identified by `name` and `reference` where `reference` can be a tag or digest. |
|
| DELETE | `/v2/<name>/manifests/<reference>` | Manifest | Delete the manifest identified by `name` and `reference`. Note that a manifest can _only_ be deleted by `digest`. |
|
||||||
| GET | `/v2/<name>/blobs/<digest>` | Blob | Retrieve the blob from the registry identified by `digest`. A `HEAD` request can also be issued to this endpoint to obtain resource information without receiving all data. |
|
| GET | `/v2/<name>/blobs/<digest>` | Blob | Retrieve the blob from the registry identified by `digest`. A `HEAD` request can also be issued to this endpoint to obtain resource information without receiving all data. |
|
||||||
| POST | `/v2/<name>/blobs/uploads/` | Intiate Blob Upload | Initiate a resumable blob upload. If successful, an upload location will be provided to complete the upload. Optionally, if the `digest` parameter is present, the request body will be used to complete the upload in a single request. |
|
| POST | `/v2/<name>/blobs/uploads/` | Intiate Blob Upload | Initiate a resumable blob upload. If successful, an upload location will be provided to complete the upload. Optionally, if the `digest` parameter is present, the request body will be used to complete the upload in a single request. |
|
||||||
| GET | `/v2/<name>/blobs/uploads/<uuid>` | Blob Upload | Retrieve status of upload identified by `uuid`. The primary purpose of this endpoint is to resolve the current status of a resumable upload. |
|
| GET | `/v2/<name>/blobs/uploads/<uuid>` | Blob Upload | Retrieve status of upload identified by `uuid`. The primary purpose of this endpoint is to resolve the current status of a resumable upload. |
|
||||||
@ -1324,7 +1408,7 @@ The error codes that may be included in the response body are enumerated below:
|
|||||||
|
|
||||||
#### DELETE Manifest
|
#### DELETE Manifest
|
||||||
|
|
||||||
Delete the manifest identified by `name` and `reference` where `reference` can be a tag or digest.
|
Delete the manifest identified by `name` and `reference`. Note that a manifest can _only_ be deleted by `digest`.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -1361,7 +1445,7 @@ The following parameters should be specified on the request:
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
###### On Failure: Invalid Name or Tag
|
###### On Failure: Invalid Name or Reference
|
||||||
|
|
||||||
```
|
```
|
||||||
400 Bad Request
|
400 Bad Request
|
||||||
@ -1379,7 +1463,7 @@ Content-Type: application/json; charset=utf-8
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The specified `name` or `tag` were invalid and the delete was unable to proceed.
|
The specified `name` or `reference` were invalid and the delete was unable to proceed.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -1449,7 +1533,7 @@ Content-Type: application/json; charset=utf-8
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The specified `name` or `tag` are unknown to the registry and the delete was unable to proceed. Clients can assume the manifest was already deleted if this response is returned.
|
The specified `name` or `reference` are unknown to the registry and the delete was unable to proceed. Clients can assume the manifest was already deleted if this response is returned.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -116,6 +116,12 @@ indicating what is different. Optionally, we may start marking parts of the
|
|||||||
specification to correspond with the versions enumerated here.
|
specification to correspond with the versions enumerated here.
|
||||||
|
|
||||||
<dl>
|
<dl>
|
||||||
|
<dt>2.0.2</dt>
|
||||||
|
<dd>
|
||||||
|
<li>Added section covering digest format.</li>
|
||||||
|
<li>Added more clarification that manifest cannot be deleted by tag.</li>
|
||||||
|
</dd>
|
||||||
|
|
||||||
<dt>2.0.1</dt>
|
<dt>2.0.1</dt>
|
||||||
<dd>
|
<dd>
|
||||||
<ul>
|
<ul>
|
||||||
@ -238,6 +244,84 @@ When a `200 OK` or `401 Unauthorized` response is returned, the
|
|||||||
Clients may require this header value to determine if the endpoint serves this
|
Clients may require this header value to determine if the endpoint serves this
|
||||||
API. When this header is omitted, clients may fallback to an older API version.
|
API. When this header is omitted, clients may fallback to an older API version.
|
||||||
|
|
||||||
|
### Content Digests
|
||||||
|
|
||||||
|
This API design is driven heavily by [content addressability](http://en.wikipedia.org/wiki/Content-addressable_storage).
|
||||||
|
The core of this design is the concept of a content addressable identifier. It
|
||||||
|
uniquely identifies content by taking a collision-resistent hash of the bytes.
|
||||||
|
Such an identifier can be independently calculated and verified by selection
|
||||||
|
of a common _algorithm_. If such an identifier can be communicated in a secure
|
||||||
|
manner, one can retrieve the content from an insecure source, calculate it
|
||||||
|
independently and be certain that the correct content was obtained. Put simply,
|
||||||
|
the identifier is a property of the content.
|
||||||
|
|
||||||
|
To disambiguate from other concepts, we call this identifier a _digest_. A
|
||||||
|
_digest_ is a serialized hash result, consisting of a _algorithm_ and _hex_
|
||||||
|
portion. The _algorithm_ identifies the methodology used to calculate the
|
||||||
|
digest. The _hex_ portion is the hex-encoded result of the hash.
|
||||||
|
|
||||||
|
We define a _digest_ string to match the following grammar:
|
||||||
|
|
||||||
|
digest := algorithm ":" hex
|
||||||
|
algorithm := /[A-Fa-f0-9_+.-]+/
|
||||||
|
hex := /[A-Fa-f0-9]+/
|
||||||
|
|
||||||
|
Some examples of _digests_ include the following:
|
||||||
|
|
||||||
|
digest | description |
|
||||||
|
----------------------------------------------------------------------------------|------------------------------------------------
|
||||||
|
sha256:6c3c624b58dbbcd3c0dd82b4c53f04194d1247c6eebdaab7c610cf7d66709b3b | Common sha256 based digest |
|
||||||
|
tarsum.v1+sha256:6c3c624b58dbbcd3c0dd82b4c53f04194d1247c6eebdaab7c610cf7d66709b3b | Tarsum digest, used for legacy layer digests. |
|
||||||
|
|
||||||
|
> __NOTE:__ While we show an example of using a `tarsum` digest, the security
|
||||||
|
> of tarsum has not been verified. It is recommended that most implementations
|
||||||
|
> use sha256 for interoperability.
|
||||||
|
|
||||||
|
While the _algorithm_ does allow one to implement a wide variety of
|
||||||
|
algorithms, compliant implementations should use sha256. Heavy processing of
|
||||||
|
input before calculating a hash is discouraged to avoid degrading the
|
||||||
|
uniqueness of the _digest_ but some canonicalization may be performed to
|
||||||
|
ensure consistent identifiers.
|
||||||
|
|
||||||
|
Let's use a simple example in pseudo-code to demonstrate a digest calculation:
|
||||||
|
|
||||||
|
```
|
||||||
|
let C = 'a small string'
|
||||||
|
let B = sha256(C)
|
||||||
|
let D = 'sha256:' + EncodeHex(B)
|
||||||
|
let ID(C) = D
|
||||||
|
```
|
||||||
|
|
||||||
|
Above, we have bytestring _C_ passed into a function, _SHA256_, that returns a
|
||||||
|
bytestring B, which is the hash of _C_. _D_ gets the algorithm concatenated
|
||||||
|
with the hex encoding of _B_. We then define the identifier of _C_ to _ID(C)_
|
||||||
|
as equal to _D_. A digest can be verified by independently calculating _D_ and
|
||||||
|
comparing it with identifier _ID(C)_
|
||||||
|
|
||||||
|
#### Digest Header
|
||||||
|
|
||||||
|
To provide verification of http content, any response may include a `Docker-
|
||||||
|
Content-Digest` header. This will include the digest of the target entity
|
||||||
|
returned in the response. For blobs, this is the entire blob content. For
|
||||||
|
manifests, this is the manifest body without the signature content, also known
|
||||||
|
as the JWS payload. Note that the commonly used canonicalization for digest
|
||||||
|
calculation may be dependent on the mediatype of the content, such as with
|
||||||
|
manifests.
|
||||||
|
|
||||||
|
The client may choose to ignore the header or may verify it to ensure content
|
||||||
|
integrity and transport security. This is most important when fetching by a
|
||||||
|
digest. To ensure security, the content should be verified against the digest
|
||||||
|
used to fetch the content. At times, the returned digest may differ from that
|
||||||
|
used to initiate a request. Such digests are considered to be from different
|
||||||
|
_domains_, meaning they have different values for _algorithm_. In such a case,
|
||||||
|
the client may choose to verify the digests in both domains or ignore the
|
||||||
|
server's digest. To maintain security, the client _must_ always verify the
|
||||||
|
content against the _digest_ used to fetch the content.
|
||||||
|
|
||||||
|
> __IMPORTANT:__ If a _digest_ is used to fetch content, the client should use
|
||||||
|
> the same digest used to fetch the content to verify it. The header `Docker-
|
||||||
|
> Content-Digest` should not be trusted over the "local" digest.
|
||||||
|
|
||||||
### Pulling An Image
|
### Pulling An Image
|
||||||
|
|
||||||
An "image" is a combination of a JSON manifest and individual layer files. The
|
An "image" is a combination of a JSON manifest and individual layer files. The
|
||||||
|
@ -639,7 +639,7 @@ var routeDescriptors = []RouteDescriptor{
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Method: "DELETE",
|
Method: "DELETE",
|
||||||
Description: "Delete the manifest identified by `name` and `reference` where `reference` can be a tag or digest.",
|
Description: "Delete the manifest identified by `name` and `reference`. Note that a manifest can _only_ be deleted by `digest`.",
|
||||||
Requests: []RequestDescriptor{
|
Requests: []RequestDescriptor{
|
||||||
{
|
{
|
||||||
Headers: []ParameterDescriptor{
|
Headers: []ParameterDescriptor{
|
||||||
@ -657,8 +657,8 @@ var routeDescriptors = []RouteDescriptor{
|
|||||||
},
|
},
|
||||||
Failures: []ResponseDescriptor{
|
Failures: []ResponseDescriptor{
|
||||||
{
|
{
|
||||||
Name: "Invalid Name or Tag",
|
Name: "Invalid Name or Reference",
|
||||||
Description: "The specified `name` or `tag` were invalid and the delete was unable to proceed.",
|
Description: "The specified `name` or `reference` were invalid and the delete was unable to proceed.",
|
||||||
StatusCode: http.StatusBadRequest,
|
StatusCode: http.StatusBadRequest,
|
||||||
ErrorCodes: []ErrorCode{
|
ErrorCodes: []ErrorCode{
|
||||||
ErrorCodeNameInvalid,
|
ErrorCodeNameInvalid,
|
||||||
@ -690,7 +690,7 @@ var routeDescriptors = []RouteDescriptor{
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "Unknown Manifest",
|
Name: "Unknown Manifest",
|
||||||
Description: "The specified `name` or `tag` are unknown to the registry and the delete was unable to proceed. Clients can assume the manifest was already deleted if this response is returned.",
|
Description: "The specified `name` or `reference` are unknown to the registry and the delete was unable to proceed. Clients can assume the manifest was already deleted if this response is returned.",
|
||||||
StatusCode: http.StatusNotFound,
|
StatusCode: http.StatusNotFound,
|
||||||
ErrorCodes: []ErrorCode{
|
ErrorCodes: []ErrorCode{
|
||||||
ErrorCodeNameUnknown,
|
ErrorCodeNameUnknown,
|
||||||
|
Loading…
Reference in New Issue
Block a user