Support using SSL on worker endpoints. (#14128)
* Fix missing SSL support in worker endpoints. * Add changelog * SSL for Replication endpoint * Remove unit test change * Refactor listener creation to reduce duplicated code * Fix the logger message * Update synapse/app/_base.py Co-authored-by: Patrick Cloke <clokep@users.noreply.github.com> * Update synapse/app/_base.py Co-authored-by: Patrick Cloke <clokep@users.noreply.github.com> * Update synapse/app/_base.py Co-authored-by: Patrick Cloke <clokep@users.noreply.github.com> * Add config documentation for new TLS option Co-authored-by: Tuomas Ojamies <tojamies@palantir.com> Co-authored-by: Patrick Cloke <clokep@users.noreply.github.com> Co-authored-by: Olivier Wilkinson (reivilibre) <oliverw@matrix.org>pull/14455/head
parent
634359b083
commit
b5ab2c428a
|
@ -0,0 +1 @@
|
||||||
|
Add TLS support for generic worker endpoints.
|
|
@ -3893,6 +3893,26 @@ Example configuration:
|
||||||
worker_replication_http_port: 9093
|
worker_replication_http_port: 9093
|
||||||
```
|
```
|
||||||
---
|
---
|
||||||
|
### `worker_replication_http_tls`
|
||||||
|
|
||||||
|
Whether TLS should be used for talking to the HTTP replication port on the main
|
||||||
|
Synapse process.
|
||||||
|
The main Synapse process defines this with the `tls` option on its [listener](#listeners) that
|
||||||
|
has the `replication` resource enabled.
|
||||||
|
|
||||||
|
**Please note:** by default, it is not safe to expose replication ports to the
|
||||||
|
public Internet, even with TLS enabled.
|
||||||
|
See [`worker_replication_secret`](#worker_replication_secret).
|
||||||
|
|
||||||
|
Defaults to `false`.
|
||||||
|
|
||||||
|
*Added in Synapse 1.72.0.*
|
||||||
|
|
||||||
|
Example configuration:
|
||||||
|
```yaml
|
||||||
|
worker_replication_http_tls: true
|
||||||
|
```
|
||||||
|
---
|
||||||
### `worker_listeners`
|
### `worker_listeners`
|
||||||
|
|
||||||
A worker can handle HTTP requests. To do so, a `worker_listeners` option
|
A worker can handle HTTP requests. To do so, a `worker_listeners` option
|
||||||
|
|
|
@ -47,6 +47,7 @@ from twisted.internet.tcp import Port
|
||||||
from twisted.logger import LoggingFile, LogLevel
|
from twisted.logger import LoggingFile, LogLevel
|
||||||
from twisted.protocols.tls import TLSMemoryBIOFactory
|
from twisted.protocols.tls import TLSMemoryBIOFactory
|
||||||
from twisted.python.threadpool import ThreadPool
|
from twisted.python.threadpool import ThreadPool
|
||||||
|
from twisted.web.resource import Resource
|
||||||
|
|
||||||
import synapse.util.caches
|
import synapse.util.caches
|
||||||
from synapse.api.constants import MAX_PDU_SIZE
|
from synapse.api.constants import MAX_PDU_SIZE
|
||||||
|
@ -55,12 +56,13 @@ from synapse.app.phone_stats_home import start_phone_stats_home
|
||||||
from synapse.config import ConfigError
|
from synapse.config import ConfigError
|
||||||
from synapse.config._base import format_config_error
|
from synapse.config._base import format_config_error
|
||||||
from synapse.config.homeserver import HomeServerConfig
|
from synapse.config.homeserver import HomeServerConfig
|
||||||
from synapse.config.server import ManholeConfig
|
from synapse.config.server import ListenerConfig, ManholeConfig
|
||||||
from synapse.crypto import context_factory
|
from synapse.crypto import context_factory
|
||||||
from synapse.events.presence_router import load_legacy_presence_router
|
from synapse.events.presence_router import load_legacy_presence_router
|
||||||
from synapse.events.spamcheck import load_legacy_spam_checkers
|
from synapse.events.spamcheck import load_legacy_spam_checkers
|
||||||
from synapse.events.third_party_rules import load_legacy_third_party_event_rules
|
from synapse.events.third_party_rules import load_legacy_third_party_event_rules
|
||||||
from synapse.handlers.auth import load_legacy_password_auth_providers
|
from synapse.handlers.auth import load_legacy_password_auth_providers
|
||||||
|
from synapse.http.site import SynapseSite
|
||||||
from synapse.logging.context import PreserveLoggingContext
|
from synapse.logging.context import PreserveLoggingContext
|
||||||
from synapse.logging.opentracing import init_tracer
|
from synapse.logging.opentracing import init_tracer
|
||||||
from synapse.metrics import install_gc_manager, register_threadpool
|
from synapse.metrics import install_gc_manager, register_threadpool
|
||||||
|
@ -357,6 +359,55 @@ def listen_tcp(
|
||||||
return r # type: ignore[return-value]
|
return r # type: ignore[return-value]
|
||||||
|
|
||||||
|
|
||||||
|
def listen_http(
|
||||||
|
listener_config: ListenerConfig,
|
||||||
|
root_resource: Resource,
|
||||||
|
version_string: str,
|
||||||
|
max_request_body_size: int,
|
||||||
|
context_factory: IOpenSSLContextFactory,
|
||||||
|
reactor: IReactorSSL = reactor,
|
||||||
|
) -> List[Port]:
|
||||||
|
port = listener_config.port
|
||||||
|
bind_addresses = listener_config.bind_addresses
|
||||||
|
tls = listener_config.tls
|
||||||
|
|
||||||
|
assert listener_config.http_options is not None
|
||||||
|
|
||||||
|
site_tag = listener_config.http_options.tag
|
||||||
|
if site_tag is None:
|
||||||
|
site_tag = str(port)
|
||||||
|
|
||||||
|
site = SynapseSite(
|
||||||
|
"synapse.access.%s.%s" % ("https" if tls else "http", site_tag),
|
||||||
|
site_tag,
|
||||||
|
listener_config,
|
||||||
|
root_resource,
|
||||||
|
version_string,
|
||||||
|
max_request_body_size=max_request_body_size,
|
||||||
|
reactor=reactor,
|
||||||
|
)
|
||||||
|
if tls:
|
||||||
|
# refresh_certificate should have been called before this.
|
||||||
|
assert context_factory is not None
|
||||||
|
ports = listen_ssl(
|
||||||
|
bind_addresses,
|
||||||
|
port,
|
||||||
|
site,
|
||||||
|
context_factory,
|
||||||
|
reactor=reactor,
|
||||||
|
)
|
||||||
|
logger.info("Synapse now listening on TCP port %d (TLS)", port)
|
||||||
|
else:
|
||||||
|
ports = listen_tcp(
|
||||||
|
bind_addresses,
|
||||||
|
port,
|
||||||
|
site,
|
||||||
|
reactor=reactor,
|
||||||
|
)
|
||||||
|
logger.info("Synapse now listening on TCP port %d", port)
|
||||||
|
return ports
|
||||||
|
|
||||||
|
|
||||||
def listen_ssl(
|
def listen_ssl(
|
||||||
bind_addresses: Collection[str],
|
bind_addresses: Collection[str],
|
||||||
port: int,
|
port: int,
|
||||||
|
|
|
@ -44,7 +44,7 @@ from synapse.config.server import ListenerConfig
|
||||||
from synapse.federation.transport.server import TransportLayerServer
|
from synapse.federation.transport.server import TransportLayerServer
|
||||||
from synapse.http.server import JsonResource, OptionsResource
|
from synapse.http.server import JsonResource, OptionsResource
|
||||||
from synapse.http.servlet import RestServlet, parse_json_object_from_request
|
from synapse.http.servlet import RestServlet, parse_json_object_from_request
|
||||||
from synapse.http.site import SynapseRequest, SynapseSite
|
from synapse.http.site import SynapseRequest
|
||||||
from synapse.logging.context import LoggingContext
|
from synapse.logging.context import LoggingContext
|
||||||
from synapse.metrics import METRICS_PREFIX, MetricsResource, RegistryProxy
|
from synapse.metrics import METRICS_PREFIX, MetricsResource, RegistryProxy
|
||||||
from synapse.replication.http import REPLICATION_PREFIX, ReplicationRestResource
|
from synapse.replication.http import REPLICATION_PREFIX, ReplicationRestResource
|
||||||
|
@ -288,15 +288,9 @@ class GenericWorkerServer(HomeServer):
|
||||||
DATASTORE_CLASS = GenericWorkerSlavedStore # type: ignore
|
DATASTORE_CLASS = GenericWorkerSlavedStore # type: ignore
|
||||||
|
|
||||||
def _listen_http(self, listener_config: ListenerConfig) -> None:
|
def _listen_http(self, listener_config: ListenerConfig) -> None:
|
||||||
port = listener_config.port
|
|
||||||
bind_addresses = listener_config.bind_addresses
|
|
||||||
|
|
||||||
assert listener_config.http_options is not None
|
assert listener_config.http_options is not None
|
||||||
|
|
||||||
site_tag = listener_config.http_options.tag
|
|
||||||
if site_tag is None:
|
|
||||||
site_tag = str(port)
|
|
||||||
|
|
||||||
# We always include a health resource.
|
# We always include a health resource.
|
||||||
resources: Dict[str, Resource] = {"/health": HealthResource()}
|
resources: Dict[str, Resource] = {"/health": HealthResource()}
|
||||||
|
|
||||||
|
@ -395,23 +389,15 @@ class GenericWorkerServer(HomeServer):
|
||||||
|
|
||||||
root_resource = create_resource_tree(resources, OptionsResource())
|
root_resource = create_resource_tree(resources, OptionsResource())
|
||||||
|
|
||||||
_base.listen_tcp(
|
_base.listen_http(
|
||||||
bind_addresses,
|
listener_config,
|
||||||
port,
|
root_resource,
|
||||||
SynapseSite(
|
self.version_string,
|
||||||
"synapse.access.http.%s" % (site_tag,),
|
max_request_body_size(self.config),
|
||||||
site_tag,
|
self.tls_server_context_factory,
|
||||||
listener_config,
|
|
||||||
root_resource,
|
|
||||||
self.version_string,
|
|
||||||
max_request_body_size=max_request_body_size(self.config),
|
|
||||||
reactor=self.get_reactor(),
|
|
||||||
),
|
|
||||||
reactor=self.get_reactor(),
|
reactor=self.get_reactor(),
|
||||||
)
|
)
|
||||||
|
|
||||||
logger.info("Synapse worker now listening on port %d", port)
|
|
||||||
|
|
||||||
def start_listening(self) -> None:
|
def start_listening(self) -> None:
|
||||||
for listener in self.config.worker.worker_listeners:
|
for listener in self.config.worker.worker_listeners:
|
||||||
if listener.type == "http":
|
if listener.type == "http":
|
||||||
|
|
|
@ -37,8 +37,7 @@ from synapse.api.urls import (
|
||||||
from synapse.app import _base
|
from synapse.app import _base
|
||||||
from synapse.app._base import (
|
from synapse.app._base import (
|
||||||
handle_startup_exception,
|
handle_startup_exception,
|
||||||
listen_ssl,
|
listen_http,
|
||||||
listen_tcp,
|
|
||||||
max_request_body_size,
|
max_request_body_size,
|
||||||
redirect_stdio_to_logs,
|
redirect_stdio_to_logs,
|
||||||
register_start,
|
register_start,
|
||||||
|
@ -53,7 +52,6 @@ from synapse.http.server import (
|
||||||
RootOptionsRedirectResource,
|
RootOptionsRedirectResource,
|
||||||
StaticResource,
|
StaticResource,
|
||||||
)
|
)
|
||||||
from synapse.http.site import SynapseSite
|
|
||||||
from synapse.logging.context import LoggingContext
|
from synapse.logging.context import LoggingContext
|
||||||
from synapse.metrics import METRICS_PREFIX, MetricsResource, RegistryProxy
|
from synapse.metrics import METRICS_PREFIX, MetricsResource, RegistryProxy
|
||||||
from synapse.replication.http import REPLICATION_PREFIX, ReplicationRestResource
|
from synapse.replication.http import REPLICATION_PREFIX, ReplicationRestResource
|
||||||
|
@ -83,8 +81,6 @@ class SynapseHomeServer(HomeServer):
|
||||||
self, config: HomeServerConfig, listener_config: ListenerConfig
|
self, config: HomeServerConfig, listener_config: ListenerConfig
|
||||||
) -> Iterable[Port]:
|
) -> Iterable[Port]:
|
||||||
port = listener_config.port
|
port = listener_config.port
|
||||||
bind_addresses = listener_config.bind_addresses
|
|
||||||
tls = listener_config.tls
|
|
||||||
# Must exist since this is an HTTP listener.
|
# Must exist since this is an HTTP listener.
|
||||||
assert listener_config.http_options is not None
|
assert listener_config.http_options is not None
|
||||||
site_tag = listener_config.http_options.tag
|
site_tag = listener_config.http_options.tag
|
||||||
|
@ -140,37 +136,15 @@ class SynapseHomeServer(HomeServer):
|
||||||
else:
|
else:
|
||||||
root_resource = OptionsResource()
|
root_resource = OptionsResource()
|
||||||
|
|
||||||
site = SynapseSite(
|
ports = listen_http(
|
||||||
"synapse.access.%s.%s" % ("https" if tls else "http", site_tag),
|
|
||||||
site_tag,
|
|
||||||
listener_config,
|
listener_config,
|
||||||
create_resource_tree(resources, root_resource),
|
create_resource_tree(resources, root_resource),
|
||||||
self.version_string,
|
self.version_string,
|
||||||
max_request_body_size=max_request_body_size(self.config),
|
max_request_body_size(self.config),
|
||||||
|
self.tls_server_context_factory,
|
||||||
reactor=self.get_reactor(),
|
reactor=self.get_reactor(),
|
||||||
)
|
)
|
||||||
|
|
||||||
if tls:
|
|
||||||
# refresh_certificate should have been called before this.
|
|
||||||
assert self.tls_server_context_factory is not None
|
|
||||||
ports = listen_ssl(
|
|
||||||
bind_addresses,
|
|
||||||
port,
|
|
||||||
site,
|
|
||||||
self.tls_server_context_factory,
|
|
||||||
reactor=self.get_reactor(),
|
|
||||||
)
|
|
||||||
logger.info("Synapse now listening on TCP port %d (TLS)", port)
|
|
||||||
|
|
||||||
else:
|
|
||||||
ports = listen_tcp(
|
|
||||||
bind_addresses,
|
|
||||||
port,
|
|
||||||
site,
|
|
||||||
reactor=self.get_reactor(),
|
|
||||||
)
|
|
||||||
logger.info("Synapse now listening on TCP port %d", port)
|
|
||||||
|
|
||||||
return ports
|
return ports
|
||||||
|
|
||||||
def _configure_named_resource(
|
def _configure_named_resource(
|
||||||
|
|
|
@ -67,6 +67,7 @@ class InstanceLocationConfig:
|
||||||
|
|
||||||
host: str
|
host: str
|
||||||
port: int
|
port: int
|
||||||
|
tls: bool = False
|
||||||
|
|
||||||
|
|
||||||
@attr.s
|
@attr.s
|
||||||
|
@ -149,6 +150,12 @@ class WorkerConfig(Config):
|
||||||
# The port on the main synapse for HTTP replication endpoint
|
# The port on the main synapse for HTTP replication endpoint
|
||||||
self.worker_replication_http_port = config.get("worker_replication_http_port")
|
self.worker_replication_http_port = config.get("worker_replication_http_port")
|
||||||
|
|
||||||
|
# The tls mode on the main synapse for HTTP replication endpoint.
|
||||||
|
# For backward compatibility this defaults to False.
|
||||||
|
self.worker_replication_http_tls = config.get(
|
||||||
|
"worker_replication_http_tls", False
|
||||||
|
)
|
||||||
|
|
||||||
# The shared secret used for authentication when connecting to the main synapse.
|
# The shared secret used for authentication when connecting to the main synapse.
|
||||||
self.worker_replication_secret = config.get("worker_replication_secret", None)
|
self.worker_replication_secret = config.get("worker_replication_secret", None)
|
||||||
|
|
||||||
|
|
|
@ -184,8 +184,10 @@ class ReplicationEndpoint(metaclass=abc.ABCMeta):
|
||||||
client = hs.get_simple_http_client()
|
client = hs.get_simple_http_client()
|
||||||
local_instance_name = hs.get_instance_name()
|
local_instance_name = hs.get_instance_name()
|
||||||
|
|
||||||
|
# The value of these option should match the replication listener settings
|
||||||
master_host = hs.config.worker.worker_replication_host
|
master_host = hs.config.worker.worker_replication_host
|
||||||
master_port = hs.config.worker.worker_replication_http_port
|
master_port = hs.config.worker.worker_replication_http_port
|
||||||
|
master_tls = hs.config.worker.worker_replication_http_tls
|
||||||
|
|
||||||
instance_map = hs.config.worker.instance_map
|
instance_map = hs.config.worker.instance_map
|
||||||
|
|
||||||
|
@ -205,9 +207,11 @@ class ReplicationEndpoint(metaclass=abc.ABCMeta):
|
||||||
if instance_name == "master":
|
if instance_name == "master":
|
||||||
host = master_host
|
host = master_host
|
||||||
port = master_port
|
port = master_port
|
||||||
|
tls = master_tls
|
||||||
elif instance_name in instance_map:
|
elif instance_name in instance_map:
|
||||||
host = instance_map[instance_name].host
|
host = instance_map[instance_name].host
|
||||||
port = instance_map[instance_name].port
|
port = instance_map[instance_name].port
|
||||||
|
tls = instance_map[instance_name].tls
|
||||||
else:
|
else:
|
||||||
raise Exception(
|
raise Exception(
|
||||||
"Instance %r not in 'instance_map' config" % (instance_name,)
|
"Instance %r not in 'instance_map' config" % (instance_name,)
|
||||||
|
@ -238,7 +242,11 @@ class ReplicationEndpoint(metaclass=abc.ABCMeta):
|
||||||
"Unknown METHOD on %s replication endpoint" % (cls.NAME,)
|
"Unknown METHOD on %s replication endpoint" % (cls.NAME,)
|
||||||
)
|
)
|
||||||
|
|
||||||
uri = "http://%s:%s/_synapse/replication/%s/%s" % (
|
# Here the protocol is hard coded to be http by default or https in case the replication
|
||||||
|
# port is set to have tls true.
|
||||||
|
scheme = "https" if tls else "http"
|
||||||
|
uri = "%s://%s:%s/_synapse/replication/%s/%s" % (
|
||||||
|
scheme,
|
||||||
host,
|
host,
|
||||||
port,
|
port,
|
||||||
cls.NAME,
|
cls.NAME,
|
||||||
|
|
Loading…
Reference in New Issue