Categories
Container Docker Encryption Linux Network Proxy Virtualization Webserver

Setting up a docker registry proxy

Docker hub recently announced an upcoming request limit to their registry. So in order to reduce the amount of requests issued there, the idea of a caching proxy solution comes to mind.

Possible solutions

Fortunately there are some projects already working on that problem. However there are different approaches.

Some of the projects I found during research are:

https://github.com/lhns/docker-registry-cache

https://github.com/rpardini/docker-registry-proxy

Comparison

Featuredocker-registry-cachedocker-registry-proxy
Central changes sufficientnoyes
Changes of all docker hub names requiredyesno
Breaks end-to-end encryption (MITM)noyes
Caches manifest gets???yes
Uses mirror conceptyesno
Acts as http proxynoyes
Containers requiredN+21

docker-registry-cache

This project is used to create a docker compose environment based on several standard components to create a multi registry cache. Components are

  1. multiple docker registries
  2. traefik load balancer
  3. redis
  4. S3 storage (optional)

However this setup seems quite complex and I couldn’t find a single line about whether it’d really solve the problem of the upcoming docker hub limits.

So for now I decided to give docker-registry-proxy a try.

docker-registry-proxy

The docker-registry-proxy was explicitly build to get around docker’s access limits. It is acting as a man-in-the-middle (MITM) http proxy and in order to use it needs to be treated like one.

Startup is very easy as a docker container is available:

linux # docker run --rm --name docker_registry_proxy -it \
       -p 0.0.0.0:3128:3128 -e ENABLE_MANIFEST_CACHE=true \
       -v $(pwd)/docker_mirror_cache:/docker_mirror_cache \
       -v $(pwd)/docker_mirror_certs:/ca \
       rpardini/docker-registry-proxy:0.6.5

In order to use it with docker just add some proxies to its configuration and restart docker:

linux # vi /etc/docker/daemon.json 
{
  "proxies": {
    "http-proxy": "http://127.0.0.1:3128",
    "https-proxy": "http://127.0.0.1:3128",
    "no-proxy": "registry.mydomain.de,127.0.0.0/8"
  }
}

A different approach is to modify some systemd files (this is shown below in the output of the curl command).

A classical http proxy has two main purposes: to connect to the outside world (in restricted networks) and to cache requests (to save network bandwidth).

However nowadays most of the http traffic is encrypted (and therefore can no longer be cached by a proxy – at least not without breaking the encryption and causing trouble).

How it works

But that’s exactly what this MITM setup does: it retrieves information from the docker registry via https (by acting like a http(s) client). It then decrypts the retrieved data, stores it in a cache, re-encrypts the data (with its own certificate) and sends this response back to the client.

As a result, the client receives encrypted data, but won’t accept the SSL certificate used for encryption (as the MITM proxy used its own local certificate authority (CA) that is not yet known by the client).

Client-side changes

So in order to make things work, we need to make all possible clients accept this CA. How to do that is either documented on the project page, but also within the software itself: you can just get a certain URL from the proxy to get the commands needed to

  1. export the used MITM CA
  2. include it into the systems accepted CA pool
  3. restart systemd and docker to apply changes

The short version of things to do is this:

linux # curl -sS http://127.0.0.1:3128/ca.crt > /usr/share/ca-certificates/docker_registry_proxy.crt
linux # echo "docker_registry_proxy.crt" >> /etc/ca-certificates.conf
linux # update-ca-certificates --fresh
linux # systemctl daemon-reload
linux # systemctl restart docker.service

If everything succeeded you can now pull images that will be cached locally:

linux # docker pull ubuntu

Meanwhile you can observe what’s going on in a second terminal:

linux # docker logs -f docker_registry_proxy

Additional information

As mentioned above the basic information required to set things up can also be retrieved via URL. This contains some extra steps (and uses systemd configuration instead of docker’s daemon.js):

linux # curl http://127.0.0.1:3128/setup/systemd

set -e

if [ ! -d /etc/systemd ]; then
   echo "Not a systemd system"
   exit 1
fi

mkdir -p /etc/systemd/system/docker.service.d
cat << EOD > /etc/systemd/system/docker.service.d/http-proxy.conf
[Service]
Environment="HTTPS_PROXY=http://127.0.0.1:3128/"
EOD

# Get the CA certificate from the proxy and make it a trusted root.
curl -sS http://127.0.0.1:3128/ca.crt > /usr/share/ca-certificates/docker_registry_proxy.crt
if fgrep -q "docker_registry_proxy.crt" /etc/ca-certificates.conf ; then
   echo "certificate refreshed"
else
   echo "docker_registry_proxy.crt" >> /etc/ca-certificates.conf
fi

update-ca-certificates --fresh

# Reload systemd
systemctl daemon-reload

# Restart dockerd
systemctl restart docker.service
echo "Docker configured with HTTPS_PROXY=http://127.0.0.1:3128/"

More detailed configuration options can be found on the project page.

Leave a Reply

Your email address will not be published. Required fields are marked *