mirror of
https://github.com/predis/predis.git
synced 2026-08-17 15:23:51 +00:00
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:
committed by
GitHub
parent
a24a7be9b4
commit
d8a99ef674
@@ -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"}
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
ACLs
|
||||
AUTH
|
||||
Autoloading
|
||||
cafile
|
||||
CAS
|
||||
CN
|
||||
Customizable
|
||||
ElastiCache
|
||||
FPM
|
||||
GC
|
||||
IANA
|
||||
mTLS
|
||||
Lua
|
||||
PSR
|
||||
Packagist
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 don’t 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.
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -189,6 +189,9 @@ abstract class ClusterStrategy implements StrategyInterface
|
||||
|
||||
/* cluster */
|
||||
'CLUSTER' => [$this, 'getFakeKey'],
|
||||
|
||||
/* control */
|
||||
'ACL' => [$this, 'getFakeKey'],
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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')));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user