Configuring Spinnaker services to connect to Redis over mTLS
Introduction
Mutual TLS, or mTLS, is a method used for mutual authentication and ensures that both the server and the client authenticate each other simultaneously. This is achieved by the server providing a server certificate to the client and the client providing a client certificate to the server. These certificates are signed by the same Certificate Authority (CA). This article explains how to enable mTLS on Redis and configure Spinnaker services to connect to Redis over mTLS.
Prerequisites
Below are the prerequisites to enable mTLS between Spinnaker and Redis instance
- Redis server with version 6 and above. Older versions of Redis do not support mTLS
- Recent Spinnaker distribution
- Tools like OpenSSL and Keytool for generating certificates, signing them, and importing them to Java keystore.
Instructions
Table of Contents
- Generating certificates and enabling mTLS on Redis* Enable Redis to Accept mTLS Requests* Enable Spinnaker to use mTLS to connect to Redis* Considerations before enabling mTLS for Redis in Spinnaker
Generating certificates and enabling mTLS on Redis
The CA, server, and client certs should be created before proceeding with the documentation below. While there are various ways to generate this, the script https://github.com/redis/redis/blob/unstable/utils/gen-test-certs.sh should help get started and generate the certs.
Enable Redis to Accept mTLS Requests
The next step is to enable Redis with mTLS enabled. This can be enabled on the redis.conf or when starting the Redis process.
Enabling TLS in redis.conf
Adding the below config to the redis.conf
file will allow TLS on Redis.
port 0
tls-port 6379
tls-cert-file /usr/local/etc/redis/tls/redis.crt tls-key-file /usr/local/etc/redis/tls/redis.key
tls-ca-cert-file /usr/local/etc/redis/tls/ca.crt Enabling TLS through start-up command
containers:
- name: redis-ssl
image: redis:6.2
command:
- redis-server
- "/redis-master/redis.conf"
args:
- --TLS-port 6379
- --port 0
- --TLS-cert-file /certs/redis.crt
- --TLS-key-file /certs/redis.key
- --TLS-ca-cert-file /certs/ca.crt
- --TLS-auth-clients yes
If running Redis as a container on the kubernetes cluster, the certificates must be mounted on the container through a secret.
Validate login using redis-cli
Connect to the Redis instance over mTLS using the certificates and keys created above to validate the login:
$redis-cli -u rediss://redis-ssl.instance.endpoint --tls --cert redis_client.crt --key redis_client.key --cacert ca.crt
In case of issues with certs, the login shall fail with errors similar to the one below, and the prompt shall default to not connected
.
Could not connect to Redis at redis-ssl.instance.endpoint:6379: SSL_connect failed: certificate verify failed
not connected> exit
Enable Spinnaker to use mTLS to connect to Redis
All the services in the core Spinnaker are built on Java, while a couple of services in Armory CDSH are built on goLang (Namely Dinghy and Terraformer). Although the GoLang services support Redis connections with TLS enabled, they do not currently support mTLS. To have Spinnaker Java-based services connect to Redis through mTLS, the below steps are to be followed:
To begin with, Spinnaker has to point to the Redis with mTLS enabled. To start this, the following content has to be added under ~/.hal/$DEPLOYMENT/service-settings/redis.yml
for Halyard installations
overrideBaseUrl: rediss://redis-ssl.instance.endpoint:6379
skipLifeCycleManagement: true
A similar config should be added in the spec.spinnakerConfig.service-settings
section for Operator
apiVersion: spinnaker.armory.io/v1alpha2
kind: SpinnakerService
metadata:
name: spinnaker
spec:
spinnakerConfig:
service-settings:
redis:
overrideBaseUrl: rediss://redis-ssl.instance.endpoint:6379
skipLifeCycleManagement: true
The above setting would override the Redis URL for all services. If the Redis endpoints are to be overridden for specific services, the below config should be added under the profiles section
for each serviceFor Halyard installations, for the example of the Gate service, add the configuration in the file ~/.hal/$DEPLOYMENT/profiles/gate-local.yml
. Each service would have its corresponding YAML file and should be created if they do not exist.
redis:
baseUrl: rediss://redis-ssl.instance.endpoint:6379
enabled: true
In Operator installations, administrators will need to add the following changes for each service in the spec.spinnakerConfig.profiles
section. In the example below, it is being added to the Gate service:
apiVersion: spinnaker.armory.io/v1alpha2
kind: SpinnakerService
metadata:
name: spinnaker # name doesn't matter since this is a patch
spec:
# spec.spinnakerConfig - This section is how to specify configuration spinnaker
spinnakerConfig:
profiles:
gate:
redis:
baseUrl: rediss://redis-ssl.instance.endpoint:6379
enabled: true
Administrators will also need to make the following modification to Gate
. In Halyard, add the following configuration to the ~/.hal/$DEPLOYMENT/profiles/gate-local.yml
file
redis:
configuration:
secure:
true
In Operator installations, administrators will need to add the following changes for each service in the spec.spinnakerConfig.profiles.gate
section.
apiVersion: spinnaker.armory.io/v1alpha2
kind: SpinnakerService
metadata:
name: spinnaker
spec:
spinnakerConfig:
profiles:
gate:
redis:
configuration:
secure:
true
Create a Keystore file that stores the key and certificate created earlier. The certs and the key have to be converted to pkcs12 format
:
openssl pkcs12 -export \\
-in redis_client.crt \\
-inkey redis_client.key \\
-out redis-keystore.p12 \\
-name "redis-keystore"
###Import the pkcs12 file into Java keystore
keytool -genkey \\
-dname "cn=Generic-cert" \\
-alias truststorekey \\
-keyalg RSA \\
-keystore ./redis-truststore.p12 \\
-keypass "Somepassword" \\
-storepass "Somepassword" \\
-storetype pkcs12
Create a **Trust Store** file and add the Redis cluster certificate to it
keytool -import \\
-keystore cacerts \\
-file ./ca.crt \\
-alias redis-truststore
Create secrets with the Keystore and the Trust Store.
kubectl create secret generic internal-trust-store --from-file cacerts
kubectl create secret generic redis-key-store --from-file redis-keystore.p12
Mount the secrets to Spinnaker services. Mount the Keystore and Trust Store from the secret onto the Spinnaker service(s).For Halyard installations, for the example of the Gate service, add the configuration in the file ~/.hal/$DEPLOYMENT/service-settings/$SERVICE.yml
. Each Spinnaker service would have its own corresponding YAML file and should be created if they do not exist.
kubernetes:
volumes:
- id: internal-trust-store
mountPath: /etc/ssl/certs/java
type: secret
- id: redis-key-store
mountPath: /tmp/certs
type: secret
For Operator installations, the changes should be added in the spec.spinnakerConfig.service-settings
section for each service. For example, for Gate, customers should make the following change:
apiVersion: spinnaker.armory.io/v1alpha2
kind: SpinnakerService
metadata:
name: spinnaker
spec:
spinnakerConfig:
service-settings:
gate:
kubernetes:
volumes:
- id: internal-trust-store
mountPath: /etc/ssl/certs/java
type: secret
- id: redis-key-store
mountPath: /tmp/certs
type: secret
Supply JAVA_OPTS with the keystore cert and the password in the environment variable section.For Halyard installations, for the example of the Gate service, add the configuration in the file ~/.hal/$DEPLOYMENT/service-settings/$SERVICE.yml
. Each Spinnaker service would have its own corresponding YAML file and should be created if they do not exist.
env:
JAVA_OPTS: -Djavax.net.ssl.keyStore=/tmp/certs/redis-keystore.p12 -Djavax.net.ssl.keyStorePassword=somepassword
For Operator installations, the changes should be added in the spec.spinnakerConfig.service-settings
section for each service. For example, for Gate, customers should make the following change:
apiVersion: spinnaker.armory.io/v1alpha2
kind: SpinnakerService
metadata:
name: spinnaker
spec:
spinnakerConfig:
service-settings:
gate:
env:
JAVA_OPTS: -Djavax.net.ssl.keyStore=/tmp/certs/redis-keystore.p12 -Djavax.net.ssl.keyStorePassword=somepassword
- Apply all the above changes, and Spinnaker should be able to connect to Redis over mTLS. Considerations before enabling mTLS for Redis in Spinnaker Services such as Clouddriver, Front50, Orca, and Fiat support SQL and Redis. The features in these services can be split to use either Redis or SQL. For those services, it is recommended not to use mTLS as the service would try to connect to the SQL instance with the mTLS certificate, which shall be rejected by the SQL server, causing the service not to start. For such cases, overriding the Redis endpoint at the service level is recommended, as discussed above.