Merge pull request #731 from mstanleyjones/distribution_docs_from_upstream
Pull distribution reference docs from upstream repo
This commit is contained in:
commit
4a0bf57871
12 changed files with 0 additions and 8821 deletions
File diff suppressed because it is too large
Load diff
5478
docs/spec/api.md
5478
docs/spec/api.md
File diff suppressed because it is too large
Load diff
|
@ -1,10 +0,0 @@
|
||||||
---
|
|
||||||
description: Docker Registry v2 authentication schema
|
|
||||||
keywords: registry, on-prem, images, tags, repository, distribution, authentication, advanced
|
|
||||||
title: Docker Registry v2 authentication
|
|
||||||
---
|
|
||||||
|
|
||||||
See the [Token Authentication Specification](token.md),
|
|
||||||
[Token Authentication Implementation](jwt.md),
|
|
||||||
[Token Scope Documentation](scope.md),
|
|
||||||
[OAuth2 Token Authentication](oauth.md) for more information.
|
|
|
@ -1,327 +0,0 @@
|
||||||
---
|
|
||||||
description: Describe the reference implementation of the Docker Registry v2 authentication schema
|
|
||||||
keywords: registry, on-prem, images, tags, repository, distribution, JWT authentication, advanced
|
|
||||||
title: Docker Registry v2 Bearer token specification
|
|
||||||
---
|
|
||||||
|
|
||||||
This specification covers the `docker/distribution` implementation of the
|
|
||||||
v2 Registry's authentication schema. Specifically, it describes the JSON
|
|
||||||
Web Token schema that `docker/distribution` has adopted to implement the
|
|
||||||
client-opaque Bearer token issued by an authentication service and
|
|
||||||
understood by the registry.
|
|
||||||
|
|
||||||
This document borrows heavily from the [JSON Web Token Draft Spec](https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-32)
|
|
||||||
|
|
||||||
## Getting a Bearer Token
|
|
||||||
|
|
||||||
For this example, the client makes an HTTP GET request to the following URL:
|
|
||||||
|
|
||||||
```
|
|
||||||
https://auth.docker.io/token?service=registry.docker.io&scope=repository:samalba/my-app:pull,push
|
|
||||||
```
|
|
||||||
|
|
||||||
The token server should first attempt to authenticate the client using any
|
|
||||||
authentication credentials provided with the request. As of Docker 1.8, the
|
|
||||||
registry client in the Docker Engine only supports Basic Authentication to
|
|
||||||
these token servers. If an attempt to authenticate to the token server fails,
|
|
||||||
the token server should return a `401 Unauthorized` response indicating that
|
|
||||||
the provided credentials are invalid.
|
|
||||||
|
|
||||||
Whether the token server requires authentication is up to the policy of that
|
|
||||||
access control provider. Some requests may require authentication to determine
|
|
||||||
access (such as pushing or pulling a private repository) while others may not
|
|
||||||
(such as pulling from a public repository).
|
|
||||||
|
|
||||||
After authenticating the client (which may simply be an anonymous client if
|
|
||||||
no attempt was made to authenticate), the token server must next query its
|
|
||||||
access control list to determine whether the client has the requested scope. In
|
|
||||||
this example request, if I have authenticated as user `jlhawn`, the token
|
|
||||||
server will determine what access I have to the repository `samalba/my-app`
|
|
||||||
hosted by the entity `registry.docker.io`.
|
|
||||||
|
|
||||||
Once the token server has determined what access the client has to the
|
|
||||||
resources requested in the `scope` parameter, it will take the intersection of
|
|
||||||
the set of requested actions on each resource and the set of actions that the
|
|
||||||
client has in fact been granted. If the client only has a subset of the
|
|
||||||
requested access **it must not be considered an error** as it is not the
|
|
||||||
responsibility of the token server to indicate authorization errors as part of
|
|
||||||
this workflow.
|
|
||||||
|
|
||||||
Continuing with the example request, the token server will find that the
|
|
||||||
client's set of granted access to the repository is `[pull, push]` which when
|
|
||||||
intersected with the requested access `[pull, push]` yields an equal set. If
|
|
||||||
the granted access set was found only to be `[pull]` then the intersected set
|
|
||||||
would only be `[pull]`. If the client has no access to the repository then the
|
|
||||||
intersected set would be empty, `[]`.
|
|
||||||
|
|
||||||
It is this intersected set of access which is placed in the returned token.
|
|
||||||
|
|
||||||
The server will now construct a JSON Web Token to sign and return. A JSON Web
|
|
||||||
Token has 3 main parts:
|
|
||||||
|
|
||||||
1. Headers
|
|
||||||
|
|
||||||
The header of a JSON Web Token is a standard JOSE header. The "typ" field
|
|
||||||
will be "JWT" and it will also contain the "alg" which identifies the
|
|
||||||
signing algorithm used to produce the signature. It also must have a "kid"
|
|
||||||
field, representing the ID of the key which was used to sign the token.
|
|
||||||
|
|
||||||
The "kid" field has to be in a libtrust fingerprint compatible format.
|
|
||||||
Such a format can be generated by following steps:
|
|
||||||
|
|
||||||
1. Take the DER encoded public key which the JWT token was signed against.
|
|
||||||
|
|
||||||
2. Create a SHA256 hash out of it and truncate to 240bits.
|
|
||||||
|
|
||||||
3. Split the result into 12 base32 encoded groups with `:` as delimiter.
|
|
||||||
|
|
||||||
Here is an example JOSE Header for a JSON Web Token (formatted with
|
|
||||||
whitespace for readability):
|
|
||||||
|
|
||||||
```
|
|
||||||
{
|
|
||||||
"typ": "JWT",
|
|
||||||
"alg": "ES256",
|
|
||||||
"kid": "PYYO:TEWU:V7JH:26JV:AQTZ:LJC3:SXVJ:XGHA:34F2:2LAQ:ZRMK:Z7Q6"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
It specifies that this object is going to be a JSON Web token signed using
|
|
||||||
the key with the given ID using the Elliptic Curve signature algorithm
|
|
||||||
using a SHA256 hash.
|
|
||||||
|
|
||||||
2. Claim Set
|
|
||||||
|
|
||||||
The Claim Set is a JSON struct containing these standard registered claim
|
|
||||||
name fields:
|
|
||||||
|
|
||||||
<dl>
|
|
||||||
<dt>
|
|
||||||
<code>iss</code> (Issuer)
|
|
||||||
</dt>
|
|
||||||
<dd>
|
|
||||||
The issuer of the token, typically the fqdn of the authorization
|
|
||||||
server.
|
|
||||||
</dd>
|
|
||||||
<dt>
|
|
||||||
<code>sub</code> (Subject)
|
|
||||||
</dt>
|
|
||||||
<dd>
|
|
||||||
The subject of the token; the name or id of the client which
|
|
||||||
requested it. This should be empty (`""`) if the client did not
|
|
||||||
authenticate.
|
|
||||||
</dd>
|
|
||||||
<dt>
|
|
||||||
<code>aud</code> (Audience)
|
|
||||||
</dt>
|
|
||||||
<dd>
|
|
||||||
The intended audience of the token; the name or id of the service
|
|
||||||
which will verify the token to authorize the client/subject.
|
|
||||||
</dd>
|
|
||||||
<dt>
|
|
||||||
<code>exp</code> (Expiration)
|
|
||||||
</dt>
|
|
||||||
<dd>
|
|
||||||
The token should only be considered valid up to this specified date
|
|
||||||
and time.
|
|
||||||
</dd>
|
|
||||||
<dt>
|
|
||||||
<code>nbf</code> (Not Before)
|
|
||||||
</dt>
|
|
||||||
<dd>
|
|
||||||
The token should not be considered valid before this specified date
|
|
||||||
and time.
|
|
||||||
</dd>
|
|
||||||
<dt>
|
|
||||||
<code>iat</code> (Issued At)
|
|
||||||
</dt>
|
|
||||||
<dd>
|
|
||||||
Specifies the date and time which the Authorization server
|
|
||||||
generated this token.
|
|
||||||
</dd>
|
|
||||||
<dt>
|
|
||||||
<code>jti</code> (JWT ID)
|
|
||||||
</dt>
|
|
||||||
<dd>
|
|
||||||
A unique identifier for this token. Can be used by the intended
|
|
||||||
audience to prevent replays of the token.
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
|
|
||||||
The Claim Set will also contain a private claim name unique to this
|
|
||||||
authorization server specification:
|
|
||||||
|
|
||||||
<dl>
|
|
||||||
<dt>
|
|
||||||
<code>access</code>
|
|
||||||
</dt>
|
|
||||||
<dd>
|
|
||||||
An array of access entry objects with the following fields:
|
|
||||||
|
|
||||||
<dl>
|
|
||||||
<dt>
|
|
||||||
<code>type</code>
|
|
||||||
</dt>
|
|
||||||
<dd>
|
|
||||||
The type of resource hosted by the service.
|
|
||||||
</dd>
|
|
||||||
<dt>
|
|
||||||
<code>name</code>
|
|
||||||
</dt>
|
|
||||||
<dd>
|
|
||||||
The name of the resource of the given type hosted by the
|
|
||||||
service.
|
|
||||||
</dd>
|
|
||||||
<dt>
|
|
||||||
<code>actions</code>
|
|
||||||
</dt>
|
|
||||||
<dd>
|
|
||||||
An array of strings which give the actions authorized on
|
|
||||||
this resource.
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
|
|
||||||
Here is an example of such a JWT Claim Set (formatted with whitespace for
|
|
||||||
readability):
|
|
||||||
|
|
||||||
```
|
|
||||||
{
|
|
||||||
"iss": "auth.docker.com",
|
|
||||||
"sub": "jlhawn",
|
|
||||||
"aud": "registry.docker.com",
|
|
||||||
"exp": 1415387315,
|
|
||||||
"nbf": 1415387015,
|
|
||||||
"iat": 1415387015,
|
|
||||||
"jti": "tYJCO1c6cnyy7kAn0c7rKPgbV1H1bFws",
|
|
||||||
"access": [
|
|
||||||
{
|
|
||||||
"type": "repository",
|
|
||||||
"name": "samalba/my-app",
|
|
||||||
"actions": [
|
|
||||||
"pull",
|
|
||||||
"push"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
3. Signature
|
|
||||||
|
|
||||||
The authorization server will produce a JOSE header and Claim Set with no
|
|
||||||
extraneous whitespace, i.e., the JOSE Header from above would be
|
|
||||||
|
|
||||||
```
|
|
||||||
{"typ":"JWT","alg":"ES256","kid":"PYYO:TEWU:V7JH:26JV:AQTZ:LJC3:SXVJ:XGHA:34F2:2LAQ:ZRMK:Z7Q6"}
|
|
||||||
```
|
|
||||||
|
|
||||||
and the Claim Set from above would be
|
|
||||||
|
|
||||||
```
|
|
||||||
{"iss":"auth.docker.com","sub":"jlhawn","aud":"registry.docker.com","exp":1415387315,"nbf":1415387015,"iat":1415387015,"jti":"tYJCO1c6cnyy7kAn0c7rKPgbV1H1bFws","access":[{"type":"repository","name":"samalba/my-app","actions":["push","pull"]}]}
|
|
||||||
```
|
|
||||||
|
|
||||||
The utf-8 representation of this JOSE header and Claim Set are then
|
|
||||||
url-safe base64 encoded (sans trailing '=' buffer), producing:
|
|
||||||
|
|
||||||
```
|
|
||||||
eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IlBZWU86VEVXVTpWN0pIOjI2SlY6QVFUWjpMSkMzOlNYVko6WEdIQTozNEYyOjJMQVE6WlJNSzpaN1E2In0
|
|
||||||
```
|
|
||||||
|
|
||||||
for the JOSE Header and
|
|
||||||
|
|
||||||
```
|
|
||||||
eyJpc3MiOiJhdXRoLmRvY2tlci5jb20iLCJzdWIiOiJqbGhhd24iLCJhdWQiOiJyZWdpc3RyeS5kb2NrZXIuY29tIiwiZXhwIjoxNDE1Mzg3MzE1LCJuYmYiOjE0MTUzODcwMTUsImlhdCI6MTQxNTM4NzAxNSwianRpIjoidFlKQ08xYzZjbnl5N2tBbjBjN3JLUGdiVjFIMWJGd3MiLCJhY2Nlc3MiOlt7InR5cGUiOiJyZXBvc2l0b3J5IiwibmFtZSI6InNhbWFsYmEvbXktYXBwIiwiYWN0aW9ucyI6WyJwdXNoIl19XX0
|
|
||||||
```
|
|
||||||
|
|
||||||
for the Claim Set. These two are concatenated using a '.' character,
|
|
||||||
yielding the string:
|
|
||||||
|
|
||||||
```
|
|
||||||
eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IlBZWU86VEVXVTpWN0pIOjI2SlY6QVFUWjpMSkMzOlNYVko6WEdIQTozNEYyOjJMQVE6WlJNSzpaN1E2In0.eyJpc3MiOiJhdXRoLmRvY2tlci5jb20iLCJzdWIiOiJqbGhhd24iLCJhdWQiOiJyZWdpc3RyeS5kb2NrZXIuY29tIiwiZXhwIjoxNDE1Mzg3MzE1LCJuYmYiOjE0MTUzODcwMTUsImlhdCI6MTQxNTM4NzAxNSwianRpIjoidFlKQ08xYzZjbnl5N2tBbjBjN3JLUGdiVjFIMWJGd3MiLCJhY2Nlc3MiOlt7InR5cGUiOiJyZXBvc2l0b3J5IiwibmFtZSI6InNhbWFsYmEvbXktYXBwIiwiYWN0aW9ucyI6WyJwdXNoIl19XX0
|
|
||||||
```
|
|
||||||
|
|
||||||
This is then used as the payload to a the `ES256` signature algorithm
|
|
||||||
specified in the JOSE header and specified fully in [Section 3.4 of the JSON Web Algorithms (JWA)
|
|
||||||
draft specification](https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-38#section-3.4)
|
|
||||||
|
|
||||||
This example signature will use the following ECDSA key for the server:
|
|
||||||
|
|
||||||
```
|
|
||||||
{
|
|
||||||
"kty": "EC",
|
|
||||||
"crv": "P-256",
|
|
||||||
"kid": "PYYO:TEWU:V7JH:26JV:AQTZ:LJC3:SXVJ:XGHA:34F2:2LAQ:ZRMK:Z7Q6",
|
|
||||||
"d": "R7OnbfMaD5J2jl7GeE8ESo7CnHSBm_1N2k9IXYFrKJA",
|
|
||||||
"x": "m7zUpx3b-zmVE5cymSs64POG9QcyEpJaYCD82-549_Q",
|
|
||||||
"y": "dU3biz8sZ_8GPB-odm8Wxz3lNDr1xcAQQPQaOcr1fmc"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
A resulting signature of the above payload using this key is:
|
|
||||||
|
|
||||||
```
|
|
||||||
QhflHPfbd6eVF4lM9bwYpFZIV0PfikbyXuLx959ykRTBpe3CYnzs6YBK8FToVb5R47920PVLrh8zuLzdCr9t3w
|
|
||||||
```
|
|
||||||
|
|
||||||
Concatenating all of these together with a `.` character gives the
|
|
||||||
resulting JWT:
|
|
||||||
|
|
||||||
```
|
|
||||||
eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IlBZWU86VEVXVTpWN0pIOjI2SlY6QVFUWjpMSkMzOlNYVko6WEdIQTozNEYyOjJMQVE6WlJNSzpaN1E2In0.eyJpc3MiOiJhdXRoLmRvY2tlci5jb20iLCJzdWIiOiJqbGhhd24iLCJhdWQiOiJyZWdpc3RyeS5kb2NrZXIuY29tIiwiZXhwIjoxNDE1Mzg3MzE1LCJuYmYiOjE0MTUzODcwMTUsImlhdCI6MTQxNTM4NzAxNSwianRpIjoidFlKQ08xYzZjbnl5N2tBbjBjN3JLUGdiVjFIMWJGd3MiLCJhY2Nlc3MiOlt7InR5cGUiOiJyZXBvc2l0b3J5IiwibmFtZSI6InNhbWFsYmEvbXktYXBwIiwiYWN0aW9ucyI6WyJwdXNoIl19XX0.QhflHPfbd6eVF4lM9bwYpFZIV0PfikbyXuLx959ykRTBpe3CYnzs6YBK8FToVb5R47920PVLrh8zuLzdCr9t3w
|
|
||||||
```
|
|
||||||
|
|
||||||
This can now be placed in an HTTP response and returned to the client to use to
|
|
||||||
authenticate to the audience service:
|
|
||||||
|
|
||||||
|
|
||||||
```
|
|
||||||
HTTP/1.1 200 OK
|
|
||||||
Content-Type: application/json
|
|
||||||
|
|
||||||
{"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IlBZWU86VEVXVTpWN0pIOjI2SlY6QVFUWjpMSkMzOlNYVko6WEdIQTozNEYyOjJMQVE6WlJNSzpaN1E2In0.eyJpc3MiOiJhdXRoLmRvY2tlci5jb20iLCJzdWIiOiJqbGhhd24iLCJhdWQiOiJyZWdpc3RyeS5kb2NrZXIuY29tIiwiZXhwIjoxNDE1Mzg3MzE1LCJuYmYiOjE0MTUzODcwMTUsImlhdCI6MTQxNTM4NzAxNSwianRpIjoidFlKQ08xYzZjbnl5N2tBbjBjN3JLUGdiVjFIMWJGd3MiLCJhY2Nlc3MiOlt7InR5cGUiOiJyZXBvc2l0b3J5IiwibmFtZSI6InNhbWFsYmEvbXktYXBwIiwiYWN0aW9ucyI6WyJwdXNoIl19XX0.QhflHPfbd6eVF4lM9bwYpFZIV0PfikbyXuLx959ykRTBpe3CYnzs6YBK8FToVb5R47920PVLrh8zuLzdCr9t3w"}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Using the signed token
|
|
||||||
|
|
||||||
Once the client has a token, it will try the registry request again with the
|
|
||||||
token placed in the HTTP `Authorization` header like so:
|
|
||||||
|
|
||||||
```
|
|
||||||
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IkJWM0Q6MkFWWjpVQjVaOktJQVA6SU5QTDo1RU42Ok40SjQ6Nk1XTzpEUktFOkJWUUs6M0ZKTDpQT1RMIn0.eyJpc3MiOiJhdXRoLmRvY2tlci5jb20iLCJzdWIiOiJCQ0NZOk9VNlo6UUVKNTpXTjJDOjJBVkM6WTdZRDpBM0xZOjQ1VVc6NE9HRDpLQUxMOkNOSjU6NUlVTCIsImF1ZCI6InJlZ2lzdHJ5LmRvY2tlci5jb20iLCJleHAiOjE0MTUzODczMTUsIm5iZiI6MTQxNTM4NzAxNSwiaWF0IjoxNDE1Mzg3MDE1LCJqdGkiOiJ0WUpDTzFjNmNueXk3a0FuMGM3cktQZ2JWMUgxYkZ3cyIsInNjb3BlIjoiamxoYXduOnJlcG9zaXRvcnk6c2FtYWxiYS9teS1hcHA6cHVzaCxwdWxsIGpsaGF3bjpuYW1lc3BhY2U6c2FtYWxiYTpwdWxsIn0.Y3zZSwaZPqy4y9oRBVRImZyv3m_S9XDHF1tWwN7mL52C_IiA73SJkWVNsvNqpJIn5h7A2F8biv_S2ppQ1lgkbw
|
|
||||||
```
|
|
||||||
|
|
||||||
This is also described in [Section 2.1 of RFC 6750: The OAuth 2.0 Authorization Framework: Bearer Token Usage](https://tools.ietf.org/html/rfc6750#section-2.1)
|
|
||||||
|
|
||||||
## Verifying the token
|
|
||||||
|
|
||||||
The registry must now verify the token presented by the user by inspecting the
|
|
||||||
claim set within. The registry will:
|
|
||||||
|
|
||||||
- Ensure that the issuer (`iss` claim) is an authority it trusts.
|
|
||||||
- Ensure that the registry identifies as the audience (`aud` claim).
|
|
||||||
- Check that the current time is between the `nbf` and `exp` claim times.
|
|
||||||
- If enforcing single-use tokens, check that the JWT ID (`jti` claim) value has
|
|
||||||
not been seen before.
|
|
||||||
- To enforce this, the registry may keep a record of `jti`s it has seen for
|
|
||||||
up to the `exp` time of the token to prevent token replays.
|
|
||||||
- Check the `access` claim value and use the identified resources and the list
|
|
||||||
of actions authorized to determine whether the token grants the required
|
|
||||||
level of access for the operation the client is attempting to perform.
|
|
||||||
- Verify that the signature of the token is valid.
|
|
||||||
|
|
||||||
If any of these requirements are not met, the registry will return a
|
|
||||||
`403 Forbidden` response to indicate that the token is invalid.
|
|
||||||
|
|
||||||
**Note**: it is only at this point in the workflow that an authorization error
|
|
||||||
may occur. The token server should *not* return errors when the user does not
|
|
||||||
have the requested authorization. Instead, the returned token should indicate
|
|
||||||
whatever of the requested scope the client does have (the intersection of
|
|
||||||
requested and granted access). If the token does not supply proper
|
|
||||||
authorization then the registry will return the appropriate error.
|
|
||||||
|
|
||||||
At no point in this process should the registry need to call back to the
|
|
||||||
authorization server. The registry only needs to be supplied with the trusted
|
|
||||||
public keys to verify the token signatures.
|
|
|
@ -1,183 +0,0 @@
|
||||||
---
|
|
||||||
description: Specifies the Docker Registry v2 authentication
|
|
||||||
keywords: registry, on-prem, images, tags, repository, distribution, oauth2, advanced
|
|
||||||
title: Docker Registry v2 authentication using OAuth2
|
|
||||||
---
|
|
||||||
|
|
||||||
This document describes support for the OAuth2 protocol within the authorization
|
|
||||||
server. [RFC6749](https://tools.ietf.org/html/rfc6749) should be used as a
|
|
||||||
reference for the protocol and HTTP endpoints described here.
|
|
||||||
|
|
||||||
## Refresh token format
|
|
||||||
|
|
||||||
The format of the refresh token is completely opaque to the client and should be
|
|
||||||
determined by the authorization server. The authorization should ensure the
|
|
||||||
token is sufficiently long and is responsible for storing any information about
|
|
||||||
long-lived tokens which may be needed for revoking. Any information stored
|
|
||||||
inside the token will not be extracted and presented by clients.
|
|
||||||
|
|
||||||
## Getting a token
|
|
||||||
|
|
||||||
POST /token
|
|
||||||
|
|
||||||
#### Headers
|
|
||||||
Content-Type: application/x-www-form-urlencoded
|
|
||||||
|
|
||||||
#### Post parameters
|
|
||||||
|
|
||||||
<dl>
|
|
||||||
<dt>
|
|
||||||
<code>grant_type</code>
|
|
||||||
</dt>
|
|
||||||
<dd>
|
|
||||||
(REQUIRED) Type of grant used to get token. When getting a refresh token
|
|
||||||
using credentials this type should be set to "password" and have the
|
|
||||||
accompanying username and password paramters. Type "authorization_code"
|
|
||||||
is reserved for future use for authenticating to an authorization server
|
|
||||||
without having to send credentials directly from the client. When
|
|
||||||
requesting an access token with a refresh token this should be set to
|
|
||||||
"refresh_token".
|
|
||||||
</dd>
|
|
||||||
<dt>
|
|
||||||
<code>service</code>
|
|
||||||
</dt>
|
|
||||||
<dd>
|
|
||||||
(REQUIRED) The name of the service which hosts the resource to get
|
|
||||||
access for. Refresh tokens will only be good for getting tokens for
|
|
||||||
this service.
|
|
||||||
</dd>
|
|
||||||
<dt>
|
|
||||||
<code>client_id</code>
|
|
||||||
</dt>
|
|
||||||
<dd>
|
|
||||||
(REQUIRED) String identifying the client. This client_id does not need
|
|
||||||
to be registered with the authorization server but should be set to a
|
|
||||||
meaningful value in order to allow auditing keys created by unregistered
|
|
||||||
clients. Accepted syntax is defined in
|
|
||||||
[RFC6749 Appendix A.1](https://tools.ietf.org/html/rfc6749#appendix-A.1)
|
|
||||||
</dd>
|
|
||||||
<dt>
|
|
||||||
<code>access_type</code>
|
|
||||||
</dt>
|
|
||||||
<dd>
|
|
||||||
(OPTIONAL) Access which is being requested. If "offline" is provided
|
|
||||||
then a refresh token will be returned. The default is "online" only
|
|
||||||
returning short lived access token. If the grant type is "refresh_token"
|
|
||||||
this will only return the same refresh token and not a new one.
|
|
||||||
</dd>
|
|
||||||
<dt>
|
|
||||||
<code>scope</code>
|
|
||||||
</dt>
|
|
||||||
<dd>
|
|
||||||
(OPTIONAL) The resource in question, formatted as one of the space-delimited
|
|
||||||
entries from the <code>scope</code> parameters from the <code>WWW-Authenticate</code> header
|
|
||||||
shown above. This query parameter should only be specified once but may
|
|
||||||
contain multiple scopes using the scope list format defined in the scope
|
|
||||||
grammar. If multiple <code>scope</code> is provided from
|
|
||||||
<code>WWW-Authenticate</code> header the scopes should first be
|
|
||||||
converted to a scope list before requesting the token. The above example
|
|
||||||
would be specified as: <code>scope=repository:samalba/my-app:push</code>.
|
|
||||||
When requesting a refresh token the scopes may be empty since the
|
|
||||||
refresh token will not be limited by this scope, only the provided short
|
|
||||||
lived access token will have the scope limitation.
|
|
||||||
</dd>
|
|
||||||
<dt>
|
|
||||||
<code>refresh_token</code>
|
|
||||||
</dt>
|
|
||||||
<dd>
|
|
||||||
(OPTIONAL) The refresh token to use for authentication when grant type "refresh_token" is used.
|
|
||||||
</dd>
|
|
||||||
<dt>
|
|
||||||
<code>username</code>
|
|
||||||
</dt>
|
|
||||||
<dd>
|
|
||||||
(OPTIONAL) The username to use for authentication when grant type "password" is used.
|
|
||||||
</dd>
|
|
||||||
<dt>
|
|
||||||
<code>password</code>
|
|
||||||
</dt>
|
|
||||||
<dd>
|
|
||||||
(OPTIONAL) The password to use for authentication when grant type "password" is used.
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
|
|
||||||
#### Response fields
|
|
||||||
|
|
||||||
<dl>
|
|
||||||
<dt>
|
|
||||||
<code>access_token</code>
|
|
||||||
</dt>
|
|
||||||
<dd>
|
|
||||||
(REQUIRED) An opaque <code>Bearer</code> token that clients should
|
|
||||||
supply to subsequent requests in the <code>Authorization</code> header.
|
|
||||||
This token should not be attempted to be parsed or understood by the
|
|
||||||
client but treated as opaque string.
|
|
||||||
</dd>
|
|
||||||
<dt>
|
|
||||||
<code>scope</code>
|
|
||||||
</dt>
|
|
||||||
<dd>
|
|
||||||
(REQUIRED) The scope granted inside the access token. This may be the
|
|
||||||
same scope as requested or a subset. This requirement is stronger than
|
|
||||||
specified in [RFC6749 Section 4.2.2](https://tools.ietf.org/html/rfc6749#section-4.2.2)
|
|
||||||
by strictly requiring the scope in the return value.
|
|
||||||
</dd>
|
|
||||||
<dt>
|
|
||||||
<code>expires_in</code>
|
|
||||||
</dt>
|
|
||||||
<dd>
|
|
||||||
(REQUIRED) The duration in seconds since the token was issued that it
|
|
||||||
will remain valid. When omitted, this defaults to 60 seconds. For
|
|
||||||
compatibility with older clients, a token should never be returned with
|
|
||||||
less than 60 seconds to live.
|
|
||||||
</dd>
|
|
||||||
<dt>
|
|
||||||
<code>issued_at</code>
|
|
||||||
</dt>
|
|
||||||
<dd>
|
|
||||||
(Optional) The <a href="https://www.ietf.org/rfc/rfc3339.txt">RFC3339</a>-serialized UTC
|
|
||||||
standard time at which a given token was issued. If <code>issued_at</code> is omitted, the
|
|
||||||
expiration is from when the token exchange completed.
|
|
||||||
</dd>
|
|
||||||
<dt>
|
|
||||||
<code>refresh_token</code>
|
|
||||||
</dt>
|
|
||||||
<dd>
|
|
||||||
(Optional) Token which can be used to get additional access tokens for
|
|
||||||
the same subject with different scopes. This token should be kept secure
|
|
||||||
by the client and only sent to the authorization server which issues
|
|
||||||
bearer tokens. This field will only be set when `access_type=offline` is
|
|
||||||
provided in the request.
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
|
|
||||||
|
|
||||||
#### Example getting refresh token
|
|
||||||
|
|
||||||
```
|
|
||||||
POST /token HTTP/1.1
|
|
||||||
Host: auth.docker.io
|
|
||||||
Content-Type: application/x-www-form-urlencoded
|
|
||||||
|
|
||||||
grant_type=password&username=johndoe&password=A3ddj3w&service=hub.docker.io&client_id=dockerengine&access_type=offline
|
|
||||||
|
|
||||||
HTTP/1.1 200 OK
|
|
||||||
Content-Type: application/json
|
|
||||||
|
|
||||||
{"refresh_token":"kas9Da81Dfa8","access_token":"eyJhbGciOiJFUzI1NiIsInR5","expires_in":900,"scope":""}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Example refreshing an Access Token
|
|
||||||
|
|
||||||
```
|
|
||||||
POST /token HTTP/1.1
|
|
||||||
Host: auth.docker.io
|
|
||||||
Content-Type: application/x-www-form-urlencoded
|
|
||||||
|
|
||||||
grant_type=refresh_token&refresh_token=kas9Da81Dfa8&service=registry-1.docker.io&client_id=dockerengine&scope=repository:samalba/my-app:pull,push
|
|
||||||
|
|
||||||
HTTP/1.1 200 OK
|
|
||||||
Content-Type: application/json
|
|
||||||
|
|
||||||
{"refresh_token":"kas9Da81Dfa8","access_token":"eyJhbGciOiJFUzI1NiIsInR5":"expires_in":900,"scope":"repository:samalba/my-app:pull,repository:samalba/my-app:push"}
|
|
||||||
```
|
|
|
@ -1,135 +0,0 @@
|
||||||
---
|
|
||||||
description: Describes the scope and access fields used for registry authorization tokens
|
|
||||||
keywords: registry, on-prem, images, tags, repository, distribution, advanced, access, scope
|
|
||||||
title: Docker Registry token scope and access
|
|
||||||
---
|
|
||||||
|
|
||||||
Tokens used by the registry are always restricted what resources they may
|
|
||||||
be used to access, where those resources may be accessed, and what actions
|
|
||||||
may be done on those resources. Tokens always have the context of a user which
|
|
||||||
the token was originally created for. This document describes how these
|
|
||||||
restrictions are represented and enforced by the authorization server and
|
|
||||||
resource providers.
|
|
||||||
|
|
||||||
## Scope Components
|
|
||||||
|
|
||||||
### Subject (Authenticated User)
|
|
||||||
|
|
||||||
The subject represents the user for which a token is valid. Any actions
|
|
||||||
performed using an access token should be considered on behalf of the subject.
|
|
||||||
This is included in the `sub` field of access token JWT. A refresh token should
|
|
||||||
be limited to a single subject and only be able to give out access tokens for
|
|
||||||
that subject.
|
|
||||||
|
|
||||||
### Audience (Resource Provider)
|
|
||||||
|
|
||||||
The audience represents a resource provider which is intended to be able to
|
|
||||||
perform the actions specified in the access token. Any resource provider which
|
|
||||||
does not match the audience should not use that access token. The audience is
|
|
||||||
included in the `aud` field of the access token JWT. A refresh token should be
|
|
||||||
limited to a single audience and only be able to give out access tokens for that
|
|
||||||
audience.
|
|
||||||
|
|
||||||
### Resource Type
|
|
||||||
|
|
||||||
The resource type represents the type of resource which the resource name is
|
|
||||||
intended to represent. This type may be specific to a resource provider but must
|
|
||||||
be understood by the authorization server in order to validate the subject
|
|
||||||
is authorized for a specific resource.
|
|
||||||
|
|
||||||
#### Example Resource Types
|
|
||||||
|
|
||||||
- `repository` - represents a single repository within a registry. A
|
|
||||||
repository may represent many manifest or content blobs, but the resource type
|
|
||||||
is considered the collections of those items. Actions which may be performed on
|
|
||||||
a `repository` are `pull` for accessing the collection and `push` for adding to
|
|
||||||
it.
|
|
||||||
|
|
||||||
### Resource Name
|
|
||||||
|
|
||||||
The resource name represent the name which identifies a resource for a resource
|
|
||||||
provider. A resource is identified by this name and the provided resource type.
|
|
||||||
An example of a resource name would be the name component of an image tag, such
|
|
||||||
as "samalba/myapp" or "hostname/samalba/myapp".
|
|
||||||
|
|
||||||
### Resource Actions
|
|
||||||
|
|
||||||
The resource actions define the actions which the access token allows to be
|
|
||||||
performed on the identified resource. These actions are type specific but will
|
|
||||||
normally have actions identifying read and write access on the resource. Example
|
|
||||||
for the `repository` type are `pull` for read access and `push` for write
|
|
||||||
access.
|
|
||||||
|
|
||||||
## Authorization Server Use
|
|
||||||
|
|
||||||
Each access token request may include a scope and an audience. The subject is
|
|
||||||
always derived from the passed in credentials or refresh token. When using
|
|
||||||
a refresh token the passed in audience must match the audience defined for
|
|
||||||
the refresh token. The audience (resource provider) is provided using the
|
|
||||||
`service` field. Multiple resource scopes may be provided using multiple `scope`
|
|
||||||
fields on the `GET` request. The `POST` request only takes in a single
|
|
||||||
`scope` field but may use a space to separate a list of multiple resource
|
|
||||||
scopes.
|
|
||||||
|
|
||||||
### Resource Scope Grammar
|
|
||||||
|
|
||||||
```
|
|
||||||
scope := resourcescope [ ' ' resourcescope ]*
|
|
||||||
resourcescope := resourcetype ":" resourcename ":" action [ ',' action ]*
|
|
||||||
resourcetype := /[a-z]*/
|
|
||||||
resourcename := [ hostname '/' ] component [ '/' component ]*
|
|
||||||
hostname := hostcomponent ['.' hostcomponent]* [':' port-number]
|
|
||||||
hostcomponent := /([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])/
|
|
||||||
port-number := /[0-9]+/
|
|
||||||
action := /[a-z]*/
|
|
||||||
component := alpha-numeric [ separator alpha-numeric ]*
|
|
||||||
alpha-numeric := /[a-z0-9]+/
|
|
||||||
separator := /[_.]|__|[-]*/
|
|
||||||
```
|
|
||||||
Full reference grammar is defined
|
|
||||||
[here](https://godoc.org/github.com/docker/distribution/reference). Currently
|
|
||||||
the scope name grammar is a subset of the reference grammar.
|
|
||||||
|
|
||||||
> **NOTE:** that the `resourcename` may contain one `:` due to a possible port
|
|
||||||
> number in the hostname component of the `resourcename`, so a naive
|
|
||||||
> implementation that interprets the first three `:`-delimited tokens of a
|
|
||||||
> `scope` to be the `resourcetype`, `resourcename`, and a list of `action`
|
|
||||||
> would be insufficient.
|
|
||||||
|
|
||||||
## Resource Provider Use
|
|
||||||
|
|
||||||
Once a resource provider has verified the authenticity of the scope through
|
|
||||||
JWT access token verification, the resource provider must ensure that scope
|
|
||||||
satisfies the request. The resource provider should match the given audience
|
|
||||||
according to name or URI the resource provider uses to identify itself. Any
|
|
||||||
denial based on subject is not defined here and is up to resource provider, the
|
|
||||||
subject is mainly provided for audit logs and any other user-specific rules
|
|
||||||
which may need to be provided but are not defined by the authorization server.
|
|
||||||
|
|
||||||
The resource provider must ensure that ANY resource being accessed as the
|
|
||||||
result of a request has the appropriate access scope. Both the resource type
|
|
||||||
and resource name must match the accessed resource and an appropriate action
|
|
||||||
scope must be included.
|
|
||||||
|
|
||||||
When appropriate authorization is not provided either due to lack of scope
|
|
||||||
or missing token, the resource provider to return a `WWW-AUTHENTICATE` HTTP
|
|
||||||
header with the `realm` as the authorization server, the `service` as the
|
|
||||||
expected audience identifying string, and a `scope` field for each required
|
|
||||||
resource scope to complete the request.
|
|
||||||
|
|
||||||
## JWT Access Tokens
|
|
||||||
|
|
||||||
Each JWT access token may only have a single subject and audience but multiple
|
|
||||||
resource scopes. The subject and audience are put into standard JWT fields
|
|
||||||
`sub` and `aud`. The resource scope is put into the `access` field. The
|
|
||||||
structure of the access field can be seen in the
|
|
||||||
[jwt documentation](jwt.md).
|
|
||||||
|
|
||||||
## Refresh Tokens
|
|
||||||
|
|
||||||
A refresh token must be defined for a single subject and audience. Further
|
|
||||||
restricting scope to specific type, name, and actions combinations should be
|
|
||||||
done by fetching an access token using the refresh token. Since the refresh
|
|
||||||
token is not scoped to specific resources for an audience, extra care should
|
|
||||||
be taken to only use the refresh token to negotiate new access tokens directly
|
|
||||||
with the authorization server, and never with a resource provider.
|
|
|
@ -1,250 +0,0 @@
|
||||||
---
|
|
||||||
description: Specifies the Docker Registry v2 authentication
|
|
||||||
keywords: registry, on-prem, images, tags, repository, distribution, Bearer authentication, advanced
|
|
||||||
title: Docker Registry v2 authentication via central service
|
|
||||||
---
|
|
||||||
|
|
||||||
This document outlines the v2 Docker registry authentication scheme:
|
|
||||||
|
|
||||||
![v2 registry auth](../../images/v2-registry-auth.png)
|
|
||||||
|
|
||||||
1. Attempt to begin a push/pull operation with the registry.
|
|
||||||
2. If the registry requires authorization it will return a `401 Unauthorized`
|
|
||||||
HTTP response with information on how to authenticate.
|
|
||||||
3. The registry client makes a request to the authorization service for a
|
|
||||||
Bearer token.
|
|
||||||
4. The authorization service returns an opaque Bearer token representing the
|
|
||||||
client's authorized access.
|
|
||||||
5. The client retries the original request with the Bearer token embedded in
|
|
||||||
the request's Authorization header.
|
|
||||||
6. The Registry authorizes the client by validating the Bearer token and the
|
|
||||||
claim set embedded within it and begins the push/pull session as usual.
|
|
||||||
|
|
||||||
## Requirements
|
|
||||||
|
|
||||||
- Registry clients which can understand and respond to token auth challenges
|
|
||||||
returned by the resource server.
|
|
||||||
- An authorization server capable of managing access controls to their
|
|
||||||
resources hosted by any given service (such as repositories in a Docker
|
|
||||||
Registry).
|
|
||||||
- A Docker Registry capable of trusting the authorization server to sign tokens
|
|
||||||
which clients can use for authorization and the ability to verify these
|
|
||||||
tokens for single use or for use during a sufficiently short period of time.
|
|
||||||
|
|
||||||
## Authorization Server Endpoint Descriptions
|
|
||||||
|
|
||||||
The described server is meant to serve as a standalone access control manager
|
|
||||||
for resources hosted by other services which wish to authenticate and manage
|
|
||||||
authorizations using a separate access control manager.
|
|
||||||
|
|
||||||
A service like this is used by the official Docker Registry to authenticate
|
|
||||||
clients and verify their authorization to Docker image repositories.
|
|
||||||
|
|
||||||
As of Docker 1.6, the registry client within the Docker Engine has been updated
|
|
||||||
to handle such an authorization workflow.
|
|
||||||
|
|
||||||
## How to authenticate
|
|
||||||
|
|
||||||
Registry V1 clients first contact the index to initiate a push or pull. Under
|
|
||||||
the Registry V2 workflow, clients should contact the registry first. If the
|
|
||||||
registry server requires authentication it will return a `401 Unauthorized`
|
|
||||||
response with a `WWW-Authenticate` header detailing how to authenticate to this
|
|
||||||
registry.
|
|
||||||
|
|
||||||
For example, say I (username `jlhawn`) am attempting to push an image to the
|
|
||||||
repository `samalba/my-app`. For the registry to authorize this, I will need
|
|
||||||
`push` access to the `samalba/my-app` repository. The registry will first
|
|
||||||
return this response:
|
|
||||||
|
|
||||||
```
|
|
||||||
HTTP/1.1 401 Unauthorized
|
|
||||||
Content-Type: application/json; charset=utf-8
|
|
||||||
Docker-Distribution-Api-Version: registry/2.0
|
|
||||||
Www-Authenticate: Bearer realm="https://auth.docker.io/token",service="registry.docker.io",scope="repository:samalba/my-app:pull,push"
|
|
||||||
Date: Thu, 10 Sep 2015 19:32:31 GMT
|
|
||||||
Content-Length: 235
|
|
||||||
Strict-Transport-Security: max-age=31536000
|
|
||||||
|
|
||||||
{"errors":[{"code":"UNAUTHORIZED","message":"access to the requested resource is not authorized","detail":[{"Type":"repository","Name":"samalba/my-app","Action":"pull"},{"Type":"repository","Name":"samalba/my-app","Action":"push"}]}]}
|
|
||||||
```
|
|
||||||
|
|
||||||
Note the HTTP Response Header indicating the auth challenge:
|
|
||||||
|
|
||||||
```
|
|
||||||
Www-Authenticate: Bearer realm="https://auth.docker.io/token",service="registry.docker.io",scope="repository:samalba/my-app:pull,push"
|
|
||||||
```
|
|
||||||
|
|
||||||
This format is documented in [Section 3 of RFC 6750: The OAuth 2.0 Authorization
|
|
||||||
Framework: Bearer Token Usage](https://tools.ietf.org/html/rfc6750#section-3)
|
|
||||||
|
|
||||||
This challenge indicates that the registry requires a token issued by the
|
|
||||||
specified token server and that the request the client is attempting will
|
|
||||||
need to include sufficient access entries in its claim set. To respond to this
|
|
||||||
challenge, the client will need to make a `GET` request to the URL
|
|
||||||
`https://auth.docker.io/token` using the `service` and `scope` values from the
|
|
||||||
`WWW-Authenticate` header.
|
|
||||||
|
|
||||||
## Requesting a Token
|
|
||||||
|
|
||||||
Defines getting a bearer and refresh token using the token endpoint.
|
|
||||||
|
|
||||||
#### Query Parameters
|
|
||||||
|
|
||||||
<dl>
|
|
||||||
<dt>
|
|
||||||
<code>service</code>
|
|
||||||
</dt>
|
|
||||||
<dd>
|
|
||||||
The name of the service which hosts the resource.
|
|
||||||
</dd>
|
|
||||||
<dt>
|
|
||||||
<code>offline_token</code>
|
|
||||||
</dt>
|
|
||||||
<dd>
|
|
||||||
Whether to return a refresh token along with the bearer token. A refresh
|
|
||||||
token is capable of getting additional bearer tokens for the same
|
|
||||||
subject with different scopes. The refresh token does not have an
|
|
||||||
expiration and should be considered completely opaque to the client.
|
|
||||||
</dd>
|
|
||||||
<dt>
|
|
||||||
<code>client_id</code>
|
|
||||||
</dt>
|
|
||||||
<dd>
|
|
||||||
String identifying the client. This client_id does not need
|
|
||||||
to be registered with the authorization server but should be set to a
|
|
||||||
meaningful value in order to allow auditing keys created by unregistered
|
|
||||||
clients. Accepted syntax is defined in
|
|
||||||
[RFC6749 Appendix A.1](https://tools.ietf.org/html/rfc6749#appendix-A.1).
|
|
||||||
</dd>
|
|
||||||
<dt>
|
|
||||||
<code>scope</code>
|
|
||||||
</dt>
|
|
||||||
<dd>
|
|
||||||
The resource in question, formatted as one of the space-delimited
|
|
||||||
entries from the <code>scope</code> parameters from the <code>WWW-Authenticate</code> header
|
|
||||||
shown above. This query parameter should be specified multiple times if
|
|
||||||
there is more than one <code>scope</code> entry from the <code>WWW-Authenticate</code>
|
|
||||||
header. The above example would be specified as:
|
|
||||||
<code>scope=repository:samalba/my-app:push</code>. The scope field may
|
|
||||||
be empty to request a refresh token without providing any resource
|
|
||||||
permissions to the returned bearer token.
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
|
|
||||||
|
|
||||||
#### Token Response Fields
|
|
||||||
|
|
||||||
<dl>
|
|
||||||
<dt>
|
|
||||||
<code>token</code>
|
|
||||||
</dt>
|
|
||||||
<dd>
|
|
||||||
An opaque <code>Bearer</code> token that clients should supply to subsequent
|
|
||||||
requests in the <code>Authorization</code> header.
|
|
||||||
</dd>
|
|
||||||
<dt>
|
|
||||||
<code>access_token</code>
|
|
||||||
</dt>
|
|
||||||
<dd>
|
|
||||||
For compatibility with OAuth 2.0, we will also accept <code>token</code> under the name
|
|
||||||
<code>access_token</code>. At least one of these fields <b>must</b> be specified, but
|
|
||||||
both may also appear (for compatibility with older clients). When both are specified,
|
|
||||||
they should be equivalent; if they differ the client's choice is undefined.
|
|
||||||
</dd>
|
|
||||||
<dt>
|
|
||||||
<code>expires_in</code>
|
|
||||||
</dt>
|
|
||||||
<dd>
|
|
||||||
(Optional) The duration in seconds since the token was issued that it
|
|
||||||
will remain valid. When omitted, this defaults to 60 seconds. For
|
|
||||||
compatibility with older clients, a token should never be returned with
|
|
||||||
less than 60 seconds to live.
|
|
||||||
</dd>
|
|
||||||
<dt>
|
|
||||||
<code>issued_at</code>
|
|
||||||
</dt>
|
|
||||||
<dd>
|
|
||||||
(Optional) The <a href="https://www.ietf.org/rfc/rfc3339.txt">RFC3339</a>-serialized UTC
|
|
||||||
standard time at which a given token was issued. If <code>issued_at</code> is omitted, the
|
|
||||||
expiration is from when the token exchange completed.
|
|
||||||
</dd>
|
|
||||||
<dt>
|
|
||||||
<code>refresh_token</code>
|
|
||||||
</dt>
|
|
||||||
<dd>
|
|
||||||
(Optional) Token which can be used to get additional access tokens for
|
|
||||||
the same subject with different scopes. This token should be kept secure
|
|
||||||
by the client and only sent to the authorization server which issues
|
|
||||||
bearer tokens. This field will only be set when `offline_token=true` is
|
|
||||||
provided in the request.
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
|
|
||||||
#### Example
|
|
||||||
|
|
||||||
For this example, the client makes an HTTP GET request to the following URL:
|
|
||||||
|
|
||||||
```
|
|
||||||
https://auth.docker.io/token?service=registry.docker.io&scope=repository:samalba/my-app:pull,push
|
|
||||||
```
|
|
||||||
|
|
||||||
The token server should first attempt to authenticate the client using any
|
|
||||||
authentication credentials provided with the request. From Docker 1.11 the
|
|
||||||
Docker engine supports both Basic Authentication and [OAuth2](oauth.md) for
|
|
||||||
getting tokens. Docker 1.10 and before, the registry client in the Docker Engine
|
|
||||||
only supports Basic Authentication. If an attempt to authenticate to the token
|
|
||||||
server fails, the token server should return a `401 Unauthorized` response
|
|
||||||
indicating that the provided credentials are invalid.
|
|
||||||
|
|
||||||
Whether the token server requires authentication is up to the policy of that
|
|
||||||
access control provider. Some requests may require authentication to determine
|
|
||||||
access (such as pushing or pulling a private repository) while others may not
|
|
||||||
(such as pulling from a public repository).
|
|
||||||
|
|
||||||
After authenticating the client (which may simply be an anonymous client if
|
|
||||||
no attempt was made to authenticate), the token server must next query its
|
|
||||||
access control list to determine whether the client has the requested scope. In
|
|
||||||
this example request, if I have authenticated as user `jlhawn`, the token
|
|
||||||
server will determine what access I have to the repository `samalba/my-app`
|
|
||||||
hosted by the entity `registry.docker.io`.
|
|
||||||
|
|
||||||
Once the token server has determined what access the client has to the
|
|
||||||
resources requested in the `scope` parameter, it will take the intersection of
|
|
||||||
the set of requested actions on each resource and the set of actions that the
|
|
||||||
client has in fact been granted. If the client only has a subset of the
|
|
||||||
requested access **it must not be considered an error** as it is not the
|
|
||||||
responsibility of the token server to indicate authorization errors as part of
|
|
||||||
this workflow.
|
|
||||||
|
|
||||||
Continuing with the example request, the token server will find that the
|
|
||||||
client's set of granted access to the repository is `[pull, push]` which when
|
|
||||||
intersected with the requested access `[pull, push]` yields an equal set. If
|
|
||||||
the granted access set was found only to be `[pull]` then the intersected set
|
|
||||||
would only be `[pull]`. If the client has no access to the repository then the
|
|
||||||
intersected set would be empty, `[]`.
|
|
||||||
|
|
||||||
It is this intersected set of access which is placed in the returned token.
|
|
||||||
|
|
||||||
The server then constructs an implementation-specific token with this
|
|
||||||
intersected set of access, and returns it to the Docker client to use to
|
|
||||||
authenticate to the audience service (within the indicated window of time):
|
|
||||||
|
|
||||||
```
|
|
||||||
HTTP/1.1 200 OK
|
|
||||||
Content-Type: application/json
|
|
||||||
|
|
||||||
{"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IlBZWU86VEVXVTpWN0pIOjI2SlY6QVFUWjpMSkMzOlNYVko6WEdIQTozNEYyOjJMQVE6WlJNSzpaN1E2In0.eyJpc3MiOiJhdXRoLmRvY2tlci5jb20iLCJzdWIiOiJqbGhhd24iLCJhdWQiOiJyZWdpc3RyeS5kb2NrZXIuY29tIiwiZXhwIjoxNDE1Mzg3MzE1LCJuYmYiOjE0MTUzODcwMTUsImlhdCI6MTQxNTM4NzAxNSwianRpIjoidFlKQ08xYzZjbnl5N2tBbjBjN3JLUGdiVjFIMWJGd3MiLCJhY2Nlc3MiOlt7InR5cGUiOiJyZXBvc2l0b3J5IiwibmFtZSI6InNhbWFsYmEvbXktYXBwIiwiYWN0aW9ucyI6WyJwdXNoIl19XX0.QhflHPfbd6eVF4lM9bwYpFZIV0PfikbyXuLx959ykRTBpe3CYnzs6YBK8FToVb5R47920PVLrh8zuLzdCr9t3w", "expires_in": 3600,"issued_at": "2009-11-10T23:00:00Z"}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Using the Bearer token
|
|
||||||
|
|
||||||
Once the client has a token, it will try the registry request again with the
|
|
||||||
token placed in the HTTP `Authorization` header like so:
|
|
||||||
|
|
||||||
```
|
|
||||||
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IkJWM0Q6MkFWWjpVQjVaOktJQVA6SU5QTDo1RU42Ok40SjQ6Nk1XTzpEUktFOkJWUUs6M0ZKTDpQT1RMIn0.eyJpc3MiOiJhdXRoLmRvY2tlci5jb20iLCJzdWIiOiJCQ0NZOk9VNlo6UUVKNTpXTjJDOjJBVkM6WTdZRDpBM0xZOjQ1VVc6NE9HRDpLQUxMOkNOSjU6NUlVTCIsImF1ZCI6InJlZ2lzdHJ5LmRvY2tlci5jb20iLCJleHAiOjE0MTUzODczMTUsIm5iZiI6MTQxNTM4NzAxNSwiaWF0IjoxNDE1Mzg3MDE1LCJqdGkiOiJ0WUpDTzFjNmNueXk3a0FuMGM3cktQZ2JWMUgxYkZ3cyIsInNjb3BlIjoiamxoYXduOnJlcG9zaXRvcnk6c2FtYWxiYS9teS1hcHA6cHVzaCxwdWxsIGpsaGF3bjpuYW1lc3BhY2U6c2FtYWxiYTpwdWxsIn0.Y3zZSwaZPqy4y9oRBVRImZyv3m_S9XDHF1tWwN7mL52C_IiA73SJkWVNsvNqpJIn5h7A2F8biv_S2ppQ1lgkbw
|
|
||||||
```
|
|
||||||
|
|
||||||
This is also described in [Section 2.1 of RFC 6750: The OAuth 2.0 Authorization
|
|
||||||
Framework: Bearer Token Usage](https://tools.ietf.org/html/rfc6750#section-2.1)
|
|
|
@ -1,30 +0,0 @@
|
||||||
---
|
|
||||||
published: false
|
|
||||||
---
|
|
||||||
|
|
||||||
# Distribution API Implementations
|
|
||||||
|
|
||||||
This is a list of known implementations of the Distribution API spec.
|
|
||||||
|
|
||||||
## [Docker Distribution Registry](https://github.com/docker/distribution)
|
|
||||||
|
|
||||||
Docker distribution is the reference implementation of the distribution API
|
|
||||||
specification. It aims to fully implement the entire specification.
|
|
||||||
|
|
||||||
### Releases
|
|
||||||
#### 2.0.1 (_in development_)
|
|
||||||
Implements API 2.0.1
|
|
||||||
|
|
||||||
_Known Issues_
|
|
||||||
- No resumable push support
|
|
||||||
- Content ranges ignored
|
|
||||||
- Blob upload status will always return a starting range of 0
|
|
||||||
|
|
||||||
#### 2.0.0
|
|
||||||
Implements API 2.0.0
|
|
||||||
|
|
||||||
_Known Issues_
|
|
||||||
- No resumable push support
|
|
||||||
- No PATCH implementation for blob upload
|
|
||||||
- Content ranges ignored
|
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
---
|
|
||||||
description: Explains registry JSON objects
|
|
||||||
keywords: registry, service, images, repository, json
|
|
||||||
title: Docker Registry Reference
|
|
||||||
---
|
|
||||||
|
|
||||||
* [HTTP API V2](api.md)
|
|
||||||
* [Storage Driver](../storage-drivers/index.md)
|
|
||||||
* [Token Authentication Specification](auth/token.md)
|
|
||||||
* [Token Authentication Implementation](auth/jwt.md)
|
|
|
@ -1,86 +0,0 @@
|
||||||
---
|
|
||||||
description: Explains registry JSON objects
|
|
||||||
keywords: registry, service, images, repository, json
|
|
||||||
published: false
|
|
||||||
title: Docker Distribution JSON canonicalization
|
|
||||||
---
|
|
||||||
|
|
||||||
To provide consistent content hashing of JSON objects throughout Docker
|
|
||||||
Distribution APIs, the specification defines a canonical JSON format. Adopting
|
|
||||||
such a canonicalization also aids in caching JSON responses.
|
|
||||||
|
|
||||||
Note that protocols should not be designed to depend on identical JSON being
|
|
||||||
generated across different versions or clients. The canonicalization rules are
|
|
||||||
merely useful for caching and consistency.
|
|
||||||
|
|
||||||
## Rules
|
|
||||||
|
|
||||||
Compliant JSON should conform to the following rules:
|
|
||||||
|
|
||||||
1. All generated JSON should comply with [RFC
|
|
||||||
7159](http://www.ietf.org/rfc/rfc7159.txt).
|
|
||||||
2. Resulting "JSON text" shall always be encoded in UTF-8.
|
|
||||||
3. Unless a canonical key order is defined for a particular schema, object
|
|
||||||
keys shall always appear in lexically sorted order.
|
|
||||||
4. All whitespace between tokens should be removed.
|
|
||||||
5. No "trailing commas" are allowed in object or array definitions.
|
|
||||||
6. The angle brackets "<" and ">" are escaped to "\u003c" and "\u003e".
|
|
||||||
Ampersand "&" is escaped to "\u0026".
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
The following is a simple example of a canonicalized JSON string:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{"asdf":1,"qwer":[],"zxcv":[{},true,1000000000,"tyui"]}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Reference
|
|
||||||
|
|
||||||
### Other Canonicalizations
|
|
||||||
|
|
||||||
The OLPC project specifies [Canonical
|
|
||||||
JSON](http://wiki.laptop.org/go/Canonical_JSON). While this is used in
|
|
||||||
[TUF](http://theupdateframework.com/), which may be used with other
|
|
||||||
distribution-related protocols, this alternative format has been proposed in
|
|
||||||
case the original source changes. Specifications complying with either this
|
|
||||||
specification or an alternative should explicitly call out the
|
|
||||||
canonicalization format. Except for key ordering, this specification is mostly
|
|
||||||
compatible.
|
|
||||||
|
|
||||||
### Go
|
|
||||||
|
|
||||||
In Go, the [`encoding/json`](http://golang.org/pkg/encoding/json/) library
|
|
||||||
will emit canonical JSON by default. Simply using `json.Marshal` will suffice
|
|
||||||
in most cases:
|
|
||||||
|
|
||||||
```go
|
|
||||||
incoming := map[string]interface{}{
|
|
||||||
"asdf": 1,
|
|
||||||
"qwer": []interface{}{},
|
|
||||||
"zxcv": []interface{}{
|
|
||||||
map[string]interface{}{},
|
|
||||||
true,
|
|
||||||
int(1e9),
|
|
||||||
"tyui",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
canonical, err := json.Marshal(incoming)
|
|
||||||
if err != nil {
|
|
||||||
// ... handle error
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
To apply canonical JSON format spacing to an existing serialized JSON buffer, one
|
|
||||||
can use
|
|
||||||
[`json.Indent`](http://golang.org/src/encoding/json/indent.go?s=1918:1989#L65)
|
|
||||||
with the following arguments:
|
|
||||||
|
|
||||||
```go
|
|
||||||
incoming := getBytes()
|
|
||||||
var canonical bytes.Buffer
|
|
||||||
if err := json.Indent(&canonical, incoming, "", ""); err != nil {
|
|
||||||
// ... handle error
|
|
||||||
}
|
|
||||||
```
|
|
|
@ -1,161 +0,0 @@
|
||||||
---
|
|
||||||
description: image manifest for the Registry.
|
|
||||||
keywords: registry, on-prem, images, tags, repository, distribution, api, advanced, manifest
|
|
||||||
title: Image manifest V2, schema 1
|
|
||||||
---
|
|
||||||
|
|
||||||
This document outlines the format of of the V2 image manifest. The image
|
|
||||||
manifest described herein was introduced in the Docker daemon in the [v1.3.0
|
|
||||||
release](https://github.com/docker/docker/commit/9f482a66ab37ec396ac61ed0c00d59122ac07453).
|
|
||||||
It is a provisional manifest to provide a compatibility with the [V1 Image
|
|
||||||
format](https://github.com/docker/docker/blob/master/image/spec/v1.md), as the
|
|
||||||
requirements are defined for the [V2 Schema 2
|
|
||||||
image](https://github.com/docker/distribution/pull/62).
|
|
||||||
|
|
||||||
|
|
||||||
Image manifests describe the various constituents of a docker image. Image
|
|
||||||
manifests can be serialized to JSON format with the following media types:
|
|
||||||
|
|
||||||
Manifest Type | Media Type
|
|
||||||
------------- | -------------
|
|
||||||
manifest | "application/vnd.docker.distribution.manifest.v1+json"
|
|
||||||
signed manifest | "application/vnd.docker.distribution.manifest.v1+prettyjws"
|
|
||||||
|
|
||||||
*Note that "application/json" will also be accepted for schema 1.*
|
|
||||||
|
|
||||||
References:
|
|
||||||
|
|
||||||
- [Proposal: JSON Registry API V2.1](https://github.com/docker/docker/issues/9015)
|
|
||||||
- [Proposal: Provenance step 1 - Transform images for validation and verification](https://github.com/docker/docker/issues/8093)
|
|
||||||
|
|
||||||
## *Manifest* Field Descriptions
|
|
||||||
|
|
||||||
Manifest provides the base accessible fields for working with V2 image format
|
|
||||||
in the registry.
|
|
||||||
|
|
||||||
- **`name`** *string*
|
|
||||||
|
|
||||||
name is the name of the image's repository
|
|
||||||
|
|
||||||
- **`tag`** *string*
|
|
||||||
|
|
||||||
tag is the tag of the image
|
|
||||||
|
|
||||||
- **`architecture`** *string*
|
|
||||||
|
|
||||||
architecture is the host architecture on which this image is intended to
|
|
||||||
run. This is for information purposes and not currently used by the engine
|
|
||||||
|
|
||||||
- **`fsLayers`** *array*
|
|
||||||
|
|
||||||
fsLayers is a list of filesystem layer blob sums contained in this image.
|
|
||||||
|
|
||||||
An fsLayer is a struct consisting of the following fields
|
|
||||||
- **`blobSum`** *digest.Digest*
|
|
||||||
|
|
||||||
blobSum is the digest of the referenced filesystem image layer. A
|
|
||||||
digest must be a sha256 hash.
|
|
||||||
|
|
||||||
|
|
||||||
- **`history`** *array*
|
|
||||||
|
|
||||||
history is a list of unstructured historical data for v1 compatibility. It
|
|
||||||
contains ID of the image layer and ID of the layer's parent layers.
|
|
||||||
|
|
||||||
history is a struct consisting of the following fields
|
|
||||||
- **`v1Compatibility`** string
|
|
||||||
|
|
||||||
V1Compatibility is the raw V1 compatibility information. This will
|
|
||||||
contain the JSON object describing the V1 of this image.
|
|
||||||
|
|
||||||
- **`schemaVersion`** *int*
|
|
||||||
|
|
||||||
SchemaVersion is the image manifest schema that this image follows.
|
|
||||||
|
|
||||||
>**Note**:the length of `history` must be equal to the length of `fsLayers` and
|
|
||||||
>entries in each are correlated by index.
|
|
||||||
|
|
||||||
## Signed Manifests
|
|
||||||
|
|
||||||
Signed manifests provides an envelope for a signed image manifest. A signed
|
|
||||||
manifest consists of an image manifest along with an additional field
|
|
||||||
containing the signature of the manifest.
|
|
||||||
|
|
||||||
The docker client can verify signed manifests and displays a message to the user.
|
|
||||||
|
|
||||||
### Signing Manifests
|
|
||||||
|
|
||||||
Image manifests can be signed in two different ways: with a *libtrust* private
|
|
||||||
key or an x509 certificate chain. When signing with an x509 certificate chain,
|
|
||||||
the public key of the first element in the chain must be the public key
|
|
||||||
corresponding with the sign key.
|
|
||||||
|
|
||||||
### Signed Manifest Field Description
|
|
||||||
|
|
||||||
Signed manifests include an image manifest and a list of signatures generated
|
|
||||||
by *libtrust*. A signature consists of the following fields:
|
|
||||||
|
|
||||||
|
|
||||||
- **`header`** *[JOSE](http://tools.ietf.org/html/draft-ietf-jose-json-web-signature-31#section-2)*
|
|
||||||
|
|
||||||
A [JSON Web Signature](http://self-issued.info/docs/draft-ietf-jose-json-web-signature.html)
|
|
||||||
|
|
||||||
- **`signature`** *string*
|
|
||||||
|
|
||||||
A signature for the image manifest, signed by a *libtrust* private key
|
|
||||||
|
|
||||||
- **`protected`** *string*
|
|
||||||
|
|
||||||
The signed protected header
|
|
||||||
|
|
||||||
## Example Manifest
|
|
||||||
|
|
||||||
*Example showing the official 'hello-world' image manifest.*
|
|
||||||
|
|
||||||
```
|
|
||||||
{
|
|
||||||
"name": "hello-world",
|
|
||||||
"tag": "latest",
|
|
||||||
"architecture": "amd64",
|
|
||||||
"fsLayers": [
|
|
||||||
{
|
|
||||||
"blobSum": "sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"blobSum": "sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"blobSum": "sha256:cc8567d70002e957612902a8e985ea129d831ebe04057d88fb644857caa45d11"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"blobSum": "sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"history": [
|
|
||||||
{
|
|
||||||
"v1Compatibility": "{\"id\":\"e45a5af57b00862e5ef5782a9925979a02ba2b12dff832fd0991335f4a11e5c5\",\"parent\":\"31cbccb51277105ba3ae35ce33c22b69c9e3f1002e76e4c736a2e8ebff9d7b5d\",\"created\":\"2014-12-31T22:57:59.178729048Z\",\"container\":\"27b45f8fb11795b52e9605b686159729b0d9ca92f76d40fb4f05a62e19c46b4f\",\"container_config\":{\"Hostname\":\"8ce6509d66e2\",\"Domainname\":\"\",\"User\":\"\",\"Memory\":0,\"MemorySwap\":0,\"CpuShares\":0,\"Cpuset\":\"\",\"AttachStdin\":false,\"AttachStdout\":false,\"AttachStderr\":false,\"PortSpecs\":null,\"ExposedPorts\":null,\"Tty\":false,\"OpenStdin\":false,\"StdinOnce\":false,\"Env\":[\"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\"],\"Cmd\":[\"/bin/sh\",\"-c\",\"#(nop) CMD [/hello]\"],\"Image\":\"31cbccb51277105ba3ae35ce33c22b69c9e3f1002e76e4c736a2e8ebff9d7b5d\",\"Volumes\":null,\"WorkingDir\":\"\",\"Entrypoint\":null,\"NetworkDisabled\":false,\"MacAddress\":\"\",\"OnBuild\":[],\"SecurityOpt\":null,\"Labels\":null},\"docker_version\":\"1.4.1\",\"config\":{\"Hostname\":\"8ce6509d66e2\",\"Domainname\":\"\",\"User\":\"\",\"Memory\":0,\"MemorySwap\":0,\"CpuShares\":0,\"Cpuset\":\"\",\"AttachStdin\":false,\"AttachStdout\":false,\"AttachStderr\":false,\"PortSpecs\":null,\"ExposedPorts\":null,\"Tty\":false,\"OpenStdin\":false,\"StdinOnce\":false,\"Env\":[\"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\"],\"Cmd\":[\"/hello\"],\"Image\":\"31cbccb51277105ba3ae35ce33c22b69c9e3f1002e76e4c736a2e8ebff9d7b5d\",\"Volumes\":null,\"WorkingDir\":\"\",\"Entrypoint\":null,\"NetworkDisabled\":false,\"MacAddress\":\"\",\"OnBuild\":[],\"SecurityOpt\":null,\"Labels\":null},\"architecture\":\"amd64\",\"os\":\"linux\",\"Size\":0}\n"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"v1Compatibility": "{\"id\":\"e45a5af57b00862e5ef5782a9925979a02ba2b12dff832fd0991335f4a11e5c5\",\"parent\":\"31cbccb51277105ba3ae35ce33c22b69c9e3f1002e76e4c736a2e8ebff9d7b5d\",\"created\":\"2014-12-31T22:57:59.178729048Z\",\"container\":\"27b45f8fb11795b52e9605b686159729b0d9ca92f76d40fb4f05a62e19c46b4f\",\"container_config\":{\"Hostname\":\"8ce6509d66e2\",\"Domainname\":\"\",\"User\":\"\",\"Memory\":0,\"MemorySwap\":0,\"CpuShares\":0,\"Cpuset\":\"\",\"AttachStdin\":false,\"AttachStdout\":false,\"AttachStderr\":false,\"PortSpecs\":null,\"ExposedPorts\":null,\"Tty\":false,\"OpenStdin\":false,\"StdinOnce\":false,\"Env\":[\"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\"],\"Cmd\":[\"/bin/sh\",\"-c\",\"#(nop) CMD [/hello]\"],\"Image\":\"31cbccb51277105ba3ae35ce33c22b69c9e3f1002e76e4c736a2e8ebff9d7b5d\",\"Volumes\":null,\"WorkingDir\":\"\",\"Entrypoint\":null,\"NetworkDisabled\":false,\"MacAddress\":\"\",\"OnBuild\":[],\"SecurityOpt\":null,\"Labels\":null},\"docker_version\":\"1.4.1\",\"config\":{\"Hostname\":\"8ce6509d66e2\",\"Domainname\":\"\",\"User\":\"\",\"Memory\":0,\"MemorySwap\":0,\"CpuShares\":0,\"Cpuset\":\"\",\"AttachStdin\":false,\"AttachStdout\":false,\"AttachStderr\":false,\"PortSpecs\":null,\"ExposedPorts\":null,\"Tty\":false,\"OpenStdin\":false,\"StdinOnce\":false,\"Env\":[\"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\"],\"Cmd\":[\"/hello\"],\"Image\":\"31cbccb51277105ba3ae35ce33c22b69c9e3f1002e76e4c736a2e8ebff9d7b5d\",\"Volumes\":null,\"WorkingDir\":\"\",\"Entrypoint\":null,\"NetworkDisabled\":false,\"MacAddress\":\"\",\"OnBuild\":[],\"SecurityOpt\":null,\"Labels\":null},\"architecture\":\"amd64\",\"os\":\"linux\",\"Size\":0}\n"
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"schemaVersion": 1,
|
|
||||||
"signatures": [
|
|
||||||
{
|
|
||||||
"header": {
|
|
||||||
"jwk": {
|
|
||||||
"crv": "P-256",
|
|
||||||
"kid": "OD6I:6DRK:JXEJ:KBM4:255X:NSAA:MUSF:E4VM:ZI6W:CUN2:L4Z6:LSF4",
|
|
||||||
"kty": "EC",
|
|
||||||
"x": "3gAwX48IQ5oaYQAYSxor6rYYc_6yjuLCjtQ9LUakg4A",
|
|
||||||
"y": "t72ge6kIA1XOjqjVoEOiPPAURltJFBMGDSQvEGVB010"
|
|
||||||
},
|
|
||||||
"alg": "ES256"
|
|
||||||
},
|
|
||||||
"signature": "XREm0L8WNn27Ga_iE_vRnTxVMhhYY0Zst_FfkKopg6gWSoTOZTuW4rK0fg_IqnKkEKlbD83tD46LKEGi5aIVFg",
|
|
||||||
"protected": "eyJmb3JtYXRMZW5ndGgiOjY2MjgsImZvcm1hdFRhaWwiOiJDbjAiLCJ0aW1lIjoiMjAxNS0wNC0wOFQxODo1Mjo1OVoifQ"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
|
@ -1,292 +0,0 @@
|
||||||
---
|
|
||||||
description: image manifest for the Registry.
|
|
||||||
keywords: registry, on-prem, images, tags, repository, distribution, api, advanced, manifest
|
|
||||||
title: Image manifest V2, schema 2
|
|
||||||
---
|
|
||||||
|
|
||||||
This document outlines the format of of the V2 image manifest, schema version 2.
|
|
||||||
The original (and provisional) image manifest for V2 (schema 1), was introduced
|
|
||||||
in the Docker daemon in the [v1.3.0
|
|
||||||
release](https://github.com/docker/docker/commit/9f482a66ab37ec396ac61ed0c00d59122ac07453)
|
|
||||||
and is specified in the [schema 1 manifest definition](manifest-v2-1.md)
|
|
||||||
|
|
||||||
This second schema version has two primary goals. The first is to allow
|
|
||||||
multi-architecture images, through a "fat manifest" which references image
|
|
||||||
manifests for platform-specific versions of an image. The second is to
|
|
||||||
move the Docker engine towards content-addressable images, by supporting
|
|
||||||
an image model where the image's configuration can be hashed to generate
|
|
||||||
an ID for the image.
|
|
||||||
|
|
||||||
# Media Types
|
|
||||||
|
|
||||||
The following media types are used by the manifest formats described here, and
|
|
||||||
the resources they reference:
|
|
||||||
|
|
||||||
- `application/vnd.docker.distribution.manifest.v1+json`: schema1 (existing manifest format)
|
|
||||||
- `application/vnd.docker.distribution.manifest.v2+json`: New image manifest format (schemaVersion = 2)
|
|
||||||
- `application/vnd.docker.distribution.manifest.list.v2+json`: Manifest list, aka "fat manifest"
|
|
||||||
- `application/vnd.docker.image.rootfs.diff.tar.gzip`: "Layer", as a gzipped tar
|
|
||||||
- `application/vnd.docker.container.image.v1+json`: Container config JSON
|
|
||||||
|
|
||||||
## Manifest List
|
|
||||||
|
|
||||||
The manifest list is the "fat manifest" which points to specific image manifests
|
|
||||||
for one or more platforms. Its use is optional, and relatively few images will
|
|
||||||
use one of these manifests. A client will distinguish a manifest list from an
|
|
||||||
image manifest based on the Content-Type returned in the HTTP response.
|
|
||||||
|
|
||||||
## *Manifest List* Field Descriptions
|
|
||||||
|
|
||||||
- **`schemaVersion`** *int*
|
|
||||||
|
|
||||||
This field specifies the image manifest schema version as an integer. This
|
|
||||||
schema uses the version `2`.
|
|
||||||
|
|
||||||
- **`mediaType`** *string*
|
|
||||||
|
|
||||||
The MIME type of the manifest list. This should be set to
|
|
||||||
`application/vnd.docker.distribution.manifest.list.v2+json`.
|
|
||||||
|
|
||||||
- **`manifests`** *array*
|
|
||||||
|
|
||||||
The manifests field contains a list of manifests for specific platforms.
|
|
||||||
|
|
||||||
Fields of an object in the manifests list are:
|
|
||||||
|
|
||||||
- **`mediaType`** *string*
|
|
||||||
|
|
||||||
The MIME type of the referenced object. This will generally be
|
|
||||||
`application/vnd.docker.image.manifest.v2+json`, but it could also
|
|
||||||
be `application/vnd.docker.image.manifest.v1+json` if the manifest
|
|
||||||
list references a legacy schema-1 manifest.
|
|
||||||
|
|
||||||
- **`size`** *int*
|
|
||||||
|
|
||||||
The size in bytes of the object. This field exists so that a client
|
|
||||||
will have an expected size for the content before validating. If the
|
|
||||||
length of the retrieved content does not match the specified length,
|
|
||||||
the content should not be trusted.
|
|
||||||
|
|
||||||
- **`digest`** *string*
|
|
||||||
|
|
||||||
The digest of the content, as defined by the
|
|
||||||
[Registry V2 HTTP API Specificiation](api.md#digest-parameter).
|
|
||||||
|
|
||||||
- **`platform`** *object*
|
|
||||||
|
|
||||||
The platform object describes the platform which the image in the
|
|
||||||
manifest runs on. A full list of valid operating system and architecture
|
|
||||||
values are listed in the [Go language documentation for `$GOOS` and
|
|
||||||
`$GOARCH`](https://golang.org/doc/install/source#environment)
|
|
||||||
|
|
||||||
- **`architecture`** *string*
|
|
||||||
|
|
||||||
The architecture field specifies the CPU architecture, for example
|
|
||||||
`amd64` or `ppc64le`.
|
|
||||||
|
|
||||||
- **`os`** *string*
|
|
||||||
|
|
||||||
The os field specifies the operating system, for example
|
|
||||||
`linux` or `windows`.
|
|
||||||
|
|
||||||
- **`os.version`** *string*
|
|
||||||
|
|
||||||
The optional os.version field specifies the operating system version,
|
|
||||||
for example `10.0.10586`.
|
|
||||||
|
|
||||||
- **`os.features`** *array*
|
|
||||||
|
|
||||||
The optional os.features field specifies an array of strings,
|
|
||||||
each listing a required OS feature (for example on Windows
|
|
||||||
`win32k`).
|
|
||||||
|
|
||||||
- **`variant`** *string*
|
|
||||||
|
|
||||||
The optional variant field specifies a variant of the CPU, for
|
|
||||||
example `armv6l` to specify a particular CPU variant of the ARM CPU.
|
|
||||||
|
|
||||||
- **`features`** *array*
|
|
||||||
|
|
||||||
The optional features field specifies an array of strings, each
|
|
||||||
listing a required CPU feature (for example `sse4` or `aes`).
|
|
||||||
|
|
||||||
## Example Manifest List
|
|
||||||
|
|
||||||
*Example showing a simple manifest list pointing to image manifests for two platforms:*
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"schemaVersion": 2,
|
|
||||||
"mediaType": "application/vnd.docker.distribution.manifest.list.v2+json",
|
|
||||||
"manifests": [
|
|
||||||
{
|
|
||||||
"mediaType": "application/vnd.docker.image.manifest.v2+json",
|
|
||||||
"size": 7143,
|
|
||||||
"digest": "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f",
|
|
||||||
"platform": {
|
|
||||||
"architecture": "ppc64le",
|
|
||||||
"os": "linux",
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mediaType": "application/vnd.docker.image.manifest.v2+json",
|
|
||||||
"size": 7682,
|
|
||||||
"digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270",
|
|
||||||
"platform": {
|
|
||||||
"architecture": "amd64",
|
|
||||||
"os": "linux",
|
|
||||||
"features": [
|
|
||||||
"sse4"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
# Image Manifest
|
|
||||||
|
|
||||||
The image manifest provides a configuration and a set of layers for a container
|
|
||||||
image. It's the direct replacement for the schema-1 manifest.
|
|
||||||
|
|
||||||
## *Image Manifest* Field Descriptions
|
|
||||||
|
|
||||||
- **`schemaVersion`** *int*
|
|
||||||
|
|
||||||
This field specifies the image manifest schema version as an integer. This
|
|
||||||
schema uses version `2`.
|
|
||||||
|
|
||||||
- **`mediaType`** *string*
|
|
||||||
|
|
||||||
The MIME type of the manifest. This should be set to
|
|
||||||
`application/vnd.docker.distribution.manifest.v2+json`.
|
|
||||||
|
|
||||||
- **`config`** *object*
|
|
||||||
|
|
||||||
The config field references a configuration object for a container, by
|
|
||||||
digest. This configuration item is a JSON blob that the runtime uses
|
|
||||||
to set up the container. This new schema uses a tweaked version
|
|
||||||
of this configuration to allow image content-addressability on the
|
|
||||||
daemon side.
|
|
||||||
|
|
||||||
Fields of a config object are:
|
|
||||||
|
|
||||||
- **`mediaType`** *string*
|
|
||||||
|
|
||||||
The MIME type of the referenced object. This should generally be
|
|
||||||
`application/vnd.docker.container.image.v1+json`.
|
|
||||||
|
|
||||||
- **`size`** *int*
|
|
||||||
|
|
||||||
The size in bytes of the object. This field exists so that a client
|
|
||||||
will have an expected size for the content before validating. If the
|
|
||||||
length of the retrieved content does not match the specified length,
|
|
||||||
the content should not be trusted.
|
|
||||||
|
|
||||||
- **`digest`** *string*
|
|
||||||
|
|
||||||
The digest of the content, as defined by the
|
|
||||||
[Registry V2 HTTP API Specificiation](api.md#digest-parameter).
|
|
||||||
|
|
||||||
- **`layers`** *array*
|
|
||||||
|
|
||||||
The layer list is ordered starting from the base image (opposite order of schema1).
|
|
||||||
|
|
||||||
Fields of an item in the layers list are:
|
|
||||||
|
|
||||||
- **`mediaType`** *string*
|
|
||||||
|
|
||||||
The MIME type of the referenced object. This should
|
|
||||||
generally be `application/vnd.docker.image.rootfs.diff.tar.gzip`.
|
|
||||||
|
|
||||||
- **`size`** *int*
|
|
||||||
|
|
||||||
The size in bytes of the object. This field exists so that a client
|
|
||||||
will have an expected size for the content before validating. If the
|
|
||||||
length of the retrieved content does not match the specified length,
|
|
||||||
the content should not be trusted.
|
|
||||||
|
|
||||||
- **`digest`** *string*
|
|
||||||
|
|
||||||
The digest of the content, as defined by the
|
|
||||||
[Registry V2 HTTP API Specificiation](api.md#digest-parameter).
|
|
||||||
|
|
||||||
- **`urls`** *array*
|
|
||||||
|
|
||||||
For an ordinary layer, this is empty, and the layer contents can be
|
|
||||||
retrieved directly from the registry. For a layer with *`mediatype`* of
|
|
||||||
`application/vnd.docker.image.rootfs.foreign.diff.tar.gzip`, this
|
|
||||||
contains a non-empty list of URLs from which this object can be
|
|
||||||
downloaded.
|
|
||||||
|
|
||||||
## Example Image Manifest
|
|
||||||
|
|
||||||
*Example showing an image manifest:*
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"schemaVersion": 2,
|
|
||||||
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
|
|
||||||
"config": {
|
|
||||||
"mediaType": "application/vnd.docker.container.image.v1+json",
|
|
||||||
"size": 7023,
|
|
||||||
"digest": "sha256:b5b2b2c507a0944348e0303114d8d93aaaa081732b86451d9bce1f432a537bc7"
|
|
||||||
},
|
|
||||||
"layers": [
|
|
||||||
{
|
|
||||||
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
|
|
||||||
"size": 32654,
|
|
||||||
"digest": "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
|
|
||||||
"size": 16724,
|
|
||||||
"digest": "sha256:3c3a4604a545cdc127456d94e421cd355bca5b528f4a9c1905b15da2eb4a4c6b"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
|
|
||||||
"size": 73109,
|
|
||||||
"digest": "sha256:ec4b8955958665577945c89419d1af06b5f7636b4ac3da7f12184802ad867736"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
# Backward compatibility
|
|
||||||
|
|
||||||
The registry will continue to accept uploads of manifests in both the old and
|
|
||||||
new formats.
|
|
||||||
|
|
||||||
When pushing images, clients which support the new manifest format should first
|
|
||||||
construct a manifest in the new format. If uploading this manifest fails,
|
|
||||||
presumably because the registry only supports the old format, the client may
|
|
||||||
fall back to uploading a manifest in the old format.
|
|
||||||
|
|
||||||
When pulling images, clients indicate support for this new version of the
|
|
||||||
manifest format by sending the
|
|
||||||
`application/vnd.docker.distribution.manifest.v2+json` and
|
|
||||||
`application/vnd.docker.distribution.manifest.list.v2+json` media types in an
|
|
||||||
`Accept` header when making a request to the `manifests` endpoint. Updated
|
|
||||||
clients should check the `Content-Type` header to see whether the manifest
|
|
||||||
returned from the endpoint is in the old format, or is an image manifest or
|
|
||||||
manifest list in the new format.
|
|
||||||
|
|
||||||
If the manifest being requested uses the new format, and the appropriate media
|
|
||||||
type is not present in an `Accept` header, the registry will assume that the
|
|
||||||
client cannot handle the manifest as-is, and rewrite it on the fly into the old
|
|
||||||
format. If the object that would otherwise be returned is a manifest list, the
|
|
||||||
registry will look up the appropriate manifest for the amd64 platform and
|
|
||||||
linux OS, rewrite that manifest into the old format if necessary, and return
|
|
||||||
the result to the client. If no suitable manifest is found in the manifest
|
|
||||||
list, the registry will return a 404 error.
|
|
||||||
|
|
||||||
One of the challenges in rewriting manifests to the old format is that the old
|
|
||||||
format involves an image configuration for each layer in the manifest, but the
|
|
||||||
new format only provides one image configuration. To work around this, the
|
|
||||||
registry will create synthetic image configurations for all layers except the
|
|
||||||
top layer. These image configurations will not result in runnable images on
|
|
||||||
their own, but only serve to fill in the parent chain in a compatible way.
|
|
||||||
The IDs in these synthetic configurations will be derived from hashes of their
|
|
||||||
respective blobs. The registry will create these configurations and their IDs
|
|
||||||
using the same scheme as Docker 1.10 when it creates a legacy manifest to push
|
|
||||||
to a registry which doesn't support the new format.
|
|
Loading…
Reference in a new issue