From d6af2907fd2dca5a6751d7d42090dd7ebb8ccd48 Mon Sep 17 00:00:00 2001 From: Yaroslav Date: Thu, 19 Feb 2026 01:11:05 +0100 Subject: [PATCH] feat(sources/redis): add TLS support for Redis connections (#2432) ## Summary - Add `tlsEnabled` config field to Redis source for enabling TLS on connections - Apply TLS config to both cluster and standalone Redis clients - Add test case for TLS config parsing and update docs This is needed for cloud-managed Redis services like AWS ElastiCache (Redis OSS) that require TLS for secure connections. ## Example config (tools.yaml) ```yaml sources: leadsforge-redis: kind: redis address: - ${REDIS_HOST} clusterEnabled: true tls: enabled: true insecureSkipVerify: true ``` --------- Co-authored-by: Yuan Teoh <45984206+Yuan325@users.noreply.github.com> --- docs/en/resources/sources/redis.md | 31 ++++++++++++++++----------- internal/sources/redis/redis.go | 32 +++++++++++++++++++++------- internal/sources/redis/redis_test.go | 7 ++++++ 3 files changed, 49 insertions(+), 21 deletions(-) diff --git a/docs/en/resources/sources/redis.md b/docs/en/resources/sources/redis.md index 51c8cfde00e..62ca36be704 100644 --- a/docs/en/resources/sources/redis.md +++ b/docs/en/resources/sources/redis.md @@ -4,8 +4,8 @@ linkTitle: "Redis" type: docs weight: 1 description: > - Redis is a in-memory data structure store. - + Redis is a in-memory data structure store. + --- ## About @@ -44,6 +44,9 @@ password: ${MY_AUTH_STRING} # Omit this field if you don't have a password. # database: 0 # clusterEnabled: false # useGCPIAM: false +# tls: +# enabled: false +# insecureSkipVerify: false ``` {{< notice tip >}} @@ -61,7 +64,7 @@ Here is an example tools.yaml config with [AUTH][auth] enabled: ```yaml kind: sources name: my-redis-cluster-instance -type: memorystore-redis +type: redis address: - 127.0.0.1:6379 password: ${MY_AUTH_STRING} @@ -78,7 +81,7 @@ using IAM authentication: ```yaml kind: sources name: my-redis-cluster-instance -type: memorystore-redis +type: redis address: - 127.0.0.1:6379 useGCPIAM: true @@ -89,14 +92,16 @@ clusterEnabled: true ## Reference -| **field** | **type** | **required** | **description** | -|----------------|:--------:|:------------:|---------------------------------------------------------------------------------------------------------------------------------| -| type | string | true | Must be "memorystore-redis". | -| address | string | true | Primary endpoint for the Memorystore Redis instance to connect to. | -| username | string | false | If you are using a non-default user, specify the user name here. If you are using Memorystore for Redis, leave this field blank | -| password | string | false | If you have [Redis AUTH][auth] enabled, specify the AUTH string here | -| database | int | false | The Redis database to connect to. Not applicable for cluster enabled instances. The default database is `0`. | -| clusterEnabled | bool | false | Set it to `true` if using a Redis Cluster instance. Defaults to `false`. | -| useGCPIAM | string | false | Set it to `true` if you are using GCP's IAM authentication. Defaults to `false`. | +| **field** | **type** | **required** | **description** | +|------------------------|:--------:|:------------:|-----------------------------------------------------------------------------------------------------------------------------------------------| +| type | string | true | Must be "redis". | +| address | string | true | Primary endpoint for the Memorystore Redis instance to connect to. | +| username | string | false | If you are using a non-default user, specify the user name here. If you are using Memorystore for Redis, leave this field blank | +| password | string | false | If you have [Redis AUTH][auth] enabled, specify the AUTH string here | +| database | int | false | The Redis database to connect to. Not applicable for cluster enabled instances. The default database is `0`. | +| tls.enabled | bool | false | Set it to `true` to enable TLS for the Redis connection. Defaults to `false`. | +| tls.insecureSkipVerify | bool | false | Set it to `true` to skip TLS certificate verification. **Warning:** This is insecure and not recommended for production. Defaults to `false`. | +| clusterEnabled | bool | false | Set it to `true` if using a Redis Cluster instance. Defaults to `false`. | +| useGCPIAM | bool | false | Set it to `true` if you are using GCP's IAM authentication. Defaults to `false`. | [auth]: https://cloud.google.com/memorystore/docs/redis/about-redis-auth diff --git a/internal/sources/redis/redis.go b/internal/sources/redis/redis.go index e2a28c52a7e..2a0b4f81a44 100644 --- a/internal/sources/redis/redis.go +++ b/internal/sources/redis/redis.go @@ -15,6 +15,7 @@ package redis import ( "context" + "crypto/tls" "fmt" "time" @@ -44,14 +45,20 @@ func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (sources } type Config struct { - Name string `yaml:"name" validate:"required"` - Type string `yaml:"type" validate:"required"` - Address []string `yaml:"address" validate:"required"` - Username string `yaml:"username"` - Password string `yaml:"password"` - Database int `yaml:"database"` - UseGCPIAM bool `yaml:"useGCPIAM"` - ClusterEnabled bool `yaml:"clusterEnabled"` + Name string `yaml:"name" validate:"required"` + Type string `yaml:"type" validate:"required"` + Address []string `yaml:"address" validate:"required"` + Username string `yaml:"username"` + Password string `yaml:"password"` + Database int `yaml:"database"` + UseGCPIAM bool `yaml:"useGCPIAM"` + ClusterEnabled bool `yaml:"clusterEnabled"` + TLS TLSConfig `yaml:"tls"` +} + +type TLSConfig struct { + Enabled bool `yaml:"enabled"` + InsecureSkipVerify bool `yaml:"insecureSkipVerify"` } func (r Config) SourceConfigType() string { @@ -91,6 +98,13 @@ func initRedisClient(ctx context.Context, r Config) (RedisClient, error) { } } + var tlsConfig *tls.Config + if r.TLS.Enabled { + tlsConfig = &tls.Config{ + InsecureSkipVerify: r.TLS.InsecureSkipVerify, + } + } + var client RedisClient var err error if r.ClusterEnabled { @@ -104,6 +118,7 @@ func initRedisClient(ctx context.Context, r Config) (RedisClient, error) { CredentialsProviderContext: authFn, Username: r.Username, Password: r.Password, + TLSConfig: tlsConfig, }) err = clusterClient.ForEachShard(ctx, func(ctx context.Context, shard *redis.Client) error { return shard.Ping(ctx).Err() @@ -125,6 +140,7 @@ func initRedisClient(ctx context.Context, r Config) (RedisClient, error) { CredentialsProviderContext: authFn, Username: r.Username, Password: r.Password, + TLSConfig: tlsConfig, }) _, err = standaloneClient.Ping(ctx).Result() if err != nil { diff --git a/internal/sources/redis/redis_test.go b/internal/sources/redis/redis_test.go index 63e2d01beec..ce44c53afbf 100644 --- a/internal/sources/redis/redis_test.go +++ b/internal/sources/redis/redis_test.go @@ -63,6 +63,9 @@ func TestParseFromYamlRedis(t *testing.T) { database: 1 useGCPIAM: true clusterEnabled: true + tls: + enabled: true + insecureSkipVerify: true `, want: map[string]sources.SourceConfig{ "my-redis-instance": redis.Config{ @@ -73,6 +76,10 @@ func TestParseFromYamlRedis(t *testing.T) { Database: 1, ClusterEnabled: true, UseGCPIAM: true, + TLS: redis.TLSConfig{ + Enabled: true, + InsecureSkipVerify: true, + }, }, }, },