2017-11-11

Using a dockerized Nexus as a Docker registry

Recently, I was playing with Docker Swarm and I decided to setup a containerized Nexus as my Docker registry.
But to be able to work as a Docker registry, you need to use HTTPS.
I found lots of article about using Nexus as a docker registry, but not a containerized Nexus. That's what I'm going to describe.
A solution is to use a proxy, let it handle the security and use plain HTTP between the proxy and Nexus, but that’s not what I’m interested in.

Install dockerized Nexus

That part is the easiest one :)
Although it’s not the recommended way to do it, I’m going to use a mounted volume. I find it easier to manipulate what's inside that way.

$ mkdir /some/dir/nexus-data && chown -R 200 /some/dir/nexus-data 
$ docker run -d -p 8081:8081 -p 8082:8082 --name nexus -v /some/dir/nexus-data:/nexus-data sonatype/nexus3

Note that, despite of the command line given in the doc, we are opening two ports since we need another one for HTTPS. Choose it carefully as it will be used after.

Source : https://github.com/sonatype/docker-nexus3

Once you start Nexus, you can see the /some/dir/nexus-data directory growing.
Content of nexus-data :

total 56
drwxr-xr-x  22  max staff 748B 4 nov 10:13 .
drwxr-xr-x  4   max staff 136B 2 nov 23:15 ..
-rw-r--r--@ 1   max staff 8,0K 2 nov 23:15 .DS_Store
-rw-------  1   max staff 524B 4 nov 09:49 .bash_history
-rw-rw-r--  1   max staff 53B  2 nov 22:06 .install4j
drwxr-xr-x  3   max staff 102B 2 nov 22:06 .oracle_jre_usage
drwxr-xr-x  2   max staff 68B  2 nov 22:06 backup
drwxr-xr-x  3   max staff 102B 2 nov 22:06 blobs
drwxr-xr-x  260 max staff 8,6K 4 nov 10:14 cache
drwxr-xr-x  11  max staff 374B 2 nov 22:06 db
drwxr-xr-x  4   max staff 136B 2 nov 22:29 elasticsearch
drwxr-xr-x  6   max staff 204B 5 nov 09:53 etc
drwxr-xr-x  2   max staff 68B  2 nov 22:06 generated-bundles
drwxr-xr-x  2   max staff 68B  2 nov 22:06 health-check
drwxr-xr-x  3   max staff 102B 2 nov 22:06 instances
drwxr-xr-x  3   max staff 102B 2 nov 22:06 javaprefs
drwxr-xr-x  4   max staff 136B 2 nov 22:17 keystores
-rw-r--r--  1   max staff 14B  4 nov 10:13 lock
drwxr-xr-x  10  max staff 340B 5 nov 01:07 log
drwxr-xr-x  2   max staff 68B  2 nov 22:06 orient
-rw-r--r--  1   max staff 5B   4 nov 10:13 port
drwxr-xr-x  49  max staff 1,6K 4 nov 10:14 tmp

The 3 directories in bold are the ones we are going to use next.

Adding a valid name for our certificate

In real life, your certificate should match the DNS or machine on which you're hosting Nexus.
But in my case, it's just a local environment for testing purpose.
So I just added a value in /etc/hosts

127.0.0.1 nexus

Creating JKS

As Jetty is the underlying web container, a JKS (Java KeyStore) is needed to contain your certificate.
Here, we are going to use a CSR.

First, go to the directory /some/dir/nexus-data/keystores and then create your CSR.

keytool 
 -genkeypair 
 -keystore keystore.jks 
 -storepass changeit 
 -keypass changeit 
 -alias jetty 
 -keyalg RSA 
 -keysize 2048 
 -validity 5000 
 -dname "CN=nexus, OU=MyUnit, O=MyOrg, L=MyLoc, ST=MyState, C=FR" 
 -ext "SAN=DNS:nexus,IP:127.0.0.1" 
 -ext "BC=ca:true"

Source : https://support.sonatype.com/hc/en-us/articles/217542177-Using-Self-Signed-Certificates-with-Nexus-Repository-Manager-and-Docker-Daemon

Updating the configuration


jetty-https.xml

Since the dockerized image of Nexus expose only the directory /nexus-data through a volume, we don’t have access to the HTTPS configuration, which is located in /opt/sonatype/nexus/etc/jetty/jetty-https.xml.
To do so, let’s create a writable jetty configuration directory.

mkdir /some/dir/nexus-data/etc/jetty

Then start your dockerized Nexus and launch a bash prompt.

docker start nexus
docker exec -it nexus bash

Once in the container, copy the file jetty-https.xml so that it’ll be accessible from the host and not deleted every time we relaunch the container.

bash> cp /opt/sonatype/nexus/etc/jetty/jetty-https.xml /nexus-data/etc/jetty/

Edit the file jetty-https.xml and modify the section accordingly :

<new id="sslContextFactory" class="org.eclipse.jetty.util.ssl.SslContextFactory">   
<set name="KeyStorePath">/nexus-data/keystores/keystore.jks</set>
<set name="KeyStorePassword">changeit</set>   
<set name="KeyManagerPassword">changeit</set>
<set name="TrustStorePath">/nexus-data/keystores/keystore.jks</set>
<set name="TrustStorePassword">changeit</set> 
[ … ]

As you can see, we deleted the property name « ssl.etc » since it tells jetty to look for the path under /etc/ssl, which a directory we cannot modify within the container.

nexus.properties

Then edit the file /some/dir/nexus-data/etc/nexus.properties to uncomment the line enabling the HTTPS port, and write the value you defined when you created your container.

application-port-ssl=8082

Uncomment and modify the line allowing to tweak jetty configuration.

nexus-args=${jetty.etc}/jetty.xml,${jetty.etc}/jetty-http.xml,${jetty.etc}/jetty-requestlog.xml,/nexus-data/etc/jetty/jetty-https.xml

Then restart your container and tail the log.

docker start nexus;tail -500f /some/dir/nexus-data/log/nexus.log

You should observe the correct opening of both HTTP and HTTPS ports.

2017-11-04 09:14:16,900+0000 INFO [jetty-main-1] *SYSTEM
 org.eclipse.jetty.server.AbstractConnector - Started ServerConnector@276df136{HTTP/1.1,[http/1.1]}{0.0.0.0:8081}
2017-11-04 09:14:16,910+0000 INFO [jetty-main-1] *SYSTEM 
org.eclipse.jetty.util.ssl.SslContextFactory - x509=X509@4f12e7b6(jetty,h=[nexus],w=[]) 
for SslContextFactory@5fc3c41c(file:///nexus-data/keystores/keystore.jks,file:///nexus-data/keystores/keystore.jks)
2017-11-04 09:14:16,946+0000 INFO [jetty-main-1] *SYSTEM 
org.eclipse.jetty.server.AbstractConnector
- Started ServerConnector@287f5f5{SSL,[ssl, http/1.1]}{0.0.0.0:8082}

Source : https://help.sonatype.com/display/NXRM3/Configuring+SSL#ConfiguringSSL-InboundSSL-ConfiguringtoServeContentviaHTTPS

Our certificate is self-signed, that's why it is considered not valid by the browser.

Configure Docker Daemon to trust the certificate

You'll now have to configure the docker daemon to trust your certificate.
I'm not going to describe something that has already have been : just look at the description at the end of this article and you're good to go.

No comments: