Skip to content
Snippets Groups Projects
Unverified Commit 6da74d0e authored by Fengtan 冯坦's avatar Fengtan 冯坦 Committed by GitHub
Browse files

Add support for Redis-Sentinel (#1699)


* Add support for Redis-Sentinel.

* Add tests for redis sentinel store.

* Documentation about Redis-Sentinel and password protection.

* Fix markdown syntax brought up by github actions.

* Add missing config parameter to unit tests.

* Add missing return-type

* Fix indentation

* Fix incorrect configuration-methods

Co-authored-by: default avatarTim van Dijen <tvdijen@gmail.com>
parent 7e825424
No related branches found
No related tags found
No related merge requests found
...@@ -1221,4 +1221,21 @@ $config = [ ...@@ -1221,4 +1221,21 @@ $config = [
* The prefix we should use on our Redis datastore. * The prefix we should use on our Redis datastore.
*/ */
'store.redis.prefix' => 'SimpleSAMLphp', 'store.redis.prefix' => 'SimpleSAMLphp',
/*
* The master group to use for Redis Sentinel.
*/
'store.redis.mastergroup' => 'mymaster',
/*
* The Redis Sentinel hosts.
* Example:
* array(
* 'tcp://[yoursentinel1]:[port]'
* 'tcp://[yoursentinel2]:[port]',
* 'tcp://[yoursentinel3]:[port]
* )
*/
'store.redis.sentinels' => [],
]; ];
...@@ -179,6 +179,30 @@ For Redis instances that [require authentication](https://redis.io/commands/auth ...@@ -179,6 +179,30 @@ For Redis instances that [require authentication](https://redis.io/commands/auth
* If authentication is managed with the `requirepass` directive (legacy password protection): use the `store.redis.password` option * If authentication is managed with the `requirepass` directive (legacy password protection): use the `store.redis.password` option
* If authentication is managed with [ACL's](https://redis.io/docs/manual/security/acl/) (which are recommended as of Redis 6): use the `store.redis.password` and `store.redis.username` options * If authentication is managed with [ACL's](https://redis.io/docs/manual/security/acl/) (which are recommended as of Redis 6): use the `store.redis.password` and `store.redis.username` options
#### Redis-Sentinel
If your Redis servers are controlled by [Redis-Sentinel](https://redis.io/docs/manual/sentinel/), then configure your sentinels by setting `store.redis.sentinels` to
```php
[
'tcp://[yoursentinel1]:[port]',
'tcp://[yoursentinel2]:[port]',
'tcp://[yoursentinel3]:[port]',
]
```
If your sentinels are password-protected and use the same password as your Redis servers, then setting `store.redis.password` is enough. However if your sentinels use a different password than that of your Redis servers, then set the password of each sentinel:
```php
[
'tcp://[yoursentinel1]:[port]?password=[password1]',
'tcp://[yoursentinel2]:[port]?password=[password2]',
'tcp://[yoursentinel3]:[port]?password=[password3]',
]
```
Configure your master group by setting `store.redis.mastergroup` (`mymaster` by default).
## Metadata storage ## Metadata storage
Several metadata storage backends are available by default, including `flatfile`, `serialize`, `mdq` and Several metadata storage backends are available by default, including `flatfile`, `serialize`, `mdq` and
......
...@@ -42,19 +42,38 @@ class RedisStore implements StoreInterface ...@@ -42,19 +42,38 @@ class RedisStore implements StoreInterface
$username = $config->getOptionalString('store.redis.username', null); $username = $config->getOptionalString('store.redis.username', null);
$database = $config->getOptionalInteger('store.redis.database', 0); $database = $config->getOptionalInteger('store.redis.database', 0);
$redis = new Client( $sentinels = $config->getOptionalArray('store.redis.sentinels', []);
[
'scheme' => 'tcp', if (empty($sentinels)) {
'host' => $host, $redis = new Client(
'port' => $port, [
'database' => $database, 'scheme' => 'tcp',
] 'host' => $host,
+ (!empty($password) ? ['password' => $password] : []) 'port' => $port,
+ (!empty($username) ? ['username' => $username] : []), 'database' => $database,
[ ]
'prefix' => $prefix, + (!empty($username) ? ['username' => $username] : [])
] + (!empty($password) ? ['password' => $password] : []),
); [
'prefix' => $prefix,
]
);
} else {
$mastergroup = $config->getOptionalString('store.redis.mastergroup', 'mymaster');
$redis = new Client(
$sentinels,
[
'replication' => 'sentinel',
'service' => $mastergroup,
'prefix' => $prefix,
'parameters' => [
'database' => $database,
]
+ (!empty($username) ? ['username' => $username] : [])
+ (!empty($password) ? ['password' => $password] : []),
]
);
}
} }
$this->redis = $redis; $this->redis = $redis;
......
...@@ -143,6 +143,21 @@ class RedisStoreTest extends TestCase ...@@ -143,6 +143,21 @@ class RedisStoreTest extends TestCase
$this->assertInstanceOf(Store\RedisStore::class, $this->store); $this->assertInstanceOf(Store\RedisStore::class, $this->store);
} }
/**
* @covers \SimpleSAML\Store::getInstance
* @covers \SimpleSAML\Store\Redis::__construct
* @test
*/
public function testRedisSentinelInstance(): void
{
$config = Configuration::loadFromArray(array(
'store.type' => 'redis',
'store.redis.prefix' => 'phpunit_',
'store.redis.mastergroup' => 'phpunit_mastergroup',
'store.redis.sentinels' => array('tcp://sentinel1', 'tcp://sentinel2', 'tcp://sentinel3'),
), '[ARRAY]', 'simplesaml');
$this->assertInstanceOf(Store\RedisStore::class, $this->store);
}
/** /**
* @test * @test
......
...@@ -86,6 +86,7 @@ class StoreFactoryTest extends TestCase ...@@ -86,6 +86,7 @@ class StoreFactoryTest extends TestCase
Configuration::loadFromArray([ Configuration::loadFromArray([
'store.type' => 'redis', 'store.type' => 'redis',
'store.redis.prefix' => 'phpunit_', 'store.redis.prefix' => 'phpunit_',
'store.redis.sentinels' => [],
], '[ARRAY]', 'simplesaml'); ], '[ARRAY]', 'simplesaml');
$config = Configuration::getInstance(); $config = Configuration::getInstance();
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment