How to view Docker image layers on Docker Hub? - docker

I know that I can use this command $ docker images --tree docker history to view the layers of a Docker image, but how do I do that for images on Docker Hub without pulling it? This is so that I know what is on an image before I download it.
E.g., for the Tomcat repo, https://registry.hub.docker.com/_/tomcat/, the webpage doesn't seem to show what is on the image. I have to look at the Dockerfile on Github to find out.
Update
I see this repo https://registry.hub.docker.com/u/tutum/tomcat/ has more tabs. The "Dockerfile" tab shows how it is created, but only seems to show the latest version. Is there no choice to view the file for other tags?

Docker Hub is quite limited at the moment and does not offer the feature you asked for.
When an image is configured to build from source at Docker Hub (an Automated Build) you can see what went into it, but when it is uploaded pre-built you have no information.

This is now possible by clicking into the image tag on Docker Hub.

Use microbadger, which comes with a few badges as well, eg:
So your tomcat image on https://microbadger.com/images/tomcat would look like:

You can get this for any registry by using the registry API. Within the image manifest is a pointer to the image config, and that config is a json blob that includes the history steps. Note that this history is not authoritative, and depending on the build tooling, could report different commands than what were actually used to build the image layers.
A shell script version of this using the auth for Docker Hub looks like:
#!/bin/sh
ref="${1:-library/ubuntu:latest}"
repo="${ref%:*}"
tag="${ref##*:}"
token=$(curl -s "https://auth.docker.io/token?service=registry.docker.io&scope=repository:${repo}:pull" \
| jq -r '.token')
digest=$(curl -H "Accept: application/vnd.docker.distribution.manifest.v2+json" \
-H "Authorization: Bearer $token" \
-s "https://registry-1.docker.io/v2/${repo}/manifests/${tag}" \
| jq -r .config.digest)
curl -H "Accept: application/vnd.docker.container.image.v1+json" \
-H "Authorization: Bearer $token" \
-s -L "https://registry-1.docker.io/v2/${repo}/blobs/${digest}" | jq .history
Behind the scenes, what's happening is the registry first resolves any manifest list to a single manifest for the default architecture, but you could pull any architecture manually:
$ regctl image manifest --list localhost:5000/library/alpine:latest --format '{{jsonPretty .}}'
{
"manifests": [
{
"digest": "sha256:69704ef328d05a9f806b6b8502915e6a0a4faa4d72018dc42343f511490daf8a",
"mediaType": "application\/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "amd64",
"os": "linux"
},
"size": 528
},
{
"digest": "sha256:18c29393a090ba5cde8a5f00926e9e419f47cfcfd206cc3f7f590e91b19adfe9",
"mediaType": "application\/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "arm",
"os": "linux",
"variant": "v6"
},
"size": 528
},
...
Then that platform specific manifest includes the config and layers:
$ regctl image manifest localhost:5000/library/alpine#sha256:69704ef328d05a9f806b6b8502915e6a0a4faa4d72018dc42343f511490daf8a --format '{{jsonPretty .}}'
{
"schemaVersion": 2,
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"config": {
"mediaType": "application/vnd.docker.container.image.v1+json",
"size": 1471,
"digest": "sha256:14119a10abf4669e8cdbdff324a9f9605d99697215a0d21c360fe8dfa8471bab"
},
"layers": [
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 2814446,
"digest": "sha256:a0d0a0d46f8b52473982a3c466318f479767577551a53ffc9074c9fa7035982e"
}
]
}
And pulling the config blob shows all of the contents you may recognize from the history and inspect commands:
$ regctl blob get localhost:5000/library/alpine sha256:14119a10abf4669e8cdbdff324a9f9605d99697215a0d21c360fe8dfa8471bab | jq .
{
"architecture": "amd64",
"config": {
"Hostname": "",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
],
"Cmd": [
"/bin/sh"
],
"Image": "sha256:d3e0b6258ec2f725c19668f11ae5323c3b0245e197ec478424ec6a87935690eb",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": null,
"OnBuild": null,
"Labels": null
},
"container": "330289c649db86f5fb1ae5bfef18501012b550adb0638b9193d4a3a4b65a2f9b",
"container_config": {
"Hostname": "330289c649db",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
],
"Cmd": [
"/bin/sh",
"-c",
"#(nop) ",
"CMD [\"/bin/sh\"]"
],
"Image": "sha256:d3e0b6258ec2f725c19668f11ae5323c3b0245e197ec478424ec6a87935690eb",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": null,
"OnBuild": null,
"Labels": {}
},
"created": "2021-08-27T17:19:45.758611523Z",
"docker_version": "20.10.7",
"history": [
{
"created": "2021-08-27T17:19:45.553092363Z",
"created_by": "/bin/sh -c #(nop) ADD file:aad4290d27580cc1a094ffaf98c3ca2fc5d699fe695dfb8e6e9fac20f1129450 in / "
},
{
"created": "2021-08-27T17:19:45.758611523Z",
"created_by": "/bin/sh -c #(nop) CMD [\"/bin/sh\"]",
"empty_layer": true
}
],
"os": "linux",
"rootfs": {
"type": "layers",
"diff_ids": [
"sha256:e2eb06d8af8218cfec8210147357a68b7e13f7c485b991c288c2d01dc228bb68"
]
}
}
The regctl commands above come from my regclient project, but I believe you can do similar commands with skopeo and crane. The advantage of these tools over curl commands is handling the authentication that varies between registries (the bearer token from Hub won't work for other registries).
Lastly, the one-line answer using regctl is a formatted image config command, which performs all the above steps without needing jq:
$ regctl image config localhost:5000/library/alpine:latest --format '{{jsonPretty .History}}'
[
{
"created": "2021-08-27T17:19:45.553092363Z",
"created_by": "/bin/sh -c #(nop) ADD file:aad4290d27580cc1a094ffaf98c3ca2fc5d699fe695dfb8e6e9fac20f1129450 in / "
},
{
"created": "2021-08-27T17:19:45.758611523Z",
"created_by": "/bin/sh -c #(nop) CMD [\"/bin/sh\"]",
"empty_layer": true
}
]

Related

How to download docker image using HTTP API using docker hub credentials

So I have a private repository at docker hub
and I am trying to download image (blobs) manually using HTTP API.
Now there are some issues
I have tried https://hub.docker.com/support/doc/how-do-i-authenticate-with-the-v2-api
and this script works and I can see my tags.
But there is no API in docker HUB api to get list of blobs from a tag and then download it.
There is a docker registry api, but there my username password does not work.
What to do?
For an image, you first pull the manifest and parse that for the list of blobs that you need to pull. All of these API's need the same authorization headers used to list the tags. I'm going to be using regctl from my regclient project to query a local registry, but you could also use curl against Hub which I show below.
$ regctl tag ls localhost:5000/library/alpine
3
3-bkup-20210904
3.10
3.11
3.12
3.13
3.14
3.2
3.3
3.4
3.5
3.6
3.7
3.8
3.9
latest
$ regctl manifest get localhost:5000/library/alpine:latest --format body | jq .
{
"manifests": [
{
"digest": "sha256:14b55f5bb845c7b810283290ce057f175de87838be56f49060e941580032c60c",
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "amd64",
"os": "linux"
},
"size": 528
},
{
"digest": "sha256:40f396779ba29da16f29f780963bd4ad5b7719e3eb5dec04516d583713256aa8",
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "arm",
"os": "linux",
"variant": "v6"
},
"size": 528
},
{
"digest": "sha256:392d9d85dff31e34d756be33579f05ef493cb1b0edccc36a11b3295365553bfd",
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "arm",
"os": "linux",
"variant": "v7"
},
"size": 528
},
{
"digest": "sha256:4fb53f12d2ec18199f16d7c305a12c54cda68cc622484bfc3b7346a44d5024ac",
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "arm64",
"os": "linux",
"variant": "v8"
},
"size": 528
},
{
"digest": "sha256:e8d9cf28250078f08e890a3466efbefda68a8feac03cc4076d3ada3397370d6e",
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "386",
"os": "linux"
},
"size": 528
},
{
"digest": "sha256:d860569a59af627dafee0b0f2b8069e31b07fbdaebe552904dbaec28047ccf64",
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "ppc64le",
"os": "linux"
},
"size": 528
},
{
"digest": "sha256:6640b198347e5bf1e9a9dc5fc864e927154275dc31f3d26193b74350a5c94c9f",
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "s390x",
"os": "linux"
},
"size": 528
}
],
"mediaType": "application/vnd.docker.distribution.manifest.list.v2+json",
"schemaVersion": 2
}
$ regctl manifest get localhost:5000/library/alpine#sha256:14b55f5bb845c7b810283290ce057f175de87838be56f49060e941580032c60c --format body | jq .
{
"schemaVersion": 2,
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"config": {
"mediaType": "application/vnd.docker.container.image.v1+json",
"size": 1472,
"digest": "sha256:e9adb5357e84d853cc3eb08cd4d3f9bd6cebdb8a67f0415cc884be7b0202416d"
},
"layers": [
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 2812636,
"digest": "sha256:3d243047344378e9b7136d552d48feb7ea8b6fe14ce0990e0cc011d5e369626a"
}
]
}
$ regctl blob get localhost:5000/library/alpine sha256:e9adb5357e84d853cc3eb08cd4d3f9bd6cebdb8a67f0415cc884be7b0202416d | jq .
{
"architecture": "amd64",
"config": {
"Hostname": "",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
],
"Cmd": [
"/bin/sh"
],
"Image": "sha256:e211ac20c5c7aaa4ed30d5553654d4679082ec48efcb4d164bac6d50d62653fd",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": null,
"OnBuild": null,
"Labels": null
},
"container": "b6ba94212561a8075e1d324fb050db160e25035ffcfbbe5b410e411e2b7000e2",
"container_config": {
"Hostname": "b6ba94212561",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
],
"Cmd": [
"/bin/sh",
"-c",
"#(nop) ",
"CMD [\"/bin/sh\"]"
],
"Image": "sha256:e211ac20c5c7aaa4ed30d5553654d4679082ec48efcb4d164bac6d50d62653fd",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": null,
"OnBuild": null,
"Labels": {}
},
"created": "2022-03-17T04:01:59.188838147Z",
"docker_version": "20.10.12",
"history": [
{
"created": "2022-03-17T04:01:58.883733237Z",
"created_by": "/bin/sh -c #(nop) ADD file:cf4b631a115c2bbfbd81cad2d3041bceb64a8136aac92ba8a63b6c51d60af764 in / "
},
{
"created": "2022-03-17T04:01:59.188838147Z",
"created_by": "/bin/sh -c #(nop) CMD [\"/bin/sh\"]",
"empty_layer": true
}
],
"os": "linux",
"rootfs": {
"type": "layers",
"diff_ids": [
"sha256:5e03d8cae8773cb694fff1d55da34a40d23c2349087ed15ce68476395d33753c"
]
}
}
$ regctl blob get localhost:5000/library/alpine sha256:3d243047344378e9b7136d552d48feb7ea8b6fe14ce0990e0cc011d5e369626a | tar -tvzf - | head
drwxr-xr-x 0/0 0 2022-03-16 16:15 bin/
lrwxrwxrwx 0/0 0 2022-03-16 16:15 bin/arch -> /bin/busybox
lrwxrwxrwx 0/0 0 2022-03-16 16:15 bin/ash -> /bin/busybox
lrwxrwxrwx 0/0 0 2022-03-16 16:15 bin/base64 -> /bin/busybox
lrwxrwxrwx 0/0 0 2022-03-16 16:15 bin/bbconfig -> /bin/busybox
-rwxr-xr-x 0/0 824984 2022-02-02 13:21 bin/busybox
lrwxrwxrwx 0/0 0 2022-03-16 16:15 bin/cat -> /bin/busybox
lrwxrwxrwx 0/0 0 2022-03-16 16:15 bin/chgrp -> /bin/busybox
lrwxrwxrwx 0/0 0 2022-03-16 16:15 bin/chmod -> /bin/busybox
lrwxrwxrwx 0/0 0 2022-03-16 16:15 bin/chown -> /bin/busybox
...
A few examples if you try doing this with curl:
Get a token (specific to Docker Hub, each registry may have different auth methods and servers):
token=$(curl -s "https://auth.docker.io/token?service=registry.docker.io&scope=repository:${repo}:pull" \
| jq -r '.token')
To use a user/password rather than an anonymous login, call curl with the -u user:pass option:
token=$(curl -u "$user:$pass" -s "https://auth.docker.io/token?service=registry.docker.io&scope=repository:${repo}:pull" \
| jq -r '.token')
Tags:
curl -H "Authorization: Bearer $token" \
-s "https://registry-1.docker.io/v2/${repo}/tags/list" | jq .
Manifests:
api="application/vnd.docker.distribution.manifest.v2+json"
apil="application/vnd.docker.distribution.manifest.list.v2+json"
curl -H "Accept: ${api}" -H "Accept: ${apil}" \
-H "Authorization: Bearer $token" \
-s "https://registry-1.docker.io/v2/${repo}/manifests/${sha:-$tag}" | jq .
And blobs:
curl -H "Authorization: Bearer $token" \
-s -L -o - "https://registry-1.docker.io/v2/${repo}/blobs/${digest}"
If you want to export everything to be able to later import, regctl image export will convert the image to a tar of manifests and blobs. This is output in the OCI Layout format, and if you only pull a single image manifest (and not a multi-platform manifest) it will also include the needed files for docker load to import the tar.
There is a docker registry api, but there my username password does not work. What to do?
It probably depends on how you supply them. I didn't try it with a private repository on Docker Hub, but here's a script that downloads public images from Docker Hub, and images from private registries:
#!/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/^(([^",]|"([^"]|\")*")*),/\1\n/; 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`
config=`curl "${curl_args[#]}" -L "$registry/v2/$image/blobs/$config_digest"`
layers=`echo "$manifest" | jq -r '.layers[] | .digest'`
echo "$layers" | \
while IFS= read -r digest; do
curl "${curl_args[#]}" -L "$registry/v2/$image/blobs/$digest" | wc -c
done
Usage:
$ ./download-image.sh hello-world
$ ./download-image.sh library/hello-world
$ ./download-image.sh docker.io/library/hello-world
$ ./download-image.sh myregistry.com/hello-world testuser:testpassword
$ ./download-image.sh localhost:5000/hello-world
Or to be more precise it retrieves the image config and the layers. The thing is, what are you going to do with them? You can try to create a tar archive that looks like the one produced by docker save. Which is what docker-drag basically does. But ideally for that you should know what exactly docker pull and docker save do.
To give you some links to the source code, docker pull (the server part) more or less starts here:
https://github.com/moby/moby/blob/v20.10.18/api/server/router/image/image.go#L37
https://github.com/moby/moby/blob/v20.10.18/api/server/router/image/image_routes.go#L78
https://github.com/moby/moby/blob/v20.10.18/daemon/images/image_pull.go#L54
https://github.com/moby/moby/blob/v20.10.18/daemon/images/image_pull.go#L130
https://github.com/moby/moby/blob/v20.10.18/distribution/pull.go#L52
Most of the relevant code is in distribution/pull_v2.go (the high-level part).
The code that does the http requests is in vendor/github.com/docker/distribution/registry/client/auth/session.go and vendor/github.com/docker/distribution/registry/client/repository.go.
docker save:
https://github.com/moby/moby/blob/v20.10.18/api/server/router/image/image.go#L31
https://github.com/moby/moby/blob/v20.10.18/api/server/router/image/image_routes.go#L160
https://github.com/moby/moby/blob/v20.10.18/daemon/images/image_exporter.go#L16
https://github.com/moby/moby/blob/v20.10.18/image/tarexport/save.go#L187
Most of the code is in image/tarexport/save.go.
But well, apparently docker-drag's developer didn't concern himself with it, and it (docker-drag) seems to work.
In case of private Docker Hub repositories you probably need to add -u user:password to the $realm?service=$service&scope=$scope request.
A couple of remarks about using the API:
A registry is a collection of repositories. Think GitHub. Generally repository names are of the form user/name (e.g. nginxproxy/nginx-proxy). But with official repositories (that all in fact start with library/) the first segment may be omitted (library/ruby -> ruby). Also there might be just one segment (e.g. in private registries), and occasionaly the first part alone is called a repository (example). Or the second.
A repository is a collection of images. Some of them are tagged. Untagged images usually come into being when you push a new version of an image using the same tag.
An old, but maybe somewhat up-to-date relevant article (at least the beginning).
For the Docker Hub's registry URL see this answer.
Don't expect everything in the spec to work. E.g. Docker Hub doesn't implement the /v2/_catalog route.

Get manifest of a public Docker image hosted on Docker Hub using the Docker Registry API

I'm trying to figure out the correct URL to use for this. As an example, let's say I want to get the manifest for the alpine:3.9 tag. I've tried https://hub.docker.com/v2/repositories/library/alpine/manifests/3.9 but that yields a 404 error.
I've found that Docker Hub's registry implementation doesn't really match their documentation. For example, https://docs.docker.com/registry/spec/api/#tags indicates that the URL for getting the list of tags is v2/<name>/tags/list, but when you query Docker Hub, you actually need to leave off the "list" part of the URL: https://hub.docker.com/v2/repositories/library/alpine/tags/. So that makes me question everything about their documentation now when it comes to querying the Docker Hub registry.
TL;DR
The hub.docker.com REST API is not the docker registry API, rather it's a custom API mainly used for the Dockerhub frontend, but could occasionally be useful for other things.
The docker registry API for dockerhub is hosted on registry-1.docker.io, with an alias on registry.docker.io. Even more confusing is that both API's start with the root path /v2.
Full answer
Here are some cURL commands that exercise some of the V2 endpoints. I'm super confused about what the hub.docker.com endpoints are for (https://hub.docker.com/v2/users/login, https://hub.docker.com/v2/repositories/library/, etc.) but I think the /v2/ there is a total red herring and unrelated to the registry V2 API? This article using hub.docker.com can get you tags, but not the manifests.
DOCKERHUB_USERNAME=$(jq -r '.username' < ~/.secrets/docker.json)
DOCKERHUB_PASSWORD=$(jq -r '.password' < ~/.secrets/docker.json)
TARGET_NS_REPO=library/debian
# yes, you need a new token for each repository, maybe you can have multiple scopes though?
PARAMS="service=registry.docker.io&scope=repository:$TARGET_NS_REPO:pull"
TOKEN=$(curl --user "$DOCKERHUB_USERNAME:$DOCKERHUB_PASSWORD" \
"https://auth.docker.io/token?$PARAMS" \
| jq -r '.token'
)
curl "https://registry-1.docker.io/v2/$TARGET_NS_REPO/tags/list" \
-H "Authorization:Bearer $TOKEN" \
| jq '.tags[:10]'
TAG="10-slim"
curl "https://registry-1.docker.io/v2/$TARGET_NS_REPO/manifests/$TAG" \
-H "Authorization:Bearer $TOKEN" \
| jq '.fsLayers'
Output:
[
"10-slim",
"10.0-slim",
"10.0",
"10",
"6.0.10",
"6.0.8",
"6.0.9",
"6.0",
"6",
"7-slim"
]
[
{
"blobSum": "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"
},
{
"blobSum": "sha256:1ab2bdfe97783562315f98f94c0769b1897a05f7b0395ca1520ebee08666703b"
}
]
Reverse engineer it
I basically had to reverse engineer this with mitmproxy. If you want to know how anything else works:
Install/run mitmproxy. Check it's working via:
curl -x localhost:8080 http://mitm.it/cert/pem # should print out a cert
Get/install it's certificate (to MITM yourself):
# Ubuntu 18.04, other distros may vary
MITM_CERT_PATH=/usr/local/share/ca-certificates/mitmproxy.crt
sudo cp ~/.mitmproxy/mitmproxy-ca-cert.cer "$MITM_CERT_PATH"
sudo chown root:root "$MITM_CERT_PATH"
sudo chmod 644 "$MITM_CERT_PATH"
sudo update-ca-certificates
# Verify MITM root cert accepted
curl -x localhost:8080 https://sha256.badssl.com/
# Troubleshooting
# - see if installed (https://unix.stackexchange.com/a/97252/42385)
awk -v cmd='openssl x509 -noout -subject' \
'/BEGIN/{close(cmd)};{print | cmd}' \
< /etc/ssl/certs/ca-certificates.crt \
| grep -i mitmproxy
# - print the cert used (OpenSSL 1.1.0+)
openssl s_client -proxy localhost:8080 -showcerts -connect sha256.badssl.com:443 </dev/null
Uninstall the cert later if desired
sudo rm /usr/local/share/ca-certificates/mitmproxy.crt
sudo update-ca-certificates
Check not in the list
awk -v cmd='openssl x509 -noout -subject' \
'/BEGIN/{close(cmd)};{print | cmd}' \
< /etc/ssl/certs/ca-certificates.crt \
| grep -i mitmproxy
# Double-check MITM root cert rejected
curl -x localhost:8080 https://sha256.badssl.com/
Run dockerd (stop the service if it already is running) with HTTPS_PROXY set
sudo HTTPS_PROXY=http://localhost:8080/ dockerd # bash
# sudo env HTTPS_PROXY=http://localhost:8080/ dockerd # fish
Tell the Docker daemon to do something, e.g. docker pull alpine. In mitmproxy you'd see something like
Flows
GET https://registry-1.docker.io/v2/
← 401 application/json 87b 213ms
GET https://auth.docker.io/token?account=youraccount&scope=repository%3Alibrary%2Fal
pine%3Apull&service=registry.docker.io
← 200 application/json 4.18k 245ms
>> GET https://registry-1.docker.io/v2/library/alpine/manifests/latest
← 200 application/vnd.docker.distribution.manifest.list.v2+json 1.6k 294ms
GET https://registry-1.docker.io/v2/library/alpine/manifests/sha256:57334c50959f26ce
1ee025d08f136c2292c128f84e7b229d1b0da5dac89e9866
← 200 application/vnd.docker.distribution.manifest.v2+json 528b 326ms
GET https://registry-1.docker.io/v2/library/alpine/blobs/sha256:b7b28af77ffec6054d13
378df4fdf02725830086c7444d9c278af25312aa39b9
← 307 text/html 242b 288ms
GET https://registry-1.docker.io/v2/library/alpine/blobs/sha256:0503825856099e6adb39
c8297af09547f69684b7016b7f3680ed801aa310baaa
← 307 text/html 242b 322ms
GET https://production.cloudflare.docker.com/registry-v2/docker/registry/v2/blobs/sh
a256/b7/b7b28af77ffec6054d13378df4fdf02725830086c7444d9c278af25312aa39b9/data?…
← 200 application/octet-stream 1.48k 191ms
GET https://production.cloudflare.docker.com/registry-v2/docker/registry/v2/blobs/sh
a256/05/0503825856099e6adb39c8297af09547f69684b7016b7f3680ed801aa310baaa/data?…
← 200 application/octet-stream 2.66m 207ms
⇩ [27/32] [*:8080]
Inspect the requests. Picking the ...manifests/latest request to look at:
Flow Details
2019-08-20 13:43:44 GET https://registry-1.docker.io/v2/library/alpine/manifests/latest
← 200 OK application/vnd.docker.distribution.manifest.list.v2+json 1.6k 294ms
[[ Request ]] Response Detail
Host: registry-1.docker.io
User-Agent: docker/19.03.1 go/go1.12.5 git-commit/74b1e89 kernel/4.15.0-55-generic
os/linux arch/amd64 UpstreamClient(Docker-Client/19.03.1\\(linux\\))
Accept: application/vnd.docker.distribution.manifest.v2+json
Accept: application/vnd.docker.distribution.manifest.list.v2+json
Accept: application/vnd.oci.image.index.v1+json
Accept: application/vnd.docker.distribution.manifest.v1+prettyjws
Accept: application/json
Accept: application/vnd.oci.image.manifest.v1+json
Authorization: Bearer eyJhbGci...(a big JWT returned by the auth.docker.io req.)
Accept-Encoding: gzip
Connection: close
The registry API is defined by OCI in the distribution-spec.
The complicated part of this is getting auth and headers setup. For an anonymous manifest pull from Docker Hub, that looks like:
#!/bin/sh
ref="${1:-library/ubuntu:latest}"
sha="${ref#*#}"
if [ "$sha" = "$ref" ]; then
sha=""
fi
wosha="${ref%%#*}"
repo="${wosha%:*}"
tag="${wosha##*:}"
if [ "$tag" = "$wosha" ]; then
tag="latest"
fi
api="application/vnd.docker.distribution.manifest.v2+json"
apil="application/vnd.docker.distribution.manifest.list.v2+json"
token=$(curl -s "https://auth.docker.io/token?service=registry.docker.io&scope=repository:${repo}:pull" \
| jq -r '.token')
curl -H "Accept: ${api}" -H "Accept: ${apil}" \
-H "Authorization: Bearer $token" \
-s "https://registry-1.docker.io/v2/${repo}/manifests/${sha:-$tag}" | jq .
Note that official images are all within the library repository, e.g. library/alpine. So this script can be called like the following to pull the alpine:3.9 manifest:
$ ./hub-manifest.sh library/alpine:3.9
{
"manifests": [
{
"digest": "sha256:65b3a80ebe7471beecbc090c5b2cdd0aafeaefa0715f8f12e40dc918a3a70e32",
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "amd64",
"os": "linux"
},
"size": 528
},
{
"digest": "sha256:7a3d88cbc7e2d6c0213deaf2d006933c9f5905c4eb7846b703a66fc6504000b7",
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "arm",
"os": "linux",
"variant": "v6"
},
"size": 528
},
{
"digest": "sha256:cfd8b55d209956f63c8fcc931f5c6874984e5e0ffdcb8f45ba9085f190385d73",
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "arm",
"os": "linux",
"variant": "v7"
},
"size": 528
},
{
"digest": "sha256:f920ccc826134587fffcf1ddc6b2a554947e0f1a5ae5264bbf3435da5b2e8e61",
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "arm64",
"os": "linux",
"variant": "v8"
},
"size": 528
},
{
"digest": "sha256:2a41778b4675b9a91bd2ea3a55a2cfdaf4436aa85a476ee8b48993cdd6989a18",
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "386",
"os": "linux"
},
"size": 528
},
{
"digest": "sha256:6ee74256ce03a4280792ddb67cfefee9119349a63e86ca1c4c6407b08fec008e",
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "ppc64le",
"os": "linux"
},
"size": 528
},
{
"digest": "sha256:7e474fa79d2fc816da8fb626ac37d0344c83cfdffad3d55158123d0cc2683b98",
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "s390x",
"os": "linux"
},
"size": 528
}
],
"mediaType": "application/vnd.docker.distribution.manifest.list.v2+json",
"schemaVersion": 2
}
From there you can see it's outputting a manifest list, and you could pull individual manifests from there:
$ hub-manifest.sh library/alpine#sha256:65b3a80ebe7471beecbc090c5b2cdd0aafeaefa0715f8f12e40dc918a3a70e32
{
"schemaVersion": 2,
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"config": {
"mediaType": "application/vnd.docker.container.image.v1+json",
"size": 1509,
"digest": "sha256:78a2ce922f8665f5a227dc5cd9fda87221acba8a7a952b9665f99bc771a29963"
},
"layers": [
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 2773413,
"digest": "sha256:31603596830fc7e56753139f9c2c6bd3759e48a850659506ebfb885d1cf3aef5"
}
]
}
Shell scripts only get me so far with this, so I've been writing regclient with regctl. There's also crane from Google and skopeo from RedHat that do similar things:
$ regctl manifest get alpine:3.9 --format '{{jsonPretty .}}'
{
"manifests": [
{
"digest": "sha256:65b3a80ebe7471beecbc090c5b2cdd0aafeaefa0715f8f12e40dc918a3a70e32",
"mediaType": "application\/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "amd64",
"os": "linux"
},
"size": 528
},
...
$ regctl manifest get alpine:3.9 --format '{{jsonPretty .}}' --platform linux/amd64
{
"schemaVersion": 2,
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"config": {
"mediaType": "application/vnd.docker.container.image.v1+json",
"size": 1509,
"digest": "sha256:78a2ce922f8665f5a227dc5cd9fda87221acba8a7a952b9665f99bc771a29963"
},
"layers": [
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 2773413,
"digest": "sha256:31603596830fc7e56753139f9c2c6bd3759e48a850659506ebfb885d1cf3aef5"
}
]
}
The advantage of these other commands over curl is they handle different types of auth (basic and bearer), can use credential helpers, and they pass headers for a lot more media types, including the old v1 schema from Docker and the newer OCI schemas.
I was entirely unable to get Docker's Registry API working with DockerHub for manifests :-(
If you find a way, please let me know.
Unfortunately, DockerHub's UI does not include manifest details either.
If you have access to Google Container Registry (GCR), its implementation of Docker Registry API works. Also GCR's UI includes manifests and digests.
I summarized my experiences here:
https://medium.com/google-cloud/adventures-w-docker-manifests-78f255d662ff

Is there a way to convert a x86-only docker image to a multi-platform one?

The newest docker registry/engine have supported "manifest list" feature to allow user referencing images with different CPU architectures, OSes and other characteristics, by solo entry in registry.
So saying I have a legacy x86-only image in my repository ,and unfortunately it'd been altered during previous running as container (successive docker commits) which means no Dockerfile available. Is there a way I can convert this x86-only image to support manifest list without rebuilding it?
Docker can export the contents of a container started from the image. Then a new image can be created FROM scratch that ADDs the contents back.
Steps
docker export will create a tar file of the complete contents of a container created from the image.
$ CID=$(docker create myimage)
$ docker export -o myimage.tar $CID
$ docker rm $CID
Build a new Dockerfile FROM scratch that ADDs the exported contents tar file back.
FROM scratch
ADD myimage.tar /
Any extended meta data for Dockerfile, like ENTRYPOINT, CMD or VOLUMES, can be queried via inspect or history:
$ docker image inspect myimage -f '{{json .Config}}' | jq
{
"Hostname": "",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"ExposedPorts": {
"27017/tcp": {}
},
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"GOSU_VERSION=1.10",
"JSYAML_VERSION=3.10.0",
"GPG_KEYS=2930ADAE8CAF5059EE73BB4B58712A2291FA4AD5",
"MONGO_PACKAGE=mongodb-org",
"MONGO_REPO=repo.mongodb.org",
"MONGO_MAJOR=3.6",
"MONGO_VERSION=3.6.3"
],
"Cmd": [
"mongod"
],
"ArgsEscaped": true,
"Image": "sha256:bac19e2cfd49108534b108c101a68a2046090d25da581ae04dc020aac93b4e31",
"Volumes": {
"/data/configdb": {},
"/data/db": {}
},
"WorkingDir": "",
"Entrypoint": [
"docker-entrypoint.sh"
],
"OnBuild": [],
"Labels": null
}
or
docker image history myimage --no-trunc

How to set Architecture for docker build to arm64?

I have a Dockerfile that I run on amd64 but want to run on arm64. Since go build tool takes GOARCH=arm64 as argument I don't need any other cross compilation tool to make the binary.
# Run the build
FROM mojlighetsministeriet/go-polymer-faster-build
ENV WORKDIR /go/src/github.com/mojlighetsministeriet/email
COPY . $WORKDIR
WORKDIR $WORKDIR
RUN go get -t -v ./...
RUN CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build
# Create the final docker image
FROM scratch
COPY --from=0 /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
COPY --from=0 /go/src/github.com/mojlighetsministeriet/email/email /
ENTRYPOINT ["/email"]
The problem is that the resulting image gets marked with the wrong Architecture amd64 instead of arm64. How can I pass an argument to docker build so that it sets Architecture to arm64?
$ docker image inspect mojlighetsministeriet/email-arm64
[
{
"Id": "sha256:33bcd7da8631c7a0829d61cc53479a26ab7f31fab1cb039105de415ddc6178f3",
"RepoTags": [
"mojlighetsministeriet/email-arm64:latest"
],
"RepoDigests": [
"mojlighetsministeriet/email-arm64#sha256:ab3f05d5597c3a304953b5c14f795179aa75bdfd458af3dc3cfb8b8d8eb87bc3"
],
"Parent": "sha256:e5888262d93ea0946b5fd8146cf1c19ec3a7bac4d36eb51848ef0aefa75cf8e7",
"Comment": "",
"Created": "2017-12-05T18:36:36.273648787Z",
"Container": "7a226edb3b52aaeeefec9e0fb4dd1da50d84992fb6cc374aeda9d82eec1bb2c8",
"ContainerConfig": {
"Hostname": "7a226edb3b52",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
],
"Cmd": [
"/bin/sh",
"-c",
"#(nop) ",
"ENTRYPOINT [\"/email\"]"
],
"Image": "sha256:e5888262d93ea0946b5fd8146cf1c19ec3a7bac4d36eb51848ef0aefa75cf8e7",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": [
"/email"
],
"OnBuild": null,
"Labels": {}
},
"DockerVersion": "17.10.0-ce",
"Author": "",
"Config": {
"Hostname": "",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
],
"Cmd": null,
"Image": "sha256:e5888262d93ea0946b5fd8146cf1c19ec3a7bac4d36eb51848ef0aefa75cf8e7",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": [
"/email"
],
"OnBuild": null,
"Labels": null
},
"Architecture": "amd64",
"Os": "linux",
"Size": 7861466,
"VirtualSize": 7861466,
"GraphDriver": {
"Data": {
"LowerDir": "/var/lib/docker/overlay2/03cb0162bf922636e4e0ec90123b81565a447c6cd511741103551d2f0e7f09f9/diff",
"MergedDir": "/var/lib/docker/overlay2/091f74815a0caf457df7e57ade43b41c4dd8551388beca44815a7038501742ee/merged",
"UpperDir": "/var/lib/docker/overlay2/091f74815a0caf457df7e57ade43b41c4dd8551388beca44815a7038501742ee/diff",
"WorkDir": "/var/lib/docker/overlay2/091f74815a0caf457df7e57ade43b41c4dd8551388beca44815a7038501742ee/work"
},
"Name": "overlay2"
},
"RootFS": {
"Type": "layers",
"Layers": [
"sha256:66f615d03920919b0fa8bc9fed45515bb95636be1837fdd10a82b2c183e2ad5b",
"sha256:bd6a01b885eb6e3eec38a3fe3a2899646509633730b210cf6987456fd40b8a1c"
]
},
"Metadata": {
"LastTagTime": "2017-12-14T10:24:10.796813522+01:00"
}
}
]
https://docs.docker.com/desktop/multi-arch/
# Shows builders installed
docker buildx ls
# Use builder that supports platform
docker buildx use default
docker buildx build --platform linux/arm64 -t <image_name>:<image_tag> --push .
For building single docker images: Set your environment variable using the command line or modifying your .bashrc or .zshenv file. (introduced in v19.03.0 in 03/2019)
export DOCKER_DEFAULT_PLATFORM=linux/arm64
Alternatively, in the Dockerfile, include the following flag in the FROM command (for a multi-stage Dockerfile build, the flag is only needed for the first stage):
FROM --platform=linux/arm64 python:3.7-alpine
For building images as part of a docker-compose build, include the platform: linux/arm64 for each service. For example:
services:
frontend:
platform: linux/arm64
build: frontend
ports:
- 80:80
depends_on:
- backend
backend:
platform: linux/arm64
build: backend
This also works the other way around, for instance if you have an Apple M1 Chip and want to deploy images to a Linux or Windows based AMD64 environment. Simply swap 'linux/arm64' for 'linux/amd64'
I was able to solve the problem, not exactly as I wanted, but close enough.
Have an amd64 Linux machine with docker
Setup qemu user static for arm support https://hub.docker.com/r/multiarch/qemu-user-static/
In your docker file use base image with support for arm. E.g. ubuntu
Build your image with command similar to the following:
docker build --platform arm --pull -t your_tag .
This command will force docker to pull arm version of the base image and will also set arm architecture to your result image. But be careful, if you use tags with multiple architectures, the command above will set the tag to arm version. So to run the native amd64 version you will need to pull the tag again without --platform arg.

Get list of docker tags available to pull from command line?

I want to get some basic information about the published versions/tags of a docker image, to know what image:tag's I can pull. I would also like to see the time that each tag was most recently published.
Is there a way to do this on the command line?
Docker version 1.10.2, build c3959b1
Basically looking for the equivalent of npm info {pkg} for a docker image.
Not from the command line. You have docker search but it only returns a subset of the data you want, and only for the image with the :latest tag:
> docker search sixeyed/hadoop-dotnet
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
sixeyed/hadoop-dotnet Hadoop with .NET Core installed 1 [OK]
If you want more detail, you'll need to use the registry API, but it only has a catalog endpoint for listing repositories, the issue for search is still open.
Assuming you know the repository name, you can navigate the API - first you need an auth token:
> curl "https://auth.docker.io/token?service=registry.docker.io&scope=repository:sixeyed/hadoop-dotnet:pull"
{"token":"eyJhbG...
Then you pass the token to subsequent requests, e.g. to list the tags:
> curl --header "Authorization: Bearer eyJh..." https://index.docker.io/v2/sixeyed/hadoop-dotnet/tags/list
{"name":"sixeyed/hadoop-dotnet","tags":["2.7.2","latest"]}
And then get all the information about one image by its repository name and tag:
> curl --header "Authorization: Bearer eyJh..." https://index.docker.io/v2/sixeyed/hadoop-dotnet/manifests/latest
I want to get some basic information about the published versions/tags of a docker image, to know what image:tag's I can pull.
This is from the tags/list API. Here's a small script that does it:
#!/bin/sh
ref="${1:-library/ubuntu:latest}"
repo="${ref%:*}"
tag="${ref##*:}"
token=$(curl -s "https://auth.docker.io/token?service=registry.docker.io&scope=repository:${repo}:pull" \
| jq -r '.token')
curl -H "Authorization: Bearer $token" \
-s "https://registry-1.docker.io/v2/${repo}/tags/list" | jq .
Running that looks like:
$ ./tags-v2.sh library/ubuntu:latest
{
"name": "library/ubuntu",
"tags": [
"10.04",
"12.04",
"12.04.5",
"12.10",
"13.04",
"13.10",
"14.04",
"14.04.1",
...
I would also like to see the time that each tag was most recently published.
You likely want to pull the image config for this. First you need to pull the manifest, parse the config descriptor, then pull that blob for the config json. Here's a script that will do that for docker images:
#!/bin/sh
ref="${1:-library/ubuntu:latest}"
repo="${ref%:*}"
tag="${ref##*:}"
token=$(curl -s "https://auth.docker.io/token?service=registry.docker.io&scope=repository:${repo}:pull" \
| jq -r '.token')
digest=$(curl -H "Accept: application/vnd.docker.distribution.manifest.v2+json" \
-H "Authorization: Bearer $token" \
-s "https://registry-1.docker.io/v2/${repo}/manifests/${tag}" \
| jq -r .config.digest)
curl -H "Accept: application/vnd.docker.container.image.v1+json" \
-H "Authorization: Bearer $token" \
-s -L "https://registry-1.docker.io/v2/${repo}/blobs/${digest}" | jq .
And an example of running that script:
$ ./get-config-v2.sh library/ubuntu:latest
{
"architecture": "amd64",
"config": {
"Hostname": "",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
],
"Cmd": [
"bash"
],
"Image": "sha256:6c18a628d47eacf574eb93da2324293a0e6c845084cca2ea13efaa3cee4d0799",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": null,
"OnBuild": null,
"Labels": null
},
"container": "249e88be79ad9986a479c71c15a056946ae26b0c54c1f634f115be6d5f9ba1c8",
"container_config": {
"Hostname": "249e88be79ad",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
],
"Cmd": [
"/bin/sh",
"-c",
"#(nop) ",
"CMD [\"bash\"]"
],
"Image": "sha256:6c18a628d47eacf574eb93da2324293a0e6c845084cca2ea13efaa3cee4d0799",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": null,
"OnBuild": null,
"Labels": {}
},
"created": "2021-10-16T00:37:47.578710012Z",
"docker_version": "20.10.7",
"history": [
{
"created": "2021-10-16T00:37:47.226745473Z",
"created_by": "/bin/sh -c #(nop) ADD file:5d68d27cc15a80653c93d3a0b262a28112d47a46326ff5fc2dfbf7fa3b9a0ce8 in / "
},
{
"created": "2021-10-16T00:37:47.578710012Z",
"created_by": "/bin/sh -c #(nop) CMD [\"bash\"]",
"empty_layer": true
}
],
"os": "linux",
"rootfs": {
"type": "layers",
"diff_ids": [
"sha256:9f54eef412758095c8079ac465d494a2872e02e90bf1fb5f12a1641c0d1bb78b"
]
}
}
Note that this will only give you details from the build tooling, the registry itself does not track when an image was uploaded to that registry from any OCI APIs (some registries may add their own metadata, but this would be registry specific).
To handle more authentication types, different kinds of images (OCI vs Docker), and similar, I've packaged these commands and more into regctl in the regclient project. Similar projects exist from Google's container registry crane command, and RedHat's skopeo, each giving access to registries from the command line:
$ regctl tag ls ubuntu
10.04
12.04
12.04.5
12.10
13.04
13.10
14.04
14.04.1
14.04.2
14.04.3
14.04.4
14.04.5
...
$ regctl image config ubuntu
{
"created": "2021-10-16T00:37:47.578710012Z",
"architecture": "amd64",
"os": "linux",
"config": {
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
],
"Cmd": [
"bash"
]
},
"rootfs": {
"type": "layers",
"diff_ids": [
"sha256:9f54eef412758095c8079ac465d494a2872e02e90bf1fb5f12a1641c0d1bb78b"
]
},
"history": [
{
"created": "2021-10-16T00:37:47.226745473Z",
"created_by": "/bin/sh -c #(nop) ADD file:5d68d27cc15a80653c93d3a0b262a28112d47a46326ff5fc2dfbf7fa3b9a0ce8 in / "
},
{
"created": "2021-10-16T00:37:47.578710012Z",
"created_by": "/bin/sh -c #(nop) CMD [\"bash\"]",
"empty_layer": true
}
]
}
If you don't want to use a token:
curl -L -s 'https://registry.hub.docker.com/v2/repositories/<$repo_name>/tags?page=<$page_nb>&page_size=<$page_size>' | jq '."results"[]["name"]'
Where you can get <$repo_name> with:
docker search <$expression>
For example:
$ docker search piwigo
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
linuxserver/piwigo A Piwigo container, brought to you by LinuxS… 152
mathieuruellan/piwigo Easy deployment of piwigo for my personal us… 19 [OK]
lsioarmhf/piwigo 3
hg8496/piwigo 2 [OK]
[…]
$ curl -L -s 'https://registry.hub.docker.com/v2/repositories/linuxserver/piwigo/tags?page=1&page_size=10' | jq '."results"[]["name"]'
"latest"
"12.0.0"
"version-12.0.0"
"12.0.0-ls137"
"arm64v8-12.0.0"
"arm32v7-12.0.0"
"amd64-12.0.0"
"arm64v8-version-12.0.0"
"arm32v7-version-12.0.0"
"amd64-version-12.0.0"
Source: Démarrer avec les containers Docker

Resources