-
Notifications
You must be signed in to change notification settings - Fork 51
feat: redis TLS encryption enabled by default for all connections #664
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,8 +16,11 @@ package agent | |
|
|
||
| import ( | ||
| "context" | ||
| "crypto/tls" | ||
| "crypto/x509" | ||
| "fmt" | ||
| "net/http" | ||
| "os" | ||
| "sync" | ||
| "sync/atomic" | ||
| "time" | ||
|
|
@@ -139,10 +142,11 @@ type AgentOption func(*Agent) error | |
| // options. | ||
| func NewAgent(ctx context.Context, client *kube.KubernetesClient, namespace string, opts ...AgentOption) (*Agent, error) { | ||
| a := &Agent{ | ||
| version: version.New("argocd-agent"), | ||
| deletions: manager.NewDeletionTracker(), | ||
| sourceCache: cache.NewSourceCache(), | ||
| inflightLogs: make(map[string]struct{}), | ||
| version: version.New("argocd-agent"), | ||
| deletions: manager.NewDeletionTracker(), | ||
| sourceCache: cache.NewSourceCache(), | ||
| inflightLogs: make(map[string]struct{}), | ||
| cacheRefreshInterval: 30 * time.Second, // Default interval, can be overridden via WithCacheRefreshInterval | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rather than setting a default, let's just return an error if WithCacheRefreshInterval didn't set the vaue. |
||
| } | ||
| a.infStopCh = make(chan struct{}) | ||
| a.namespace = namespace | ||
|
|
@@ -321,7 +325,29 @@ func NewAgent(ctx context.Context, client *kube.KubernetesClient, namespace stri | |
| connMap: map[string]connectionEntry{}, | ||
| } | ||
|
|
||
| clusterCache, err := cluster.NewClusterCacheInstance(a.redisProxyMsgHandler.redisAddress, a.redisProxyMsgHandler.redisPassword, cacheutil.RedisCompressionGZip) | ||
| // Create TLS config for cluster cache Redis client (same as for Redis proxy) | ||
| var clusterCacheTLSConfig *tls.Config = nil | ||
| if a.redisProxyMsgHandler.redisTLSEnabled { | ||
| clusterCacheTLSConfig = &tls.Config{ | ||
| MinVersion: tls.VersionTLS12, | ||
| } | ||
| if a.redisProxyMsgHandler.redisTLSInsecure { | ||
| log().Warn("INSECURE: Not verifying Redis TLS certificate for cluster cache") | ||
| clusterCacheTLSConfig.InsecureSkipVerify = true | ||
| } else if a.redisProxyMsgHandler.redisTLSCAPath != "" { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we return an error if |
||
| caCertPEM, err := os.ReadFile(a.redisProxyMsgHandler.redisTLSCAPath) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("failed to read CA certificate for cluster cache: %w", err) | ||
| } | ||
| certPool := x509.NewCertPool() | ||
| if !certPool.AppendCertsFromPEM(caCertPEM) { | ||
| return nil, fmt.Errorf("failed to parse CA certificate for cluster cache from %s", a.redisProxyMsgHandler.redisTLSCAPath) | ||
| } | ||
| clusterCacheTLSConfig.RootCAs = certPool | ||
| } | ||
| } | ||
|
|
||
| clusterCache, err := cluster.NewClusterCacheInstance(a.redisProxyMsgHandler.redisAddress, a.redisProxyMsgHandler.redisPassword, cacheutil.RedisCompressionGZip, clusterCacheTLSConfig) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("failed to create cluster cache instance: %v", err) | ||
| } | ||
|
|
@@ -421,20 +447,22 @@ func (a *Agent) Start(ctx context.Context) error { | |
|
|
||
| // Start the background process of periodic sync of cluster cache info. | ||
| // This will send periodic updates of Application, Resource and API counts to principal. | ||
| if a.mode == types.AgentModeManaged { | ||
| go func() { | ||
| ticker := time.NewTicker(a.cacheRefreshInterval) | ||
| defer ticker.Stop() | ||
| for { | ||
| select { | ||
| case <-ticker.C: | ||
| a.addClusterCacheInfoUpdateToQueue() | ||
| case <-a.context.Done(): | ||
| return | ||
| } | ||
| // Both managed and autonomous agents need to send cluster cache info updates | ||
| go func() { | ||
| // Send initial update immediately on startup (don't wait for first ticker) | ||
| a.addClusterCacheInfoUpdateToQueue() | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this change required for/related to this PR? |
||
|
|
||
| ticker := time.NewTicker(a.cacheRefreshInterval) | ||
| defer ticker.Stop() | ||
| for { | ||
| select { | ||
| case <-ticker.C: | ||
| a.addClusterCacheInfoUpdateToQueue() | ||
| case <-a.context.Done(): | ||
| return | ||
| } | ||
| }() | ||
| } | ||
| } | ||
| }() | ||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| if a.remote != nil { | ||
| a.remote.SetClientMode(a.mode) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -70,6 +70,11 @@ func NewAgentRunCommand() *cobra.Command { | |
|
|
||
| // Time interval for agent to refresh cluster cache info in principal | ||
| cacheRefreshInterval time.Duration | ||
|
|
||
| // Redis TLS configuration | ||
| redisTLSEnabled bool | ||
| redisTLSCAPath string | ||
| redisTLSInsecure bool | ||
| ) | ||
| command := &cobra.Command{ | ||
| Use: "agent", | ||
|
|
@@ -176,6 +181,23 @@ func NewAgentRunCommand() *cobra.Command { | |
| agentOpts = append(agentOpts, agent.WithRedisUsername(redisUsername)) | ||
| agentOpts = append(agentOpts, agent.WithRedisPassword(redisPassword)) | ||
|
|
||
| // Configure Redis TLS | ||
| agentOpts = append(agentOpts, agent.WithRedisTLSEnabled(redisTLSEnabled)) | ||
| if redisTLSEnabled { | ||
| // Validate Redis TLS configuration - only one mode allowed | ||
| if redisTLSInsecure && redisTLSCAPath != "" { | ||
| cmdutil.Fatal("Only one Redis TLS mode can be specified: --redis-tls-insecure or --redis-tls-ca-path") | ||
| } | ||
|
|
||
| if redisTLSInsecure { | ||
| logrus.Warn("INSECURE: Not verifying Redis TLS certificate") | ||
| agentOpts = append(agentOpts, agent.WithRedisTLSInsecure(true)) | ||
| } else if redisTLSCAPath != "" { | ||
| logrus.Infof("Loading Redis CA certificate from file %s", redisTLSCAPath) | ||
| agentOpts = append(agentOpts, agent.WithRedisTLSCAPath(redisTLSCAPath)) | ||
| } | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| } | ||
|
|
||
| agentOpts = append(agentOpts, agent.WithEnableResourceProxy(enableResourceProxy)) | ||
| agentOpts = append(agentOpts, agent.WithCacheRefreshInterval(cacheRefreshInterval)) | ||
|
|
||
|
|
@@ -216,6 +238,17 @@ func NewAgentRunCommand() *cobra.Command { | |
| env.StringWithDefault("REDIS_PASSWORD", nil, ""), | ||
| "The password to connect to redis with") | ||
|
|
||
| // Redis TLS flags | ||
| command.Flags().BoolVar(&redisTLSEnabled, "redis-tls-enabled", | ||
| env.BoolWithDefault("ARGOCD_AGENT_REDIS_TLS_ENABLED", true), | ||
| "Enable TLS for Redis connections (enabled by default for security)") | ||
| command.Flags().StringVar(&redisTLSCAPath, "redis-tls-ca-path", | ||
| env.StringWithDefault("ARGOCD_AGENT_REDIS_TLS_CA_PATH", nil, ""), | ||
| "Path to CA certificate for Redis TLS") | ||
| command.Flags().BoolVar(&redisTLSInsecure, "redis-tls-insecure", | ||
| env.BoolWithDefault("ARGOCD_AGENT_REDIS_TLS_INSECURE", false), | ||
| "INSECURE: Do not verify Redis TLS certificate") | ||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| command.Flags().StringVar(&logFormat, "log-format", | ||
| env.StringWithDefault("ARGOCD_PRINCIPAL_LOG_FORMAT", nil, "text"), | ||
| "The log format to use (one of: text, json)") | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we move this code into its own shell script, for example
./hack/dev-env/setup-e2e-enable-redis-tls.sh(to make up a random name)