Skip to main content

Configuring couchbase SSL for dynamic certificates in OpenShift

·3 mins

Couchbase SSL #

Suppose you have followed dynamic creation of java keystores in OpenShift post and wondered how to use similar concepts for couchbase database and a java application. This post will help you.

Couchbase setup #

Here is the couchbase documentation for configuring server-side certificates, we are interested in last few steps since OpenShift will generate key and cert by adding an annotation to the couchbase service.

Note: By adding this annotation, you can dynamically create certificates service.alpha.OpenShift.io/serving-cert-secret-name: couchbase-db-certs

couchbase service looks like this:

apiVersion: v1
kind: Service
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: ""
    service.alpha.OpenShift.io/serving-cert-secret-name: couchbase-db-certs
  labels:
    component: couchbase-db
  name: couchbase-db
  namespace: myproject
spec:
  ports:
    - name: consolerest
      port: 8091
      protocol: TCP
      targetPort: 8091

To convert certificates as couchbase expects, we will use an init container.

We will use an emptyDir volume to store the cert and key in the /opt/couchbase/var/lib/couchbase/inbox/ location so that couchbase can access them.

Init container will run a sequence of commands to split certificate, place them into /opt/couchbase/var/lib/couchbase/inbox/ location, and name cert file as chain.pem, key as pkey.key.

Init container will look as follows:

initContainers:
  - args:
      - "-c"
      - >-
        csplit -z -f crt- $crtfile '/-----BEGIN CERTIFICATE-----/' '{*}'
        && for file in crt-*; do cat $file >
        /opt/couchbase/var/lib/couchbase/inbox/service-$file; done && cat
        $crtfile > /opt/couchbase/var/lib/couchbase/inbox/chain.pem && cat
        $keyfile > /opt/couchbase/var/lib/couchbase/inbox/pkey.key        
    command:
      - /bin/bash
    env:
      - name: keyfile
        value: /var/run/secrets/OpenShift.io/services_serving_certs/tls.key
      - name: crtfile
        value: /var/run/secrets/OpenShift.io/services_serving_certs/tls.crt
      - name: password
        value: changeit
    image: registry.access.redhat.com/redhat-sso-7/sso72-OpenShift:latest
    imagePullPolicy: Always
    name: couchbase-ssl
    volumeMounts:
      - mountPath: /var/run/secrets/OpenShift.io/services_serving_certs
        name: couchbase-db-certs
      - mountPath: /opt/couchbase/var/lib/couchbase/inbox/
        name: couchbase-ssl-volume
volumes:
  - emptyDir: {}
    name: couchbase-ssl-volume
  - name: couchbase-db-certs
    secret:
      defaultMode: 420
      secretName: couchbase-db-certs

Next, add couchbase-ssl-volume emptyDir volume mount to the actual container so the file can be accessed by couchbase.

spec:
  containers:
    - env:
      ...
      volumeMounts:
        - mountPath: /opt/couchbase/var/lib/couchbase/inbox/
          name: couchbase-ssl-volume

I am using a rhel7-couchbase image; on startup, it runs an initialization script to set up the cluster; at that time, we will upload the certificate and activate it using these commands.

couchbase-cli ssl-manage -c http://localhost:8091 -u Administrator \
-p password --upload-cluster-ca=${SERVICE_CERT}
couchbase-cli ssl-manage -c http://localhost:8091 -u Administrator \
-p password --set-node-certificate

Pass the cert location as environment variable SERVICE_CERT in deployment config.

- env:
    - name: SERVICE_CERT
      value: /opt/couchbase/var/lib/couchbase/inbox/service-crt-01

Verify logs on the container

SUCCESS: Uploaded cluster certificate to http://localhost:8091
SUCCESS: Node certificate set

We can also verify in couchbase UI

Couchbase UI

At this point, we completed the couchbase setup.

Application setup #

We will be using same steps as SSL client from dynamically-creating-java-keystores-OpenShift post

To make a secure connection to the couchbase, it will need the trust store generated by the pem-to-truststore initContainer. Here is the client’s app deployment config:

- apiVersion: v1
  kind: DeploymentConfig
  metadata:
    labels:
      app: ssl-client
    name: ssl-client
  spec:
    replicas: 1
    selector:
      deploymentconfig: ssl-client
    template:
      metadata:
        labels:
          app: ssl-client
          deploymentconfig: ssl-client
      spec:
        containers:
          - name: ssl-client
            image: ssl-client
            imagePullPolicy: Always
            env:
              - name: JAVA_OPTIONS
                value: -Djavax.net.ssl.trustStore=/var/run/secrets/java.io/keystores/truststore.jks -Djavax.net.ssl.trustStorePassword=changeit
              - name: POD_NAMESPACE
                valueFrom:
                  fieldRef:
                    apiVersion: v1
                    fieldPath: metadata.namespace
            volumeMounts:
              - mountPath: /var/run/secrets/java.io/keystores
                name: keystore-volume
        initContainers:
          - name: pem-to-truststore
            image: registry.access.redhat.com/redhat-sso-7/sso71-OpenShift:1.1-16
            env:
              - name: ca_bundle
                value: /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt
              - name: truststore_jks
                value: /var/run/secrets/java.io/keystores/truststore.jks
              - name: password
                value: changeit
            command: ["/bin/bash"]
            args:
              [
                "-c",
                "csplit -z -f crt- $ca_bundle '/-----BEGIN CERTIFICATE-----/' '{*}' && for file in crt-*; do keytool -import -noprompt -keystore $truststore_jks -file $file -storepass changeit -alias service-$file; done",
              ]
            volumeMounts:
              - mountPath: /var/run/secrets/java.io/keystores
                name: keystore-volume
        volumes:
          - emtpyDir: {}
            name: keystore-volume

The next step is to enable encryption and pass the path and password of the truststore generated by the initContainer

CouchbaseEnvironment  env = DefaultCouchbaseEnvironment.builder().sslEnabled(true)
  .sslTruststoreFile("/var/run/secrets/java.io/keystores/truststore.jks")
  .sslTruststorePassword("changeit").build();
cachedCluster = CouchbaseCluster.create(env, "couchbase-db")
  .authenticate("Administrator", "password");

Deploy your application; if successful, you should see similar output in container logs

2019-08-28 15:33:27.952  INFO 1 --- [cTaskExecutor-1]
com.couchbase.client.core.CouchbaseCore  : CouchbaseEnvironment:
{sslEnabled=true, sslKeystoreFile='null', sslTruststoreFile='/var/run/secrets/java.io/keystores/truststore.jks',
sslKeystorePassword=false, sslTruststorePassword=true,
sslKeystore=null, sslTruststore=null, bootstrapHttpEnabled=true,
 bootstrapCarrierEnabled=true, bootstrapHttpDirectPort=8091,
...