registry v2 解析以及如何實現token驗證

提到registry v2,主要改進是支持並行pull鏡像,鏡像層id變成惟一的,解決同一個tag可能對應多個鏡像的問題等等。若是還不太瞭解,能夠且聽我細細道來。git

首先不得不說的是v2 新加了一個概念Digest

他是基於內容進行尋址(Content-addressable)算法算出來的一串hash值。簡單的說就是內容不一樣,得出了的digest值是不一樣的,可是內容相同的話,得出的digest值是必定相同的。咱們的每一個鏡像層id就是根據每一個鏡像層的內容得出來的digest的。github

因此你在改動鏡像層之後生成的digest就不一樣了,而不動的話,這個digest仍是不變的,那麼這個digest id是何時生成的呢?咱們在本地構建鏡像時生成的鏡像層id每次都是不同的,這個digest是咱們在push鏡像時生成的。redis

爲了驗證內容相同,push到registry獲得的digest相同,我作了個小實驗,用以下Dockerfile 註釋掉第三行和不註釋構建了兩次鏡像,再push到registry算法

圖片描述

若是是v1的話,push上去獲得的層id確定是不同的,可是v2裏面,註釋第三行獲得了5個鏡像層,不註釋掉第三行獲得了6個鏡像層,而且第一次的5個層都包含在第二次6個裏面。因此得出結論這個digest確實是根據內容生成的。docker

接下來講一下鏡像的id

鏡像id也是生成了一個digest值,鏡像id是根據_manifest這個文件,也就是鏡像層id和鏡像名字等一些其餘信息生成的digest。咱們在每次向v2 push鏡像時候,最後都會返回給docker client一個digest值,這個值就表明了鏡像的digest id。這個id的做用就是能夠指定惟一的鏡像了。相似tag使用。數據庫

由於咱們知道v1時候用tag有個弊病就是屢次構建的鏡像可使用同一個tag,致使咱們用tag標識鏡像的時候可能並非咱們想要的,而用了digest就不會出現這種問題。api

咱們在寫Dockerfile的時候引用鏡像就能夠這麼用:安全

FROM localhost:5000/test@sha256:ac81211548c0d228e10daaf76f6e0024e5f91879c8a7e105e777d6f964270449dom

像使用tag同樣,用本地docker查看鏡像digests時候可使用:docker images --digests,ui

固然,目前來講你看到的都是<none>,咱們須要從registry上pull下來,使用
docker pull localhost:5000/test@sha256:ac81211548c0d228e10daaf76f6e0024e5f91879c8a7e105e777d6f964270449

瞭解了digest,咱們來看一看registry的存儲結構

這部分最好能夠對着registry的文件夾結構來看。簡單的畫了個草圖。

圖片描述

是v2的文件夾層級關係

圖片描述

這個是目錄下具體的樣子,能夠先看一下子,能夠看到registry 下面有兩個文件夾blobs和repositories,

blobs下面存儲了registry的全部基本信息元素,包括鏡像層digest和鏡像digest,以後在經過某種方式將調用這裏的信息。

blobs文件夾下面先分了一個等級,是digest的前兩位字符組成的文件夾爲了篩選digest,避免了這個文件夾太大,查看時都難。這樣方便定位。每一個blob裏都有一個data文件,存儲相關信息。

repositories下面存儲的是各個鏡像名字命名的文件夾,進入一個鏡像文件夾後,能夠看到三個子文件夾,_layers, _manifests, _uploads,_layers這個文件夾是跟這個鏡像有關的全部鏡像層,進入其中一個鏡像層文件夾,下面只有一個文件--link命名的文件,裏面的內容就是一個digest。

這就跟blobs下面的鏡像層創建起了聯繫,在repositories這個文件夾下,都是經過link文件與blobs文件夾下的文件創建的聯繫。

_upload這個文件夾,平時點進去是空的,這個文件夾主要做用是上傳用的。咱們上傳鏡像層的時候,先上傳到這個文件夾下,等完成傳輸之後,在將這個文件夾下的內容移動到blobs下面,而後將原來的文件刪除。

_manifest這個文件夾很是重要,是鏡像的相關信息。他下面有兩個子文件夾,revisions和tags,revisions這個文件夾下是這個鏡像的全部可用的鏡像digest,裏面的link文件指向了鏡像的digest。咱們去blobs裏面找相應的id對應的文件,查看文件下面的data,咱們發現這個data文件裏面存儲的信息,和咱們經過registry v2 rest api請求manifest信息相同~在看_manifest/tags/。下面就是這個鏡像的不一樣tag了。又分了current和index分別表示當前tag對應的digest和此tag下的全部鏡像digest。

下面來介紹一下如何搭建token驗證的registry

先看一下官方給的圖

圖片描述

能夠看到,v2和v1相比,訪問形式徹底變了,去掉了index server,加上了一個auth server。

訪問順序是這樣的

  1. 咱們經過docker client讓docker deamon先訪問registry,registry若是不須要身份驗證,則直接返回結果,若須要驗證,返回401並在header附帶一些信息,

  2. daemon根據信息訪問auth server。auth server判斷經過了驗證,並返回給daemon一串token。

  3. daemon帶着這串token再去訪問registry則能夠得到到信息,pull ,push,api都走的這套流程。

接下來貼下個人配置文件config.yml,配置了選項auth/token表示要token驗證。這個流程確實須要好好讀一下,跟她的加密方式有很大關係

version: 0.1
log:
fields:
service: registry
storage:
cache:
blobdescriptor: redis
filesystem:
rootdirectory: /var/lib/registry
http:
addr: :5000
secret: randomstringsecret
tls:
certificate: /root/sslkeys/domain.crt
key: /root/sslkeys/domain.key
auth:
token:
realm: https://registry.tenxcloud.com:5001/auth
service: test123.tenxcloud.com:5000
issuer: qwertyui
rootcertbundle: /root/sslkeys/domain.crt
固然,我是經過容器啓動的v2,把這個config volume進去的

個人啓動命令:docker run -d -p 5000:5000 --restart=always --name registry -v pwd/sslkeys:/root/sslkeys -v pwd/config.yml:/etc/docker/registry/config.yml -v pwd/data:/var/lib/registry registry:2.1.1

auth/token下的4個子選項都是必須配置的,realm表示個人auth server地址,service表示的registry的地址,issuer是一串標示符,隨便寫一下,auth server加密的時候也須要配置一樣的字符串。rootcertbundle配置一個祕鑰,對token進行加密。(我這裏複用了個人tls token)

當咱們第一次未帶token訪問registry時會返回401並附帶以下信息:Www-Authenticate: Bearer realm="test123.tenxcloud.com:5000 ",service="test123.tenxcloud.com:5000 "scope="repository:husseingalal/hello:push",realm和service就是前面說的,scope表示的是我要作的操做,repository表明鏡像,接着是鏡像名字,而後是pull或者是push或者兩者都有

知道了各個參數的功能,就能夠搭建咱們的auth server了,我從github下找到了一個項目:https://github.com/cesanta/docker_auth

用go語言寫的,其中的帳號密碼存儲方式有:寫入文件,ldap和google帳號的。這個server實現了動態加載配置文件,配置文件有變化這個server會進行安全的重啓,因此能夠對文件動態添加帳號密碼。固然也能夠本身寫身份驗證,添加數據庫等方式的,只須要繼承Authenticator這個interface就能夠。添加起來很容易。身份驗證後有權限控制acl,而且咱們也能夠自定義權限控制,繼承Authorizer這個interface便可。

前兩項經過後就是生成token(這一部分這個項目已經封裝好了,不用改動),
v2的token採用的JWT加密方式,JWT分了三個部分,header,payload,signature,header裏面帶的信息是token加密方式,通常都是base64, payload裏帶的都是須要的基本信息,user,權限,過時時間,還有前面說的issure 等等,
signature是header+payload後用祕鑰進行加密,這個祕鑰就是配置在registry裏的rootcertbundle對應的祕鑰。

三部分經過 . 鏈接, 由於有祕鑰加密,保證了token信息不會被竄改,這種加密方式保證了將權限驗證和registry分離也 是安全的, 將token發送回daemon後daemon就能夠帶着token去正常訪問registry了,若是是rest api,直接將其寫成Bearer token就能夠請求了。

本文做者:時速雲工程師丁麒偉 原文連接:registry v2 解析以及如何實現token驗證

相關文章
相關標籤/搜索