# auth

## Registry的Token认证

registry的认证方式有三种，本文介绍最常用的token认证机制。

### 认证流程

我们以`docker pull`操作描述一下token认证的流程

![](/files/-MA1FH0_z9cmNKrS4cdC)

1、docker-daemon向registry发起pull操作\
2、如果registry开启了认证，则会返回`401 Unauthorized`的响应，并且在响应头中携带如何去认证的信息\
3、docker-daemon向授权服务（authorization service）发起请求，获取token\
4、授权服务返回一个token，该token中携带了权限信息\
5、docker-daemon重新向registry发起同样的请求，并且在头部中携带token\
6、registry对token进行验证，然后开始正常的pull流程

### registry开启token认证

registry要开启token认证，需要在配置文件中添加如下内容，以下四个参数都为必填。

```
auth:
    token:
        realm: {token-realm}
        service: {token-service}
        issuer: {registry-token-issuer}
        rootcertbundle: {/root/certs/bundle}
```

* realm：授权服务器的url
* service：被认证的服务的名字
* issuer：token签发者的名字，授权服务器签发的token中也有该字段；registry与授权服务的该字段要配置成一致，以便registry验证token是否由目标授权服务签发
* rootcertbundle：根证书的绝对路径

以下为本地harbor的registry配置文件中关于token配置的样例：

```
auth:
  token:
    realm: http://192.168.1.103:8021/service/token
    service: token-service
    issuer: registry-token-issuer
    rootcertbundle: /etc/registry/root.crt
```

### 如何认证

当docker-daemon没有携带任何认证信息向registry发起某个API请求时（对应上图中的步骤1），registry会返回`401 Unauthorized`，并且在响应头的参数`WWW-Authenticate`中给出如何去获取token的详细信息。

比如，pull镜像的第一步是获取manifest，那么我们没有携带任何认证信息去拉取本地harbor中的`library/registry:2.5.0`镜像的manifest文件，会得到如下的响应头（docker-daemon也是请求registry的API，这里我们使用curl模拟下载镜像的API请求）：

```
$ curl -I 192.168.1.103:8021/v2/library/registry/manifests/2.5.0
HTTP/1.1 401 Unauthorized
Server: nginx/1.11.5
Date: Wed, 05 Sep 2018 06:55:23 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 148
Connection: keep-alive
Docker-Distribution-Api-Version: registry/2.0
Www-Authenticate: Bearer realm="http://192.168.1.103:8021/service/token",service="token-service",scope="repository:library/registry:pull"
```

注意头部中的`Www-Authenticate`字段的值，告诉我们应该如何去获取token：

```
Www-Authenticate: Bearer realm="http://192.168.1.103:8021/service/token",service="token-service",scope="repository:library/registry:pull"
```

其中`realm`与`service`字段的值就是registry的配置文件中的值；`scope`字段的值，是registry根据接收到的API请求生成的，关于scope字段的语法请参考[官网](/docker/registry/auth.md)

### 请求Token

**QUERY参数**

* `service`：使用registry返回的值
* `scope`：使用registry返回的值
* `client_id`：（可选）发起请求的客户端的标识，比如docker-daemon发起的请求会将该字段设置为`docker`，harbor的复制策略中会将该字段设置为`harbor-registry-client`

**响应BODY字段**

* `token`：授权服务器返回的带有授权信息的token
* `issued_at`：（可选）token的签发时间，UTC标准时间格式
* `expires_in`：（可选）token在多少秒以后过期，如果没有说明则默认为60秒

**Example**

接下来，我们去获取token

```
$ curl 192.168.1.103:8021/service/token?service=token-service\&scope=repository:library/registry:pull\&client_id=curl
{
  "expires_in": 1800,
  "issued_at": "2018-09-05T08:34:40Z",
  "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IkhNNjY6NkNYUzpaQlBROk1ENVo6QlJZVTpTVE9EOkNCUEs6Uk5ORjpYN0VDOkZMUUw6TFNFMjpLUUtTIn0.eyJpc3MiOiJyZWdpc3RyeS10b2tlbi1pc3N1ZXIiLCJzdWIiOiIiLCJhdWQiOiJ0b2tlbi1zZXJ2aWNlIiwiZXhwIjoxNTM2MTM4MjgwLCJuYmYiOjE1MzYxMzY0ODAsImlhdCI6MTUzNjEzNjQ4MCwianRpIjoiZFpxVkgxVDFjZkhXdnFZTiIsImFjY2VzcyI6W3sidHlwZSI6InJlcG9zaXRvcnkiLCJuYW1lIjoibGlicmFyeS9yZWdpc3RyeSIsImFjdGlvbnMiOlsicHVsbCJdfV19.ilCKa2-oJ9bKQpAo8ntcx1lHpbs0BcWYtbRrvItHAProaDEDpll9EZrzkzg6XR9OOLByFm_oJKKk8Y_wYwQfxYdjvhLbFjCNXzE6MckY8dEcSR5BmYxOK54zAqNVkw24ugUcagGFi7p8Gy0YZqBqf7AP8qCarhuWhKsZ7B4esMQk2xBEn1hh8r_9tb6wnZOkDl7trW0IWbPkqKSaP8ycq8oS9J0T6zaItyTLnERsV_GFJOh6DdfhSYzGwoWUFQH6cmp05ZHXF_-4O6N6d8tosGH9gTsam-ffeVHmWp8da_gpS_R15z3ELR5I2FO0s4gWo1UbTI3yuyV8stSURrCs6GHZSMb2C9_2R2r_Q-uDKmdpoazw2G1DxM3PgfXEwANWEjPJMjD0areUXmjwz_hefSMqYFxLi26TaQinG0th7pNz5m0qroefOy1AGyhRZK-t8rsduZJ9EWQCqtXHrPbTES0FoItJmcMqcJZcvQsrJsBMirtijvGdNn55l44-eDFyrIuExerHzU1dJoSijCtqIYxbdnclLE8HSP-vnBD5TOAJoUUdUfA1N8TvF2QqDjr_LATUOctahrFoWiuDjrFXH-ptmcJJ6lPjo1oCOne3ImKe_mieRR7YCOQLejuCbItIIweuqwBzJU5d33k3Drra0qvbvk-MkO7iBNgpCtfWqD8"
}
```

得到的token是一个很长的字符串。不过我们可以复制上面的token字段的内容到[jwt.io](https://github.com/pshizhsysu/docker/tree/f8e99cafd95650f9fc654ebc5fc9df01b2a5c0e2/registry/jwt.io)，查看token的明文，得到payload的内容如下：，查看token的明文，得到payload的内容如下：

```
{
 "iss": "registry-token-issuer",
 "sub": "",
 "aud": "token-service",
 "exp": 1536138280,
 "nbf": 1536136480,
 "iat": 1536136480,
 "jti": "dZqVH1T1cfHWvqYN",
 "access": [
  {
   "type": "repository",
   "name": "library/registry",
   "actions": [
    "pull"
   ]
  }
 ]
}
```

从`access`字段可以看出，该token对`library/registry`这个`repository`具有`pull`权限。

**携带用户信息**

在上面获取token的请求中，没有携带任何的用户信息，所以payload的`sub`字段为空。但是harbor的授权服务器返回的token还是具有pull权限，这是因为harbor允许匿名用户pull `library`中的镜像。不过我们可以携带harbor的admin用户信息去获取token，如下：

```
$ curl -H "Authorization: Basic YWRtaW46SGFyYm9yMTIzNDU=" 192.168.1.103:8021/service/token?service=token-service\&scope=repository:library/registry:pull\&client_id=curl
```

其中`YWRtaW46SGFyYm9yMTIzNDU=`是`admin:Harbor12345`使用base64加密后的字符串：

```
$ echo -n "admin:Harbor12345" | base64
YWRtaW46SGFyYm9yMTIzNDU=
```

### 使用Token

在得到token后，我们就可以在API请求的Header中添加token信息，比如下载镜像的manifest，这次请求就会正常返回了：

```
curl -H "Authorization: Bearer [token]" 192.168.1.103:8021/v2/library/registry/manifests/2.5.0
```

## 扩展知识

### 说说docker-login

docker login命令的使用方法如下：

```
docker login -u admin -p Harbor12345 192.168.1.103:8021
```

当docker login登录成功后，会在当前用户的 `~/.docker/config.json`中出现如下的内容：

```
{
    "auths": {
        "192.168.1.103:8021": {
            "auth": "YWRtaW46SGFyYm9yMTIzNDU="
        }
    }
}
```

其中字符串`YWRtaW46SGFyYm9yMTIzNDU=`是通过base64算法加密得到的字符串。关于base64算法的加密与解密如下：

```
$ echo -n "admin:Harbor12345" | base64
YWRtaW46SGFyYm9yMTIzNDU=
$ echo -n "YWRtaW46SGFyYm9yMTIzNDU=" | base64 -d
admin:Harbor12345
```

当docker login成功后，我们再执行docker pull或push操作的时候，docker client便会从当前用户下查找registry的登录信息，并携带给docker daemon，docker-daemon便会在向Authorization Service请求token的API请求头中携带这个字符串。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://pshizhsysu.gitbook.io/docker/registry/auth.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
