Add redis SSL configuration options (#15312)

* Add SSL options to redis config

* fix lint issues

* Add documentation and changelog file

* add missing . at the end of the changelog

* Move client context factory to new file

* Rename ssl to tls and fix typo

* fix lint issues

* Added when redis attributes were added
This commit is contained in:
Roel ter Maat 2023-05-11 14:02:51 +02:00 committed by GitHub
parent 5bf9ec9e3e
commit 2611433b70
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 98 additions and 14 deletions

View file

@ -0,0 +1 @@
Add redis TLS configuration options.

View file

@ -70,6 +70,10 @@ redis:
port: 6379 port: 6379
# dbid: <redis_logical_db_id> # dbid: <redis_logical_db_id>
# password: <secret_password> # password: <secret_password>
# use_tls: True
# certificate_file: <path_to_certificate>
# private_key_file: <path_to_private_key>
# ca_file: <path_to_ca_certificate>
``` ```
This assumes that your Redis service is called `redis` in your Docker Compose file. This assumes that your Redis service is called `redis` in your Docker Compose file.

View file

@ -3981,9 +3981,16 @@ This setting has the following sub-options:
localhost and 6379 localhost and 6379
* `password`: Optional password if configured on the Redis instance. * `password`: Optional password if configured on the Redis instance.
* `dbid`: Optional redis dbid if needs to connect to specific redis logical db. * `dbid`: Optional redis dbid if needs to connect to specific redis logical db.
* `use_tls`: Whether to use tls connection. Defaults to false.
* `certificate_file`: Optional path to the certificate file
* `private_key_file`: Optional path to the private key file
* `ca_file`: Optional path to the CA certificate file. Use this one or:
* `ca_path`: Optional path to the folder containing the CA certificate file
_Added in Synapse 1.78.0._ _Added in Synapse 1.78.0._
_Changed in Synapse 1.84.0: Added use\_tls, certificate\_file, private\_key\_file, ca\_file and ca\_path attributes_
Example configuration: Example configuration:
```yaml ```yaml
redis: redis:
@ -3992,6 +3999,10 @@ redis:
port: 6379 port: 6379
password: <secret_password> password: <secret_password>
dbid: <dbid> dbid: <dbid>
#use_tls: True
#certificate_file: <path_to_the_certificate_file>
#private_key_file: <path_to_the_private_key_file>
#ca_file: <path_to_the_ca_certificate_file>
``` ```
--- ---
## Individual worker configuration ## Individual worker configuration

View file

@ -35,3 +35,9 @@ class RedisConfig(Config):
self.redis_port = redis_config.get("port", 6379) self.redis_port = redis_config.get("port", 6379)
self.redis_dbid = redis_config.get("dbid", None) self.redis_dbid = redis_config.get("dbid", None)
self.redis_password = redis_config.get("password") self.redis_password = redis_config.get("password")
self.redis_use_tls = redis_config.get("use_tls", False)
self.redis_certificate = redis_config.get("certificate_file", None)
self.redis_private_key = redis_config.get("private_key_file", None)
self.redis_ca_file = redis_config.get("ca_file", None)
self.redis_ca_path = redis_config.get("ca_path", None)

View file

@ -0,0 +1,34 @@
# Copyright 2023 The Matrix.org Foundation C.I.C.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from OpenSSL.SSL import Context
from twisted.internet import ssl
from synapse.config.redis import RedisConfig
class ClientContextFactory(ssl.ClientContextFactory):
def __init__(self, redis_config: RedisConfig):
self.redis_config = redis_config
def getContext(self) -> Context:
ctx = super().getContext()
if self.redis_config.redis_certificate:
ctx.use_certificate_file(self.redis_config.redis_certificate)
if self.redis_config.redis_private_key:
ctx.use_privatekey_file(self.redis_config.redis_private_key)
if self.redis_config.redis_ca_file:
ctx.load_verify_locations(cafile=self.redis_config.redis_ca_file)
elif self.redis_config.redis_ca_path:
ctx.load_verify_locations(capath=self.redis_config.redis_ca_path)
return ctx

View file

@ -46,6 +46,7 @@ from synapse.replication.tcp.commands import (
UserIpCommand, UserIpCommand,
UserSyncCommand, UserSyncCommand,
) )
from synapse.replication.tcp.context import ClientContextFactory
from synapse.replication.tcp.protocol import IReplicationConnection from synapse.replication.tcp.protocol import IReplicationConnection
from synapse.replication.tcp.streams import ( from synapse.replication.tcp.streams import (
STREAMS_MAP, STREAMS_MAP,
@ -348,13 +349,27 @@ class ReplicationCommandHandler:
outbound_redis_connection, outbound_redis_connection,
channel_names=self._channels_to_subscribe_to, channel_names=self._channels_to_subscribe_to,
) )
hs.get_reactor().connectTCP(
hs.config.redis.redis_host, reactor = hs.get_reactor()
hs.config.redis.redis_port, redis_config = hs.config.redis
self._factory, if hs.config.redis.redis_use_tls:
timeout=30, ssl_context_factory = ClientContextFactory(hs.config.redis)
bindAddress=None, reactor.connectSSL(
) redis_config.redis_host,
redis_config.redis_port,
self._factory,
ssl_context_factory,
timeout=30,
bindAddress=None,
)
else:
reactor.connectTCP(
redis_config.redis_host,
redis_config.redis_port,
self._factory,
timeout=30,
bindAddress=None,
)
def get_streams(self) -> Dict[str, Stream]: def get_streams(self) -> Dict[str, Stream]:
"""Get a map from stream name to all streams.""" """Get a map from stream name to all streams."""

View file

@ -35,6 +35,7 @@ from synapse.replication.tcp.commands import (
ReplicateCommand, ReplicateCommand,
parse_command_from_line, parse_command_from_line,
) )
from synapse.replication.tcp.context import ClientContextFactory
from synapse.replication.tcp.protocol import ( from synapse.replication.tcp.protocol import (
IReplicationConnection, IReplicationConnection,
tcp_inbound_commands_counter, tcp_inbound_commands_counter,
@ -386,12 +387,24 @@ def lazyConnection(
factory.continueTrying = reconnect factory.continueTrying = reconnect
reactor = hs.get_reactor() reactor = hs.get_reactor()
reactor.connectTCP(
host, if hs.config.redis.redis_use_tls:
port, ssl_context_factory = ClientContextFactory(hs.config.redis)
factory, reactor.connectSSL(
timeout=30, host,
bindAddress=None, port,
) factory,
ssl_context_factory,
timeout=30,
bindAddress=None,
)
else:
reactor.connectTCP(
host,
port,
factory,
timeout=30,
bindAddress=None,
)
return factory.handler return factory.handler