github.com/lusis/distribution@v2.0.1+incompatible/docs/spec/auth/token.md (about)

     1  <!--GITHUB
     2  page_title: Docker Registry v2 Authentication
     3  page_description: Introduces the Docker Registry v2 authentication
     4  page_keywords: registry, images, repository, v2, authentication
     5  IGNORES-->
     6  
     7  
     8  # Docker Registry v2 authentication via central service
     9  
    10  Today a Docker Registry can run in standalone mode in which there are no
    11  authorization checks. While adding your own HTTP authorization requirements in
    12  a proxy placed between the client and the registry can give you greater access
    13  control, we'd like a native authorization mechanism that's public key based
    14  with access control lists managed separately with the ability to have fine
    15  granularity in access control on a by-key, by-user, by-namespace, and
    16  by-repository basis. In v1 this can be configured by specifying an
    17  `index_endpoint` in the registry's config. Clients present tokens generated by
    18  the index and tokens are validated on-line by the registry with every request.
    19  This results in a complex authentication and authorization loop that occurs
    20  with every registry operation. Some people are very familiar with this image:
    21  
    22  ![index auth](https://docs.docker.com/static_files/docker_pull_chart.png)
    23  
    24  The above image outlines the 6-step process in accessing the Official Docker
    25  Registry.
    26  
    27  1. Contact the Docker Hub to know where I should download “samalba/busybox”
    28  2. Docker Hub replies:
    29      a. samalba/busybox is on Registry A
    30      b. here are the checksums for samalba/busybox (for all layers)
    31      c. token
    32  3. Contact Registry A to receive the layers for samalba/busybox (all of them to
    33     the base image). Registry A is authoritative for “samalba/busybox” but keeps
    34     a copy of all inherited layers and serve them all from the same location.
    35  4. Registry contacts Docker Hub to verify if token/user is allowed to download
    36     images.
    37  5. Docker Hub returns true/false lettings registry know if it should proceed or
    38     error out.
    39  6. Get the payload for all layers.
    40  
    41  The goal of this document is to outline a way to eliminate steps 4 and 5 from
    42  the above process by using cryptographically signed tokens and no longer
    43  require the client to authenticate each request with a username and password
    44  stored locally in plain text.
    45  
    46  The new registry workflow is more like this:
    47  
    48  ![v2 registry auth](https://docs.google.com/drawings/d/1EHZU9uBLmcH0kytDClBv6jv6WR4xZjE8RKEUw1mARJA/pub?w=480&h=360)
    49  
    50  1. Attempt to begin a push/pull operation with the registry.
    51  2. If the registry requires authorization it will return a `401 Unauthorized`
    52     HTTP response with information on how to authenticate.
    53  3. The registry client makes a request to the authorization service for a
    54     signed JSON Web Token.
    55  4. The authorization service returns a token.
    56  5. The client retries the original request with the token embedded in the
    57     request header.
    58  6. The Registry authorizes the client and begins the push/pull session as
    59     usual. 
    60  
    61  ## Requirements
    62  
    63  - Registry Clients capable of generating key pairs which can be used to
    64    authenticate to an authorization server.
    65  - An authorization server capable of managing user accounts, their public keys,
    66    and access controls to their resources hosted by any given service (such as
    67    repositories in a Docker Registry).
    68  - A Docker Registry capable of trusting the authorization server to sign tokens
    69    which clients can use for authorization and the ability to verify these
    70    tokens for single use or for use during a sufficiently short period of time.
    71  
    72  ## Authorization Server Endpoint Descriptions
    73  
    74  This document borrows heavily from the [JSON Web Token Draft Spec](https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-32)
    75  
    76  The described server is meant to serve as a user account and key manager and a
    77  centralized access control list for resources hosted by other services which
    78  wish to authenticate and manage authorizations using this services accounts and
    79  their public keys.
    80  
    81  Such a service could be used by the official docker registry to authenticate
    82  clients and verify their authorization to docker image repositories.
    83  
    84  Docker will need to be updated to interact with an authorization server to get
    85  an authorization token.
    86  
    87  ## How to authenticate
    88  
    89  Today, registry clients first contact the index to initiate a push or pull.
    90  For v2, clients should contact the registry first. If the registry server
    91  requires authentication it will return a `401 Unauthorized` response with a
    92  `WWW-Authenticate` header detailing how to authenticate to this registry.
    93  
    94  For example, say I (username `jlhawn`) am attempting to push an image to the
    95  repository `samalba/my-app`. For the registry to authorize this, I either need
    96  `push` access to the `samalba/my-app` repository or `push` access to the whole
    97  `samalba` namespace in general. The registry will first return this response:
    98  
    99  ```
   100  HTTP/1.1 401 Unauthorized
   101  WWW-Authenticate: Bearer realm="https://auth.docker.com/v2/token/",service="registry.docker.com",scope="repository:samalba/my-app:push"
   102  ```
   103  
   104  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)
   105  
   106  The client will then know to make a `GET` request to the URL
   107  `https://auth.docker.com/v2/token/` using the `service` and `scope` values from
   108  the `WWW-Authenticate` header.
   109  
   110  ## Requesting a Token
   111  
   112  #### Query Parameters
   113  
   114  <dl>
   115      <dt>
   116          <code>service</code>
   117      </dt>
   118      <dd>
   119          The name of the service which hosts the resource.
   120      </dd>
   121      <dt>
   122          <code>scope</code>
   123      </dt>
   124      <dd>
   125          The resource in question, formatted as one of the space-delimited
   126          entries from the <code>scope</code> parameters from the <code>WWW-Authenticate</code> header
   127          shown above. This query parameter should be specified multiple times if
   128          there is more than one <code>scope</code> entry from the <code>WWW-Authenticate</code>
   129          header. The above example would be specified as:
   130          <code>scope=repository:samalba/my-app:push</code>.
   131      </dd>
   132      <dt>
   133          <code>account</code>
   134      </dt>
   135      <dd>
   136          The name of the account which the client is acting as. Optional if it
   137          can be inferred from client authentication.
   138      </dd>
   139  </dl>
   140  
   141  #### Description
   142  
   143  Requests an authorization token for access to a specific resource hosted by a
   144  specific service provider. Requires the client to authenticate either using a
   145  TLS client certificate or using basic authentication (or any other kind of
   146  digest/challenge/response authentication scheme if the client doesn't support
   147  TLS client certs). If the key in the client certificate is linked to an account
   148  then the token is issued for that account key. If the key in the certificate is
   149  linked to multiple accounts then the client must specify the `account` query
   150  parameter. The returned token is in JWT (JSON Web Token) format, signed using
   151  the authorization server's private key.
   152  
   153  #### Example
   154  
   155  For this example, the client makes an HTTP request to the following endpoint
   156  over TLS using a client certificate with the server being configured to allow a
   157  non-verified issuer during the handshake (i.e., a self-signed client cert is
   158  okay).
   159  
   160  ```
   161  GET /v2/token/?service=registry.docker.com&scope=repository:samalba/my-app:push&account=jlhawn HTTP/1.1
   162  Host: auth.docker.com
   163  ```
   164  
   165  The server first inspects the client certificate to extract the subject key and
   166  lookup which account it is associated with. The client is now authenticated
   167  using that account.
   168  
   169  The server next searches its access control list for the account's access to
   170  the repository `samalba/my-app` hosted by the service `registry.docker.com`.
   171  
   172  The server will now construct a JSON Web Token to sign and return. A JSON Web
   173  Token has 3 main parts:
   174  
   175  1.  Headers
   176  
   177      The header of a JSON Web Token is a standard JOSE header. The "typ" field
   178      will be "JWT" and it will also contain the "alg" which identifies the
   179      signing algorithm used to produce the signature. It will also usually have
   180      a "kid" field, the ID of the key which was used to sign the token.
   181  
   182      Here is an example JOSE Header for a JSON Web Token (formatted with
   183      whitespace for readability):
   184  
   185      ```
   186      {
   187          "typ": "JWT",
   188          "alg": "ES256",
   189          "kid": "PYYO:TEWU:V7JH:26JV:AQTZ:LJC3:SXVJ:XGHA:34F2:2LAQ:ZRMK:Z7Q6"
   190      }
   191      ```
   192  
   193      It specifies that this object is going to be a JSON Web token signed using
   194      the key with the given ID using the Elliptic Curve signature algorithm
   195      using a SHA256 hash.
   196  
   197  2.  Claim Set
   198  
   199      The Claim Set is a JSON struct containing these standard registered claim
   200      name fields:
   201  
   202      <dl>
   203          <dt>
   204              <code>iss</code> (Issuer)
   205          </dt>
   206          <dd>
   207              The issuer of the token, typically the fqdn of the authorization
   208              server.
   209          </dd>
   210          <dt>
   211              <code>sub</code> (Subject)
   212          </dt>
   213          <dd>
   214              The subject of the token; the id of the client which requested it.
   215          </dd>
   216          <dt>
   217              <code>aud</code> (Audience)
   218          </dt>
   219          <dd>
   220              The intended audience of the token; the id of the service which
   221              will verify the token to authorize the client/subject.
   222          </dd>
   223          <dt>
   224              <code>exp</code> (Expiration)
   225          </dt>
   226          <dd>
   227              The token should only be considered valid up to this specified date
   228              and time.
   229          </dd>
   230          <dt>
   231              <code>nbf</code> (Not Before)
   232          </dt>
   233          <dd>
   234              The token should not be considered valid before this specified date
   235              and time.
   236          </dd>
   237          <dt>
   238              <code>iat</code> (Issued At)
   239          </dt>
   240          <dd>
   241              Specifies the date and time which the Authorization server
   242              generated this token.
   243          </dd>
   244          <dt>
   245              <code>jti</code> (JWT ID)
   246          </dt>
   247          <dd>
   248              A unique identifier for this token. Can be used by the intended
   249              audience to prevent replays of the token.
   250          </dd>
   251      </dl>
   252  
   253      The Claim Set will also contain a private claim name unique to this
   254      authorization server specification:
   255  
   256      <dl>
   257          <dt>
   258              <code>access</code>
   259          </dt>
   260          <dd>
   261              An array of access entry objects with the following fields:
   262  
   263              <dl>
   264                  <dt>
   265                      <code>type</code>
   266                  </dt>
   267                  <dd>
   268                      The type of resource hosted by the service.
   269                  </dd>
   270                  <dt>
   271                      <code>name</code>
   272                  </dt>
   273                  <dd>
   274                      The name of the recource of the given type hosted by the
   275                      service.
   276                  </dd>
   277                  <dt>
   278                      <code>actions</code>
   279                  </dt>
   280                  <dd>
   281                      An array of strings which give the actions authorized on
   282                      this resource.
   283                  </dd>
   284              </dl>
   285          </dd>
   286      </dl>
   287  
   288      Here is an example of such a JWT Claim Set (formatted with whitespace for
   289      readability):
   290  
   291      ```
   292      {
   293          "iss": "auth.docker.com",
   294          "sub": "jlhawn",
   295          "aud": "registry.docker.com",
   296          "exp": 1415387315,
   297          "nbf": 1415387015,
   298          "iat": 1415387015,
   299          "jti": "tYJCO1c6cnyy7kAn0c7rKPgbV1H1bFws",
   300          "access": [
   301              {
   302                  "type": "repository",
   303                  "name": "samalba/my-app",
   304                  "actions": [
   305                      "push"
   306                  ]
   307              }
   308          ]
   309      }
   310      ```
   311  
   312  3.  Signature
   313  
   314      The authorization server will produce a JOSE header and Claim Set with no
   315      extraneous whitespace, i.e., the JOSE Header from above would be
   316  
   317      ```
   318      {"typ":"JWT","alg":"ES256","kid":"PYYO:TEWU:V7JH:26JV:AQTZ:LJC3:SXVJ:XGHA:34F2:2LAQ:ZRMK:Z7Q6"}
   319      ```
   320  
   321      and the Claim Set from above would be
   322  
   323      ```
   324      {"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"]}]}
   325      ```
   326  
   327      The utf-8 representation of this JOSE header and Claim Set are then
   328      url-safe base64 encoded (sans trailing '=' buffer), producing:
   329  
   330      ```
   331      eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IlBZWU86VEVXVTpWN0pIOjI2SlY6QVFUWjpMSkMzOlNYVko6WEdIQTozNEYyOjJMQVE6WlJNSzpaN1E2In0
   332      ```
   333  
   334      for the JOSE Header and
   335  
   336      ```
   337      eyJpc3MiOiJhdXRoLmRvY2tlci5jb20iLCJzdWIiOiJqbGhhd24iLCJhdWQiOiJyZWdpc3RyeS5kb2NrZXIuY29tIiwiZXhwIjoxNDE1Mzg3MzE1LCJuYmYiOjE0MTUzODcwMTUsImlhdCI6MTQxNTM4NzAxNSwianRpIjoidFlKQ08xYzZjbnl5N2tBbjBjN3JLUGdiVjFIMWJGd3MiLCJhY2Nlc3MiOlt7InR5cGUiOiJyZXBvc2l0b3J5IiwibmFtZSI6InNhbWFsYmEvbXktYXBwIiwiYWN0aW9ucyI6WyJwdXNoIl19XX0
   338      ```
   339  
   340      for the Claim Set. These two are concatenated using a '.' character,
   341      yielding the string:
   342  
   343      ```
   344      eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IlBZWU86VEVXVTpWN0pIOjI2SlY6QVFUWjpMSkMzOlNYVko6WEdIQTozNEYyOjJMQVE6WlJNSzpaN1E2In0.eyJpc3MiOiJhdXRoLmRvY2tlci5jb20iLCJzdWIiOiJqbGhhd24iLCJhdWQiOiJyZWdpc3RyeS5kb2NrZXIuY29tIiwiZXhwIjoxNDE1Mzg3MzE1LCJuYmYiOjE0MTUzODcwMTUsImlhdCI6MTQxNTM4NzAxNSwianRpIjoidFlKQ08xYzZjbnl5N2tBbjBjN3JLUGdiVjFIMWJGd3MiLCJhY2Nlc3MiOlt7InR5cGUiOiJyZXBvc2l0b3J5IiwibmFtZSI6InNhbWFsYmEvbXktYXBwIiwiYWN0aW9ucyI6WyJwdXNoIl19XX0
   345      ```
   346  
   347      This is then used as the payload to a the `ES256` signature algorithm
   348      specified in the JOSE header and specified fully in [Section 3.4 of the JSON Web Algorithms (JWA)
   349      draft specification](https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-38#section-3.4)
   350  
   351      This example signature will use the following ECDSA key for the server:
   352  
   353      ```
   354      {
   355          "kty": "EC",
   356          "crv": "P-256",
   357          "kid": "PYYO:TEWU:V7JH:26JV:AQTZ:LJC3:SXVJ:XGHA:34F2:2LAQ:ZRMK:Z7Q6",
   358          "d": "R7OnbfMaD5J2jl7GeE8ESo7CnHSBm_1N2k9IXYFrKJA",
   359          "x": "m7zUpx3b-zmVE5cymSs64POG9QcyEpJaYCD82-549_Q",
   360          "y": "dU3biz8sZ_8GPB-odm8Wxz3lNDr1xcAQQPQaOcr1fmc"
   361      }
   362      ```
   363  
   364      A resulting signature of the above payload using this key is:
   365  
   366      ```
   367      QhflHPfbd6eVF4lM9bwYpFZIV0PfikbyXuLx959ykRTBpe3CYnzs6YBK8FToVb5R47920PVLrh8zuLzdCr9t3w
   368      ```
   369  
   370      Concatenating all of these together with a `.` character gives the
   371      resulting JWT:
   372  
   373      ```
   374      eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IlBZWU86VEVXVTpWN0pIOjI2SlY6QVFUWjpMSkMzOlNYVko6WEdIQTozNEYyOjJMQVE6WlJNSzpaN1E2In0.eyJpc3MiOiJhdXRoLmRvY2tlci5jb20iLCJzdWIiOiJqbGhhd24iLCJhdWQiOiJyZWdpc3RyeS5kb2NrZXIuY29tIiwiZXhwIjoxNDE1Mzg3MzE1LCJuYmYiOjE0MTUzODcwMTUsImlhdCI6MTQxNTM4NzAxNSwianRpIjoidFlKQ08xYzZjbnl5N2tBbjBjN3JLUGdiVjFIMWJGd3MiLCJhY2Nlc3MiOlt7InR5cGUiOiJyZXBvc2l0b3J5IiwibmFtZSI6InNhbWFsYmEvbXktYXBwIiwiYWN0aW9ucyI6WyJwdXNoIl19XX0.QhflHPfbd6eVF4lM9bwYpFZIV0PfikbyXuLx959ykRTBpe3CYnzs6YBK8FToVb5R47920PVLrh8zuLzdCr9t3w
   375      ```
   376  
   377  This can now be placed in an HTTP response and returned to the client to use to
   378  authenticate to the audience service:
   379  
   380  
   381  ```
   382  HTTP/1.1 200 OK
   383  Content-Type: application/json
   384  
   385  {"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IlBZWU86VEVXVTpWN0pIOjI2SlY6QVFUWjpMSkMzOlNYVko6WEdIQTozNEYyOjJMQVE6WlJNSzpaN1E2In0.eyJpc3MiOiJhdXRoLmRvY2tlci5jb20iLCJzdWIiOiJqbGhhd24iLCJhdWQiOiJyZWdpc3RyeS5kb2NrZXIuY29tIiwiZXhwIjoxNDE1Mzg3MzE1LCJuYmYiOjE0MTUzODcwMTUsImlhdCI6MTQxNTM4NzAxNSwianRpIjoidFlKQ08xYzZjbnl5N2tBbjBjN3JLUGdiVjFIMWJGd3MiLCJhY2Nlc3MiOlt7InR5cGUiOiJyZXBvc2l0b3J5IiwibmFtZSI6InNhbWFsYmEvbXktYXBwIiwiYWN0aW9ucyI6WyJwdXNoIl19XX0.QhflHPfbd6eVF4lM9bwYpFZIV0PfikbyXuLx959ykRTBpe3CYnzs6YBK8FToVb5R47920PVLrh8zuLzdCr9t3w"}
   386  ```
   387  
   388  ## Using the signed token
   389  
   390  Once the client has a token, it will try the registry request again with the
   391  token placed in the HTTP `Authorization` header like so:
   392  
   393  ```
   394  Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IkJWM0Q6MkFWWjpVQjVaOktJQVA6SU5QTDo1RU42Ok40SjQ6Nk1XTzpEUktFOkJWUUs6M0ZKTDpQT1RMIn0.eyJpc3MiOiJhdXRoLmRvY2tlci5jb20iLCJzdWIiOiJCQ0NZOk9VNlo6UUVKNTpXTjJDOjJBVkM6WTdZRDpBM0xZOjQ1VVc6NE9HRDpLQUxMOkNOSjU6NUlVTCIsImF1ZCI6InJlZ2lzdHJ5LmRvY2tlci5jb20iLCJleHAiOjE0MTUzODczMTUsIm5iZiI6MTQxNTM4NzAxNSwiaWF0IjoxNDE1Mzg3MDE1LCJqdGkiOiJ0WUpDTzFjNmNueXk3a0FuMGM3cktQZ2JWMUgxYkZ3cyIsInNjb3BlIjoiamxoYXduOnJlcG9zaXRvcnk6c2FtYWxiYS9teS1hcHA6cHVzaCxwdWxsIGpsaGF3bjpuYW1lc3BhY2U6c2FtYWxiYTpwdWxsIn0.Y3zZSwaZPqy4y9oRBVRImZyv3m_S9XDHF1tWwN7mL52C_IiA73SJkWVNsvNqpJIn5h7A2F8biv_S2ppQ1lgkbw
   395  ```
   396  
   397  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)
   398  
   399  ## Verifying the token
   400  
   401  The registry must now verify the token presented by the user by inspecting the
   402  claim set within. The registry will:
   403  
   404  - Ensure that the issuer (`iss` claim) is an authority it trusts.
   405  - Ensure that the registry identifies as the audience (`aud` claim).
   406  - Check that the current time is between the `nbf` and `exp` claim times.
   407  - If enforcing single-use tokens, check that the JWT ID (`jti` claim) value has
   408    not been seen before.
   409    - To enforce this, the registry may keep a record of `jti`s it has seen for
   410      up to the `exp` time of the token to prevent token replays.
   411  - Check the `access` claim value and use the identified resources and the list
   412    of actions authorized to determine whether the token grants the required
   413    level of access for the operation the client is attempting to perform.
   414  - Verify that the signature of the token is valid.
   415  
   416  At no point in this process should the registry need to <em>call back</em> to
   417  the authorization server. If anything, it would only need to update a list of
   418  trusted public keys for verifying token signatures or use a separate API
   419  (still to be spec'd) to add/update resource records on the authorization
   420  server.