Configuring couchbase SSL for dynamic certificates in OpenShift
Table of Contents
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
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,
...