Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,11 @@ REDIS_HOST=127.0.0.1
REDIS_PORT=6379
REDIS_PASSWORD=difyai123456
REDIS_DB=0
REDIS_USE_SSL=false
# SSL configuration for Redis (when REDIS_USE_SSL=true)
REDIS_SSL_CERT_REQS=CERT_NONE
# Options: CERT_NONE, CERT_OPTIONAL, CERT_REQUIRED
REDIS_SSL_CA_CERTS=

# Whether to use Redis Sentinel mode.
# If set to true, the application will automatically discover and connect to the master node through Sentinel.
Expand All @@ -98,8 +103,8 @@ DB_PASSWORD=difyai123456
DB_HOST=localhost
DB_PORT=5432
DB_DATABASE=dify_plugin
# Specifies the SSL mode for the database connection.
# Possible values include 'disable', 'require', 'verify-ca', and 'verify-full'.
# Specifies the SSL mode for the database connection.
# Possible values include 'disable', 'require', 'verify-ca', and 'verify-full'.
# 'disable' means SSL is not used for the connection.
DB_SSL_MODE=disable
# database connection pool settings
Expand Down
8 changes: 8 additions & 0 deletions internal/core/plugin_manager/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,12 @@ func (p *PluginManager) GetAsset(id string) ([]byte, error) {
func (p *PluginManager) Launch(configuration *app.Config) {
log.Info("start plugin manager daemon")

// Build TLS config for Redis (nil when RedisUseSsl=false)
tlsConf, err := configuration.RedisTLSConfig()
if err != nil {
log.Panic("invalid Redis TLS config: %s", err.Error())
}

// init redis client
if configuration.RedisUseSentinel {
// use Redis Sentinel
Expand All @@ -116,6 +122,7 @@ func (p *PluginManager) Launch(configuration *app.Config) {
configuration.RedisUseSsl,
configuration.RedisDB,
configuration.RedisSentinelSocketTimeout,
tlsConf, // pass TLS to cache initializer
); err != nil {
log.Panic("init redis sentinel client failed", "error", err)
}
Expand All @@ -126,6 +133,7 @@ func (p *PluginManager) Launch(configuration *app.Config) {
configuration.RedisPass,
configuration.RedisUseSsl,
configuration.RedisDB,
tlsConf, // pass TLS to cache initializer
); err != nil {
log.Panic("init redis client failed", "error", err)
}
Expand Down
79 changes: 72 additions & 7 deletions internal/types/app/config.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package app

import (
"crypto/tls"
"crypto/x509"
"fmt"
"os"
"strings"

"github.com/go-playground/validator/v10"
"github.com/langgenius/dify-plugin-daemon/pkg/entities/plugin_entities"
Expand Down Expand Up @@ -68,7 +72,7 @@ type Config struct {
HuaweiOBSAccessKey string `envconfig:"HUAWEI_OBS_ACCESS_KEY"`
HuaweiOBSSecretKey string `envconfig:"HUAWEI_OBS_SECRET_KEY"`
HuaweiOBSServer string `envconfig:"HUAWEI_OBS_SERVER"`
HuaweiOBSPathStyle bool `envconfig:"HUAWEI_OBS_PATH_STYLE" default:"false"`
HuaweiOBSPathStyle bool `envconfig:"HUAWEI_OBS_PATH_STYLE" default:"false"`

// volcengine tos
VolcengineTOSEndpoint string `envconfig:"VOLCENGINE_TOS_ENDPOINT"`
Expand Down Expand Up @@ -109,12 +113,14 @@ type Config struct {
RoutinePoolSize int `envconfig:"ROUTINE_POOL_SIZE" validate:"required"`

// redis
RedisHost string `envconfig:"REDIS_HOST"`
RedisPort uint16 `envconfig:"REDIS_PORT"`
RedisPass string `envconfig:"REDIS_PASSWORD"`
RedisUser string `envconfig:"REDIS_USERNAME"`
RedisUseSsl bool `envconfig:"REDIS_USE_SSL"`
RedisDB int `envconfig:"REDIS_DB"`
RedisHost string `envconfig:"REDIS_HOST"`
RedisPort uint16 `envconfig:"REDIS_PORT"`
RedisPass string `envconfig:"REDIS_PASSWORD"`
RedisUser string `envconfig:"REDIS_USERNAME"`
RedisDB int `envconfig:"REDIS_DB"`
RedisUseSsl bool `envconfig:"REDIS_USE_SSL"`
RedisSSLCertReqs string `envconfig:"REDIS_SSL_CERT_REQS"`
RedisSSLCACerts string `envconfig:"REDIS_SSL_CA_CERTS"`

// redis sentinel
RedisUseSentinel bool `envconfig:"REDIS_USE_SENTINEL"`
Expand Down Expand Up @@ -281,6 +287,65 @@ func (c *Config) GetLocalRuntimeMaxBufferSize() int {
return c.PluginRuntimeMaxBufferSize
}

// RedisTLSConfig builds a *tls.Config for Redis based on envs.
func (c *Config) RedisTLSConfig() (*tls.Config, error) {
if !c.RedisUseSsl {
return nil, nil
}

tlsConf := &tls.Config{
MinVersion: tls.VersionTLS12,
}

// Load custom CA certificates if provided
if strings.TrimSpace(c.RedisSSLCACerts) != "" {
pem, err := os.ReadFile(c.RedisSSLCACerts)
if err != nil {
return nil, fmt.Errorf("read REDIS_SSL_CA_CERTS: %w", err)
}
pool := x509.NewCertPool()
if !pool.AppendCertsFromPEM(pem) {
return nil, fmt.Errorf("failed to append CA certs from %s", c.RedisSSLCACerts)
}
tlsConf.RootCAs = pool
}

// Configure certificate verification based on REDIS_SSL_CERT_REQS
certReqs := strings.ToUpper(strings.TrimSpace(c.RedisSSLCertReqs))
switch certReqs {
case "CERT_NONE":
// Skip all certificate verification (insecure)
tlsConf.InsecureSkipVerify = true
case "CERT_OPTIONAL":
// Accept the connection whether or not a certificate is provided
// This is truly "optional" - we verify if a cert is provided, but don't require it
tlsConf.InsecureSkipVerify = true
tlsConf.VerifyConnection = func(cs tls.ConnectionState) error {
// If the server provides certificates, verify them
if len(cs.PeerCertificates) > 0 {
opts := x509.VerifyOptions{
Roots: tlsConf.RootCAs,
Intermediates: x509.NewCertPool(),
}
for _, cert := range cs.PeerCertificates[1:] {
opts.Intermediates.AddCert(cert)
}
_, err := cs.PeerCertificates[0].Verify(opts)
return err
}
return nil
}
case "CERT_REQUIRED", "":
// Require valid certificate verification (default and most secure)
tlsConf.InsecureSkipVerify = false
default:
// Invalid value - return an error instead of silently defaulting
return nil, fmt.Errorf("invalid REDIS_SSL_CERT_REQS value: %s (valid options: CERT_NONE, CERT_OPTIONAL, CERT_REQUIRED)", certReqs)
}

return tlsConf, nil
}

type PlatformType string

const (
Expand Down
35 changes: 27 additions & 8 deletions pkg/utils/cache/redis.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,26 @@ var (
ErrNotFound = errors.New("key not found")
)

func getRedisOptions(addr, username, password string, useSsl bool, db int) *redis.Options {
func getRedisOptions(addr, username, password string, useSsl bool, db int, tlsConf *tls.Config) *redis.Options {
opts := &redis.Options{
Addr: addr,
Username: username,
Password: password,
DB: db,
}
if useSsl {
opts.TLSConfig = &tls.Config{}
// Use provided TLS config (encodes CERT_NONE / REQUIRED policy). If nil, default to system roots.
if tlsConf != nil {
opts.TLSConfig = tlsConf
} else {
opts.TLSConfig = &tls.Config{}
}
}
return opts
}

func InitRedisClient(addr, username, password string, useSsl bool, db int) error {
opts := getRedisOptions(addr, username, password, useSsl, db)
func InitRedisClient(addr, username, password string, useSsl bool, db int, tlsConf *tls.Config) error {
opts := getRedisOptions(addr, username, password, useSsl, db, tlsConf)
client = redis.NewClient(opts)

if _, err := client.Ping(ctx).Result(); err != nil {
Expand All @@ -45,7 +50,14 @@ func InitRedisClient(addr, username, password string, useSsl bool, db int) error
return nil
}

func InitRedisSentinelClient(sentinels []string, masterName, username, password, sentinelUsername, sentinelPassword string, useSsl bool, db int, socketTimeout float64) error {
func InitRedisSentinelClient(
sentinels []string,
masterName, username, password, sentinelUsername, sentinelPassword string,
useSsl bool,
db int,
socketTimeout float64,
tlsConf *tls.Config,
) error {
opts := &redis.FailoverOptions{
MasterName: masterName,
SentinelAddrs: sentinels,
Expand All @@ -57,7 +69,12 @@ func InitRedisSentinelClient(sentinels []string, masterName, username, password,
}

if useSsl {
opts.TLSConfig = &tls.Config{}
// go-redis v9 uses TLSConfig for both Sentinel discovery and data connections
if tlsConf != nil {
opts.TLSConfig = tlsConf
} else {
opts.TLSConfig = &tls.Config{}
}
}

if socketTimeout > 0 {
Expand Down Expand Up @@ -366,13 +383,15 @@ func ScanMap[V any](key string, match string, context ...redis.Cmdable) (map[str

result := make(map[string]V)

ScanMapAsync[V](key, match, func(m map[string]V) error {
if err := ScanMapAsync[V](key, match, func(m map[string]V) error {
for k, v := range m {
result[k] = v
}

return nil
})
}, context...); err != nil {
return nil, err
}

return result, nil
}
Expand Down