如何使用 Docker 注册表 API 提取有关容器的信息?未经授权



不久前,我看到了一篇题为"检查Docker映像"的文章,但没有拉动它们,该文章涉及使用REST调用进行docker inspect所需的特定API调用的本质。但是,我想知道自撰写该文章以来,Docker 注册表 API 是否发生了一些变化。

本文分解了您需要进行三次 REST 调用才能获取有关容器的信息。对于公共 Docker 注册表,它们如下所示:

  1. auth.docker.io 获取令牌的 GET 请求

    curl "https://auth.docker.io/token?scope=repository:<image>:pull&service=registry.docker.io"
    

    在这种情况下,image可以是类似nginxdocker- 基本上是你正在查找的任何图像。此 REST 调用返回要在后续请求中使用的令牌。

  2. 用于检索清单列表的 GET 请求

    curl -H "Accept: application/vnd.docker.distribution.manifest.v2+json"
    -H "Authorization: Bearer <token-from-step-1>"
    "https://registry-1.docker.io/v2/<image>/manifests/<tag>"
    

    这里的image与步骤 1 中的相同,tag可能是类似latest.此调用返回一些 JSON;关键是我们需要提取.config.digest的值。这是我们在最终请求中使用的摘要字符串。

  3. 最后,一个 GET 请求,使用我们在步骤 2 中收到的摘要检索容器配置

    curl -H "Accept: application/vnd.docker.distribution.manifest.v2+json"
    -H "Authorization: Bearer <token-from-step-1>"
    "https://registry-1.docker.io/v2/<image>/blobs/<digest-from-step-2>"
    

    这返回了一些 JSON,我关心的字段是.config

我能够在私有 Docker 注册表上成功测试这一点,尽管在那里我必须为身份验证做一些不同的事情。但是,当我尝试遵循公共 Docker 注册表的指南(我在上面的这些步骤中概述)时,我遇到了相反的问题:步骤 1 给了我一个令牌,但该令牌毫无价值。每当我尝试使用它时,在步骤 2 或 3 中,我都会得到这个:

{"errors":[{"code":"UNAUTHORIZED","message":"authentication required","detail":[{"Type":"repository","Class":"","Name":"docker","Action":"pull"}]}]}

关于如何使其工作的任何想法?

使用以下步骤,可以检索任何公共容器映像的配置。

  1. 获取映像的相应令牌。请注意,您必须指定映像的全名(官方映像使用library存储库)。因此,NGINX图像应称为:library/nginx

    curl 
    --silent 
    "https://auth.docker.io/token?scope=repository:library/nginx:pull&service=registry.docker.io" 
    | jq -r '.token'
    

    为简洁起见,Token Shorted :eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXV...

  2. 从清单中检索映像摘要。对于此请求,您还需要指定有效的标签(此示例使用latest标签)。

    curl 
    --silent 
    --header "Accept: application/vnd.docker.distribution.manifest.v2+json" 
    --header "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXV..." 
    "https://registry-1.docker.io/v2/library/nginx/manifests/latest" 
    | jq -r '.config.digest'
    

    SHA256:2BCB04BDB83f7c5dc30f0edaca1609a716bda1c7d2244d4f5fbbdfef33da366c

  3. 最后,使用以下请求获取容器配置。在 URL 中,必须指定步骤 2 中的摘要。

    curl 
    --silent 
    --location 
    --header "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXV..." 
    "https://registry-1.docker.io/v2/library/nginx/blobs/sha256:2bcb04bdb83f7c5dc30f0edaca1609a716bda1c7d2244d4f5fbbdfef33da366c" 
    | jq -r '.container_config'
    

    为简洁起见,缩短了输出:

    {
    "Hostname": "6c02a05b3d09",
    "Env": [
    "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
    "NGINX_VERSION=1.15.10-1~stretch",
    "NJS_VERSION=1.15.10.0.3.0-1~stretch"
    ],
    "Labels": {
    "maintainer": "NGINX Docker Maintainers <docker-maint@nginx.com>"
    },
    "StopSignal": "SIGTERM"
    }
    

本文分解了您需要进行三次 REST 调用才能获取有关容器的信息。

关于图像

最后是一个 GET 请求来检索容器配置

映像配置

显示映像配置的脚本。它适用于私有注册表和 Docker Hub。对于基本的身份验证注册表,您需要提供第二个参数 (user:pass)。

首先它得到$registry/v2/.如果它返回 401,则执行身份验证。

在 Docker Hub 的情况下,它从WWW-Authenticate标头中获取realmservice,获取$realm?service=$service&scope=repository:$image:pull,并从结果中获取.token,以在以下请求中使用 (-H "Authorization: Bearer $token")。

在其他情况下,它使用基本身份验证(-u "$user:$pass")。

#!/usr/bin/env bash
set -eu
image=$1
creds=${2-}
# https://github.com/moby/moby/blob/v20.10.18/vendor/github.com/docker/distribution/reference/normalize.go#L29-L57
# https://github.com/moby/moby/blob/v20.10.18/vendor/github.com/docker/distribution/reference/normalize.go#L88-L105
registry=${image%%/*}
if [ "$registry" = "$image" ] 
|| { [ "`expr index "$registry" .:`" = 0 ] && [ "$registry" != localhost ]; }; then
registry=docker.io
else
image=${image#*/}
fi
if [ "$registry" = docker.io ] && [ "`expr index "$image" /`" = 0 ]; then
image=library/$image
fi
if [ "`expr index "$image" :`" = 0 ]; then
tag=latest
else
tag=${image#*:}
image=${image%:*}
fi
if [ "$registry" = docker.io ]; then
registry=https://registry-1.docker.io
elif ! [[ "$registry" =~ ^localhost(:[0-9]+)$ ]]; then
registry=https://$registry
fi
r=`curl -sS "$registry/v2/" 
-o /dev/null 
-w '%{http_code}:%header{www-authenticate}'`
http_code=`echo "$r" | cut -d: -f1`
curl_args=(-sS -H 'Accept: application/vnd.docker.distribution.manifest.v2+json')
if [ "$http_code" = 401 ]; then
if [ "$registry" = https://registry-1.docker.io ]; then
header_www_authenticate=`echo "$r" | cut -d: -f2-`
header_www_authenticate=`echo "$header_www_authenticate" | sed -E 's/^Bearer +//'`
split_into_lines() {
sed -Ee :1 -e 's/^(([^",]|"([^"]|")*")*),/1n/; t1'
}
header_www_authenticate=`echo "$header_www_authenticate" | split_into_lines`
extract_value() {
sed -E 's/^[^=]+="(([^"]|")*)"$/1/; s/\(.)/1/g'
}
realm=$(echo "$header_www_authenticate" | grep '^realm=' | extract_value)
service=$(echo "$header_www_authenticate" | grep '^service=' | extract_value)
scope=repository:$image:pull
token=`curl -sS "$realm?service=$service&scope=$scope" | jq -r .token`
curl_args+=(-H "Authorization: Bearer $token")
else
curl_args+=(-u "$creds")
fi
fi
manifest=`curl "${curl_args[@]}" "$registry/v2/$image/manifests/$tag"`
config_digest=`echo "$manifest" | jq -r .config.digest`
curl "${curl_args[@]}" -L "$registry/v2/$image/blobs/$config_digest" | jq -C

用法:

$ ./image-config.sh ruby
$ ./image-config.sh library/ruby            # same as the one above
$ ./image-config.sh docker.io/library/ruby  # same as the previous two
$ ./image-config.sh docker.io/library/ruby:3.0.4
$ ./image-config.sh myregistry.com/hello-world testuser:testpassword
$ ./image-config.sh localhost:5000/hello-world

关于使用 API 的几点说明:

  • 注册表是存储库的集合。想想GitHub。通常存储库名称的形式为user/name(例如nginxproxy/nginx-proxy)。但是对于官方存储库(实际上都以library/开头),可以省略第一段(library/ruby->ruby)。此外,可能只有一个段(例如,在私有注册表中),有时单独的第一部分称为存储库(示例)。或者第二个。
  • 存储库是映像的集合。其中一些被标记。未标记的图像通常在您使用相同的标记推送图像的新版本时出现。
  • 一篇旧的,但可能有点最新的相关文章(至少是开头)。
  • 有关 Docker Hub 的注册表 URL,请参阅此答案。
  • 不要指望规范中的所有内容都能正常工作。 例如,Docker Hub 不实现/v2/_catalog路由。

最新更新