Storing containers securely is a crucial process in the shipment industry and of course in software developments. I know something about linux containers, especially docker containers, when I compare it with containers in shipment industry. Here in this post, I will show you how we could securely store linux containers, especially docker containers in a private registry with user interface on your own premise.
disclaimers: I neither work for Sonatype nor a paid promoter of nexus repository manager. There isn’t many free, opensource, docker private registry with user interface which could be self hosted as nexus repository manager and wanted to share how one could be setup easily. A prior understanding of docker concepts is necessary. A working nexus repository manager with nginx reverse proxy translating ssl.
Interacting with Docker Registry
Docker clients can interact with a docker registry either securely or insecurely. By default, docker clients are meant to look for https endpoint, this means, if you have an secured registry, i.e. a registry which can accept https connections, there is no need to make extra configuration changes to the clients. Otherwise, every docker client should be configured explicitly by passing insecure-registries params. Docker documentation better explains it.
This blog explains how one could setting up a docker private registry with a secure connection. Insecure registry or insecure docker client is out of scope.
Understanding Nexus repository connectors
A note from the docker registry setup from nexus documentation:
dockerclient does not allow a context as part of the path to a registry, as the namespace and image name are embedded in the URLs it uses. This is why requests to repositories on the repository manager are served on a specific and separate port from the rest of the application instead of how most other repositories serve content via a path i.e.
<nexus-hostname>/<repositoryName>/<path to content>.
This means that, we need to setup repository connectors for each repository to receive push requests and a single repository connector for pull requests on different ports.
From the figure above, we have two repository connectors running on port 8083 and 8084 corresponding to Private repository 1 and 2. Another repository connector running on port 8082 corresponds to a repository group composing of our private physical repositories and a public docker hub.
Installing nexus 3 repository manager — OSS
There is good documentation from sonatype, just follow that. Its also possible to run this repository manager as a docker container. Also, ensure that you run it behind a nginx reverse proxy on https.
Docker hosted-repository — docker push
Create a hosted repository for receiving docker push with some name. Lets name it docker-test. Create a http connector on port 8083 and leave https blank in the repository connectors section. Also force basic authentication. Again, there is good documentation on the sonatype website or there is a nice blog written by ivankrizsan
Docker repository-group — docker pull
Create a proxy repository for the public docker hub with proxy remote storage as https://registry-1.docker.io and leave the HTTP section empty. Lets name it docker-hub
Also, create a docker repository group and include all the necessary hosted and proxy repository. Our just created docker-test and docker-hub in this case.
Lets name it docker-proxy. In the same setting, create repository connectors similar to hosted-repository as above, but use port 8082.
Verify repository connectors
After you have saved your changes, verify listeners from command line. On Ubuntu 16.04.x you should be able to do something like this as root or sudo user and see if repository connector listeners running on ports 8082 and 8083. The listener running on port 8081 corresponds to user interface of the nexus repository manager.
Configure Nginx for SSL translation
Now we have an insecure registry running, lets secure it. Add reverse proxy server annotations corresponding to each repository connector to your nginx reverse proxy.
Use easy mapping ports for ssl translation. E.g. 8082 over ssl corresponds to 48082, so and so forth. Refer this gist if needed (replace necessary hostname, domain, certificate names).
Verify again with netstat
Testing docker push and docker pull
- Login to each repository connector from the machine where you want to test (yes, every repository connector). Remember to use secure ports. You could also use the default admin/admin123 credentials.
2. Test docker push. Fetch nginx from public docker hub, tag it and push to our private repository (port 48083)
Verify by searching for my-nginx version v1 from the user interface.
3. Test docker pull. Fetch the my-nginx:v1 which we just pushed. Also try to pull nginx from docker hub through our private repository group docker-group running on port 48082.
Also verify docker-hub nginx is cached in our proxy repository
Drawback: Manual one time login is necessary to each repository connectors individually, this will become a problem if number of repositories are more as each repository corresponds to a repository connector.
Improvement: It might be possible to configure Nginx to translate ports to context based urls, which I have not tried. Perhaps, the login problem which is mentioned above can be circumvented with this.
OS: Ubuntu 16.04.x, Nexus Repository manager: 3.7.1–02, Docker: 17.12.0-ce