Quick Docker Registry with basic security
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:
# 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:
{
"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:
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:
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:
version: '3.7'
services:
someproject:
image: registry.example.org/someproject:1.0
# ports volumes networks etc ...
localdev:
image: localhost:5000/localdev:1.2.3
# ...
# ...