bug #40882 [Cache] phpredis: Added full TLS support for RedisCluster (jackthomasatl)

This PR was squashed before being merged into the 4.4 branch.

Discussion
----------

[Cache] phpredis: Added full TLS support for RedisCluster

| Q             | A
| ------------- | ---
| Branch?       | 4.4
| Bug fix?      | yes
| New feature?  | no
| Deprecations? | no
| Tickets       | -
| License       | MIT
| Doc PR        | n/a

This Pr bridges the gap for full TLS support when using phpredis driver implementation of TLS.

Adds the 'ssl' options array for cache configuration when using RedisCluster
https://www.php.net/manual/en/context.ssl.php

Switches directed node commands from using individual \Redis connections to using the recommended implementation from the phpredis documentation:
https://github.com/phpredis/phpredis/blob/develop/cluster.markdown#directed-node-commands

This pr will enable compatibility with Amazon ElastiCache redis cluster mode using In Transit encryption (TLS) using the phpredis driver, Supports tagging & binary data types.

Commits
-------

a1e0408d08 [Cache] phpredis: Added full TLS support for RedisCluster
This commit is contained in:
Nicolas Grekas 2021-04-23 09:10:02 +02:00
commit f8518ca16a
2 changed files with 53 additions and 5 deletions

View File

@ -0,0 +1,48 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Cache\Traits;
/**
* This file acts as a wrapper to the \RedisCluster implementation so it can accept the same type of calls as
* individual \Redis objects.
*
* Calls are made to individual nodes via: RedisCluster->{method}($host, ...args)'
* according to https://github.com/phpredis/phpredis/blob/develop/cluster.markdown#directed-node-commands
*
* @author Jack Thomas <jack.thomas@solidalpha.com>
*
* @internal
*/
class RedisClusterNodeProxy
{
private $host;
private $redis;
/**
* @param \RedisCluster|RedisClusterProxy $redis
*/
public function __construct(array $host, $redis)
{
$this->host = $host;
$this->redis = $redis;
}
public function __call(string $method, array $args)
{
return $this->redis->{$method}($this->host, ...$args);
}
public function scan(&$iIterator, $strPattern = null, $iCount = null)
{
return $this->redis->scan($iIterator, $this->host, $strPattern, $iCount);
}
}

View File

@ -41,6 +41,7 @@ trait RedisTrait
'redis_sentinel' => null,
'dbindex' => 0,
'failover' => 'none',
'ssl' => null, // see https://php.net/context.ssl
];
private $redis;
private $marshaller;
@ -187,7 +188,7 @@ trait RedisTrait
}
try {
@$redis->{$connect}($host, $port, $params['timeout'], (string) $params['persistent_id'], $params['retry_interval'], $params['read_timeout']);
@$redis->{$connect}($host, $port, $params['timeout'], (string) $params['persistent_id'], $params['retry_interval'], $params['read_timeout'], ['stream' => $params['ssl'] ?? null]);
set_error_handler(function ($type, $msg) use (&$error) { $error = $msg; });
$isConnected = $redis->isConnected();
@ -250,7 +251,7 @@ trait RedisTrait
}
try {
$redis = new $class(null, $hosts, $params['timeout'], $params['read_timeout'], (bool) $params['persistent'], $params['auth'] ?? '');
$redis = new $class(null, $hosts, $params['timeout'], $params['read_timeout'], (bool) $params['persistent'], $params['auth'] ?? '', $params['ssl'] ?? null);
} catch (\RedisClusterException $e) {
throw new InvalidArgumentException(sprintf('Redis connection "%s" failed: ', $dsn).$e->getMessage());
}
@ -299,7 +300,7 @@ trait RedisTrait
}
$params['exceptions'] = false;
$redis = new $class($hosts, array_diff_key($params, self::$defaultConnectionOptions));
$redis = new $class($hosts, array_diff_key($params, array_diff_key(self::$defaultConnectionOptions, ['ssl' => null])));
if (isset($params['redis_sentinel'])) {
$redis->getConnection()->setSentinelTimeout($params['timeout']);
}
@ -530,8 +531,7 @@ trait RedisTrait
} elseif ($this->redis instanceof RedisClusterProxy || $this->redis instanceof \RedisCluster) {
$hosts = [];
foreach ($this->redis->_masters() as $host) {
$hosts[] = $h = new \Redis();
$h->connect($host[0], $host[1]);
$hosts[] = new RedisClusterNodeProxy($host, $this->redis);
}
}