Create a secure private registry (2.0)
docker run -d \ -p 5000:5000 \ -p 443:443 \ --restart=always \ --name registry \ -v /var/lib/docker/volumes/my-registry/_data:/var/lib/registry \ -v /srv/registry:/srv/registry \ -e REGISTRY_HTTP_TLS_CERTIFICATE=/srv/registry/domain.crt \ -e REGISTRY_HTTP_TLS_KEY=/srv/registry/domain.key \ -e REGISTRY_HTTP_ADDR=0.0.0.0:443 \ registry:2 |
Some notes :
– 5000 and 443 are the http port (no sure and secure port) of the registry. Exposing both is maybe not required.
– by default, registry uses a volume for data (/var/lib/registry). To improve performance we use a bind mount on that.
– we overwrite the /srv/registry content of the container with a bind mount (not for performance but for convenience).
Create a private registry with a user/pass access
Two steps :
– first : create the user/password and store it into the docker host
docker run \ --rm \ --entrypoint htpasswd \ registry:2.7 -Bbn fooUser fooPass > /foo/auth/htpasswd |
– second : create a new registry instance by setting the user/password info. Here only the docker args to pass related to that :
Set the registry password when the register is created : -v /foo/auth:/auth \ -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \ -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \ |
Registry login
docker login -u username -p passwordNotBase64Encoded urlOfTheRegistry
If the login is successful, as a result we could access to that docker registry (docker pull, FROM …).
Besides, the user-password will be stored as basic authent (base64 encoded with : separator) and stored in $HOME/.docker/config.json
or /root/.docker/config.json
(if the user is root).
That file looks like :
{ "auths": { "myregistry.org": { "auth": "base64userAndPass...." } }, "HttpHeaders": { "User-Agent": "Docker-Client/18.20.1-ce (linux)" } }[ |
Tag and push a custom local image in a private registry
Here the registry is stored on a machine with the hostname : david-virtual-machine
and the port 443 of the docker registry container is mapped on the 444 port of the host.
At last the custom local image is maven-with-nexus-configured:maven-3.6-java-11
.
docker tag maven-with-nexus-configured:maven-3.6-java-11 david-virtual-machine:444/maven-with-nexus-configured:maven-3.6-java-11 docker push david-virtual-machine:444/maven-with-nexus-configured:maven-3.6-java-11 |
Deploy an unsecure registry
On the host of the registry and on every docker hosts that use the registry, add the registry configuration : /etc/docker/daemon.json :
{ "insecure-registries" : ["myregistrydomain.com:5000"] }
Use a self-signed certificates
This is more secure than the insecure registry solution (above).
Steps :
1) Generate a client certificate (domain.crt) and a private key (domain.key) on the registry host :
openssl req \ -newkey rsa:4096 -nodes -sha256 -keyout domain.key \ -x509 -days 5000 -subj "/C=FR/ST=Foo/L=Foo/O=Foo/CN=myregistrydomain.com" -out domain.crt |
2) On the registry host :
* copy domain.crt
in /etc/docker/certs.d/myregistrydomain.com:443
folder.
Warn : don’t copy domain.key. Helpless because that is passed as the registry is launched.
* the first time : start the docker registry by passing the domain.key and domain.crt as TLS_CERTIF.. AND TLS…
such as :
-e REGISTRY_HTTP_TLS_CERTIFICATE=...foo/registry/domain.crt \
-e REGISTRY_HTTP_TLS_KEY=foo./registry/domain.key
If the certificates have changed: stop and remove the registry container and start a new instance of that.
3) Instruct each docker daemon of docker clients to use that certificate.
* copy and domain.crt in /etc/docker/certs.d/myregistrydomain.com:443 folder
* restart docker daemon
Beware : don’t copy domain.key on docker clients because it may provoke errors.
To view the certificate content:
openssl x509 -in domain.cert -noout -text
Registry WS API
To access to a secure registry :
curl -kv https://localhost:443/v2/_catalog
List repositories
List all images but don’t specify all tags for them:
GET /_catalog
List image tags
List all tags of a image.
GET /<imageName>/tags/list
Get manifest by a reference (tag or digest)
reference parameter : tag or digest.
It returns metadata such as history, image date creation, layers, and so for…
GET /<imageName>/manifests/<reference>
Retrieve the blob from the registry identified by name and digest (not working):
GET /<name>/blobs/<digest>
Delete a layer (image blob) identified by image name and digest
Digest parameter : the layer digest are listed by Get manifest for an image+tag(see above).
DELETE /v2/<imageName>/blobs/<digest>
ex: delete layer
curl -kv -X DELETE "https://localhost:443/v2/foo/image/blobs/sha256:000000000000e000e0e000"
Delete an image identified by image name and a « special » digest:
First:
Retrieve the digest of the image foo/bar
with the version latest
:
curl -v -k -X HEAD -H "Accept: application/vnd.docker.distribution.manifest.v2+json" "https://localhost:443/v2/foo/bar/manifests/latest"
Output :
< HTTP/1.1 200 OK < Content-Length: 12735 < Content-Type: application/vnd.docker.distribution.manifest.v1+prettyjws < Docker-Content-Digest: sha256:34e8501z4c11c3z7ec873f9a9b5546ce4bc83e7413fzb0z9e68z8333a739z041 < Docker-Distribution-Api-Version: registry/2.0 < Etag: "sha256:34e8501z4c11c3z7ec873f9a9b5546ce4bc83e7413fzb0z9e68z8333a739z041" < X-Content-Type-Options: nosniff < Date: Tue, 06 Aug 2020 14:06:43 GMT |
Digest is : sha256:34e8501z4c11c3z7ec873f9a9b5546ce4bc83e7413fzb0z9e68z8333a739z041
Then we could delete the image
The version of the tag is helpless here. What we need is the image name and the digest (retrieved above) :
curl -v -k -X DELETE "http://localhost:443/v2/foo/bar/manifests/sha256:34e8501z4c11c3z7ec873f9a9b5546ce4bc83e7413fzb0z9e68z8333a739z041"
Output :
< HTTP/1.1 202 Accepted < Docker-Distribution-Api-Version: registry/2.0 < X-Content-Type-Options: nosniff < Date: Tue, 06 Aug 2020 14:07:32 GMT < Content-Length: 0 |
Deleting an image may return a 405 (method not allowed) :
{"errors":[{"code":"UNSUPPORTED","message":"The operation is unsupported."}]}
To fix that we need that our registry allows deleting operation.
The storage
element of the registry.yml
must value the delete.enabled
property with true
:
storage: cache: blobdescriptor: inmemory filesystem: rootdirectory: /var/lib/registry delete: enabled: true |
Common issues
Problem : docker pull foo-image
loops on one or multiple layers and terminates with unexpected EOF such as :
7a9d5b1b344: Retrying in XX seconds 7a9d5b1b322: Retrying in XX seconds 7a9d5b1ba11: Retrying in XX seconds 7a9d5b1ba55: Download complete unexpected EOF |
Solution :