Background
Say, we have a RabbitMQ server where SSL/TLS
is enabled, so thats clients are enforced to connect to the server using the AMQPS
protocol via port 5671
. To further continue with this article, it is advisable to have a basic understanding of key concepts like SSL
, TLS
, mutual TLS
, etc. Knowing the fundamentals of kubernetes is also important as the client under this article is assumed to be running in a kubernetes pod.
Following the below instructions or steps will help in configuring the client and the necessary credentials for the above purpose. Refer here for general, official documentation and samples.
Prerequisites
- RabbitMQ server with SSL/TLS enabled
- Kubernetes environment and required privileges to create a pod (client application)
- Client certificate, key, and root certificate (for mutual TLS)
- Spring boot java application (client application)
- Java installed & %PATH% configured accordingly
Getting Started
As spring java client requires a keystore to pull the certificates from rather than a certificate file path, we need to create the required keystores in the first place and then import the required certificates and keys into. This is one of the main reasons for this article to be uniquely focussed for spring or java client applications.
This article doesn’t focus on keytool
and the creation of the keystore and it’s advanced options, but provided below are some basic instructions and commands that can be used to import the certificates and keys into a keystore. Please refer to the official keytool
documentation for more information.
As mentioned in the prerequisites, make sure that the required certificates are available. Ensure that the right CN
or SAN
is used. Ideally, for the client certificates, $(hostname)
can be used or else if permitted, a wildcard *
can be used. Refer here for more details on this topic.
To import a client certificate client.crt
and its corresponding private key client.key
into a keystore file called keystore.jks
using keytool
, the certificate should be usually in PEM
or DER
format, and the private key in PEM
or PKCS#12
format. You can convert them using OpenSSL
, as below. You may protect the client.p12
file using a password, which will be the source keystore password source_password
in the later steps.
openssl pkcs12 -export -in client.crt -inkey client.key -out client.p12 -name client_certs
Next, we need to import client.p12
file, as below. We may be prompted for creating password for the new jks keystore, so provide one as needed.
keytool -importkeystore -srckeystore client.p12 -srcstoretype PKCS12 -destkeystore keystore.jks -deststoretype JKS -srcstorepass <source_password> -deststorepass <destination_password>
Now, we need to create truststore.jks
and import certificates from trusted Certificate Authorities (CAs), as below.
keytool -import -trustcacerts -file root_ca.crt -keystore truststore.jks -alias ca_certs
We have both keystore
ane truststore
created, need to get them into kubernetes so that it can be consumed by the client application. For that, let’s create the kubernetes secrets to store the keystore and truststore files, using below commands. Please not that these secrets to be created in the same namespace as your application resides.
kubectl create secret generic rabbitmq-spring-client-keystore --from-file=keystore.jks
kubectl create secret generic rabbitmq-spring-client-truststore --from-file=truststore.jks
We need to add the password of the each keystores into kubernetes secrets. Below is the sample yaml (secret_definition.yaml
) we can use. Replace <BASE64_ENCODED_KEYSTORE_PASSWORD>
with the correct passwords that are base64 encoded, sample command echo -n "password_value" | base64
. Use kubectl apply -f secret_definition.yaml
command to create the below kubernetes secrets. If we don’t want to create an yaml file, use the second option below.
apiVersion: v1
kind: Secret
metadata:
name: rabbitmq-spring-client-keystore-password
type: Opaque
data:
password: <BASE64_ENCODED_KEYSTORE_PASSWORD>
---
apiVersion: v1
kind: Secret
metadata:
name: rabbitmq-spring-client-truststore-password
type: Opaque
data:
password: <BASE64_ENCODED_TRUSTSTORE_PASSWORD>
Second option is to use inline command as below to create the kubernetes secrets.
kubectl apply -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
name: rabbitmq-spring-client-keystore-password
type: Opaque
data:
password: $(echo -n "password_value" | base64)
---
apiVersion: v1
kind: Secret
metadata:
name: rabbitmq-spring-client-truststore-password
type: Opaque
data:
password: $(echo -n "password_value" | base64)
EOF
Now that the secrets are created, we need to have them used by the application kubernetes pods, for which we need to mount these keystore files in the kubernetes pod
or deployment
similar to the definition as below. A full deployment_definition.yaml
file content looks like the below sample. Use kubectl apply -f deployment_definition.yaml
command to create the below kubernetes deployment.
apiVersion: apps/v1
kind: Deployment
metadata:
name: rabbitmq-spring-client-app
spec:
replicas: 1
selector:
matchLabels:
app: rabbitmq-spring-client-app
template:
metadata:
labels:
app: rabbitmq-spring-client-app
spec:
containers:
- name: rabbitmq-spring-client-app
image: rabbitmq-spring-client-app-image:tag
ports:
- containerPort: 8080
volumeMounts:
- name: rabbitmq-spring-client-keystore
mountPath: /etc/keystore
readOnly: true
- name: rabbitmq-spring-client-truststore
mountPath: /etc/truststore
readOnly: true
env:
- name: KEYSTORE_PASSWORD
valueFrom:
secretKeyRef:
name: rabbitmq-spring-client-keystore-password
key: password
- name: TRUSTSTORE_PASSWORD
valueFrom:
secretKeyRef:
name: rabbitmq-spring-client-truststore-password
key: password
volumes:
- name: rabbitmq-spring-client-keystore
secret:
secretName: rabbitmq-spring-client-keystore
- name: rabbitmq-spring-client-truststore
secret:
secretName: rabbitmq-spring-client-truststore
We are into the final step, where we have to apply the configuration (yaml) to the spring application as below. Below configuration can be modified according to the requirements, say tls-version
, key-store-type
, etc.
spring:
rabbitmq:
host: <rabbitmq-host>
port: 5671
username: <rabbitmq-username>
password: <rabbitmq-password>
ssl:
enabled: true
key-store: file:/etc/keystore/keystore.jks
key-store-password: ${KEYSTORE_PASSWORD}
key-store-type: JKS
trust-store: file:/etc/truststore/truststore.jks
trust-store-password: ${TRUSTSTORE_PASSWORD}
trust-store-type: JKS
verify-hostname: true
tls-version: TLSv1.2
If the certificates and credentials are valid, the client should be able to establish a successful connection with the RabbitMQ Server.
Disclaimer: I have not tested the above example definitions and commands, just scripted them for demonstration purposes only. However, this should give an overall idea of,
what to do!
Additional References
Hope you had fun coding!
comments powered by Disqus