From bc233a44e8d7d83baa967cbdc802203876360110 Mon Sep 17 00:00:00 2001 From: Abhishek Veeramalla Date: Wed, 22 May 2024 12:42:22 +0530 Subject: [PATCH] feat: secure redis authentication [CVE-2024-31989] (#1364) (#1369) * feat: secure redis authentication * fix: failing unit tests * fix: code review comments and liniting issue * fix: issues with HA pod crashing * fix: mount password env --------- Signed-off-by: iam-veeramalla --- .github/workflows/lint.yaml | 2 +- .golangci.yml | 2 +- build/redis/haproxy.cfg.tpl | 8 + build/redis/haproxy_init.sh.tpl | 18 +- build/redis/init.sh.tpl | 6 +- build/redis/redis.conf.tpl | 4 + build/redis/redis_liveness.sh.tpl | 1 + build/redis/redis_readiness.sh.tpl | 1 + build/redis/sentinel.conf.tpl | 1 + build/redis/sentinel_liveness.sh.tpl | 1 + common/defaults.go | 9 + controllers/argocd/deployment.go | 64 ++++- controllers/argocd/deployment_test.go | 53 ++++- controllers/argocd/networkpolicies.go | 264 +++++++++++++++++++++ controllers/argocd/networkpolicies_test.go | 81 +++++++ controllers/argocd/policyrule.go | 29 ++- controllers/argocd/secret.go | 27 +++ controllers/argocd/statefulset.go | 37 +++ controllers/argocd/statefulset_test.go | 45 ++++ controllers/argocd/util.go | 15 ++ controllers/argocd/util_test.go | 11 + 21 files changed, 650 insertions(+), 29 deletions(-) create mode 100644 controllers/argocd/networkpolicies.go create mode 100644 controllers/argocd/networkpolicies_test.go diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 4bc96f985..250d90e09 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -16,7 +16,7 @@ jobs: - name: Run golangci-lint uses: golangci/golangci-lint-action@v3 with: - version: v1.52.2 + version: v1.56.2 args: --timeout 5m --exclude SA5011 only-new-issues: true diff --git a/.golangci.yml b/.golangci.yml index 74323c10c..ef4c15761 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -18,4 +18,4 @@ linters-settings: goimports: local-prefixes: github.com/argoproj-labs/argocd-operator service: - golangci-lint-version: 1.52.2 + golangci-lint-version: 1.56.2 diff --git a/build/redis/haproxy.cfg.tpl b/build/redis/haproxy.cfg.tpl index 6fbdeb6c8..8b0b2b8f0 100644 --- a/build/redis/haproxy.cfg.tpl +++ b/build/redis/haproxy.cfg.tpl @@ -24,6 +24,8 @@ backend check_if_redis_is_master_0 {{- else}} tcp-check connect ssl {{- end}} + tcp-check send "AUTH replace-with-redis-auth"\r\n + tcp-check expect string +OK tcp-check send PING\r\n tcp-check expect string +PONG tcp-check send SENTINEL\ get-master-addr-by-name\ argocd\r\n @@ -48,6 +50,8 @@ backend check_if_redis_is_master_1 {{- else}} tcp-check connect ssl {{- end}} + tcp-check send "AUTH replace-with-redis-auth"\r\n + tcp-check expect string +OK tcp-check send PING\r\n tcp-check expect string +PONG tcp-check send SENTINEL\ get-master-addr-by-name\ argocd\r\n @@ -72,6 +76,8 @@ backend check_if_redis_is_master_2 {{- else}} tcp-check connect ssl {{- end}} + tcp-check send "AUTH replace-with-redis-auth"\r\n + tcp-check expect string +OK tcp-check send PING\r\n tcp-check expect string +PONG tcp-check send SENTINEL\ get-master-addr-by-name\ argocd\r\n @@ -102,6 +108,8 @@ backend bk_redis_master {{- else}} tcp-check connect ssl {{- end}} + tcp-check send "AUTH replace-with-redis-auth"\r\n + tcp-check expect string +OK tcp-check send PING\r\n tcp-check expect string +PONG tcp-check send info\ replication\r\n diff --git a/build/redis/haproxy_init.sh.tpl b/build/redis/haproxy_init.sh.tpl index ddfd5f65e..9956b1ed4 100644 --- a/build/redis/haproxy_init.sh.tpl +++ b/build/redis/haproxy_init.sh.tpl @@ -11,11 +11,6 @@ if [ -z "$ANNOUNCE_IP0" ]; then fi sed -i "s/REPLACE_ANNOUNCE0/$ANNOUNCE_IP0/" "$HAPROXY_CONF" -if [ "${AUTH:-}" ]; then - echo "Setting auth values" - ESCAPED_AUTH=$(echo "$AUTH" | sed -e 's/[\/&]/\\&/g'); - sed -i "s/REPLACE_AUTH_SECRET/${ESCAPED_AUTH}/" "$HAPROXY_CONF" -fi for loop in $(seq 1 10); do getent hosts {{.ServiceName}}-announce-1 && break echo "Waiting for service {{.ServiceName}}-announce-1 to be ready ($loop) ..." && sleep 1 @@ -27,11 +22,6 @@ if [ -z "$ANNOUNCE_IP1" ]; then fi sed -i "s/REPLACE_ANNOUNCE1/$ANNOUNCE_IP1/" "$HAPROXY_CONF" -if [ "${AUTH:-}" ]; then - echo "Setting auth values" - ESCAPED_AUTH=$(echo "$AUTH" | sed -e 's/[\/&]/\\&/g'); - sed -i "s/REPLACE_AUTH_SECRET/${ESCAPED_AUTH}/" "$HAPROXY_CONF" -fi for loop in $(seq 1 10); do getent hosts {{.ServiceName}}-announce-2 && break echo "Waiting for service {{.ServiceName}}-announce-2 to be ready ($loop) ..." && sleep 1 @@ -43,8 +33,6 @@ if [ -z "$ANNOUNCE_IP2" ]; then fi sed -i "s/REPLACE_ANNOUNCE2/$ANNOUNCE_IP2/" "$HAPROXY_CONF" -if [ "${AUTH:-}" ]; then - echo "Setting auth values" - ESCAPED_AUTH=$(echo "$AUTH" | sed -e 's/[\/&]/\\&/g'); - sed -i "s/REPLACE_AUTH_SECRET/${ESCAPED_AUTH}/" "$HAPROXY_CONF" -fi +auth=$(cat /redis-initial-pass/admin.password) +sed -i "s/replace-with-redis-auth/$auth/" "$HAPROXY_CONF" + diff --git a/build/redis/init.sh.tpl b/build/redis/init.sh.tpl index 9e08adadd..9d8f288c4 100644 --- a/build/redis/init.sh.tpl +++ b/build/redis/init.sh.tpl @@ -23,7 +23,7 @@ set -eu sentinel_get_master() { set +e if [ "$SENTINEL_PORT" -eq 0 ]; then - redis-cli -h "${SERVICE}" -p "${SENTINEL_TLS_PORT}" --tls --cacert /app/config/redis/tls/tls.crt sentinel get-master-addr-by-name "${MASTER_GROUP}" |\ + redis-cli -h "${SERVICE}" -p "${SENTINEL_TLS_PORT}" --tls --cacert /app/config/redis/tls/tls.crt sentinel get-master-addr-by-name "${MASTER_GROUP}" |\ grep -E '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' else redis-cli -h "${SERVICE}" -p "${SENTINEL_PORT}" sentinel get-master-addr-by-name "${MASTER_GROUP}" |\ @@ -133,9 +133,9 @@ setup_defaults() { redis_ping() { set +e if [ "$REDIS_PORT" -eq 0 ]; then - redis-cli -h "${MASTER}" -p "${REDIS_TLS_PORT}" --tls --cacert /app/config/redis/tls/tls.crt ping + redis-cli -h "${MASTER}" -a "${AUTH}" --no-auth-warning -p "${REDIS_TLS_PORT}" --tls --cacert /app/config/redis/tls/tls.crt ping else - redis-cli -h "${MASTER}" -p "${REDIS_PORT}" ping + redis-cli -h "${MASTER}" -a "${AUTH}" --no-auth-warning -p "${REDIS_PORT}" ping fi set -e } diff --git a/build/redis/redis.conf.tpl b/build/redis/redis.conf.tpl index 1adee8153..7e1ddbf4b 100644 --- a/build/redis/redis.conf.tpl +++ b/build/redis/redis.conf.tpl @@ -20,3 +20,7 @@ rdbcompression yes repl-diskless-sync yes save "" protected-mode no +requirepass replace-default-auth +masterauth replace-default-auth + + diff --git a/build/redis/redis_liveness.sh.tpl b/build/redis/redis_liveness.sh.tpl index b64807182..7b7d1caa7 100644 --- a/build/redis/redis_liveness.sh.tpl +++ b/build/redis/redis_liveness.sh.tpl @@ -1,5 +1,6 @@ response=$( redis-cli \ + -a "${AUTH}" --no-auth-warning \ -h localhost \ -p 6379 \ {{- if eq .UseTLS "true"}} diff --git a/build/redis/redis_readiness.sh.tpl b/build/redis/redis_readiness.sh.tpl index c6caf15ce..29e7d58c3 100644 --- a/build/redis/redis_readiness.sh.tpl +++ b/build/redis/redis_readiness.sh.tpl @@ -1,5 +1,6 @@ response=$( redis-cli \ + -a "${AUTH}" --no-auth-warning \ -h localhost \ -p 6379 \ {{- if eq .UseTLS "true"}} diff --git a/build/redis/sentinel.conf.tpl b/build/redis/sentinel.conf.tpl index 5466fd5ed..130539a66 100644 --- a/build/redis/sentinel.conf.tpl +++ b/build/redis/sentinel.conf.tpl @@ -15,3 +15,4 @@ bind 0.0.0.0 sentinel failover-timeout argocd 180000 maxclients 10000 sentinel parallel-syncs argocd 5 + sentinel auth-pass argocd replace-default-auth diff --git a/build/redis/sentinel_liveness.sh.tpl b/build/redis/sentinel_liveness.sh.tpl index 5629c6f39..7ff9239f5 100644 --- a/build/redis/sentinel_liveness.sh.tpl +++ b/build/redis/sentinel_liveness.sh.tpl @@ -1,5 +1,6 @@ response=$( redis-cli \ + -a "${AUTH}" --no-auth-warning \ -h localhost \ -p 26379 \ {{- if eq .UseTLS "true"}} diff --git a/common/defaults.go b/common/defaults.go index b581f50c6..9b5bb63b5 100644 --- a/common/defaults.go +++ b/common/defaults.go @@ -274,6 +274,15 @@ gitlab.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCsj2bNKTBSpIYDEGk9KxsGh3mySTRgM ssh.dev.azure.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7Hr1oTWqNqOlzGJOfGJ4NakVyIzf1rXYd4d7wo6jBlkLvCA4odBlL0mDUyZ0/QUfTTqeu+tm22gOsv+VrVTMk6vwRU75gY/y9ut5Mb3bR5BV58dKXyq9A9UeB5Cakehn5Zgm6x1mKoVyf+FFn26iYqXJRgzIZZcZ5V6hrE0Qg39kZm4az48o0AUbf6Sp4SLdvnuMa2sVNwHBboS7EJkm57XQPVU3/QpyNLHbWDdzwtrlS+ez30S3AdYhLKEOxAG8weOnyrtLJAUen9mTkol8oII1edf7mWWbWVf0nBmly21+nZcmCTISQBtdcyPaEno7fFQMDD26/s0lfKob4Kw8H vs-ssh.visualstudio.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7Hr1oTWqNqOlzGJOfGJ4NakVyIzf1rXYd4d7wo6jBlkLvCA4odBlL0mDUyZ0/QUfTTqeu+tm22gOsv+VrVTMk6vwRU75gY/y9ut5Mb3bR5BV58dKXyq9A9UeB5Cakehn5Zgm6x1mKoVyf+FFn26iYqXJRgzIZZcZ5V6hrE0Qg39kZm4az48o0AUbf6Sp4SLdvnuMa2sVNwHBboS7EJkm57XQPVU3/QpyNLHbWDdzwtrlS+ez30S3AdYhLKEOxAG8weOnyrtLJAUen9mTkol8oII1edf7mWWbWVf0nBmly21+nZcmCTISQBtdcyPaEno7fFQMDD26/s0lfKob4Kw8H ` + // RedisDefaultAdminPasswordLength is the length of the generated default redis admin password. + RedisDefaultAdminPasswordLength = 16 + + // RedisDefaultAdminPasswordNumDigits is the number of digits to use for the generated default redis admin password. + RedisDefaultAdminPasswordNumDigits = 5 + + // RedisDefaultAdminPasswordNumSymbols is the number of symbols to use for the generated default redis admin password. + RedisDefaultAdminPasswordNumSymbols = 0 + // OperatorMetricsPort is the port that is used to expose default controller-runtime metrics for the operator pod. OperatorMetricsPort = 8080 diff --git a/controllers/argocd/deployment.go b/controllers/argocd/deployment.go index a70c8244f..23022c31c 100644 --- a/controllers/argocd/deployment.go +++ b/controllers/argocd/deployment.go @@ -212,6 +212,7 @@ func getArgoRedisArgs(useTLS bool) []string { args = append(args, "--save", "") args = append(args, "--appendonly", "no") + args = append(args, "--requirepass $(REDIS_PASSWORD)") if useTLS { args = append(args, "--tls-port", "6379") @@ -461,6 +462,18 @@ func (r *ReconcileArgoCD) reconcileGrafanaDeployment(cr *argoproj.ArgoCD) error func (r *ReconcileArgoCD) reconcileRedisDeployment(cr *argoproj.ArgoCD, useTLS bool) error { deploy := newDeploymentWithSuffix("redis", "redis", cr) + env := append(proxyEnvVars(), corev1.EnvVar{ + Name: "REDIS_PASSWORD", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: fmt.Sprintf("%s-%s", cr.Name, "redis-initial-password"), + }, + Key: "admin.password", + }, + }, + }) + AddSeccompProfileForOpenShift(r.Client, &deploy.Spec.Template.Spec) deploy.Spec.Template.Spec.Containers = []corev1.Container{{ @@ -474,7 +487,7 @@ func (r *ReconcileArgoCD) reconcileRedisDeployment(cr *argoproj.ArgoCD, useTLS b }, }, Resources: getRedisResources(cr), - Env: proxyEnvVars(), + Env: env, SecurityContext: &corev1.SecurityContext{ AllowPrivilegeEscalation: boolPtr(false), Capabilities: &corev1.Capabilities{ @@ -571,6 +584,18 @@ func (r *ReconcileArgoCD) reconcileRedisDeployment(cr *argoproj.ArgoCD, useTLS b func (r *ReconcileArgoCD) reconcileRedisHAProxyDeployment(cr *argoproj.ArgoCD) error { deploy := newDeploymentWithSuffix("redis-ha-haproxy", "redis", cr) + var redisEnv = append(proxyEnvVars(), corev1.EnvVar{ + Name: "AUTH", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: fmt.Sprintf("%s-%s", cr.Name, "redis-initial-password"), + }, + Key: "admin.password", + }, + }, + }) + deploy.Spec.Template.Spec.Affinity = &corev1.Affinity{ PodAntiAffinity: &corev1.PodAntiAffinity{ PreferredDuringSchedulingIgnoredDuringExecution: []corev1.WeightedPodAffinityTerm{ @@ -603,7 +628,7 @@ func (r *ReconcileArgoCD) reconcileRedisHAProxyDeployment(cr *argoproj.ArgoCD) e Image: getRedisHAProxyContainerImage(cr), ImagePullPolicy: corev1.PullIfNotPresent, Name: "haproxy", - Env: proxyEnvVars(), + Env: redisEnv, LivenessProbe: &corev1.Probe{ ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ @@ -676,6 +701,10 @@ func (r *ReconcileArgoCD) reconcileRedisHAProxyDeployment(cr *argoproj.ArgoCD) e Name: "data", MountPath: "/data", }, + { + Name: "redis-initial-pass", + MountPath: "/redis-initial-pass", + }, }, }} @@ -711,6 +740,15 @@ func (r *ReconcileArgoCD) reconcileRedisHAProxyDeployment(cr *argoproj.ArgoCD) e }, }, }, + { + Name: "redis-initial-pass", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: fmt.Sprintf("%s-%s", cr.Name, "redis-initial-password"), + Optional: boolPtr(true), + }, + }, + }, } deploy.Spec.Template.Spec.SecurityContext = &corev1.PodSecurityContext{ @@ -789,6 +827,17 @@ func (r *ReconcileArgoCD) reconcileRepoDeployment(cr *argoproj.ArgoCD, useTLSFor // Global proxy env vars go first repoEnv := cr.Spec.Repo.Env + repoEnv = append(repoEnv, corev1.EnvVar{ + Name: "REDIS_PASSWORD", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: fmt.Sprintf("%s-%s", cr.Name, "redis-initial-password"), + }, + Key: "admin.password", + }, + }, + }) // Environment specified in the CR take precedence over everything else repoEnv = argoutil.EnvMerge(repoEnv, proxyEnvVars(), false) if cr.Spec.Repo.ExecTimeout != nil { @@ -1092,6 +1141,17 @@ func (r *ReconcileArgoCD) reconcileRepoDeployment(cr *argoproj.ArgoCD, useTLSFor func (r *ReconcileArgoCD) reconcileServerDeployment(cr *argoproj.ArgoCD, useTLSForRedis bool) error { deploy := newDeploymentWithSuffix("server", "server", cr) serverEnv := cr.Spec.Server.Env + serverEnv = append(serverEnv, corev1.EnvVar{ + Name: "REDIS_PASSWORD", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: fmt.Sprintf("%s-%s", cr.Name, "redis-initial-password"), + }, + Key: "admin.password", + }, + }, + }) serverEnv = argoutil.EnvMerge(serverEnv, proxyEnvVars(), false) AddSeccompProfileForOpenShift(r.Client, &deploy.Spec.Template.Spec) deploy.Spec.Template.Spec.Containers = []corev1.Container{{ diff --git a/controllers/argocd/deployment_test.go b/controllers/argocd/deployment_test.go index 1050260c5..6d6d392e7 100644 --- a/controllers/argocd/deployment_test.go +++ b/controllers/argocd/deployment_test.go @@ -2,6 +2,7 @@ package argocd import ( "context" + "fmt" "reflect" "strings" "testing" @@ -312,7 +313,8 @@ func TestReconcileArgoCD_reconcile_ServerDeployment_env(t *testing.T) { }, deployment) assert.NoError(t, err) - assert.Len(t, deployment.Spec.Template.Spec.Containers[0].Env, 2) + // Check that the env vars are set, Count is 3 because of the default REDIS_PASSWORD env var + assert.Len(t, deployment.Spec.Template.Spec.Containers[0].Env, 3) assert.Contains(t, deployment.Spec.Template.Spec.Containers[0].Env, corev1.EnvVar{Name: "FOO", Value: "BAR"}) assert.Contains(t, deployment.Spec.Template.Spec.Containers[0].Env, corev1.EnvVar{Name: "BAR", Value: "FOO"}) }) @@ -352,7 +354,8 @@ func TestReconcileArgoCD_reconcileRepoDeployment_env(t *testing.T) { }, deployment) assert.NoError(t, err) - assert.Len(t, deployment.Spec.Template.Spec.Containers[0].Env, 3) + // Check that the env vars are set, Count is 4 because of the default REDIS_PASSWORD env var + assert.Len(t, deployment.Spec.Template.Spec.Containers[0].Env, 4) assert.Contains(t, deployment.Spec.Template.Spec.Containers[0].Env, corev1.EnvVar{Name: "FOO", Value: "BAR"}) assert.Contains(t, deployment.Spec.Template.Spec.Containers[0].Env, corev1.EnvVar{Name: "BAR", Value: "FOO"}) assert.Contains(t, deployment.Spec.Template.Spec.Containers[0].Env, corev1.EnvVar{Name: "ARGOCD_EXEC_TIMEOUT", Value: "600s"}) @@ -380,7 +383,8 @@ func TestReconcileArgoCD_reconcileRepoDeployment_env(t *testing.T) { }, deployment) assert.NoError(t, err) - assert.Len(t, deployment.Spec.Template.Spec.Containers[0].Env, 1) + // Check that the env vars are set, Count is 2 because of the default REDIS_PASSWORD env var + assert.Len(t, deployment.Spec.Template.Spec.Containers[0].Env, 2) assert.Contains(t, deployment.Spec.Template.Spec.Containers[0].Env, corev1.EnvVar{Name: "ARGOCD_EXEC_TIMEOUT", Value: "600s"}) }) @@ -412,7 +416,8 @@ func TestReconcileArgoCD_reconcileRepoDeployment_env(t *testing.T) { }, deployment) assert.NoError(t, err) - assert.Len(t, deployment.Spec.Template.Spec.Containers[0].Env, 1) + // Check that the env vars are set, Count is 2 because of the default REDIS_PASSWORD env var + assert.Len(t, deployment.Spec.Template.Spec.Containers[0].Env, 2) assert.Contains(t, deployment.Spec.Template.Spec.Containers[0].Env, corev1.EnvVar{Name: "ARGOCD_EXEC_TIMEOUT", Value: "600s"}) }) t.Run("ExecTimeout not set", func(t *testing.T) { @@ -434,7 +439,6 @@ func TestReconcileArgoCD_reconcileRepoDeployment_env(t *testing.T) { Namespace: testNamespace, }, deployment) assert.NoError(t, err) - assert.Empty(t, deployment.Spec.Template.Spec.Containers[0].Env) }) } @@ -1077,6 +1081,18 @@ func TestReconcileArgoCD_reconcileServerDeployment(t *testing.T) { "--logformat", "text", }, + Env: []corev1.EnvVar{ + {Name: "REDIS_PASSWORD", Value: "", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: fmt.Sprintf("argocd-redis-initial-password"), + }, + Key: "admin.password", + }, + }, + }, + }, Ports: []corev1.ContainerPort{ {ContainerPort: 8080}, {ContainerPort: 8083}, @@ -1304,6 +1320,18 @@ func TestReconcileArgoCD_reconcileServerDeploymentWithInsecure(t *testing.T) { "--logformat", "text", }, + Env: []corev1.EnvVar{ + {Name: "REDIS_PASSWORD", Value: "", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: fmt.Sprintf("argocd-redis-initial-password"), + }, + Key: "admin.password", + }, + }, + }, + }, Ports: []corev1.ContainerPort{ {ContainerPort: 8080}, {ContainerPort: 8083}, @@ -1396,6 +1424,18 @@ func TestReconcileArgoCD_reconcileServerDeploymentChangedToInsecure(t *testing.T "--logformat", "text", }, + Env: []corev1.EnvVar{ + {Name: "REDIS_PASSWORD", Value: "", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: fmt.Sprintf("argocd-redis-initial-password"), + }, + Key: "admin.password", + }, + }, + }, + }, Ports: []corev1.ContainerPort{ {ContainerPort: 8080}, {ContainerPort: 8083}, @@ -1454,6 +1494,7 @@ func TestReconcileArgoCD_reconcileRedisDeploymentWithoutTLS(t *testing.T) { "--save", "", "--appendonly", "no", + "--requirepass $(REDIS_PASSWORD)", } assert.NoError(t, r.reconcileRedisDeployment(cr, false)) @@ -1478,6 +1519,7 @@ func TestReconcileArgoCD_reconcileRedisDeploymentWithTLS(t *testing.T) { want := []string{ "--save", "", "--appendonly", "no", + "--requirepass $(REDIS_PASSWORD)", "--tls-port", "6379", "--port", "0", "--tls-cert-file", "/app/config/redis/tls/tls.crt", @@ -1639,7 +1681,6 @@ func assertDeploymentHasProxyVars(t *testing.T, c client.Client, name string) { {Name: "no_proxy", Value: testNoProxy}, } for _, c := range deployment.Spec.Template.Spec.Containers { - assert.Len(t, c.Env, len(want)) for _, w := range want { assert.Contains(t, c.Env, w) } diff --git a/controllers/argocd/networkpolicies.go b/controllers/argocd/networkpolicies.go new file mode 100644 index 000000000..80c7bc7a5 --- /dev/null +++ b/controllers/argocd/networkpolicies.go @@ -0,0 +1,264 @@ +package argocd + +import ( + "context" + "fmt" + "reflect" + + argoproj "github.com/argoproj-labs/argocd-operator/api/v1beta1" + "github.com/argoproj-labs/argocd-operator/controllers/argoutil" + corev1 "k8s.io/api/core/v1" + networkingv1 "k8s.io/api/networking/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" +) + +var ( + TCPProtocol = func() *corev1.Protocol { + tcpProtocol := corev1.ProtocolTCP + return &tcpProtocol + }() +) + +const ( + // RedisIngressNetworkPolicy is the name of the network policy which controls Redis Ingress traffic + RedisNetworkPolicy = "redis-network-policy" + // RedisHAIngressNetworkPolicy is the name of the network policy which controls Redis HA Ingress traffic + RedisHANetworkPolicy = "redis-ha-network-policy" +) + +func (r *ReconcileArgoCD) ReconcileNetworkPolicies(cr *argoproj.ArgoCD) error { + + // Reconcile Redis network policy + if err := r.ReconcileRedisNetworkPolicy(cr); err != nil { + return err + } + + // Reconcile Redis HA network policy + if err := r.ReconcileRedisHANetworkPolicy(cr); err != nil { + return err + } + + return nil +} + +// ReconcileRedisNetworkPolicy creates and reconciles network policy for Redis +func (r *ReconcileArgoCD) ReconcileRedisNetworkPolicy(cr *argoproj.ArgoCD) error { + + networkPolicy := &networkingv1.NetworkPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("%s-%s", cr.Name, RedisNetworkPolicy), + Namespace: cr.Namespace, + }, + Spec: networkingv1.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app.kubernetes.io/name": fmt.Sprintf("%s-%s", cr.Name, "redis"), + }, + }, + PolicyTypes: []networkingv1.PolicyType{ + networkingv1.PolicyTypeIngress, + }, + Ingress: []networkingv1.NetworkPolicyIngressRule{ + { + From: []networkingv1.NetworkPolicyPeer{ + { + PodSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app.kubernetes.io/name": fmt.Sprintf("%s-%s", cr.Name, "application-controller"), + }, + }, + }, + { + PodSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app.kubernetes.io/name": fmt.Sprintf("%s-%s", cr.Name, "repo-server"), + }, + }, + }, + { + PodSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app.kubernetes.io/name": fmt.Sprintf("%s-%s", cr.Name, "server"), + }, + }, + }, + }, + Ports: []networkingv1.NetworkPolicyPort{ + { + Protocol: TCPProtocol, + Port: &intstr.IntOrString{Type: intstr.Int, IntVal: 6379}, + }, + }, + }, + }, + }, + } + + // Check if the network policy already exists + existing := &networkingv1.NetworkPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("%s-%s", cr.Name, RedisNetworkPolicy), + Namespace: cr.Namespace, + }, + } + + if argoutil.IsObjectFound(r.Client, cr.Namespace, existing.Name, existing) { + + modified := false + if !reflect.DeepEqual(existing.Spec.PodSelector, networkPolicy.Spec.PodSelector) { + existing.Spec.PodSelector = networkPolicy.Spec.PodSelector + modified = true + } + if !reflect.DeepEqual(existing.Spec.PolicyTypes, networkPolicy.Spec.PolicyTypes) { + existing.Spec.PolicyTypes = networkPolicy.Spec.PolicyTypes + modified = true + } + if !reflect.DeepEqual(existing.Spec.Ingress, networkPolicy.Spec.Ingress) { + existing.Spec.Ingress = networkPolicy.Spec.Ingress + modified = true + } + + if modified { + log.Info("Updating redis network policy", "namespace", networkPolicy.Namespace, "name", networkPolicy.Name) + err := r.Client.Update(context.TODO(), existing) + if err != nil { + log.Error(err, "Failed to update redis network policy") + return err + } + } + + // Nothing to do, NetworkPolicy already exists and not modified + return nil + + } + + // Set the ArgoCD instance as the owner and controller + if err := controllerutil.SetControllerReference(cr, networkPolicy, r.Scheme); err != nil { + log.Error(err, "Failed to set controller reference on redis network policy") + return err + } + + log.Info("Creating redis network policy", "namespace", networkPolicy.Namespace, "name", networkPolicy.Name) + err := r.Client.Create(context.TODO(), networkPolicy) + if err != nil { + log.Error(err, "Failed to create redis network policy") + return err + } + + return nil + +} + +// ReconcileRedisHANetworkPolicy creates and reconciles network policy for Redis HA +func (r *ReconcileArgoCD) ReconcileRedisHANetworkPolicy(cr *argoproj.ArgoCD) error { + + networkPolicy := &networkingv1.NetworkPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("%s-%s", cr.Name, RedisHANetworkPolicy), + Namespace: cr.Namespace, + }, + Spec: networkingv1.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app.kubernetes.io/name": fmt.Sprintf("%s-%s", cr.Name, "redis-ha-haproxy"), + }, + }, + PolicyTypes: []networkingv1.PolicyType{ + networkingv1.PolicyTypeIngress, + }, + Ingress: []networkingv1.NetworkPolicyIngressRule{ + { + From: []networkingv1.NetworkPolicyPeer{ + { + PodSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app.kubernetes.io/name": fmt.Sprintf("%s-%s", cr.Name, "application-controller"), + }, + }, + }, + { + PodSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app.kubernetes.io/name": fmt.Sprintf("%s-%s", cr.Name, "repo-server"), + }, + }, + }, + { + PodSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app.kubernetes.io/name": fmt.Sprintf("%s-%s", cr.Name, "server"), + }, + }, + }, + }, + Ports: []networkingv1.NetworkPolicyPort{ + { + Protocol: TCPProtocol, + Port: &intstr.IntOrString{Type: intstr.Int, IntVal: 6379}, + }, + { + Protocol: TCPProtocol, + Port: &intstr.IntOrString{Type: intstr.Int, IntVal: 26379}, + }, + }, + }, + }, + }, + } + + // Check if the network policy already exists + existing := &networkingv1.NetworkPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("%s-%s", cr.Name, RedisHANetworkPolicy), + Namespace: cr.Namespace, + }, + } + + if argoutil.IsObjectFound(r.Client, cr.Namespace, existing.Name, existing) { + + modified := false + if !reflect.DeepEqual(existing.Spec.PodSelector, networkPolicy.Spec.PodSelector) { + existing.Spec.PodSelector = networkPolicy.Spec.PodSelector + modified = true + } + if !reflect.DeepEqual(existing.Spec.PolicyTypes, networkPolicy.Spec.PolicyTypes) { + existing.Spec.PolicyTypes = networkPolicy.Spec.PolicyTypes + modified = true + } + if !reflect.DeepEqual(existing.Spec.Ingress, networkPolicy.Spec.Ingress) { + existing.Spec.Ingress = networkPolicy.Spec.Ingress + modified = true + } + + if modified { + log.Info("Updating redis ha network policy", "namespace", networkPolicy.Namespace, "name", networkPolicy.Name) + err := r.Client.Update(context.TODO(), existing) + if err != nil { + log.Error(err, "Failed to update redis ha network policy") + return err + } + } + + // Nothing to do, NetworkPolicy already exists and not modified + return nil + + } + + // Set the ArgoCD instance as the owner and controller + if err := controllerutil.SetControllerReference(cr, networkPolicy, r.Scheme); err != nil { + log.Error(err, "Failed to set controller reference on redis ha network policy") + return err + } + + log.Info("Creating redis ha network policy", "namespace", networkPolicy.Namespace, "name", networkPolicy.Name) + err := r.Client.Create(context.TODO(), networkPolicy) + if err != nil { + log.Error(err, "Failed to create redis ha network policy") + return err + } + + return nil + +} diff --git a/controllers/argocd/networkpolicies_test.go b/controllers/argocd/networkpolicies_test.go new file mode 100644 index 000000000..91efd5a1b --- /dev/null +++ b/controllers/argocd/networkpolicies_test.go @@ -0,0 +1,81 @@ +package argocd + +import ( + "context" + "fmt" + "testing" + + argoproj "github.com/argoproj-labs/argocd-operator/api/v1beta1" + "github.com/stretchr/testify/assert" + networkingv1 "k8s.io/api/networking/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/intstr" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +func TestReconcileNetworkPolicies(t *testing.T) { + + a := makeTestArgoCD() + r := makeTestReconciler(makeTestReconcilerClient(makeTestReconcilerScheme(argoproj.AddToScheme), []client.Object{a}, []client.Object{a}, []runtime.Object{}), makeTestReconcilerScheme(argoproj.AddToScheme)) + + err := r.ReconcileRedisNetworkPolicy(a) + assert.NoError(t, err) + + err = r.ReconcileRedisHANetworkPolicy(a) + assert.NoError(t, err) +} + +func TestRedisNetworkPolicy(t *testing.T) { + a := makeTestArgoCD() + r := makeTestReconciler(makeTestReconcilerClient(makeTestReconcilerScheme(argoproj.AddToScheme), []client.Object{a}, []client.Object{a}, []runtime.Object{}), makeTestReconcilerScheme(argoproj.AddToScheme)) + + err := r.ReconcileRedisNetworkPolicy(a) + assert.NoError(t, err) + + // Check if the network policy was created + np := &networkingv1.NetworkPolicy{} + err = r.Get(context.TODO(), client.ObjectKey{Name: fmt.Sprintf("%s-%s", a.Name, RedisNetworkPolicy), Namespace: a.Namespace}, np) + assert.NoError(t, err) + + // Check if the network policy has the correct pod selector + assert.Equal(t, fmt.Sprintf("%s-%s", a.Name, "redis"), np.Spec.PodSelector.MatchLabels["app.kubernetes.io/name"]) + + // Check if the network policy has the correct policy types + assert.Equal(t, networkingv1.PolicyTypeIngress, np.Spec.PolicyTypes[0]) + + // Check if the network policy has the correct ingress rules + assert.Equal(t, 3, len(np.Spec.Ingress[0].From)) + assert.Equal(t, "argocd-application-controller", np.Spec.Ingress[0].From[0].PodSelector.MatchLabels["app.kubernetes.io/name"]) + assert.Equal(t, "argocd-repo-server", np.Spec.Ingress[0].From[1].PodSelector.MatchLabels["app.kubernetes.io/name"]) + assert.Equal(t, "argocd-server", np.Spec.Ingress[0].From[2].PodSelector.MatchLabels["app.kubernetes.io/name"]) + assert.Equal(t, 1, len(np.Spec.Ingress[0].Ports)) + assert.Equal(t, intstr.FromInt(6379), *np.Spec.Ingress[0].Ports[0].Port) +} + +func TestRedisHANetworkPolicy(t *testing.T) { + a := makeTestArgoCD() + r := makeTestReconciler(makeTestReconcilerClient(makeTestReconcilerScheme(argoproj.AddToScheme), []client.Object{a}, []client.Object{a}, []runtime.Object{}), makeTestReconcilerScheme(argoproj.AddToScheme)) + + err := r.ReconcileRedisHANetworkPolicy(a) + assert.NoError(t, err) + + // Check if the network policy was created + np := &networkingv1.NetworkPolicy{} + err = r.Get(context.TODO(), client.ObjectKey{Name: fmt.Sprintf("%s-%s", a.Name, RedisHANetworkPolicy), Namespace: a.Namespace}, np) + assert.NoError(t, err) + + // Check if the network policy has the correct pod selector + assert.Equal(t, fmt.Sprintf("%s-%s", a.Name, "redis-ha-haproxy"), np.Spec.PodSelector.MatchLabels["app.kubernetes.io/name"]) + + // Check if the network policy has the correct policy types + assert.Equal(t, networkingv1.PolicyTypeIngress, np.Spec.PolicyTypes[0]) + + // Check if the network policy has the correct ingress rules + assert.Equal(t, 3, len(np.Spec.Ingress[0].From)) + assert.Equal(t, "argocd-application-controller", np.Spec.Ingress[0].From[0].PodSelector.MatchLabels["app.kubernetes.io/name"]) + assert.Equal(t, "argocd-repo-server", np.Spec.Ingress[0].From[1].PodSelector.MatchLabels["app.kubernetes.io/name"]) + assert.Equal(t, "argocd-server", np.Spec.Ingress[0].From[2].PodSelector.MatchLabels["app.kubernetes.io/name"]) + assert.Equal(t, 2, len(np.Spec.Ingress[0].Ports)) + assert.Equal(t, intstr.FromInt(6379), *np.Spec.Ingress[0].Ports[0].Port) + assert.Equal(t, intstr.FromInt(26379), *np.Spec.Ingress[0].Ports[1].Port) +} diff --git a/controllers/argocd/policyrule.go b/controllers/argocd/policyrule.go index c94e9e559..f564501af 100644 --- a/controllers/argocd/policyrule.go +++ b/controllers/argocd/policyrule.go @@ -29,7 +29,21 @@ func policyRuleForApplicationController() []v1.PolicyRule { } func policyRuleForRedis(client client.Client) []v1.PolicyRule { - rules := []v1.PolicyRule{} + rules := []v1.PolicyRule{ + { + APIGroups: []string{ + "", + }, + Resources: []string{ + "secrets", + }, + Verbs: []string{ + "get", + "list", + "watch", + }, + }, + } // Need additional policy rules if we are running on openshift, else the stateful set won't have the right // permissions to start @@ -52,6 +66,19 @@ func policyRuleForRedisHa(client client.Client) []v1.PolicyRule { "get", }, }, + { + APIGroups: []string{ + "", + }, + Resources: []string{ + "secrets", + }, + Verbs: []string{ + "get", + "list", + "watch", + }, + }, } // Need additional policy rules if we are running on openshift, else the stateful set won't have the right diff --git a/controllers/argocd/secret.go b/controllers/argocd/secret.go index b00f0948c..c546d3b25 100644 --- a/controllers/argocd/secret.go +++ b/controllers/argocd/secret.go @@ -278,6 +278,10 @@ func (r *ReconcileArgoCD) reconcileClusterSecrets(cr *argoproj.ArgoCD) error { return err } + if err := r.reconcileRedisInitialPasswordSecret(cr); err != nil { + return err + } + if err := r.reconcileClusterCASecret(cr); err != nil { return err } @@ -647,3 +651,26 @@ func (r *ReconcileArgoCD) getClusterSecrets(cr *argoproj.ArgoCD) (*corev1.Secret return clusterSecrets, nil } + +// reconcileRedisInitialPasswordSecret will ensure that the redis Secret is present for the cluster. +func (r *ReconcileArgoCD) reconcileRedisInitialPasswordSecret(cr *argoproj.ArgoCD) error { + secret := argoutil.NewSecretWithSuffix(cr, "redis-initial-password") + if argoutil.IsObjectFound(r.Client, cr.Namespace, secret.Name, secret) { + return nil // Secret found, do nothing + } + + redisInitialPassword, err := generateRedisAdminPassword() + if err != nil { + return err + } + + secret.Data = map[string][]byte{ + "immutable": []byte("true"), + common.ArgoCDKeyAdminPassword: redisInitialPassword, + } + + if err := controllerutil.SetControllerReference(cr, secret, r.Scheme); err != nil { + return err + } + return r.Client.Create(context.TODO(), secret) +} diff --git a/controllers/argocd/statefulset.go b/controllers/argocd/statefulset.go index a0d16e9cd..6d011fbdf 100644 --- a/controllers/argocd/statefulset.go +++ b/controllers/argocd/statefulset.go @@ -94,6 +94,18 @@ func newStatefulSetWithSuffix(suffix string, component string, cr *argoproj.Argo func (r *ReconcileArgoCD) reconcileRedisStatefulSet(cr *argoproj.ArgoCD) error { ss := newStatefulSetWithSuffix("redis-ha-server", "redis", cr) + redisEnv := append(proxyEnvVars(), corev1.EnvVar{ + Name: "AUTH", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: fmt.Sprintf("%s-%s", cr.Name, "redis-initial-password"), + }, + Key: "admin.password", + }, + }, + }) + ss.Spec.PodManagementPolicy = appsv1.OrderedReadyPodManagement ss.Spec.Replicas = getRedisHAReplicas(cr) ss.Spec.Selector = &metav1.LabelSelector{ @@ -137,6 +149,7 @@ func (r *ReconcileArgoCD) reconcileRedisStatefulSet(cr *argoproj.ArgoCD) error { Command: []string{ "redis-server", }, + Env: redisEnv, Image: getRedisHAContainerImage(cr), ImagePullPolicy: corev1.PullIfNotPresent, LivenessProbe: &corev1.Probe{ @@ -208,6 +221,7 @@ func (r *ReconcileArgoCD) reconcileRedisStatefulSet(cr *argoproj.ArgoCD) error { Command: []string{ "redis-sentinel", }, + Env: redisEnv, Image: getRedisHAContainerImage(cr), ImagePullPolicy: corev1.PullIfNotPresent, LivenessProbe: &corev1.Probe{ @@ -294,6 +308,17 @@ func (r *ReconcileArgoCD) reconcileRedisStatefulSet(cr *argoproj.ArgoCD) error { Name: "SENTINEL_ID_2", Value: "2bbec7894d954a8af3bb54d13eaec53cb024e2ca", // TODO: Should this be hard-coded? }, + { + Name: "AUTH", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: fmt.Sprintf("%s-%s", cr.Name, "redis-initial-password"), + }, + Key: "admin.password", + }, + }, + }, }, Image: getRedisHAContainerImage(cr), ImagePullPolicy: corev1.PullIfNotPresent, @@ -447,6 +472,18 @@ func getArgoControllerContainerEnv(cr *argoproj.ArgoCD) []corev1.EnvVar { Value: "/home/argocd", }) + env = append(env, corev1.EnvVar{ + Name: "REDIS_PASSWORD", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: fmt.Sprintf("%s-%s", cr.Name, "redis-initial-password"), + }, + Key: "admin.password", + }, + }, + }) + if cr.Spec.Controller.Sharding.Enabled { env = append(env, corev1.EnvVar{ Name: "ARGOCD_CONTROLLER_REPLICAS", diff --git a/controllers/argocd/statefulset_test.go b/controllers/argocd/statefulset_test.go index 634f5a204..68538e409 100644 --- a/controllers/argocd/statefulset_test.go +++ b/controllers/argocd/statefulset_test.go @@ -360,6 +360,15 @@ func TestReconcileArgoCD_reconcileApplicationController_withSharding(t *testing. replicas: 1, vars: []corev1.EnvVar{ {Name: "HOME", Value: "/home/argocd"}, + {Name: "REDIS_PASSWORD", Value: "", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: fmt.Sprintf("argocd-redis-initial-password"), + }, + Key: "admin.password", + }, + }}, }, }, { @@ -371,6 +380,15 @@ func TestReconcileArgoCD_reconcileApplicationController_withSharding(t *testing. vars: []corev1.EnvVar{ {Name: "ARGOCD_CONTROLLER_REPLICAS", Value: "1"}, {Name: "HOME", Value: "/home/argocd"}, + {Name: "REDIS_PASSWORD", Value: "", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: fmt.Sprintf("argocd-redis-initial-password"), + }, + Key: "admin.password", + }, + }}, }, }, { @@ -382,6 +400,15 @@ func TestReconcileArgoCD_reconcileApplicationController_withSharding(t *testing. vars: []corev1.EnvVar{ {Name: "ARGOCD_CONTROLLER_REPLICAS", Value: "3"}, {Name: "HOME", Value: "/home/argocd"}, + {Name: "REDIS_PASSWORD", Value: "", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: fmt.Sprintf("argocd-redis-initial-password"), + }, + Key: "admin.password", + }, + }}, }, }, } @@ -430,6 +457,15 @@ func TestReconcileArgoCD_reconcileApplicationController_withAppSync(t *testing.T expectedEnv := []corev1.EnvVar{ {Name: "ARGOCD_RECONCILIATION_TIMEOUT", Value: "600s"}, {Name: "HOME", Value: "/home/argocd"}, + {Name: "REDIS_PASSWORD", Value: "", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: fmt.Sprintf("argocd-redis-initial-password"), + }, + Key: "admin.password", + }, + }}, } a := makeTestArgoCD(func(a *argoproj.ArgoCD) { @@ -468,6 +504,15 @@ func TestReconcileArgoCD_reconcileApplicationController_withEnv(t *testing.T) { expectedEnv := []corev1.EnvVar{ {Name: "CUSTOM_ENV_VAR", Value: "custom-value"}, {Name: "HOME", Value: "/home/argocd"}, + {Name: "REDIS_PASSWORD", Value: "", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: fmt.Sprintf("argocd-redis-initial-password"), + }, + Key: "admin.password", + }, + }}, } a := makeTestArgoCD(func(a *argoproj.ArgoCD) { diff --git a/controllers/argocd/util.go b/controllers/argocd/util.go index e60170ae4..bb11b8783 100644 --- a/controllers/argocd/util.go +++ b/controllers/argocd/util.go @@ -99,6 +99,17 @@ func generateArgoAdminPassword() ([]byte, error) { return []byte(pass), err } +// generateRedisAdminPassword will generate and return the admin password for Redis. +func generateRedisAdminPassword() ([]byte, error) { + pass, err := password.Generate( + common.RedisDefaultAdminPasswordLength, + common.RedisDefaultAdminPasswordNumDigits, + common.RedisDefaultAdminPasswordNumSymbols, + false, false) + + return []byte(pass), err +} + // generateArgoServerKey will generate and return the server signature key for session validation. func generateArgoServerSessionKey() ([]byte, error) { pass, err := password.Generate( @@ -818,6 +829,10 @@ func (r *ReconcileArgoCD) reconcileResources(cr *argoproj.ArgoCD) error { return err } + if err := r.ReconcileNetworkPolicies(cr); err != nil { + return err + } + return nil } diff --git a/controllers/argocd/util_test.go b/controllers/argocd/util_test.go index 4d4190474..f1f31621b 100644 --- a/controllers/argocd/util_test.go +++ b/controllers/argocd/util_test.go @@ -3,6 +3,7 @@ package argocd import ( "context" b64 "encoding/base64" + "fmt" "reflect" "strings" "testing" @@ -16,6 +17,7 @@ import ( "github.com/argoproj-labs/argocd-operator/common" "github.com/argoproj-labs/argocd-operator/controllers/argoutil" + corev1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -548,6 +550,15 @@ func TestGetArgoApplicationContainerEnv(t *testing.T) { sync60s := []v1.EnvVar{ v1.EnvVar{Name: "HOME", Value: "/home/argocd", ValueFrom: (*v1.EnvVarSource)(nil)}, + v1.EnvVar{Name: "REDIS_PASSWORD", Value: "", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: fmt.Sprintf("argocd-redis-initial-password"), + }, + Key: "admin.password", + }, + }}, v1.EnvVar{Name: "ARGOCD_RECONCILIATION_TIMEOUT", Value: "60s", ValueFrom: (*v1.EnvVarSource)(nil)}} cmdTests := []struct {