docs: add hugo site

Signed-off-by: David Karlsson <35727626+dvdksn@users.noreply.github.com>
This commit is contained in:
David Karlsson 2023-10-11 11:59:20 +02:00
parent f7b3869062
commit e2ae76f1f2
291 changed files with 2833 additions and 2 deletions

63
docs/content/_index.md Normal file
View file

@ -0,0 +1,63 @@
---
description: High-level overview of the Registry
keywords: registry, on-prem, images, tags, repository, distribution
redirect_from:
- /registry/overview/
title: Docker Registry
---
## What it is
The Registry is a stateless, highly scalable server side application that stores
and lets you distribute Docker images. The Registry is open-source, under the
permissive [Apache license](https://en.wikipedia.org/wiki/Apache_License).
## Why use it
You should use the Registry if you want to:
* tightly control where your images are being stored
* fully own your images distribution pipeline
* integrate image storage and distribution tightly into your in-house development workflow
## Alternatives
Users looking for a zero maintenance, ready-to-go solution are encouraged to
head-over to the [Docker Hub](https://hub.docker.com), which provides a
free-to-use, hosted Registry, plus additional features (organization accounts,
automated builds, and more).
## Requirements
The Registry is compatible with Docker engine **version 1.6.0 or higher**.
## Basic commands
Start your registry
docker run -d -p 5000:5000 --name registry registry:2
Pull (or build) some image from the hub
docker pull ubuntu
Tag the image so that it points to your registry
docker image tag ubuntu localhost:5000/myfirstimage
Push it
docker push localhost:5000/myfirstimage
Pull it back
docker pull localhost:5000/myfirstimage
Now stop your registry and remove all data
docker container stop registry && docker container rm -v registry
## Next
You should now read the [detailed introduction about the registry](./about/_index.md),
or jump directly to [deployment instructions](./about/deploying.md).

View file

@ -0,0 +1,73 @@
---
description: Explains what the Registry is, basic use cases and requirements
keywords: registry, on-prem, images, tags, repository, distribution, use cases, requirements
title: About Registry
---
A registry is a storage and content delivery system, holding named Docker
images, available in different tagged versions.
> Example: the image `distribution/registry`, with tags `2.0` and `2.1`.
Users interact with a registry by using docker push and pull commands.
> Example: `docker pull registry-1.docker.io/distribution/registry:2.1`.
Storage itself is delegated to drivers. The default storage driver is the local
posix filesystem, which is suitable for development or small deployments.
Additional cloud-based storage drivers like S3, Microsoft Azure and Google Cloud Storage
are supported. People looking into using other storage drivers should consider if
the driver they'd like to be supported is S3 compatible like many cloud storage systems
as adding new storage driver support has been put on hold for the time being.
Since securing access to your hosted images is paramount, the Registry natively
supports TLS and basic authentication.
The Registry GitHub repository includes additional information about advanced
authentication and authorization methods. Only very large or public deployments
are expected to extend the Registry in this way.
Finally, the Registry ships with a robust [notification system](notifications.md),
calling webhooks in response to activity, and both extensive logging and reporting,
mostly useful for large installations that want to collect metrics.
## Understanding image naming
Image names as used in typical docker commands reflect their origin:
* `docker pull ubuntu` instructs docker to pull an image named `ubuntu` from the official Docker Hub. This is simply a shortcut for the longer `docker pull docker.io/library/ubuntu` command
* `docker pull myregistrydomain:port/foo/bar` instructs docker to contact the registry located at `myregistrydomain:port` to find the image `foo/bar`
You can find out more about the various Docker commands dealing with images in
the [official Docker engine documentation](../engine/reference/commandline/cli.md).
## Use cases
Running your own Registry is a great solution to integrate with and complement
your CI/CD system. In a typical workflow, a commit to your source revision
control system would trigger a build on your CI system, which would then push a
new image to your Registry if the build is successful. A notification from the
Registry would then trigger a deployment on a staging environment, or notify
other systems that a new image is available.
It's also an essential component if you want to quickly deploy a new image over
a large cluster of machines.
Finally, it's the best way to distribute images inside an isolated network.
## Requirements
You absolutely need to be familiar with Docker, specifically with regard to
pushing and pulling images. You must understand the difference between the
daemon and the cli, and at least grasp basic concepts about networking.
Also, while just starting a registry is fairly easy, operating it in a
production environment requires operational skills, just like any other service.
You are expected to be familiar with systems availability and scalability,
logging and log processing, systems monitoring, and security 101. Strong
understanding of http and overall network communications, plus familiarity with
golang are certainly useful as well for advanced operations or hacking.
## Next
Dive into [deploying your registry](deploying.md)

View file

@ -0,0 +1,52 @@
---
published: false
---
# Architecture
## Design
**TODO(stevvooe):** Discuss the architecture of the registry, internally and externally, in a few different deployment scenarios.
### Eventual Consistency
> **NOTE:** This section belongs somewhere, perhaps in a design document. We
> are leaving this here so the information is not lost.
Running the registry on eventually consistent backends has been part of the
design from the beginning. This section covers some of the approaches to
dealing with this reality.
There are a few classes of issues that we need to worry about when
implementing something on top of the storage drivers:
1. Read-After-Write consistency (see this [article on
s3](http://shlomoswidler.com/2009/12/read-after-write-consistency-in-amazon.html)).
2. [Write-Write Conflicts](http://en.wikipedia.org/wiki/Write%E2%80%93write_conflict).
In reality, the registry must worry about these kinds of errors when doing the
following:
1. Accepting data into a temporary upload file may not have latest data block
yet (read-after-write).
2. Moving uploaded data into its blob location (write-write race).
3. Modifying the "current" manifest for given tag (write-write race).
4. A whole slew of operations around deletes (read-after-write, delete-write
races, garbage collection, etc.).
The backend path layout employs a few techniques to avoid these problems:
1. Large writes are done to private upload directories. This alleviates most
of the corruption potential under multiple writers by avoiding multiple
writers.
2. Constraints in storage driver implementations, such as support for writing
after the end of a file to extend it.
3. Digest verification to avoid data corruption.
4. Manifest files are stored by digest and cannot change.
5. All other non-content files (links, hashes, etc.) are written as an atomic
unit. Anything that requires additions and deletions is broken out into
separate "files". Last writer still wins.
Unfortunately, one must play this game when trying to build something like
this on top of eventually consistent storage systems. If we run into serious
problems, we can wrap the storagedrivers in a shared consistency layer but
that would increase complexity and hinder registry cluster performance.

View file

@ -0,0 +1,78 @@
---
description: describes get by digest pitfall
keywords: registry, manifest, images, tags, repository, distribution, digest
title: Registry compatibility
---
## Synopsis
If a manifest is pulled by _digest_ from a registry 2.3 with Docker Engine 1.9
and older, and the manifest was pushed with Docker Engine 1.10, a security check
causes the Engine to receive a manifest it cannot use and the pull fails.
## Registry manifest support
Historically, the registry has supported a [single manifest type](./spec/manifest-v2-1.md)
known as _Schema 1_.
With the move toward multiple architecture images, the distribution project
introduced two new manifest types: Schema 2 manifests and manifest lists. Registry
2.3 supports all three manifest types and sometimes performs an on-the-fly
transformation of a manifest before serving the JSON in the response, to
preserve compatibility with older versions of Docker Engine.
This conversion has some implications for pulling manifests by digest and this
document enumerates these implications.
## Content Addressable Storage (CAS)
Manifests are stored and retrieved in the registry by keying off a digest
representing a hash of the contents. One of the advantages provided by CAS is
security: if the contents are changed, then the digest no longer matches.
This prevents any modification of the manifest by a MITM attack or an untrusted
third party.
When a manifest is stored by the registry, this digest is returned in the HTTP
response headers and, if events are configured, delivered within the event. The
manifest can either be retrieved by the tag, or this digest.
For registry versions 2.2.1 and below, the registry always stores and
serves _Schema 1_ manifests. Engine 1.10 first
attempts to send a _Schema 2_ manifest, falling back to sending a
Schema 1 type manifest when it detects that the registry does not
support the new version.
## Registry v2.3
### Manifest push with Docker 1.10
The Engine constructs a _Schema 2_ manifest which the
registry persists to disk.
When the manifest is pulled by digest or tag with Docker Engine 1.10, a
_Schema 2_ manifest is returned. Docker Engine 1.10
understands the new manifest format.
When the manifest is pulled by *tag* with Docker Engine 1.9 and older, the
manifest is converted on-the-fly to _Schema 1_ and sent in the
response. The Docker Engine 1.9 is compatible with this older format.
When the manifest is pulled by _digest_ with Docker Engine 1.9 and older, the
same rewriting process does not happen in the registry. If it did,
the digest would no longer match the hash of the manifest and would violate the
constraints of CAS.
For this reason if a manifest is pulled by _digest_ from a registry 2.3 with Docker
Engine 1.9 and older, and the manifest was pushed with Docker Engine 1.10, a
security check causes the Engine to receive a manifest it cannot use and the
pull fails.
### Manifest push with Docker 1.9 and older
The Docker Engine constructs a _Schema 1_ manifest which the
registry persists to disk.
When the manifest is pulled by digest or tag with any Docker version, a
_Schema 1_ manifest is returned.

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,577 @@
---
description: Explains how to deploy a registry
keywords: registry, on-prem, images, tags, repository, distribution, deployment
title: Deploy a registry server
---
Before you can deploy a registry, you need to install Docker on the host.
A registry is an instance of the `registry` image, and runs within Docker.
This topic provides basic information about deploying and configuring a
registry. For an exhaustive list of configuration options, see the
[configuration reference](configuration.md).
If you have an air-gapped datacenter, see
[Considerations for air-gapped registries](#considerations-for-air-gapped-registries).
## Run a local registry
Use a command like the following to start the registry container:
```console
$ docker run -d -p 5000:5000 --restart=always --name registry registry:2
```
The registry is now ready to use.
> **Warning**: These first few examples show registry configurations that are
> only appropriate for testing. A production-ready registry must be protected by
> TLS and should ideally use an access-control mechanism. Keep reading and then
> continue to the [configuration guide](configuration.md) to deploy a
> production-ready registry.
## Copy an image from Docker Hub to your registry
You can pull an image from Docker Hub and push it to your registry. The
following example pulls the `ubuntu:16.04` image from Docker Hub and re-tags it
as `my-ubuntu`, then pushes it to the local registry. Finally, the
`ubuntu:16.04` and `my-ubuntu` images are deleted locally and the
`my-ubuntu` image is pulled from the local registry.
1. Pull the `ubuntu:16.04` image from Docker Hub.
```console
$ docker pull ubuntu:16.04
```
2. Tag the image as `localhost:5000/my-ubuntu`. This creates an additional tag
for the existing image. When the first part of the tag is a hostname and
port, Docker interprets this as the location of a registry, when pushing.
```console
$ docker tag ubuntu:16.04 localhost:5000/my-ubuntu
```
3. Push the image to the local registry running at `localhost:5000`:
```console
$ docker push localhost:5000/my-ubuntu
```
4. Remove the locally-cached `ubuntu:16.04` and `localhost:5000/my-ubuntu`
images, so that you can test pulling the image from your registry. This
does not remove the `localhost:5000/my-ubuntu` image from your registry.
```console
$ docker image remove ubuntu:16.04
$ docker image remove localhost:5000/my-ubuntu
```
5. Pull the `localhost:5000/my-ubuntu` image from your local registry.
```console
$ docker pull localhost:5000/my-ubuntu
```
## Stop a local registry
To stop the registry, use the same `docker container stop` command as with any other
container.
```console
$ docker container stop registry
```
To remove the container, use `docker container rm`.
```console
$ docker container stop registry && docker container rm -v registry
```
## Basic configuration
To configure the container, you can pass additional or modified options to the
`docker run` command.
The following sections provide basic guidelines for configuring your registry.
For more details, see the [registry configuration reference](configuration.md).
### Start the registry automatically
If you want to use the registry as part of your permanent infrastructure, you
should set it to restart automatically when Docker restarts or if it exits.
This example uses the `--restart always` flag to set a restart policy for the
registry.
```console
$ docker run -d \
-p 5000:5000 \
--restart=always \
--name registry \
registry:2
```
### Customize the published port
If you are already using port 5000, or you want to run multiple local
registries to separate areas of concern, you can customize the registry's
port settings. This example runs the registry on port 5001 and also names it
`registry-test`. Remember, the first part of the `-p` value is the host port
and the second part is the port within the container. Within the container, the
registry listens on port `5000` by default.
```console
$ docker run -d \
-p 5001:5000 \
--name registry-test \
registry:2
```
If you want to change the port the registry listens on within the container, you
can use the environment variable `REGISTRY_HTTP_ADDR` to change it. This command
causes the registry to listen on port 5001 within the container:
```console
$ docker run -d \
-e REGISTRY_HTTP_ADDR=0.0.0.0:5001 \
-p 5001:5001 \
--name registry-test \
registry:2
```
## Storage customization
### Customize the storage location
By default, your registry data is persisted as a [docker volume](../storage/volumes.md)
on the host filesystem. If you want to store your registry contents at a specific
location on your host filesystem, such as if you have an SSD or SAN mounted into
a particular directory, you might decide to use a bind mount instead. A bind mount
is more dependent on the filesystem layout of the Docker host, but more performant
in many situations. The following example bind-mounts the host directory
`/mnt/registry` into the registry container at `/var/lib/registry/`.
```console
$ docker run -d \
-p 5000:5000 \
--restart=always \
--name registry \
-v /mnt/registry:/var/lib/registry \
registry:2
```
### Customize the storage back-end
By default, the registry stores its data on the local filesystem, whether you
use a bind mount or a volume. You can store the registry data in an Amazon S3
bucket, Google Cloud Platform, or on another storage back-end by using
[storage drivers](./storage-drivers/index.md). For more information, see
[storage configuration options](./configuration.md#storage).
## Run an externally-accessible registry
Running a registry only accessible on `localhost` has limited usefulness. In
order to make your registry accessible to external hosts, you must first secure
it using TLS.
This example is extended in [Run the registry as a
service](#run-the-registry-as-a-service) below.
### Get a certificate
These examples assume the following:
- Your registry URL is `https://myregistry.domain.com/`.
- Your DNS, routing, and firewall settings allow access to the registry's host
on port 443.
- You have already obtained a certificate from a certificate authority (CA).
If you have been issued an _intermediate_ certificate instead, see
[use an intermediate certificate](#use-an-intermediate-certificate).
1. Create a `certs` directory.
```console
$ mkdir -p certs
```
Copy the `.crt` and `.key` files from the CA into the `certs` directory.
The following steps assume that the files are named `domain.crt` and
`domain.key`.
2. Stop the registry if it is currently running.
```console
$ docker container stop registry
```
3. Restart the registry, directing it to use the TLS certificate. This command
bind-mounts the `certs/` directory into the container at `/certs/`, and sets
environment variables that tell the container where to find the `domain.crt`
and `domain.key` file. The registry runs on port 443, the default HTTPS port.
```console
$ docker run -d \
--restart=always \
--name registry \
-v "$(pwd)"/certs:/certs \
-e REGISTRY_HTTP_ADDR=0.0.0.0:443 \
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \
-e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \
-p 443:443 \
registry:2
```
4. Docker clients can now pull from and push to your registry using its
external address. The following commands demonstrate this:
```console
$ docker pull ubuntu:16.04
$ docker tag ubuntu:16.04 myregistry.domain.com/my-ubuntu
$ docker push myregistry.domain.com/my-ubuntu
$ docker pull myregistry.domain.com/my-ubuntu
```
#### Use an intermediate certificate
A certificate issuer may supply you with an *intermediate* certificate. In this
case, you must concatenate your certificate with the intermediate certificate to
form a *certificate bundle*. You can do this using the `cat` command:
```console
cat domain.crt intermediate-certificates.pem > certs/domain.crt
```
You can use the certificate bundle just as you use the `domain.crt` file in
the previous example.
### Support for Let's Encrypt
The registry supports using Let's Encrypt to automatically obtain a
browser-trusted certificate. For more information on Let's Encrypt, see
[https://letsencrypt.org/how-it-works/](https://letsencrypt.org/how-it-works/)
and the relevant section of the
[registry configuration](configuration.md#letsencrypt).
### Use an insecure registry (testing only)
It is possible to use a self-signed certificate, or to use our registry
insecurely. Unless you have set up verification for your self-signed
certificate, this is for testing only. See [run an insecure registry](insecure.md).
## Run the registry as a service
[Swarm services](../engine/swarm/services.md) provide several advantages over
standalone containers. They use a declarative model, which means that you define
the desired state and Docker works to keep your service in that state. Services
provide automatic load balancing scaling, and the ability to control the
distribution of your service, among other advantages. Services also allow you to
store sensitive data such as TLS certificates in
[secrets](../engine/swarm/secrets.md).
The storage back-end you use determines whether you use a fully scaled service
or a service with either only a single node or a node constraint.
- If you use a distributed storage driver, such as Amazon S3, you can use a
fully replicated service. Each worker can write to the storage back-end
without causing write conflicts.
- If you use a local bind mount or volume, each worker node writes to its
own storage location, which means that each registry contains a different
data set. You can solve this problem by using a single-replica service and a
node constraint to ensure that only a single worker is writing to the bind
mount.
The following example starts a registry as a single-replica service, which is
accessible on any swarm node on port 80. It assumes you are using the same
TLS certificates as in the previous examples.
First, save the TLS certificate and key as secrets:
```console
$ docker secret create domain.crt certs/domain.crt
$ docker secret create domain.key certs/domain.key
```
Next, add a label to the node where you want to run the registry.
To get the node's name, use `docker node ls`. Substitute your node's name for
`node1` below.
```console
$ docker node update --label-add registry=true node1
```
Next, create the service, granting it access to the two secrets and constraining
it to only run on nodes with the label `registry=true`. Besides the constraint,
you are also specifying that only a single replica should run at a time. The
example bind-mounts `/mnt/registry` on the swarm node to `/var/lib/registry/`
within the container. Bind mounts rely on the pre-existing source directory,
so be sure `/mnt/registry` exists on `node1`. You might need to create it before
running the following `docker service create` command.
By default, secrets are mounted into a service at `/run/secrets/<secret-name>`.
```console
$ docker service create \
--name registry \
--secret domain.crt \
--secret domain.key \
--constraint 'node.labels.registry==true' \
--mount type=bind,src=/mnt/registry,dst=/var/lib/registry \
-e REGISTRY_HTTP_ADDR=0.0.0.0:443 \
-e REGISTRY_HTTP_TLS_CERTIFICATE=/run/secrets/domain.crt \
-e REGISTRY_HTTP_TLS_KEY=/run/secrets/domain.key \
--publish published=443,target=443 \
--replicas 1 \
registry:2
```
You can access the service on port 443 of any swarm node. Docker sends the
requests to the node which is running the service.
## Load balancing considerations
One may want to use a load balancer to distribute load, terminate TLS or
provide high availability. While a full load balancing setup is outside the
scope of this document, there are a few considerations that can make the process
smoother.
The most important aspect is that a load balanced cluster of registries must
share the same resources. For the current version of the registry, this means
the following must be the same:
- Storage Driver
- HTTP Secret
- Redis Cache (if configured)
Differences in any of the above cause problems serving requests.
As an example, if you're using the filesystem driver, all registry instances
must have access to the same filesystem root, on
the same machine. For other drivers, such as S3 or Azure, they should be
accessing the same resource and share an identical configuration.
The _HTTP Secret_ coordinates uploads, so also must be the same across
instances. Configuring different redis instances works (at the time
of writing), but is not optimal if the instances are not shared, because
more requests are directed to the backend.
### Important/Required HTTP-Headers
Getting the headers correct is very important. For all responses to any
request under the "/v2/" url space, the `Docker-Distribution-API-Version`
header should be set to the value "registry/2.0", even for a 4xx response.
This header allows the docker engine to quickly resolve authentication realms
and fallback to version 1 registries, if necessary. Confirming this is setup
correctly can help avoid problems with fallback.
In the same train of thought, you must make sure you are properly sending the
`X-Forwarded-Proto`, `X-Forwarded-For`, and `Host` headers to their "client-side"
values. Failure to do so usually makes the registry issue redirects to internal
hostnames or downgrading from https to http.
A properly secured registry should return 401 when the "/v2/" endpoint is hit
without credentials. The response should include a `WWW-Authenticate`
challenge, providing guidance on how to authenticate, such as with basic auth
or a token service. If the load balancer has health checks, it is recommended
to configure it to consider a 401 response as healthy and any other as down.
This secures your registry by ensuring that configuration problems with
authentication don't accidentally expose an unprotected registry. If you're
using a less sophisticated load balancer, such as Amazon's Elastic Load
Balancer, that doesn't allow one to change the healthy response code, health
checks can be directed at "/", which always returns a `200 OK` response.
## Restricting access
Except for registries running on secure local networks, registries should always
implement access restrictions.
### Native basic auth
The simplest way to achieve access restriction is through basic authentication
(this is very similar to other web servers' basic authentication mechanism).
This example uses native basic authentication using `htpasswd` to store the
secrets.
> **Warning**:
> You **cannot** use authentication with authentication schemes that send
> credentials as clear text. You must
> [configure TLS first](deploying.md#run-an-externally-accessible-registry) for
> authentication to work.
{:.warning}
> **Warning**
> The official registry image **only** supports htpasswd credentials in
> bcrypt format, so if you omit the `-B` option when generating the credential
> using htpasswd, all authentication attempts will fail.
{:.warning}
1. Create a password file with one entry for the user `testuser`, with password
`testpassword`:
```console
$ mkdir auth
$ docker run \
--entrypoint htpasswd \
httpd:2 -Bbn testuser testpassword > auth/htpasswd
```
On Windows, make sure the output file is correctly encoded:
```powershell
docker run --rm --entrypoint htpasswd httpd:2 -Bbn testuser testpassword | Set-Content -Encoding ASCII auth/htpasswd
```
2. Stop the registry.
```console
$ docker container stop registry
```
3. Start the registry with basic authentication.
```console
$ docker run -d \
-p 5000:5000 \
--restart=always \
--name registry \
-v "$(pwd)"/auth:/auth \
-e "REGISTRY_AUTH=htpasswd" \
-e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
-e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
-v "$(pwd)"/certs:/certs \
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \
-e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \
registry:2
```
4. Try to pull an image from the registry, or push an image to the registry.
These commands fail.
5. Log in to the registry.
```console
$ docker login myregistrydomain.com:5000
```
Provide the username and password from the first step.
Test that you can now pull an image from the registry or push an image to
the registry.
> **X509 errors**: X509 errors usually indicate that you are attempting to use
> a self-signed certificate without configuring the Docker daemon correctly.
> See [run an insecure registry](insecure.md).
### More advanced authentication
You may want to leverage more advanced basic auth implementations by using a
proxy in front of the registry. See the [recipes list](recipes/index.md).
The registry also supports delegated authentication which redirects users to a
specific trusted token server. This approach is more complicated to set up, and
only makes sense if you need to fully configure ACLs and need more control over
the registry's integration into your global authorization and authentication
systems. Refer to the following [background information](spec/auth/token.md) and
[configuration information here](configuration.md#auth).
This approach requires you to implement your own authentication system or
leverage a third-party implementation.
## Deploy your registry using a Compose file
If your registry invocation is advanced, it may be easier to use a Docker
compose file to deploy it, rather than relying on a specific `docker run`
invocation. Use the following example `docker-compose.yml` as a template.
```yaml
registry:
restart: always
image: registry:2
ports:
- 5000:5000
environment:
REGISTRY_HTTP_TLS_CERTIFICATE: /certs/domain.crt
REGISTRY_HTTP_TLS_KEY: /certs/domain.key
REGISTRY_AUTH: htpasswd
REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd
REGISTRY_AUTH_HTPASSWD_REALM: Registry Realm
volumes:
- /path/data:/var/lib/registry
- /path/certs:/certs
- /path/auth:/auth
```
Replace `/path` with the directory which contains the `certs/` and `auth/`
directories.
{:.warning}
Start your registry by issuing the following command in the directory containing
the `docker-compose.yml` file:
```console
$ docker-compose up -d
```
## Considerations for air-gapped registries
You can run a registry in an environment with no internet connectivity.
However, if you rely on any images which are not local, you need to consider the
following:
- You may need to build your local registry's data volume on a connected
host where you can run `docker pull` to get any images which are available
remotely, and then migrate the registry's data volume to the air-gapped
network.
- Certain images, such as the official Microsoft Windows base images, are not
distributable. This means that when you push an image based on one of these
images to your private registry, the non-distributable layers are **not**
pushed, but are always fetched from their authorized location. This is fine
for internet-connected hosts, but not in an air-gapped set-up.
You can configure the Docker daemon to allow pushing non-distributable layers
to private registries.
**This is only useful in air-gapped set-ups in the presence of
non-distributable images, or in extremely bandwidth-limited situations.**
You are responsible for ensuring that you are in compliance with the terms of
use for non-distributable layers.
1. Edit the `daemon.json` file, which is located in `/etc/docker/` on Linux
hosts and `C:\ProgramData\docker\config\daemon.json` on Windows Server.
Assuming the file was previously empty, add the following contents:
```json
{
"allow-nondistributable-artifacts": ["myregistrydomain.com:5000"]
}
```
The value is an array of registry addresses, separated by commas.
Save and exit the file.
2. Restart Docker.
3. Restart the registry if it does not start automatically.
4. When you push images to the registries in the list, their
non-distributable layers are pushed to the registry.
> **Warning**: Non-distributable artifacts typically have restrictions on
> how and where they can be distributed and shared. Only use this feature
> to push artifacts to private registries and ensure that you are in
> compliance with any terms that cover redistributing non-distributable
> artifacts.
## Next steps
More specific and advanced information is available in the following sections:
- [Configuration reference](configuration.md)
- [Working with notifications](notifications.md)
- [Advanced "recipes"](recipes/index.md)
- [Registry API](spec/api.md)
- [Storage driver model](storage-drivers/index.md)
- [Token authentication](spec/auth/token.md)

View file

@ -0,0 +1,20 @@
---
description: describes deprecated functionality
keywords: registry, manifest, images, signatures, repository, distribution, digest
title: Docker Registry deprecation
---
This document details functionality or components which are deprecated within
the registry.
### v2.5.0
The signature store has been removed from the registry. Since `v2.4.0` it has
been possible to configure the registry to generate manifest signatures rather
than load them from storage. In this version of the registry this becomes
the default behavior. Signatures which are attached to manifests on put are
not stored in the registry. This does not alter the functional behavior of
the registry.
Old signatures blobs can be removed from the registry storage by running the
garbage-collect subcommand.

View file

@ -0,0 +1,124 @@
---
description: High level discussion of garbage collection
keywords: registry, garbage, images, tags, repository, distribution
title: Garbage collection
---
As of v2.4.0 a garbage collector command is included within the registry binary.
This document describes what this command does and how and why it should be used.
## About garbage collection
In the context of the Docker registry, garbage collection is the process of
removing blobs from the filesystem when they are no longer referenced by a
manifest. Blobs can include both layers and manifests.
Registry data can occupy considerable amounts of disk space. In addition,
garbage collection can be a security consideration, when it is desirable to ensure
that certain layers no longer exist on the filesystem.
## Garbage collection in practice
Filesystem layers are stored by their content address in the Registry. This
has many advantages, one of which is that data is stored once and referred to by manifests.
See [here](compatibility.md#content-addressable-storage-cas) for more details.
Layers are therefore shared amongst manifests; each manifest maintains a reference
to the layer. As long as a layer is referenced by one manifest, it cannot be garbage
collected.
Manifests and layers can be `deleted` with the registry API (refer to the API
documentation [here](spec/api.md#deleting-a-layer) and
[here](spec/api.md#deleting-an-image) for details). This API removes references
to the target and makes them eligible for garbage collection. It also makes them
unable to be read via the API.
If a layer is deleted, it is removed from the filesystem when garbage collection
is run. If a manifest is deleted the layers to which it refers are removed from
the filesystem if no other manifests refers to them.
### Example
In this example manifest A references two layers: `a` and `b`. Manifest `B` references
layers `a` and `c`. In this state, nothing is eligible for garbage collection:
```
A -----> a <----- B
\--> b |
c <--/
```
Manifest B is deleted via the API:
```
A -----> a B
\--> b
c
```
In this state layer `c` no longer has a reference and is eligible for garbage
collection. Layer `a` had one reference removed but not garbage
collected as it is still referenced by manifest `A`. The blob representing
manifest `B` is eligible for garbage collection.
After garbage collection has been run, manifest `A` and its blobs remain.
```
A -----> a
\--> b
```
### More details about garbage collection
Garbage collection runs in two phases. First, in the 'mark' phase, the process
scans all the manifests in the registry. From these manifests, it constructs a
set of content address digests. This set is the 'mark set' and denotes the set
of blobs to *not* delete. Secondly, in the 'sweep' phase, the process scans all
the blobs and if a blob's content address digest is not in the mark set, the
process deletes it.
> **Note**: You should ensure that the registry is in read-only mode or not running at
> all. If you were to upload an image while garbage collection is running, there is the
> risk that the image's layers are mistakenly deleted leading to a corrupted image.
This type of garbage collection is known as stop-the-world garbage collection.
## Run garbage collection
Garbage collection can be run as follows
`bin/registry garbage-collect [--dry-run] /path/to/config.yml`
The garbage-collect command accepts a `--dry-run` parameter, which prints the progress
of the mark and sweep phases without removing any data. Running with a log level of `info`
gives a clear indication of items eligible for deletion.
The config.yml file should be in the following format:
```yaml
version: 0.1
storage:
filesystem:
rootdirectory: /registry/data
```
_Sample output from a dry run garbage collection with registry log level set to `info`_
```
hello-world
hello-world: marking manifest sha256:fea8895f450959fa676bcc1df0611ea93823a735a01205fd8622846041d0c7cf
hello-world: marking blob sha256:03f4658f8b782e12230c1783426bd3bacce651ce582a4ffb6fbbfa2079428ecb
hello-world: marking blob sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
hello-world: marking configuration sha256:690ed74de00f99a7d00a98a5ad855ac4febd66412be132438f9b8dbd300a937d
ubuntu
4 blobs marked, 5 blobs eligible for deletion
blob eligible for deletion: sha256:28e09fddaacbfc8a13f82871d9d66141a6ed9ca526cb9ed295ef545ab4559b81
blob eligible for deletion: sha256:7e15ce58ccb2181a8fced7709e9893206f0937cc9543bc0c8178ea1cf4d7e7b5
blob eligible for deletion: sha256:87192bdbe00f8f2a62527f36bb4c7c7f4eaf9307e4b87e8334fb6abec1765bcb
blob eligible for deletion: sha256:b549a9959a664038fc35c155a95742cf12297672ca0ae35735ec027d55bf4e97
blob eligible for deletion: sha256:f251d679a7c61455f06d793e43c06786d7766c88b8c24edf242b2c08e3c3f599
```

View file

@ -0,0 +1,68 @@
---
published: false
---
# Glossary
This page contains definitions for distribution related terms.
<dl>
<dt id="blob"><h4>Blob</h4></dt>
<dd>
<blockquote>A blob is any kind of content that is stored by a Registry under a content-addressable identifier (a "digest").</blockquote>
<p>
<a href="#layer">Layers</a> are a good example of "blobs".
</p>
</dd>
<dt id="image"><h4>Image</h4></dt>
<dd>
<blockquote>An image is a named set of immutable data from which a Docker container can be created.</blockquote>
<p>
An image is represented by a json file called a <a href="#manifest">manifest</a>, and is conceptually a set of <a href="#layer">layers</a>.
Image names indicate the location where they can be pulled from and pushed to, as they usually start with a <a href="#registry">registry</a> domain name and port.
</p>
</dd>
<dt id="layer"><h4>Layer</h4></dt>
<dd>
<blockquote>A layer is a tar archive bundling partial content from a filesystem.</blockquote>
<p>
Layers from an <a href="#image">image</a> are usually extracted in order on top of each other to make up a root filesystem from which containers run out.
</p>
</dd>
<dt id="manifest"><h4>Manifest</h4></dt>
<dd><blockquote>A manifest is the JSON representation of an image.</blockquote></dd>
<dt id="namespace"><h4>Namespace</h4></dt>
<dd><blockquote>A namespace is a collection of repositories with a common name prefix.</blockquote>
<p>
The namespace with an empty prefix is considered the Global Namespace.
</p>
</dd>
<dt id="registry"><h4>Registry</h4></dt>
<dd><blockquote>A registry is a service that let you store and deliver <a href="#images">images</a>.</blockquote>
</dd>
<dt id="registry"><h4>Repository</h4></dt>
<dd>
<blockquote>A repository is a set of data containing all versions of a given image.</blockquote>
</dd>
<dt id="scope"><h4>Scope</h4></dt>
<dd><blockquote>A scope is the portion of a namespace onto which a given authorization token is granted.</blockquote></dd>
<dt id="tag"><h4>Tag</h4></dt>
<dd><blockquote>A tag is conceptually a "version" of a <a href="#image">named image</a>.</blockquote>
<p>
Example: `docker pull myimage:latest` instructs docker to pull the image "myimage" in version "latest".
</p>
</dd>
</dl>

View file

@ -0,0 +1,14 @@
---
description: Getting help with the Registry
keywords: registry, on-prem, images, tags, repository, distribution, help, 101, TL;DR
title: Get help
---
If you need help, or just want to chat about development, you can reach us on the #distribution channel in the CNCF Slack.
If you want to report a bug:
- be sure to first read about [how to contribute](https://github.com/distribution/distribution/blob/master/CONTRIBUTING.md).
- you can then do so on the [GitHub project bugtracker](https://github.com/distribution/distribution/issues).
You can also find out more about the Docker's project [Getting Help resources](../opensource/ways.md).

View file

@ -0,0 +1,165 @@
---
description: Deploying a Registry in an insecure fashion
keywords: registry, on-prem, images, tags, repository, distribution, insecure
title: Test an insecure registry
---
While it's highly recommended to secure your registry using a TLS certificate
issued by a known CA, you can choose to use self-signed certificates, or use
your registry over an unencrypted HTTP connection. Either of these choices
involves security trade-offs and additional configuration steps.
## Deploy a plain HTTP registry
> **Warning**:
> It's not possible to use an insecure registry with basic authentication.
{:.warning}
This procedure configures Docker to entirely disregard security for your
registry. This is **very** insecure and is not recommended. It exposes your
registry to trivial man-in-the-middle (MITM) attacks. Only use this solution for
isolated testing or in a tightly controlled, air-gapped environment.
1. Edit the `daemon.json` file, whose default location is
`/etc/docker/daemon.json` on Linux or
`C:\ProgramData\docker\config\daemon.json` on Windows Server. If you use
Docker Desktop for Mac or Docker Desktop for Windows, click the Docker icon, choose
**Preferences** (Mac) or **Settings** (Windows), and choose **Docker Engine**.
If the `daemon.json` file does not exist, create it. Assuming there are no
other settings in the file, it should have the following contents:
```json
{
"insecure-registries" : ["myregistrydomain.com:5000"]
}
```
Substitute the address of your insecure registry for the one in the example.
With insecure registries enabled, Docker goes through the following steps:
- First, try using HTTPS.
- If HTTPS is available but the certificate is invalid, ignore the error
about the certificate.
- If HTTPS is not available, fall back to HTTP.
2. Restart Docker for the changes to take effect.
Repeat these steps on every Engine host that wants to access your registry.
## Use self-signed certificates
> **Warning**:
> Using this along with basic authentication requires to **also** trust the certificate into the OS cert store for some versions of docker (see below)
{:.warning}
This is more secure than the insecure registry solution.
1. Generate your own certificate:
```console
$ mkdir -p certs
$ openssl req \
-newkey rsa:4096 -nodes -sha256 -keyout certs/domain.key \
-addext "subjectAltName = DNS:myregistry.domain.com" \
-x509 -days 365 -out certs/domain.crt
```
Be sure to use the name `myregistry.domain.com` as a CN.
2. Use the result to [start your registry with TLS enabled](./deploying.md#get-a-certificate).
3. Instruct every Docker daemon to trust that certificate. The way to do this
depends on your OS.
- **Linux**: Copy the `domain.crt` file to
`/etc/docker/certs.d/myregistrydomain.com:5000/ca.crt` on every Docker
host. You do not need to restart Docker.
- **Windows Server**:
1. Open Windows Explorer, right-click the `domain.crt`
file, and choose Install certificate. When prompted, select the following
options:
| Store location | local machine |
| Place all certificates in the following store | selected |
2. Click **Browser** and select **Trusted Root Certificate Authorities**.
3. Click **Finish**. Restart Docker.
- **Docker Desktop for Mac**: Follow the instructions in
[Adding custom CA certificates](../desktop/mac/index.md#add-tls-certificates){: target="_blank" rel="noopener" class="_"}.
Restart Docker.
- **Docker Desktop for Windows**: Follow the instructions in
[Adding custom CA certificates](../desktop/windows/index.md#adding-tls-certificates){: target="_blank" rel="noopener" class="_"}.
Restart Docker.
## Troubleshoot insecure registry
This section lists some common failures and how to recover from them.
### Failing...
Failing to configure the Engine daemon and trying to pull from a registry that is not using
TLS results in the following message:
```none
FATA[0000] Error response from daemon: v1 ping attempt failed with error:
Get https://myregistrydomain.com:5000/v1/_ping: tls: oversized record received with length 20527.
If this private registry supports only HTTP or HTTPS with an unknown CA certificate, add
`--insecure-registry myregistrydomain.com:5000` to the daemon's arguments.
In the case of HTTPS, if you have access to the registry's CA certificate, no need for the flag;
simply place the CA certificate at /etc/docker/certs.d/myregistrydomain.com:5000/ca.crt
```
### Docker still complains about the certificate when using authentication?
When using authentication, some versions of Docker also require you to trust the
certificate at the OS level.
#### Ubuntu
```console
$ cp certs/domain.crt /usr/local/share/ca-certificates/myregistrydomain.com.crt
update-ca-certificates
```
#### Red Hat Enterprise Linux
```console
$ cp certs/domain.crt /etc/pki/ca-trust/source/anchors/myregistrydomain.com.crt
update-ca-trust
```
#### Oracle Linux
```console
$ update-ca-trust enable
```
Restart Docker for the changes to take effect.
### Windows
Open Windows Explorer, right-click the certificate, and choose
**Install certificate**.
Then, select the following options:
* Store location: local machine
* Check **place all certificates in the following store**
* Click **Browser**, and select **Trusted Root Certificate Authorities**
* Click **Finish**
[Learn more about managing TLS certificates](https://technet.microsoft.com/en-us/library/cc754841(v=ws.11).aspx#BKMK_addlocal).
After adding the CA certificate to Windows, restart Docker Desktop for Windows.

View file

@ -0,0 +1,348 @@
---
description: Explains how to work with registry notifications
keywords: registry, on-prem, images, tags, repository, distribution, notifications, advanced
title: Work with notifications
---
The Registry supports sending webhook notifications in response to events
happening within the registry. Notifications are sent in response to manifest
pushes and pulls and layer pushes and pulls. These actions are serialized into
events. The events are queued into a registry-internal broadcast system which
queues and dispatches events to [_Endpoints_](notifications.md#endpoints).
![Workflow of registry notifications](images/notifications.png)
## Endpoints
Notifications are sent to _endpoints_ via HTTP requests. Each configured
endpoint has isolated queues, retry configuration and http targets within each
instance of a registry. When an action happens within the registry, it is
converted into an event which is dropped into an inmemory queue. When the
event reaches the end of the queue, an http request is made to the endpoint
until the request succeeds. The events are sent serially to each endpoint but
order is not guaranteed.
## Configuration
To setup a registry instance to send notifications to endpoints, one must add
them to the configuration. A simple example follows:
```yaml
notifications:
endpoints:
- name: alistener
url: https://mylistener.example.com/event
headers:
Authorization: [Bearer <your token, if needed>]
timeout: 500ms
threshold: 5
backoff: 1s
```
The above would configure the registry with an endpoint to send events to
`https://mylistener.example.com/event`, with the header "Authorization: Bearer
<your token, if needed>". The request would timeout after 500 milliseconds. If
5 failures happen consecutively, the registry backs off for 1 second before
trying again.
For details on the fields, see the [configuration documentation](configuration.md#notifications).
A properly configured endpoint should lead to a log message from the registry
upon startup:
```
INFO[0000] configuring endpoint alistener (https://mylistener.example.com/event), timeout=500ms, headers=map[Authorization:[Bearer <your token if needed>]] app.id=812bfeb2-62d6-43cf-b0c6-152f541618a3 environment=development service=registry
```
## Events
Events have a well-defined JSON structure and are sent as the body of
notification requests. One or more events are sent in a structure called an
envelope. Each event has a unique ID that can be used to uniquely identify incoming
requests, if required. Along with that, an _action_ is provided with a
_target_, identifying the object mutated during the event.
The fields available in an `event` are described below.
Field | Type | Description
----- | ----- | -------------
id | string |ID provides a unique identifier for the event.
timestamp | Time | Timestamp is the time at which the event occurred.
action | string | Action indicates what action encompasses the provided event.
target | distribution.Descriptor | Target uniquely describes the target of the event.
length | int | Length in bytes of content. Same as Size field in Descriptor.
repository | string | Repository identifies the named repository.
fromRepository | string | FromRepository identifies the named repository which a blob was mounted from if appropriate.
url | string | URL provides a direct link to the content.
tag | string | Tag identifies a tag name in tag events.
request | [RequestRecord](https://pkg.go.dev/github.com/distribution/distribution/notifications#RequestRecord) | Request covers the request that generated the event.
actor | [ActorRecord](https://pkg.go.dev/github.com/distribution/distribution/notifications#ActorRecord). | Actor specifies the agent that initiated the event. For most situations, this could be from the authorization context of the request.
source | [SourceRecord](https://pkg.go.dev/github.com/distribution/distribution/notifications#SourceRecord) | Source identifies the registry node that generated the event. Put differently, while the actor "initiates" the event, the source "generates" it.
The following is an example of a JSON event, sent in response to the pull of a
manifest:
```json
{
"events": [
{
"id": "320678d8-ca14-430f-8bb6-4ca139cd83f7",
"timestamp": "2016-03-09T14:44:26.402973972-08:00",
"action": "pull",
"target": {
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"digest": "sha256:fea8895f450959fa676bcc1df0611ea93823a735a01205fd8622846041d0c7cf",
"size": 708,
"length": 708,
"repository": "hello-world",
"url": "http://192.168.100.227:5000/v2/hello-world/manifests/sha256:fea8895f450959fa676bcc1df0611ea93823a735a01205fd8622846041d0c7cf",
"tag": "latest"
},
"request": {
"id": "6df24a34-0959-4923-81ca-14f09767db19",
"addr": "192.168.64.11:42961",
"host": "192.168.100.227:5000",
"method": "GET",
"useragent": "curl/7.38.0"
},
"actor": {},
"source": {
"addr": "xtal.local:5000",
"instanceID": "a53db899-3b4b-4a62-a067-8dd013beaca4"
}
}
]
}
```
The target struct of events which are sent when manifests and blobs are deleted
contains a subset of the data contained in Get and Put events. Specifically,
only the digest and repository are sent.
```json
{
"target": {
"digest": "sha256:d89e1bee20d9cb344674e213b581f14fbd8e70274ecf9d10c514bab78a307845",
"repository": "library/test"
}
}
```
> **Note**: As of version 2.1, the `length` field for event targets
> is being deprecated for the `size` field, bringing the target in line with
> common nomenclature. Both will continue to be set for the foreseeable
> future. Newer code should favor `size` but accept either.
## Envelope
The envelope contains one or more events, with the following json structure:
```json
{
"events": [ "..." ]
}
```
While events may be sent in the same envelope, the set of events within that
envelope have no implied relationship. For example, the registry may choose to
group unrelated events and send them in the same envelope to reduce the total
number of requests.
The full package has the mediatype
"application/vnd.docker.distribution.events.v2+json", which is set on the
request coming to an endpoint.
An example of a full event may look as follows:
```http request
POST /callback HTTP/1.1
Host: application/vnd.docker.distribution.events.v2+json
Authorization: Bearer <your token, if needed>
Content-Type: application/vnd.docker.distribution.events.v2+json
{
"events": [
{
"id": "asdf-asdf-asdf-asdf-0",
"timestamp": "2006-01-02T15:04:05Z",
"action": "push",
"target": {
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"digest": "sha256:fea8895f450959fa676bcc1df0611ea93823a735a01205fd8622846041d0c7cf",
"length": 1,
"repository": "library/test",
"url": "https://example.com/v2/library/test/manifests/sha256:c3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d5"
},
"request": {
"id": "asdfasdf",
"addr": "client.local",
"host": "registrycluster.local",
"method": "PUT",
"useragent": "test/0.1"
},
"actor": {
"name": "test-actor"
},
"source": {
"addr": "hostname.local:port"
}
},
{
"id": "asdf-asdf-asdf-asdf-1",
"timestamp": "2006-01-02T15:04:05Z",
"action": "push",
"target": {
"mediaType": "application/vnd.docker.container.image.rootfs.diff+x-gtar",
"digest": "sha256:c3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d5",
"length": 2,
"repository": "library/test",
"url": "https://example.com/v2/library/test/blobs/sha256:c3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d5"
},
"request": {
"id": "asdfasdf",
"addr": "client.local",
"host": "registrycluster.local",
"method": "PUT",
"useragent": "test/0.1"
},
"actor": {
"name": "test-actor"
},
"source": {
"addr": "hostname.local:port"
}
},
{
"id": "asdf-asdf-asdf-asdf-2",
"timestamp": "2006-01-02T15:04:05Z",
"action": "push",
"target": {
"mediaType": "application/vnd.docker.container.image.rootfs.diff+x-gtar",
"digest": "sha256:c3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d5",
"length": 3,
"repository": "library/test",
"url": "https://example.com/v2/library/test/blobs/sha256:c3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d5"
},
"request": {
"id": "asdfasdf",
"addr": "client.local",
"host": "registrycluster.local",
"method": "PUT",
"useragent": "test/0.1"
},
"actor": {
"name": "test-actor"
},
"source": {
"addr": "hostname.local:port"
}
}
]
}
```
## Responses
The registry is fairly accepting of the response codes from endpoints. If an
endpoint responds with any 2xx or 3xx response code (after following
redirects), the message is considered to have been delivered, and is discarded.
In turn, it is recommended that endpoints are accepting of incoming responses,
as well. While the format of event envelopes are standardized by media type,
any "pickyness" about validation may cause the queue to backup on the
registry.
## Monitoring
The state of the endpoints are reported via the debug/vars http interface,
usually configured to `http://localhost:5001/debug/vars`. Information such as
configuration and metrics are available by endpoint.
The following provides an example of a few endpoints that have experienced
several failures and have since recovered:
```json
{
"notifications": {
"endpoints": [
{
"name": "local-5003",
"url": "http://localhost:5003/callback",
"Headers": {
"Authorization": [
"Bearer \u003can example token\u003e"
]
},
"Timeout": 1000000000,
"Threshold": 10,
"Backoff": 1000000000,
"Metrics": {
"Pending": 76,
"Events": 76,
"Successes": 0,
"Failures": 0,
"Errors": 46,
"Statuses": {
}
}
},
{
"name": "local-8083",
"url": "http://localhost:8083/callback",
"Headers": null,
"Timeout": 1000000000,
"Threshold": 10,
"Backoff": 1000000000,
"Metrics": {
"Pending": 0,
"Events": 76,
"Successes": 76,
"Failures": 0,
"Errors": 28,
"Statuses": {
"202 Accepted": 76
}
}
}
]
}
}
```
If using notification as part of a larger application, it is _critical_ to
monitor the size ("Pending" above) of the endpoint queues. If failures or
queue sizes are increasing, it can indicate a larger problem.
The logs are also a valuable resource for monitoring problems. A failing
endpoint leads to messages similar to the following:
```none
ERRO[0340] retryingsink: error writing events: httpSink{http://localhost:5003/callback}: error posting: Post http://localhost:5003/callback: dial tcp 127.0.0.1:5003: connection refused, retrying
WARN[0340] httpSink{http://localhost:5003/callback} encountered too many errors, backing off
```
The above indicates that several errors caused a backoff and the registry
waits before retrying.
## Considerations
Currently, the queues are inmemory, so endpoints should be _reasonably
reliable_. They are designed to make a best-effort to send the messages but if
an instance is lost, messages may be dropped. If an endpoint goes down, care
should be taken to ensure that the registry instance is not terminated before
the endpoint comes back up or messages are lost.
This can be mitigated by running endpoints in close proximity to the registry
instances. One could run an endpoint that pages to disk and then forwards a
request to provide better durability.
The notification system is designed around a series of interchangeable _sinks_
which can be wired up to achieve interesting behavior. If this system doesn't
provide acceptable guarantees, adding a transactional `Sink` to the registry
is a possibility, although it may have an effect on request service time.
See the
[godoc](https://pkg.go.dev/github.com/distribution/distribution/notifications#Sink)
for more information.

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View file

@ -0,0 +1,28 @@
---
description: Fun stuff to do with your registry
keywords: registry, on-prem, images, tags, repository, distribution, recipes, advanced
title: Recipes overview
---
This list of "recipes" provides end-to-end scenarios for exotic or otherwise advanced use-cases.
These recipes are not useful for most standard set-ups.
## Requirements
Before following these steps, work through the [deployment guide](../deploying.md).
At this point, it's assumed that:
* you understand Docker security requirements, and how to configure your docker engines properly
* you have installed Docker Compose
* it's HIGHLY recommended that you get a certificate from a known CA instead of self-signed certificates
* inside the current directory, you have a X509 `domain.crt` and `domain.key`, for the CN `myregistrydomain.com`
* be sure you have stopped and removed any previously running registry (typically `docker container stop registry && docker container rm -v registry`)
## The List
* [using Apache as an authenticating proxy](apache.md)
* [using Nginx as an authenticating proxy](nginx.md)
* [running a Registry on macOS](osx-setup-guide.md)
* [mirror the Docker Hub](mirror.md)
* [start registry via systemd](systemd.md)

View file

@ -0,0 +1,209 @@
---
description: Restricting access to your registry using an apache proxy
keywords: registry, on-prem, images, tags, repository, distribution, authentication, proxy, apache, httpd, TLS, recipe, advanced
title: Authenticate proxy with apache
---
## Use-case
People already relying on an apache proxy to authenticate their users to other services might want to leverage it and have Registry communications tunneled through the same pipeline.
Usually, that includes enterprise setups using LDAP/AD on the backend and a SSO mechanism fronting their internal http portal.
### Alternatives
If you just want authentication for your registry, and are happy maintaining users access separately, you should really consider sticking with the native [basic auth registry feature](../deploying.md#native-basic-auth).
### Solution
With the method presented here, you implement basic authentication for docker engines in a reverse proxy that sits in front of your registry.
While we use a simple htpasswd file as an example, any other apache authentication backend should be fairly easy to implement once you are done with the example.
We also implement push restriction (to a limited user group) for the sake of the example. Again, you should modify this to fit your mileage.
### Gotchas
While this model gives you the ability to use whatever authentication backend you want through the secondary authentication mechanism implemented inside your proxy, it also requires that you move TLS termination from the Registry to the proxy itself.
Furthermore, introducing an extra http layer in your communication pipeline adds complexity when deploying, maintaining, and debugging.
## Setting things up
Read again [the requirements](index.md#requirements).
Ready?
Run the following script:
```
mkdir -p auth
mkdir -p data
# This is the main apache configuration
cat <<EOF > auth/httpd.conf
LoadModule headers_module modules/mod_headers.so
LoadModule authn_file_module modules/mod_authn_file.so
LoadModule authn_core_module modules/mod_authn_core.so
LoadModule authz_groupfile_module modules/mod_authz_groupfile.so
LoadModule authz_user_module modules/mod_authz_user.so
LoadModule authz_core_module modules/mod_authz_core.so
LoadModule auth_basic_module modules/mod_auth_basic.so
LoadModule access_compat_module modules/mod_access_compat.so
LoadModule log_config_module modules/mod_log_config.so
LoadModule ssl_module modules/mod_ssl.so
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule unixd_module modules/mod_unixd.so
<IfModule ssl_module>
SSLRandomSeed startup builtin
SSLRandomSeed connect builtin
</IfModule>
<IfModule unixd_module>
User daemon
Group daemon
</IfModule>
ServerAdmin you@example.com
ErrorLog /proc/self/fd/2
LogLevel warn
<IfModule log_config_module>
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
LogFormat "%h %l %u %t \"%r\" %>s %b" common
<IfModule logio_module>
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio
</IfModule>
CustomLog /proc/self/fd/1 common
</IfModule>
ServerRoot "/usr/local/apache2"
Listen 5043
<Directory />
AllowOverride none
Require all denied
</Directory>
<VirtualHost *:5043>
ServerName myregistrydomain.com
SSLEngine on
SSLCertificateFile /usr/local/apache2/conf/domain.crt
SSLCertificateKeyFile /usr/local/apache2/conf/domain.key
## SSL settings recommendation from: https://raymii.org/s/tutorials/Strong_SSL_Security_On_Apache2.html
# Anti CRIME
SSLCompression off
# POODLE and other stuff
SSLProtocol all -SSLv2 -SSLv3 -TLSv1
# Secure cypher suites
SSLCipherSuite EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH
SSLHonorCipherOrder on
Header always set "Docker-Distribution-Api-Version" "registry/2.0"
Header onsuccess set "Docker-Distribution-Api-Version" "registry/2.0"
RequestHeader set X-Forwarded-Proto "https"
ProxyRequests off
ProxyPreserveHost on
# no proxy for /error/ (Apache HTTPd errors messages)
ProxyPass /error/ !
ProxyPass /v2 http://registry:5000/v2
ProxyPassReverse /v2 http://registry:5000/v2
<Location /v2>
Order deny,allow
Allow from all
AuthName "Registry Authentication"
AuthType basic
AuthUserFile "/usr/local/apache2/conf/httpd.htpasswd"
AuthGroupFile "/usr/local/apache2/conf/httpd.groups"
# Read access to authentified users
<Limit GET HEAD>
Require valid-user
</Limit>
# Write access to docker-deployer only
<Limit POST PUT DELETE PATCH>
Require group pusher
</Limit>
</Location>
</VirtualHost>
EOF
# Now, create a password file for "testuser" and "testpassword"
docker run --entrypoint htpasswd httpd:2.4 -Bbn testuser testpassword > auth/httpd.htpasswd
# Create another one for "testuserpush" and "testpasswordpush"
docker run --entrypoint htpasswd httpd:2.4 -Bbn testuserpush testpasswordpush >> auth/httpd.htpasswd
# Create your group file
echo "pusher: testuserpush" > auth/httpd.groups
# Copy over your certificate files
cp domain.crt auth
cp domain.key auth
# Now create your compose file
cat <<EOF > docker-compose.yml
apache:
image: "httpd:2.4"
hostname: myregistrydomain.com
ports:
- 5043:5043
links:
- registry:registry
volumes:
- `pwd`/auth:/usr/local/apache2/conf
registry:
image: registry:2
ports:
- 127.0.0.1:5000:5000
volumes:
- `pwd`/data:/var/lib/registry
EOF
```
## Starting and stopping
Now, start your stack:
docker-compose up -d
Log in with a "push" authorized user (using `testuserpush` and `testpasswordpush`), then tag and push your first image:
docker login myregistrydomain.com:5043
docker tag ubuntu myregistrydomain.com:5043/test
docker push myregistrydomain.com:5043/test
Now, log in with a "pull-only" user (using `testuser` and `testpassword`), then pull back the image:
docker login myregistrydomain.com:5043
docker pull myregistrydomain.com:5043/test
Verify that the "pull-only" can NOT push:
docker push myregistrydomain.com:5043/test

View file

@ -0,0 +1,147 @@
---
description: Setting-up a local mirror for Docker Hub images
keywords: registry, on-prem, images, tags, repository, distribution, mirror, Hub, recipe, advanced
title: Registry as a pull through cache
redirect_from:
- /engine/admin/registry_mirror/
---
## Use-case
If you have multiple instances of Docker running in your environment, such as
multiple physical or virtual machines all running Docker, each daemon goes out
to the internet and fetches an image it doesn't have locally, from the Docker
repository. You can run a local registry mirror and point all your daemons
there, to avoid this extra internet traffic.
> **Note**
>
> Docker Official Images are an intellectual property of Docker.
### Alternatives
Alternatively, if the set of images you are using is well delimited, you can
simply pull them manually and push them to a simple, local, private registry.
Furthermore, if your images are all built in-house, not using the Hub at all and
relying entirely on your local registry is the simplest scenario.
### Gotcha
It's currently not possible to mirror another private registry. Only the central
Hub can be mirrored.
The URL of a pull-through registry mirror must be the root of a domain.
No path components other than an optional trailing slash (`/`) are allowed.
The following table shows examples of allowed and disallowed mirror URLs.
| URL | Allowed |
| -------------------------------------- | ------- |
| `https://mirror.company.example` | Yes |
| `https://mirror.company.example/` | Yes |
| `https://mirror.company.example/foo` | No |
| `https://mirror.company.example#bar` | No |
| `https://mirror.company.example?baz=1` | No |
> **Note**
>
> Mirrors of Docker Hub are still subject to Docker's [fair usage policy](https://www.docker.com/pricing/resource-consumption-updates){: target="blank" rel="noopener" class=“”}.
### Solution
The Registry can be configured as a pull through cache. In this mode a Registry
responds to all normal docker pull requests but stores all content locally.
## How does it work?
The first time you request an image from your local registry mirror, it pulls
the image from the public Docker registry and stores it locally before handing
it back to you. On subsequent requests, the local registry mirror is able to
serve the image from its own storage.
### What if the content changes on the Hub?
When a pull is attempted with a tag, the Registry checks the remote to
ensure if it has the latest version of the requested content. Otherwise, it
fetches and caches the latest content.
### What about my disk?
In environments with high churn rates, stale data can build up in the cache.
When running as a pull through cache the Registry periodically removes old
content to save disk space. Subsequent requests for removed content causes a
remote fetch and local re-caching.
To ensure best performance and guarantee correctness the Registry cache should
be configured to use the `filesystem` driver for storage.
## Run a Registry as a pull-through cache
The easiest way to run a registry as a pull through cache is to run the official
Registry image.
At least, you need to specify `proxy.remoteurl` within `/etc/docker/registry/config.yml`
as described in the following subsection.
Multiple registry caches can be deployed over the same back-end. A single
registry cache ensures that concurrent requests do not pull duplicate data,
but this property does not hold true for a registry cache cluster.
> **Note**
>
> Service accounts included in the Team plan are limited to 5,000 pulls per day. See [Service Accounts](/docker-hub/service-accounts/) for more details.
### Configure the cache
To configure a Registry to run as a pull through cache, the addition of a
`proxy` section is required to the config file.
To access private images on the Docker Hub, a username and password can
be supplied.
```yaml
proxy:
remoteurl: https://registry-1.docker.io
username: [username]
password: [password]
ttl: 168h
```
> **Warning**: If you specify a username and password, it's very important to
> understand that private resources that this user has access to Docker Hub is
> made available on your mirror. **You must secure your mirror** by
> implementing authentication if you expect these resources to stay private!
> **Warning**: For the scheduler to clean up old entries, `delete` must
> be enabled in the registry configuration. See
> [Registry Configuration](../configuration.md) for more details.
### Configure the Docker daemon
Either pass the `--registry-mirror` option when starting `dockerd` manually,
or edit [`/etc/docker/daemon.json`](../../engine/reference/commandline/dockerd.md#daemon-configuration-file)
and add the `registry-mirrors` key and value, to make the change persistent.
```json
{
"registry-mirrors": ["https://mirror.company.example"]
}
```
> **Note**
>
> The mirror URL must be the root of the domain.
Save the file and reload Docker for the change to take effect.
> Some log messages that appear to be errors are actually informational messages.
>
> Check the `level` field to determine whether
> the message is warning you about an error or is giving you information.
> For example, this log message is informational:
>
> ```conf
> time="2017-06-02T15:47:37Z" level=info msg="error statting local store, serving from upstream: unknown blob" go.version=go1.7.4
> ```
>
> It's telling you that the file doesn't exist yet in the local cache and is
> being pulled from upstream.

View file

@ -0,0 +1,205 @@
---
description: Restricting access to your registry using a nginx proxy
keywords: registry, on-prem, images, tags, repository, distribution, nginx, proxy, authentication, TLS, recipe, advanced
title: Authenticate proxy with nginx
redirect_from:
- /registry/nginx/
---
## Use-case
People already relying on a nginx proxy to authenticate their users to other
services might want to leverage it and have Registry communications tunneled
through the same pipeline.
Usually, that includes enterprise setups using LDAP/AD on the backend and a SSO
mechanism fronting their internal http portal.
### Alternatives
If you just want authentication for your registry, and are happy maintaining
users access separately, you should really consider sticking with the native
[basic auth registry feature](../deploying.md#native-basic-auth).
### Solution
With the method presented here, you implement basic authentication for docker
engines in a reverse proxy that sits in front of your registry.
While we use a simple htpasswd file as an example, any other nginx
authentication backend should be fairly easy to implement once you are done with
the example.
We also implement push restriction (to a limited user group) for the sake of the
example. Again, you should modify this to fit your mileage.
### Gotchas
While this model gives you the ability to use whatever authentication backend
you want through the secondary authentication mechanism implemented inside your
proxy, it also requires that you move TLS termination from the Registry to the
proxy itself.
> **Note**: It is not recommended to bind your registry to `localhost:5000` without
> authentication. This creates a potential loophole in your registry security.
> As a result, anyone who can log on to the server where your registry is running
> can push images without authentication.
Furthermore, introducing an extra http layer in your communication pipeline
makes it more complex to deploy, maintain, and debug. Make sure the extra
complexity is required.
For instance, Amazon's Elastic Load Balancer (ELB) in HTTPS mode already sets
the following client header:
```
X-Real-IP
X-Forwarded-For
X-Forwarded-Proto
```
So if you have an Nginx instance sitting behind it, remove these lines from the
example config below:
```none
proxy_set_header Host $http_host; # required for docker client's sake
proxy_set_header X-Real-IP $remote_addr; # pass on real client's IP
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
```
Otherwise Nginx resets the ELB's values, and the requests are not routed
properly. For more information, see
[#970](https://github.com/distribution/distribution/issues/970).
## Setting things up
Review the [requirements](index.md#requirements), then follow these steps.
1. Create the required directories
```console
$ mkdir -p auth data
```
2. Create the main nginx configuration. Paste this code block into a new file called `auth/nginx.conf`:
```conf
events {
worker_connections 1024;
}
http {
upstream docker-registry {
server registry:5000;
}
## Set a variable to help us decide if we need to add the
## 'Docker-Distribution-Api-Version' header.
## The registry always sets this header.
## In the case of nginx performing auth, the header is unset
## since nginx is auth-ing before proxying.
map $upstream_http_docker_distribution_api_version $docker_distribution_api_version {
'' 'registry/2.0';
}
server {
listen 443 ssl;
server_name myregistrydomain.com;
# SSL
ssl_certificate /etc/nginx/conf.d/domain.crt;
ssl_certificate_key /etc/nginx/conf.d/domain.key;
# Recommendations from https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html
ssl_protocols TLSv1.1 TLSv1.2;
ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
# disable any limits to avoid HTTP 413 for large image uploads
client_max_body_size 0;
# required to avoid HTTP 411: see Issue #1486 (https://github.com/moby/moby/issues/1486)
chunked_transfer_encoding on;
location /v2/ {
# Do not allow connections from docker 1.5 and earlier
# docker pre-1.6.0 did not properly set the user agent on ping, catch "Go *" user agents
if ($http_user_agent ~ "^(docker\/1\.(3|4|5(?!\.[0-9]-dev))|Go ).*$" ) {
return 404;
}
# To add basic authentication to v2 use auth_basic setting.
auth_basic "Registry realm";
auth_basic_user_file /etc/nginx/conf.d/nginx.htpasswd;
## If $docker_distribution_api_version is empty, the header is not added.
## See the map directive above where this variable is defined.
add_header 'Docker-Distribution-Api-Version' $docker_distribution_api_version always;
proxy_pass http://docker-registry;
proxy_set_header Host $http_host; # required for docker client's sake
proxy_set_header X-Real-IP $remote_addr; # pass on real client's IP
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 900;
}
}
}
```
3. Create a password file `auth/nginx.htpasswd` for "testuser" and "testpassword".
```console
$ docker run --rm --entrypoint htpasswd registry:2 -Bbn testuser testpassword > auth/nginx.htpasswd
```
> **Note**: If you do not want to use `bcrypt`, you can omit the `-B` parameter.
4. Copy your certificate files to the `auth/` directory.
```console
$ cp domain.crt auth
$ cp domain.key auth
```
5. Create the compose file. Paste the following YAML into a new file called `docker-compose.yml`.
```yaml
version: "3"
services:
nginx:
# Note : Only nginx:alpine supports bcrypt.
# If you don't need to use bcrypt, you can use a different tag.
# Ref. https://github.com/nginxinc/docker-nginx/issues/29
image: "nginx:alpine"
ports:
- 5043:443
depends_on:
- registry
volumes:
- ./auth:/etc/nginx/conf.d
- ./auth/nginx.conf:/etc/nginx/nginx.conf:ro
registry:
image: registry:2
volumes:
- ./data:/var/lib/registry
```
## Starting and stopping
Now, start your stack:
docker-compose up -d
Login with a "push" authorized user (using `testuser` and `testpassword`), then
tag and push your first image:
docker login -u=testuser -p=testpassword -e=root@example.ch myregistrydomain.com:5043
docker tag ubuntu myregistrydomain.com:5043/test
docker push myregistrydomain.com:5043/test
docker pull myregistrydomain.com:5043/test

View file

@ -0,0 +1,74 @@
---
description: Explains how to run a registry on macOS
keywords: registry, on-prem, images, tags, repository, distribution, macOS, recipe, advanced
title: macOS setup guide
---
## Use-case
This is useful if you intend to run a registry server natively on macOS.
### Alternatives
You can start a VM on macOS, and deploy your registry normally as a container using Docker inside that VM.
### Solution
Using the method described here, you install and compile your own from the git repository and run it as an macOS agent.
### Gotchas
Production services operation on macOS is out of scope of this document. Be sure you understand well these aspects before considering going to production with this.
## Setup golang on your machine
If you know, safely skip to the next section.
If you don't, the TLDR is:
bash < <(curl -s -S -L https://raw.githubusercontent.com/moovweb/gvm/master/binscripts/gvm-installer)
source ~/.gvm/scripts/gvm
gvm install go1.4.2
gvm use go1.4.2
If you want to understand, you should read [How to Write Go Code](https://golang.org/doc/code.html).
## Checkout the source tree
mkdir -p $GOPATH/src/github.com/distribution
git clone https://github.com/distribution/distribution.git $GOPATH/src/github.com/distribution/distribution
cd $GOPATH/src/github.com/distribution/distribution
## Build the binary
GOPATH=$(PWD)/Godeps/_workspace:$GOPATH make binaries
sudo mkdir -p /usr/local/libexec
sudo cp bin/registry /usr/local/libexec/registry
## Setup
Copy the registry configuration file in place:
mkdir /Users/Shared/Registry
cp docs/osx/config.yml /Users/Shared/Registry/config.yml
## Run the registry under launchd
Copy the registry plist into place:
plutil -lint docs/recipes/osx/com.docker.registry.plist
cp docs/recipes/osx/com.docker.registry.plist ~/Library/LaunchAgents/
chmod 644 ~/Library/LaunchAgents/com.docker.registry.plist
Start the registry:
launchctl load ~/Library/LaunchAgents/com.docker.registry.plist
### Restart the registry service
launchctl stop com.docker.registry
launchctl start com.docker.registry
### Unload the registry service
launchctl unload ~/Library/LaunchAgents/com.docker.registry.plist

View file

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.docker.registry</string>
<key>KeepAlive</key>
<true/>
<key>StandardErrorPath</key>
<string>/Users/Shared/Registry/registry.log</string>
<key>StandardOutPath</key>
<string>/Users/Shared/Registry/registry.log</string>
<key>Program</key>
<string>/usr/local/libexec/registry</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/libexec/registry</string>
<string>/Users/Shared/Registry/config.yml</string>
</array>
<key>Sockets</key>
<dict>
<key>http-listen-address</key>
<dict>
<key>SockServiceName</key>
<string>5000</string>
<key>SockType</key>
<string>dgram</string>
<key>SockFamily</key>
<string>IPv4</string>
</dict>
<key>http-debug-address</key>
<dict>
<key>SockServiceName</key>
<string>5001</string>
<key>SockType</key>
<string>dgram</string>
<key>SockFamily</key>
<string>IPv4</string>
</dict>
</dict>
</dict>
</plist>

View file

@ -0,0 +1,16 @@
version: 0.1
log:
level: info
fields:
service: registry
environment: macbook-air
storage:
cache:
blobdescriptor: inmemory
filesystem:
rootdirectory: /Users/Shared/Registry
http:
addr: 0.0.0.0:5000
secret: mytokensecret
debug:
addr: localhost:5001

View file

@ -0,0 +1,105 @@
---
description: Using systemd to manage registry container
keywords: registry, on-prem, systemd, socket-activated, recipe, advanced
title: Start registry via systemd
---
## Use-case
Using systemd to manage containers can make service discovery and maintenance easier
by managining all services in the same way. Additionally, when using Podman, systemd
can start the registry with socket-activation, providing additional security options:
* Run as non-root and expose on a low-numbered socket (< 1024)
* Run with `--network=none`
### Docker
When deploying the registry via Docker, a simple service file can be used to manage
the registry:
registry.service
```
[Unit]
Description=Docker registry
After=docker.service
Requires=docker.service
[Service]
#TimeoutStartSec=0
Restart=always
ExecStartPre=-/usr/bin/docker stop %N
ExecStartPre=-/usr/bin/docker rm %N
ExecStart=/usr/bin/docker run --name %N \
-v registry:/var/lib/registry \
-p 5000:5000 \
registry:2
[Install]
WantedBy=multi-user.target
```
In this case, the registry will store images in the named-volume `registry`.
Note that the container is destroyed on restart instead of using `--rm` or
destroy on stop. This is done to make accessing `docker logs ...` easier in
the case of issues.
### Podman
Podman offers tighter integration with systemd than Docker does, and supports
socket-activation of containers.
#### Create service file
```
podman create --name registry --network=none -v registry:/var/lib/registry registry:2
podman generate systemd --name --new registry > registry.service
```
#### Create socket file
registry.socket
```
[Unit]
Description=container registry
[Socket]
ListenStream=5000
[Install]
WantedBy=sockets.target
```
### Installation
Installation can be either rootful or rootless. For Docker, rootless configurations
often include additional setup steps that are beyond the scope of this recipe, whereas
for Podman, rootless containers generally work out of the box.
#### Rootful
Run as root:
* Copy registry.service (and registry.socket if relevant) to /etc/systemd/service/
* Run `systemctl daemon-reload`
* Enable the service:
* When using socket activation: `systemctl enable registry.socket`
* When **not** using socket activation: `systemctl enable registry.service`
* Start the service:
* When using socket activation: `systemctl start registry.socket`
* When **not** using socket activation: `systemctl start registry.service`
#### Rootless
Run as the target user:
* Copy registry.service (and registry.socket if relevant) to ~/.config/systemd/user/
* Run `systemctl --user daemon-reload`
* Enable the service:
* When using socket activation: `systemctl --user enable registry.socket`
* When **not** using socket activation: `systemctl --user enable registry.service`
* Start the service:
* When using socket activation: `systemctl --user start registry.socket`
* When **not** using socket activation: `systemctl --user start registry.service`
**Note**: To have rootless services start on boot, it may be necessary to enable linger
via `loginctl enable-linger $USER`.

View file

@ -0,0 +1,12 @@
---
title: "Reference Overview"
description: "Explains registry JSON objects"
keywords: registry, service, images, repository, json
---
# Docker Registry Reference
* [HTTP API V2](api.md)
* [Storage Driver](https://docs.docker.com/registry/storage-drivers/)
* [Token Authentication Specification](auth/token.md)
* [Token Authentication Implementation](auth/jwt.md)

5505
docs/content/spec/api.md Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,12 @@
---
title: "Docker Registry Token Authentication"
description: "Docker Registry v2 authentication schema"
keywords: registry, on-prem, images, tags, repository, distribution, authentication, advanced
---
# Docker Registry v2 authentication
See the [Token Authentication Specification](token.md),
[Token Authentication Implementation](jwt.md),
[Token Scope Documentation](scope.md),
[OAuth2 Token Authentication](oauth.md) for more information.

View file

@ -0,0 +1,328 @@
---
title: "Token Authentication Implementation"
description: "Describe the reference implementation of the Docker Registry v2 authentication schema"
keywords: registry, on-prem, images, tags, repository, distribution, JWT authentication, advanced
---
# Docker Registry v2 Bearer token specification
This specification covers the `distribution/distribution` implementation of the
v2 Registry's authentication schema. Specifically, it describes the JSON
Web Token schema that `distribution/distribution` has adopted to implement the
client-opaque Bearer token issued by an authentication service and
understood by the registry.
This document borrows heavily from the [JSON Web Token Draft Spec](https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-32)
## Getting a Bearer Token
For this example, the client makes an HTTP GET request to the following URL:
```
https://auth.docker.io/token?service=registry.docker.io&scope=repository:samalba/my-app:pull,push
```
The token server should first attempt to authenticate the client using any
authentication credentials provided with the request. As of Docker 1.8, the
registry client in the Docker Engine only supports Basic Authentication to
these token servers. If an attempt to authenticate to the token server fails,
the token server should return a `401 Unauthorized` response indicating that
the provided credentials are invalid.
Whether the token server requires authentication is up to the policy of that
access control provider. Some requests may require authentication to determine
access (such as pushing or pulling a private repository) while others may not
(such as pulling from a public repository).
After authenticating the client (which may simply be an anonymous client if
no attempt was made to authenticate), the token server must next query its
access control list to determine whether the client has the requested scope. In
this example request, if I have authenticated as user `jlhawn`, the token
server will determine what access I have to the repository `samalba/my-app`
hosted by the entity `registry.docker.io`.
Once the token server has determined what access the client has to the
resources requested in the `scope` parameter, it will take the intersection of
the set of requested actions on each resource and the set of actions that the
client has in fact been granted. If the client only has a subset of the
requested access **it must not be considered an error** as it is not the
responsibility of the token server to indicate authorization errors as part of
this workflow.
Continuing with the example request, the token server will find that the
client's set of granted access to the repository is `[pull, push]` which when
intersected with the requested access `[pull, push]` yields an equal set. If
the granted access set was found only to be `[pull]` then the intersected set
would only be `[pull]`. If the client has no access to the repository then the
intersected set would be empty, `[]`.
It is this intersected set of access which is placed in the returned token.
The server will now construct a JSON Web Token to sign and return. A JSON Web
Token has 3 main parts:
1. Headers
The header of a JSON Web Token is a standard JOSE header. The "typ" field
will be "JWT" and it will also contain the "alg" which identifies the
signing algorithm used to produce the signature. It also must have a "kid"
field, representing the ID of the key which was used to sign the token.
The "kid" field has to be in a libtrust fingerprint compatible format.
Such a format can be generated by following steps:
1. Take the DER encoded public key which the JWT token was signed against.
2. Create a SHA256 hash out of it and truncate to 240bits.
3. Split the result into 12 base32 encoded groups with `:` as delimiter.
Here is an example JOSE Header for a JSON Web Token (formatted with
whitespace for readability):
```
{
"typ": "JWT",
"alg": "ES256",
"kid": "PYYO:TEWU:V7JH:26JV:AQTZ:LJC3:SXVJ:XGHA:34F2:2LAQ:ZRMK:Z7Q6"
}
```
It specifies that this object is going to be a JSON Web token signed using
the key with the given ID using the Elliptic Curve signature algorithm
using a SHA256 hash.
2. Claim Set
The Claim Set is a JSON struct containing these standard registered claim
name fields:
<dl>
<dt>
<code>iss</code> (Issuer)
</dt>
<dd>
The issuer of the token, typically the fqdn of the authorization
server.
</dd>
<dt>
<code>sub</code> (Subject)
</dt>
<dd>
The subject of the token; the name or id of the client which
requested it. This should be empty (`""`) if the client did not
authenticate.
</dd>
<dt>
<code>aud</code> (Audience)
</dt>
<dd>
The intended audience of the token; the name or id of the service
which will verify the token to authorize the client/subject.
</dd>
<dt>
<code>exp</code> (Expiration)
</dt>
<dd>
The token should only be considered valid up to this specified date
and time.
</dd>
<dt>
<code>nbf</code> (Not Before)
</dt>
<dd>
The token should not be considered valid before this specified date
and time.
</dd>
<dt>
<code>iat</code> (Issued At)
</dt>
<dd>
Specifies the date and time which the Authorization server
generated this token.
</dd>
<dt>
<code>jti</code> (JWT ID)
</dt>
<dd>
A unique identifier for this token. Can be used by the intended
audience to prevent replays of the token.
</dd>
</dl>
The Claim Set will also contain a private claim name unique to this
authorization server specification:
<dl>
<dt>
<code>access</code>
</dt>
<dd>
An array of access entry objects with the following fields:
<dl>
<dt>
<code>type</code>
</dt>
<dd>
The type of resource hosted by the service.
</dd>
<dt>
<code>name</code>
</dt>
<dd>
The name of the resource of the given type hosted by the
service.
</dd>
<dt>
<code>actions</code>
</dt>
<dd>
An array of strings which give the actions authorized on
this resource.
</dd>
</dl>
</dd>
</dl>
Here is an example of such a JWT Claim Set (formatted with whitespace for
readability):
```
{
"iss": "auth.docker.com",
"sub": "jlhawn",
"aud": "registry.docker.com",
"exp": 1415387315,
"nbf": 1415387015,
"iat": 1415387015,
"jti": "tYJCO1c6cnyy7kAn0c7rKPgbV1H1bFws",
"access": [
{
"type": "repository",
"name": "samalba/my-app",
"actions": [
"pull",
"push"
]
}
]
}
```
3. Signature
The authorization server will produce a JOSE header and Claim Set with no
extraneous whitespace, i.e., the JOSE Header from above would be
```
{"typ":"JWT","alg":"ES256","kid":"PYYO:TEWU:V7JH:26JV:AQTZ:LJC3:SXVJ:XGHA:34F2:2LAQ:ZRMK:Z7Q6"}
```
and the Claim Set from above would be
```
{"iss":"auth.docker.com","sub":"jlhawn","aud":"registry.docker.com","exp":1415387315,"nbf":1415387015,"iat":1415387015,"jti":"tYJCO1c6cnyy7kAn0c7rKPgbV1H1bFws","access":[{"type":"repository","name":"samalba/my-app","actions":["push","pull"]}]}
```
The utf-8 representation of this JOSE header and Claim Set are then
url-safe base64 encoded (sans trailing '=' buffer), producing:
```
eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IlBZWU86VEVXVTpWN0pIOjI2SlY6QVFUWjpMSkMzOlNYVko6WEdIQTozNEYyOjJMQVE6WlJNSzpaN1E2In0
```
for the JOSE Header and
```
eyJpc3MiOiJhdXRoLmRvY2tlci5jb20iLCJzdWIiOiJqbGhhd24iLCJhdWQiOiJyZWdpc3RyeS5kb2NrZXIuY29tIiwiZXhwIjoxNDE1Mzg3MzE1LCJuYmYiOjE0MTUzODcwMTUsImlhdCI6MTQxNTM4NzAxNSwianRpIjoidFlKQ08xYzZjbnl5N2tBbjBjN3JLUGdiVjFIMWJGd3MiLCJhY2Nlc3MiOlt7InR5cGUiOiJyZXBvc2l0b3J5IiwibmFtZSI6InNhbWFsYmEvbXktYXBwIiwiYWN0aW9ucyI6WyJwdXNoIl19XX0
```
for the Claim Set. These two are concatenated using a '.' character,
yielding the string:
```
eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IlBZWU86VEVXVTpWN0pIOjI2SlY6QVFUWjpMSkMzOlNYVko6WEdIQTozNEYyOjJMQVE6WlJNSzpaN1E2In0.eyJpc3MiOiJhdXRoLmRvY2tlci5jb20iLCJzdWIiOiJqbGhhd24iLCJhdWQiOiJyZWdpc3RyeS5kb2NrZXIuY29tIiwiZXhwIjoxNDE1Mzg3MzE1LCJuYmYiOjE0MTUzODcwMTUsImlhdCI6MTQxNTM4NzAxNSwianRpIjoidFlKQ08xYzZjbnl5N2tBbjBjN3JLUGdiVjFIMWJGd3MiLCJhY2Nlc3MiOlt7InR5cGUiOiJyZXBvc2l0b3J5IiwibmFtZSI6InNhbWFsYmEvbXktYXBwIiwiYWN0aW9ucyI6WyJwdXNoIl19XX0
```
This is then used as the payload to a the `ES256` signature algorithm
specified in the JOSE header and specified fully in [Section 3.4 of the JSON Web Algorithms (JWA)
draft specification](https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-38#section-3.4)
This example signature will use the following ECDSA key for the server:
```
{
"kty": "EC",
"crv": "P-256",
"kid": "PYYO:TEWU:V7JH:26JV:AQTZ:LJC3:SXVJ:XGHA:34F2:2LAQ:ZRMK:Z7Q6",
"d": "R7OnbfMaD5J2jl7GeE8ESo7CnHSBm_1N2k9IXYFrKJA",
"x": "m7zUpx3b-zmVE5cymSs64POG9QcyEpJaYCD82-549_Q",
"y": "dU3biz8sZ_8GPB-odm8Wxz3lNDr1xcAQQPQaOcr1fmc"
}
```
A resulting signature of the above payload using this key is:
```
QhflHPfbd6eVF4lM9bwYpFZIV0PfikbyXuLx959ykRTBpe3CYnzs6YBK8FToVb5R47920PVLrh8zuLzdCr9t3w
```
Concatenating all of these together with a `.` character gives the
resulting JWT:
```
eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IlBZWU86VEVXVTpWN0pIOjI2SlY6QVFUWjpMSkMzOlNYVko6WEdIQTozNEYyOjJMQVE6WlJNSzpaN1E2In0.eyJpc3MiOiJhdXRoLmRvY2tlci5jb20iLCJzdWIiOiJqbGhhd24iLCJhdWQiOiJyZWdpc3RyeS5kb2NrZXIuY29tIiwiZXhwIjoxNDE1Mzg3MzE1LCJuYmYiOjE0MTUzODcwMTUsImlhdCI6MTQxNTM4NzAxNSwianRpIjoidFlKQ08xYzZjbnl5N2tBbjBjN3JLUGdiVjFIMWJGd3MiLCJhY2Nlc3MiOlt7InR5cGUiOiJyZXBvc2l0b3J5IiwibmFtZSI6InNhbWFsYmEvbXktYXBwIiwiYWN0aW9ucyI6WyJwdXNoIl19XX0.QhflHPfbd6eVF4lM9bwYpFZIV0PfikbyXuLx959ykRTBpe3CYnzs6YBK8FToVb5R47920PVLrh8zuLzdCr9t3w
```
This can now be placed in an HTTP response and returned to the client to use to
authenticate to the audience service:
```
HTTP/1.1 200 OK
Content-Type: application/json
{"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IlBZWU86VEVXVTpWN0pIOjI2SlY6QVFUWjpMSkMzOlNYVko6WEdIQTozNEYyOjJMQVE6WlJNSzpaN1E2In0.eyJpc3MiOiJhdXRoLmRvY2tlci5jb20iLCJzdWIiOiJqbGhhd24iLCJhdWQiOiJyZWdpc3RyeS5kb2NrZXIuY29tIiwiZXhwIjoxNDE1Mzg3MzE1LCJuYmYiOjE0MTUzODcwMTUsImlhdCI6MTQxNTM4NzAxNSwianRpIjoidFlKQ08xYzZjbnl5N2tBbjBjN3JLUGdiVjFIMWJGd3MiLCJhY2Nlc3MiOlt7InR5cGUiOiJyZXBvc2l0b3J5IiwibmFtZSI6InNhbWFsYmEvbXktYXBwIiwiYWN0aW9ucyI6WyJwdXNoIl19XX0.QhflHPfbd6eVF4lM9bwYpFZIV0PfikbyXuLx959ykRTBpe3CYnzs6YBK8FToVb5R47920PVLrh8zuLzdCr9t3w"}
```
## Using the signed token
Once the client has a token, it will try the registry request again with the
token placed in the HTTP `Authorization` header like so:
```
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IkJWM0Q6MkFWWjpVQjVaOktJQVA6SU5QTDo1RU42Ok40SjQ6Nk1XTzpEUktFOkJWUUs6M0ZKTDpQT1RMIn0.eyJpc3MiOiJhdXRoLmRvY2tlci5jb20iLCJzdWIiOiJCQ0NZOk9VNlo6UUVKNTpXTjJDOjJBVkM6WTdZRDpBM0xZOjQ1VVc6NE9HRDpLQUxMOkNOSjU6NUlVTCIsImF1ZCI6InJlZ2lzdHJ5LmRvY2tlci5jb20iLCJleHAiOjE0MTUzODczMTUsIm5iZiI6MTQxNTM4NzAxNSwiaWF0IjoxNDE1Mzg3MDE1LCJqdGkiOiJ0WUpDTzFjNmNueXk3a0FuMGM3cktQZ2JWMUgxYkZ3cyIsInNjb3BlIjoiamxoYXduOnJlcG9zaXRvcnk6c2FtYWxiYS9teS1hcHA6cHVzaCxwdWxsIGpsaGF3bjpuYW1lc3BhY2U6c2FtYWxiYTpwdWxsIn0.Y3zZSwaZPqy4y9oRBVRImZyv3m_S9XDHF1tWwN7mL52C_IiA73SJkWVNsvNqpJIn5h7A2F8biv_S2ppQ1lgkbw
```
This is also described in [Section 2.1 of RFC 6750: The OAuth 2.0 Authorization Framework: Bearer Token Usage](https://tools.ietf.org/html/rfc6750#section-2.1)
## Verifying the token
The registry must now verify the token presented by the user by inspecting the
claim set within. The registry will:
- Ensure that the issuer (`iss` claim) is an authority it trusts.
- Ensure that the registry identifies as the audience (`aud` claim).
- Check that the current time is between the `nbf` and `exp` claim times.
- If enforcing single-use tokens, check that the JWT ID (`jti` claim) value has
not been seen before.
- To enforce this, the registry may keep a record of `jti`s it has seen for
up to the `exp` time of the token to prevent token replays.
- Check the `access` claim value and use the identified resources and the list
of actions authorized to determine whether the token grants the required
level of access for the operation the client is attempting to perform.
- Verify that the signature of the token is valid.
If any of these requirements are not met, the registry will return a
`403 Forbidden` response to indicate that the token is invalid.
**Note**: it is only at this point in the workflow that an authorization error
may occur. The token server should *not* return errors when the user does not
have the requested authorization. Instead, the returned token should indicate
whatever of the requested scope the client does have (the intersection of
requested and granted access). If the token does not supply proper
authorization then the registry will return the appropriate error.
At no point in this process should the registry need to call back to the
authorization server. The registry only needs to be supplied with the trusted
public keys to verify the token signatures.

View file

@ -0,0 +1,190 @@
---
title: "Oauth2 Token Authentication"
description: "Specifies the Docker Registry v2 authentication"
keywords: registry, on-prem, images, tags, repository, distribution, oauth2, advanced
---
# Docker Registry v2 authentication using OAuth2
This document describes support for the OAuth2 protocol within the authorization
server. [RFC6749](https://tools.ietf.org/html/rfc6749) should be used as a
reference for the protocol and HTTP endpoints described here.
**Note**: Not all token servers implement oauth2. If the request to the endpoint
returns `404` using the HTTP `POST` method, refer to
[Token Documentation](token.md) for using the HTTP `GET` method supported by all
token servers.
## Refresh token format
The format of the refresh token is completely opaque to the client and should be
determined by the authorization server. The authorization should ensure the
token is sufficiently long and is responsible for storing any information about
long-lived tokens which may be needed for revoking. Any information stored
inside the token will not be extracted and presented by clients.
## Getting a token
POST /token
#### Headers
Content-Type: application/x-www-form-urlencoded
#### Post parameters
<dl>
<dt>
<code>grant_type</code>
</dt>
<dd>
(REQUIRED) Type of grant used to get token. When getting a refresh token
using credentials this type should be set to "password" and have the
accompanying username and password parameters. Type "authorization_code"
is reserved for future use for authenticating to an authorization server
without having to send credentials directly from the client. When
requesting an access token with a refresh token this should be set to
"refresh_token".
</dd>
<dt>
<code>service</code>
</dt>
<dd>
(REQUIRED) The name of the service which hosts the resource to get
access for. Refresh tokens will only be good for getting tokens for
this service.
</dd>
<dt>
<code>client_id</code>
</dt>
<dd>
(REQUIRED) String identifying the client. This client_id does not need
to be registered with the authorization server but should be set to a
meaningful value in order to allow auditing keys created by unregistered
clients. Accepted syntax is defined in
<a href="https://tools.ietf.org/html/rfc6749#appendix-A.1" rel="noopener noreferrer nofollow" target="_blank">RFC6749 Appendix A.1</a>.
</dd>
<dt>
<code>access_type</code>
</dt>
<dd>
(OPTIONAL) Access which is being requested. If "offline" is provided
then a refresh token will be returned. The default is "online" only
returning short lived access token. If the grant type is "refresh_token"
this will only return the same refresh token and not a new one.
</dd>
<dt>
<code>scope</code>
</dt>
<dd>
(OPTIONAL) The resource in question, formatted as one of the space-delimited
entries from the <code>scope</code> parameters from the <code>WWW-Authenticate</code> header
shown above. This query parameter should only be specified once but may
contain multiple scopes using the scope list format defined in the scope
grammar. If multiple <code>scope</code> is provided from
<code>WWW-Authenticate</code> header the scopes should first be
converted to a scope list before requesting the token. The above example
would be specified as: <code>scope=repository:samalba/my-app:push</code>.
When requesting a refresh token the scopes may be empty since the
refresh token will not be limited by this scope, only the provided short
lived access token will have the scope limitation.
</dd>
<dt>
<code>refresh_token</code>
</dt>
<dd>
(OPTIONAL) The refresh token to use for authentication when grant type "refresh_token" is used.
</dd>
<dt>
<code>username</code>
</dt>
<dd>
(OPTIONAL) The username to use for authentication when grant type "password" is used.
</dd>
<dt>
<code>password</code>
</dt>
<dd>
(OPTIONAL) The password to use for authentication when grant type "password" is used.
</dd>
</dl>
#### Response fields
<dl>
<dt>
<code>access_token</code>
</dt>
<dd>
(REQUIRED) An opaque <code>Bearer</code> token that clients should
supply to subsequent requests in the <code>Authorization</code> header.
This token should not be attempted to be parsed or understood by the
client but treated as opaque string.
</dd>
<dt>
<code>scope</code>
</dt>
<dd>
(REQUIRED) The scope granted inside the access token. This may be the
same scope as requested or a subset. This requirement is stronger than
specified in <a href="https://tools.ietf.org/html/rfc6749#section-4.2.2" rel="noopener noreferrer nofollow" target="_blank">RFC6749 Section 4.2.2</a>
by strictly requiring the scope in the return value.
</dd>
<dt>
<code>expires_in</code>
</dt>
<dd>
(REQUIRED) The duration in seconds since the token was issued that it
will remain valid. When omitted, this defaults to 60 seconds. For
compatibility with older clients, a token should never be returned with
less than 60 seconds to live.
</dd>
<dt>
<code>issued_at</code>
</dt>
<dd>
(Optional) The <a href="https://www.ietf.org/rfc/rfc3339.txt" rel="noopener noreferrer nofollow" target="_blank">RFC3339</a>-serialized UTC
standard time at which a given token was issued. If <code>issued_at</code> is omitted, the
expiration is from when the token exchange completed.
</dd>
<dt>
<code>refresh_token</code>
</dt>
<dd>
(Optional) Token which can be used to get additional access tokens for
the same subject with different scopes. This token should be kept secure
by the client and only sent to the authorization server which issues
bearer tokens. This field will only be set when `access_type=offline` is
provided in the request.
</dd>
</dl>
#### Example getting refresh token
```
POST /token HTTP/1.1
Host: auth.docker.io
Content-Type: application/x-www-form-urlencoded
grant_type=password&username=johndoe&password=A3ddj3w&service=hub.docker.io&client_id=dockerengine&access_type=offline
HTTP/1.1 200 OK
Content-Type: application/json
{"refresh_token":"kas9Da81Dfa8","access_token":"eyJhbGciOiJFUzI1NiIsInR5","expires_in":900,"scope":""}
```
#### Example refreshing an Access Token
```
POST /token HTTP/1.1
Host: auth.docker.io
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token&refresh_token=kas9Da81Dfa8&service=registry-1.docker.io&client_id=dockerengine&scope=repository:samalba/my-app:pull,push
HTTP/1.1 200 OK
Content-Type: application/json
{"refresh_token":"kas9Da81Dfa8","access_token":"eyJhbGciOiJFUzI1NiIsInR5":"expires_in":900,"scope":"repository:samalba/my-app:pull,repository:samalba/my-app:push"}
```

View file

@ -0,0 +1,153 @@
---
title: "Token Scope Documentation"
description: "Describes the scope and access fields used for registry authorization tokens"
keywords: registry, on-prem, images, tags, repository, distribution, advanced, access, scope
---
# Docker Registry Token Scope and Access
Tokens used by the registry are always restricted what resources they may
be used to access, where those resources may be accessed, and what actions
may be done on those resources. Tokens always have the context of a user which
the token was originally created for. This document describes how these
restrictions are represented and enforced by the authorization server and
resource providers.
## Scope Components
### Subject (Authenticated User)
The subject represents the user for which a token is valid. Any actions
performed using an access token should be considered on behalf of the subject.
This is included in the `sub` field of access token JWT. A refresh token should
be limited to a single subject and only be able to give out access tokens for
that subject.
### Audience (Resource Provider)
The audience represents a resource provider which is intended to be able to
perform the actions specified in the access token. Any resource provider which
does not match the audience should not use that access token. The audience is
included in the `aud` field of the access token JWT. A refresh token should be
limited to a single audience and only be able to give out access tokens for that
audience.
### Resource Type
The resource type represents the type of resource which the resource name is
intended to represent. This type may be specific to a resource provider but must
be understood by the authorization server in order to validate the subject
is authorized for a specific resource.
#### Resource Class
> [!WARNING]
> Resource Class is deprecated and ignored.
> `repository` and `repository(plugin)` are considered equal when authorizing a token.
> Authorization services should no longer return scopes with a resource class.
The resource type might have a resource class which further classifies the
the resource name within the resource type. A class is not required and
is specific to the resource type.
#### Example Resource Types
- `repository` - represents a single repository within a registry. A
repository may represent many manifest or content blobs, but the resource type
is considered the collections of those items. Actions which may be performed on
a `repository` are `pull` for accessing the collection and `push` for adding to
it. By default the `repository` type has the class of `image`.
- `repository(plugin)` - represents a single repository of plugins within a
registry. A plugin repository has the same content and actions as a repository.
- `registry` - represents the entire registry. Used for administrative actions
or lookup operations that span an entire registry.
### Resource Name
The resource name represent the name which identifies a resource for a resource
provider. A resource is identified by this name and the provided resource type.
An example of a resource name would be the name component of an image tag, such
as "samalba/myapp" or "hostname/samalba/myapp".
### Resource Actions
The resource actions define the actions which the access token allows to be
performed on the identified resource. These actions are type specific but will
normally have actions identifying read and write access on the resource. Example
for the `repository` type are `pull` for read access and `push` for write
access.
## Authorization Server Use
Each access token request may include a scope and an audience. The subject is
always derived from the passed in credentials or refresh token. When using
a refresh token the passed in audience must match the audience defined for
the refresh token. The audience (resource provider) is provided using the
`service` field. Multiple resource scopes may be provided using multiple `scope`
fields on the `GET` request. The `POST` request only takes in a single
`scope` field but may use a space to separate a list of multiple resource
scopes.
### Resource Scope Grammar
```
scope := resourcescope [ ' ' resourcescope ]*
resourcescope := resourcetype ":" resourcename ":" action [ ',' action ]*
resourcetype := resourcetypevalue [ '(' resourcetypevalue ')' ]
resourcetypevalue := /[a-z0-9]+/
resourcename := [ hostname '/' ] component [ '/' component ]*
hostname := hostcomponent ['.' hostcomponent]* [':' port-number]
hostcomponent := /([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])/
port-number := /[0-9]+/
action := /[a-z]*/
component := alpha-numeric [ separator alpha-numeric ]*
alpha-numeric := /[a-z0-9]+/
separator := /[_.]|__|[-]*/
```
Full reference grammar is defined
[here](https://pkg.go.dev/github.com/distribution/distribution/reference). Currently
the scope name grammar is a subset of the reference grammar.
> **NOTE:** that the `resourcename` may contain one `:` due to a possible port
> number in the hostname component of the `resourcename`, so a naive
> implementation that interprets the first three `:`-delimited tokens of a
> `scope` to be the `resourcetype`, `resourcename`, and a list of `action`
> would be insufficient.
## Resource Provider Use
Once a resource provider has verified the authenticity of the scope through
JWT access token verification, the resource provider must ensure that scope
satisfies the request. The resource provider should match the given audience
according to name or URI the resource provider uses to identify itself. Any
denial based on subject is not defined here and is up to resource provider, the
subject is mainly provided for audit logs and any other user-specific rules
which may need to be provided but are not defined by the authorization server.
The resource provider must ensure that ANY resource being accessed as the
result of a request has the appropriate access scope. Both the resource type
and resource name must match the accessed resource and an appropriate action
scope must be included.
When appropriate authorization is not provided either due to lack of scope
or missing token, the resource provider to return a `WWW-AUTHENTICATE` HTTP
header with the `realm` as the authorization server, the `service` as the
expected audience identifying string, and a `scope` field for each required
resource scope to complete the request.
## JWT Access Tokens
Each JWT access token may only have a single subject and audience but multiple
resource scopes. The subject and audience are put into standard JWT fields
`sub` and `aud`. The resource scope is put into the `access` field. The
structure of the access field can be seen in the
[jwt documentation](jwt.md).
## Refresh Tokens
A refresh token must be defined for a single subject and audience. Further
restricting scope to specific type, name, and actions combinations should be
done by fetching an access token using the refresh token. Since the refresh
token is not scoped to specific resources for an audience, extra care should
be taken to only use the refresh token to negotiate new access tokens directly
with the authorization server, and never with a resource provider.

View file

@ -0,0 +1,250 @@
---
title: "Token Authentication Specification"
description: "Specifies the Docker Registry v2 authentication"
keywords: registry, on-prem, images, tags, repository, distribution, Bearer authentication, advanced
---
# Docker Registry v2 authentication via central service
This document outlines the v2 Docker registry authentication scheme:
![v2 registry auth](../images/v2-registry-auth.png)
1. Attempt to begin a push/pull operation with the registry.
2. If the registry requires authorization it will return a `401 Unauthorized`
HTTP response with information on how to authenticate.
3. The registry client makes a request to the authorization service for a
Bearer token.
4. The authorization service returns an opaque Bearer token representing the
client's authorized access.
5. The client retries the original request with the Bearer token embedded in
the request's Authorization header.
6. The Registry authorizes the client by validating the Bearer token and the
claim set embedded within it and begins the push/pull session as usual.
## Requirements
- Registry clients which can understand and respond to token auth challenges
returned by the resource server.
- An authorization server capable of managing access controls to their
resources hosted by any given service (such as repositories in a Docker
Registry).
- A Docker Registry capable of trusting the authorization server to sign tokens
which clients can use for authorization and the ability to verify these
tokens for single use or for use during a sufficiently short period of time.
## Authorization Server Endpoint Descriptions
The described server is meant to serve as a standalone access control manager
for resources hosted by other services which wish to authenticate and manage
authorizations using a separate access control manager.
A service like this is used by the official Docker Registry to authenticate
clients and verify their authorization to Docker image repositories.
As of Docker 1.6, the registry client within the Docker Engine has been updated
to handle such an authorization workflow.
## How to authenticate
Registry V1 clients first contact the index to initiate a push or pull. Under
the Registry V2 workflow, clients should contact the registry first. If the
registry server requires authentication it will return a `401 Unauthorized`
response with a `WWW-Authenticate` header detailing how to authenticate to this
registry.
For example, say I (username `jlhawn`) am attempting to push an image to the
repository `samalba/my-app`. For the registry to authorize this, I will need
`push` access to the `samalba/my-app` repository. The registry will first
return this response:
```
HTTP/1.1 401 Unauthorized
Content-Type: application/json
Docker-Distribution-Api-Version: registry/2.0
Www-Authenticate: Bearer realm="https://auth.docker.io/token",service="registry.docker.io",scope="repository:samalba/my-app:pull,push"
Date: Thu, 10 Sep 2015 19:32:31 GMT
Content-Length: 235
Strict-Transport-Security: max-age=31536000
{"errors":[{"code":"UNAUTHORIZED","message":"access to the requested resource is not authorized","detail":[{"Type":"repository","Name":"samalba/my-app","Action":"pull"},{"Type":"repository","Name":"samalba/my-app","Action":"push"}]}]}
```
Note the HTTP Response Header indicating the auth challenge:
```
Www-Authenticate: Bearer realm="https://auth.docker.io/token",service="registry.docker.io",scope="repository:samalba/my-app:pull,push"
```
This format is documented in [Section 3 of RFC 6750: The OAuth 2.0 Authorization Framework: Bearer Token Usage](https://tools.ietf.org/html/rfc6750#section-3)
This challenge indicates that the registry requires a token issued by the
specified token server and that the request the client is attempting will
need to include sufficient access entries in its claim set. To respond to this
challenge, the client will need to make a `GET` request to the URL
`https://auth.docker.io/token` using the `service` and `scope` values from the
`WWW-Authenticate` header.
## Requesting a Token
Defines getting a bearer and refresh token using the token endpoint.
#### Query Parameters
<dl>
<dt>
<code>service</code>
</dt>
<dd>
The name of the service which hosts the resource.
</dd>
<dt>
<code>offline_token</code>
</dt>
<dd>
Whether to return a refresh token along with the bearer token. A refresh
token is capable of getting additional bearer tokens for the same
subject with different scopes. The refresh token does not have an
expiration and should be considered completely opaque to the client.
</dd>
<dt>
<code>client_id</code>
</dt>
<dd>
String identifying the client. This client_id does not need
to be registered with the authorization server but should be set to a
meaningful value in order to allow auditing keys created by unregistered
clients. Accepted syntax is defined in
<a href="https://tools.ietf.org/html/rfc6749#appendix-A.1" rel="noopener noreferrer nofollow" target="_blank">RFC6749 Appendix A.1</a>.
</dd>
<dt>
<code>scope</code>
</dt>
<dd>
The resource in question, formatted as one of the space-delimited
entries from the <code>scope</code> parameters from the <code>WWW-Authenticate</code> header
shown above. This query parameter should be specified multiple times if
there is more than one <code>scope</code> entry from the <code>WWW-Authenticate</code>
header. The above example would be specified as:
<code>scope=repository:samalba/my-app:push</code>. The scope field may
be empty to request a refresh token without providing any resource
permissions to the returned bearer token.
</dd>
</dl>
#### Token Response Fields
<dl>
<dt>
<code>token</code>
</dt>
<dd>
An opaque <code>Bearer</code> token that clients should supply to subsequent
requests in the <code>Authorization</code> header.
</dd>
<dt>
<code>access_token</code>
</dt>
<dd>
For compatibility with OAuth 2.0, we will also accept <code>token</code> under the name
<code>access_token</code>. At least one of these fields <b>must</b> be specified, but
both may also appear (for compatibility with older clients). When both are specified,
they should be equivalent; if they differ the client's choice is undefined.
</dd>
<dt>
<code>expires_in</code>
</dt>
<dd>
(Optional) The duration in seconds since the token was issued that it
will remain valid. When omitted, this defaults to 60 seconds. For
compatibility with older clients, a token should never be returned with
less than 60 seconds to live.
</dd>
<dt>
<code>issued_at</code>
</dt>
<dd>
(Optional) The <a href="https://www.ietf.org/rfc/rfc3339.txt">RFC3339</a>-serialized UTC
standard time at which a given token was issued. If <code>issued_at</code> is omitted, the
expiration is from when the token exchange completed.
</dd>
<dt>
<code>refresh_token</code>
</dt>
<dd>
(Optional) Token which can be used to get additional access tokens for
the same subject with different scopes. This token should be kept secure
by the client and only sent to the authorization server which issues
bearer tokens. This field will only be set when `offline_token=true` is
provided in the request.
</dd>
</dl>
#### Example
For this example, the client makes an HTTP GET request to the following URL:
```
https://auth.docker.io/token?service=registry.docker.io&scope=repository:samalba/my-app:pull,push
```
The token server should first attempt to authenticate the client using any
authentication credentials provided with the request. From Docker 1.11 the
Docker engine supports both Basic Authentication and [OAuth2](oauth.md) for
getting tokens. Docker 1.10 and before, the registry client in the Docker Engine
only supports Basic Authentication. If an attempt to authenticate to the token
server fails, the token server should return a `401 Unauthorized` response
indicating that the provided credentials are invalid.
Whether the token server requires authentication is up to the policy of that
access control provider. Some requests may require authentication to determine
access (such as pushing or pulling a private repository) while others may not
(such as pulling from a public repository).
After authenticating the client (which may simply be an anonymous client if
no attempt was made to authenticate), the token server must next query its
access control list to determine whether the client has the requested scope. In
this example request, if I have authenticated as user `jlhawn`, the token
server will determine what access I have to the repository `samalba/my-app`
hosted by the entity `registry.docker.io`.
Once the token server has determined what access the client has to the
resources requested in the `scope` parameter, it will take the intersection of
the set of requested actions on each resource and the set of actions that the
client has in fact been granted. If the client only has a subset of the
requested access **it must not be considered an error** as it is not the
responsibility of the token server to indicate authorization errors as part of
this workflow.
Continuing with the example request, the token server will find that the
client's set of granted access to the repository is `[pull, push]` which when
intersected with the requested access `[pull, push]` yields an equal set. If
the granted access set was found only to be `[pull]` then the intersected set
would only be `[pull]`. If the client has no access to the repository then the
intersected set would be empty, `[]`.
It is this intersected set of access which is placed in the returned token.
The server then constructs an implementation-specific token with this
intersected set of access, and returns it to the Docker client to use to
authenticate to the audience service (within the indicated window of time):
```
HTTP/1.1 200 OK
Content-Type: application/json
{"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IlBZWU86VEVXVTpWN0pIOjI2SlY6QVFUWjpMSkMzOlNYVko6WEdIQTozNEYyOjJMQVE6WlJNSzpaN1E2In0.eyJpc3MiOiJhdXRoLmRvY2tlci5jb20iLCJzdWIiOiJqbGhhd24iLCJhdWQiOiJyZWdpc3RyeS5kb2NrZXIuY29tIiwiZXhwIjoxNDE1Mzg3MzE1LCJuYmYiOjE0MTUzODcwMTUsImlhdCI6MTQxNTM4NzAxNSwianRpIjoidFlKQ08xYzZjbnl5N2tBbjBjN3JLUGdiVjFIMWJGd3MiLCJhY2Nlc3MiOlt7InR5cGUiOiJyZXBvc2l0b3J5IiwibmFtZSI6InNhbWFsYmEvbXktYXBwIiwiYWN0aW9ucyI6WyJwdXNoIl19XX0.QhflHPfbd6eVF4lM9bwYpFZIV0PfikbyXuLx959ykRTBpe3CYnzs6YBK8FToVb5R47920PVLrh8zuLzdCr9t3w", "expires_in": 3600,"issued_at": "2009-11-10T23:00:00Z"}
```
## Using the Bearer token
Once the client has a token, it will try the registry request again with the
token placed in the HTTP `Authorization` header like so:
```
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IkJWM0Q6MkFWWjpVQjVaOktJQVA6SU5QTDo1RU42Ok40SjQ6Nk1XTzpEUktFOkJWUUs6M0ZKTDpQT1RMIn0.eyJpc3MiOiJhdXRoLmRvY2tlci5jb20iLCJzdWIiOiJCQ0NZOk9VNlo6UUVKNTpXTjJDOjJBVkM6WTdZRDpBM0xZOjQ1VVc6NE9HRDpLQUxMOkNOSjU6NUlVTCIsImF1ZCI6InJlZ2lzdHJ5LmRvY2tlci5jb20iLCJleHAiOjE0MTUzODczMTUsIm5iZiI6MTQxNTM4NzAxNSwiaWF0IjoxNDE1Mzg3MDE1LCJqdGkiOiJ0WUpDTzFjNmNueXk3a0FuMGM3cktQZ2JWMUgxYkZ3cyIsInNjb3BlIjoiamxoYXduOnJlcG9zaXRvcnk6c2FtYWxiYS9teS1hcHA6cHVzaCxwdWxsIGpsaGF3bjpuYW1lc3BhY2U6c2FtYWxiYTpwdWxsIn0.Y3zZSwaZPqy4y9oRBVRImZyv3m_S9XDHF1tWwN7mL52C_IiA73SJkWVNsvNqpJIn5h7A2F8biv_S2ppQ1lgkbw
```
This is also described in [Section 2.1 of RFC 6750: The OAuth 2.0 Authorization Framework: Bearer Token Usage](https://tools.ietf.org/html/rfc6750#section-2.1)

View file

@ -0,0 +1,41 @@
---
title: Update deprecated schema image manifest version 2, v1 images
description: Update deprecated schema v1 iamges
keywords: registry, on-prem, images, tags, repository, distribution, api, advanced, manifest
---
## Image manifest version 2, schema 1
With the release of image manifest version 2, schema 2, image manifest version
2, schema 1 has been deprecated. This could lead to compatibility and
vulnerability issues in images that haven't been updated to image manifest
version 2, schema 2.
This page contains information on how to update from image manifest version 2,
schema 1. However, these instructions will not ensure your new image will run
successfully. There may be several other issues to troubleshoot that are
associated with the deprecated image manifest that will block your image from
running successfully. A list of possible methods to help update your image is
also included below.
### Update to image manifest version 2, schema 2
One way to upgrade an image from image manifest version 2, schema 1 to
schema 2 is to `docker pull` the image and then `docker push` the image with a
current version of Docker. Doing so will automatically convert the image to use
the latest image manifest specification.
Converting an image to image manifest version 2, schema 2 converts the
manifest format, but does not update the contents within the image. Images
using manifest version 2, schema 1 may contain unpatched vulnerabilities. We
recommend looking for an alternative image or rebuilding it.
### Update FROM statement
You can rebuild the image by updating the `FROM` statement in your
`Dockerfile`. If your image manifest is out-of-date, there is a chance the
image pulled from your `FROM` statement in your `Dockerfile` is also
out-of-date. See the [Dockerfile reference](https://docs.docker.com/engine/reference/builder/#from)
and the [Dockerfile best practices guide](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/)
for more information on how to update the `FROM` statement in your
`Dockerfile`.

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View file

@ -0,0 +1,30 @@
---
published: false
---
# Distribution API Implementations
This is a list of known implementations of the Distribution API spec.
## [Docker Distribution Registry](https://github.com/distribution/distribution)
Docker distribution is the reference implementation of the distribution API
specification. It aims to fully implement the entire specification.
### Releases
#### 2.0.1 (_in development_)
Implements API 2.0.1
_Known Issues_
- No resumable push support
- Content ranges ignored
- Blob upload status will always return a starting range of 0
#### 2.0.0
Implements API 2.0.0
_Known Issues_
- No resumable push support
- No PATCH implementation for blob upload
- Content ranges ignored

90
docs/content/spec/json.md Normal file
View file

@ -0,0 +1,90 @@
---
published: false
title: "Docker Distribution JSON Canonicalization"
description: "Explains registry JSON objects"
keywords: ["registry, service, images, repository, json"]
---
# Docker Distribution JSON Canonicalization
To provide consistent content hashing of JSON objects throughout Docker
Distribution APIs, the specification defines a canonical JSON format. Adopting
such a canonicalization also aids in caching JSON responses.
Note that protocols should not be designed to depend on identical JSON being
generated across different versions or clients. The canonicalization rules are
merely useful for caching and consistency.
## Rules
Compliant JSON should conform to the following rules:
1. All generated JSON should comply with [RFC
7159](http://www.ietf.org/rfc/rfc7159.txt).
2. Resulting "JSON text" shall always be encoded in UTF-8.
3. Unless a canonical key order is defined for a particular schema, object
keys shall always appear in lexically sorted order.
4. All whitespace between tokens should be removed.
5. No "trailing commas" are allowed in object or array definitions.
6. The angle brackets "<" and ">" are escaped to "\u003c" and "\u003e".
Ampersand "&" is escaped to "\u0026".
## Examples
The following is a simple example of a canonicalized JSON string:
```json
{"asdf":1,"qwer":[],"zxcv":[{},true,1000000000,"tyui"]}
```
## Reference
### Other Canonicalizations
The OLPC project specifies [Canonical
JSON](http://wiki.laptop.org/go/Canonical_JSON). While this is used in
[TUF](http://theupdateframework.com/), which may be used with other
distribution-related protocols, this alternative format has been proposed in
case the original source changes. Specifications complying with either this
specification or an alternative should explicitly call out the
canonicalization format. Except for key ordering, this specification is mostly
compatible.
### Go
In Go, the [`encoding/json`](http://golang.org/pkg/encoding/json/) library
will emit canonical JSON by default. Simply using `json.Marshal` will suffice
in most cases:
```go
incoming := map[string]interface{}{
"asdf": 1,
"qwer": []interface{}{},
"zxcv": []interface{}{
map[string]interface{}{},
true,
int(1e9),
"tyui",
},
}
canonical, err := json.Marshal(incoming)
if err != nil {
// ... handle error
}
```
To apply canonical JSON format spacing to an existing serialized JSON buffer, one
can use
[`json.Indent`](http://golang.org/src/encoding/json/indent.go?s=1918:1989#L65)
with the following arguments:
```go
incoming := getBytes()
var canonical bytes.Buffer
if err := json.Indent(&canonical, incoming, "", ""); err != nil {
// ... handle error
}
```

View file

@ -0,0 +1,292 @@
---
title: "Image Manifest V 2, Schema 2 "
description: "image manifest for the Registry."
keywords: registry, on-prem, images, tags, repository, distribution, api, advanced, manifest
---
# Image Manifest Version 2, Schema 2
This document outlines the format of the V2 image manifest, schema version 2.
The original (and provisional) image manifest for V2 (schema 1), was introduced
in the Docker daemon in the [v1.3.0
release](https://github.com/docker/docker/commit/9f482a66ab37ec396ac61ed0c00d59122ac07453)
and is specified in the [schema 1 manifest definition](manifest-v2-1.md)
This second schema version has two primary goals. The first is to allow
multi-architecture images, through a "fat manifest" which references image
manifests for platform-specific versions of an image. The second is to
move the Docker engine towards content-addressable images, by supporting
an image model where the image's configuration can be hashed to generate
an ID for the image.
# Media Types
The following media types are used by the manifest formats described here, and
the resources they reference:
- `application/vnd.docker.distribution.manifest.v2+json`: New image manifest format (schemaVersion = 2)
- `application/vnd.docker.distribution.manifest.list.v2+json`: Manifest list, aka "fat manifest"
- `application/vnd.docker.container.image.v1+json`: Container config JSON
- `application/vnd.docker.image.rootfs.diff.tar.gzip`: "Layer", as a gzipped tar
- `application/vnd.docker.image.rootfs.foreign.diff.tar.gzip`: "Layer", as a gzipped tar that should never be pushed
- `application/vnd.docker.plugin.v1+json`: Plugin config JSON
## Manifest List
The manifest list is the "fat manifest" which points to specific image manifests
for one or more platforms. Its use is optional, and relatively few images will
use one of these manifests. A client will distinguish a manifest list from an
image manifest based on the Content-Type returned in the HTTP response.
## *Manifest List* Field Descriptions
- **`schemaVersion`** *int*
This field specifies the image manifest schema version as an integer. This
schema uses the version `2`.
- **`mediaType`** *string*
The MIME type of the manifest list. This should be set to
`application/vnd.docker.distribution.manifest.list.v2+json`.
- **`manifests`** *array*
The manifests field contains a list of manifests for specific platforms.
Fields of an object in the manifests list are:
- **`mediaType`** *string*
The MIME type of the referenced object. This will generally be
`application/vnd.docker.distribution.manifest.v2+json`.
- **`size`** *int*
The size in bytes of the object. This field exists so that a client
will have an expected size for the content before validating. If the
length of the retrieved content does not match the specified length,
the content should not be trusted.
- **`digest`** *string*
The digest of the content, as defined by the
[Registry V2 HTTP API Specificiation](api.md#digest-parameter).
- **`platform`** *object*
The platform object describes the platform which the image in the
manifest runs on. A full list of valid operating system and architecture
values are listed in the [Go language documentation for `$GOOS` and
`$GOARCH`](https://golang.org/doc/install/source#environment)
- **`architecture`** *string*
The architecture field specifies the CPU architecture, for example
`amd64` or `ppc64le`.
- **`os`** *string*
The os field specifies the operating system, for example
`linux` or `windows`.
- **`os.version`** *string*
The optional os.version field specifies the operating system version,
for example `10.0.10586`.
- **`os.features`** *array*
The optional os.features field specifies an array of strings,
each listing a required OS feature (for example on Windows
`win32k`).
- **`variant`** *string*
The optional variant field specifies a variant of the CPU, for
example `v6` to specify a particular CPU variant of the ARM CPU.
- **`features`** *array*
The optional features field specifies an array of strings, each
listing a required CPU feature (for example `sse4` or `aes`).
## Example Manifest List
*Example showing a simple manifest list pointing to image manifests for two platforms:*
```json
{
"schemaVersion": 2,
"mediaType": "application/vnd.docker.distribution.manifest.list.v2+json",
"manifests": [
{
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"digest": "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f",
"size": 7143,
"platform": {
"architecture": "ppc64le",
"os": "linux"
}
},
{
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270",
"size": 7682,
"platform": {
"architecture": "amd64",
"os": "linux",
"features": [
"sse4"
]
}
}
]
}
```
# Image Manifest
The image manifest provides a configuration and a set of layers for a container
image. It's the direct replacement for the schema-1 manifest.
## *Image Manifest* Field Descriptions
- **`schemaVersion`** *int*
This field specifies the image manifest schema version as an integer. This
schema uses version `2`.
- **`mediaType`** *string*
The MIME type of the manifest. This should be set to
`application/vnd.docker.distribution.manifest.v2+json`.
- **`config`** *object*
The config field references a configuration object for a container, by
digest. This configuration item is a JSON blob that the runtime uses
to set up the container. This new schema uses a tweaked version
of this configuration to allow image content-addressability on the
daemon side.
Fields of a config object are:
- **`mediaType`** *string*
The MIME type of the referenced object. This should generally be
`application/vnd.docker.container.image.v1+json`.
- **`size`** *int*
The size in bytes of the object. This field exists so that a client
will have an expected size for the content before validating. If the
length of the retrieved content does not match the specified length,
the content should not be trusted.
- **`digest`** *string*
The digest of the content, as defined by the
[Registry V2 HTTP API Specificiation](api.md#digest-parameter).
- **`layers`** *array*
The layer list is ordered starting from the base image (opposite order of schema1).
Fields of an item in the layers list are:
- **`mediaType`** *string*
The MIME type of the referenced object. This should
generally be `application/vnd.docker.image.rootfs.diff.tar.gzip`.
Layers of type
`application/vnd.docker.image.rootfs.foreign.diff.tar.gzip` may be
pulled from a remote location but they should never be pushed.
- **`size`** *int*
The size in bytes of the object. This field exists so that a client
will have an expected size for the content before validating. If the
length of the retrieved content does not match the specified length,
the content should not be trusted.
- **`digest`** *string*
The digest of the content, as defined by the
[Registry V2 HTTP API Specificiation](api.md#digest-parameter).
- **`urls`** *array*
Provides a list of URLs from which the content may be fetched. Content
must be verified against the `digest` and `size`. This field is
optional and uncommon.
## Example Image Manifest
*Example showing an image manifest:*
```json
{
"schemaVersion": 2,
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"config": {
"mediaType": "application/vnd.docker.container.image.v1+json",
"digest": "sha256:b5b2b2c507a0944348e0303114d8d93aaaa081732b86451d9bce1f432a537bc7",
"size": 7023
},
"layers": [
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"digest": "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f",
"size": 32654
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"digest": "sha256:3c3a4604a545cdc127456d94e421cd355bca5b528f4a9c1905b15da2eb4a4c6b",
"size": 16724
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"digest": "sha256:ec4b8955958665577945c89419d1af06b5f7636b4ac3da7f12184802ad867736",
"size": 73109
}
]
}
```
# Backward compatibility
The registry will continue to accept uploads of manifests in both the old and
new formats.
When pushing images, clients which support the new manifest format should first
construct a manifest in the new format. If uploading this manifest fails,
presumably because the registry only supports the old format, the client may
fall back to uploading a manifest in the old format.
When pulling images, clients indicate support for this new version of the
manifest format by sending the
`application/vnd.docker.distribution.manifest.v2+json` and
`application/vnd.docker.distribution.manifest.list.v2+json` media types in an
`Accept` header when making a request to the `manifests` endpoint. Updated
clients should check the `Content-Type` header to see whether the manifest
returned from the endpoint is in the old format, or is an image manifest or
manifest list in the new format.
If the manifest being requested uses the new format, and the appropriate media
type is not present in an `Accept` header, the registry will assume that the
client cannot handle the manifest as-is, and rewrite it on the fly into the old
format. If the object that would otherwise be returned is a manifest list, the
registry will look up the appropriate manifest for the amd64 platform and
linux OS, rewrite that manifest into the old format if necessary, and return
the result to the client. If no suitable manifest is found in the manifest
list, the registry will return a 404 error.
One of the challenges in rewriting manifests to the old format is that the old
format involves an image configuration for each layer in the manifest, but the
new format only provides one image configuration. To work around this, the
registry will create synthetic image configurations for all layers except the
top layer. These image configurations will not result in runnable images on
their own, but only serve to fill in the parent chain in a compatible way.
The IDs in these synthetic configurations will be derived from hashes of their
respective blobs. The registry will create these configurations and their IDs
using the same scheme as Docker 1.10 when it creates a legacy manifest to push
to a registry which doesn't support the new format.

View file

@ -0,0 +1,7 @@
---
title: "Reference"
description: "Explains registry JSON objects"
keywords: registry, service, images, repository, json
type: "menu"
identifier: "smn_registry_ref"
---

View file

@ -0,0 +1,70 @@
---
description: Explains how to use storage drivers
keywords: registry, on-prem, images, tags, repository, distribution, storage drivers, advanced
redirect_from:
- /registry/storagedrivers/
title: Registry storage driver
---
This document describes the registry storage driver model, implementation, and explains how to contribute new storage drivers.
## Provided drivers
This storage driver package comes bundled with several drivers:
- [inmemory](inmemory.md): A temporary storage driver using a local inmemory map. This exists solely for reference and testing.
- [filesystem](filesystem.md): A local storage driver configured to use a directory tree in the local filesystem.
- [s3](s3.md): A driver storing objects in an Amazon Simple Storage Service (S3) bucket.
- [azure](azure.md): A driver storing objects in [Microsoft Azure Blob Storage](https://azure.microsoft.com/en-us/services/storage/).
- [gcs](gcs.md): A driver storing objects in a [Google Cloud Storage](https://cloud.google.com/storage/) bucket.
- oss: *NO LONGER SUPPORTED*
- swift: *NO LONGER SUPPORTED*
## Storage driver API
The storage driver API is designed to model a filesystem-like key/value storage in a manner abstract enough to support a range of drivers from the local filesystem to Amazon S3 or other distributed object storage systems.
Storage drivers are required to implement the `storagedriver.StorageDriver` interface provided in `storagedriver.go`, which includes methods for reading, writing, and deleting content, as well as listing child objects of a specified prefix key.
Storage drivers are intended to be written in Go, providing compile-time
validation of the `storagedriver.StorageDriver` interface.
## Driver selection and configuration
The preferred method of selecting a storage driver is using the `StorageDriverFactory` interface in the `storagedriver/factory` package. These factories provide a common interface for constructing storage drivers with a parameters map. The factory model is based on the [Register](https://golang.org/pkg/database/sql/#Register) and [Open](https://golang.org/pkg/database/sql/#Open) methods in the builtin [database/sql](https://golang.org/pkg/database/sql) package.
Storage driver factories may be registered by name using the
`factory.Register` method, and then later invoked by calling `factory.Create`
with a driver name and parameters map. If no such storage driver can be found,
`factory.Create` returns an `InvalidStorageDriverError`.
## Driver contribution
New storage drivers are not currently being accepted.
See https://github.com/distribution/distribution/issues/3988 for discussion.
There are forks of this repo that implement custom storage drivers.
These are not supported by the OCI distribution project.
The known forks are:
- Storj DCS: https://github.com/storj/docker-registry
- HuaweiCloud OBS: https://github.com/setoru/distribution/tree/obs
- us3: https://github.com/lambertxiao/distribution/tree/main
- Baidu BOS: https://github.com/dolfly/distribution/tree/bos
- HDFS: https://github.com/haosdent/distribution/tree/master
### Writing new storage drivers
To create a valid storage driver, one must implement the
`storagedriver.StorageDriver` interface and make sure to expose this driver
via the factory system.
#### Registering
Storage drivers should call `factory.Register` with their driver name in an `init` method, allowing callers of `factory.New` to construct instances of this driver without requiring modification of imports throughout the codebase.
## Testing
Storage driver test suites are provided in
`storagedriver/testsuites/testsuites.go` and may be used for any storage
driver written in Go. Tests can be registered using the `RegisterSuite`
function, which run the same set of tests for any registered drivers.

View file

@ -0,0 +1,26 @@
---
description: Explains how to use the Azure storage drivers
keywords: registry, service, driver, images, storage, azure
title: Microsoft Azure storage driver
---
An implementation of the `storagedriver.StorageDriver` interface which uses [Microsoft Azure Blob Storage](https://azure.microsoft.com/en-us/services/storage/) for object storage.
## Parameters
| Parameter | Required | Description |
|:-----------------------------------|:---------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `accountname` | yes | Name of the Azure Storage Account. |
| `accountkey` | yes | Primary or Secondary Key for the Storage Account. |
| `container` | yes | Name of the Azure root storage container in which all registry data is stored. Must comply the storage container name [requirements](https://docs.microsoft.com/rest/api/storageservices/fileservices/naming-and-referencing-containers--blobs--and-metadata). For example, if your url is `https://myaccount.blob.core.windows.net/myblob` use the container value of `myblob`.|
| `realm` | no | Domain name suffix for the Storage Service API endpoint. For example realm for "Azure in China" would be `core.chinacloudapi.cn` and realm for "Azure Government" would be `core.usgovcloudapi.net`. By default, this is `core.windows.net`. |
| `copy_status_poll_max_retry` | no | Max retry number for polling of copy operation status. Retries use a simple backoff algorithm where each retry number is multiplied by `copy_status_poll_delay`, and this number is used as the delay. Set to -1 to disable retries and abort if the copy does not complete immediately. Defaults to 5. |
| `copy_status_poll_delay` | no | Time to wait between retries for polling of copy operation status. This time is multiplied by N on each retry, where N is the retry number. Defaults to 100ms |
## Related information
* To get information about
[azure-blob-storage](https://azure.microsoft.com/en-us/services/storage/), visit
the Microsoft website.
* You can use Microsoft's [Blob Service REST API](https://docs.microsoft.com/en-us/rest/api/storageservices/Blob-Service-REST-API) to [create a storage container](https://docs.microsoft.com/en-us/rest/api/storageservices/Create-Container).

View file

@ -0,0 +1,19 @@
---
description: Explains how to use the filesystem storage drivers
keywords: registry, service, driver, images, storage, filesystem
title: Filesystem storage driver
---
An implementation of the `storagedriver.StorageDriver` interface which uses the local filesystem.
## Parameters
* `rootdirectory`: (optional) The absolute path to a root directory tree in which
to store all registry files. The registry stores all its data here so make sure
there is adequate space available. Defaults to `/var/lib/registry`. If the directory
does not exist, it will be created honoring [`umask`](https://man7.org/linux/man-pages/man2/umask.2.html)
bits. If `umask` bits are not set, the resulting permission will be `0777`.
* `maxthreads`: (optional) The maximum number of simultaneous blocking filesystem
operations permitted within the registry. Each operation spawns a new thread and
may cause thread exhaustion issues if many are done in parallel. Defaults to
`100`, and cannot be lower than `25`.

View file

@ -0,0 +1,19 @@
---
description: Explains how to use the Google Cloud Storage drivers
keywords: registry, service, driver, images, storage, gcs, google, cloud
title: Google Cloud Storage driver
---
An implementation of the `storagedriver.StorageDriver` interface which uses Google Cloud for object storage.
## Parameters
| Parameter | Required | Description |
|:--------------|:---------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `bucket` | yes | The name of your Google Cloud Storage bucket where you wish to store objects (needs to already be created prior to driver initialization). |
| `keyfile` | no | A private service account key file in JSON format used for [Service Account Authentication](https://cloud.google.com/storage/docs/authentication#service_accounts). |
| `rootdirectory` | no | The root directory tree in which all registry files are stored. Defaults to the empty string (bucket root). If a prefix is used, the path `bucketname/<prefix>` has to be pre-created before starting the registry. The prefix is applied to all Google Cloud Storage keys to allow you to segment data in your bucket if necessary.|
| `chunksize` | no (default 5242880) | This is the chunk size used for uploading large blobs, must be a multiple of 256*1024. |
**Note:** Instead of a key file you can use [Google Application Default Credentials](https://developers.google.com/identity/protocols/application-default-credentials).

View file

@ -0,0 +1,16 @@
---
description: Explains how to use the in-memory storage drivers
keywords: registry, service, driver, images, storage, in-memory
title: In-memory storage driver (testing only)
---
For purely tests purposes, you can use the `inmemory` storage driver. This
driver is an implementation of the `storagedriver.StorageDriver` interface which
uses local memory for object storage. If you would like to run a registry from
volatile memory, use the [`filesystem` driver](filesystem.md) on a ramdisk.
**IMPORTANT**: This storage driver *does not* persist data across runs. This is why it is only suitable for testing. *Never* use this driver in production.
## Parameters
None

View file

@ -0,0 +1,165 @@
---
description: Explains how to use the S3 storage drivers
keywords: registry, service, driver, images, storage, S3
title: S3 storage driver
---
An implementation of the `storagedriver.StorageDriver` interface which uses
Amazon S3 or S3 compatible services for object storage.
## Parameters
| Parameter | Required | Description |
|:--------------|:---------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `accesskey` | no | Your AWS Access Key. If you use [IAM roles](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html), omit to fetch temporary credentials from IAM. |
| `secretkey` | no | Your AWS Secret Key. If you use [IAM roles](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html), omit to fetch temporary credentials from IAM. |
| `region` | yes | The AWS region in which your bucket exists. |
| `regionendpoint` | no | Endpoint for S3 compatible storage services (Minio, etc). |
| `forcepathstyle` | no | To enable path-style addressing when the value is set to `true`. The default is `true`. |
| `bucket` | yes | The bucket name in which you want to store the registry's data. |
| `encrypt` | no | Specifies whether the registry stores the image in encrypted format or not. A boolean value. The default is `false`. |
| `keyid` | no | Optional KMS key ID to use for encryption (encrypt must be true, or this parameter is ignored). The default is `none`. |
| `secure` | no | Indicates whether to use HTTPS instead of HTTP. A boolean value. The default is `true`. |
| `skipverify` | no | Skips TLS verification when the value is set to `true`. The default is `false`. |
| `v4auth` | no | Indicates whether the registry uses Version 4 of AWS's authentication. The default is `true`. |
| `chunksize` | no | The S3 API requires multipart upload chunks to be at least 5MB. This value should be a number that is larger than 5 * 1024 * 1024.|
| `rootdirectory` | no | This is a prefix that is applied to all S3 keys to allow you to segment data in your bucket if necessary. |
| `storageclass` | no | The S3 storage class applied to each registry file. The default is `STANDARD`. |
| `objectacl` | no | The S3 Canned ACL for objects. The default value is "private". |
| `loglevel` | no | The log level for the S3 client. The default value is `off`. |
> **Note** You can provide empty strings for your access and secret keys to run the driver
> on an ec2 instance and handles authentication with the instance's credentials. If you
> use [IAM roles](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html),
> omit these keys to fetch temporary credentials from IAM.
`region`: The name of the aws region in which you would like to store objects (for example `us-east-1`). For a list of regions, see [Regions, Availability Zones, and Local Zones](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html).
`regionendpoint`: (optional) Endpoint URL for S3 compatible APIs. This should not be provided when using Amazon S3.
`forcepathstyle`: (optional) The force path style for S3 compatible APIs. Some manufacturers only support force path style, while others only support DNS based bucket routing. Amazon S3 supports both.
`bucket`: The name of your S3 bucket where you wish to store objects. The bucket must exist prior to the driver initialization.
`encrypt`: (optional) Whether you would like your data encrypted on the server side (defaults to false if not specified).
`keyid`: (optional) Whether you would like your data encrypted with this KMS key ID (defaults to none if not specified, is ignored if encrypt is not true).
`secure`: (optional) Whether you would like to transfer data to the bucket over ssl or not. Defaults to true (meaning transferring over ssl) if not specified. While setting this to false improves performance, it is not recommended due to security concerns.
`v4auth`: (optional) Whether you would like to use aws signature version 4 with your requests. This defaults to `false` if not specified. The `eu-central-1` region does not work with version 2 signatures, so the driver errors out if initialized with this region and v4auth set to `false`.
`chunksize`: (optional) The default part size for multipart uploads (performed by WriteStream) to S3. The default is 10 MB. Keep in mind that the minimum part size for S3 is 5MB. Depending on the speed of your connection to S3, a larger chunk size may result in better performance; faster connections benefit from larger chunk sizes.
`rootdirectory`: (optional) The root directory tree in which all registry files are stored. Defaults to the empty string (bucket root).
`storageclass`: (optional) The storage class applied to each registry file. Defaults to STANDARD. Valid options are STANDARD and REDUCED_REDUNDANCY.
`objectacl`: (optional) The canned object ACL to be applied to each registry object. Defaults to `private`. If you are using a bucket owned by another AWS account, it is recommended that you set this to `bucket-owner-full-control` so that the bucket owner can access your objects. Other valid options are available in the [AWS S3 documentation](http://docs.aws.amazon.com/AmazonS3/latest/dev/acl-overview.html#canned-acl).
`loglevel`: (optional) Valid values are: `off` (default), `debug`, `debugwithsigning`, `debugwithhttpbody`, `debugwithrequestretries`, `debugwithrequesterrors` and `debugwitheventstreambody`. See the [AWS SDK for Go API reference](https://docs.aws.amazon.com/sdk-for-go/api/aws/#LogLevelType) for details.
## S3 permission scopes
The following AWS policy is required by the registry for push and pull. Make sure to replace `S3_BUCKET_NAME` with the name of your bucket.
```
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:ListBucket",
"s3:GetBucketLocation",
"s3:ListBucketMultipartUploads"
],
"Resource": "arn:aws:s3:::S3_BUCKET_NAME"
},
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:DeleteObject",
"s3:ListMultipartUploadParts",
"s3:AbortMultipartUpload"
],
"Resource": "arn:aws:s3:::S3_BUCKET_NAME/*"
}
]
}
```
See [the S3 policy documentation](http://docs.aws.amazon.com/AmazonS3/latest/dev/mpuAndPermissions.html) for more details.
# CloudFront as Middleware with S3 backend
## Use Case
Adding CloudFront as a middleware for your S3 backed registry can dramatically
improve pull times. Your registry can retrieve your images
from edge servers, rather than the geographically limited location of your S3
bucket. The farther your registry is from your bucket, the more improvements are
possible. See [Amazon CloudFront](https://aws.amazon.com/cloudfront/details/).
An alternative method for CloudFront that requires less configuration and will use
the same edge servers is [S3 Transfer Acceleration](https://docs.aws.amazon.com/AmazonS3/latest/dev/transfer-acceleration.html).
Please check acceleration [Requirements](https://docs.aws.amazon.com/AmazonS3/latest/dev/transfer-acceleration.html#transfer-acceleration-requirements)
to see whether you need CloudFront or S3 Transfer Acceleration.
## Configuring CloudFront for Distribution
If you are unfamiliar with creating a CloudFront distribution, see [Getting
Started with
Cloudfront](http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/GettingStarted.html).
Defaults can be kept in most areas except:
### Origin:
- The CloudFront distribution must be created such that the `Origin Path` is set
to the directory level of the root "docker" key in S3. If your registry exists
on the root of the bucket, this path should be left blank.
- For private S3 buckets, you must set `Restrict Bucket Access` to `Yes`. See
the [CloudFront documentation](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/PrivateContent.html).
### Behaviors:
- Viewer Protocol Policy: HTTPS Only
- Allowed HTTP Methods: GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE
- Cached HTTP Methods: OPTIONS (checked)
- Restrict Viewer Access (Use Signed URLs or Signed Cookies): Yes
- Trusted Signers: Self (Can add other accounts as long as you have access to CloudFront Key Pairs for those additional accounts)
## Registry configuration
Here the `middleware` option is used. It is still important to keep the
`storage` option, because CloudFront only handles `pull` actions; `push` actions
are still directly written to S3.
The following example shows a minimum configuration:
```yaml
...
storage:
s3:
region: us-east-1
bucket: docker.myregistry.com
middleware:
storage:
- name: cloudfront
options:
baseurl: https://abcdefghijklmn.cloudfront.net/
privatekey: /etc/docker/cloudfront/pk-ABCEDFGHIJKLMNOPQRST.pem
keypairid: ABCEDFGHIJKLMNOPQRST
...
```
## CloudFront Key-Pair
A CloudFront key-pair is required for all AWS accounts needing access to your
CloudFront distribution. You must have access to your AWS account's root credentials to create the required Cloudfront keypair. For information, see [Creating CloudFront Key
Pairs](http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-trusted-signers.html#private-content-creating-cloudfront-key-pairs).