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  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  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.