Skip to content

Commit 211af17

Browse files
committed
feat: redis TLS encryption enabled by default for all connections
Assisted-by: Cursor Signed-off-by: Rizwana777 <[email protected]>
1 parent 101d4c8 commit 211af17

37 files changed

+2117
-84
lines changed

Makefile

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,18 @@ endif
5656
ifneq (${ARGOCD_AGENT_IN_CLUSTER},)
5757
./hack/dev-env/restart-all.sh
5858
endif
59+
@echo ""
60+
@echo "Configuring Redis TLS (required for E2E)..."
61+
./hack/dev-env/gen-redis-tls-certs.sh
62+
@echo "Step 1: Enabling TLS on Redis servers (creates secrets)..."
63+
./hack/dev-env/configure-redis-tls.sh vcluster-control-plane
64+
./hack/dev-env/configure-redis-tls.sh vcluster-agent-managed
65+
./hack/dev-env/configure-redis-tls.sh vcluster-agent-autonomous
66+
@echo "Step 2: Configuring Argo CD components for Redis TLS..."
67+
./hack/dev-env/configure-argocd-redis-tls.sh vcluster-control-plane
68+
./hack/dev-env/configure-argocd-redis-tls.sh vcluster-agent-managed
69+
./hack/dev-env/configure-argocd-redis-tls.sh vcluster-agent-autonomous
70+
@echo " E2E environment ready with Redis TLS enabled (required)"
5971

6072
.PHONY: teardown-e2e
6173
teardown-e2e:

agent/agent.go

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,11 @@ package agent
1616

1717
import (
1818
"context"
19+
"crypto/tls"
20+
"crypto/x509"
1921
"fmt"
2022
"net/http"
23+
"os"
2124
"sync"
2225
"sync/atomic"
2326
"time"
@@ -317,7 +320,28 @@ func NewAgent(ctx context.Context, client *kube.KubernetesClient, namespace stri
317320
connMap: map[string]connectionEntry{},
318321
}
319322

320-
clusterCache, err := cluster.NewClusterCacheInstance(a.redisProxyMsgHandler.redisAddress, a.redisProxyMsgHandler.redisPassword, cacheutil.RedisCompressionGZip)
323+
// Create TLS config for cluster cache Redis client (same as for Redis proxy)
324+
var clusterCacheTLSConfig *tls.Config = nil
325+
if a.redisProxyMsgHandler.redisTLSEnabled {
326+
clusterCacheTLSConfig = &tls.Config{
327+
MinVersion: tls.VersionTLS12,
328+
}
329+
if a.redisProxyMsgHandler.redisTLSInsecure {
330+
clusterCacheTLSConfig.InsecureSkipVerify = true
331+
} else if a.redisProxyMsgHandler.redisTLSCAPath != "" {
332+
caCertPEM, err := os.ReadFile(a.redisProxyMsgHandler.redisTLSCAPath)
333+
if err != nil {
334+
return nil, fmt.Errorf("failed to read CA certificate for cluster cache: %w", err)
335+
}
336+
certPool := x509.NewCertPool()
337+
if !certPool.AppendCertsFromPEM(caCertPEM) {
338+
return nil, fmt.Errorf("failed to parse CA certificate for cluster cache from %s", a.redisProxyMsgHandler.redisTLSCAPath)
339+
}
340+
clusterCacheTLSConfig.RootCAs = certPool
341+
}
342+
}
343+
344+
clusterCache, err := cluster.NewClusterCacheInstance(a.redisProxyMsgHandler.redisAddress, a.redisProxyMsgHandler.redisPassword, cacheutil.RedisCompressionGZip, clusterCacheTLSConfig)
321345
if err != nil {
322346
return nil, fmt.Errorf("failed to create cluster cache instance: %v", err)
323347
}

agent/inbound_redis.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@ package agent
1717
import (
1818
"context"
1919
"crypto/tls"
20+
"crypto/x509"
2021
"errors"
2122
"fmt"
23+
"os"
2224
"strings"
2325
"sync"
2426
"time"
@@ -45,6 +47,11 @@ type redisProxyMsgHandler struct {
4547

4648
// connections maintains statistics about redis connections from principal
4749
connections *connectionEntries
50+
51+
// Redis TLS configuration
52+
redisTLSEnabled bool
53+
redisTLSCAPath string
54+
redisTLSInsecure bool
4855
}
4956

5057
// connectionEntries maintains statistics about redis connections from principal
@@ -335,6 +342,35 @@ func stripNamespaceFromRedisKey(key string, logCtx *logrus.Entry) (string, error
335342
func (a *Agent) getRedisClientAndCache() (*redis.Client, *rediscache.Cache, error) {
336343
var tlsConfig *tls.Config = nil
337344

345+
if a.redisProxyMsgHandler.redisTLSEnabled {
346+
tlsConfig = &tls.Config{
347+
MinVersion: tls.VersionTLS12,
348+
}
349+
350+
if a.redisProxyMsgHandler.redisTLSInsecure {
351+
log().Warn("INSECURE: Not verifying Redis TLS certificate")
352+
tlsConfig.InsecureSkipVerify = true
353+
} else if a.redisProxyMsgHandler.redisTLSCAPath != "" {
354+
// Load CA certificate from file
355+
caCertPEM, err := os.ReadFile(a.redisProxyMsgHandler.redisTLSCAPath)
356+
if err != nil {
357+
return nil, nil, fmt.Errorf("failed to read CA certificate: %w", err)
358+
}
359+
360+
// Create a new cert pool and add the CA cert
361+
certPool := x509.NewCertPool()
362+
if !certPool.AppendCertsFromPEM(caCertPEM) {
363+
return nil, nil, fmt.Errorf("failed to parse CA certificate from %s", a.redisProxyMsgHandler.redisTLSCAPath)
364+
}
365+
366+
tlsConfig.RootCAs = certPool
367+
log().Debugf("Using CA certificate from %s for Redis TLS", a.redisProxyMsgHandler.redisTLSCAPath)
368+
} else {
369+
// No CA specified, will use system CAs
370+
log().Warn("Redis TLS enabled but no CA certificate specified, using system CAs. This may fail with self-signed certificates.")
371+
}
372+
}
373+
338374
opts := &redis.Options{
339375
Addr: a.redisProxyMsgHandler.redisAddress,
340376
Password: a.redisProxyMsgHandler.redisPassword,

agent/options.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,3 +107,27 @@ func WithCacheRefreshInterval(interval time.Duration) AgentOption {
107107
return nil
108108
}
109109
}
110+
111+
// WithRedisTLSEnabled enables or disables TLS for Redis connections
112+
func WithRedisTLSEnabled(enabled bool) AgentOption {
113+
return func(o *Agent) error {
114+
o.redisProxyMsgHandler.redisTLSEnabled = enabled
115+
return nil
116+
}
117+
}
118+
119+
// WithRedisTLSCAPath sets the CA certificate path for Redis TLS
120+
func WithRedisTLSCAPath(caPath string) AgentOption {
121+
return func(o *Agent) error {
122+
o.redisProxyMsgHandler.redisTLSCAPath = caPath
123+
return nil
124+
}
125+
}
126+
127+
// WithRedisTLSInsecure enables insecure Redis TLS (for testing only)
128+
func WithRedisTLSInsecure(insecure bool) AgentOption {
129+
return func(o *Agent) error {
130+
o.redisProxyMsgHandler.redisTLSInsecure = insecure
131+
return nil
132+
}
133+
}

agent/outbound_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -461,7 +461,7 @@ func Test_addClusterCacheInfoUpdateToQueue(t *testing.T) {
461461
a.emitter = event.NewEventSource("principal")
462462

463463
// First populate the cache with dummy data
464-
clusterMgr, err := cluster.NewManager(a.context, a.namespace, miniRedis.Addr(), "", cacheutil.RedisCompressionGZip, a.kubeClient.Clientset)
464+
clusterMgr, err := cluster.NewManager(a.context, a.namespace, miniRedis.Addr(), "", cacheutil.RedisCompressionGZip, a.kubeClient.Clientset, nil)
465465
require.NoError(t, err)
466466
err = clusterMgr.MapCluster("test-agent", &v1alpha1.Cluster{
467467
Name: "test-cluster",

cmd/argocd-agent/agent.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,11 @@ func NewAgentRunCommand() *cobra.Command {
7070

7171
// Time interval for agent to refresh cluster cache info in principal
7272
cacheRefreshInterval time.Duration
73+
74+
// Redis TLS configuration
75+
redisTLSEnabled bool
76+
redisTLSCAPath string
77+
redisTLSInsecure bool
7378
)
7479
command := &cobra.Command{
7580
Use: "agent",
@@ -176,6 +181,23 @@ func NewAgentRunCommand() *cobra.Command {
176181
agentOpts = append(agentOpts, agent.WithRedisUsername(redisUsername))
177182
agentOpts = append(agentOpts, agent.WithRedisPassword(redisPassword))
178183

184+
// Configure Redis TLS
185+
agentOpts = append(agentOpts, agent.WithRedisTLSEnabled(redisTLSEnabled))
186+
if redisTLSEnabled {
187+
// Validate Redis TLS configuration - only one mode allowed
188+
if redisTLSInsecure && redisTLSCAPath != "" {
189+
cmdutil.Fatal("Only one Redis TLS mode can be specified: --redis-tls-insecure or --redis-tls-ca-path")
190+
}
191+
192+
if redisTLSInsecure {
193+
logrus.Warn("INSECURE: Not verifying Redis TLS certificate")
194+
agentOpts = append(agentOpts, agent.WithRedisTLSInsecure(true))
195+
} else if redisTLSCAPath != "" {
196+
logrus.Infof("Loading Redis CA certificate from file %s", redisTLSCAPath)
197+
agentOpts = append(agentOpts, agent.WithRedisTLSCAPath(redisTLSCAPath))
198+
}
199+
}
200+
179201
agentOpts = append(agentOpts, agent.WithEnableResourceProxy(enableResourceProxy))
180202
agentOpts = append(agentOpts, agent.WithCacheRefreshInterval(cacheRefreshInterval))
181203

@@ -216,6 +238,17 @@ func NewAgentRunCommand() *cobra.Command {
216238
env.StringWithDefault("REDIS_PASSWORD", nil, ""),
217239
"The password to connect to redis with")
218240

241+
// Redis TLS flags
242+
command.Flags().BoolVar(&redisTLSEnabled, "redis-tls-enabled",
243+
env.BoolWithDefault("ARGOCD_AGENT_REDIS_TLS_ENABLED", false),
244+
"Enable TLS for Redis connections")
245+
command.Flags().StringVar(&redisTLSCAPath, "redis-tls-ca-path",
246+
env.StringWithDefault("ARGOCD_AGENT_REDIS_TLS_CA_PATH", nil, ""),
247+
"Path to CA certificate for Redis TLS")
248+
command.Flags().BoolVar(&redisTLSInsecure, "redis-tls-insecure",
249+
env.BoolWithDefault("ARGOCD_AGENT_REDIS_TLS_INSECURE", false),
250+
"INSECURE: Do not verify Redis TLS certificate")
251+
219252
command.Flags().StringVar(&logFormat, "log-format",
220253
env.StringWithDefault("ARGOCD_PRINCIPAL_LOG_FORMAT", nil, "text"),
221254
"The log format to use (one of: text, json)")

cmd/argocd-agent/principal.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,15 @@ func NewPrincipalRunCommand() *cobra.Command {
8686
redisPassword string
8787
redisCompressionType string
8888
healthzPort int
89+
90+
// Redis TLS configuration
91+
redisTLSEnabled bool
92+
redisServerTLSCertPath string
93+
redisServerTLSKeyPath string
94+
redisServerTLSSecretName string
95+
redisUpstreamTLSCAPath string
96+
redisUpstreamTLSCASecretName string
97+
redisUpstreamTLSInsecure bool
8998
)
9099
var command = &cobra.Command{
91100
Use: "principal",
@@ -246,6 +255,38 @@ func NewPrincipalRunCommand() *cobra.Command {
246255
opts = append(opts, principal.WithRedis(redisAddress, redisPassword, redisCompressionType))
247256
opts = append(opts, principal.WithHealthzPort(healthzPort))
248257

258+
// Configure Redis TLS
259+
opts = append(opts, principal.WithRedisTLSEnabled(redisTLSEnabled))
260+
if redisTLSEnabled {
261+
// Redis proxy server TLS (for incoming connections from Argo CD)
262+
if redisServerTLSCertPath != "" && redisServerTLSKeyPath != "" {
263+
logrus.Infof("Loading Redis proxy server TLS configuration from files cert=%s and key=%s", redisServerTLSCertPath, redisServerTLSKeyPath)
264+
opts = append(opts, principal.WithRedisServerTLSFromPath(redisServerTLSCertPath, redisServerTLSKeyPath))
265+
} else if (redisServerTLSCertPath != "" && redisServerTLSKeyPath == "") || (redisServerTLSCertPath == "" && redisServerTLSKeyPath != "") {
266+
cmdutil.Fatal("Both --redis-server-tls-cert and --redis-server-tls-key have to be given")
267+
} else {
268+
logrus.Infof("Loading Redis proxy server TLS certificate from secret %s/%s", namespace, redisServerTLSSecretName)
269+
opts = append(opts, principal.WithRedisServerTLSFromSecret(kubeConfig.Clientset, namespace, redisServerTLSSecretName))
270+
}
271+
272+
// Validate upstream TLS configuration - insecure and CA path are mutually exclusive
273+
if redisUpstreamTLSInsecure && redisUpstreamTLSCAPath != "" {
274+
cmdutil.Fatal("Cannot specify both --redis-upstream-tls-insecure and --redis-upstream-ca-path")
275+
}
276+
277+
// Redis upstream TLS (for connections to principal's argocd-redis)
278+
if redisUpstreamTLSInsecure {
279+
logrus.Warn("INSECURE: Not verifying upstream Redis TLS certificate")
280+
opts = append(opts, principal.WithRedisUpstreamTLSInsecure(true))
281+
} else if redisUpstreamTLSCAPath != "" {
282+
logrus.Infof("Loading Redis upstream CA certificate from file %s", redisUpstreamTLSCAPath)
283+
opts = append(opts, principal.WithRedisUpstreamTLSCAFromFile(redisUpstreamTLSCAPath))
284+
} else {
285+
logrus.Infof("Loading Redis upstream CA certificate from secret %s/%s", namespace, redisUpstreamTLSCASecretName)
286+
opts = append(opts, principal.WithRedisUpstreamTLSCAFromSecret(kubeConfig.Clientset, namespace, redisUpstreamTLSCASecretName, "tls.crt"))
287+
}
288+
}
289+
249290
s, err := principal.NewServer(ctx, kubeConfig, namespace, opts...)
250291
if err != nil {
251292
cmdutil.Fatal("Could not create new server instance: %v", err)
@@ -375,6 +416,29 @@ func NewPrincipalRunCommand() *cobra.Command {
375416
env.NumWithDefault("ARGOCD_PRINCIPAL_HEALTH_CHECK_PORT", cmdutil.ValidPort, 8003),
376417
"Port the health check server will listen on")
377418

419+
// Redis TLS flags
420+
command.Flags().BoolVar(&redisTLSEnabled, "redis-tls-enabled",
421+
env.BoolWithDefault("ARGOCD_PRINCIPAL_REDIS_TLS_ENABLED", false),
422+
"Enable TLS for Redis connections")
423+
command.Flags().StringVar(&redisServerTLSCertPath, "redis-server-tls-cert",
424+
env.StringWithDefault("ARGOCD_PRINCIPAL_REDIS_SERVER_TLS_CERT_PATH", nil, ""),
425+
"Path to TLS certificate for Redis proxy server")
426+
command.Flags().StringVar(&redisServerTLSKeyPath, "redis-server-tls-key",
427+
env.StringWithDefault("ARGOCD_PRINCIPAL_REDIS_SERVER_TLS_KEY_PATH", nil, ""),
428+
"Path to TLS private key for Redis proxy server")
429+
command.Flags().StringVar(&redisServerTLSSecretName, "redis-server-tls-secret-name",
430+
env.StringWithDefault("ARGOCD_PRINCIPAL_REDIS_SERVER_TLS_SECRET_NAME", nil, "argocd-redis-tls"),
431+
"Secret name containing TLS certificate and key for Redis proxy server")
432+
command.Flags().StringVar(&redisUpstreamTLSCAPath, "redis-upstream-ca-path",
433+
env.StringWithDefault("ARGOCD_PRINCIPAL_REDIS_UPSTREAM_CA_PATH", nil, ""),
434+
"Path to CA certificate for verifying upstream Redis TLS certificate")
435+
command.Flags().StringVar(&redisUpstreamTLSCASecretName, "redis-upstream-ca-secret-name",
436+
env.StringWithDefault("ARGOCD_PRINCIPAL_REDIS_UPSTREAM_CA_SECRET_NAME", nil, "argocd-redis-tls"),
437+
"Secret name containing CA certificate for verifying upstream Redis TLS certificate")
438+
command.Flags().BoolVar(&redisUpstreamTLSInsecure, "redis-upstream-tls-insecure",
439+
env.BoolWithDefault("ARGOCD_PRINCIPAL_REDIS_UPSTREAM_TLS_INSECURE", false),
440+
"INSECURE: Do not verify upstream Redis TLS certificate")
441+
378442
command.Flags().StringVar(&kubeConfig, "kubeconfig", "", "Path to a kubeconfig file to use")
379443
command.Flags().StringVar(&kubeContext, "kubecontext", "", "Override the default kube context")
380444

0 commit comments

Comments
 (0)