From eb6141f871063464d83089321eb163d01f823d5c Mon Sep 17 00:00:00 2001 From: Abhishek Veeramalla Date: Wed, 22 May 2024 14:06:50 +0530 Subject: [PATCH] feat: secure redis authentication [CVE-2024-31989] (#1364) (#1370) --- .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 | 86 +++++++ controllers/argocd/util.go | 15 ++ 20 files changed, 680 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 a88c100b3..80c3ba237 100644 --- a/common/defaults.go +++ b/common/defaults.go @@ -304,6 +304,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 125cd555f..567f91915 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") @@ -584,6 +585,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{{ @@ -597,7 +610,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{ @@ -694,6 +707,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{ @@ -726,7 +751,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{ @@ -799,6 +824,10 @@ func (r *ReconcileArgoCD) reconcileRedisHAProxyDeployment(cr *argoproj.ArgoCD) e Name: "data", MountPath: "/data", }, + { + Name: "redis-initial-pass", + MountPath: "/redis-initial-pass", + }, }, }} @@ -834,6 +863,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{ @@ -912,6 +950,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 { @@ -1214,6 +1263,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 f96a187af..539c1766c 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" @@ -313,7 +314,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"}) }) @@ -353,7 +355,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"}) @@ -381,7 +384,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"}) }) @@ -413,7 +417,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) { @@ -435,7 +440,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) }) } @@ -1078,6 +1082,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}, @@ -1305,6 +1321,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}, @@ -1397,6 +1425,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}, @@ -1455,6 +1495,7 @@ func TestReconcileArgoCD_reconcileRedisDeploymentWithoutTLS(t *testing.T) { "--save", "", "--appendonly", "no", + "--requirepass $(REDIS_PASSWORD)", } assert.NoError(t, r.reconcileRedisDeployment(cr, false)) @@ -1479,6 +1520,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", @@ -1646,7 +1688,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 641af56f0..699791e47 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 d596aa7c4..cd0ba7f63 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 } @@ -698,3 +702,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 c5c779a86..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) { @@ -463,6 +499,56 @@ func TestReconcileArgoCD_reconcileApplicationController_withAppSync(t *testing.T } } +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) { + // Assuming spec.controller.env is a slice + a.Spec.Controller.Env = []corev1.EnvVar{ + {Name: "CUSTOM_ENV_VAR", Value: "custom-value"}, + } + }) + + resObjs := []client.Object{a} + subresObjs := []client.Object{a} + runtimeObjs := []runtime.Object{} + sch := makeTestReconcilerScheme(argoproj.AddToScheme) + cl := makeTestReconcilerClient(sch, resObjs, subresObjs, runtimeObjs) + r := makeTestReconciler(cl, sch) + + assert.NoError(t, r.reconcileApplicationControllerStatefulSet(a, false)) + + ss := &appsv1.StatefulSet{} + assert.NoError(t, r.Client.Get( + context.TODO(), + types.NamespacedName{ + Name: "argocd-application-controller", + Namespace: a.Namespace, + }, + ss)) + + env := ss.Spec.Template.Spec.Containers[0].Env + + diffEnv := cmp.Diff(env, expectedEnv) + + if diffEnv != "" { + t.Fatalf("Reconciliation of EnvVars failed:\n%s", diffEnv) + } +} + func Test_UpdateNodePlacementStateful(t *testing.T) { ss := &appsv1.StatefulSet{ diff --git a/controllers/argocd/util.go b/controllers/argocd/util.go index 8c093fcf4..6749a7420 100644 --- a/controllers/argocd/util.go +++ b/controllers/argocd/util.go @@ -91,6 +91,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( @@ -841,6 +852,10 @@ func (r *ReconcileArgoCD) reconcileResources(cr *argoproj.ArgoCD) error { return err } + if err := r.ReconcileNetworkPolicies(cr); err != nil { + return err + } + return nil }