From 5a4fc609da836262d0cbca8a8678b208c884319c Mon Sep 17 00:00:00 2001 From: Felipe Alfaro Solana Date: Fri, 31 Jan 2020 12:29:45 +0100 Subject: [PATCH 1/3] Add support for custom command renaming in Redis. The ability to rename commands in Redis is a way to improve security by obscuring certain operations or by disabling them entirely. --- api/redisfailover/v1/types.go | 43 ++++++++++++--------- operator/redisfailover/service/client.go | 2 +- operator/redisfailover/service/generator.go | 31 +++++++++++---- 3 files changed, 50 insertions(+), 26 deletions(-) diff --git a/api/redisfailover/v1/types.go b/api/redisfailover/v1/types.go index 9019d8765..94fa080e2 100644 --- a/api/redisfailover/v1/types.go +++ b/api/redisfailover/v1/types.go @@ -23,26 +23,33 @@ type RedisFailoverSpec struct { LabelWhitelist []string `json:"labelWhitelist,omitempty"` } +// RedisCommandRename defines the specification of a "rename-command" configuration option +type RedisCommandRename struct { + From string `json:"from,omitempty"` + To string `json:"to,omitempty"` +} + // RedisSettings defines the specification of the redis cluster type RedisSettings struct { - Image string `json:"image,omitempty"` - ImagePullPolicy corev1.PullPolicy `json:"imagePullPolicy,omitempty"` - Replicas int32 `json:"replicas,omitempty"` - Resources corev1.ResourceRequirements `json:"resources,omitempty"` - CustomConfig []string `json:"customConfig,omitempty"` - Command []string `json:"command,omitempty"` - ShutdownConfigMap string `json:"shutdownConfigMap,omitempty"` - Storage RedisStorage `json:"storage,omitempty"` - Exporter RedisExporter `json:"exporter,omitempty"` - Affinity *corev1.Affinity `json:"affinity,omitempty"` - SecurityContext *corev1.PodSecurityContext `json:"securityContext,omitempty"` - ImagePullSecrets []corev1.LocalObjectReference `json:"imagePullSecrets,omitempty"` - Tolerations []corev1.Toleration `json:"tolerations,omitempty"` - NodeSelector map[string]string `json:"nodeSelector,omitempty"` - PodAnnotations map[string]string `json:"podAnnotations,omitempty"` - ServiceAnnotations map[string]string `json:"serviceAnnotations,omitempty"` - HostNetwork bool `json:"hostNetwork,omitempty"` - DNSPolicy corev1.DNSPolicy `json:"dnsPolicy,omitempty"` + Image string `json:"image,omitempty"` + ImagePullPolicy corev1.PullPolicy `json:"imagePullPolicy,omitempty"` + Replicas int32 `json:"replicas,omitempty"` + Resources corev1.ResourceRequirements `json:"resources,omitempty"` + CustomConfig []string `json:"customConfig,omitempty"` + CustomCommandRenames []RedisCommandRename `json:"customCommandRenames,omitempty"` + Command []string `json:"command,omitempty"` + ShutdownConfigMap string `json:"shutdownConfigMap,omitempty"` + Storage RedisStorage `json:"storage,omitempty"` + Exporter RedisExporter `json:"exporter,omitempty"` + Affinity *corev1.Affinity `json:"affinity,omitempty"` + SecurityContext *corev1.PodSecurityContext `json:"securityContext,omitempty"` + ImagePullSecrets []corev1.LocalObjectReference `json:"imagePullSecrets,omitempty"` + Tolerations []corev1.Toleration `json:"tolerations,omitempty"` + NodeSelector map[string]string `json:"nodeSelector,omitempty"` + PodAnnotations map[string]string `json:"podAnnotations,omitempty"` + ServiceAnnotations map[string]string `json:"serviceAnnotations,omitempty"` + HostNetwork bool `json:"hostNetwork,omitempty"` + DNSPolicy corev1.DNSPolicy `json:"dnsPolicy,omitempty"` } // SentinelSettings defines the specification of the sentinel cluster diff --git a/operator/redisfailover/service/client.go b/operator/redisfailover/service/client.go index 042d776ae..adabc4182 100644 --- a/operator/redisfailover/service/client.go +++ b/operator/redisfailover/service/client.go @@ -76,7 +76,7 @@ func (r *RedisFailoverKubeClient) EnsureRedisStatefulset(rf *redisfailoverv1.Red return r.K8SService.CreateOrUpdateStatefulSet(rf.Namespace, ss) } -// EnsureRedisConfigMap makes sure the sentinel configmap exists +// EnsureRedisConfigMap makes sure the Redis ConfigMap exists func (r *RedisFailoverKubeClient) EnsureRedisConfigMap(rf *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) error { password, err := k8s.GetRedisPassword(r.K8SService, rf) diff --git a/operator/redisfailover/service/generator.go b/operator/redisfailover/service/generator.go index 6b4786222..4e7a52d71 100644 --- a/operator/redisfailover/service/generator.go +++ b/operator/redisfailover/service/generator.go @@ -10,13 +10,24 @@ import ( "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" + "bytes" redisfailoverv1 "github.com/spotahome/redis-operator/api/redisfailover/v1" "github.com/spotahome/redis-operator/operator/redisfailover/util" + "text/template" ) const ( redisConfigurationVolumeName = "redis-config" + // Template used to build the Redis configuration + redisConfigTemplate = `slaveof 127.0.0.1 6379 +tcp-keepalive 60 +save 900 1 +save 300 10 +{{- range .Spec.Redis.CustomCommandRenames}} +rename-command {{.From}} {{.To}} +{{- end}} +` redisShutdownConfigurationVolumeName = "redis-shutdown-config" redisReadinessVolumeName = "redis-readiness-config" redisStorageVolumeName = "redis-data" @@ -115,13 +126,19 @@ sentinel parallel-syncs mymaster 2` func generateRedisConfigMap(rf *redisfailoverv1.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference, password string) *corev1.ConfigMap { name := GetRedisName(rf) - namespace := rf.Namespace - labels = util.MergeLabels(labels, generateSelectorLabels(redisRoleName, rf.Name)) - redisConfigFileContent := `slaveof 127.0.0.1 6379 -tcp-keepalive 60 -save 900 1 -save 300 10` + + tmpl, err := template.New("redis").Parse(redisConfigTemplate) + if err != nil { + panic(err) + } + + var tplOutput bytes.Buffer + if err := tmpl.Execute(&tplOutput, rf); err != nil { + panic(err) + } + + redisConfigFileContent := tplOutput.String() if password != "" { redisConfigFileContent = fmt.Sprintf("%s\nmasterauth %s\nrequirepass %s", redisConfigFileContent, password, password) @@ -130,7 +147,7 @@ save 300 10` return &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: name, - Namespace: namespace, + Namespace: rf.Namespace, Labels: labels, OwnerReferences: ownerRefs, }, From 30a012776848a3cf2f4bb3b38870fed0f56d9bdb Mon Sep 17 00:00:00 2001 From: Felipe Alfaro Solana Date: Fri, 31 Jan 2020 12:46:03 +0100 Subject: [PATCH 2/3] Add an example of how to use custom command renames. --- example/redisfailover/custom-renames.yaml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 example/redisfailover/custom-renames.yaml diff --git a/example/redisfailover/custom-renames.yaml b/example/redisfailover/custom-renames.yaml new file mode 100644 index 000000000..889398e69 --- /dev/null +++ b/example/redisfailover/custom-renames.yaml @@ -0,0 +1,16 @@ +apiVersion: databases.spotahome.com/v1 +kind: RedisFailover +metadata: + name: redisfailover +spec: + sentinel: + replicas: 3 + redis: + replicas: 3 + customCommandRenames: + - from: "monitor" + to: "" + - from: "flushall" + to: "fa" + - from: flushdb + to: xxfd From 19542b3196a096d1bd9527c2cc050650df7b5c20 Mon Sep 17 00:00:00 2001 From: Felipe Alfaro Solana Date: Fri, 31 Jan 2020 12:58:53 +0100 Subject: [PATCH 3/3] Add double quotes to `rename-command`. This allows constructions like `rename-command "monitor" ""`. --- operator/redisfailover/service/generator.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/operator/redisfailover/service/generator.go b/operator/redisfailover/service/generator.go index 4e7a52d71..d6ee7b4a2 100644 --- a/operator/redisfailover/service/generator.go +++ b/operator/redisfailover/service/generator.go @@ -25,7 +25,7 @@ tcp-keepalive 60 save 900 1 save 300 10 {{- range .Spec.Redis.CustomCommandRenames}} -rename-command {{.From}} {{.To}} +rename-command "{{.From}}" "{{.To}}" {{- end}} ` redisShutdownConfigurationVolumeName = "redis-shutdown-config"