diff --git a/api/v1beta1/common_types.go b/api/v1beta1/common_types.go index 92134a7de..890795946 100644 --- a/api/v1beta1/common_types.go +++ b/api/v1beta1/common_types.go @@ -54,6 +54,15 @@ type RedisExporter struct { EnvVars *[]corev1.EnvVar `json:"env,omitempty"` } +// TLS Configuration for redis instances +type TLSConfig struct { + CaKeyFile string `json:"ca,omitempty"` + CertKeyFile string `json:"cert,omitempty"` + KeyFile string `json:"key,omitempty"` + // Reference to secret which contains the certificates + Secret corev1.SecretVolumeSource `json:"secret"` +} + // Sidecar for each Redis pods type Sidecar struct { Name string `json:"name"` diff --git a/api/v1beta1/redis_types.go b/api/v1beta1/redis_types.go index 9eb6d9881..2e7a9b745 100644 --- a/api/v1beta1/redis_types.go +++ b/api/v1beta1/redis_types.go @@ -35,6 +35,7 @@ type RedisSpec struct { PriorityClassName string `json:"priorityClassName,omitempty"` Affinity *corev1.Affinity `json:"affinity,omitempty"` Tolerations *[]corev1.Toleration `json:"tolerations,omitempty"` + TLS *TLSConfig `json:"TLS,omitempty"` ReadinessProbe *corev1.Probe `json:"readinessProbe,omitempty" protobuf:"bytes,11,opt,name=readinessProbe"` LivenessProbe *corev1.Probe `json:"livenessProbe,omitempty" protobuf:"bytes,11,opt,name=livenessProbe"` Sidecars *[]Sidecar `json:"sidecars,omitempty"` diff --git a/api/v1beta1/rediscluster_types.go b/api/v1beta1/rediscluster_types.go index fc0310b91..9a97f3ef2 100644 --- a/api/v1beta1/rediscluster_types.go +++ b/api/v1beta1/rediscluster_types.go @@ -35,6 +35,7 @@ type RedisClusterSpec struct { PriorityClassName string `json:"priorityClassName,omitempty"` Tolerations *[]corev1.Toleration `json:"tolerations,omitempty"` Resources *corev1.ResourceRequirements `json:"resources,omitempty"` + TLS *TLSConfig `json:"TLS,omitempty"` Sidecars *[]Sidecar `json:"sidecars,omitempty"` } diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index e1bc1ef70..34ea30de6 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -1,4 +1,3 @@ -//go:build !ignore_autogenerated // +build !ignore_autogenerated /* @@ -220,6 +219,11 @@ func (in *RedisClusterSpec) DeepCopyInto(out *RedisClusterSpec) { *out = new(v1.ResourceRequirements) (*in).DeepCopyInto(*out) } + if in.TLS != nil { + in, out := &in.TLS, &out.TLS + *out = new(TLSConfig) + (*in).DeepCopyInto(*out) + } if in.Sidecars != nil { in, out := &in.Sidecars, &out.Sidecars *out = new([]Sidecar) @@ -504,6 +508,11 @@ func (in *RedisSpec) DeepCopyInto(out *RedisSpec) { } } } + if in.TLS != nil { + in, out := &in.TLS, &out.TLS + *out = new(TLSConfig) + (*in).DeepCopyInto(*out) + } if in.ReadinessProbe != nil { in, out := &in.ReadinessProbe, &out.ReadinessProbe *out = new(v1.Probe) @@ -599,3 +608,19 @@ func (in *Storage) DeepCopy() *Storage { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TLSConfig) DeepCopyInto(out *TLSConfig) { + *out = *in + in.Secret.DeepCopyInto(&out.Secret) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLSConfig. +func (in *TLSConfig) DeepCopy() *TLSConfig { + if in == nil { + return nil + } + out := new(TLSConfig) + in.DeepCopyInto(out) + return out +} diff --git a/config/crd/bases/redis.redis.opstreelabs.in_redis.yaml b/config/crd/bases/redis.redis.opstreelabs.in_redis.yaml index d4693dd3c..a1e3f6a01 100644 --- a/config/crd/bases/redis.redis.opstreelabs.in_redis.yaml +++ b/config/crd/bases/redis.redis.opstreelabs.in_redis.yaml @@ -36,6 +36,80 @@ spec: spec: description: RedisSpec defines the desired state of Redis properties: + TLS: + description: TLS Configuration for redis instances + properties: + ca: + type: string + cert: + type: string + key: + type: string + secret: + description: Reference to secret which contains the certificates + properties: + defaultMode: + description: 'Optional: mode bits used to set permissions + on created files by default. Must be an octal value between + 0000 and 0777 or a decimal value between 0 and 511. YAML + accepts both octal and decimal values, JSON requires decimal + values for mode bits. Defaults to 0644. Directories within + the path are not affected by this setting. This might be + in conflict with other options that affect the file mode, + like fsGroup, and the result can be other mode bits set.' + format: int32 + type: integer + items: + description: If unspecified, each key-value pair in the Data + field of the referenced Secret will be projected into the + volume as a file whose name is the key and content is the + value. If specified, the listed keys will be projected into + the specified paths, and unlisted keys will not be present. + If a key is specified which is not present in the Secret, + the volume setup will error unless it is marked optional. + Paths must be relative and may not contain the '..' path + or start with '..'. + items: + description: Maps a string key to a path within a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits used to set permissions + on this file. Must be an octal value between 0000 + and 0777 or a decimal value between 0 and 511. YAML + accepts both octal and decimal values, JSON requires + decimal values for mode bits. If not specified, the + volume defaultMode will be used. This might be in + conflict with other options that affect the file mode, + like fsGroup, and the result can be other mode bits + set.' + format: int32 + type: integer + path: + description: The relative path of the file to map the + key to. May not be an absolute path. May not contain + the path element '..'. May not start with the string + '..'. + type: string + required: + - key + - path + type: object + type: array + optional: + description: Specify whether the Secret or its keys must be + defined + type: boolean + secretName: + description: 'Name of the secret in the pod''s namespace to + use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + type: string + type: object + required: + - secret + type: object affinity: description: Affinity is a group of affinity scheduling rules. properties: @@ -1642,6 +1716,81 @@ spec: redis: description: RedisSpec defines the desired state of Redis properties: + TLS: + description: TLS Configuration for redis instances + properties: + ca: + type: string + cert: + type: string + key: + type: string + secret: + description: Reference to secret which contains the certificates + properties: + defaultMode: + description: 'Optional: mode bits used to set permissions + on created files by default. Must be an octal value + between 0000 and 0777 or a decimal value between 0 and + 511. YAML accepts both octal and decimal values, JSON + requires decimal values for mode bits. Defaults to 0644. + Directories within the path are not affected by this + setting. This might be in conflict with other options + that affect the file mode, like fsGroup, and the result + can be other mode bits set.' + format: int32 + type: integer + items: + description: If unspecified, each key-value pair in the + Data field of the referenced Secret will be projected + into the volume as a file whose name is the key and + content is the value. If specified, the listed keys + will be projected into the specified paths, and unlisted + keys will not be present. If a key is specified which + is not present in the Secret, the volume setup will + error unless it is marked optional. Paths must be relative + and may not contain the '..' path or start with '..'. + items: + description: Maps a string key to a path within a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits used to set permissions + on this file. Must be an octal value between 0000 + and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON + requires decimal values for mode bits. If not + specified, the volume defaultMode will be used. + This might be in conflict with other options that + affect the file mode, like fsGroup, and the result + can be other mode bits set.' + format: int32 + type: integer + path: + description: The relative path of the file to map + the key to. May not be an absolute path. May not + contain the path element '..'. May not start with + the string '..'. + type: string + required: + - key + - path + type: object + type: array + optional: + description: Specify whether the Secret or its keys must + be defined + type: boolean + secretName: + description: 'Name of the secret in the pod''s namespace + to use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + type: string + type: object + required: + - secret + type: object affinity: description: Affinity is a group of affinity scheduling rules. properties: diff --git a/config/crd/bases/redis.redis.opstreelabs.in_redisclusters.yaml b/config/crd/bases/redis.redis.opstreelabs.in_redisclusters.yaml index acdab1009..000df56cf 100644 --- a/config/crd/bases/redis.redis.opstreelabs.in_redisclusters.yaml +++ b/config/crd/bases/redis.redis.opstreelabs.in_redisclusters.yaml @@ -53,6 +53,80 @@ spec: spec: description: RedisClusterSpec defines the desired state of RedisCluster properties: + TLS: + description: TLS Configuration for redis instances + properties: + ca: + type: string + cert: + type: string + key: + type: string + secret: + description: Reference to secret which contains the certificates + properties: + defaultMode: + description: 'Optional: mode bits used to set permissions + on created files by default. Must be an octal value between + 0000 and 0777 or a decimal value between 0 and 511. YAML + accepts both octal and decimal values, JSON requires decimal + values for mode bits. Defaults to 0644. Directories within + the path are not affected by this setting. This might be + in conflict with other options that affect the file mode, + like fsGroup, and the result can be other mode bits set.' + format: int32 + type: integer + items: + description: If unspecified, each key-value pair in the Data + field of the referenced Secret will be projected into the + volume as a file whose name is the key and content is the + value. If specified, the listed keys will be projected into + the specified paths, and unlisted keys will not be present. + If a key is specified which is not present in the Secret, + the volume setup will error unless it is marked optional. + Paths must be relative and may not contain the '..' path + or start with '..'. + items: + description: Maps a string key to a path within a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits used to set permissions + on this file. Must be an octal value between 0000 + and 0777 or a decimal value between 0 and 511. YAML + accepts both octal and decimal values, JSON requires + decimal values for mode bits. If not specified, the + volume defaultMode will be used. This might be in + conflict with other options that affect the file mode, + like fsGroup, and the result can be other mode bits + set.' + format: int32 + type: integer + path: + description: The relative path of the file to map the + key to. May not be an absolute path. May not contain + the path element '..'. May not start with the string + '..'. + type: string + required: + - key + - path + type: object + type: array + optional: + description: Specify whether the Secret or its keys must be + defined + type: boolean + secretName: + description: 'Name of the secret in the pod''s namespace to + use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + type: string + type: object + required: + - secret + type: object clusterSize: format: int32 minimum: 3 @@ -2620,6 +2694,80 @@ spec: status: description: RedisClusterSpec defines the desired state of RedisCluster properties: + TLS: + description: TLS Configuration for redis instances + properties: + ca: + type: string + cert: + type: string + key: + type: string + secret: + description: Reference to secret which contains the certificates + properties: + defaultMode: + description: 'Optional: mode bits used to set permissions + on created files by default. Must be an octal value between + 0000 and 0777 or a decimal value between 0 and 511. YAML + accepts both octal and decimal values, JSON requires decimal + values for mode bits. Defaults to 0644. Directories within + the path are not affected by this setting. This might be + in conflict with other options that affect the file mode, + like fsGroup, and the result can be other mode bits set.' + format: int32 + type: integer + items: + description: If unspecified, each key-value pair in the Data + field of the referenced Secret will be projected into the + volume as a file whose name is the key and content is the + value. If specified, the listed keys will be projected into + the specified paths, and unlisted keys will not be present. + If a key is specified which is not present in the Secret, + the volume setup will error unless it is marked optional. + Paths must be relative and may not contain the '..' path + or start with '..'. + items: + description: Maps a string key to a path within a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits used to set permissions + on this file. Must be an octal value between 0000 + and 0777 or a decimal value between 0 and 511. YAML + accepts both octal and decimal values, JSON requires + decimal values for mode bits. If not specified, the + volume defaultMode will be used. This might be in + conflict with other options that affect the file mode, + like fsGroup, and the result can be other mode bits + set.' + format: int32 + type: integer + path: + description: The relative path of the file to map the + key to. May not be an absolute path. May not contain + the path element '..'. May not start with the string + '..'. + type: string + required: + - key + - path + type: object + type: array + optional: + description: Specify whether the Secret or its keys must be + defined + type: boolean + secretName: + description: 'Name of the secret in the pod''s namespace to + use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + type: string + type: object + required: + - secret + type: object clusterSize: format: int32 minimum: 3 diff --git a/example/tls_enabled/redis-cluster.yaml b/example/tls_enabled/redis-cluster.yaml new file mode 100644 index 000000000..ac554922f --- /dev/null +++ b/example/tls_enabled/redis-cluster.yaml @@ -0,0 +1,56 @@ +--- +apiVersion: redis.redis.opstreelabs.in/v1beta1 +kind: RedisCluster +metadata: + name: redis-cluster +spec: + clusterSize: 3 + + TLS: + ca: ca.key + cert: tls.crt + key: tls.key + secret: + secretName: sample-cert + + kubernetesConfig: + image: quay.io/opstree/redis:v6.2.5 + imagePullPolicy: IfNotPresent + resources: + requests: + cpu: 101m + memory: 128Mi + limits: + cpu: 101m + memory: 128Mi + redisSecret: + name: redis-secret + key: password + + redisExporter: + enabled: false + image: quay.io/opstree/redis-exporter:1.0 + imagePullPolicy: Always + resources: + requests: + cpu: 100m + memory: 128Mi + limits: + cpu: 100m + memory: 128Mi + + redisLeader: + redisConfig: + additionalRedisConfig: redis-external-config + + redisFollower: + redisConfig: + additionalRedisConfig: redis-external-config + + storage: + volumeClaimTemplate: + spec: + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: 1Gi diff --git a/example/tls_enabled/redis-standalone.yaml b/example/tls_enabled/redis-standalone.yaml new file mode 100644 index 000000000..024b02240 --- /dev/null +++ b/example/tls_enabled/redis-standalone.yaml @@ -0,0 +1,45 @@ +# Examples of creating a valid TLS certificate can be found at ./config/certmanager via Cert-manager +--- +apiVersion: redis.redis.opstreelabs.in/v1beta1 +kind: Redis +metadata: + name: redis-standalone +spec: + + TLS: + ca: ca.key + cert: tls.crt + key: tls.key + secret: + secretName: sample-cert + + kubernetesConfig: + image: quay.io/opstree/redis:v6.2.5 + imagePullPolicy: IfNotPresent + resources: + requests: + cpu: 101m + memory: 128Mi + limits: + cpu: 101m + memory: 128Mi + + redisExporter: + enabled: false + image: quay.io/opstree/redis-exporter:1.0 + imagePullPolicy: Always + resources: + requests: + cpu: 100m + memory: 128Mi + limits: + cpu: 100m + memory: 128Mi + + storage: + volumeClaimTemplate: + spec: + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: 1Gi diff --git a/k8sutils/redis.go b/k8sutils/redis.go index 22013db6c..695896b52 100644 --- a/k8sutils/redis.go +++ b/k8sutils/redis.go @@ -58,10 +58,27 @@ func ExecuteRedisClusterCommand(cr *redisv1beta1.RedisCluster) { cmd = append(cmd, "-a") cmd = append(cmd, pass) } + cmd = append(cmd, getRedisTLSArgs(cr.Spec.TLS, cr.ObjectMeta.Name+"-leader-0")...) logger.Info("Redis cluster creation command is", "Command", cmd) executeCommand(cr, cmd, cr.ObjectMeta.Name+"-leader-0") } +func getRedisTLSArgs(tlsConfig *redisv1beta1.TLSConfig, clientHost string) []string { + cmd := []string{} + if tlsConfig != nil { + cmd = append(cmd, "--tls") + cmd = append(cmd, "--cert") + cmd = append(cmd, "/tls/tls.crt") + cmd = append(cmd, "--key") + cmd = append(cmd, "/tls/tls.key") + cmd = append(cmd, "--cacert") + cmd = append(cmd, "/tls/ca.crt") + cmd = append(cmd, "-h") + cmd = append(cmd, clientHost) + } + return cmd +} + // createRedisReplicationCommand will create redis replication creation command func createRedisReplicationCommand(cr *redisv1beta1.RedisCluster, leaderPod RedisDetails, followerPod RedisDetails) []string { logger := generateRedisManagerLogger(cr.Namespace, cr.ObjectMeta.Name) @@ -78,6 +95,7 @@ func createRedisReplicationCommand(cr *redisv1beta1.RedisCluster, leaderPod Redi cmd = append(cmd, "-a") cmd = append(cmd, pass) } + cmd = append(cmd, getRedisTLSArgs(cr.Spec.TLS, leaderPod.PodName)...) logger.Info("Redis replication creation command is", "Command", cmd) return cmd } @@ -226,15 +244,17 @@ func configureRedisClient(cr *redisv1beta1.RedisCluster, podName string) *redis. logger.Error(err, "Error in getting redis password") } client = redis.NewClient(&redis.Options{ - Addr: getRedisServerIP(redisInfo) + ":6379", - Password: pass, - DB: 0, + Addr: getRedisServerIP(redisInfo) + ":6379", + Password: pass, + DB: 0, + TLSConfig: getRedisTLSConfig(cr, redisInfo), }) } else { client = redis.NewClient(&redis.Options{ - Addr: getRedisServerIP(redisInfo) + ":6379", - Password: "", - DB: 0, + Addr: getRedisServerIP(redisInfo) + ":6379", + Password: "", + DB: 0, + TLSConfig: getRedisTLSConfig(cr, redisInfo), }) } return client diff --git a/k8sutils/secrets.go b/k8sutils/secrets.go index 2490b0af4..6f14db196 100644 --- a/k8sutils/secrets.go +++ b/k8sutils/secrets.go @@ -2,6 +2,10 @@ package k8sutils import ( "context" + "crypto/tls" + "crypto/x509" + redisv1beta1 "redis-operator/api/v1beta1" + "github.com/go-logr/logr" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" logf "sigs.k8s.io/controller-runtime/pkg/log" @@ -29,3 +33,51 @@ func secretLogger(namespace string, name string) logr.Logger { reqLogger := log.WithValues("Request.Secret.Namespace", namespace, "Request.Secret.Name", name) return reqLogger } + +func getRedisTLSConfig(cr *redisv1beta1.RedisCluster, redisInfo RedisDetails) *tls.Config { + if cr.Spec.TLS != nil { + reqLogger := log.WithValues("Request.Namespace", cr.Namespace, "Request.Name", cr.ObjectMeta.Name) + secretName, err := generateK8sClient().CoreV1().Secrets(cr.Namespace).Get(context.TODO(), cr.Spec.TLS.Secret.SecretName, metav1.GetOptions{}) + if err != nil { + reqLogger.Error(err, "Failed in getting TLS secret for redis") + } + + var ( + tlsClientCert []byte + tlsClientKey []byte + tlsCaCertificate []byte + tlsCaCertificates *x509.CertPool + tlsClientCertificates []tls.Certificate + ) + for key, value := range secretName.Data { + if key == cr.Spec.TLS.CaKeyFile || key == "ca.crt" { + tlsCaCertificate = value + } else if key == cr.Spec.TLS.CertKeyFile || key == "tls.key" { + tlsClientKey = value + } else if key == cr.Spec.TLS.KeyFile || key == "tls.crt" { + tlsClientCert = value + } + } + + cert, err := tls.X509KeyPair(tlsClientCert, tlsClientKey) + if err != nil { + reqLogger.Error(err, "Couldn't load TLS client key pair") + } + tlsClientCertificates = append(tlsClientCertificates, cert) + + tlsCaCertificates = x509.NewCertPool() + ok := tlsCaCertificates.AppendCertsFromPEM(tlsCaCertificate) + if !ok { + reqLogger.Info("Failed to load CA Certificates from Secret") + } + + return &tls.Config{ + Certificates: tlsClientCertificates, + ServerName: redisInfo.PodName, + RootCAs: tlsCaCertificates, + MinVersion: 2, + ClientAuth: 0, + } + } + return nil +} diff --git a/k8sutils/statefulset.go b/k8sutils/statefulset.go index d869e5552..74d6b4688 100644 --- a/k8sutils/statefulset.go +++ b/k8sutils/statefulset.go @@ -2,6 +2,7 @@ package k8sutils import ( "context" + "path" redisv1beta1 "redis-operator/api/v1beta1" "sort" @@ -46,6 +47,7 @@ type containerParameters struct { SecretName *string SecretKey *string PersistenceEnabled *bool + TLSConfig *redisv1beta1.TLSConfig ReadinessProbe *corev1.Probe LivenessProbe *corev1.Probe } @@ -172,8 +174,19 @@ func generateContainerDef(name string, containerParams containerParameters, enab Name: name, Image: containerParams.Image, ImagePullPolicy: containerParams.ImagePullPolicy, - Env: getEnvironmentVariables(containerParams.Role, containerParams.EnabledPassword, containerParams.SecretName, containerParams.SecretKey, containerParams.PersistenceEnabled, containerParams.RedisExporterEnv), - VolumeMounts: getVolumeMount(name, containerParams.PersistenceEnabled, externalConfig), + Env: getEnvironmentVariables( + containerParams.Role, + containerParams.EnabledPassword, + containerParams.SecretName, + containerParams.SecretKey, + containerParams.PersistenceEnabled, + containerParams.RedisExporterEnv, + containerParams.TLSConfig, + ), + Resources: *containerParams.Resources, + ReadinessProbe: getProbeInfo(), + LivenessProbe: getProbeInfo(), + VolumeMounts: getVolumeMount(name, containerParams.PersistenceEnabled, externalConfig, containerParams.TLSConfig), }, } if containerParams.ReadinessProbe != nil { @@ -210,13 +223,61 @@ func generateContainerDef(name string, containerParams containerParameters, enab return containerDefinition } +func GenerateTLSEnvironmentVariables(tlsconfig *redisv1beta1.TLSConfig) []corev1.EnvVar { + var envVars []corev1.EnvVar + root := "/tls/" + + // get and set Defaults + caCert := "ca.crt" + tlsCert := "tls.crt" + tlsCertKey := "tls.key" + + if tlsconfig.CaKeyFile != "" { + caCert = tlsconfig.CaKeyFile + } + if tlsconfig.CertKeyFile != "" { + tlsCert = tlsconfig.CertKeyFile + } + if tlsconfig.KeyFile != "" { + tlsCertKey = tlsconfig.KeyFile + } + + envVars = append(envVars, corev1.EnvVar{ + Name: "TLS_MODE", + Value: "true", + }) + envVars = append(envVars, corev1.EnvVar{ + Name: "REDIS_TLS_CA_KEY", + Value: path.Join(root, caCert), + }) + envVars = append(envVars, corev1.EnvVar{ + Name: "REDIS_TLS_CERT", + Value: path.Join(root, tlsCert), + }) + envVars = append(envVars, corev1.EnvVar{ + Name: "REDIS_TLS_CERT_KEY", + Value: path.Join(root, tlsCertKey), + }) + return envVars +} + // enableRedisMonitoring will add Redis Exporter as sidecar container func enableRedisMonitoring(params containerParameters) corev1.Container { exporterDefinition := corev1.Container{ Name: redisExporterContainer, Image: params.RedisExporterImage, ImagePullPolicy: params.RedisExporterImagePullPolicy, - Env: getEnvironmentVariables(params.Role, params.EnabledPassword, params.SecretName, params.SecretKey, params.PersistenceEnabled, params.RedisExporterEnv), + Env: getEnvironmentVariables( + params.Role, + params.EnabledPassword, + params.SecretName, + params.SecretKey, + params.PersistenceEnabled, + params.RedisExporterEnv, + params.TLSConfig, + ), + Resources: *params.RedisExporterResources, + VolumeMounts: getVolumeMount("", nil, nil, params.TLSConfig), // We need/want the tls-certs but we DON'T need the PVC (if one is available) } if params.RedisExporterResources != nil { exporterDefinition.Resources = *params.RedisExporterResources @@ -225,24 +286,32 @@ func enableRedisMonitoring(params containerParameters) corev1.Container { } // getVolumeMount gives information about persistence mount -func getVolumeMount(name string, persistenceEnabled *bool, externalConfig *string) []corev1.VolumeMount { - var volumeMounts []corev1.VolumeMount - if persistenceEnabled != nil && *persistenceEnabled { - volumeMounts = []corev1.VolumeMount{ - { - Name: name, - MountPath: "/data", - }, - } +func getVolumeMount(name string, persistenceEnabled *bool, externalConfig *string, tlsConfig *redisv1beta1.TLSConfig) []corev1.VolumeMount { + var VolumeMounts []corev1.VolumeMount + + if *persistenceEnabled && persistenceEnabled != nil { + VolumeMounts = append(VolumeMounts, corev1.VolumeMount{ + Name: name, + MountPath: "/data", + }) + } + + if tlsConfig != nil { + VolumeMounts = append(VolumeMounts, corev1.VolumeMount{ + Name: "tls-certs", + ReadOnly: true, + MountPath: "/tls", + }) } if externalConfig != nil { - volumeMounts = append(volumeMounts, corev1.VolumeMount{ + VolumeMounts = append(VolumeMounts, corev1.VolumeMount{ Name: "external-config", MountPath: "/etc/redis/external.conf.d", }) } - return volumeMounts + + return VolumeMounts } // getProbeInfo generates probe information for Redis @@ -264,12 +333,38 @@ func getProbeInfo() *corev1.Probe { } // getEnvironmentVariables returns all the required Environment Variables -func getEnvironmentVariables(role string, enabledPassword *bool, secretName *string, secretKey *string, persistenceEnabled *bool, extraEnv *[]corev1.EnvVar) []corev1.EnvVar { +func getEnvironmentVariables(role string, enabledPassword *bool, secretName *string, secretKey *string, persistenceEnabled *bool, extraEnv *[]corev1.EnvVar, tlsConfig *redisv1beta1.TLSConfig) []corev1.EnvVar { envVars := []corev1.EnvVar{ {Name: "SERVER_MODE", Value: role}, {Name: "SETUP_MODE", Value: role}, - {Name: "REDIS_ADDR", Value: "redis://localhost:6379"}, } + + redisHost := "redis://localhost:6379" + if tlsConfig != nil { + redisHost = "rediss://localhost:6379" + envVars = append(envVars, corev1.EnvVar{ + Name: "REDIS_EXPORTER_TLS_CLIENT_KEY_FILE", + Value: "/tls/tls.key", + }) + envVars = append(envVars, corev1.EnvVar{ + Name: "REDIS_EXPORTER_TLS_CLIENT_CERT_FILE", + Value: "/tls/tls.crt", + }) + envVars = append(envVars, corev1.EnvVar{ + Name: "REDIS_EXPORTER_TLS_CA_CERT_FILE", + Value: "/tls/ca.crt", + }) + envVars = append(envVars, corev1.EnvVar{ + Name: "REDIS_EXPORTER_SKIP_TLS_VERIFICATION", + Value: "true", + }) + } + + envVars = append(envVars, corev1.EnvVar{ + Name: "REDIS_ADDR", + Value: redisHost, + }) + if enabledPassword != nil && *enabledPassword { envVars = append(envVars, corev1.EnvVar{ Name: "REDIS_PASSWORD",