Lead Image © Viktoriya Malova, 123RF.com

Lead Image © Viktoriya Malova, 123RF.com

Registry for Docker images

Repository D

Article from ADMIN 52/2019
By
Running your own registry for Docker images is not difficult. We'll show you how to get started using the free docker_auth software.

If you create Docker images yourself, you also need your own registry. Although registries are available as Docker images, their functionality is limited, whereas adding an authentication (auth) server gives you a useful repository for images.

With no offense intended, the release of the Docker software was quick and dirty. Only later were many desperately needed features introduced. One example is the registry – the storage space for Docker images – which did not even have a mechanism for authentication. Later, the image format was changed and Registry 2.0 (a.k.a., "Docker Distribution") was released, which cleared up many limitations. At least it now has a basic authentication mechanism, although it is limited to the htpasswd files known from Apache.

Restricting Access and Controlling Privileges

Unfortunately, the Docker mechanism is limited to authentication. It does not provide granular authorization of authenticated users for individual resources (i.e., read/write access to images). For many users who just want to try out Docker, and teams in which everyone is allowed to do everything, this is perfectly okay. You can just add a user to the htpasswd file and start the registry container, where you mount the directory with the password file. Next, you pass in the authentication settings as environment variables (Listing 1).

Listing 1

Authentication Settings

$ sudo mkdir /etc/docker-registry
$ htpasswd -Bbn oliver T0Ps3crEt | sudo tee /etc/docker-registry/htpasswd
oliver:$2y$05$lAmkjHRcR0.TK52/rHR/Pe86AGZqpRleXenHVT/eabFe8He5UZiPu
$ docker run -p 5000:5000 --name registry -v /etc/docker-registry/:/auth -e "REGISTRY_AUTH=htpasswd" -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" registry:2

You can now try to use the registry to store images. First, download an image from the official Docker Hub with, docker pull alpine:latest, an image of the space-saving Alpine distribution. To load the image into your registry, you'll need to tag it with its hostname:

$ docker tag alpine:latest remote.repository.com:5000/alpine-latest

If you now try to upload the image, you will see an unauthorized: authentication required error message. You must log in with the credentials assigned above using the docker login command for the upload to work (Listing 2). The listing also shows that the login credentials are stored in $HOME/.docker/config.json. To remove them, use:

docker logout remote.repository.com:5000

Listing 2

Login and Upload to Registry

$ Docker login remote.repository.com
Authenticating with existing credentials...
Login did not succeed, error: Error response from daemon: login attempt to
https://remote.repository.com:5000/v2/ failed with status: 401 Unauthorized
Username (ofrommel): oliver
Password:
WARNING! Your password will be stored unencrypted in /home/oliver/.Docker/config.json. Configure a credential helper to remove this warning.
See https://docs.Docker.com/engine/reference/commandline/login/#credentials-store
$
Login Succeeded
Docker push remote.repository.com:5000/alpine:latest
The push refers to repository [remote.repository.com:5000/alpine]
73046094a9b8: Pushed
latest: digest: sha256:0873c923e00e0fd2ba78041bfb64a105e1ecb7678916d1f7776311e45bf5634b size: 528

This solution may be okay if you are just looking to familiarize yourself with Docker. Most of the time, however, you will want to take things further – say, to include the previously mentioned authorization for individual resources. In concrete terms, this can mean that a user receives unrestricted read and write rights for their namespace (remote.repository.com/User) but only read rights for other images.

This step can be managed with the Docker Registry's Identity Management API, which is token-based. For example, assume a user wants to access the registry; the server redirects the client to an authentication server, which then checks the login/password and issues a token (Figure 1). The token's scope field additionally determines in detail the user authorizations.

Figure 1: For the Docker Registry to support authentication and authorization, it needs a separate auth server that issues tokens.

docker_auth as an Auth Server

The described procedure is implemented for example by the free docker_auth software, which was developed by Cesanta and can be found on GitHub [1]; you can also find an image on Docker Hub. Docker_auth offers the following authentication methods: static user list, login via Google or GitHub, LDAP connection, MongoDB, or an external program. Static access control lists (ACLs), MongoDB, or an external program can be used for authorization.

A YAML file is used for the configuration; you add this to the container as a volume with a bind mount in the usual way. This also applies to the TLS certificates that docker_auth uses. Configuration examples can be found in the GitHub repository examples directory, where the reference.yml file is also located. The file contains all the available options along with comments. An example of the configuration, which does without back ends like MongoDB and already contains the user accounts, as well as the ACLs, is shown in Listing 3.

Listing 3

Auth Server Configuration

01 server:
02       addr: ":5001"
03       certificate: "/le/live/remote.repository.com/cert.pem"
04       key: "/le/live/remote.repository.com/privkey.pem"
05 token:
06       issuer: "MyRepository auth server" # muss mit Registry-Config ¸bereinstimmen!
07       expiration: 900
08 users:
09       # Password is specified as a BCrypt hash. Use `htpasswd -nB USERNAME` to generate.
10       "oliver":
11          password: "$2y$05$4dIrCZLpgSYDClrS6pN2BOxVm.rkPy/4IgnurlHbukOxOJldlhJM."
12 acl:
13       - match: {account: "admin"}
14         actions: ["*"]
15         comment: "Admin has full access to everything."
16       - match: {account: "user"}
17         actions: ["pull"]
18         comment: "User \"user\" can pull stuff."

Again, the password hashes are stored; they can be generated with htpasswd. The ACLs comprise three elements. Here, match specifies who or what the rule applies to, and actions specifies what can be done with it, followed by a comment. You have many ways to control access (e.g., with regular expressions). There are no real groups, but they can be simulated with labels. A restriction to certain IP addresses is also possible.

The documentation contains a number of example ACLs that can also be copied and pasted. Now start the auth server:

$ docker run --name docker_auth -p 5001:5001 -v `pwd`:/config:ro -v /var/log/docker_auth:/logs -v /etc/letsencrypt:/le cesanta/docker_auth:1 /config/config.yml

The Docker registry, which was started with environment variables above, can also be configured by a YAML file. Each setting in the file corresponds to an environment variable that separates the YAML path with underscores. The first value, REGISTRY, is omitted because it only identifies the container or the application. REGISTRY_AUTH_HTPASSWD_PATH would correspond to the following structure in the YAML file:

auth:
    htpasswd:
       path: "/auth/htpasswd"

However, the concrete value is not used because you want to use the auth server instead of htpasswd authentication. The corresponding configuration can be seen in Listing 4.

Listing 4

Registry Configuration

01 version: 0.1
02 log:
03       fields:
04          service: registry
05 storage:
06       filesystem:
07          rootdirectory: /var/lib/registry
08 http:
09       addr: :5000
10       tls:
11          certificate: "/le/live/remote.repository.com/cert.pem"
12          key: "/le/live/remote.repository.com/privkey.pem"
13 auth:
14       token:
15          realm: "https://remote.repository.com:5001/auth"
16          service: "Docker registry"
17          issuer: "MyRepository auth server"
18          rootcertbundle: "/le/live/remote.repository.com/fullchain.pem"

In addition to the location of the registry data (storage/filesystem/rootdirectory), which of course is not in the container but on the host, the authentication realm behind which the auth server is located is of particular interest. You also can use Let's Encrypt certificates, which are installed on the host, as well.

For a configuration file stored as config.yml in the current directory, you can start the registry with the call shown in Listing 5. Now you can use the registry as shown previously: Store the user accounts and ACLs in the docker_auth configuration file as described and restart the container. In the log output of the registry, you can view the individual requests, which also helps with troubleshooting (Figure 2). The setup with a MongoDB database is somewhat more dynamic than with the configuration file, because you can change the configuration without having to restart the container all the time.

Listing 5

Start the Registry

docker run -p 5000:5000 --name registry -v `pwd`/config.yml:/etc/docker/registry/config.yml -v /var/docker-registry:/var/lib/registry -v /etc/letsencrypt:/le registry:2
Figure 2: The Docker Registry logs every single request.

This setup, however, does not necessarily make administration more convenient, because the ACLs stored in the database use the same syntax as in the configuration file (Figure 3). For authentication, as mentioned above, LDAP is also interesting for many users, but the ACLs still have to be stored differently, be it in a file or in MongoDB.

Figure 3: The ACLs can be stored in a MongoDB database, as well.

Troubleshooting Problems with Certificates

If you see an error message about an insecure certificate, the easiest solution is to start the Docker daemon with the --insecure-registry option followed by a list of hostnames of your registries, including ports. You'll need to repeat this on every client that wants to use the registry.

The reason for the error message could be that current Linux distributions do not have Let's Encrypt's root certificate installed in their keychains – resorting to standard tools such as the Docker daemon (via the crypto libraries) – whereas web browsers come with their own keychains.

Listing 6 shows how to install and enable root certificates on an Ubuntu system. However, you will need to repeat this on all the machines from which you or your co-workers will be using Docker, and that will possibly include machines running Windows or macOS. The option with --insecure-registries is certainly the simpler choice.

Listing 6

Installing Certificates from Let's Encrypt

curl -O https://letsencrypt.org/certs/isrgrootx1.pem
curl -O https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem
openssl x509 -in isrgrootx1.pem -inform PEM -out isrgrootx1.crt
openssl x509 -in lets-encrypt-x3-cross-signed.pem -inform PEM -out lets-encrypt-x3-cross-signed.crt
sudo cp *.crt /usr/local/share/ca-certificates/
sudo update-ca-certificates --verbose
I already trust 148, your new list has 150
Certificate added: C=US, O=Let's Encrypt, CN=Let's Encrypt Authority X3
1 new root certificates were added to your trust store.
...
sudo systemctl restart docker

Buy this article as PDF

Express-Checkout as PDF
Price $2.95
(incl. VAT)

Buy ADMIN Magazine

SINGLE ISSUES
 
SUBSCRIPTIONS
 
TABLET & SMARTPHONE APPS
Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content

comments powered by Disqus