Added testing with SSL authentication using CN (#1627)

* Added testing with SSL authentication using CN

* Update CHANGELOG.md

* Codestyle changes

* Fixed broken tests

* Fixed hybdrid tests

* Fixed SSL tests

* Fixed array merging

* Updated README.md

* Updated spelling list

* Added support for Redis 8.6

* Codestyle changes and test fixes

* Fixed hybdrid tests

* Updated CHANGELOG.md and composer.json

* Fixed XINFO tests
This commit is contained in:
Vladyslav Vildanov
2026-02-04 11:18:53 +02:00
committed by GitHub
parent a24a7be9b4
commit d8a99ef674
10 changed files with 150 additions and 6 deletions
+2
View File
@@ -24,6 +24,7 @@ services:
container_name: redis-standalone
environment:
- TLS_ENABLED=yes
- TLS_CLIENT_CNS=test_user
- REDIS_CLUSTER=no
- REDIS_PASSWORD=foobar
- PORT=6379
@@ -66,6 +67,7 @@ services:
- NODES=6
- REPLICAS=1
- TLS_ENABLED=yes
- TLS_CLIENT_CNS=test_user
- PORT=6372
- TLS_PORT=27379
command: ${REDIS_EXTRA_ARGS:---enable-debug-command yes --enable-module-command yes --tls-auth-clients optional --save "" --requirepass "foobar"}
+4
View File
@@ -1,11 +1,15 @@
ACLs
AUTH
Autoloading
cafile
CAS
CN
Customizable
ElastiCache
FPM
GC
IANA
mTLS
Lua
PSR
Packagist
+1
View File
@@ -13,6 +13,7 @@
### Maintenance
- Added testing with SSL connection (#1624)
- Added testing with SSL authentication using CN (#1627)
- Added support for Redis 8.6 (#1631)
## v3.3.0 (2025-11-24)
+34
View File
@@ -114,6 +114,40 @@ The connection schemes [`redis`](http://www.iana.org/assignments/uri-schemes/pro
also supported, with the difference that URI strings containing these schemes are parsed following
the rules described on their respective IANA provisional registration documents.
Since Redis 8.6, you can authenticate a client using the Subject CN from its TLS client certificate (mTLS).
When this is enabled on the server, the client is authenticated during the TLS handshake, so you dont need
to send an AUTH command.
To use this, configure:
- a CA certificate used to verify the server certificate (cafile),
- a client certificate (local_cert) signed by a CA trusted by the Redis server for client authentication,
- the corresponding private key (local_pk).
Make sure:
- the Redis server certificate is signed by a CA trusted by the client, and
- the client certificate is signed by a CA trusted by the Redis server (mTLS).
```php
// Named array of connection parameters:
$client = new Predis\Client([
'scheme' => 'tls',
'ssl' => [
'cafile' => 'ca.pem', // CA used to verify the server certificate
'local_cert' => 'client.crt', // client certificate (Subject CN maps to ACL user)
'local_pk' => 'client.key', // client private key
'verify_peer' => true,
],
]);
// ACL user must exist and match the certificate Subject CN (example: CN=CN_NAME).
// Enable the user and grant permissions as needed:
$client->acl->setUser('CN_NAME', 'on', '>clientpass', 'allcommands', 'allkeys')
echo $client->acl->whoami() // CN_NAME
```
The actual list of supported connection parameters can vary depending on each connection backend so
it is recommended to refer to their specific documentation or implementation for details.
+5
View File
@@ -57,8 +57,13 @@
<!-- SSL -->
<env name="STANDALONE_CA_CERT_PATH" value=".github/dockers/standalone/tls/ca.crt" />
<env name="STANDALONE_LOCAL_CERT_PATH" value=".github/dockers/standalone/tls/test_user.crt" />
<env name="STANDALONE_LOCAL_PK_PATH" value=".github/dockers/standalone/tls/test_user.key" />
<env name="CN_USER_NAME" value="test_user" />
<env name="REDIS_SSL_PORT" value="6666" />
<env name="CLUSTER_CA_CERT_PATH" value=".github/dockers/cluster/tls/ca.crt" />
<env name="CLUSTER_LOCAL_CERT_PATH" value=".github/dockers/cluster/tls/test_user.crt" />
<env name="CLUSTER_LOCAL_PK_PATH" value=".github/dockers/cluster/tls/test_user.key" />
<const
name="SSL_REDIS_CLUSTER_ENDPOINTS"
value="127.0.0.1:27379?password=foobar,127.0.0.1:27380?password=foobar,127.0.0.1:27381?password=foobar"
+3
View File
@@ -189,6 +189,9 @@ abstract class ClusterStrategy implements StrategyInterface
/* cluster */
'CLUSTER' => [$this, 'getFakeKey'],
/* control */
'ACL' => [$this, 'getFakeKey'],
];
}
+8 -4
View File
@@ -286,27 +286,31 @@ abstract class PredisTestCase extends PHPUnit\Framework\TestCase
);
if ($this->isSSLTest()) {
$options = array_merge($options, [
$options = $options + [
'parameters' => [
'ssl' => [
'cafile' => getenv('CLUSTER_CA_CERT_PATH'),
'local_cert' => getenv('CLUSTER_LOCAL_CERT_PATH'),
'local_pk' => getenv('CLUSTER_LOCAL_PK_PATH'),
'verify_peer' => true,
'verify_peer_name' => false,
'allow_self_signed' => false,
],
],
]);
];
}
} else {
if ($this->isSSLTest()) {
$parameters = array_merge($parameters, [
$parameters = $parameters + [
'ssl' => [
'cafile' => getenv('STANDALONE_CA_CERT_PATH'),
'local_cert' => getenv('STANDALONE_LOCAL_CERT_PATH'),
'local_pk' => getenv('STANDALONE_LOCAL_PK_PATH'),
'verify_peer' => true,
'verify_peer_name' => false,
'allow_self_signed' => false,
],
]);
];
}
}
@@ -508,6 +508,9 @@ class PredisStrategyTest extends PredisTestCase
/* cluster */
'CLUSTER' => 'keys-fake',
/* control */
'ACL' => 'keys-fake',
];
if (isset($type)) {
@@ -531,6 +531,9 @@ class RedisStrategyTest extends PredisTestCase
/* cluster */
'CLUSTER' => 'keys-fake',
/* control */
'ACL' => 'keys-fake',
];
if (isset($type)) {
+87 -2
View File
@@ -26,7 +26,13 @@ class SSLTest extends PredisTestCase
*/
public function testExecuteCommandOverSSLConnection()
{
$redis = $this->createClient();
$redis = $this->createClient([
'ssl' => [
'cafile' => getenv('STANDALONE_CA_CERT_PATH'),
'verify_peer' => true,
'verify_peer_name' => false,
],
]);
$this->assertEquals('PONG', $redis->ping());
}
@@ -76,7 +82,16 @@ class SSLTest extends PredisTestCase
*/
public function testClusterExecuteCommandOverSSLConnection()
{
$redis = $this->createClient();
$redis = $this->createClient(null, [
'cluster' => 'redis',
'parameters' => [
'ssl' => [
'cafile' => getenv('CLUSTER_CA_CERT_PATH'),
'verify_peer' => true,
'verify_peer_name' => false,
],
],
]);
$redis->set('foo', 'bar');
$this->assertEquals('bar', $redis->get('foo'));
}
@@ -123,4 +138,74 @@ class SSLTest extends PredisTestCase
$redis->set('foo', 'bar');
}
/**
* @group connected
* @group ssl
* @group relay-incompatible
* @requiresRedisVersion >= 8.5.0
* @return void
*/
public function testAuthWithSSLCertificateWithCNSpecified()
{
$redis = $this->createClient();
$this->assertEquals(
'OK',
$redis->acl->setUser('test_user', 'on', '>clientpass', 'allcommands', 'allkeys')
);
$redis->disconnect();
// Remove AUTH
$redis = $this->createClient(['password' => null]);
$this->assertEquals(getenv('CN_USER_NAME'), $redis->acl->whoami());
$this->assertEquals(1, $redis->acl->delUser(getenv('CN_USER_NAME')));
}
/**
* @group connected
* @group ssl
* @group cluster
* @group relay-incompatible
* @requiresRedisVersion >= 8.5.0
* @return void
*/
public function testClusterAuthWithSSLCertificateWithCNSpecified()
{
$redis = $this->createClient();
$this->assertEquals(
'OK',
$redis->acl->setUser('test_user', 'on', '>clientpass', 'allcommands', 'allkeys')
);
$redis->disconnect();
// Remove AUTH
$defaultParameters = $this->getDefaultParametersArray();
$trimmedParameters = array_map(function (string $parameter) {
return explode('?', $parameter)[0];
}, $defaultParameters);
$redis = new Client(
$trimmedParameters,
[
'cluster' => 'redis',
'parameters' => [
'ssl' => [
'cafile' => getenv('CLUSTER_CA_CERT_PATH'),
'local_cert' => getenv('CLUSTER_LOCAL_CERT_PATH'),
'local_pk' => getenv('CLUSTER_LOCAL_PK_PATH'),
'verify_peer' => true,
'verify_peer_name' => false,
],
],
]
);
$this->assertEquals(getenv('CN_USER_NAME'), $redis->acl->whoami());
$this->assertEquals(1, $redis->acl->delUser(getenv('CN_USER_NAME')));
}
}