Docker Registry/Distribution概述 - Go语言中文社区

Docker Registry/Distribution概述


Registry vs Index

Docker Registry服务中的两个重要组件,分别承担以下不同的职责:

Index:主要负责集中管理用户账户,访问权限,镜像的校验和 以及区分公有和私有repos(也就是公共的命名空间)等,通常是以公开的web接口来实现。

Registry:主要负责镜像数据的存储,提供Pull和Push镜像的功能,以及委派授权部分给index实现.

当你用docker push/pull的时候,index确定你是否有使用/修改镜像的权限,确认之后,registry只是负责把镜像存好或把镜像通过某种方式发送给你。而且,index还能指出某个特定的镜像具体存放的registry地址并向它发出请求。除此之外,当你只是在本地运行docker images这样的命令时,并不需要依赖和Index /registry进行交互。


Docker Hub vs Docker Trusted Registry

Docker Hub 是Docker公司提供的云平台服务,用来分发应用:包括容器镜像分发,变更管理,用户/团队管理,自动化生命周期工作流。实际上,Docker Hub包括了Index模块功能上的实现。

Docker Hub提供的服务主要包括:

>1. 托管docker镜像
>2. 用户授权
>3. 自动构建镜像,以及像Build triggers、Web Hooks这样的工作流工具。
>4. 和Github &Bitbucket集成

Docker Trusted Registry是 Docker Hub 企业版的新版本,DTR 提供了运行和管理私有镜像的存储服务,让你在公司内部可以安全的分发、交付管理自己定制的各种镜像。同样提供了对Registry集群的各种性能上的监控。


Docker Registry

Registry本身是一个无状态,高可扩展性的服务端应用,用来存储和分发docker镜像。经常被用来与subversion,git等做类比。

它实现了:

* 以分层的方式来存储的镜像以及它的描述信息
* 实现与docker client一致的API 
*  以web应用的方式,简单实现一个web服务器让镜像可用。

说白了,Registry是用来存储和内容分发的系统,托管已经命名并且存在不同tag的Docker镜像。上层封装了http接口的服务,底层存储委托给Driver,也就是在对象存储上封装了一层Repo的镜像graph关系,抽象成接口调用。默认的存储驱动是本地POSIX文件系统。

以松耦合的方式把Web UI,用户认证,镜像元数据独立出来,给想做Private Registry服务的开发者自己实现。而Registry是所有模块的复用部分,无状态,单纯用于镜像服务。由于Registry无状态,所以可以水平扩展。

先引入v2版本的一些新的概念。


Content-Addressable 内容可寻址 & Digest

众所周知,镜像在底层是分层存储的。所谓的内容可寻址,是指镜像的层被当作可以根据digest来划分镜像的blobs。那么什么又是digest ?什么是blobs?

实际上,digest是内容唯一的标识符。在内容可寻址系统中,把Digest类型设计成一种灵活的标识符。类比与CRC,md5等,这里的digest是根据密码学中哈希算法生成的校验值。在Registry v2中,使用SHA256算法来生成每个镜像层对应的digest。这种方式易于实现和校验。

digest的格式是一个简单的字符串,由两部分组成。

<algorithm>:<digest>

例如 :

sha256:7173b809ca12ec5dee4506cd86be934c4596dd234ee82c0662eac04a8c2c71dc

blob可以简单理解成镜像层+ JSON数据等构成的一个镜像数据块。它是存储在Registry后端文件系统中,由digest唯一标识的镜像数据。


镜像本身

对于docker-registry中,镜像id是需要确保它的保密性的,因为这个ID是全局性的,不同的镜像仓库中该镜像的id是一致的。这样无疑方便了镜像管理,但同时也存在一定的风险。即,可以通过images id来获取镜像的详细信息。而镜像的layer IDs是被随机分配的,也就是说不能通过layer id来分享images,因为在registry下镜像的layer id可能不同。同时存在着校验的复杂性。

每一层layer都有一个对应的JSON对象来对镜像层进行描述,如下图:

docker-registry

在新版本distribution中,镜像层是可以被直接获取的,并且支持并发的方式,提高了传输效率。这也就是所谓的镜像是内容可寻址的。镜像的id也不再需要保证它的保密性,也更加安全,原因之前讲过不再赘述。具体命令如下:

docker pull ubuntu@sha256:7173b809ca12ec5dee4506cd86be934c4596dd234ee82c0662eac04a8c2c71dc 

镜像的digest以JSON的格式存放在manifest中,如果该层的数据有任何的改动,它的manifest中的digest也会改变。与v1版本的区别在于将所有镜像层的描述放在一个JSON Object中,也就是manifest中。如下图:

输入图片说明

一言以蔽之,新的镜像描述文件简化了镜像的定义和提升了安全性,利用这些新特性提升registry的性能,减少了带宽和后端崩溃的可能性。


工作流

v2版本的工作流较v1版本更为简单,省略了在认证auth token时的往返鉴权方式。 v1版本在部署的时候可以选择鉴权的模式,standalone index_endpoint disable_token_auth. 如果使用disable_token_auth, 你需要自己提供权限认证的方式.(Basic auth, Oauth等)

v2版本的pull/push工作流

输入图片说明

  1. 尝试向Registry发出请求操作
  2. 如果Registry需要权限认证,它会返回一个 401的HTTP响应,其中包括了如何获得授权的信息。
  3. 客户端再向授权服务中心发出请求,请求一个已签名的 JSON web token。
  4. 授权服务中心返回该token。
  5. 客户端向Registry端重试原来的请求,并在请求的头部嵌入token。
  6. Registry授权客户端并且开始Push/Pull的会话。

认证授权服务

通过以上的工作流分析,我们可以清晰得看到两版本的认证方式的变化。 当然,Docker Registry能以无认证检查的方式跑在standalone模式。但是我们可以在Client和Registry中架设认证授权服务,基于原生的公钥鉴权机制增加HTTP认证方式,来提供更好的权限控制。这个认证授权服务,在两版本中均由index中的认证服务来提供。

不过,在之前我们先明确认证授权服务的具体需求:

  • Registry客户端能够生成密钥对,用来授权一个认证服务器。
  • 认证服务器能为任意特定服务所拥有资源的用户账户、公钥和权限控制提供管理。例如,Docker Registry中的repository认证。
  • Docker Registry能够授信”认证服务器“去签署token,让客户端有权限在某段时间能有访问权限。

认证服务器 为Docker Registry中的Repo充当管理员的角色,通过 使用服务账户和公钥 来授权和管理权限的方式,来管理 用户账户和密钥、 集中权限控制表 。


v1版本中,用户在client端发出请求,会先去index服务上做认证,找到镜像所在的registry地址并返回给client端。最后,client端再从registry下载镜像,当然下载之前,registry会先去index校验client发来请求中token的合法性。不同的镜像可以保存在不同的registry服务上,其索引信息都放在index服务上。


v2版本中,认证过程其实包括四个阶段。寻找认证服务器、请求token、返回token、验证token

寻找认证服务器

client端先向Registry发出请求。如果是未授权的请求,会返回401响应。而这个响应中包括三个信息,realm, scope, service 具体代表 用户可以使用scope和 service的信息向realm发请求。举个例子:

realm="https://auth.docker.com/v2/token/",service="registry.docker.com",scope="repository:samalba/my-app:push"

之后客户端会知道,在头部WWW-Authenticate中 加入service 和 scope的值,向 URL https://auth.docker.com/v2/token/ 发送GET请求,来获取token。

请求token

这里分别举例解释一下遇到的参数。

参数描述实例
service是指Registry服务所在的域名service="registry.docker.com"
realm申请授权的URL请求地址realm="https://auth.docker.com/v2/token/"
scope请求资源的具体内容scope=repository:samalba/my-app:push
account客户端的用户名account=gzzhangyi2015

向认证服务器请求一个token来获取访问Repo资源的权限。需要客户端通过TLS或者某种方式授权,并且如果客户端证书中的key关联账号信息,这个token就要签署账号信息。如果证书关联了多个账号,客户端必须指定账号查询参数。返回的token是JSON web token格式,并且用认证服务器的私钥签名。

举个例子,首先,配置认证服务器允许未被验证的客户端进行握手。然后,客户端向认证服务器发送一个HTTP请求到如下的endpoint,该请求用客户端的TLS证书加密。(这个证书可以是自签的)

GET /v2/token/?service=registry.docker.com&scope=repository:samalba/my-app:push&account=jlhawn HTTP/1.1
Host: auth.docker.com

接下来,服务器首先检查客户端证书,提取出子Key,并找到与之关联的用户。 然后,服务器检查它的权限控制列表,验证用户是否具有 registry.docker.com服务下的 samlba/my-app repo的使用权限。

返回token

最后,服务器会形成一个JSON Web Token来签名并返回。

验证token

这个token是用对称算法加密,之后在Registry端用相同的算法解密后,再来验证这个token的一些信息。在整个流程中,Registry都不需要再向认证服务器发出请求。如果有,也只是需要更新认证服务器上的受信任的公钥列表,用来验证token的签名或者使用其他的API来更新认证服务器上的Repo资源记录。

在Registry端 验证Token的步骤如下:

1、确认证书颁发者(issuer :iss claim)是受信任的。一般issuer是认证服务器的FQDN。iss: auth.docker.com

2、确认Registry识别出token的接收方。aud : registry.docker.com

3、检查当前时间在有效时间范围之内。

4、如果token只能使用一次,就要检查JWT的id值是否出现过,来避免token的重复申请。通常会在registry中保持jti记录,延续一个token的有效时间,来避免重复申请token。

5、检查 repo的使用权限,access值就包含了repo名和能够对该repo执行的操作。(决定了token是否授予用户具有对该操作所需权限)。

6、验证token签名的有效性。

转载于:https://my.oschina.net/markz0928/blog/856652

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/weixin_33894640/article/details/92063826
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2019-10-27 18:34:30
  • 阅读 ( 1436 )
  • 分类:docker

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢