Quick Docker Registry with basic security

2024 . Dec . 30 / Notes++ to self

What is a Docker Registry?

According to the DockerDocs, the Registry is the open source implementation for storing and distributing container images and other content.

In other words: it's the place where you can upload to/download from your Docker images for all your needs, just like the Docker Hub.

Starting a local registry

We can use docker compose to start a registry server locally:

registry.yml
# docker-compose -f registry.yml up -d

version: '3.7'


services:


  registry:
    image: registry:2.8.3
    ports:
      - 5000:5000
    restart: unless-stopped
    volumes:
      - /path/to/a/local/directory/:/var/lib/registry
    networks:
      - registry-network


networks:
  registry-network:

Now we have our registry at http://localhost:5000; we can verify it is working by checking some of its API like: http://localhost:5000/v2/_catalog.

Tagging images

Now we can build and push our images to our own registry. For this we have to tag the image appropriately as: host/myimage:version, where host in this case is localhost:5000. E.g. localhost:5000/hello:1.0.

Insecure registries

But there is a little problem here, because it is a host that is not using https, docker won't push the image 'cause it is unsafe to do it. The workaround (though, not recommended for production and critical stuff) is to edit the file /etc/docker/daemon.json by adding the property:

/etc/docker/daemon.json
{
    "insecure-registries": ["localhost:5000"]
}

After this just restart docker sudo service docker start and try pushing your images now.

So, what is the recommended way? Just use a host with ssl/https and tag your images accordingly. E.g. if your host is https://registry.example.org then tag the image as registry.example.org/hello:1.0.

Build, tag, push commands

We can build and tag the image with:

# Build
docker build -f someproject.Dockerfile -t registry.example.org/someproject:1.0 .

And then pushing it to the registry:

# Push
docker push registry.example.org/someproject:1.0

But what if the image was already built? Well, we just create a new tag for the image and then push it.

# Tag
docker image tag alreadythere:v0.9.1 registry.example.org/someproject:1.0

Build with files

We can use docker-compose to make the process of build_tag_push-ing a little bit easier by creating a YAML file like this:

builder.yml
version: '3.7'


services:


  someproject:
    image: registry.example.org/someproject:1.0
    build:
      context: .
      dockerfile: someproject.Dockerfile


  otherone:
    image: registry.example.org/otherone:1.2.3
    build:
      context: ./otherdir/
      dockerfile: otherfile.Dockerfile


  localdev:
    image: localhost:5000/localdev:1.2.3
    build:
      context: ./localdev/
      dockerfile: localdev.Dockerfile

Basically a more succinct docker compose file, with just the necessary info. Then, we just need to execute:

# Build
docker-compose -f builder.yml build

# Push
docker-compose -f builder.yml push

Security

Now that we got the basics of the registry, we're going to use basic security for it, so if we want to push/pull an image from it we're going to need to be authenticated.

First we're going to create the user and password:

# Pull httpd image
docker pull httpd:2.4.58

# Run it
docker run --entrypoint htpasswd httpd:2.4.58 -Bbn user_here password_here > auth/htpasswd

Now we have an auth directory with a file called htpasswd

Next let's update the registry.yml file:

registry.yml
version: '3.7'


services:


  registry:
    image: registry:2.8.3
    ports:
      - 5000:5000
    restart: unless-stopped
    # Adding environment
    environment:
      REGISTRY_AUTH: htpasswd
      REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd
      REGISTRY_AUTH_HTPASSWD_REALM: Registry Realm
    # Adding auth dir
    volumes:
      - /path/to/a/local/directory/:/var/lib/registry
      - /path/to/the/auth/directory:/auth
    networks:
      - registry-network


networks:
  registry-network:

If we try now to open localhost:5000/v2/_catalog, the browser will ask for credentials. And what about the docker pull/push commands? We have to execute docker login command with the host and its credentials:

docker login localhost:5000

# Or
docker login registry.example.org

This is going to save the credentials at ~/.docker/config.json for future pull/push actions.

Pulling images

Finally, we just need to execute the pull command the same way as always:

docker pull registry.example.org/someproject:1.0
docker pull localhost:5000/localdev:1.2.3

Or directly with a docker compose file:

compose-file.yml
version: '3.7'


services:


  someproject:
    image: registry.example.org/someproject:1.0
    # ports volumes networks etc ...


  localdev:
    image: localhost:5000/localdev:1.2.3
    # ...


# ...
Comments
If you have any doubt or question, or know how to improve this post, please feel free to write a comment.