如何使用Docker注册表HTTP API V2获取Docker注册表中所有存储库的列表



我与之合作的外部组织使我访问了私人(Auth Token受保护的(Docker注册表,最终我希望能够使用Docker的HTTP API来查询此注册表V2,为了获得注册表中所有可用的存储库和/或图像的列表。

但是,在我这样做之前,我首先想在公共注册表(例如Docker Hub(上构建这些类型的API查询来获得一些基本实践。因此,我继续在Docker Hub上注册了自己的用户名和密码,还咨询了API V2文档,该文档可能会请求API版本检查为:

GET /v2/

或请求存储库列表为:

GET /v2/_catalog

使用Curl,以及用于注册Docker Hub帐户的用户名和密码,我尝试在命令行中构造GET请求:

stachyra> curl -u stachyra:<my_password> -X GET https://index.docker.io/v2/
{"errors":[{"code":"UNAUTHORIZED","message":"authentication required","detail":null}]}
stachyra> curl -u stachyra:<my_password> -X GET https://index.docker.io/v2/_catalog
{"errors":[{"code":"UNAUTHORIZED","message":"authentication required","detail":[{"Type":"registry","Class":"","Name":"catalog","Action":"*"}]}]}

当然,在哪里代替<my_password>,我替换了我的实际帐户密码。

我从此查询中期望的响应是一条巨大的JSON消息,列出了数千个存储库名称,但似乎API拒绝了我的Docker Hub凭据。

问题1:我是否对Docker Hub注册表有正确的URL(index.docker.io(?(我首先根据命令行工具docker info返回的状态信息进行了此假设,因此我有充分的理由认为这是正确的。(

问题2:假设我对注册表服务本身具有正确的URL,为什么我的查询返回"未经授权"错误代码?当我尝试通过网络登录Hub.docker.com登录时,我的帐户凭据正常工作,那么两种情况之间有什么区别?

我什至有正确的URL

  • " Docker"是" Dockerhub"协议。是实现Docker协议但不限于它的产品。Docker API也由其他提供商实施,例如:
    • gitlab(registry.gitlab.com(
    • github cr(ghcr.io(
    • GCP GCR(gcr.io(
    • aws ecr(public.ecr.aws&amp;&lt; councel_id&gt; .dkr.ecr..amazonaws.com(
    • azure acr(&lt; gungistry_name&gt; .azurecr.io(
  • index.docker.io由Dockerhub主持Docker实施。
  • hub.docker.com托管富的dockerhub特定API。
  • 注意:Dockerhub实现了通用Docker HTTP API V2,但它不会从通用API集实现_catalog API。

为什么我的查询返回"未授权"错误代码?

为了使用Docker V2 API,需要从https://auth.docker.io/token生成JWT auth令牌,每个呼叫都需要在index.docker.io

的DockerHub呼叫中用作buarer令牌。

当我们像这样击中DockerHub API时:https://index.docker.io/v2/library/alpine/tags/list,它将返回401,其中包含有关丢失的飞行前验证呼叫的信息。我们在失败的请求中寻找www-authenticate响应标头。

eg: www-authenticate: Bearer realm="https://auth.docker.io/token",service="registry.docker.io",scope="repository:library/alpine:pull",error="invalid_token"

这意味着,我们需要明确致电API以获取auth令牌。

https://auth.docker.io/token?service=registry.docker.io&scope=repository:library/alpine:pull

https://auth.docker.io/token无需任何验证即可用于公共存储库。要访问私人存储库,我们需要在请求中添加基本的HTTP Auth。

https://<username>:<password>@auth.docker.io/token?service=registry.docker.io&scope=repository:<repo>:pull

注意:即使请求无效,auth.docker.io也会生成令牌(无效的信用,范围或任何内容(。要验证令牌,我们可以解析JWT(例如:来自JWT.IO(并在有效载荷中检查access字段,应包含请求的范围参考。

这是一个示例程序,可以从注册表中读取存储库。我将其用作Docker Hub的学习帮助。

#!/bin/bash
set -e
# set username and password
UNAME="username"
UPASS="password"
# get token to be able to talk to Docker Hub
TOKEN=$(curl -s -H "Content-Type: application/json" -X POST -d '{"username": "'${UNAME}'", "password": "'${UPASS}'"}' https://hub.docker.com/v2/users/login/ | jq -r .token)
# get list of repos for that user account
REPO_LIST=$(curl -s -H "Authorization: JWT ${TOKEN}" 
https://hub.docker.com/v2/repositories/${UNAME}/?page_size=10000 | jq -r '.results|.[]|.name')
# build a list of all images & tags
for i in ${REPO_LIST}
do
  # get tags for repo
  IMAGE_TAGS=$(curl -s -H "Authorization: JWT ${TOKEN}" 
  https://hub.docker.com/v2/repositories/${UNAME}/${i}/tags/?page_size=10000 | jq -r '.results|.[]|.name')
  # build a list of images from tags
  for j in ${IMAGE_TAGS}
  do
    # add each tag to list
    FULL_IMAGE_LIST="${FULL_IMAGE_LIST} ${UNAME}/${i}:${j}"
  done
done
# output list of all docker images
for i in ${FULL_IMAGE_LIST}
do
  echo ${i}
done

(这来自Docker网站上的一篇文章,该文章描述了如何使用API。(

本质上...

  • 获取令牌
  • 将令牌作为标头Authorization: JWT <token>通过任何API调用您制作的
  • 您要用来列出存储库的API调用是https://hub.docker.com/v2/repositories/<username>/

这个网站说我们不能:(

dockerhub拥有公共和私人存储库的组合,但没有 公开目录端点以编程方式列出它们。

我已经修改了https://stackoverflow.com/a/a/60549026/7281491,这样我就可以搜索任何其他任何其他用户/org dockerhub image list:

#!/bin/bash
set -e
# User to search for
UNAME=${1}

# Put your own docker hub TOKEN.
# You can use pass command or 1password cli to store pat 
TOKEN=dckr_pat_XXXXXXXXXXXXXXXXXXXXXXXx

# get list of namespaces accessible by user (not in use right now)
#NAMESPACES=$(curl -s -H "Authorization: JWT ${TOKEN}" https://hub.docker.com/v2/repositories/namespaces/ | jq -r '.namespaces|.[]')
# get list of repos for that user account
REPO_LIST=$(curl -s -H "Authorization: JWT ${TOKEN}" https://hub.docker.com/v2/repositories/${UNAME}/?page_size=10000 | jq -r '.results|.[]|.name')
# build a list of all images & tags
for i in ${REPO_LIST}
do
  # get tags for repo
  IMAGE_TAGS=$(curl -s -H "Authorization: JWT ${TOKEN}" https://hub.docker.com/v2/repositories/${UNAME}/${i}/tags/?page_size=10000 | jq -r '.results|.[]|.name')
  # build a list of images from tags
  for j in ${IMAGE_TAGS}
  do
    # add each tag to list
    FULL_IMAGE_LIST="${FULL_IMAGE_LIST} ${UNAME}/${i}:${j}"
  done
done
# output list of all docker images
for i in ${FULL_IMAGE_LIST}
do
  echo ${i}
done

样本输出:

gitlab/gitlab-ce:latest
gitlab/gitlab-ce:nightly
gitlab/gitlab-ce:15.5.9-ce.0
gitlab/gitlab-ce:15.6.6-ce.0
gitlab/gitlab-ce:rc
gitlab/gitlab-ce:15.7.5-ce.0
gitlab/gitlab-ce:15.7.3-ce.0
gitlab/gitlab-ce:15.5.7-ce.0
gitlab/gitlab-ce:15.6.4-ce.0
gitlab/gitlab-ce:15.7.2-ce.0
gitlab/gitlab-ce:15.7.1-ce.0
gitlab/gitlab-ce:15.7.0-ce.0
gitlab/gitlab-ce:15.6.3-ce.0
gitlab/gitlab-ce:15.5.6-ce.0
gitlab/gitlab-ce:15.6.2-ce.0
gitlab/gitlab-ce:15.4.6-ce.0
gitlab/gitlab-ce:15.5.5-ce.0
.....

以下是这样做的python代码。这可以访问您的组织和您自己的私人存储库。

附带说明,我还有一堆可以访问清单的代码,但仅在私人/公共用户存储库上,但没有组织级别的存储库,任何人都知道为什么是?

docker_username = ""
docker_password = ""
docker_organization = ""

auth_url = "https://hub.docker.com/v2/users/login/"
auth_data = {
    "username": docker_username,
    "password": docker_password
}
auth_response = requests.post(auth_url, json=auth_data)
auth_response.raise_for_status()
docker_hub_token = auth_response.json()["token"]
repositories_list = f"https://hub.docker.com/v2/repositories/{docker_username}/?page_size=100"
# repositories_list = f"https://hub.docker.com/v2/repositories/{docker_organization}/?page_size=100"
repos_headers = {
    "Authorization": f"JWT {docker_hub_token}"
}
repos_response = requests.get(repositories_list, headers=repos_headers)
repository_list = repos_response.json()["results"]
for repo in repository_list:
    namespace = repo["namespace"]
    repo_name = repo["name"]
    combined_name = f"{namespace}/{repo_name}"
    print(combined_name)

最新更新