diff --git a/charts/redis-operator/README.md b/charts/redis-operator/README.md index 97f02ea2bd..8afd00b283 100644 --- a/charts/redis-operator/README.md +++ b/charts/redis-operator/README.md @@ -92,6 +92,7 @@ kubectl create secret tls --key tls.key --cert tls.crt -n | certificate.secretName | string | `"webhook-server-cert"` | | | certmanager.apiVersion | string | `"cert-manager.io/v1"` | | | certmanager.enabled | bool | `false` | | +| featureGates.AvoidCommandLinePassword | bool | `false` | | | featureGates.GenerateConfigInInitContainer | bool | `false` | | | issuer.create | bool | `true` | | | issuer.email | string | `"shubham.gupta@opstree.com"` | | diff --git a/charts/redis-operator/values.yaml b/charts/redis-operator/values.yaml index f5b9288f64..e7aad182cb 100644 --- a/charts/redis-operator/values.yaml +++ b/charts/redis-operator/values.yaml @@ -106,6 +106,8 @@ securityContext: {} # Feature gates for alpha/experimental features featureGates: + # Never execute redis-cli -a , even if authentication cannot succeed without it + AvoidCommandLinePassword: false # Enable generating Redis configuration using an init container instead of a regular container GenerateConfigInInitContainer: false diff --git a/docs/content/en/docs/Advance Configuration/Feature Gates/_index.md b/docs/content/en/docs/Advance Configuration/Feature Gates/_index.md index 24d98790a2..d0374f00e2 100644 --- a/docs/content/en/docs/Advance Configuration/Feature Gates/_index.md +++ b/docs/content/en/docs/Advance Configuration/Feature Gates/_index.md @@ -17,10 +17,34 @@ Feature gates can be configured in the Helm chart values: featureGates: # Enable generating Redis configuration using an init container instead of a regular container GenerateConfigInInitContainer: false + # Never execute redis-cli -a , even if authentication cannot succeed without it + AvoidCommandLinePassword: false ``` ## Available Feature Gates +### AvoidCommandLinePassword + +When enabled, Redis Operator will never execute `redis-cli -a `, which can leak passwords. The Operator sets the +`REDISCLI_AUTH` variable on all Redis pods, so the password does not need to be provided on the command line and it is normally +safe to turn this on unless you are simultaneously upgrading the operator. This is an alpha feature and may change in future releases. + +However, if you upgrade from a version that does not add `REDISCLI_AUTH` to the pods (a behavior introduced in the same version that +added `AvoidCommandLinePassword`), simultaneously enabling `AvoidCommandLinePassword` will make Redis Operator unable to manage +your current pods, since `-a ` is still needed on them. Hence, to guarantee that the Redis password will never be included +on a command line, you must either risk an operator downtime or upgrade in two steps: + +1. Upgrade to a version that adds `REDISCLI_AUTH` to the pods (which was introduced at the same time as `AvoidCommandLinePassword`). +2. Turn on `AvoidCommandLinePassword`. + +**Default**: `false` + +**Usage**: +```yaml +featureGates: + AvoidCommandLinePassword: true +``` + ### GenerateConfigInInitContainer When enabled, Redis configuration will be generated using an init container instead of a regular container. This is an alpha feature and may change in future releases. diff --git a/internal/cmd/manager/cmd.go b/internal/cmd/manager/cmd.go index 3598e67c62..0179ea98bc 100644 --- a/internal/cmd/manager/cmd.go +++ b/internal/cmd/manager/cmd.go @@ -97,8 +97,9 @@ func addFlags(cmd *cobra.Command, opts *managerOptions) { cmd.Flags().BoolVar(&opts.enableLeaderElection, "leader-elect", false, "Enable leader election for controller manager. Enabling this will ensure there is only one active controller manager.") cmd.Flags().BoolVar(&opts.enableWebhooks, "enable-webhooks", internalenv.IsWebhookEnabled(), "Enable webhooks") cmd.Flags().IntVar(&opts.maxConcurrentReconciles, "max-concurrent-reconciles", 1, "Max concurrent reconciles") - cmd.Flags().StringVar(&opts.featureGatesString, "feature-gates", internalenv.GetFeatureGates(), "A set of key=value pairs that describe feature gates for alpha/experimental features. "+ - "Options are:\n GenerateConfigInInitContainer=true|false: enables using init container for config generation") + cmd.Flags().StringVar(&opts.featureGatesString, "feature-gates", internalenv.GetFeatureGates(), "A set of key=value pairs that describe feature gates for alpha/experimental features. Options are:"+ + "\n GenerateConfigInInitContainer=true|false: enables using init container for config generation"+ + "\n AvoidCommandLinePassword=true|false: prevents using -a in redis-cli commands") cmd.Flags().Duration( operator.KubeClientTimeoutMGRFlag, 60*time.Second, diff --git a/internal/features/features.go b/internal/features/features.go index bef3fe525a..6ebcacdbef 100644 --- a/internal/features/features.go +++ b/internal/features/features.go @@ -9,12 +9,14 @@ const ( // GenerateConfigInInitContainer enables generating Redis configuration using an init container // instead of a regular container GenerateConfigInInitContainer featuregate.Feature = "GenerateConfigInInitContainer" + AvoidCommandLinePassword featuregate.Feature = "AvoidCommandLinePassword" ) // DefaultRedisOperatorFeatureGates consists of all known Redis operator feature gates. // To add a new feature, define a key for it above and add it here. var DefaultRedisOperatorFeatureGates = map[featuregate.Feature]featuregate.FeatureSpec{ GenerateConfigInInitContainer: {Default: false, PreRelease: featuregate.Alpha}, + AvoidCommandLinePassword: {Default: false, PreRelease: featuregate.Alpha}, } // MutableFeatureGate is a feature gate that can be dynamically set diff --git a/internal/k8sutils/cluster-scaling.go b/internal/k8sutils/cluster-scaling.go index c7fc8ca75d..27b7f2f3ab 100644 --- a/internal/k8sutils/cluster-scaling.go +++ b/internal/k8sutils/cluster-scaling.go @@ -35,15 +35,11 @@ func ReshardRedisCluster(ctx context.Context, client kubernetes.Interface, cr *r } cmd = []string{"redis-cli", "--cluster", "reshard"} cmd = append(cmd, getEndpoint(ctx, client, cr, transferPOD)) - if cr.Spec.KubernetesConfig.ExistingPasswordSecret != nil { - pass, err := getRedisPassword(ctx, client, cr.Namespace, *cr.Spec.KubernetesConfig.ExistingPasswordSecret.Name, *cr.Spec.KubernetesConfig.ExistingPasswordSecret.Key) - if err != nil { - log.FromContext(ctx).Error(err, "error in getting redis password") - return - } - cmd = append(cmd, "-a") - cmd = append(cmd, pass) + authArgs, err := getRedisClusterAuthArgs(ctx, client, cr, transferNodeName) + if err != nil { + log.FromContext(ctx).Error(err, "Failed to get password authentication arguments") } + cmd = append(cmd, authArgs...) cmd = append(cmd, getRedisTLSArgs(cr.Spec.TLS, transferNodeName)...) @@ -129,7 +125,7 @@ func getRedisNodeID(ctx context.Context, client kubernetes.Interface, cr *rcvb2. // Rebalance the Redis CLuster using the Empty Master Nodes func RebalanceRedisClusterEmptyMasters(ctx context.Context, client kubernetes.Interface, cr *rcvb2.RedisCluster) { - // cmd = redis-cli --cluster rebalance : --cluster-use-empty-masters -a + // cmd = redis-cli --cluster rebalance : --cluster-use-empty-masters var cmd []string pod := RedisDetails{ PodName: cr.Name + "-leader-1", @@ -138,14 +134,11 @@ func RebalanceRedisClusterEmptyMasters(ctx context.Context, client kubernetes.In cmd = []string{"redis-cli", "--cluster", "rebalance"} cmd = append(cmd, getEndpoint(ctx, client, cr, pod)) cmd = append(cmd, "--cluster-use-empty-masters") - if cr.Spec.KubernetesConfig.ExistingPasswordSecret != nil { - pass, err := getRedisPassword(ctx, client, cr.Namespace, *cr.Spec.KubernetesConfig.ExistingPasswordSecret.Name, *cr.Spec.KubernetesConfig.ExistingPasswordSecret.Key) - if err != nil { - log.FromContext(ctx).Error(err, "Error in getting redis password") - } - cmd = append(cmd, "-a") - cmd = append(cmd, pass) + authArgs, err := getRedisClusterAuthArgs(ctx, client, cr, cr.Name+"-leader-0") + if err != nil { + log.FromContext(ctx).Error(err, "Failed to get password authentication arguments") } + cmd = append(cmd, authArgs...) cmd = append(cmd, getRedisTLSArgs(cr.Spec.TLS, cr.Name+"-leader-0")...) @@ -175,7 +168,7 @@ func CheckIfEmptyMasters(ctx context.Context, client kubernetes.Interface, cr *r // Rebalance Redis Cluster Would Rebalance the Redis Cluster without using the empty masters func RebalanceRedisCluster(ctx context.Context, client kubernetes.Interface, cr *rcvb2.RedisCluster) { - // cmd = redis-cli --cluster rebalance : -a + // cmd = redis-cli --cluster rebalance : var cmd []string pod := RedisDetails{ PodName: cr.Name + "-leader-1", @@ -183,14 +176,11 @@ func RebalanceRedisCluster(ctx context.Context, client kubernetes.Interface, cr } cmd = []string{"redis-cli", "--cluster", "rebalance"} cmd = append(cmd, getEndpoint(ctx, client, cr, pod)) - if cr.Spec.KubernetesConfig.ExistingPasswordSecret != nil { - pass, err := getRedisPassword(ctx, client, cr.Namespace, *cr.Spec.KubernetesConfig.ExistingPasswordSecret.Name, *cr.Spec.KubernetesConfig.ExistingPasswordSecret.Key) - if err != nil { - log.FromContext(ctx).Error(err, "Error in getting redis password") - } - cmd = append(cmd, "-a") - cmd = append(cmd, pass) + authArgs, err := getRedisClusterAuthArgs(ctx, client, cr, cr.Name+"-leader-0") + if err != nil { + log.FromContext(ctx).Error(err, "Failed to get password authentication arguments") } + cmd = append(cmd, authArgs...) cmd = append(cmd, getRedisTLSArgs(cr.Spec.TLS, cr.Name+"-leader-0")...) @@ -211,14 +201,11 @@ func AddRedisNodeToCluster(ctx context.Context, client kubernetes.Interface, cr } cmd = append(cmd, getEndpoint(ctx, client, cr, newPod)) cmd = append(cmd, getEndpoint(ctx, client, cr, existingPod)) - if cr.Spec.KubernetesConfig.ExistingPasswordSecret != nil { - pass, err := getRedisPassword(ctx, client, cr.Namespace, *cr.Spec.KubernetesConfig.ExistingPasswordSecret.Name, *cr.Spec.KubernetesConfig.ExistingPasswordSecret.Key) - if err != nil { - log.FromContext(ctx).Error(err, "Error in getting redis password") - } - cmd = append(cmd, "-a") - cmd = append(cmd, pass) + authArgs, err := getRedisClusterAuthArgs(ctx, client, cr, cr.Name+"-leader-0") + if err != nil { + log.FromContext(ctx).Error(err, "Failed to get password authentication arguments") } + cmd = append(cmd, authArgs...) cmd = append(cmd, getRedisTLSArgs(cr.Spec.TLS, cr.Name+"-leader-0")...) @@ -259,14 +246,11 @@ func RemoveRedisFollowerNodesFromCluster(ctx context.Context, client kubernetes. cmd = []string{"redis-cli"} - if cr.Spec.KubernetesConfig.ExistingPasswordSecret != nil { - pass, err := getRedisPassword(ctx, client, cr.Namespace, *cr.Spec.KubernetesConfig.ExistingPasswordSecret.Name, *cr.Spec.KubernetesConfig.ExistingPasswordSecret.Key) - if err != nil { - log.FromContext(ctx).Error(err, "Error in getting redis password") - } - cmd = append(cmd, "-a") - cmd = append(cmd, pass) + authArgs, err := getRedisClusterAuthArgs(ctx, client, cr, cr.Name+"-leader-0") + if err != nil { + log.FromContext(ctx).Error(err, "Failed to get password authentication arguments") } + cmd = append(cmd, authArgs...) cmd = append(cmd, getRedisTLSArgs(cr.Spec.TLS, cr.Name+"-leader-0")...) lastLeaderPodNodeID := getRedisNodeID(ctx, client, cr, lastLeaderPod) @@ -292,14 +276,11 @@ func RemoveRedisNodeFromCluster(ctx context.Context, client kubernetes.Interface cmd := []string{"redis-cli", "--cluster", "del-node"} cmd = append(cmd, getEndpoint(ctx, client, cr, existingPod)) cmd = append(cmd, getRedisNodeID(ctx, client, cr, removePod)) - if cr.Spec.KubernetesConfig.ExistingPasswordSecret != nil { - pass, err := getRedisPassword(ctx, client, cr.Namespace, *cr.Spec.KubernetesConfig.ExistingPasswordSecret.Name, *cr.Spec.KubernetesConfig.ExistingPasswordSecret.Key) - if err != nil { - log.FromContext(ctx).Error(err, "Error in getting redis password") - } - cmd = append(cmd, "-a") - cmd = append(cmd, pass) + authArgs, err := getRedisClusterAuthArgs(ctx, client, cr, cr.Name+"-leader-0") + if err != nil { + log.FromContext(ctx).Error(err, "Failed to get password authentication arguments") } + cmd = append(cmd, authArgs...) cmd = append(cmd, getRedisTLSArgs(cr.Spec.TLS, cr.Name+"-leader-0")...) executeCommand(ctx, client, cr, cmd, cr.Name+"-leader-0") } @@ -333,7 +314,7 @@ func verifyLeaderPodInfo(ctx context.Context, redisClient *redis.Client, podName func ClusterFailover(ctx context.Context, client kubernetes.Interface, cr *rcvb2.RedisCluster, shardIdx int32) error { slavePodName := cr.Name + "-leader-" + strconv.Itoa(int(shardIdx)) - // cmd = redis-cli cluster failover -a + // cmd = redis-cli cluster failover var cmd []string pod := RedisDetails{ PodName: slavePodName, @@ -344,14 +325,11 @@ func ClusterFailover(ctx context.Context, client kubernetes.Interface, cr *rcvb2 return err } cmd = []string{"redis-cli", "-h", host, "-p", port} - if cr.Spec.KubernetesConfig.ExistingPasswordSecret != nil { - pass, err := getRedisPassword(ctx, client, cr.Namespace, *cr.Spec.KubernetesConfig.ExistingPasswordSecret.Name, *cr.Spec.KubernetesConfig.ExistingPasswordSecret.Key) - if err != nil { - log.FromContext(ctx).Error(err, "Error in getting redis password") - } - cmd = append(cmd, "-a") - cmd = append(cmd, pass) + authArgs, err := getRedisClusterAuthArgs(ctx, client, cr, slavePodName) + if err != nil { + log.FromContext(ctx).Error(err, "Failed to get password authentication arguments") } + cmd = append(cmd, authArgs...) cmd = append(cmd, getRedisTLSArgs(cr.Spec.TLS, slavePodName)...) cmd = append(cmd, "cluster", "failover") diff --git a/internal/k8sutils/redis.go b/internal/k8sutils/redis.go index 905d236a4c..367dfb08d0 100644 --- a/internal/k8sutils/redis.go +++ b/internal/k8sutils/redis.go @@ -14,6 +14,7 @@ import ( rcvb2 "github.com/OT-CONTAINER-KIT/redis-operator/api/rediscluster/v1beta2" rrvb2 "github.com/OT-CONTAINER-KIT/redis-operator/api/redisreplication/v1beta2" common "github.com/OT-CONTAINER-KIT/redis-operator/internal/controller/common" + "github.com/OT-CONTAINER-KIT/redis-operator/internal/features" redis "github.com/redis/go-redis/v9" "github.com/samber/lo" corev1 "k8s.io/api/core/v1" @@ -122,6 +123,66 @@ func getEndpoint(ctx context.Context, client kubernetes.Interface, cr *rcvb2.Red return host + ":" + strconv.Itoa(port) } +// checkRedisCLIAuthInEnv returns true if we can use the pod's REDISCLI_AUTH variable instead of sending redis-cli -a . +// It checks only variables specified via env[].valueFrom since this is what the operator sets; it does not look at envFrom. +func checkRedisCLIAuthInEnv(ctx context.Context, client kubernetes.Interface, cr *rcvb2.RedisCluster, podName, secretName, secretKey string) (bool, error) { + redisPod, err := client.CoreV1().Pods(cr.Namespace).Get(context.TODO(), podName, metav1.GetOptions{}) + if err != nil { + log.FromContext(ctx).Error(err, "Error checking Redis pod's REDISCLI_AUTH variable", "namespace", cr.Namespace, "podName", podName) + return false, err + } + + for _, tr := range redisPod.Spec.Containers { + if tr.Name == cr.Name+"-leader" { + for _, e := range tr.Env { + if e.Name != "REDISCLI_AUTH" { + continue + } + + if e.ValueFrom == nil || e.ValueFrom.SecretKeyRef == nil { + continue + } + + if e.ValueFrom.SecretKeyRef.Name != secretName || e.ValueFrom.SecretKeyRef.Key != secretKey { + return false, nil + } + + return true, nil + } + + log.FromContext(ctx).V(1).Info("Leader container not configured with REDISCLI_AUTH", "podName", podName) + return false, nil + } + } + + log.FromContext(ctx).V(1).Info("Leader container not found in pod", "podName", podName) + return false, nil +} + +func getRedisClusterAuthArgs(ctx context.Context, client kubernetes.Interface, cr *rcvb2.RedisCluster, podName string) ([]string, error) { + if cr.Spec.KubernetesConfig.ExistingPasswordSecret != nil { + passwordInEnv, err := checkRedisCLIAuthInEnv(ctx, client, cr, podName, *cr.Spec.KubernetesConfig.ExistingPasswordSecret.Name, *cr.Spec.KubernetesConfig.ExistingPasswordSecret.Key) + if err != nil { + return []string{}, fmt.Errorf("error checking pod authentication config: %w", err) + } + + if !passwordInEnv { + if features.Enabled(features.AvoidCommandLinePassword) { + return []string{}, errors.New("refusing to use command-line authentication because AvoidCommandLinePassword is set") + } + + pass, err := getRedisPassword(ctx, client, cr.Namespace, *cr.Spec.KubernetesConfig.ExistingPasswordSecret.Name, *cr.Spec.KubernetesConfig.ExistingPasswordSecret.Key) + if err != nil { + return []string{}, fmt.Errorf("error getting Redis password: %w", err) + } + + return []string{"-a", pass}, nil + } + } + + return []string{}, nil +} + // CreateSingleLeaderRedisCommand will create command for single leader cluster creation func CreateSingleLeaderRedisCommand(ctx context.Context, cr *rcvb2.RedisCluster) RedisInvocation { cmd := RedisInvocation{ @@ -245,13 +306,12 @@ func ExecuteRedisClusterCommand(ctx context.Context, client kubernetes.Interface cmd = CreateMultipleLeaderRedisCommand(ctx, client, cr) } - if cr.Spec.KubernetesConfig.ExistingPasswordSecret != nil { - pass, err := getRedisPassword(ctx, client, cr.Namespace, *cr.Spec.KubernetesConfig.ExistingPasswordSecret.Name, *cr.Spec.KubernetesConfig.ExistingPasswordSecret.Key) - if err != nil { - log.FromContext(ctx).Error(err, "Error in getting redis password") - } - cmd.AddFlag("-a") - cmd.AddFlag(pass) + authArgs, err := getRedisClusterAuthArgs(ctx, client, cr, cr.Name+"-leader-0") + if err != nil { + log.FromContext(ctx).Error(err, "Failed to get password authentication arguments") + } + for _, arg := range authArgs { + cmd.AddFlag(arg) } cmd.AddFlag(getRedisTLSArgs(cr.Spec.TLS, cr.Name+"-leader-0")...) executeCommand(ctx, client, cr, cmd.Args(), cr.Name+"-leader-0") @@ -274,14 +334,11 @@ func createRedisReplicationCommand(ctx context.Context, client kubernetes.Interf cmd = append(cmd, getEndpoint(ctx, client, cr, followerPod)) cmd = append(cmd, getEndpoint(ctx, client, cr, leaderPod)) cmd = append(cmd, "--cluster-slave") - if cr.Spec.KubernetesConfig.ExistingPasswordSecret != nil { - pass, err := getRedisPassword(ctx, client, cr.Namespace, *cr.Spec.KubernetesConfig.ExistingPasswordSecret.Name, *cr.Spec.KubernetesConfig.ExistingPasswordSecret.Key) - if err != nil { - log.FromContext(ctx).Error(err, "Failed to retrieve Redis password", "Secret", *cr.Spec.KubernetesConfig.ExistingPasswordSecret.Name) - } else { - cmd = append(cmd, "-a", pass) - } + authArgs, err := getRedisClusterAuthArgs(ctx, client, cr, leaderPod.PodName) + if err != nil { + log.FromContext(ctx).Error(err, "Failed to get password authentication arguments") } + cmd = append(cmd, authArgs...) cmd = append(cmd, getRedisTLSArgs(cr.Spec.TLS, leaderPod.PodName)...) return cmd } @@ -444,14 +501,11 @@ func RedisClusterStatusHealth(ctx context.Context, client kubernetes.Interface, defer redisClient.Close() cmd := []string{"redis-cli", "--cluster", "check", fmt.Sprintf("127.0.0.1:%d", *cr.Spec.Port)} - if cr.Spec.KubernetesConfig.ExistingPasswordSecret != nil { - pass, err := getRedisPassword(ctx, client, cr.Namespace, *cr.Spec.KubernetesConfig.ExistingPasswordSecret.Name, *cr.Spec.KubernetesConfig.ExistingPasswordSecret.Key) - if err != nil { - log.FromContext(ctx).Error(err, "Error in getting redis password") - } - cmd = append(cmd, "-a") - cmd = append(cmd, pass) + authArgs, err := getRedisClusterAuthArgs(ctx, client, cr, cr.Name+"-leader-0") + if err != nil { + log.FromContext(ctx).Error(err, "Failed to get password authentication arguments") } + cmd = append(cmd, authArgs...) cmd = append(cmd, getRedisTLSArgs(cr.Spec.TLS, cr.Name+"-leader-0")...) out, err := executeCommand1(ctx, client, cr, cmd, cr.Name+"-leader-0") if err != nil { diff --git a/internal/k8sutils/statefulset.go b/internal/k8sutils/statefulset.go index edac10da5a..7d0600c1b8 100644 --- a/internal/k8sutils/statefulset.go +++ b/internal/k8sutils/statefulset.go @@ -458,7 +458,7 @@ func generateContainerDef(name string, containerParams containerParameters, clus } } - if preStopCmd := GeneratePreStopCommand(containerParams.Role, enableAuth, enableTLS); preStopCmd != "" { + if preStopCmd := GeneratePreStopCommand(containerParams.Role, enableTLS); preStopCmd != "" { containerDefinition[0].Lifecycle = &corev1.Lifecycle{ PreStop: &corev1.LifecycleHandler{ Exec: &corev1.ExecAction{ @@ -517,39 +517,35 @@ func generateContainerDef(name string, containerParams containerParameters, clus // GeneratePreStopCommand generates the preStop script based on the Redis role. // Only "cluster" role is supported for now; other roles return an empty string. -func GeneratePreStopCommand(role string, enableAuth, enableTLS bool) string { - authArgs, tlsArgs := GenerateAuthAndTLSArgs(enableAuth, enableTLS) +func GeneratePreStopCommand(role string, enableTLS bool) string { + tlsArgs := GenerateTLSArgs(enableTLS) switch role { case "cluster": - return generateClusterPreStop(authArgs, tlsArgs) + return generateClusterPreStop(tlsArgs) default: return "" } } -// GenerateAuthAndTLSArgs constructs authentication and TLS arguments for redis-cli. -func GenerateAuthAndTLSArgs(enableAuth, enableTLS bool) (string, string) { - authArgs := "" +// GenerateTLSArgs constructs authentication and TLS arguments for redis-cli. +func GenerateTLSArgs(enableTLS bool) string { tlsArgs := "" - if enableAuth { - authArgs = " -a \"${REDIS_PASSWORD}\"" - } if enableTLS { tlsArgs = " --tls --cert \"${REDIS_TLS_CERT}\" --key \"${REDIS_TLS_CERT_KEY}\" --cacert \"${REDIS_TLS_CA_KEY}\"" } - return authArgs, tlsArgs + return tlsArgs } // generateClusterPreStop generates the preStop script for Redis cluster mode. // It identifies the master node and triggers a failover to the best available slave before shutdown. -func generateClusterPreStop(authArgs, tlsArgs string) string { +func generateClusterPreStop(tlsArgs string) string { return fmt.Sprintf(`#!/bin/sh -ROLE=$(redis-cli -h $(hostname) -p ${REDIS_PORT} %s %s info replication | awk -F: '/role:master/ {print "master"}') +ROLE=$(redis-cli -h $(hostname) -p ${REDIS_PORT} %s info replication | awk -F: '/role:master/ {print "master"}') if [ "$ROLE" = "master" ]; then - BEST_SLAVE=$(redis-cli -h $(hostname) -p ${REDIS_PORT} %s %s info replication | awk -F: ' + BEST_SLAVE=$(redis-cli -h $(hostname) -p ${REDIS_PORT} %s info replication | awk -F: ' BEGIN { maxOffset = -1; bestSlave = "" } /slave[0-9]+:ip/ { split($2, a, ","); @@ -566,9 +562,9 @@ if [ "$ROLE" = "master" ]; then ') if [ -n "$BEST_SLAVE" ]; then - redis-cli -h "$BEST_SLAVE" -p ${REDIS_PORT} %s %s cluster failover + redis-cli -h "$BEST_SLAVE" -p ${REDIS_PORT} %s cluster failover fi -fi`, authArgs, tlsArgs, authArgs, tlsArgs, authArgs, tlsArgs) +fi`, tlsArgs, tlsArgs, tlsArgs) } func generateInitContainerDef(role, name string, initcontainerParams initContainerParameters, externalConfig *string, mountpath []corev1.VolumeMount, containerParams containerParameters, clusterVersion *string) []corev1.Container { @@ -832,9 +828,6 @@ func getProbeInfo(probe *corev1.Probe, sentinel, enableTLS, enableAuth bool) *co } else { healthChecker = append(healthChecker, "-p", "${REDIS_PORT}") } - if enableAuth { - healthChecker = append(healthChecker, "-a", "${REDIS_PASSWORD}") - } if enableTLS { healthChecker = append(healthChecker, "--tls", "--cert", "${REDIS_TLS_CERT}", "--key", "${REDIS_TLS_CERT_KEY}", "--cacert", "${REDIS_TLS_CA_KEY}") } @@ -909,6 +902,16 @@ func getEnvironmentVariables(role string, enabledPassword *bool, secretName *str Key: *secretKey, }, }, + }, corev1.EnvVar{ + Name: "REDISCLI_AUTH", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: *secretName, + }, + Key: *secretKey, + }, + }, }) } if persistenceEnabled != nil && *persistenceEnabled { diff --git a/internal/k8sutils/statefulset_test.go b/internal/k8sutils/statefulset_test.go index 4d710eca47..0218deb664 100644 --- a/internal/k8sutils/statefulset_test.go +++ b/internal/k8sutils/statefulset_test.go @@ -22,24 +22,19 @@ import ( func TestGenerateAuthAndTLSArgs(t *testing.T) { tests := []struct { - name string - enableAuth bool - enableTLS bool - expectedAuth string - expectedTLS string + name string + enableTLS bool + expectedTLS string }{ - {"NoAuthNoTLS", false, false, "", ""}, - {"AuthOnly", true, false, " -a \"${REDIS_PASSWORD}\"", ""}, - {"TLSOnly", false, true, "", " --tls --cert \"${REDIS_TLS_CERT}\" --key \"${REDIS_TLS_CERT_KEY}\" --cacert \"${REDIS_TLS_CA_KEY}\""}, - {"AuthAndTLS", true, true, " -a \"${REDIS_PASSWORD}\"", " --tls --cert \"${REDIS_TLS_CERT}\" --key \"${REDIS_TLS_CERT_KEY}\" --cacert \"${REDIS_TLS_CA_KEY}\""}, + {"NoAuthNoTLS", false, ""}, + {"AuthOnly", false, ""}, + {"TLSOnly", true, " --tls --cert \"${REDIS_TLS_CERT}\" --key \"${REDIS_TLS_CERT_KEY}\" --cacert \"${REDIS_TLS_CA_KEY}\""}, + {"AuthAndTLS", true, " --tls --cert \"${REDIS_TLS_CERT}\" --key \"${REDIS_TLS_CERT_KEY}\" --cacert \"${REDIS_TLS_CA_KEY}\""}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - authArgs, tlsArgs := GenerateAuthAndTLSArgs(tt.enableAuth, tt.enableTLS) - if authArgs != tt.expectedAuth { - t.Errorf("expected auth args %q, got %q", tt.expectedAuth, authArgs) - } + tlsArgs := GenerateTLSArgs(tt.enableTLS) if tlsArgs != tt.expectedTLS { t.Errorf("expected TLS args %q, got %q", tt.expectedTLS, tlsArgs) } @@ -62,7 +57,7 @@ func TestGeneratePreStopCommand(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - result := GeneratePreStopCommand(tt.role, true, true) + result := GeneratePreStopCommand(tt.role, true) if (result == "") != tt.expectEmpty { t.Errorf("expected empty: %v, got: %q", tt.expectEmpty, result) } @@ -1501,6 +1496,14 @@ func TestGetEnvironmentVariables(t *testing.T) { Key: "test-key", }, }}, + {Name: "REDISCLI_AUTH", ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "test-secret", + }, + Key: "test-key", + }, + }}, {Name: "SERVER_MODE", Value: "sentinel"}, {Name: "SETUP_MODE", Value: "sentinel"}, {Name: "TEST_ENV", Value: "test-value"}, @@ -1566,6 +1569,14 @@ func TestGetEnvironmentVariables(t *testing.T) { Key: "test-key", }, }}, + {Name: "REDISCLI_AUTH", ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "test-secret", + }, + Key: "test-key", + }, + }}, {Name: "SERVER_MODE", Value: "cluster"}, {Name: "SETUP_MODE", Value: "cluster"}, {Name: "TEST_ENV", Value: "test-value"},