How to deploy nexus repository server in k8s

Docker images are usually stored in docker repositories. Nexus server provides host, proxy and group type docker repositories. It can be deployed as a docker container itself with persistent storage. See https://hub.docker.com/r/sonatype/nexus3 for more info.

Step-by-step guide

1. Using company PKI, generate nexus cert p12 cert for respective domain; for example, nexus.example.com

2. Store the cert passphrase in a k8s secret

 kubectl create secret generic nexus-cert-pass --from-literal="password=**********"

3. Generate java keystore password and store in in k8s secret

 openssl rand -base64 32
 kubectl create secret generic nexus-keystore-pass --from-literal="password=******"

4. Store the nexus p12 certificate in k8s config map

 kubectl create configmap nexus-cert --from-file=nexus.example.com.p12

5. Save CA certificates in k8s config map. They will later be imported into the java trusted store.

 kubectl create configmap nexus-trusted-ca --from-file=nexus-trusted-ca.cert

6. Modify jetty-https.xml file from nexus and save it in k8s secret (to get the complete file, you may just run docker run -ti --rm sonatype/nexus3 bash)

 ...
   <New id="sslContextFactory" class="org.eclipse.jetty.util.ssl.SslContextFactory">
     <Set name="KeyStorePath"><Property name="ssl.etc"/>/keystore.pkcs12</Set>
     <Set name="KeyStorePassword">**************</Set>
     <Set name="KeyManagerPassword">**************</Set>
     <Set name="TrustStorePath"><Property name="ssl.etc"/>/truststore.pkcs12</Set>
     <Set name="TrustStorePassword">**************</Set>
 ...
 kubectl create secret generic jetty-https --from-file=jetty-https.xml

7. Java comes with elevated security, which needs to be lowered to work with our poor MS AD LDAP servers. So we need to overwrite two java security files with config maps

 vi java.security (re-enable MD5)
 ...
 #keystore.type=jks
 keystore.type=pkcs12
 #jdk.certpath.disabledAlgorithms=MD2, MD5, SHA1 jdkCA & usage TLSServer, \
 #    RSA keySize < 1024, DSA keySize < 1024, EC keySize < 224
 jdk.certpath.disabledAlgorithms=MD2, SHA1 jdkCA & usage TLSServer, RSA keySize < 1024, DSA keySize < 768, EC keySize < 224
 #jdk.certpath.disabledAlgorithms=MD2, RSA keySize < 1024, DSA keySize < 1024, EC keySize < 224
 jdk.certpath.disabledAlgorithms=MD2, RSA keySize < 1024, DSA keySize < 1024, EC keySize < 224
 #jdk.jar.disabledAlgorithms=MD2, MD5, RSA keySize < 1024, DSA keySize < 1024
 jdk.jar.disabledAlgorithms=MD2, RSA keySize < 1024, DSA keySize < 1024
 #jdk.tls.disabledAlgorithms=SSLv3, RC4, DES, MD5withRSA, DH keySize < 1024, \
 #    EC keySize < 224, 3DES_EDE_CBC, anon, NULL
 jdk.tls.disabledAlgorithms=RC4, DES, DH keySize < 1024, \
     EC keySize < 224, anon, NULL
 ...
  
 vi java.config (reduce the key size back to 1024)
 #cat /etc/crypto-policies/back-ends/java.config
 #jdk.tls.ephemeralDHKeySize=2048
 jdk.tls.ephemeralDHKeySize=1024
 #jdk.certpath.disabledAlgorithms=MD2, MD5, DSA, RSA keySize < 2048
 jdk.certpath.disabledAlgorithms=MD2, DSA, RSA keySize < 1024
 #jdk.tls.disabledAlgorithms=DH keySize < 2048, SSLv2, SSLv3, TLSv1, TLSv1.1, DHE_DSS, RSA_EXPORT, DHE_DSS_EXPORT, DHE_RSA_EXPORT, DH_DSS_EXPORT, DH_RSA_EXPORT, DH_anon, ECDH_anon, DH_RSA, DH_DSS, ECDH, 3DES_EDE_CBC, DES_CBC, RC4_40, RC4_128, DES40_CBC, RC2, HmacMD5
 jdk.tls.disabledAlgorithms=DH keySize < 1024, SSLv2, DHE_DSS, RSA_EXPORT, DHE_DSS_EXPORT, DHE_RSA_EXPORT, DH_DSS_EXPORT, DH_RSA_EXPORT, DH_anon, ECDH_anon, DH_RSA, DH_DSS, ECDH, 3DES_EDE_CBC, DES_CBC, RC4_40, RC4_128, DES40_CBC, RC2, HmacMD5
 jdk.tls.legacyAlgorithms=
  
 kubectl create configmap java-security --from-file=java.security
 kubectl create configmap java-config --from-file=java.config
 8. Export NFS mount nexus-data owned by used/group id 200:200 - the account that nexus server runs under
 
 mount nfs_server:/export /mnt
 mkdir /mnt/nexus-data
 chown 200:200 /mnt/nexus-data
 9. Create deployment manifest nexus.yaml and apply it
 
 apiVersion: v1
 kind: Service
 metadata:
   name: nexus
 spec:
   selector:
     app: nexus
   ports:
   - port: 8443
 ---
 apiVersion: v1
 kind: Service
 metadata:
   name: docker-repo
 spec:
   type: NodePort
   selector:
     app: nexus
   ports:
   - port: 8888
     nodePort: 30888
 ---
 apiVersion: v1
 kind: Service
 metadata:
   name: docker
 spec:
   type: NodePort
   selector:
     app: nexus
   ports:
   - port: 4443
     nodePort: 31443
 ---
 apiVersion: networking.k8s.io/v1beta1
 kind: Ingress
 metadata:
   name: nexus-ssl-ingress
   annotations:
     ingress.kubernetes.io/ssl-passthrough: "true"
   namespace: default
 spec:
   rules:
   - host: nexus.example.com
     http:
       paths:
       - backend:
           serviceName: nexus
           servicePort: 8443
 ---
 apiVersion: apps/v1
 kind: Deployment
 metadata:
   namespace: default
   name: nexus
   labels:
     app: nexus
 spec:
   selector:
     matchLabels:
       app: nexus
   template:
     metadata:
       labels:
         app: nexus
     spec:
       containers:
       - name: nexus
         image: sonatype/nexus3
         imagePullPolicy: IfNotPresent
         resources:
           limits:
             cpu: "4"
             memory: "8Gi"
           requests:
             cpu: "4"
             memory: "8Gi"
         ports:
         - containerPort: 8081
         - containerPort: 8443
         - containerPort: 4443
         - containerPort: 8000
         - containerPort: 8888
         env:
         - name: password
           valueFrom:
             secretKeyRef:
               key: password
               name: nexus-keystore-pass
               optional: false
         - name: nexuscertpass
           valueFrom:
             secretKeyRef:
               key: password
               name: nexus-cert-pass
               optional: false
         - name: INSTALL4J_ADD_VM_PARAMS
           value: "-Xms1200m -Xmx1200m -XX:MaxDirectMemorySize=5g -Djava.util.prefs.userRoot=/nexus-data/javaprefs -Djavax.net.ssl.keyStore=/nexus-data/etc/ssl/keystore.pkcs12 -Djavax.net.ssl.keyStorePassword=$(password) -Djavax.net.ssl.trustStore=/nexus-data/etc/ssl/truststore.jks -Djavax.net.ssl.trustStorePassword=$(password)"
         args: ['sh', '-c', "if [[ ! -d /nexus-data/etc/ssl ]]; then mkdir -p /nexus-data/etc/ssl; fi && if [[ ! -d /nexus-data/tmp ]]; then mkdir /nexus-data/tmp; fi && rm -f /nexus-data/etc/ssl/*.* && keytool -importkeystore -noprompt -destkeystore /nexus-data/etc/ssl/keystore.pkcs12 -deststoretype PKCS12 -srckeystore /mnt/nexus.lat.internal.p12 -srcstoretype PKCS12 -deststorepass $password -destkeypass $password -srcstorepass $nexuscertpass && cd /nexus-data/tmp && csplit -z -f crt- /etc/pki/ca-trust/source/anchors/nexus-trusted-ca.cert '/-----BEGIN CERTIFICATE-----/' '{*}' && for file in crt-*; do keytool -import -noprompt -keystore /nexus-data/etc/ssl/truststore.jks -file $file -storepass $password -alias crt-$file; done && keytool -importkeystore -noprompt -srcstorepass $password -deststorepass $password -srckeystore /nexus-data/etc/ssl/truststore.jks -srcstoretype JKS -destkeystore /nexus-data/etc/ssl/truststore.pkcs12 -deststoretype PKCS12 && /opt/sonatype/start-nexus-repository-manager.sh"]
         livenessProbe:
           httpGet:
             scheme: HTTP
             path: /
             port: 8081
           initialDelaySeconds: 30
           timeoutSeconds: 30
         volumeMounts:
         - name: data
           mountPath: /nexus-data
         - name: jetty-https
           mountPath: /opt/sonatype/nexus/etc/jetty/jetty-https.xml
           subPath: jetty-https.xml
           readOnly: true
         - name: nexus-cert
           mountPath: /mnt
         - name: java-security
           mountPath: /etc/java/java-1.8.0-openjdk/java-1.8.0-openjdk-1.8.0.232.b09-2.el8_1.x86_64/lib/security/java.security
           subPath: java.security
           readOnly: true
         - name: java-config
           mountPath: /etc/crypto-policies/back-ends/java.config
           subPath: java.config
           readOnly: true
         - name: nexus-trusted-ca
           mountPath: /etc/pki/ca-trust/source/anchors
       volumes:
       - name: data
         nfs:
           path: /export/nexus-data
           server: nfs_server
       - name: nexus-cert
         configMap:
           name: nexus-cert
       - name: nexus-trusted-ca
         configMap:
           name: nexus-trusted-ca
       - name: jetty-https
         secret:
           secretName: jetty-https
       - name: java-security
         configMap:
           name: java-security
       - name: java-config
         configMap:
           name: java-config
 ---
  
 kubectl apply -f nexus.yaml

10. Once the nexus is up and running, which can be confirmed by kubectl logs nexus-* -f, modify /mnt/nexus-data/nexus.properties file to allow https and reload the server

 vi /mnt/nexus-data/etc/nexus.properties
 nexus-args=${jetty.etc}/jetty.xml,${jetty.etc}/jetty-http.xml,${jetty.etc}/jetty-https.xml,${jetty.etc}/jetty-requestlog.xml
 ssl.etc=${karaf.data}/etc/ssl
 application-port-ssl=8443
 umount /mnt
  
 kubectl delete pod nexus-***