forked from TrueCloudLab/distribution
docs: add hugo site
Signed-off-by: David Karlsson <35727626+dvdksn@users.noreply.github.com>
This commit is contained in:
parent
f7b3869062
commit
e2ae76f1f2
291 changed files with 2833 additions and 2 deletions
63
docs/content/_index.md
Normal file
63
docs/content/_index.md
Normal 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).
|
73
docs/content/about/_index.md
Normal file
73
docs/content/about/_index.md
Normal 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)
|
52
docs/content/about/architecture.md
Normal file
52
docs/content/about/architecture.md
Normal 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.
|
78
docs/content/about/compatibility.md
Normal file
78
docs/content/about/compatibility.md
Normal 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.
|
||||
|
1215
docs/content/about/configuration.md
Normal file
1215
docs/content/about/configuration.md
Normal file
File diff suppressed because it is too large
Load diff
577
docs/content/about/deploying.md
Normal file
577
docs/content/about/deploying.md
Normal 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)
|
20
docs/content/about/deprecated.md
Normal file
20
docs/content/about/deprecated.md
Normal 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.
|
124
docs/content/about/garbage-collection.md
Normal file
124
docs/content/about/garbage-collection.md
Normal 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
|
||||
```
|
68
docs/content/about/glossary.md
Normal file
68
docs/content/about/glossary.md
Normal 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>
|
14
docs/content/about/help.md
Normal file
14
docs/content/about/help.md
Normal 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).
|
165
docs/content/about/insecure.md
Normal file
165
docs/content/about/insecure.md
Normal 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.
|
348
docs/content/about/notifications.md
Normal file
348
docs/content/about/notifications.md
Normal 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).
|
||||
|
||||

|
||||
|
||||
## 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.
|
1
docs/content/images/notifications.gliffy
Normal file
1
docs/content/images/notifications.gliffy
Normal file
File diff suppressed because one or more lines are too long
BIN
docs/content/images/notifications.png
Normal file
BIN
docs/content/images/notifications.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 20 KiB |
1
docs/content/images/notifications.svg
Normal file
1
docs/content/images/notifications.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 31 KiB |
BIN
docs/content/images/v2-registry-auth.png
Normal file
BIN
docs/content/images/v2-registry-auth.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
28
docs/content/recipes/_index.md
Normal file
28
docs/content/recipes/_index.md
Normal 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)
|
209
docs/content/recipes/apache.md
Normal file
209
docs/content/recipes/apache.md
Normal 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
|
147
docs/content/recipes/mirror.md
Normal file
147
docs/content/recipes/mirror.md
Normal 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.
|
205
docs/content/recipes/nginx.md
Normal file
205
docs/content/recipes/nginx.md
Normal 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
|
74
docs/content/recipes/osx-setup-guide.md
Normal file
74
docs/content/recipes/osx-setup-guide.md
Normal 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
|
42
docs/content/recipes/osx/com.docker.registry.plist
Normal file
42
docs/content/recipes/osx/com.docker.registry.plist
Normal 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>
|
16
docs/content/recipes/osx/config.yml
Normal file
16
docs/content/recipes/osx/config.yml
Normal 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
|
105
docs/content/recipes/systemd.md
Normal file
105
docs/content/recipes/systemd.md
Normal 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`.
|
12
docs/content/spec/_index.md
Normal file
12
docs/content/spec/_index.md
Normal 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
5505
docs/content/spec/api.md
Normal file
File diff suppressed because it is too large
Load diff
1204
docs/content/spec/api.md.tmpl
Normal file
1204
docs/content/spec/api.md.tmpl
Normal file
File diff suppressed because it is too large
Load diff
12
docs/content/spec/auth/_index.md
Normal file
12
docs/content/spec/auth/_index.md
Normal 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.
|
328
docs/content/spec/auth/jwt.md
Normal file
328
docs/content/spec/auth/jwt.md
Normal 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.
|
190
docs/content/spec/auth/oauth.md
Normal file
190
docs/content/spec/auth/oauth.md
Normal 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"}
|
||||
```
|
153
docs/content/spec/auth/scope.md
Normal file
153
docs/content/spec/auth/scope.md
Normal 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.
|
250
docs/content/spec/auth/token.md
Normal file
250
docs/content/spec/auth/token.md
Normal 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:
|
||||
|
||||

|
||||
|
||||
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)
|
41
docs/content/spec/deprecated-schema-v1.md
Normal file
41
docs/content/spec/deprecated-schema-v1.md
Normal 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`.
|
BIN
docs/content/spec/images/v2-registry-auth.png
Normal file
BIN
docs/content/spec/images/v2-registry-auth.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
30
docs/content/spec/implementations.md
Normal file
30
docs/content/spec/implementations.md
Normal 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
90
docs/content/spec/json.md
Normal 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
|
||||
}
|
||||
```
|
292
docs/content/spec/manifest-v2-2.md
Normal file
292
docs/content/spec/manifest-v2-2.md
Normal 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.
|
7
docs/content/spec/menu.md
Normal file
7
docs/content/spec/menu.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
title: "Reference"
|
||||
description: "Explains registry JSON objects"
|
||||
keywords: registry, service, images, repository, json
|
||||
type: "menu"
|
||||
identifier: "smn_registry_ref"
|
||||
---
|
70
docs/content/storage-drivers/_index.md
Normal file
70
docs/content/storage-drivers/_index.md
Normal 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.
|
26
docs/content/storage-drivers/azure.md
Normal file
26
docs/content/storage-drivers/azure.md
Normal 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).
|
19
docs/content/storage-drivers/filesystem.md
Normal file
19
docs/content/storage-drivers/filesystem.md
Normal 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`.
|
19
docs/content/storage-drivers/gcs.md
Normal file
19
docs/content/storage-drivers/gcs.md
Normal 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).
|
||||
|
16
docs/content/storage-drivers/inmemory.md
Normal file
16
docs/content/storage-drivers/inmemory.md
Normal 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
|
165
docs/content/storage-drivers/s3.md
Normal file
165
docs/content/storage-drivers/s3.md
Normal 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).
|
Loading…
Add table
Add a link
Reference in a new issue