Redis TLS Configuration in Sugar 25.1.x?

We currently have the following Redis configuration:

$sugar_config['external_cache_disabled_redis'] = false;
$sugar_config['external_cache_disabled'] = false;
$sugar_config['external_cache']['redis']['host'] = 'redis-***.redis.cache.windows.net';
$sugar_config['external_cache']['redis']['password'] = '******';
$sugar_config['external_cache']['redis']['timeout'] = 2;
$sugar_config['external_cache']['redis']['persistent'] = true;
$sugar_config['cache']['backend'] = 'Sugarcrm\\Sugarcrm\\Cache\\Backend\\Redis';


Can the Redis host be updated to use tls:// (or rediss://) and the port changed to 6380?
We are looking for the exact steps required to enable and validate TLS for this configuration. The environment is using phpredis 6.0.2.

Specifically, please confirm:
• Whether Sugar supports specifying a TLS scheme in the Redis host value
• Any additional Redis or PHP configuration required to enable TLS
• Certificate or CA requirements, if applicable
• Any Sugar-specific changes needed beyond the Redis connection settings

Parents
  • Hi  ,

    TLS is not supported out of the box. That said, there are a few alternative options available. Although we haven’t tested them ourselves, the guidance below should be helpful.

    Alternative 1: Override Redis Service via after_entry_point Logic Hook
    Approach: Use the after_entry_point application hook to override the Redis::class service definition in the container before cache is used.
    Files to create:

    • custom/Extension/application/Ext/LogicHooks/RedisTlsHook.php
    • custom/include/RedisTlsOverride.php
    Implementation:
    // custom/Extension/application/Ext/LogicHooks/RedisTlsHook.php
    <?php
    $hook_array['after_entry_point'][] = [
        0, // Priority - run first
        'Redis TLS Override',
        'custom/include/RedisTlsOverride.php',
        'RedisTlsOverride',
        'overrideRedisService',
    ];
    
    // custom/include/RedisTlsOverride.php
    <?php
    
    use Sugarcrm\Sugarcrm\DependencyInjection\Container;
    use Sugarcrm\Sugarcrm\Cache\Exception as SugarCacheException;
    use Psr\Container\ContainerInterface;
    
    class RedisTlsOverride
    {
        public function overrideRedisService()
        {
            $container = Container::getInstance();
    
            // Override the Redis service with TLS support
            $container->set(\Redis::class, function (ContainerInterface $container): \Redis {
                if (!extension_loaded('redis')) {
                    throw new SugarCacheException('Redis extension is not loaded');
                }
    
                $client = new \Redis();
                $config = $container->get(\SugarConfig::class)->get('external_cache.redis');
                $persistentId = $container->get(\SugarConfig::class)->get('unique_key');
    
                $host = $config['host'] ?? '127.0.0.1';
                $port = $config['port'] ?? 6379;
                $timeout = $config['timeout'] ?? 0;
                $persistent = $config['persistent'] ?? false;
                $retryInterval = $config['retry_interval'] ?? 0;
                $readTimeout = $config['read_timeout'] ?? 0;
    
                // Build context array for TLS/SSL stream options
                $context = [];
                if (isset($config['stream'])) {
                    $context['stream'] = $config['stream'];
                }
    
                try {
                    if ($persistent) {
                        $client->pconnect(
                            $host,
                            $port,
                            $timeout,
                            $persistentId ?? '',
                            $retryInterval,
                            $readTimeout,
                            $context
                        );
                    } else {
                        $client->connect(
                            $host,
                            $port,
                            $timeout,
                            '',
                            $retryInterval,
                            $readTimeout,
                            $context
                        );
                    }
                } catch (\RedisException $e) {
                    throw new SugarCacheException($e->getMessage(), 0, $e);
                }
    
                return $client;
            });
        }
    }
    Config in config_override.php:
    $sugar_config['external_cache']['redis'] = [
        'host' => 'tls://redis.example.com',
        'port' => 6380,
        'timeout' => 2.5,
        'stream' => [
            'verify_peer' => true,
            'verify_peer_name' => true,
            'cafile' => '/path/to/ca-bundle.crt',
        ],
    ];
    Pros:
    • Uses standard SugarCRM extension framework
    • No core file modifications
    • Survives upgrades
    Cons:
    • after_entry_point runs after some initialization - cache may already be accessed
    • Container service may already be instantiated
    Alternative 2: Custom Container Configuration File
    Approach: Create a custom container configuration file that loads after the core container and overrides the Redis service.
    Files to create:
    • custom/etc/container.php (custom container definitions)
    • custom/include/SugarContainerLoader.php (loader class)
    • custom/Extension/application/Ext/LogicHooks/ContainerLoader.php
    Implementation:
    // custom/etc/container.php
    <?php
    
    use Sugarcrm\Sugarcrm\Cache\Exception as SugarCacheException;
    use Psr\Container\ContainerInterface;
    
    return [
        \Redis::class => function (ContainerInterface $container): \Redis {
            if (!extension_loaded('redis')) {
                throw new SugarCacheException('Redis extension is not loaded');
            }
    
            $client = new \Redis();
            $config = $container->get(\SugarConfig::class)->get('external_cache.redis');
            $persistentId = $container->get(\SugarConfig::class)->get('unique_key');
    
            $host = $config['host'] ?? '127.0.0.1';
            $port = $config['port'] ?? 6379;
            $timeout = $config['timeout'] ?? 0;
            $persistent = $config['persistent'] ?? false;
            $retryInterval = $config['retry_interval'] ?? 0;
            $readTimeout = $config['read_timeout'] ?? 0;
    
            // Build context for TLS
            $context = [];
            if (isset($config['stream'])) {
                $context['stream'] = $config['stream'];
            }
    
            try {
                if ($persistent) {
                    $client->pconnect(
                        $host, $port, $timeout,
                        $persistentId ?? '', $retryInterval, $readTimeout, $context
                    );
                } else {
                    $client->connect(
                        $host, $port, $timeout,
                        '', $retryInterval, $readTimeout, $context
                    );
                }
            } catch (\RedisException $e) {
                throw new SugarCacheException($e->getMessage(), 0, $e);
            }
    
            return $client;
        },
    ];
    // custom/include/SugarContainerLoader.php
    <?php
    
    class SugarContainerLoader
    {
        public function loadCustomContainer()
        {
            $customContainerPath = 'custom/etc/container.php';
            if (file_exists($customContainerPath)) {
                $container = \Sugarcrm\Sugarcrm\DependencyInjection\Container::getInstance();
                $container->configureFromFile($customContainerPath);
            }
        }
    }
    // custom/Extension/application/Ext/LogicHooks/ContainerLoader.php
    <?php
    $hook_array['after_entry_point'][] = [
        0,
        'Load Custom Container',
        'custom/include/SugarContainerLoader.php',
        'SugarContainerLoader',
        'loadCustomContainer',
    ];


    Pros:
    • Clean separation of custom container definitions
    • Uses UltraLite Container's built-in configureFromFile() method
    • Easy to manage multiple service overrides
    Cons:
    • Still uses after_entry_point hook (timing issue)
    • Custom container file needs maintenance across upgrades

    Rafael Fernandes

    Staff Developer Advocate | SugarCRM

Reply
  • Hi  ,

    TLS is not supported out of the box. That said, there are a few alternative options available. Although we haven’t tested them ourselves, the guidance below should be helpful.

    Alternative 1: Override Redis Service via after_entry_point Logic Hook
    Approach: Use the after_entry_point application hook to override the Redis::class service definition in the container before cache is used.
    Files to create:

    • custom/Extension/application/Ext/LogicHooks/RedisTlsHook.php
    • custom/include/RedisTlsOverride.php
    Implementation:
    // custom/Extension/application/Ext/LogicHooks/RedisTlsHook.php
    <?php
    $hook_array['after_entry_point'][] = [
        0, // Priority - run first
        'Redis TLS Override',
        'custom/include/RedisTlsOverride.php',
        'RedisTlsOverride',
        'overrideRedisService',
    ];
    
    // custom/include/RedisTlsOverride.php
    <?php
    
    use Sugarcrm\Sugarcrm\DependencyInjection\Container;
    use Sugarcrm\Sugarcrm\Cache\Exception as SugarCacheException;
    use Psr\Container\ContainerInterface;
    
    class RedisTlsOverride
    {
        public function overrideRedisService()
        {
            $container = Container::getInstance();
    
            // Override the Redis service with TLS support
            $container->set(\Redis::class, function (ContainerInterface $container): \Redis {
                if (!extension_loaded('redis')) {
                    throw new SugarCacheException('Redis extension is not loaded');
                }
    
                $client = new \Redis();
                $config = $container->get(\SugarConfig::class)->get('external_cache.redis');
                $persistentId = $container->get(\SugarConfig::class)->get('unique_key');
    
                $host = $config['host'] ?? '127.0.0.1';
                $port = $config['port'] ?? 6379;
                $timeout = $config['timeout'] ?? 0;
                $persistent = $config['persistent'] ?? false;
                $retryInterval = $config['retry_interval'] ?? 0;
                $readTimeout = $config['read_timeout'] ?? 0;
    
                // Build context array for TLS/SSL stream options
                $context = [];
                if (isset($config['stream'])) {
                    $context['stream'] = $config['stream'];
                }
    
                try {
                    if ($persistent) {
                        $client->pconnect(
                            $host,
                            $port,
                            $timeout,
                            $persistentId ?? '',
                            $retryInterval,
                            $readTimeout,
                            $context
                        );
                    } else {
                        $client->connect(
                            $host,
                            $port,
                            $timeout,
                            '',
                            $retryInterval,
                            $readTimeout,
                            $context
                        );
                    }
                } catch (\RedisException $e) {
                    throw new SugarCacheException($e->getMessage(), 0, $e);
                }
    
                return $client;
            });
        }
    }
    Config in config_override.php:
    $sugar_config['external_cache']['redis'] = [
        'host' => 'tls://redis.example.com',
        'port' => 6380,
        'timeout' => 2.5,
        'stream' => [
            'verify_peer' => true,
            'verify_peer_name' => true,
            'cafile' => '/path/to/ca-bundle.crt',
        ],
    ];
    Pros:
    • Uses standard SugarCRM extension framework
    • No core file modifications
    • Survives upgrades
    Cons:
    • after_entry_point runs after some initialization - cache may already be accessed
    • Container service may already be instantiated
    Alternative 2: Custom Container Configuration File
    Approach: Create a custom container configuration file that loads after the core container and overrides the Redis service.
    Files to create:
    • custom/etc/container.php (custom container definitions)
    • custom/include/SugarContainerLoader.php (loader class)
    • custom/Extension/application/Ext/LogicHooks/ContainerLoader.php
    Implementation:
    // custom/etc/container.php
    <?php
    
    use Sugarcrm\Sugarcrm\Cache\Exception as SugarCacheException;
    use Psr\Container\ContainerInterface;
    
    return [
        \Redis::class => function (ContainerInterface $container): \Redis {
            if (!extension_loaded('redis')) {
                throw new SugarCacheException('Redis extension is not loaded');
            }
    
            $client = new \Redis();
            $config = $container->get(\SugarConfig::class)->get('external_cache.redis');
            $persistentId = $container->get(\SugarConfig::class)->get('unique_key');
    
            $host = $config['host'] ?? '127.0.0.1';
            $port = $config['port'] ?? 6379;
            $timeout = $config['timeout'] ?? 0;
            $persistent = $config['persistent'] ?? false;
            $retryInterval = $config['retry_interval'] ?? 0;
            $readTimeout = $config['read_timeout'] ?? 0;
    
            // Build context for TLS
            $context = [];
            if (isset($config['stream'])) {
                $context['stream'] = $config['stream'];
            }
    
            try {
                if ($persistent) {
                    $client->pconnect(
                        $host, $port, $timeout,
                        $persistentId ?? '', $retryInterval, $readTimeout, $context
                    );
                } else {
                    $client->connect(
                        $host, $port, $timeout,
                        '', $retryInterval, $readTimeout, $context
                    );
                }
            } catch (\RedisException $e) {
                throw new SugarCacheException($e->getMessage(), 0, $e);
            }
    
            return $client;
        },
    ];
    // custom/include/SugarContainerLoader.php
    <?php
    
    class SugarContainerLoader
    {
        public function loadCustomContainer()
        {
            $customContainerPath = 'custom/etc/container.php';
            if (file_exists($customContainerPath)) {
                $container = \Sugarcrm\Sugarcrm\DependencyInjection\Container::getInstance();
                $container->configureFromFile($customContainerPath);
            }
        }
    }
    // custom/Extension/application/Ext/LogicHooks/ContainerLoader.php
    <?php
    $hook_array['after_entry_point'][] = [
        0,
        'Load Custom Container',
        'custom/include/SugarContainerLoader.php',
        'SugarContainerLoader',
        'loadCustomContainer',
    ];


    Pros:
    • Clean separation of custom container definitions
    • Uses UltraLite Container's built-in configureFromFile() method
    • Easy to manage multiple service overrides
    Cons:
    • Still uses after_entry_point hook (timing issue)
    • Custom container file needs maintenance across upgrades

    Rafael Fernandes

    Staff Developer Advocate | SugarCRM

Children
No Data