diff options
-rw-r--r-- | changelog.d/4632.feature | 1 | ||||
-rw-r--r-- | changelog.d/4642.feature | 1 | ||||
-rw-r--r-- | changelog.d/4643.misc | 1 | ||||
-rw-r--r-- | changelog.d/4651.bugfix | 1 | ||||
-rw-r--r-- | changelog.d/4667.bugfix | 1 | ||||
-rw-r--r-- | changelog.d/4668.misc | 1 | ||||
-rw-r--r-- | changelog.d/4669.misc | 1 | ||||
-rw-r--r-- | changelog.d/4674.feature | 1 | ||||
-rw-r--r-- | synapse/app/_base.py | 30 | ||||
-rw-r--r-- | synapse/app/federation_reader.py | 2 | ||||
-rw-r--r-- | synapse/config/metrics.py | 30 | ||||
-rw-r--r-- | synapse/crypto/context_factory.py | 14 | ||||
-rw-r--r-- | synapse/crypto/keyring.py | 4 | ||||
-rw-r--r-- | synapse/groups/attestations.py | 7 | ||||
-rw-r--r-- | synapse/handlers/_base.py | 2 | ||||
-rw-r--r-- | synapse/handlers/device.py | 14 | ||||
-rw-r--r-- | synapse/handlers/groups_local.py | 12 | ||||
-rw-r--r-- | synapse/handlers/room.py | 22 | ||||
-rw-r--r-- | synapse/http/server.py | 6 | ||||
-rw-r--r-- | synapse/python_dependencies.py | 1 | ||||
-rw-r--r-- | synapse/rest/well_known.py | 3 |
21 files changed, 131 insertions, 24 deletions
diff --git a/changelog.d/4632.feature b/changelog.d/4632.feature new file mode 100644 index 0000000000..d053ab5a25 --- /dev/null +++ b/changelog.d/4632.feature @@ -0,0 +1 @@ +Add basic optional sentry integration diff --git a/changelog.d/4642.feature b/changelog.d/4642.feature new file mode 100644 index 0000000000..bfbf95bcbb --- /dev/null +++ b/changelog.d/4642.feature @@ -0,0 +1 @@ +Transfer bans on room upgrade. \ No newline at end of file diff --git a/changelog.d/4643.misc b/changelog.d/4643.misc new file mode 100644 index 0000000000..556cdd2240 --- /dev/null +++ b/changelog.d/4643.misc @@ -0,0 +1 @@ +Reduce number of exceptions we log diff --git a/changelog.d/4651.bugfix b/changelog.d/4651.bugfix new file mode 100644 index 0000000000..15cb1e58c4 --- /dev/null +++ b/changelog.d/4651.bugfix @@ -0,0 +1 @@ +Set CORS headers on .well-known requests diff --git a/changelog.d/4667.bugfix b/changelog.d/4667.bugfix new file mode 100644 index 0000000000..33ad00c137 --- /dev/null +++ b/changelog.d/4667.bugfix @@ -0,0 +1 @@ +Fix kicking guest users on guest access revocation in worker mode. diff --git a/changelog.d/4668.misc b/changelog.d/4668.misc new file mode 100644 index 0000000000..556cdd2240 --- /dev/null +++ b/changelog.d/4668.misc @@ -0,0 +1 @@ +Reduce number of exceptions we log diff --git a/changelog.d/4669.misc b/changelog.d/4669.misc new file mode 100644 index 0000000000..00a1a940ae --- /dev/null +++ b/changelog.d/4669.misc @@ -0,0 +1 @@ +Cleanup request exception logging diff --git a/changelog.d/4674.feature b/changelog.d/4674.feature new file mode 100644 index 0000000000..84630bb201 --- /dev/null +++ b/changelog.d/4674.feature @@ -0,0 +1 @@ +Reduce the overhead of creating outbound federation connections over TLS by caching the TLS client options. diff --git a/synapse/app/_base.py b/synapse/app/_base.py index 73ca52bd8c..32e8b8a3f5 100644 --- a/synapse/app/_base.py +++ b/synapse/app/_base.py @@ -25,10 +25,12 @@ from daemonize import Daemonize from twisted.internet import error, reactor from twisted.protocols.tls import TLSMemoryBIOFactory +import synapse from synapse.app import check_bind_error from synapse.crypto import context_factory from synapse.util import PreserveLoggingContext from synapse.util.rlimit import change_resource_limit +from synapse.util.versionstring import get_version_string logger = logging.getLogger(__name__) @@ -270,9 +272,37 @@ def start(hs, listeners=None): # It is now safe to start your Synapse. hs.start_listening(listeners) hs.get_datastore().start_profiling() + + setup_sentry(hs) except Exception: traceback.print_exc(file=sys.stderr) reactor = hs.get_reactor() if reactor.running: reactor.stop() sys.exit(1) + + +def setup_sentry(hs): + """Enable sentry integration, if enabled in configuration + + Args: + hs (synapse.server.HomeServer) + """ + + if not hs.config.sentry_enabled: + return + + import sentry_sdk + sentry_sdk.init( + dsn=hs.config.sentry_dsn, + release=get_version_string(synapse), + ) + + # We set some default tags that give some context to this instance + with sentry_sdk.configure_scope() as scope: + scope.set_tag("matrix_server_name", hs.config.server_name) + + app = hs.config.worker_app if hs.config.worker_app else "synapse.app.homeserver" + name = hs.config.worker_name if hs.config.worker_name else "master" + scope.set_tag("worker_app", app) + scope.set_tag("worker_name", name) diff --git a/synapse/app/federation_reader.py b/synapse/app/federation_reader.py index 6ee2b76dcd..b116c17669 100644 --- a/synapse/app/federation_reader.py +++ b/synapse/app/federation_reader.py @@ -40,6 +40,7 @@ from synapse.replication.slave.storage.profile import SlavedProfileStore from synapse.replication.slave.storage.push_rule import SlavedPushRuleStore from synapse.replication.slave.storage.pushers import SlavedPusherStore from synapse.replication.slave.storage.receipts import SlavedReceiptsStore +from synapse.replication.slave.storage.registration import SlavedRegistrationStore from synapse.replication.slave.storage.room import RoomStore from synapse.replication.slave.storage.transactions import SlavedTransactionStore from synapse.replication.tcp.client import ReplicationClientHandler @@ -62,6 +63,7 @@ class FederationReaderSlavedStore( SlavedReceiptsStore, SlavedEventStore, SlavedKeyStore, + SlavedRegistrationStore, RoomStore, DirectoryStore, SlavedTransactionStore, diff --git a/synapse/config/metrics.py b/synapse/config/metrics.py index 718c43ae03..35f1074765 100644 --- a/synapse/config/metrics.py +++ b/synapse/config/metrics.py @@ -13,7 +13,13 @@ # See the License for the specific language governing permissions and # limitations under the License. -from ._base import Config +from ._base import Config, ConfigError + +MISSING_SENTRY = ( + """Missing sentry-sdk library. This is required to enable sentry + integration. + """ +) class MetricsConfig(Config): @@ -23,12 +29,34 @@ class MetricsConfig(Config): self.metrics_port = config.get("metrics_port") self.metrics_bind_host = config.get("metrics_bind_host", "127.0.0.1") + self.sentry_enabled = "sentry" in config + if self.sentry_enabled: + try: + import sentry_sdk # noqa F401 + except ImportError: + raise ConfigError(MISSING_SENTRY) + + self.sentry_dsn = config["sentry"].get("dsn") + if not self.sentry_dsn: + raise ConfigError( + "sentry.dsn field is required when sentry integration is enabled", + ) + def default_config(self, report_stats=None, **kwargs): res = """\ ## Metrics ### # Enable collection and rendering of performance metrics enable_metrics: False + + # Enable sentry integration + # NOTE: While attempts are made to ensure that the logs don't contain + # any sensitive information, this cannot be guaranteed. By enabling + # this option the sentry server may therefore receive sensitive + # information, and it in turn may then diseminate sensitive information + # through insecure notification channels if so configured. + #sentry: + # dsn: "..." """ if report_stats is None: diff --git a/synapse/crypto/context_factory.py b/synapse/crypto/context_factory.py index 85f2848fb1..49cbc7098f 100644 --- a/synapse/crypto/context_factory.py +++ b/synapse/crypto/context_factory.py @@ -1,4 +1,5 @@ # Copyright 2014-2016 OpenMarket Ltd +# Copyright 2019 New Vector Ltd # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -11,6 +12,7 @@ # 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. + import logging from zope.interface import implementer @@ -105,9 +107,7 @@ class ClientTLSOptions(object): self._hostnameBytes = _idnaBytes(hostname) self._sendSNI = True - ctx.set_info_callback( - _tolerateErrors(self._identityVerifyingInfoCallback) - ) + ctx.set_info_callback(_tolerateErrors(self._identityVerifyingInfoCallback)) def clientConnectionForTLS(self, tlsProtocol): context = self._ctx @@ -128,10 +128,8 @@ class ClientTLSOptionsFactory(object): def __init__(self, config): # We don't use config options yet - pass + self._options = CertificateOptions(verify=False) def get_options(self, host): - return ClientTLSOptions( - host, - CertificateOptions(verify=False).getContext() - ) + # Use _makeContext so that we get a fresh OpenSSL CTX each time. + return ClientTLSOptions(host, self._options._makeContext()) diff --git a/synapse/crypto/keyring.py b/synapse/crypto/keyring.py index 3a96980bed..cce40fdd2d 100644 --- a/synapse/crypto/keyring.py +++ b/synapse/crypto/keyring.py @@ -35,7 +35,7 @@ from unpaddedbase64 import decode_base64 from twisted.internet import defer -from synapse.api.errors import Codes, SynapseError +from synapse.api.errors import Codes, RequestSendFailed, SynapseError from synapse.util import logcontext, unwrapFirstError from synapse.util.logcontext import ( LoggingContext, @@ -656,7 +656,7 @@ def _handle_key_deferred(verify_request): try: with PreserveLoggingContext(): _, key_id, verify_key = yield verify_request.deferred - except IOError as e: + except (IOError, RequestSendFailed) as e: logger.warn( "Got IOError when downloading keys for %s: %s %s", server_name, type(e).__name__, str(e), diff --git a/synapse/groups/attestations.py b/synapse/groups/attestations.py index b04f4234ca..786149be65 100644 --- a/synapse/groups/attestations.py +++ b/synapse/groups/attestations.py @@ -42,7 +42,7 @@ from signedjson.sign import sign_json from twisted.internet import defer -from synapse.api.errors import SynapseError +from synapse.api.errors import RequestSendFailed, SynapseError from synapse.metrics.background_process_metrics import run_as_background_process from synapse.types import get_domain_from_id from synapse.util.logcontext import run_in_background @@ -191,6 +191,11 @@ class GroupAttestionRenewer(object): yield self.store.update_attestation_renewal( group_id, user_id, attestation ) + except RequestSendFailed as e: + logger.warning( + "Failed to renew attestation of %r in %r: %s", + user_id, group_id, e, + ) except Exception: logger.exception("Error renewing attestation of %r in %r", user_id, group_id) diff --git a/synapse/handlers/_base.py b/synapse/handlers/_base.py index 704181d2d3..594754cfd8 100644 --- a/synapse/handlers/_base.py +++ b/synapse/handlers/_base.py @@ -167,4 +167,4 @@ class BaseHandler(object): ratelimit=False, ) except Exception as e: - logger.warn("Error kicking guest user: %s" % (e,)) + logger.exception("Error kicking guest user: %s" % (e,)) diff --git a/synapse/handlers/device.py b/synapse/handlers/device.py index 8955cde4ed..c708c35d4d 100644 --- a/synapse/handlers/device.py +++ b/synapse/handlers/device.py @@ -20,7 +20,11 @@ from twisted.internet import defer from synapse.api import errors from synapse.api.constants import EventTypes -from synapse.api.errors import FederationDeniedError +from synapse.api.errors import ( + FederationDeniedError, + HttpResponseException, + RequestSendFailed, +) from synapse.types import RoomStreamToken, get_domain_from_id from synapse.util import stringutils from synapse.util.async_helpers import Linearizer @@ -504,13 +508,13 @@ class DeviceListEduUpdater(object): origin = get_domain_from_id(user_id) try: result = yield self.federation.query_user_devices(origin, user_id) - except NotRetryingDestination: + except ( + NotRetryingDestination, RequestSendFailed, HttpResponseException, + ): # TODO: Remember that we are now out of sync and try again # later logger.warn( - "Failed to handle device list update for %s," - " we're not retrying the remote", - user_id, + "Failed to handle device list update for %s", user_id, ) # We abort on exceptions rather than accepting the update # as otherwise synapse will 'forget' that its device list diff --git a/synapse/handlers/groups_local.py b/synapse/handlers/groups_local.py index 173315af6c..02c508acec 100644 --- a/synapse/handlers/groups_local.py +++ b/synapse/handlers/groups_local.py @@ -20,7 +20,7 @@ from six import iteritems from twisted.internet import defer -from synapse.api.errors import HttpResponseException, SynapseError +from synapse.api.errors import HttpResponseException, RequestSendFailed, SynapseError from synapse.types import get_domain_from_id logger = logging.getLogger(__name__) @@ -46,13 +46,19 @@ def _create_rerouter(func_name): # when the remote end responds with things like 403 Not # In Group, we can communicate that to the client instead # of a 500. - def h(failure): + def http_response_errback(failure): failure.trap(HttpResponseException) e = failure.value if e.code == 403: raise e.to_synapse_error() return failure - d.addErrback(h) + + def request_failed_errback(failure): + failure.trap(RequestSendFailed) + raise SynapseError(502, "Failed to contact group server") + + d.addErrback(http_response_errback) + d.addErrback(request_failed_errback) return d return f diff --git a/synapse/handlers/room.py b/synapse/handlers/room.py index f9af1f0046..67b15697fd 100644 --- a/synapse/handlers/room.py +++ b/synapse/handlers/room.py @@ -311,6 +311,28 @@ class RoomCreationHandler(BaseHandler): creation_content=creation_content, ) + # Transfer membership events + old_room_member_state_ids = yield self.store.get_filtered_current_state_ids( + old_room_id, StateFilter.from_types([(EventTypes.Member, None)]), + ) + + # map from event_id to BaseEvent + old_room_member_state_events = yield self.store.get_events( + old_room_member_state_ids.values(), + ) + for k, old_event in iteritems(old_room_member_state_events): + # Only transfer ban events + if ("membership" in old_event.content and + old_event.content["membership"] == "ban"): + yield self.room_member_handler.update_membership( + requester, + UserID.from_string(old_event['state_key']), + new_room_id, + "ban", + ratelimit=False, + content=old_event.content, + ) + # XXX invites/joins # XXX 3pid invites diff --git a/synapse/http/server.py b/synapse/http/server.py index 6a427d96a6..6c67a25a11 100644 --- a/synapse/http/server.py +++ b/synapse/http/server.py @@ -106,10 +106,10 @@ def wrap_json_request_handler(h): # trace. f = failure.Failure() logger.error( - "Failed handle request via %r: %r: %s", - h, + "Failed handle request via %r: %r", + request.request_metrics.name, request, - f.getTraceback().rstrip(), + exc_info=(f.type, f.value, f.getTracebackObject()), ) # Only respond with an error response if we haven't already started # writing, otherwise lets just kill the connection diff --git a/synapse/python_dependencies.py b/synapse/python_dependencies.py index 590ee59907..f71e21ff4d 100644 --- a/synapse/python_dependencies.py +++ b/synapse/python_dependencies.py @@ -86,6 +86,7 @@ CONDITIONAL_REQUIREMENTS = { "saml2": ["pysaml2>=4.5.0"], "url_preview": ["lxml>=3.5.0"], "test": ["mock>=2.0", "parameterized"], + "sentry": ["sentry-sdk>=0.7.2"], } diff --git a/synapse/rest/well_known.py b/synapse/rest/well_known.py index 6e043d6162..c0a4ae93e5 100644 --- a/synapse/rest/well_known.py +++ b/synapse/rest/well_known.py @@ -18,6 +18,8 @@ import logging from twisted.web.resource import Resource +from synapse.http.server import set_cors_headers + logger = logging.getLogger(__name__) @@ -59,6 +61,7 @@ class WellKnownResource(Resource): self._well_known_builder = WellKnownBuilder(hs) def render_GET(self, request): + set_cors_headers(request) r = self._well_known_builder.get_well_known() if not r: request.setResponseCode(404) |