Synapse 1.4.0 (2019-10-03)
========================== Bugfixes -------- - Redact `client_secret` in server logs. ([\#6158](https://github.com/matrix-org/synapse/issues/6158)) Synapse 1.4.0rc2 (2019-10-02) ============================= Bugfixes -------- - Fix bug in background update that adds last seen information to the `devices` table, and improve its performance on Postgres. ([\#6135](https://github.com/matrix-org/synapse/issues/6135)) - Fix bad performance of censoring redactions background task. ([\#6141](https://github.com/matrix-org/synapse/issues/6141)) - Fix fetching censored redactions from DB, which caused APIs like initial sync to fail if it tried to include the censored redaction. ([\#6145](https://github.com/matrix-org/synapse/issues/6145)) - Fix exceptions when storing large retry intervals for down remote servers. ([\#6146](https://github.com/matrix-org/synapse/issues/6146)) Internal Changes ---------------- - Fix up sample config entry for `redaction_retention_period` option. ([\#6117](https://github.com/matrix-org/synapse/issues/6117)) Synapse 1.4.0rc1 (2019-09-26) ============================= Note that this release includes significant changes around 3pid verification. Administrators are reminded to review the [upgrade notes](UPGRADE.rst#upgrading-to-v140). Features -------- - Changes to 3pid verification: - Add the ability to send registration emails from the homeserver rather than delegating to an identity server. ([\#5835](https://github.com/matrix-org/synapse/issues/5835), [\#5940](https://github.com/matrix-org/synapse/issues/5940), [\#5993](https://github.com/matrix-org/synapse/issues/5993), [\#5994](https://github.com/matrix-org/synapse/issues/5994), [\#5868](https://github.com/matrix-org/synapse/issues/5868)) - Replace `trust_identity_server_for_password_resets` config option with `account_threepid_delegates`, and make the `id_server` parameteter optional on `*/requestToken` endpoints, as per [MSC2263](https://github.com/matrix-org/matrix-doc/pull/2263). ([\#5876](https://github.com/matrix-org/synapse/issues/5876), [\#5969](https://github.com/matrix-org/synapse/issues/5969), [\#6028](https://github.com/matrix-org/synapse/issues/6028)) - Switch to using the v2 Identity Service `/lookup` API where available, with fallback to v1. (Implements [MSC2134](https://github.com/matrix-org/matrix-doc/pull/2134) plus `id_access_token authentication` for v2 Identity Service APIs from [MSC2140](https://github.com/matrix-org/matrix-doc/pull/2140)). ([\#5897](https://github.com/matrix-org/synapse/issues/5897)) - Remove `bind_email` and `bind_msisdn` parameters from `/register` ala [MSC2140](https://github.com/matrix-org/matrix-doc/pull/2140). ([\#5964](https://github.com/matrix-org/synapse/issues/5964)) - Add `m.id_access_token` to `unstable_features` in `/versions` as per [MSC2264](https://github.com/matrix-org/matrix-doc/pull/2264). ([\#5974](https://github.com/matrix-org/synapse/issues/5974)) - Use the v2 Identity Service API for 3PID invites. ([\#5979](https://github.com/matrix-org/synapse/issues/5979)) - Add `POST /_matrix/client/unstable/account/3pid/unbind` endpoint from [MSC2140](https://github.com/matrix-org/matrix-doc/pull/2140) for unbinding a 3PID from an identity server without removing it from the homeserver user account. ([\#5980](https://github.com/matrix-org/synapse/issues/5980), [\#6062](https://github.com/matrix-org/synapse/issues/6062)) - Use `account_threepid_delegate.email` and `account_threepid_delegate.msisdn` for validating threepid sessions. ([\#6011](https://github.com/matrix-org/synapse/issues/6011)) - Allow homeserver to handle or delegate email validation when adding an email to a user's account. ([\#6042](https://github.com/matrix-org/synapse/issues/6042)) - Implement new Client Server API endpoints `/account/3pid/add` and `/account/3pid/bind` as per [MSC2290](https://github.com/matrix-org/matrix-doc/pull/2290). ([\#6043](https://github.com/matrix-org/synapse/issues/6043)) - Add an unstable feature flag for separate add/bind 3pid APIs. ([\#6044](https://github.com/matrix-org/synapse/issues/6044)) - Remove `bind` parameter from Client Server POST `/account` endpoint as per [MSC2290](https://github.com/matrix-org/matrix-doc/pull/2290/). ([\#6067](https://github.com/matrix-org/synapse/issues/6067)) - Add `POST /add_threepid/msisdn/submit_token` endpoint for proxying submitToken on an `account_threepid_handler`. ([\#6078](https://github.com/matrix-org/synapse/issues/6078)) - Add `submit_url` response parameter to `*/msisdn/requestToken` endpoints. ([\#6079](https://github.com/matrix-org/synapse/issues/6079)) - Add `m.require_identity_server` flag to /version's unstable_features. ([\#5972](https://github.com/matrix-org/synapse/issues/5972)) - Enhancements to OpenTracing support: - Make OpenTracing work in worker mode. ([\#5771](https://github.com/matrix-org/synapse/issues/5771)) - Pass OpenTracing contexts between servers when transmitting EDUs. ([\#5852](https://github.com/matrix-org/synapse/issues/5852)) - OpenTracing for device list updates. ([\#5853](https://github.com/matrix-org/synapse/issues/5853)) - Add a tag recording a request's authenticated entity and corresponding servlet in OpenTracing. ([\#5856](https://github.com/matrix-org/synapse/issues/5856)) - Add minimum OpenTracing for client servlets. ([\#5983](https://github.com/matrix-org/synapse/issues/5983)) - Check at setup that OpenTracing is installed if it's enabled in the config. ([\#5985](https://github.com/matrix-org/synapse/issues/5985)) - Trace replication send times. ([\#5986](https://github.com/matrix-org/synapse/issues/5986)) - Include missing OpenTracing contexts in outbout replication requests. ([\#5982](https://github.com/matrix-org/synapse/issues/5982)) - Fix sending of EDUs when OpenTracing is enabled with an empty whitelist. ([\#5984](https://github.com/matrix-org/synapse/issues/5984)) - Fix invalid references to None while OpenTracing if the log context slips. ([\#5988](https://github.com/matrix-org/synapse/issues/5988), [\#5991](https://github.com/matrix-org/synapse/issues/5991)) - OpenTracing for room and e2e keys. ([\#5855](https://github.com/matrix-org/synapse/issues/5855)) - Add OpenTracing span over HTTP push processing. ([\#6003](https://github.com/matrix-org/synapse/issues/6003)) - Add an admin API to purge old rooms from the database. ([\#5845](https://github.com/matrix-org/synapse/issues/5845)) - Retry well-known lookups if we have recently seen a valid well-known record for the server. ([\#5850](https://github.com/matrix-org/synapse/issues/5850)) - Add support for filtered room-directory search requests over federation ([MSC2197](https://github.com/matrix-org/matrix-doc/pull/2197), in order to allow upcoming room directory query performance improvements. ([\#5859](https://github.com/matrix-org/synapse/issues/5859)) - Correctly retry all hosts returned from SRV when we fail to connect. ([\#5864](https://github.com/matrix-org/synapse/issues/5864)) - Add admin API endpoint for setting whether or not a user is a server administrator. ([\#5878](https://github.com/matrix-org/synapse/issues/5878)) - Enable cleaning up extremities with dummy events by default to prevent undue build up of forward extremities. ([\#5884](https://github.com/matrix-org/synapse/issues/5884)) - Add config option to sign remote key query responses with a separate key. ([\#5895](https://github.com/matrix-org/synapse/issues/5895)) - Add support for config templating. ([\#5900](https://github.com/matrix-org/synapse/issues/5900)) - Users with the type of "support" or "bot" are no longer required to consent. ([\#5902](https://github.com/matrix-org/synapse/issues/5902)) - Let synctl accept a directory of config files. ([\#5904](https://github.com/matrix-org/synapse/issues/5904)) - Increase max display name size to 256. ([\#5906](https://github.com/matrix-org/synapse/issues/5906)) - Add admin API endpoint for getting whether or not a user is a server administrator. ([\#5914](https://github.com/matrix-org/synapse/issues/5914)) - Redact events in the database that have been redacted for a week. ([\#5934](https://github.com/matrix-org/synapse/issues/5934)) - New prometheus metrics: - `synapse_federation_known_servers`: represents the total number of servers your server knows about (i.e. is in rooms with), including itself. Enable by setting `metrics_flags.known_servers` to True in the configuration.([\#5981](https://github.com/matrix-org/synapse/issues/5981)) - `synapse_build_info`: exposes the Python version, OS version, and Synapse version of the running server. ([\#6005](https://github.com/matrix-org/synapse/issues/6005)) - Give appropriate exit codes when synctl fails. ([\#5992](https://github.com/matrix-org/synapse/issues/5992)) - Apply the federation blacklist to requests to identity servers. ([\#6000](https://github.com/matrix-org/synapse/issues/6000)) - Add `report_stats_endpoint` option to configure where stats are reported to, if enabled. Contributed by @Sorunome. ([\#6012](https://github.com/matrix-org/synapse/issues/6012)) - Add config option to increase ratelimits for room admins redacting messages. ([\#6015](https://github.com/matrix-org/synapse/issues/6015)) - Stop sending federation transactions to servers which have been down for a long time. ([\#6026](https://github.com/matrix-org/synapse/issues/6026)) - Make the process for mapping SAML2 users to matrix IDs more flexible. ([\#6037](https://github.com/matrix-org/synapse/issues/6037)) - Return a clearer error message when a timeout occurs when attempting to contact an identity server. ([\#6073](https://github.com/matrix-org/synapse/issues/6073)) - Prevent password reset's submit_token endpoint from accepting trailing slashes. ([\#6074](https://github.com/matrix-org/synapse/issues/6074)) - Return 403 on `/register/available` if registration has been disabled. ([\#6082](https://github.com/matrix-org/synapse/issues/6082)) - Explicitly log when a homeserver does not have the `trusted_key_servers` config field configured. ([\#6090](https://github.com/matrix-org/synapse/issues/6090)) - Add support for pruning old rows in `user_ips` table. ([\#6098](https://github.com/matrix-org/synapse/issues/6098)) Bugfixes -------- - Don't create broken room when `power_level_content_override.users` does not contain `creator_id`. ([\#5633](https://github.com/matrix-org/synapse/issues/5633)) - Fix database index so that different backup versions can have the same sessions. ([\#5857](https://github.com/matrix-org/synapse/issues/5857)) - Fix Synapse looking for config options `password_reset_failure_template` and `password_reset_success_template`, when they are actually `password_reset_template_failure_html`, `password_reset_template_success_html`. ([\#5863](https://github.com/matrix-org/synapse/issues/5863)) - Fix stack overflow when recovering an appservice which had an outage. ([\#5885](https://github.com/matrix-org/synapse/issues/5885)) - Fix error message which referred to `public_base_url` instead of `public_baseurl`. Thanks to @aaronraimist for the fix! ([\#5909](https://github.com/matrix-org/synapse/issues/5909)) - Fix 404 for thumbnail download when `dynamic_thumbnails` is `false` and the thumbnail was dynamically generated. Fix reported by rkfg. ([\#5915](https://github.com/matrix-org/synapse/issues/5915)) - Fix a cache-invalidation bug for worker-based deployments. ([\#5920](https://github.com/matrix-org/synapse/issues/5920)) - Fix admin API for listing media in a room not being available with an external media repo. ([\#5966](https://github.com/matrix-org/synapse/issues/5966)) - Fix list media admin API always returning an error. ([\#5967](https://github.com/matrix-org/synapse/issues/5967)) - Fix room and user stats tracking. ([\#5971](https://github.com/matrix-org/synapse/issues/5971), [\#5998](https://github.com/matrix-org/synapse/issues/5998), [\#6029](https://github.com/matrix-org/synapse/issues/6029)) - Return a `M_MISSING_PARAM` if `sid` is not provided to `/account/3pid`. ([\#5995](https://github.com/matrix-org/synapse/issues/5995)) - `federation_certificate_verification_whitelist` now will not cause `TypeErrors` to be raised (a regression in 1.3). Additionally, it now supports internationalised domain names in their non-canonical representation. ([\#5996](https://github.com/matrix-org/synapse/issues/5996)) - Only count real users when checking for auto-creation of auto-join room. ([\#6004](https://github.com/matrix-org/synapse/issues/6004)) - Ensure support users can be registered even if MAU limit is reached. ([\#6020](https://github.com/matrix-org/synapse/issues/6020)) - Fix bug where login error was shown incorrectly on SSO fallback login. ([\#6024](https://github.com/matrix-org/synapse/issues/6024)) - Fix bug in calculating the federation retry backoff period. ([\#6025](https://github.com/matrix-org/synapse/issues/6025)) - Prevent exceptions being logged when extremity-cleanup events fail due to lack of user consent to the terms of service. ([\#6053](https://github.com/matrix-org/synapse/issues/6053)) - Remove POST method from password-reset `submit_token` endpoint until we implement `submit_url` functionality. ([\#6056](https://github.com/matrix-org/synapse/issues/6056)) - Fix logcontext spam on non-Linux platforms. ([\#6059](https://github.com/matrix-org/synapse/issues/6059)) - Ensure query parameters in email validation links are URL-encoded. ([\#6063](https://github.com/matrix-org/synapse/issues/6063)) - Fix a bug which caused SAML attribute maps to be overridden by defaults. ([\#6069](https://github.com/matrix-org/synapse/issues/6069)) - Fix the logged number of updated items for the `users_set_deactivated_flag` background update. ([\#6092](https://github.com/matrix-org/synapse/issues/6092)) - Add `sid` to `next_link` for email validation. ([\#6097](https://github.com/matrix-org/synapse/issues/6097)) - Threepid validity checks on msisdns should not be dependent on `threepid_behaviour_email`. ([\#6104](https://github.com/matrix-org/synapse/issues/6104)) - Ensure that servers which are not configured to support email address verification do not offer it in the registration flows. ([\#6107](https://github.com/matrix-org/synapse/issues/6107)) Updates to the Docker image --------------------------- - Avoid changing `UID/GID` if they are already correct. ([\#5970](https://github.com/matrix-org/synapse/issues/5970)) - Provide `SYNAPSE_WORKER` envvar to specify python module. ([\#6058](https://github.com/matrix-org/synapse/issues/6058)) Improved Documentation ---------------------- - Convert documentation to markdown (from rst) ([\#5849](https://github.com/matrix-org/synapse/issues/5849)) - Update `INSTALL.md` to say that Python 2 is no longer supported. ([\#5953](https://github.com/matrix-org/synapse/issues/5953)) - Add developer documentation for using SAML2. ([\#6032](https://github.com/matrix-org/synapse/issues/6032)) - Add some notes on rolling back to v1.3.1. ([\#6049](https://github.com/matrix-org/synapse/issues/6049)) - Update the upgrade notes. ([\#6050](https://github.com/matrix-org/synapse/issues/6050)) Deprecations and Removals ------------------------- - Remove shared-secret registration from `/_matrix/client/r0/register` endpoint. Contributed by Awesome Technologies Innovationslabor GmbH. ([\#5877](https://github.com/matrix-org/synapse/issues/5877)) - Deprecate the `trusted_third_party_id_servers` option. ([\#5875](https://github.com/matrix-org/synapse/issues/5875)) Internal Changes ---------------- - Lay the groundwork for structured logging output. ([\#5680](https://github.com/matrix-org/synapse/issues/5680)) - Retry well-known lookup before the cache expires, giving a grace period where the remote well-known can be down but we still use the old result. ([\#5844](https://github.com/matrix-org/synapse/issues/5844)) - Remove log line for debugging issue #5407. ([\#5860](https://github.com/matrix-org/synapse/issues/5860)) - Refactor the Appservice scheduler code. ([\#5886](https://github.com/matrix-org/synapse/issues/5886)) - Compatibility with v2 Identity Service APIs other than /lookup. ([\#5892](https://github.com/matrix-org/synapse/issues/5892), [\#6013](https://github.com/matrix-org/synapse/issues/6013)) - Stop populating some unused tables. ([\#5893](https://github.com/matrix-org/synapse/issues/5893), [\#6047](https://github.com/matrix-org/synapse/issues/6047)) - Add missing index on `users_in_public_rooms` to improve the performance of directory queries. ([\#5894](https://github.com/matrix-org/synapse/issues/5894)) - Improve the logging when we have an error when fetching signing keys. ([\#5896](https://github.com/matrix-org/synapse/issues/5896)) - Add support for database engine-specific schema deltas, based on file extension. ([\#5911](https://github.com/matrix-org/synapse/issues/5911)) - Update Buildkite pipeline to use plugins instead of buildkite-agent commands. ([\#5922](https://github.com/matrix-org/synapse/issues/5922)) - Add link in sample config to the logging config schema. ([\#5926](https://github.com/matrix-org/synapse/issues/5926)) - Remove unnecessary parentheses in return statements. ([\#5931](https://github.com/matrix-org/synapse/issues/5931)) - Remove unused `jenkins/prepare_sytest.sh` file. ([\#5938](https://github.com/matrix-org/synapse/issues/5938)) - Move Buildkite pipeline config to the pipelines repo. ([\#5943](https://github.com/matrix-org/synapse/issues/5943)) - Remove unnecessary return statements in the codebase which were the result of a regex run. ([\#5962](https://github.com/matrix-org/synapse/issues/5962)) - Remove left-over methods from v1 registration API. ([\#5963](https://github.com/matrix-org/synapse/issues/5963)) - Cleanup event auth type initialisation. ([\#5975](https://github.com/matrix-org/synapse/issues/5975)) - Clean up dependency checking at setup. ([\#5989](https://github.com/matrix-org/synapse/issues/5989)) - Update OpenTracing docs to use the unified `trace` method. ([\#5776](https://github.com/matrix-org/synapse/issues/5776)) - Small refactor of function arguments and docstrings in` RoomMemberHandler`. ([\#6009](https://github.com/matrix-org/synapse/issues/6009)) - Remove unused `origin` argument on `FederationHandler.add_display_name_to_third_party_invite`. ([\#6010](https://github.com/matrix-org/synapse/issues/6010)) - Add a `failure_ts` column to the `destinations` database table. ([\#6016](https://github.com/matrix-org/synapse/issues/6016), [\#6072](https://github.com/matrix-org/synapse/issues/6072)) - Clean up some code in the retry logic. ([\#6017](https://github.com/matrix-org/synapse/issues/6017)) - Fix the structured logging tests stomping on the global log configuration for subsequent tests. ([\#6023](https://github.com/matrix-org/synapse/issues/6023)) - Clean up the sample config for SAML authentication. ([\#6064](https://github.com/matrix-org/synapse/issues/6064)) - Change mailer logging to reflect Synapse doesn't just do chat notifications by email now. ([\#6075](https://github.com/matrix-org/synapse/issues/6075)) - Move last-seen info into devices table. ([\#6089](https://github.com/matrix-org/synapse/issues/6089)) - Remove unused parameter to `get_user_id_by_threepid`. ([\#6099](https://github.com/matrix-org/synapse/issues/6099)) - Refactor the user-interactive auth handling. ([\#6105](https://github.com/matrix-org/synapse/issues/6105)) - Refactor code for calculating registration flows. ([\#6106](https://github.com/matrix-org/synapse/issues/6106)) -----BEGIN PGP SIGNATURE----- iQJHBAABCgAxFiEEgQG31Z317NrSMt0QiISIDS7+X/QFAl2V6NsTHGFuZHJld0Bh bW9yZ2FuLnh5egAKCRCIhIgNLv5f9IUTEACE/gPcItzKAOsnEjCyBVzjwXcvqdZU 5FpEbnDqhVS20GBOctl+00QqHOeje8tDLmYy4yS/LH89iX+PQNI55M/4bmS5ZTYm x13AsxgqJ11xDOPmkzLkXDF0HnzZngGeGSShsPFUUWfMF9pcCz+kMY88dJe69sfU TK/VGIvp4MAcbeIbtgbInS7ZH1igntBLyfp48VQ0WCz6HeorJSxniRDKpRQ08Mc3 TbZsFZ/2ajvL+HSr6iSDUD32GTEfUERVpV7OrQnC9laa0Aidq0yRCMcRpBWJ8omP g/240jDxsbSZkNe+6GPuiRjqFQuHw0KVb/A10BJRvEDCgVzIdsE5oEQLr3HKl2h3 q1HQewuc2tG/ns4vMHK+dhEVlc95yUI1UxrNafP0/hhpWRiRX1VfiWv08tKNP5GJ 7an7Oqah4yZWjyzVBhiMugmQnnaf2z5TymUaRcZRNfueTpUo4DfJ3as71JcOmIGc Hg7gM1DOkLUMV5xoieW+7dKUqO8Nqv8y+q7qbK3LRT83HeE3AHXf3JmfB7/6+zi0 4coM5ZrGrp5A4PeGDVKPOT3VdmXK8r6buPqcJxUMaWAMziWGAEWROrGF+khvpUlW NkLAF/XitjqIlDQEWpUzHP7vGXNt9m83/9J8wD4/ZAjcUDfW1KSSCbBmCR4s5ZAk HVzNubTzKy2xlA== =2TFh -----END PGP SIGNATURE----- Merge tag 'v1.4.0' Synapse 1.4.0 (2019-10-03) ========================== Bugfixes -------- - Redact `client_secret` in server logs. ([\#6158](https://github.com/matrix-org/synapse/issues/6158)) Synapse 1.4.0rc2 (2019-10-02) ============================= Bugfixes -------- - Fix bug in background update that adds last seen information to the `devices` table, and improve its performance on Postgres. ([\#6135](https://github.com/matrix-org/synapse/issues/6135)) - Fix bad performance of censoring redactions background task. ([\#6141](https://github.com/matrix-org/synapse/issues/6141)) - Fix fetching censored redactions from DB, which caused APIs like initial sync to fail if it tried to include the censored redaction. ([\#6145](https://github.com/matrix-org/synapse/issues/6145)) - Fix exceptions when storing large retry intervals for down remote servers. ([\#6146](https://github.com/matrix-org/synapse/issues/6146)) Internal Changes ---------------- - Fix up sample config entry for `redaction_retention_period` option. ([\#6117](https://github.com/matrix-org/synapse/issues/6117)) Synapse 1.4.0rc1 (2019-09-26) ============================= Note that this release includes significant changes around 3pid verification. Administrators are reminded to review the [upgrade notes](UPGRADE.rst#upgrading-to-v140). Features -------- - Changes to 3pid verification: - Add the ability to send registration emails from the homeserver rather than delegating to an identity server. ([\#5835](https://github.com/matrix-org/synapse/issues/5835), [\#5940](https://github.com/matrix-org/synapse/issues/5940), [\#5993](https://github.com/matrix-org/synapse/issues/5993), [\#5994](https://github.com/matrix-org/synapse/issues/5994), [\#5868](https://github.com/matrix-org/synapse/issues/5868)) - Replace `trust_identity_server_for_password_resets` config option with `account_threepid_delegates`, and make the `id_server` parameteter optional on `*/requestToken` endpoints, as per [MSC2263](https://github.com/matrix-org/matrix-doc/pull/2263). ([\#5876](https://github.com/matrix-org/synapse/issues/5876), [\#5969](https://github.com/matrix-org/synapse/issues/5969), [\#6028](https://github.com/matrix-org/synapse/issues/6028)) - Switch to using the v2 Identity Service `/lookup` API where available, with fallback to v1. (Implements [MSC2134](https://github.com/matrix-org/matrix-doc/pull/2134) plus `id_access_token authentication` for v2 Identity Service APIs from [MSC2140](https://github.com/matrix-org/matrix-doc/pull/2140)). ([\#5897](https://github.com/matrix-org/synapse/issues/5897)) - Remove `bind_email` and `bind_msisdn` parameters from `/register` ala [MSC2140](https://github.com/matrix-org/matrix-doc/pull/2140). ([\#5964](https://github.com/matrix-org/synapse/issues/5964)) - Add `m.id_access_token` to `unstable_features` in `/versions` as per [MSC2264](https://github.com/matrix-org/matrix-doc/pull/2264). ([\#5974](https://github.com/matrix-org/synapse/issues/5974)) - Use the v2 Identity Service API for 3PID invites. ([\#5979](https://github.com/matrix-org/synapse/issues/5979)) - Add `POST /_matrix/client/unstable/account/3pid/unbind` endpoint from [MSC2140](https://github.com/matrix-org/matrix-doc/pull/2140) for unbinding a 3PID from an identity server without removing it from the homeserver user account. ([\#5980](https://github.com/matrix-org/synapse/issues/5980), [\#6062](https://github.com/matrix-org/synapse/issues/6062)) - Use `account_threepid_delegate.email` and `account_threepid_delegate.msisdn` for validating threepid sessions. ([\#6011](https://github.com/matrix-org/synapse/issues/6011)) - Allow homeserver to handle or delegate email validation when adding an email to a user's account. ([\#6042](https://github.com/matrix-org/synapse/issues/6042)) - Implement new Client Server API endpoints `/account/3pid/add` and `/account/3pid/bind` as per [MSC2290](https://github.com/matrix-org/matrix-doc/pull/2290). ([\#6043](https://github.com/matrix-org/synapse/issues/6043)) - Add an unstable feature flag for separate add/bind 3pid APIs. ([\#6044](https://github.com/matrix-org/synapse/issues/6044)) - Remove `bind` parameter from Client Server POST `/account` endpoint as per [MSC2290](https://github.com/matrix-org/matrix-doc/pull/2290/). ([\#6067](https://github.com/matrix-org/synapse/issues/6067)) - Add `POST /add_threepid/msisdn/submit_token` endpoint for proxying submitToken on an `account_threepid_handler`. ([\#6078](https://github.com/matrix-org/synapse/issues/6078)) - Add `submit_url` response parameter to `*/msisdn/requestToken` endpoints. ([\#6079](https://github.com/matrix-org/synapse/issues/6079)) - Add `m.require_identity_server` flag to /version's unstable_features. ([\#5972](https://github.com/matrix-org/synapse/issues/5972)) - Enhancements to OpenTracing support: - Make OpenTracing work in worker mode. ([\#5771](https://github.com/matrix-org/synapse/issues/5771)) - Pass OpenTracing contexts between servers when transmitting EDUs. ([\#5852](https://github.com/matrix-org/synapse/issues/5852)) - OpenTracing for device list updates. ([\#5853](https://github.com/matrix-org/synapse/issues/5853)) - Add a tag recording a request's authenticated entity and corresponding servlet in OpenTracing. ([\#5856](https://github.com/matrix-org/synapse/issues/5856)) - Add minimum OpenTracing for client servlets. ([\#5983](https://github.com/matrix-org/synapse/issues/5983)) - Check at setup that OpenTracing is installed if it's enabled in the config. ([\#5985](https://github.com/matrix-org/synapse/issues/5985)) - Trace replication send times. ([\#5986](https://github.com/matrix-org/synapse/issues/5986)) - Include missing OpenTracing contexts in outbout replication requests. ([\#5982](https://github.com/matrix-org/synapse/issues/5982)) - Fix sending of EDUs when OpenTracing is enabled with an empty whitelist. ([\#5984](https://github.com/matrix-org/synapse/issues/5984)) - Fix invalid references to None while OpenTracing if the log context slips. ([\#5988](https://github.com/matrix-org/synapse/issues/5988), [\#5991](https://github.com/matrix-org/synapse/issues/5991)) - OpenTracing for room and e2e keys. ([\#5855](https://github.com/matrix-org/synapse/issues/5855)) - Add OpenTracing span over HTTP push processing. ([\#6003](https://github.com/matrix-org/synapse/issues/6003)) - Add an admin API to purge old rooms from the database. ([\#5845](https://github.com/matrix-org/synapse/issues/5845)) - Retry well-known lookups if we have recently seen a valid well-known record for the server. ([\#5850](https://github.com/matrix-org/synapse/issues/5850)) - Add support for filtered room-directory search requests over federation ([MSC2197](https://github.com/matrix-org/matrix-doc/pull/2197), in order to allow upcoming room directory query performance improvements. ([\#5859](https://github.com/matrix-org/synapse/issues/5859)) - Correctly retry all hosts returned from SRV when we fail to connect. ([\#5864](https://github.com/matrix-org/synapse/issues/5864)) - Add admin API endpoint for setting whether or not a user is a server administrator. ([\#5878](https://github.com/matrix-org/synapse/issues/5878)) - Enable cleaning up extremities with dummy events by default to prevent undue build up of forward extremities. ([\#5884](https://github.com/matrix-org/synapse/issues/5884)) - Add config option to sign remote key query responses with a separate key. ([\#5895](https://github.com/matrix-org/synapse/issues/5895)) - Add support for config templating. ([\#5900](https://github.com/matrix-org/synapse/issues/5900)) - Users with the type of "support" or "bot" are no longer required to consent. ([\#5902](https://github.com/matrix-org/synapse/issues/5902)) - Let synctl accept a directory of config files. ([\#5904](https://github.com/matrix-org/synapse/issues/5904)) - Increase max display name size to 256. ([\#5906](https://github.com/matrix-org/synapse/issues/5906)) - Add admin API endpoint for getting whether or not a user is a server administrator. ([\#5914](https://github.com/matrix-org/synapse/issues/5914)) - Redact events in the database that have been redacted for a week. ([\#5934](https://github.com/matrix-org/synapse/issues/5934)) - New prometheus metrics: - `synapse_federation_known_servers`: represents the total number of servers your server knows about (i.e. is in rooms with), including itself. Enable by setting `metrics_flags.known_servers` to True in the configuration.([\#5981](https://github.com/matrix-org/synapse/issues/5981)) - `synapse_build_info`: exposes the Python version, OS version, and Synapse version of the running server. ([\#6005](https://github.com/matrix-org/synapse/issues/6005)) - Give appropriate exit codes when synctl fails. ([\#5992](https://github.com/matrix-org/synapse/issues/5992)) - Apply the federation blacklist to requests to identity servers. ([\#6000](https://github.com/matrix-org/synapse/issues/6000)) - Add `report_stats_endpoint` option to configure where stats are reported to, if enabled. Contributed by @Sorunome. ([\#6012](https://github.com/matrix-org/synapse/issues/6012)) - Add config option to increase ratelimits for room admins redacting messages. ([\#6015](https://github.com/matrix-org/synapse/issues/6015)) - Stop sending federation transactions to servers which have been down for a long time. ([\#6026](https://github.com/matrix-org/synapse/issues/6026)) - Make the process for mapping SAML2 users to matrix IDs more flexible. ([\#6037](https://github.com/matrix-org/synapse/issues/6037)) - Return a clearer error message when a timeout occurs when attempting to contact an identity server. ([\#6073](https://github.com/matrix-org/synapse/issues/6073)) - Prevent password reset's submit_token endpoint from accepting trailing slashes. ([\#6074](https://github.com/matrix-org/synapse/issues/6074)) - Return 403 on `/register/available` if registration has been disabled. ([\#6082](https://github.com/matrix-org/synapse/issues/6082)) - Explicitly log when a homeserver does not have the `trusted_key_servers` config field configured. ([\#6090](https://github.com/matrix-org/synapse/issues/6090)) - Add support for pruning old rows in `user_ips` table. ([\#6098](https://github.com/matrix-org/synapse/issues/6098)) Bugfixes -------- - Don't create broken room when `power_level_content_override.users` does not contain `creator_id`. ([\#5633](https://github.com/matrix-org/synapse/issues/5633)) - Fix database index so that different backup versions can have the same sessions. ([\#5857](https://github.com/matrix-org/synapse/issues/5857)) - Fix Synapse looking for config options `password_reset_failure_template` and `password_reset_success_template`, when they are actually `password_reset_template_failure_html`, `password_reset_template_success_html`. ([\#5863](https://github.com/matrix-org/synapse/issues/5863)) - Fix stack overflow when recovering an appservice which had an outage. ([\#5885](https://github.com/matrix-org/synapse/issues/5885)) - Fix error message which referred to `public_base_url` instead of `public_baseurl`. Thanks to @aaronraimist for the fix! ([\#5909](https://github.com/matrix-org/synapse/issues/5909)) - Fix 404 for thumbnail download when `dynamic_thumbnails` is `false` and the thumbnail was dynamically generated. Fix reported by rkfg. ([\#5915](https://github.com/matrix-org/synapse/issues/5915)) - Fix a cache-invalidation bug for worker-based deployments. ([\#5920](https://github.com/matrix-org/synapse/issues/5920)) - Fix admin API for listing media in a room not being available with an external media repo. ([\#5966](https://github.com/matrix-org/synapse/issues/5966)) - Fix list media admin API always returning an error. ([\#5967](https://github.com/matrix-org/synapse/issues/5967)) - Fix room and user stats tracking. ([\#5971](https://github.com/matrix-org/synapse/issues/5971), [\#5998](https://github.com/matrix-org/synapse/issues/5998), [\#6029](https://github.com/matrix-org/synapse/issues/6029)) - Return a `M_MISSING_PARAM` if `sid` is not provided to `/account/3pid`. ([\#5995](https://github.com/matrix-org/synapse/issues/5995)) - `federation_certificate_verification_whitelist` now will not cause `TypeErrors` to be raised (a regression in 1.3). Additionally, it now supports internationalised domain names in their non-canonical representation. ([\#5996](https://github.com/matrix-org/synapse/issues/5996)) - Only count real users when checking for auto-creation of auto-join room. ([\#6004](https://github.com/matrix-org/synapse/issues/6004)) - Ensure support users can be registered even if MAU limit is reached. ([\#6020](https://github.com/matrix-org/synapse/issues/6020)) - Fix bug where login error was shown incorrectly on SSO fallback login. ([\#6024](https://github.com/matrix-org/synapse/issues/6024)) - Fix bug in calculating the federation retry backoff period. ([\#6025](https://github.com/matrix-org/synapse/issues/6025)) - Prevent exceptions being logged when extremity-cleanup events fail due to lack of user consent to the terms of service. ([\#6053](https://github.com/matrix-org/synapse/issues/6053)) - Remove POST method from password-reset `submit_token` endpoint until we implement `submit_url` functionality. ([\#6056](https://github.com/matrix-org/synapse/issues/6056)) - Fix logcontext spam on non-Linux platforms. ([\#6059](https://github.com/matrix-org/synapse/issues/6059)) - Ensure query parameters in email validation links are URL-encoded. ([\#6063](https://github.com/matrix-org/synapse/issues/6063)) - Fix a bug which caused SAML attribute maps to be overridden by defaults. ([\#6069](https://github.com/matrix-org/synapse/issues/6069)) - Fix the logged number of updated items for the `users_set_deactivated_flag` background update. ([\#6092](https://github.com/matrix-org/synapse/issues/6092)) - Add `sid` to `next_link` for email validation. ([\#6097](https://github.com/matrix-org/synapse/issues/6097)) - Threepid validity checks on msisdns should not be dependent on `threepid_behaviour_email`. ([\#6104](https://github.com/matrix-org/synapse/issues/6104)) - Ensure that servers which are not configured to support email address verification do not offer it in the registration flows. ([\#6107](https://github.com/matrix-org/synapse/issues/6107)) Updates to the Docker image --------------------------- - Avoid changing `UID/GID` if they are already correct. ([\#5970](https://github.com/matrix-org/synapse/issues/5970)) - Provide `SYNAPSE_WORKER` envvar to specify python module. ([\#6058](https://github.com/matrix-org/synapse/issues/6058)) Improved Documentation ---------------------- - Convert documentation to markdown (from rst) ([\#5849](https://github.com/matrix-org/synapse/issues/5849)) - Update `INSTALL.md` to say that Python 2 is no longer supported. ([\#5953](https://github.com/matrix-org/synapse/issues/5953)) - Add developer documentation for using SAML2. ([\#6032](https://github.com/matrix-org/synapse/issues/6032)) - Add some notes on rolling back to v1.3.1. ([\#6049](https://github.com/matrix-org/synapse/issues/6049)) - Update the upgrade notes. ([\#6050](https://github.com/matrix-org/synapse/issues/6050)) Deprecations and Removals ------------------------- - Remove shared-secret registration from `/_matrix/client/r0/register` endpoint. Contributed by Awesome Technologies Innovationslabor GmbH. ([\#5877](https://github.com/matrix-org/synapse/issues/5877)) - Deprecate the `trusted_third_party_id_servers` option. ([\#5875](https://github.com/matrix-org/synapse/issues/5875)) Internal Changes ---------------- - Lay the groundwork for structured logging output. ([\#5680](https://github.com/matrix-org/synapse/issues/5680)) - Retry well-known lookup before the cache expires, giving a grace period where the remote well-known can be down but we still use the old result. ([\#5844](https://github.com/matrix-org/synapse/issues/5844)) - Remove log line for debugging issue #5407. ([\#5860](https://github.com/matrix-org/synapse/issues/5860)) - Refactor the Appservice scheduler code. ([\#5886](https://github.com/matrix-org/synapse/issues/5886)) - Compatibility with v2 Identity Service APIs other than /lookup. ([\#5892](https://github.com/matrix-org/synapse/issues/5892), [\#6013](https://github.com/matrix-org/synapse/issues/6013)) - Stop populating some unused tables. ([\#5893](https://github.com/matrix-org/synapse/issues/5893), [\#6047](https://github.com/matrix-org/synapse/issues/6047)) - Add missing index on `users_in_public_rooms` to improve the performance of directory queries. ([\#5894](https://github.com/matrix-org/synapse/issues/5894)) - Improve the logging when we have an error when fetching signing keys. ([\#5896](https://github.com/matrix-org/synapse/issues/5896)) - Add support for database engine-specific schema deltas, based on file extension. ([\#5911](https://github.com/matrix-org/synapse/issues/5911)) - Update Buildkite pipeline to use plugins instead of buildkite-agent commands. ([\#5922](https://github.com/matrix-org/synapse/issues/5922)) - Add link in sample config to the logging config schema. ([\#5926](https://github.com/matrix-org/synapse/issues/5926)) - Remove unnecessary parentheses in return statements. ([\#5931](https://github.com/matrix-org/synapse/issues/5931)) - Remove unused `jenkins/prepare_sytest.sh` file. ([\#5938](https://github.com/matrix-org/synapse/issues/5938)) - Move Buildkite pipeline config to the pipelines repo. ([\#5943](https://github.com/matrix-org/synapse/issues/5943)) - Remove unnecessary return statements in the codebase which were the result of a regex run. ([\#5962](https://github.com/matrix-org/synapse/issues/5962)) - Remove left-over methods from v1 registration API. ([\#5963](https://github.com/matrix-org/synapse/issues/5963)) - Cleanup event auth type initialisation. ([\#5975](https://github.com/matrix-org/synapse/issues/5975)) - Clean up dependency checking at setup. ([\#5989](https://github.com/matrix-org/synapse/issues/5989)) - Update OpenTracing docs to use the unified `trace` method. ([\#5776](https://github.com/matrix-org/synapse/issues/5776)) - Small refactor of function arguments and docstrings in` RoomMemberHandler`. ([\#6009](https://github.com/matrix-org/synapse/issues/6009)) - Remove unused `origin` argument on `FederationHandler.add_display_name_to_third_party_invite`. ([\#6010](https://github.com/matrix-org/synapse/issues/6010)) - Add a `failure_ts` column to the `destinations` database table. ([\#6016](https://github.com/matrix-org/synapse/issues/6016), [\#6072](https://github.com/matrix-org/synapse/issues/6072)) - Clean up some code in the retry logic. ([\#6017](https://github.com/matrix-org/synapse/issues/6017)) - Fix the structured logging tests stomping on the global log configuration for subsequent tests. ([\#6023](https://github.com/matrix-org/synapse/issues/6023)) - Clean up the sample config for SAML authentication. ([\#6064](https://github.com/matrix-org/synapse/issues/6064)) - Change mailer logging to reflect Synapse doesn't just do chat notifications by email now. ([\#6075](https://github.com/matrix-org/synapse/issues/6075)) - Move last-seen info into devices table. ([\#6089](https://github.com/matrix-org/synapse/issues/6089)) - Remove unused parameter to `get_user_id_by_threepid`. ([\#6099](https://github.com/matrix-org/synapse/issues/6099)) - Refactor the user-interactive auth handling. ([\#6105](https://github.com/matrix-org/synapse/issues/6105)) - Refactor code for calculating registration flows. ([\#6106](https://github.com/matrix-org/synapse/issues/6106))
This commit is contained in:
commit
782dd72037
|
@ -6,6 +6,7 @@ services:
|
||||||
image: postgres:9.5
|
image: postgres:9.5
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_PASSWORD: postgres
|
POSTGRES_PASSWORD: postgres
|
||||||
|
command: -c fsync=off
|
||||||
|
|
||||||
testenv:
|
testenv:
|
||||||
image: python:3.5
|
image: python:3.5
|
||||||
|
@ -16,6 +17,6 @@ services:
|
||||||
SYNAPSE_POSTGRES_HOST: postgres
|
SYNAPSE_POSTGRES_HOST: postgres
|
||||||
SYNAPSE_POSTGRES_USER: postgres
|
SYNAPSE_POSTGRES_USER: postgres
|
||||||
SYNAPSE_POSTGRES_PASSWORD: postgres
|
SYNAPSE_POSTGRES_PASSWORD: postgres
|
||||||
working_dir: /app
|
working_dir: /src
|
||||||
volumes:
|
volumes:
|
||||||
- ..:/app
|
- ..:/src
|
||||||
|
|
|
@ -6,6 +6,7 @@ services:
|
||||||
image: postgres:11
|
image: postgres:11
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_PASSWORD: postgres
|
POSTGRES_PASSWORD: postgres
|
||||||
|
command: -c fsync=off
|
||||||
|
|
||||||
testenv:
|
testenv:
|
||||||
image: python:3.7
|
image: python:3.7
|
||||||
|
@ -16,6 +17,6 @@ services:
|
||||||
SYNAPSE_POSTGRES_HOST: postgres
|
SYNAPSE_POSTGRES_HOST: postgres
|
||||||
SYNAPSE_POSTGRES_USER: postgres
|
SYNAPSE_POSTGRES_USER: postgres
|
||||||
SYNAPSE_POSTGRES_PASSWORD: postgres
|
SYNAPSE_POSTGRES_PASSWORD: postgres
|
||||||
working_dir: /app
|
working_dir: /src
|
||||||
volumes:
|
volumes:
|
||||||
- ..:/app
|
- ..:/src
|
||||||
|
|
|
@ -6,6 +6,7 @@ services:
|
||||||
image: postgres:9.5
|
image: postgres:9.5
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_PASSWORD: postgres
|
POSTGRES_PASSWORD: postgres
|
||||||
|
command: -c fsync=off
|
||||||
|
|
||||||
testenv:
|
testenv:
|
||||||
image: python:3.7
|
image: python:3.7
|
||||||
|
@ -16,6 +17,6 @@ services:
|
||||||
SYNAPSE_POSTGRES_HOST: postgres
|
SYNAPSE_POSTGRES_HOST: postgres
|
||||||
SYNAPSE_POSTGRES_USER: postgres
|
SYNAPSE_POSTGRES_USER: postgres
|
||||||
SYNAPSE_POSTGRES_PASSWORD: postgres
|
SYNAPSE_POSTGRES_PASSWORD: postgres
|
||||||
working_dir: /app
|
working_dir: /src
|
||||||
volumes:
|
volumes:
|
||||||
- ..:/app
|
- ..:/src
|
||||||
|
|
|
@ -1,3 +1,18 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2019 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.
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
from tap.parser import Parser
|
from tap.parser import Parser
|
||||||
from tap.line import Result, Unknown, Diagnostic
|
from tap.line import Result, Unknown, Diagnostic
|
||||||
|
|
|
@ -27,7 +27,7 @@ git config --global user.name "A robot"
|
||||||
|
|
||||||
# Fetch and merge. If it doesn't work, it will raise due to set -e.
|
# Fetch and merge. If it doesn't work, it will raise due to set -e.
|
||||||
git fetch -u origin $GITBASE
|
git fetch -u origin $GITBASE
|
||||||
git merge --no-edit origin/$GITBASE
|
git merge --no-edit --no-commit origin/$GITBASE
|
||||||
|
|
||||||
# Show what we are after.
|
# Show what we are after.
|
||||||
git --no-pager show -s
|
git --no-pager show -s
|
||||||
|
|
|
@ -1,240 +0,0 @@
|
||||||
env:
|
|
||||||
CODECOV_TOKEN: "2dd7eb9b-0eda-45fe-a47c-9b5ac040045f"
|
|
||||||
|
|
||||||
steps:
|
|
||||||
|
|
||||||
- command:
|
|
||||||
- "python -m pip install tox"
|
|
||||||
- "tox -e check_codestyle"
|
|
||||||
label: "\U0001F9F9 Check Style"
|
|
||||||
plugins:
|
|
||||||
- docker#v3.0.1:
|
|
||||||
image: "python:3.6"
|
|
||||||
|
|
||||||
- command:
|
|
||||||
- "python -m pip install tox"
|
|
||||||
- "tox -e packaging"
|
|
||||||
label: "\U0001F9F9 packaging"
|
|
||||||
plugins:
|
|
||||||
- docker#v3.0.1:
|
|
||||||
image: "python:3.6"
|
|
||||||
|
|
||||||
- command:
|
|
||||||
- "python -m pip install tox"
|
|
||||||
- "tox -e check_isort"
|
|
||||||
label: "\U0001F9F9 isort"
|
|
||||||
plugins:
|
|
||||||
- docker#v3.0.1:
|
|
||||||
image: "python:3.6"
|
|
||||||
|
|
||||||
- command:
|
|
||||||
- "python -m pip install tox"
|
|
||||||
- "scripts-dev/check-newsfragment"
|
|
||||||
label: ":newspaper: Newsfile"
|
|
||||||
branches: "!master !develop !release-*"
|
|
||||||
plugins:
|
|
||||||
- docker#v3.0.1:
|
|
||||||
image: "python:3.6"
|
|
||||||
propagate-environment: true
|
|
||||||
|
|
||||||
- command:
|
|
||||||
- "python -m pip install tox"
|
|
||||||
- "tox -e check-sampleconfig"
|
|
||||||
label: "\U0001F9F9 check-sample-config"
|
|
||||||
plugins:
|
|
||||||
- docker#v3.0.1:
|
|
||||||
image: "python:3.6"
|
|
||||||
|
|
||||||
- wait
|
|
||||||
|
|
||||||
|
|
||||||
- command:
|
|
||||||
- "apt-get update && apt-get install -y python3.5 python3.5-dev python3-pip libxml2-dev libxslt-dev zlib1g-dev"
|
|
||||||
- "python3.5 -m pip install tox"
|
|
||||||
- "tox -e py35-old,codecov"
|
|
||||||
label: ":python: 3.5 / SQLite / Old Deps"
|
|
||||||
env:
|
|
||||||
TRIAL_FLAGS: "-j 2"
|
|
||||||
plugins:
|
|
||||||
- docker#v3.0.1:
|
|
||||||
image: "ubuntu:xenial" # We use xenail to get an old sqlite and python
|
|
||||||
propagate-environment: true
|
|
||||||
retry:
|
|
||||||
automatic:
|
|
||||||
- exit_status: -1
|
|
||||||
limit: 2
|
|
||||||
- exit_status: 2
|
|
||||||
limit: 2
|
|
||||||
|
|
||||||
- command:
|
|
||||||
- "python -m pip install tox"
|
|
||||||
- "tox -e py35,codecov"
|
|
||||||
label: ":python: 3.5 / SQLite"
|
|
||||||
env:
|
|
||||||
TRIAL_FLAGS: "-j 2"
|
|
||||||
plugins:
|
|
||||||
- docker#v3.0.1:
|
|
||||||
image: "python:3.5"
|
|
||||||
propagate-environment: true
|
|
||||||
retry:
|
|
||||||
automatic:
|
|
||||||
- exit_status: -1
|
|
||||||
limit: 2
|
|
||||||
- exit_status: 2
|
|
||||||
limit: 2
|
|
||||||
|
|
||||||
- command:
|
|
||||||
- "python -m pip install tox"
|
|
||||||
- "tox -e py36,codecov"
|
|
||||||
label: ":python: 3.6 / SQLite"
|
|
||||||
env:
|
|
||||||
TRIAL_FLAGS: "-j 2"
|
|
||||||
plugins:
|
|
||||||
- docker#v3.0.1:
|
|
||||||
image: "python:3.6"
|
|
||||||
propagate-environment: true
|
|
||||||
retry:
|
|
||||||
automatic:
|
|
||||||
- exit_status: -1
|
|
||||||
limit: 2
|
|
||||||
- exit_status: 2
|
|
||||||
limit: 2
|
|
||||||
|
|
||||||
- command:
|
|
||||||
- "python -m pip install tox"
|
|
||||||
- "tox -e py37,codecov"
|
|
||||||
label: ":python: 3.7 / SQLite"
|
|
||||||
env:
|
|
||||||
TRIAL_FLAGS: "-j 2"
|
|
||||||
plugins:
|
|
||||||
- docker#v3.0.1:
|
|
||||||
image: "python:3.7"
|
|
||||||
propagate-environment: true
|
|
||||||
retry:
|
|
||||||
automatic:
|
|
||||||
- exit_status: -1
|
|
||||||
limit: 2
|
|
||||||
- exit_status: 2
|
|
||||||
limit: 2
|
|
||||||
|
|
||||||
- label: ":python: 3.5 / :postgres: 9.5"
|
|
||||||
agents:
|
|
||||||
queue: "medium"
|
|
||||||
env:
|
|
||||||
TRIAL_FLAGS: "-j 8"
|
|
||||||
command:
|
|
||||||
- "bash -c 'python -m pip install tox && python -m tox -e py35-postgres,codecov'"
|
|
||||||
plugins:
|
|
||||||
- docker-compose#v2.1.0:
|
|
||||||
run: testenv
|
|
||||||
config:
|
|
||||||
- .buildkite/docker-compose.py35.pg95.yaml
|
|
||||||
retry:
|
|
||||||
automatic:
|
|
||||||
- exit_status: -1
|
|
||||||
limit: 2
|
|
||||||
- exit_status: 2
|
|
||||||
limit: 2
|
|
||||||
|
|
||||||
- label: ":python: 3.7 / :postgres: 9.5"
|
|
||||||
agents:
|
|
||||||
queue: "medium"
|
|
||||||
env:
|
|
||||||
TRIAL_FLAGS: "-j 8"
|
|
||||||
command:
|
|
||||||
- "bash -c 'python -m pip install tox && python -m tox -e py37-postgres,codecov'"
|
|
||||||
plugins:
|
|
||||||
- docker-compose#v2.1.0:
|
|
||||||
run: testenv
|
|
||||||
config:
|
|
||||||
- .buildkite/docker-compose.py37.pg95.yaml
|
|
||||||
retry:
|
|
||||||
automatic:
|
|
||||||
- exit_status: -1
|
|
||||||
limit: 2
|
|
||||||
- exit_status: 2
|
|
||||||
limit: 2
|
|
||||||
|
|
||||||
- label: ":python: 3.7 / :postgres: 11"
|
|
||||||
agents:
|
|
||||||
queue: "medium"
|
|
||||||
env:
|
|
||||||
TRIAL_FLAGS: "-j 8"
|
|
||||||
command:
|
|
||||||
- "bash -c 'python -m pip install tox && python -m tox -e py37-postgres,codecov'"
|
|
||||||
plugins:
|
|
||||||
- docker-compose#v2.1.0:
|
|
||||||
run: testenv
|
|
||||||
config:
|
|
||||||
- .buildkite/docker-compose.py37.pg11.yaml
|
|
||||||
retry:
|
|
||||||
automatic:
|
|
||||||
- exit_status: -1
|
|
||||||
limit: 2
|
|
||||||
- exit_status: 2
|
|
||||||
limit: 2
|
|
||||||
|
|
||||||
|
|
||||||
- label: "SyTest - :python: 3.5 / SQLite / Monolith"
|
|
||||||
agents:
|
|
||||||
queue: "medium"
|
|
||||||
command:
|
|
||||||
- "bash .buildkite/merge_base_branch.sh"
|
|
||||||
- "bash /synapse_sytest.sh"
|
|
||||||
plugins:
|
|
||||||
- docker#v3.0.1:
|
|
||||||
image: "matrixdotorg/sytest-synapse:py35"
|
|
||||||
propagate-environment: true
|
|
||||||
always-pull: true
|
|
||||||
workdir: "/src"
|
|
||||||
retry:
|
|
||||||
automatic:
|
|
||||||
- exit_status: -1
|
|
||||||
limit: 2
|
|
||||||
- exit_status: 2
|
|
||||||
limit: 2
|
|
||||||
|
|
||||||
- label: "SyTest - :python: 3.5 / :postgres: 9.6 / Monolith"
|
|
||||||
agents:
|
|
||||||
queue: "medium"
|
|
||||||
env:
|
|
||||||
POSTGRES: "1"
|
|
||||||
command:
|
|
||||||
- "bash .buildkite/merge_base_branch.sh"
|
|
||||||
- "bash /synapse_sytest.sh"
|
|
||||||
plugins:
|
|
||||||
- docker#v3.0.1:
|
|
||||||
image: "matrixdotorg/sytest-synapse:py35"
|
|
||||||
propagate-environment: true
|
|
||||||
always-pull: true
|
|
||||||
workdir: "/src"
|
|
||||||
retry:
|
|
||||||
automatic:
|
|
||||||
- exit_status: -1
|
|
||||||
limit: 2
|
|
||||||
- exit_status: 2
|
|
||||||
limit: 2
|
|
||||||
|
|
||||||
- label: "SyTest - :python: 3.5 / :postgres: 9.6 / Workers"
|
|
||||||
agents:
|
|
||||||
queue: "medium"
|
|
||||||
env:
|
|
||||||
POSTGRES: "1"
|
|
||||||
WORKERS: "1"
|
|
||||||
BLACKLIST: "synapse-blacklist-with-workers"
|
|
||||||
command:
|
|
||||||
- "bash .buildkite/merge_base_branch.sh"
|
|
||||||
- "bash -c 'cat /src/sytest-blacklist /src/.buildkite/worker-blacklist > /src/synapse-blacklist-with-workers'"
|
|
||||||
- "bash /synapse_sytest.sh"
|
|
||||||
plugins:
|
|
||||||
- docker#v3.0.1:
|
|
||||||
image: "matrixdotorg/sytest-synapse:py35"
|
|
||||||
propagate-environment: true
|
|
||||||
always-pull: true
|
|
||||||
workdir: "/src"
|
|
||||||
retry:
|
|
||||||
automatic:
|
|
||||||
- exit_status: -1
|
|
||||||
limit: 2
|
|
||||||
- exit_status: 2
|
|
||||||
limit: 2
|
|
|
@ -1,7 +1,8 @@
|
||||||
[run]
|
[run]
|
||||||
branch = True
|
branch = True
|
||||||
parallel = True
|
parallel = True
|
||||||
include = synapse/*
|
include=$TOP/synapse/*
|
||||||
|
data_file = $TOP/.coverage
|
||||||
|
|
||||||
[report]
|
[report]
|
||||||
precision = 2
|
precision = 2
|
||||||
|
|
|
@ -20,6 +20,7 @@ _trial_temp*/
|
||||||
/*.signing.key
|
/*.signing.key
|
||||||
/env/
|
/env/
|
||||||
/homeserver*.yaml
|
/homeserver*.yaml
|
||||||
|
/logs
|
||||||
/media_store/
|
/media_store/
|
||||||
/uploads
|
/uploads
|
||||||
|
|
||||||
|
@ -29,8 +30,9 @@ _trial_temp*/
|
||||||
/.vscode/
|
/.vscode/
|
||||||
|
|
||||||
# build products
|
# build products
|
||||||
/.coverage*
|
|
||||||
!/.coveragerc
|
!/.coveragerc
|
||||||
|
/.coverage*
|
||||||
|
/.mypy_cache/
|
||||||
/.tox
|
/.tox
|
||||||
/build/
|
/build/
|
||||||
/coverage.*
|
/coverage.*
|
||||||
|
@ -38,4 +40,3 @@ _trial_temp*/
|
||||||
/docs/build/
|
/docs/build/
|
||||||
/htmlcov
|
/htmlcov
|
||||||
/pip-wheel-metadata/
|
/pip-wheel-metadata/
|
||||||
|
|
||||||
|
|
182
CHANGES.md
182
CHANGES.md
|
@ -1,3 +1,185 @@
|
||||||
|
Synapse 1.4.0 (2019-10-03)
|
||||||
|
==========================
|
||||||
|
|
||||||
|
Bugfixes
|
||||||
|
--------
|
||||||
|
|
||||||
|
- Redact `client_secret` in server logs. ([\#6158](https://github.com/matrix-org/synapse/issues/6158))
|
||||||
|
|
||||||
|
|
||||||
|
Synapse 1.4.0rc2 (2019-10-02)
|
||||||
|
=============================
|
||||||
|
|
||||||
|
Bugfixes
|
||||||
|
--------
|
||||||
|
|
||||||
|
- Fix bug in background update that adds last seen information to the `devices` table, and improve its performance on Postgres. ([\#6135](https://github.com/matrix-org/synapse/issues/6135))
|
||||||
|
- Fix bad performance of censoring redactions background task. ([\#6141](https://github.com/matrix-org/synapse/issues/6141))
|
||||||
|
- Fix fetching censored redactions from DB, which caused APIs like initial sync to fail if it tried to include the censored redaction. ([\#6145](https://github.com/matrix-org/synapse/issues/6145))
|
||||||
|
- Fix exceptions when storing large retry intervals for down remote servers. ([\#6146](https://github.com/matrix-org/synapse/issues/6146))
|
||||||
|
|
||||||
|
|
||||||
|
Internal Changes
|
||||||
|
----------------
|
||||||
|
|
||||||
|
- Fix up sample config entry for `redaction_retention_period` option. ([\#6117](https://github.com/matrix-org/synapse/issues/6117))
|
||||||
|
|
||||||
|
|
||||||
|
Synapse 1.4.0rc1 (2019-09-26)
|
||||||
|
=============================
|
||||||
|
|
||||||
|
Note that this release includes significant changes around 3pid
|
||||||
|
verification. Administrators are reminded to review the [upgrade notes](UPGRADE.rst#upgrading-to-v140).
|
||||||
|
|
||||||
|
Features
|
||||||
|
--------
|
||||||
|
|
||||||
|
- Changes to 3pid verification:
|
||||||
|
- Add the ability to send registration emails from the homeserver rather than delegating to an identity server. ([\#5835](https://github.com/matrix-org/synapse/issues/5835), [\#5940](https://github.com/matrix-org/synapse/issues/5940), [\#5993](https://github.com/matrix-org/synapse/issues/5993), [\#5994](https://github.com/matrix-org/synapse/issues/5994), [\#5868](https://github.com/matrix-org/synapse/issues/5868))
|
||||||
|
- Replace `trust_identity_server_for_password_resets` config option with `account_threepid_delegates`, and make the `id_server` parameteter optional on `*/requestToken` endpoints, as per [MSC2263](https://github.com/matrix-org/matrix-doc/pull/2263). ([\#5876](https://github.com/matrix-org/synapse/issues/5876), [\#5969](https://github.com/matrix-org/synapse/issues/5969), [\#6028](https://github.com/matrix-org/synapse/issues/6028))
|
||||||
|
- Switch to using the v2 Identity Service `/lookup` API where available, with fallback to v1. (Implements [MSC2134](https://github.com/matrix-org/matrix-doc/pull/2134) plus `id_access_token authentication` for v2 Identity Service APIs from [MSC2140](https://github.com/matrix-org/matrix-doc/pull/2140)). ([\#5897](https://github.com/matrix-org/synapse/issues/5897))
|
||||||
|
- Remove `bind_email` and `bind_msisdn` parameters from `/register` ala [MSC2140](https://github.com/matrix-org/matrix-doc/pull/2140). ([\#5964](https://github.com/matrix-org/synapse/issues/5964))
|
||||||
|
- Add `m.id_access_token` to `unstable_features` in `/versions` as per [MSC2264](https://github.com/matrix-org/matrix-doc/pull/2264). ([\#5974](https://github.com/matrix-org/synapse/issues/5974))
|
||||||
|
- Use the v2 Identity Service API for 3PID invites. ([\#5979](https://github.com/matrix-org/synapse/issues/5979))
|
||||||
|
- Add `POST /_matrix/client/unstable/account/3pid/unbind` endpoint from [MSC2140](https://github.com/matrix-org/matrix-doc/pull/2140) for unbinding a 3PID from an identity server without removing it from the homeserver user account. ([\#5980](https://github.com/matrix-org/synapse/issues/5980), [\#6062](https://github.com/matrix-org/synapse/issues/6062))
|
||||||
|
- Use `account_threepid_delegate.email` and `account_threepid_delegate.msisdn` for validating threepid sessions. ([\#6011](https://github.com/matrix-org/synapse/issues/6011))
|
||||||
|
- Allow homeserver to handle or delegate email validation when adding an email to a user's account. ([\#6042](https://github.com/matrix-org/synapse/issues/6042))
|
||||||
|
- Implement new Client Server API endpoints `/account/3pid/add` and `/account/3pid/bind` as per [MSC2290](https://github.com/matrix-org/matrix-doc/pull/2290). ([\#6043](https://github.com/matrix-org/synapse/issues/6043))
|
||||||
|
- Add an unstable feature flag for separate add/bind 3pid APIs. ([\#6044](https://github.com/matrix-org/synapse/issues/6044))
|
||||||
|
- Remove `bind` parameter from Client Server POST `/account` endpoint as per [MSC2290](https://github.com/matrix-org/matrix-doc/pull/2290/). ([\#6067](https://github.com/matrix-org/synapse/issues/6067))
|
||||||
|
- Add `POST /add_threepid/msisdn/submit_token` endpoint for proxying submitToken on an `account_threepid_handler`. ([\#6078](https://github.com/matrix-org/synapse/issues/6078))
|
||||||
|
- Add `submit_url` response parameter to `*/msisdn/requestToken` endpoints. ([\#6079](https://github.com/matrix-org/synapse/issues/6079))
|
||||||
|
- Add `m.require_identity_server` flag to /version's unstable_features. ([\#5972](https://github.com/matrix-org/synapse/issues/5972))
|
||||||
|
- Enhancements to OpenTracing support:
|
||||||
|
- Make OpenTracing work in worker mode. ([\#5771](https://github.com/matrix-org/synapse/issues/5771))
|
||||||
|
- Pass OpenTracing contexts between servers when transmitting EDUs. ([\#5852](https://github.com/matrix-org/synapse/issues/5852))
|
||||||
|
- OpenTracing for device list updates. ([\#5853](https://github.com/matrix-org/synapse/issues/5853))
|
||||||
|
- Add a tag recording a request's authenticated entity and corresponding servlet in OpenTracing. ([\#5856](https://github.com/matrix-org/synapse/issues/5856))
|
||||||
|
- Add minimum OpenTracing for client servlets. ([\#5983](https://github.com/matrix-org/synapse/issues/5983))
|
||||||
|
- Check at setup that OpenTracing is installed if it's enabled in the config. ([\#5985](https://github.com/matrix-org/synapse/issues/5985))
|
||||||
|
- Trace replication send times. ([\#5986](https://github.com/matrix-org/synapse/issues/5986))
|
||||||
|
- Include missing OpenTracing contexts in outbout replication requests. ([\#5982](https://github.com/matrix-org/synapse/issues/5982))
|
||||||
|
- Fix sending of EDUs when OpenTracing is enabled with an empty whitelist. ([\#5984](https://github.com/matrix-org/synapse/issues/5984))
|
||||||
|
- Fix invalid references to None while OpenTracing if the log context slips. ([\#5988](https://github.com/matrix-org/synapse/issues/5988), [\#5991](https://github.com/matrix-org/synapse/issues/5991))
|
||||||
|
- OpenTracing for room and e2e keys. ([\#5855](https://github.com/matrix-org/synapse/issues/5855))
|
||||||
|
- Add OpenTracing span over HTTP push processing. ([\#6003](https://github.com/matrix-org/synapse/issues/6003))
|
||||||
|
- Add an admin API to purge old rooms from the database. ([\#5845](https://github.com/matrix-org/synapse/issues/5845))
|
||||||
|
- Retry well-known lookups if we have recently seen a valid well-known record for the server. ([\#5850](https://github.com/matrix-org/synapse/issues/5850))
|
||||||
|
- Add support for filtered room-directory search requests over federation ([MSC2197](https://github.com/matrix-org/matrix-doc/pull/2197), in order to allow upcoming room directory query performance improvements. ([\#5859](https://github.com/matrix-org/synapse/issues/5859))
|
||||||
|
- Correctly retry all hosts returned from SRV when we fail to connect. ([\#5864](https://github.com/matrix-org/synapse/issues/5864))
|
||||||
|
- Add admin API endpoint for setting whether or not a user is a server administrator. ([\#5878](https://github.com/matrix-org/synapse/issues/5878))
|
||||||
|
- Enable cleaning up extremities with dummy events by default to prevent undue build up of forward extremities. ([\#5884](https://github.com/matrix-org/synapse/issues/5884))
|
||||||
|
- Add config option to sign remote key query responses with a separate key. ([\#5895](https://github.com/matrix-org/synapse/issues/5895))
|
||||||
|
- Add support for config templating. ([\#5900](https://github.com/matrix-org/synapse/issues/5900))
|
||||||
|
- Users with the type of "support" or "bot" are no longer required to consent. ([\#5902](https://github.com/matrix-org/synapse/issues/5902))
|
||||||
|
- Let synctl accept a directory of config files. ([\#5904](https://github.com/matrix-org/synapse/issues/5904))
|
||||||
|
- Increase max display name size to 256. ([\#5906](https://github.com/matrix-org/synapse/issues/5906))
|
||||||
|
- Add admin API endpoint for getting whether or not a user is a server administrator. ([\#5914](https://github.com/matrix-org/synapse/issues/5914))
|
||||||
|
- Redact events in the database that have been redacted for a week. ([\#5934](https://github.com/matrix-org/synapse/issues/5934))
|
||||||
|
- New prometheus metrics:
|
||||||
|
- `synapse_federation_known_servers`: represents the total number of servers your server knows about (i.e. is in rooms with), including itself. Enable by setting `metrics_flags.known_servers` to True in the configuration.([\#5981](https://github.com/matrix-org/synapse/issues/5981))
|
||||||
|
- `synapse_build_info`: exposes the Python version, OS version, and Synapse version of the running server. ([\#6005](https://github.com/matrix-org/synapse/issues/6005))
|
||||||
|
- Give appropriate exit codes when synctl fails. ([\#5992](https://github.com/matrix-org/synapse/issues/5992))
|
||||||
|
- Apply the federation blacklist to requests to identity servers. ([\#6000](https://github.com/matrix-org/synapse/issues/6000))
|
||||||
|
- Add `report_stats_endpoint` option to configure where stats are reported to, if enabled. Contributed by @Sorunome. ([\#6012](https://github.com/matrix-org/synapse/issues/6012))
|
||||||
|
- Add config option to increase ratelimits for room admins redacting messages. ([\#6015](https://github.com/matrix-org/synapse/issues/6015))
|
||||||
|
- Stop sending federation transactions to servers which have been down for a long time. ([\#6026](https://github.com/matrix-org/synapse/issues/6026))
|
||||||
|
- Make the process for mapping SAML2 users to matrix IDs more flexible. ([\#6037](https://github.com/matrix-org/synapse/issues/6037))
|
||||||
|
- Return a clearer error message when a timeout occurs when attempting to contact an identity server. ([\#6073](https://github.com/matrix-org/synapse/issues/6073))
|
||||||
|
- Prevent password reset's submit_token endpoint from accepting trailing slashes. ([\#6074](https://github.com/matrix-org/synapse/issues/6074))
|
||||||
|
- Return 403 on `/register/available` if registration has been disabled. ([\#6082](https://github.com/matrix-org/synapse/issues/6082))
|
||||||
|
- Explicitly log when a homeserver does not have the `trusted_key_servers` config field configured. ([\#6090](https://github.com/matrix-org/synapse/issues/6090))
|
||||||
|
- Add support for pruning old rows in `user_ips` table. ([\#6098](https://github.com/matrix-org/synapse/issues/6098))
|
||||||
|
|
||||||
|
Bugfixes
|
||||||
|
--------
|
||||||
|
|
||||||
|
- Don't create broken room when `power_level_content_override.users` does not contain `creator_id`. ([\#5633](https://github.com/matrix-org/synapse/issues/5633))
|
||||||
|
- Fix database index so that different backup versions can have the same sessions. ([\#5857](https://github.com/matrix-org/synapse/issues/5857))
|
||||||
|
- Fix Synapse looking for config options `password_reset_failure_template` and `password_reset_success_template`, when they are actually `password_reset_template_failure_html`, `password_reset_template_success_html`. ([\#5863](https://github.com/matrix-org/synapse/issues/5863))
|
||||||
|
- Fix stack overflow when recovering an appservice which had an outage. ([\#5885](https://github.com/matrix-org/synapse/issues/5885))
|
||||||
|
- Fix error message which referred to `public_base_url` instead of `public_baseurl`. Thanks to @aaronraimist for the fix! ([\#5909](https://github.com/matrix-org/synapse/issues/5909))
|
||||||
|
- Fix 404 for thumbnail download when `dynamic_thumbnails` is `false` and the thumbnail was dynamically generated. Fix reported by rkfg. ([\#5915](https://github.com/matrix-org/synapse/issues/5915))
|
||||||
|
- Fix a cache-invalidation bug for worker-based deployments. ([\#5920](https://github.com/matrix-org/synapse/issues/5920))
|
||||||
|
- Fix admin API for listing media in a room not being available with an external media repo. ([\#5966](https://github.com/matrix-org/synapse/issues/5966))
|
||||||
|
- Fix list media admin API always returning an error. ([\#5967](https://github.com/matrix-org/synapse/issues/5967))
|
||||||
|
- Fix room and user stats tracking. ([\#5971](https://github.com/matrix-org/synapse/issues/5971), [\#5998](https://github.com/matrix-org/synapse/issues/5998), [\#6029](https://github.com/matrix-org/synapse/issues/6029))
|
||||||
|
- Return a `M_MISSING_PARAM` if `sid` is not provided to `/account/3pid`. ([\#5995](https://github.com/matrix-org/synapse/issues/5995))
|
||||||
|
- `federation_certificate_verification_whitelist` now will not cause `TypeErrors` to be raised (a regression in 1.3). Additionally, it now supports internationalised domain names in their non-canonical representation. ([\#5996](https://github.com/matrix-org/synapse/issues/5996))
|
||||||
|
- Only count real users when checking for auto-creation of auto-join room. ([\#6004](https://github.com/matrix-org/synapse/issues/6004))
|
||||||
|
- Ensure support users can be registered even if MAU limit is reached. ([\#6020](https://github.com/matrix-org/synapse/issues/6020))
|
||||||
|
- Fix bug where login error was shown incorrectly on SSO fallback login. ([\#6024](https://github.com/matrix-org/synapse/issues/6024))
|
||||||
|
- Fix bug in calculating the federation retry backoff period. ([\#6025](https://github.com/matrix-org/synapse/issues/6025))
|
||||||
|
- Prevent exceptions being logged when extremity-cleanup events fail due to lack of user consent to the terms of service. ([\#6053](https://github.com/matrix-org/synapse/issues/6053))
|
||||||
|
- Remove POST method from password-reset `submit_token` endpoint until we implement `submit_url` functionality. ([\#6056](https://github.com/matrix-org/synapse/issues/6056))
|
||||||
|
- Fix logcontext spam on non-Linux platforms. ([\#6059](https://github.com/matrix-org/synapse/issues/6059))
|
||||||
|
- Ensure query parameters in email validation links are URL-encoded. ([\#6063](https://github.com/matrix-org/synapse/issues/6063))
|
||||||
|
- Fix a bug which caused SAML attribute maps to be overridden by defaults. ([\#6069](https://github.com/matrix-org/synapse/issues/6069))
|
||||||
|
- Fix the logged number of updated items for the `users_set_deactivated_flag` background update. ([\#6092](https://github.com/matrix-org/synapse/issues/6092))
|
||||||
|
- Add `sid` to `next_link` for email validation. ([\#6097](https://github.com/matrix-org/synapse/issues/6097))
|
||||||
|
- Threepid validity checks on msisdns should not be dependent on `threepid_behaviour_email`. ([\#6104](https://github.com/matrix-org/synapse/issues/6104))
|
||||||
|
- Ensure that servers which are not configured to support email address verification do not offer it in the registration flows. ([\#6107](https://github.com/matrix-org/synapse/issues/6107))
|
||||||
|
|
||||||
|
|
||||||
|
Updates to the Docker image
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
- Avoid changing `UID/GID` if they are already correct. ([\#5970](https://github.com/matrix-org/synapse/issues/5970))
|
||||||
|
- Provide `SYNAPSE_WORKER` envvar to specify python module. ([\#6058](https://github.com/matrix-org/synapse/issues/6058))
|
||||||
|
|
||||||
|
|
||||||
|
Improved Documentation
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
- Convert documentation to markdown (from rst) ([\#5849](https://github.com/matrix-org/synapse/issues/5849))
|
||||||
|
- Update `INSTALL.md` to say that Python 2 is no longer supported. ([\#5953](https://github.com/matrix-org/synapse/issues/5953))
|
||||||
|
- Add developer documentation for using SAML2. ([\#6032](https://github.com/matrix-org/synapse/issues/6032))
|
||||||
|
- Add some notes on rolling back to v1.3.1. ([\#6049](https://github.com/matrix-org/synapse/issues/6049))
|
||||||
|
- Update the upgrade notes. ([\#6050](https://github.com/matrix-org/synapse/issues/6050))
|
||||||
|
|
||||||
|
|
||||||
|
Deprecations and Removals
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
- Remove shared-secret registration from `/_matrix/client/r0/register` endpoint. Contributed by Awesome Technologies Innovationslabor GmbH. ([\#5877](https://github.com/matrix-org/synapse/issues/5877))
|
||||||
|
- Deprecate the `trusted_third_party_id_servers` option. ([\#5875](https://github.com/matrix-org/synapse/issues/5875))
|
||||||
|
|
||||||
|
|
||||||
|
Internal Changes
|
||||||
|
----------------
|
||||||
|
|
||||||
|
- Lay the groundwork for structured logging output. ([\#5680](https://github.com/matrix-org/synapse/issues/5680))
|
||||||
|
- Retry well-known lookup before the cache expires, giving a grace period where the remote well-known can be down but we still use the old result. ([\#5844](https://github.com/matrix-org/synapse/issues/5844))
|
||||||
|
- Remove log line for debugging issue #5407. ([\#5860](https://github.com/matrix-org/synapse/issues/5860))
|
||||||
|
- Refactor the Appservice scheduler code. ([\#5886](https://github.com/matrix-org/synapse/issues/5886))
|
||||||
|
- Compatibility with v2 Identity Service APIs other than /lookup. ([\#5892](https://github.com/matrix-org/synapse/issues/5892), [\#6013](https://github.com/matrix-org/synapse/issues/6013))
|
||||||
|
- Stop populating some unused tables. ([\#5893](https://github.com/matrix-org/synapse/issues/5893), [\#6047](https://github.com/matrix-org/synapse/issues/6047))
|
||||||
|
- Add missing index on `users_in_public_rooms` to improve the performance of directory queries. ([\#5894](https://github.com/matrix-org/synapse/issues/5894))
|
||||||
|
- Improve the logging when we have an error when fetching signing keys. ([\#5896](https://github.com/matrix-org/synapse/issues/5896))
|
||||||
|
- Add support for database engine-specific schema deltas, based on file extension. ([\#5911](https://github.com/matrix-org/synapse/issues/5911))
|
||||||
|
- Update Buildkite pipeline to use plugins instead of buildkite-agent commands. ([\#5922](https://github.com/matrix-org/synapse/issues/5922))
|
||||||
|
- Add link in sample config to the logging config schema. ([\#5926](https://github.com/matrix-org/synapse/issues/5926))
|
||||||
|
- Remove unnecessary parentheses in return statements. ([\#5931](https://github.com/matrix-org/synapse/issues/5931))
|
||||||
|
- Remove unused `jenkins/prepare_sytest.sh` file. ([\#5938](https://github.com/matrix-org/synapse/issues/5938))
|
||||||
|
- Move Buildkite pipeline config to the pipelines repo. ([\#5943](https://github.com/matrix-org/synapse/issues/5943))
|
||||||
|
- Remove unnecessary return statements in the codebase which were the result of a regex run. ([\#5962](https://github.com/matrix-org/synapse/issues/5962))
|
||||||
|
- Remove left-over methods from v1 registration API. ([\#5963](https://github.com/matrix-org/synapse/issues/5963))
|
||||||
|
- Cleanup event auth type initialisation. ([\#5975](https://github.com/matrix-org/synapse/issues/5975))
|
||||||
|
- Clean up dependency checking at setup. ([\#5989](https://github.com/matrix-org/synapse/issues/5989))
|
||||||
|
- Update OpenTracing docs to use the unified `trace` method. ([\#5776](https://github.com/matrix-org/synapse/issues/5776))
|
||||||
|
- Small refactor of function arguments and docstrings in` RoomMemberHandler`. ([\#6009](https://github.com/matrix-org/synapse/issues/6009))
|
||||||
|
- Remove unused `origin` argument on `FederationHandler.add_display_name_to_third_party_invite`. ([\#6010](https://github.com/matrix-org/synapse/issues/6010))
|
||||||
|
- Add a `failure_ts` column to the `destinations` database table. ([\#6016](https://github.com/matrix-org/synapse/issues/6016), [\#6072](https://github.com/matrix-org/synapse/issues/6072))
|
||||||
|
- Clean up some code in the retry logic. ([\#6017](https://github.com/matrix-org/synapse/issues/6017))
|
||||||
|
- Fix the structured logging tests stomping on the global log configuration for subsequent tests. ([\#6023](https://github.com/matrix-org/synapse/issues/6023))
|
||||||
|
- Clean up the sample config for SAML authentication. ([\#6064](https://github.com/matrix-org/synapse/issues/6064))
|
||||||
|
- Change mailer logging to reflect Synapse doesn't just do chat notifications by email now. ([\#6075](https://github.com/matrix-org/synapse/issues/6075))
|
||||||
|
- Move last-seen info into devices table. ([\#6089](https://github.com/matrix-org/synapse/issues/6089))
|
||||||
|
- Remove unused parameter to `get_user_id_by_threepid`. ([\#6099](https://github.com/matrix-org/synapse/issues/6099))
|
||||||
|
- Refactor the user-interactive auth handling. ([\#6105](https://github.com/matrix-org/synapse/issues/6105))
|
||||||
|
- Refactor code for calculating registration flows. ([\#6106](https://github.com/matrix-org/synapse/issues/6106))
|
||||||
|
|
||||||
|
|
||||||
Synapse 1.3.1 (2019-08-17)
|
Synapse 1.3.1 (2019-08-17)
|
||||||
==========================
|
==========================
|
||||||
|
|
||||||
|
|
|
@ -56,7 +56,7 @@ Code style
|
||||||
|
|
||||||
All Matrix projects have a well-defined code-style - and sometimes we've even
|
All Matrix projects have a well-defined code-style - and sometimes we've even
|
||||||
got as far as documenting it... For instance, synapse's code style doc lives
|
got as far as documenting it... For instance, synapse's code style doc lives
|
||||||
at https://github.com/matrix-org/synapse/tree/master/docs/code_style.rst.
|
at https://github.com/matrix-org/synapse/tree/master/docs/code_style.md.
|
||||||
|
|
||||||
Please ensure your changes match the cosmetic style of the existing project,
|
Please ensure your changes match the cosmetic style of the existing project,
|
||||||
and **never** mix cosmetic and functional changes in the same commit, as it
|
and **never** mix cosmetic and functional changes in the same commit, as it
|
||||||
|
|
|
@ -36,7 +36,7 @@ that your email address is probably `user@example.com` rather than
|
||||||
System requirements:
|
System requirements:
|
||||||
|
|
||||||
- POSIX-compliant system (tested on Linux & OS X)
|
- POSIX-compliant system (tested on Linux & OS X)
|
||||||
- Python 3.5, 3.6, 3.7, or 2.7
|
- Python 3.5, 3.6, or 3.7
|
||||||
- At least 1GB of free RAM if you want to join large public rooms like #matrix:matrix.org
|
- At least 1GB of free RAM if you want to join large public rooms like #matrix:matrix.org
|
||||||
|
|
||||||
Synapse is written in Python but some of the libraries it uses are written in
|
Synapse is written in Python but some of the libraries it uses are written in
|
||||||
|
@ -380,7 +380,7 @@ is suitable for local testing, but for any practical use, you will either need
|
||||||
to enable a reverse proxy, or configure Synapse to expose an HTTPS port.
|
to enable a reverse proxy, or configure Synapse to expose an HTTPS port.
|
||||||
|
|
||||||
For information on using a reverse proxy, see
|
For information on using a reverse proxy, see
|
||||||
[docs/reverse_proxy.rst](docs/reverse_proxy.rst).
|
[docs/reverse_proxy.md](docs/reverse_proxy.md).
|
||||||
|
|
||||||
To configure Synapse to expose an HTTPS port, you will need to edit
|
To configure Synapse to expose an HTTPS port, you will need to edit
|
||||||
`homeserver.yaml`, as follows:
|
`homeserver.yaml`, as follows:
|
||||||
|
@ -453,7 +453,7 @@ on your server even if `enable_registration` is `false`.
|
||||||
## Setting up a TURN server
|
## Setting up a TURN server
|
||||||
|
|
||||||
For reliable VoIP calls to be routed via this homeserver, you MUST configure
|
For reliable VoIP calls to be routed via this homeserver, you MUST configure
|
||||||
a TURN server. See [docs/turn-howto.rst](docs/turn-howto.rst) for details.
|
a TURN server. See [docs/turn-howto.md](docs/turn-howto.md) for details.
|
||||||
|
|
||||||
## URL previews
|
## URL previews
|
||||||
|
|
||||||
|
|
12
MANIFEST.in
12
MANIFEST.in
|
@ -38,14 +38,16 @@ exclude sytest-blacklist
|
||||||
include pyproject.toml
|
include pyproject.toml
|
||||||
recursive-include changelog.d *
|
recursive-include changelog.d *
|
||||||
|
|
||||||
|
prune .buildkite
|
||||||
|
prune .circleci
|
||||||
|
prune .codecov.yml
|
||||||
|
prune .coveragerc
|
||||||
prune .github
|
prune .github
|
||||||
|
prune debian
|
||||||
prune demo/etc
|
prune demo/etc
|
||||||
prune docker
|
prune docker
|
||||||
prune .circleci
|
prune mypy.ini
|
||||||
prune .coveragerc
|
prune stubs
|
||||||
prune debian
|
|
||||||
prune .codecov.yml
|
|
||||||
prune .buildkite
|
|
||||||
|
|
||||||
exclude jenkins*
|
exclude jenkins*
|
||||||
recursive-exclude jenkins *.sh
|
recursive-exclude jenkins *.sh
|
||||||
|
|
|
@ -115,7 +115,7 @@ Registering a new user from a client
|
||||||
|
|
||||||
By default, registration of new users via Matrix clients is disabled. To enable
|
By default, registration of new users via Matrix clients is disabled. To enable
|
||||||
it, specify ``enable_registration: true`` in ``homeserver.yaml``. (It is then
|
it, specify ``enable_registration: true`` in ``homeserver.yaml``. (It is then
|
||||||
recommended to also set up CAPTCHA - see `<docs/CAPTCHA_SETUP.rst>`_.)
|
recommended to also set up CAPTCHA - see `<docs/CAPTCHA_SETUP.md>`_.)
|
||||||
|
|
||||||
Once ``enable_registration`` is set to ``true``, it is possible to register a
|
Once ``enable_registration`` is set to ``true``, it is possible to register a
|
||||||
user via `riot.im <https://riot.im/app/#/register>`_ or other Matrix clients.
|
user via `riot.im <https://riot.im/app/#/register>`_ or other Matrix clients.
|
||||||
|
@ -186,7 +186,7 @@ Almost all installations should opt to use PostreSQL. Advantages include:
|
||||||
synapse itself.
|
synapse itself.
|
||||||
|
|
||||||
For information on how to install and use PostgreSQL, please see
|
For information on how to install and use PostgreSQL, please see
|
||||||
`docs/postgres.rst <docs/postgres.rst>`_.
|
`docs/postgres.md <docs/postgres.md>`_.
|
||||||
|
|
||||||
.. _reverse-proxy:
|
.. _reverse-proxy:
|
||||||
|
|
||||||
|
@ -201,7 +201,7 @@ It is recommended to put a reverse proxy such as
|
||||||
doing so is that it means that you can expose the default https port (443) to
|
doing so is that it means that you can expose the default https port (443) to
|
||||||
Matrix clients without needing to run Synapse with root privileges.
|
Matrix clients without needing to run Synapse with root privileges.
|
||||||
|
|
||||||
For information on configuring one, see `<docs/reverse_proxy.rst>`_.
|
For information on configuring one, see `<docs/reverse_proxy.md>`_.
|
||||||
|
|
||||||
Identity Servers
|
Identity Servers
|
||||||
================
|
================
|
||||||
|
|
279
UPGRADE.rst
279
UPGRADE.rst
|
@ -2,58 +2,268 @@ Upgrading Synapse
|
||||||
=================
|
=================
|
||||||
|
|
||||||
Before upgrading check if any special steps are required to upgrade from the
|
Before upgrading check if any special steps are required to upgrade from the
|
||||||
what you currently have installed to current version of synapse. The extra
|
what you currently have installed to current version of Synapse. The extra
|
||||||
instructions that may be required are listed later in this document.
|
instructions that may be required are listed later in this document.
|
||||||
|
|
||||||
1. If synapse was installed in a virtualenv then activate that virtualenv before
|
* If Synapse was installed using `prebuilt packages
|
||||||
upgrading. If synapse is installed in a virtualenv in ``~/synapse/env`` then
|
<INSTALL.md#prebuilt-packages>`_, you will need to follow the normal process
|
||||||
run:
|
for upgrading those packages.
|
||||||
|
|
||||||
.. code:: bash
|
* If Synapse was installed from source, then:
|
||||||
|
|
||||||
|
1. Activate the virtualenv before upgrading. For example, if Synapse is
|
||||||
|
installed in a virtualenv in ``~/synapse/env`` then run:
|
||||||
|
|
||||||
|
.. code:: bash
|
||||||
|
|
||||||
source ~/synapse/env/bin/activate
|
source ~/synapse/env/bin/activate
|
||||||
|
|
||||||
2. If synapse was installed using pip then upgrade to the latest version by
|
2. If Synapse was installed using pip then upgrade to the latest version by
|
||||||
running:
|
running:
|
||||||
|
|
||||||
.. code:: bash
|
.. code:: bash
|
||||||
|
|
||||||
pip install --upgrade matrix-synapse[all]
|
pip install --upgrade matrix-synapse
|
||||||
|
|
||||||
# restart synapse
|
If Synapse was installed using git then upgrade to the latest version by
|
||||||
synctl restart
|
running:
|
||||||
|
|
||||||
|
.. code:: bash
|
||||||
|
|
||||||
If synapse was installed using git then upgrade to the latest version by
|
|
||||||
running:
|
|
||||||
|
|
||||||
.. code:: bash
|
|
||||||
|
|
||||||
# Pull the latest version of the master branch.
|
|
||||||
git pull
|
git pull
|
||||||
|
pip install --upgrade .
|
||||||
|
|
||||||
# Update synapse and its python dependencies.
|
3. Restart Synapse:
|
||||||
pip install --upgrade .[all]
|
|
||||||
|
.. code:: bash
|
||||||
|
|
||||||
# restart synapse
|
|
||||||
./synctl restart
|
./synctl restart
|
||||||
|
|
||||||
|
To check whether your update was successful, you can check the running server
|
||||||
To check whether your update was successful, you can check the Server header
|
version with:
|
||||||
returned by the Client-Server API:
|
|
||||||
|
|
||||||
.. code:: bash
|
.. code:: bash
|
||||||
|
|
||||||
# replace <host.name> with the hostname of your synapse homeserver.
|
# you may need to replace 'localhost:8008' if synapse is not configured
|
||||||
# You may need to specify a port (eg, :8448) if your server is not
|
# to listen on port 8008.
|
||||||
# configured on port 443.
|
|
||||||
curl -kv https://<host.name>/_matrix/client/versions 2>&1 | grep "Server:"
|
curl http://localhost:8008/_synapse/admin/v1/server_version
|
||||||
|
|
||||||
|
Rolling back to older versions
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
Rolling back to previous releases can be difficult, due to database schema
|
||||||
|
changes between releases. Where we have been able to test the rollback process,
|
||||||
|
this will be noted below.
|
||||||
|
|
||||||
|
In general, you will need to undo any changes made during the upgrade process,
|
||||||
|
for example:
|
||||||
|
|
||||||
|
* pip:
|
||||||
|
|
||||||
|
.. code:: bash
|
||||||
|
|
||||||
|
source env/bin/activate
|
||||||
|
# replace `1.3.0` accordingly:
|
||||||
|
pip install matrix-synapse==1.3.0
|
||||||
|
|
||||||
|
* Debian:
|
||||||
|
|
||||||
|
.. code:: bash
|
||||||
|
|
||||||
|
# replace `1.3.0` and `stretch` accordingly:
|
||||||
|
wget https://packages.matrix.org/debian/pool/main/m/matrix-synapse-py3/matrix-synapse-py3_1.3.0+stretch1_amd64.deb
|
||||||
|
dpkg -i matrix-synapse-py3_1.3.0+stretch1_amd64.deb
|
||||||
|
|
||||||
|
Upgrading to v1.4.0
|
||||||
|
===================
|
||||||
|
|
||||||
|
New custom templates
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
If you have configured a custom template directory with the
|
||||||
|
``email.template_dir`` option, be aware that there are new templates regarding
|
||||||
|
registration and threepid management (see below) that must be included.
|
||||||
|
|
||||||
|
* ``registration.html`` and ``registration.txt``
|
||||||
|
* ``registration_success.html`` and ``registration_failure.html``
|
||||||
|
* ``add_threepid.html`` and ``add_threepid.txt``
|
||||||
|
* ``add_threepid_failure.html`` and ``add_threepid_success.html``
|
||||||
|
|
||||||
|
Synapse will expect these files to exist inside the configured template
|
||||||
|
directory, and **will fail to start** if they are absent.
|
||||||
|
To view the default templates, see `synapse/res/templates
|
||||||
|
<https://github.com/matrix-org/synapse/tree/master/synapse/res/templates>`_.
|
||||||
|
|
||||||
|
3pid verification changes
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
**Note: As of this release, users will be unable to add phone numbers or email
|
||||||
|
addresses to their accounts, without changes to the Synapse configuration. This
|
||||||
|
includes adding an email address during registration.**
|
||||||
|
|
||||||
|
It is possible for a user to associate an email address or phone number
|
||||||
|
with their account, for a number of reasons:
|
||||||
|
|
||||||
|
* for use when logging in, as an alternative to the user id.
|
||||||
|
* in the case of email, as an alternative contact to help with account recovery.
|
||||||
|
* in the case of email, to receive notifications of missed messages.
|
||||||
|
|
||||||
|
Before an email address or phone number can be added to a user's account,
|
||||||
|
or before such an address is used to carry out a password-reset, Synapse must
|
||||||
|
confirm the operation with the owner of the email address or phone number.
|
||||||
|
It does this by sending an email or text giving the user a link or token to confirm
|
||||||
|
receipt. This process is known as '3pid verification'. ('3pid', or 'threepid',
|
||||||
|
stands for third-party identifier, and we use it to refer to external
|
||||||
|
identifiers such as email addresses and phone numbers.)
|
||||||
|
|
||||||
|
Previous versions of Synapse delegated the task of 3pid verification to an
|
||||||
|
identity server by default. In most cases this server is ``vector.im`` or
|
||||||
|
``matrix.org``.
|
||||||
|
|
||||||
|
In Synapse 1.4.0, for security and privacy reasons, the homeserver will no
|
||||||
|
longer delegate this task to an identity server by default. Instead,
|
||||||
|
the server administrator will need to explicitly decide how they would like the
|
||||||
|
verification messages to be sent.
|
||||||
|
|
||||||
|
In the medium term, the ``vector.im`` and ``matrix.org`` identity servers will
|
||||||
|
disable support for delegated 3pid verification entirely. However, in order to
|
||||||
|
ease the transition, they will retain the capability for a limited
|
||||||
|
period. Delegated email verification will be disabled on Monday 2nd December
|
||||||
|
2019 (giving roughly 2 months notice). Disabling delegated SMS verification
|
||||||
|
will follow some time after that once SMS verification support lands in
|
||||||
|
Synapse.
|
||||||
|
|
||||||
|
Once delegated 3pid verification support has been disabled in the ``vector.im`` and
|
||||||
|
``matrix.org`` identity servers, all Synapse versions that depend on those
|
||||||
|
instances will be unable to verify email and phone numbers through them. There
|
||||||
|
are no imminent plans to remove delegated 3pid verification from Sydent
|
||||||
|
generally. (Sydent is the identity server project that backs the ``vector.im`` and
|
||||||
|
``matrix.org`` instances).
|
||||||
|
|
||||||
|
Email
|
||||||
|
~~~~~
|
||||||
|
Following upgrade, to continue verifying email (e.g. as part of the
|
||||||
|
registration process), admins can either:-
|
||||||
|
|
||||||
|
* Configure Synapse to use an email server.
|
||||||
|
* Run or choose an identity server which allows delegated email verification
|
||||||
|
and delegate to it.
|
||||||
|
|
||||||
|
Configure SMTP in Synapse
|
||||||
|
+++++++++++++++++++++++++
|
||||||
|
|
||||||
|
To configure an SMTP server for Synapse, modify the configuration section
|
||||||
|
headed ``email``, and be sure to have at least the ``smtp_host, smtp_port``
|
||||||
|
and ``notif_from`` fields filled out.
|
||||||
|
|
||||||
|
You may also need to set ``smtp_user``, ``smtp_pass``, and
|
||||||
|
``require_transport_security``.
|
||||||
|
|
||||||
|
See the `sample configuration file <docs/sample_config.yaml>`_ for more details
|
||||||
|
on these settings.
|
||||||
|
|
||||||
|
Delegate email to an identity server
|
||||||
|
++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
|
Some admins will wish to continue using email verification as part of the
|
||||||
|
registration process, but will not immediately have an appropriate SMTP server
|
||||||
|
at hand.
|
||||||
|
|
||||||
|
To this end, we will continue to support email verification delegation via the
|
||||||
|
``vector.im`` and ``matrix.org`` identity servers for two months. Support for
|
||||||
|
delegated email verification will be disabled on Monday 2nd December.
|
||||||
|
|
||||||
|
The ``account_threepid_delegates`` dictionary defines whether the homeserver
|
||||||
|
should delegate an external server (typically an `identity server
|
||||||
|
<https://matrix.org/docs/spec/identity_service/r0.2.1>`_) to handle sending
|
||||||
|
confirmation messages via email and SMS.
|
||||||
|
|
||||||
|
So to delegate email verification, in ``homeserver.yaml``, set
|
||||||
|
``account_threepid_delegates.email`` to the base URL of an identity server. For
|
||||||
|
example:
|
||||||
|
|
||||||
|
.. code:: yaml
|
||||||
|
|
||||||
|
account_threepid_delegates:
|
||||||
|
email: https://example.com # Delegate email sending to example.com
|
||||||
|
|
||||||
|
Note that ``account_threepid_delegates.email`` replaces the deprecated
|
||||||
|
``email.trust_identity_server_for_password_resets``: if
|
||||||
|
``email.trust_identity_server_for_password_resets`` is set to ``true``, and
|
||||||
|
``account_threepid_delegates.email`` is not set, then the first entry in
|
||||||
|
``trusted_third_party_id_servers`` will be used as the
|
||||||
|
``account_threepid_delegate`` for email. This is to ensure compatibility with
|
||||||
|
existing Synapse installs that set up external server handling for these tasks
|
||||||
|
before v1.4.0. If ``email.trust_identity_server_for_password_resets`` is
|
||||||
|
``true`` and no trusted identity server domains are configured, Synapse will
|
||||||
|
report an error and refuse to start.
|
||||||
|
|
||||||
|
If ``email.trust_identity_server_for_password_resets`` is ``false`` or absent
|
||||||
|
and no ``email`` delegate is configured in ``account_threepid_delegates``,
|
||||||
|
then Synapse will send email verification messages itself, using the configured
|
||||||
|
SMTP server (see above).
|
||||||
|
that type.
|
||||||
|
|
||||||
|
Phone numbers
|
||||||
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Synapse does not support phone-number verification itself, so the only way to
|
||||||
|
maintain the ability for users to add phone numbers to their accounts will be
|
||||||
|
by continuing to delegate phone number verification to the ``matrix.org`` and
|
||||||
|
``vector.im`` identity servers (or another identity server that supports SMS
|
||||||
|
sending).
|
||||||
|
|
||||||
|
The ``account_threepid_delegates`` dictionary defines whether the homeserver
|
||||||
|
should delegate an external server (typically an `identity server
|
||||||
|
<https://matrix.org/docs/spec/identity_service/r0.2.1>`_) to handle sending
|
||||||
|
confirmation messages via email and SMS.
|
||||||
|
|
||||||
|
So to delegate phone number verification, in ``homeserver.yaml``, set
|
||||||
|
``account_threepid_delegates.msisdn`` to the base URL of an identity
|
||||||
|
server. For example:
|
||||||
|
|
||||||
|
.. code:: yaml
|
||||||
|
|
||||||
|
account_threepid_delegates:
|
||||||
|
msisdn: https://example.com # Delegate sms sending to example.com
|
||||||
|
|
||||||
|
The ``matrix.org`` and ``vector.im`` identity servers will continue to support
|
||||||
|
delegated phone number verification via SMS until such time as it is possible
|
||||||
|
for admins to configure their servers to perform phone number verification
|
||||||
|
directly. More details will follow in a future release.
|
||||||
|
|
||||||
|
Rolling back to v1.3.1
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
If you encounter problems with v1.4.0, it should be possible to roll back to
|
||||||
|
v1.3.1, subject to the following:
|
||||||
|
|
||||||
|
* The 'room statistics' engine was heavily reworked in this release (see
|
||||||
|
`#5971 <https://github.com/matrix-org/synapse/pull/5971>`_), including
|
||||||
|
significant changes to the database schema, which are not easily
|
||||||
|
reverted. This will cause the room statistics engine to stop updating when
|
||||||
|
you downgrade.
|
||||||
|
|
||||||
|
The room statistics are essentially unused in v1.3.1 (in future versions of
|
||||||
|
Synapse, they will be used to populate the room directory), so there should
|
||||||
|
be no loss of functionality. However, the statistics engine will write errors
|
||||||
|
to the logs, which can be avoided by setting the following in
|
||||||
|
`homeserver.yaml`:
|
||||||
|
|
||||||
|
.. code:: yaml
|
||||||
|
|
||||||
|
stats:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
Don't forget to re-enable it when you upgrade again, in preparation for its
|
||||||
|
use in the room directory!
|
||||||
|
|
||||||
Upgrading to v1.2.0
|
Upgrading to v1.2.0
|
||||||
===================
|
===================
|
||||||
|
|
||||||
Some counter metrics have been renamed, with the old names deprecated. See
|
Some counter metrics have been renamed, with the old names deprecated. See
|
||||||
`the metrics documentation <docs/metrics-howto.rst#renaming-of-metrics--deprecation-of-old-names-in-12>`_
|
`the metrics documentation <docs/metrics-howto.md#renaming-of-metrics--deprecation-of-old-names-in-12>`_
|
||||||
for details.
|
for details.
|
||||||
|
|
||||||
Upgrading to v1.1.0
|
Upgrading to v1.1.0
|
||||||
|
@ -132,6 +342,19 @@ server for password resets, set ``trust_identity_server_for_password_resets`` to
|
||||||
See the `sample configuration file <docs/sample_config.yaml>`_
|
See the `sample configuration file <docs/sample_config.yaml>`_
|
||||||
for more details on these settings.
|
for more details on these settings.
|
||||||
|
|
||||||
|
New email templates
|
||||||
|
---------------
|
||||||
|
Some new templates have been added to the default template directory for the purpose of the
|
||||||
|
homeserver sending its own password reset emails. If you have configured a custom
|
||||||
|
``template_dir`` in your Synapse config, these files will need to be added.
|
||||||
|
|
||||||
|
``password_reset.html`` and ``password_reset.txt`` are HTML and plain text templates
|
||||||
|
respectively that contain the contents of what will be emailed to the user upon attempting to
|
||||||
|
reset their password via email. ``password_reset_success.html`` and
|
||||||
|
``password_reset_failure.html`` are HTML files that the content of which (assuming no redirect
|
||||||
|
URL is set) will be shown to the user after they attempt to click the link in the email sent
|
||||||
|
to them.
|
||||||
|
|
||||||
Upgrading to v0.99.0
|
Upgrading to v0.99.0
|
||||||
====================
|
====================
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,8 @@ from signedjson.sign import verify_signed_json, SignatureVerifyException
|
||||||
|
|
||||||
CONFIG_JSON = "cmdclient_config.json"
|
CONFIG_JSON = "cmdclient_config.json"
|
||||||
|
|
||||||
|
# TODO: The concept of trusted identity servers has been deprecated. This option and checks
|
||||||
|
# should be removed
|
||||||
TRUSTED_ID_SERVERS = ["localhost:8001"]
|
TRUSTED_ID_SERVERS = ["localhost:8001"]
|
||||||
|
|
||||||
|
|
||||||
|
@ -268,6 +270,7 @@ class SynapseCmd(cmd.Cmd):
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def _do_emailrequest(self, args):
|
def _do_emailrequest(self, args):
|
||||||
|
# TODO: Update to use v2 Identity Service API endpoint
|
||||||
url = (
|
url = (
|
||||||
self._identityServerUrl()
|
self._identityServerUrl()
|
||||||
+ "/_matrix/identity/api/v1/validate/email/requestToken"
|
+ "/_matrix/identity/api/v1/validate/email/requestToken"
|
||||||
|
@ -302,6 +305,7 @@ class SynapseCmd(cmd.Cmd):
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def _do_emailvalidate(self, args):
|
def _do_emailvalidate(self, args):
|
||||||
|
# TODO: Update to use v2 Identity Service API endpoint
|
||||||
url = (
|
url = (
|
||||||
self._identityServerUrl()
|
self._identityServerUrl()
|
||||||
+ "/_matrix/identity/api/v1/validate/email/submitToken"
|
+ "/_matrix/identity/api/v1/validate/email/submitToken"
|
||||||
|
@ -330,6 +334,7 @@ class SynapseCmd(cmd.Cmd):
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def _do_3pidbind(self, args):
|
def _do_3pidbind(self, args):
|
||||||
|
# TODO: Update to use v2 Identity Service API endpoint
|
||||||
url = self._identityServerUrl() + "/_matrix/identity/api/v1/3pid/bind"
|
url = self._identityServerUrl() + "/_matrix/identity/api/v1/3pid/bind"
|
||||||
|
|
||||||
json_res = yield self.http_client.do_request(
|
json_res = yield self.http_client.do_request(
|
||||||
|
@ -398,6 +403,7 @@ class SynapseCmd(cmd.Cmd):
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def _do_invite(self, roomid, userstring):
|
def _do_invite(self, roomid, userstring):
|
||||||
if not userstring.startswith("@") and self._is_on("complete_usernames"):
|
if not userstring.startswith("@") and self._is_on("complete_usernames"):
|
||||||
|
# TODO: Update to use v2 Identity Service API endpoint
|
||||||
url = self._identityServerUrl() + "/_matrix/identity/api/v1/lookup"
|
url = self._identityServerUrl() + "/_matrix/identity/api/v1/lookup"
|
||||||
|
|
||||||
json_res = yield self.http_client.do_request(
|
json_res = yield self.http_client.do_request(
|
||||||
|
@ -407,6 +413,7 @@ class SynapseCmd(cmd.Cmd):
|
||||||
mxid = None
|
mxid = None
|
||||||
|
|
||||||
if "mxid" in json_res and "signatures" in json_res:
|
if "mxid" in json_res and "signatures" in json_res:
|
||||||
|
# TODO: Update to use v2 Identity Service API endpoint
|
||||||
url = (
|
url = (
|
||||||
self._identityServerUrl()
|
self._identityServerUrl()
|
||||||
+ "/_matrix/identity/api/v1/pubkey/ed25519"
|
+ "/_matrix/identity/api/v1/pubkey/ed25519"
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
|
matrix-synapse-py3 (1.4.0) stable; urgency=medium
|
||||||
|
|
||||||
|
* New synapse release 1.4.0.
|
||||||
|
|
||||||
|
-- Synapse Packaging team <packages@matrix.org> Thu, 03 Oct 2019 13:22:25 +0100
|
||||||
|
|
||||||
matrix-synapse-py3 (1.3.1) stable; urgency=medium
|
matrix-synapse-py3 (1.3.1) stable; urgency=medium
|
||||||
|
|
||||||
* New synapse release 1.3.1.
|
* New synapse release 1.3.1.
|
||||||
|
|
|
@ -17,7 +17,7 @@ By default, the image expects a single volume, located at ``/data``, that will h
|
||||||
* the appservices configuration.
|
* the appservices configuration.
|
||||||
|
|
||||||
You are free to use separate volumes depending on storage endpoints at your
|
You are free to use separate volumes depending on storage endpoints at your
|
||||||
disposal. For instance, ``/data/media`` coud be stored on a large but low
|
disposal. For instance, ``/data/media`` could be stored on a large but low
|
||||||
performance hdd storage while other files could be stored on high performance
|
performance hdd storage while other files could be stored on high performance
|
||||||
endpoints.
|
endpoints.
|
||||||
|
|
||||||
|
@ -27,8 +27,8 @@ configuration file there. Multiple application services are supported.
|
||||||
|
|
||||||
## Generating a configuration file
|
## Generating a configuration file
|
||||||
|
|
||||||
The first step is to genearte a valid config file. To do this, you can run the
|
The first step is to generate a valid config file. To do this, you can run the
|
||||||
image with the `generate` commandline option.
|
image with the `generate` command line option.
|
||||||
|
|
||||||
You will need to specify values for the `SYNAPSE_SERVER_NAME` and
|
You will need to specify values for the `SYNAPSE_SERVER_NAME` and
|
||||||
`SYNAPSE_REPORT_STATS` environment variable, and mount a docker volume to store
|
`SYNAPSE_REPORT_STATS` environment variable, and mount a docker volume to store
|
||||||
|
@ -59,7 +59,7 @@ The following environment variables are supported in `generate` mode:
|
||||||
* `SYNAPSE_CONFIG_PATH`: path to the file to be generated. Defaults to
|
* `SYNAPSE_CONFIG_PATH`: path to the file to be generated. Defaults to
|
||||||
`<SYNAPSE_CONFIG_DIR>/homeserver.yaml`.
|
`<SYNAPSE_CONFIG_DIR>/homeserver.yaml`.
|
||||||
* `SYNAPSE_DATA_DIR`: where the generated config will put persistent data
|
* `SYNAPSE_DATA_DIR`: where the generated config will put persistent data
|
||||||
such as the datatase and media store. Defaults to `/data`.
|
such as the database and media store. Defaults to `/data`.
|
||||||
* `UID`, `GID`: the user id and group id to use for creating the data
|
* `UID`, `GID`: the user id and group id to use for creating the data
|
||||||
directories. Defaults to `991`, `991`.
|
directories. Defaults to `991`, `991`.
|
||||||
|
|
||||||
|
@ -89,6 +89,8 @@ The following environment variables are supported in run mode:
|
||||||
`/data`.
|
`/data`.
|
||||||
* `SYNAPSE_CONFIG_PATH`: path to the config file. Defaults to
|
* `SYNAPSE_CONFIG_PATH`: path to the config file. Defaults to
|
||||||
`<SYNAPSE_CONFIG_DIR>/homeserver.yaml`.
|
`<SYNAPSE_CONFIG_DIR>/homeserver.yaml`.
|
||||||
|
* `SYNAPSE_WORKER`: module to execute, used when running synapse with workers.
|
||||||
|
Defaults to `synapse.app.homeserver`, which is suitable for non-worker mode.
|
||||||
* `UID`, `GID`: the user and group id to run Synapse as. Defaults to `991`, `991`.
|
* `UID`, `GID`: the user and group id to run Synapse as. Defaults to `991`, `991`.
|
||||||
* `TZ`: the [timezone](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) the container will run with. Defaults to `UTC`.
|
* `TZ`: the [timezone](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) the container will run with. Defaults to `UTC`.
|
||||||
|
|
||||||
|
@ -115,7 +117,7 @@ not given).
|
||||||
|
|
||||||
To migrate from a dynamic configuration file to a static one, run the docker
|
To migrate from a dynamic configuration file to a static one, run the docker
|
||||||
container once with the environment variables set, and `migrate_config`
|
container once with the environment variables set, and `migrate_config`
|
||||||
commandline option. For example:
|
command line option. For example:
|
||||||
|
|
||||||
```
|
```
|
||||||
docker run -it --rm \
|
docker run -it --rm \
|
||||||
|
|
|
@ -41,8 +41,8 @@ def generate_config_from_template(config_dir, config_path, environ, ownership):
|
||||||
config_dir (str): where to put generated config files
|
config_dir (str): where to put generated config files
|
||||||
config_path (str): where to put the main config file
|
config_path (str): where to put the main config file
|
||||||
environ (dict): environment dictionary
|
environ (dict): environment dictionary
|
||||||
ownership (str): "<user>:<group>" string which will be used to set
|
ownership (str|None): "<user>:<group>" string which will be used to set
|
||||||
ownership of the generated configs
|
ownership of the generated configs. If None, ownership will not change.
|
||||||
"""
|
"""
|
||||||
for v in ("SYNAPSE_SERVER_NAME", "SYNAPSE_REPORT_STATS"):
|
for v in ("SYNAPSE_SERVER_NAME", "SYNAPSE_REPORT_STATS"):
|
||||||
if v not in environ:
|
if v not in environ:
|
||||||
|
@ -105,24 +105,24 @@ def generate_config_from_template(config_dir, config_path, environ, ownership):
|
||||||
log("Generating log config file " + log_config_file)
|
log("Generating log config file " + log_config_file)
|
||||||
convert("/conf/log.config", log_config_file, environ)
|
convert("/conf/log.config", log_config_file, environ)
|
||||||
|
|
||||||
subprocess.check_output(["chown", "-R", ownership, "/data"])
|
|
||||||
|
|
||||||
# Hopefully we already have a signing key, but generate one if not.
|
# Hopefully we already have a signing key, but generate one if not.
|
||||||
subprocess.check_output(
|
args = [
|
||||||
[
|
"python",
|
||||||
"su-exec",
|
"-m",
|
||||||
ownership,
|
"synapse.app.homeserver",
|
||||||
"python",
|
"--config-path",
|
||||||
"-m",
|
config_path,
|
||||||
"synapse.app.homeserver",
|
# tell synapse to put generated keys in /data rather than /compiled
|
||||||
"--config-path",
|
"--keys-directory",
|
||||||
config_path,
|
config_dir,
|
||||||
# tell synapse to put generated keys in /data rather than /compiled
|
"--generate-keys",
|
||||||
"--keys-directory",
|
]
|
||||||
config_dir,
|
|
||||||
"--generate-keys",
|
if ownership is not None:
|
||||||
]
|
subprocess.check_output(["chown", "-R", ownership, "/data"])
|
||||||
)
|
args = ["su-exec", ownership] + args
|
||||||
|
|
||||||
|
subprocess.check_output(args)
|
||||||
|
|
||||||
|
|
||||||
def run_generate_config(environ, ownership):
|
def run_generate_config(environ, ownership):
|
||||||
|
@ -130,7 +130,7 @@ def run_generate_config(environ, ownership):
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
environ (dict): env var dict
|
environ (dict): env var dict
|
||||||
ownership (str): "userid:groupid" arg for chmod
|
ownership (str|None): "userid:groupid" arg for chmod. If None, ownership will not change.
|
||||||
|
|
||||||
Never returns.
|
Never returns.
|
||||||
"""
|
"""
|
||||||
|
@ -149,9 +149,6 @@ def run_generate_config(environ, ownership):
|
||||||
log("Creating log config %s" % (log_config_file,))
|
log("Creating log config %s" % (log_config_file,))
|
||||||
convert("/conf/log.config", log_config_file, environ)
|
convert("/conf/log.config", log_config_file, environ)
|
||||||
|
|
||||||
# make sure that synapse has perms to write to the data dir.
|
|
||||||
subprocess.check_output(["chown", ownership, data_dir])
|
|
||||||
|
|
||||||
args = [
|
args = [
|
||||||
"python",
|
"python",
|
||||||
"-m",
|
"-m",
|
||||||
|
@ -170,12 +167,34 @@ def run_generate_config(environ, ownership):
|
||||||
"--open-private-ports",
|
"--open-private-ports",
|
||||||
]
|
]
|
||||||
# log("running %s" % (args, ))
|
# log("running %s" % (args, ))
|
||||||
os.execv("/usr/local/bin/python", args)
|
|
||||||
|
if ownership is not None:
|
||||||
|
args = ["su-exec", ownership] + args
|
||||||
|
os.execv("/sbin/su-exec", args)
|
||||||
|
|
||||||
|
# make sure that synapse has perms to write to the data dir.
|
||||||
|
subprocess.check_output(["chown", ownership, data_dir])
|
||||||
|
else:
|
||||||
|
os.execv("/usr/local/bin/python", args)
|
||||||
|
|
||||||
|
|
||||||
def main(args, environ):
|
def main(args, environ):
|
||||||
mode = args[1] if len(args) > 1 else None
|
mode = args[1] if len(args) > 1 else None
|
||||||
ownership = "{}:{}".format(environ.get("UID", 991), environ.get("GID", 991))
|
desired_uid = int(environ.get("UID", "991"))
|
||||||
|
desired_gid = int(environ.get("GID", "991"))
|
||||||
|
synapse_worker = environ.get("SYNAPSE_WORKER", "synapse.app.homeserver")
|
||||||
|
if (desired_uid == os.getuid()) and (desired_gid == os.getgid()):
|
||||||
|
ownership = None
|
||||||
|
else:
|
||||||
|
ownership = "{}:{}".format(desired_uid, desired_gid)
|
||||||
|
|
||||||
|
log(
|
||||||
|
"Container running as UserID %s:%s, ENV (or defaults) requests %s:%s"
|
||||||
|
% (os.getuid(), os.getgid(), desired_uid, desired_gid)
|
||||||
|
)
|
||||||
|
|
||||||
|
if ownership is None:
|
||||||
|
log("Will not perform chmod/su-exec as UserID already matches request")
|
||||||
|
|
||||||
# In generate mode, generate a configuration and missing keys, then exit
|
# In generate mode, generate a configuration and missing keys, then exit
|
||||||
if mode == "generate":
|
if mode == "generate":
|
||||||
|
@ -227,16 +246,12 @@ def main(args, environ):
|
||||||
|
|
||||||
log("Starting synapse with config file " + config_path)
|
log("Starting synapse with config file " + config_path)
|
||||||
|
|
||||||
args = [
|
args = ["python", "-m", synapse_worker, "--config-path", config_path]
|
||||||
"su-exec",
|
if ownership is not None:
|
||||||
ownership,
|
args = ["su-exec", ownership] + args
|
||||||
"python",
|
os.execv("/sbin/su-exec", args)
|
||||||
"-m",
|
else:
|
||||||
"synapse.app.homeserver",
|
os.execv("/usr/local/bin/python", args)
|
||||||
"--config-path",
|
|
||||||
config_path,
|
|
||||||
]
|
|
||||||
os.execv("/sbin/su-exec", args)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
@ -1,30 +1,31 @@
|
||||||
|
# Overview
|
||||||
Captcha can be enabled for this home server. This file explains how to do that.
|
Captcha can be enabled for this home server. This file explains how to do that.
|
||||||
The captcha mechanism used is Google's ReCaptcha. This requires API keys from Google.
|
The captcha mechanism used is Google's ReCaptcha. This requires API keys from Google.
|
||||||
|
|
||||||
Getting keys
|
## Getting keys
|
||||||
------------
|
|
||||||
Requires a public/private key pair from:
|
Requires a public/private key pair from:
|
||||||
|
|
||||||
https://developers.google.com/recaptcha/
|
<https://developers.google.com/recaptcha/>
|
||||||
|
|
||||||
Must be a reCAPTCHA v2 key using the "I'm not a robot" Checkbox option
|
Must be a reCAPTCHA v2 key using the "I'm not a robot" Checkbox option
|
||||||
|
|
||||||
Setting ReCaptcha Keys
|
## Setting ReCaptcha Keys
|
||||||
----------------------
|
|
||||||
The keys are a config option on the home server config. If they are not
|
The keys are a config option on the home server config. If they are not
|
||||||
visible, you can generate them via --generate-config. Set the following value::
|
visible, you can generate them via `--generate-config`. Set the following value:
|
||||||
|
|
||||||
recaptcha_public_key: YOUR_PUBLIC_KEY
|
recaptcha_public_key: YOUR_PUBLIC_KEY
|
||||||
recaptcha_private_key: YOUR_PRIVATE_KEY
|
recaptcha_private_key: YOUR_PRIVATE_KEY
|
||||||
|
|
||||||
In addition, you MUST enable captchas via::
|
In addition, you MUST enable captchas via:
|
||||||
|
|
||||||
enable_registration_captcha: true
|
enable_registration_captcha: true
|
||||||
|
|
||||||
|
## Configuring IP used for auth
|
||||||
|
|
||||||
Configuring IP used for auth
|
|
||||||
----------------------------
|
|
||||||
The ReCaptcha API requires that the IP address of the user who solved the
|
The ReCaptcha API requires that the IP address of the user who solved the
|
||||||
captcha is sent. If the client is connecting through a proxy or load balancer,
|
captcha is sent. If the client is connecting through a proxy or load balancer,
|
||||||
it may be required to use the X-Forwarded-For (XFF) header instead of the origin
|
it may be required to use the `X-Forwarded-For` (XFF) header instead of the origin
|
||||||
IP address. This can be configured using the x_forwarded directive in the
|
IP address. This can be configured using the `x_forwarded` directive in the
|
||||||
listeners section of the homeserver.yaml configuration file.
|
listeners section of the homeserver.yaml configuration file.
|
|
@ -147,7 +147,7 @@ your domain, you can simply route all traffic through the reverse proxy by
|
||||||
updating the SRV record appropriately (or removing it, if the proxy listens on
|
updating the SRV record appropriately (or removing it, if the proxy listens on
|
||||||
8448).
|
8448).
|
||||||
|
|
||||||
See [reverse_proxy.rst](reverse_proxy.rst) for information on setting up a
|
See [reverse_proxy.md](reverse_proxy.md) for information on setting up a
|
||||||
reverse proxy.
|
reverse proxy.
|
||||||
|
|
||||||
#### Option 3: add a .well-known file to delegate your matrix traffic
|
#### Option 3: add a .well-known file to delegate your matrix traffic
|
||||||
|
@ -319,7 +319,7 @@ We no longer actively recommend against using a reverse proxy. Many admins will
|
||||||
find it easier to direct federation traffic to a reverse proxy and manage their
|
find it easier to direct federation traffic to a reverse proxy and manage their
|
||||||
own TLS certificates, and this is a supported configuration.
|
own TLS certificates, and this is a supported configuration.
|
||||||
|
|
||||||
See [reverse_proxy.rst](reverse_proxy.rst) for information on setting up a
|
See [reverse_proxy.md](reverse_proxy.md) for information on setting up a
|
||||||
reverse proxy.
|
reverse proxy.
|
||||||
|
|
||||||
### Do I still need to give my TLS certificates to Synapse if I am using a reverse proxy?
|
### Do I still need to give my TLS certificates to Synapse if I am using a reverse proxy?
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
# Synapse Documentation
|
||||||
|
|
||||||
|
This directory contains documentation specific to the `synapse` homeserver.
|
||||||
|
|
||||||
|
All matrix-generic documentation now lives in its own project, located at [matrix-org/matrix-doc](https://github.com/matrix-org/matrix-doc)
|
||||||
|
|
||||||
|
(Note: some items here may be moved to [matrix-org/matrix-doc](https://github.com/matrix-org/matrix-doc) at some point in the future.)
|
|
@ -1,6 +0,0 @@
|
||||||
All matrix-generic documentation now lives in its own project at
|
|
||||||
|
|
||||||
github.com/matrix-org/matrix-doc.git
|
|
||||||
|
|
||||||
Only Synapse implementation-specific documentation lives here now
|
|
||||||
(together with some older stuff will be shortly migrated over to matrix-doc)
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
Purge room API
|
||||||
|
==============
|
||||||
|
|
||||||
|
This API will remove all trace of a room from your database.
|
||||||
|
|
||||||
|
All local users must have left the room before it can be removed.
|
||||||
|
|
||||||
|
The API is:
|
||||||
|
|
||||||
|
```
|
||||||
|
POST /_synapse/admin/v1/purge_room
|
||||||
|
|
||||||
|
{
|
||||||
|
"room_id": "!room:id"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You must authenticate using the access token of an admin user.
|
|
@ -84,3 +84,42 @@ with a body of:
|
||||||
}
|
}
|
||||||
|
|
||||||
including an ``access_token`` of a server admin.
|
including an ``access_token`` of a server admin.
|
||||||
|
|
||||||
|
|
||||||
|
Get whether a user is a server administrator or not
|
||||||
|
===================================================
|
||||||
|
|
||||||
|
|
||||||
|
The api is::
|
||||||
|
|
||||||
|
GET /_synapse/admin/v1/users/<user_id>/admin
|
||||||
|
|
||||||
|
including an ``access_token`` of a server admin.
|
||||||
|
|
||||||
|
A response body like the following is returned:
|
||||||
|
|
||||||
|
.. code:: json
|
||||||
|
|
||||||
|
{
|
||||||
|
"admin": true
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Change whether a user is a server administrator or not
|
||||||
|
======================================================
|
||||||
|
|
||||||
|
Note that you cannot demote yourself.
|
||||||
|
|
||||||
|
The api is::
|
||||||
|
|
||||||
|
PUT /_synapse/admin/v1/users/<user_id>/admin
|
||||||
|
|
||||||
|
with a body of:
|
||||||
|
|
||||||
|
.. code:: json
|
||||||
|
|
||||||
|
{
|
||||||
|
"admin": true
|
||||||
|
}
|
||||||
|
|
||||||
|
including an ``access_token`` of a server admin.
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
> **Warning**
|
||||||
|
> These architecture notes are spectacularly old, and date back
|
||||||
|
> to when Synapse was just federation code in isolation. This should be
|
||||||
|
> merged into the main spec.
|
||||||
|
|
||||||
|
# Server to Server
|
||||||
|
|
||||||
|
## Server to Server Stack
|
||||||
|
|
||||||
|
To use the server to server stack, home servers should only need to
|
||||||
|
interact with the Messaging layer.
|
||||||
|
|
||||||
|
The server to server side of things is designed into 4 distinct layers:
|
||||||
|
|
||||||
|
1. Messaging Layer
|
||||||
|
2. Pdu Layer
|
||||||
|
3. Transaction Layer
|
||||||
|
4. Transport Layer
|
||||||
|
|
||||||
|
Where the bottom (the transport layer) is what talks to the internet via
|
||||||
|
HTTP, and the top (the messaging layer) talks to the rest of the Home
|
||||||
|
Server with a domain specific API.
|
||||||
|
|
||||||
|
1. **Messaging Layer**
|
||||||
|
|
||||||
|
This is what the rest of the Home Server hits to send messages, join rooms,
|
||||||
|
etc. It also allows you to register callbacks for when it get's notified by
|
||||||
|
lower levels that e.g. a new message has been received.
|
||||||
|
|
||||||
|
It is responsible for serializing requests to send to the data
|
||||||
|
layer, and to parse requests received from the data layer.
|
||||||
|
|
||||||
|
2. **PDU Layer**
|
||||||
|
|
||||||
|
This layer handles:
|
||||||
|
|
||||||
|
- duplicate `pdu_id`'s - i.e., it makes sure we ignore them.
|
||||||
|
- responding to requests for a given `pdu_id`
|
||||||
|
- responding to requests for all metadata for a given context (i.e. room)
|
||||||
|
- handling incoming backfill requests
|
||||||
|
|
||||||
|
So it has to parse incoming messages to discover which are metadata and
|
||||||
|
which aren't, and has to correctly clobber existing metadata where
|
||||||
|
appropriate.
|
||||||
|
|
||||||
|
For incoming PDUs, it has to check the PDUs it references to see
|
||||||
|
if we have missed any. If we have go and ask someone (another
|
||||||
|
home server) for it.
|
||||||
|
|
||||||
|
3. **Transaction Layer**
|
||||||
|
|
||||||
|
This layer makes incoming requests idempotent. i.e., it stores
|
||||||
|
which transaction id's we have seen and what our response were.
|
||||||
|
If we have already seen a message with the given transaction id,
|
||||||
|
we do not notify higher levels but simply respond with the
|
||||||
|
previous response.
|
||||||
|
|
||||||
|
`transaction_id` is from "`GET /send/<tx_id>/`"
|
||||||
|
|
||||||
|
It's also responsible for batching PDUs into single transaction for
|
||||||
|
sending to remote destinations, so that we only ever have one
|
||||||
|
transaction in flight to a given destination at any one time.
|
||||||
|
|
||||||
|
This is also responsible for answering requests for things after a
|
||||||
|
given set of transactions, i.e., ask for everything after 'ver' X.
|
||||||
|
|
||||||
|
4. **Transport Layer**
|
||||||
|
|
||||||
|
This is responsible for starting a HTTP server and hitting the
|
||||||
|
correct callbacks on the Transaction layer, as well as sending
|
||||||
|
both data and requests for data.
|
||||||
|
|
||||||
|
## Persistence
|
||||||
|
|
||||||
|
We persist things in a single sqlite3 database. All database queries get
|
||||||
|
run on a separate, dedicated thread. This that we only ever have one
|
||||||
|
query running at a time, making it a lot easier to do things in a safe
|
||||||
|
manner.
|
||||||
|
|
||||||
|
The queries are located in the `synapse.persistence.transactions` module,
|
||||||
|
and the table information in the `synapse.persistence.tables` module.
|
|
@ -1,59 +0,0 @@
|
||||||
.. WARNING::
|
|
||||||
These architecture notes are spectacularly old, and date back to when Synapse
|
|
||||||
was just federation code in isolation. This should be merged into the main
|
|
||||||
spec.
|
|
||||||
|
|
||||||
|
|
||||||
= Server to Server =
|
|
||||||
|
|
||||||
== Server to Server Stack ==
|
|
||||||
|
|
||||||
To use the server to server stack, home servers should only need to interact with the Messaging layer.
|
|
||||||
|
|
||||||
The server to server side of things is designed into 4 distinct layers:
|
|
||||||
|
|
||||||
1. Messaging Layer
|
|
||||||
2. Pdu Layer
|
|
||||||
3. Transaction Layer
|
|
||||||
4. Transport Layer
|
|
||||||
|
|
||||||
Where the bottom (the transport layer) is what talks to the internet via HTTP, and the top (the messaging layer) talks to the rest of the Home Server with a domain specific API.
|
|
||||||
|
|
||||||
1. Messaging Layer
|
|
||||||
This is what the rest of the Home Server hits to send messages, join rooms, etc. It also allows you to register callbacks for when it get's notified by lower levels that e.g. a new message has been received.
|
|
||||||
|
|
||||||
It is responsible for serializing requests to send to the data layer, and to parse requests received from the data layer.
|
|
||||||
|
|
||||||
|
|
||||||
2. PDU Layer
|
|
||||||
This layer handles:
|
|
||||||
* duplicate pdu_id's - i.e., it makes sure we ignore them.
|
|
||||||
* responding to requests for a given pdu_id
|
|
||||||
* responding to requests for all metadata for a given context (i.e. room)
|
|
||||||
* handling incoming backfill requests
|
|
||||||
|
|
||||||
So it has to parse incoming messages to discover which are metadata and which aren't, and has to correctly clobber existing metadata where appropriate.
|
|
||||||
|
|
||||||
For incoming PDUs, it has to check the PDUs it references to see if we have missed any. If we have go and ask someone (another home server) for it.
|
|
||||||
|
|
||||||
|
|
||||||
3. Transaction Layer
|
|
||||||
This layer makes incoming requests idempotent. I.e., it stores which transaction id's we have seen and what our response were. If we have already seen a message with the given transaction id, we do not notify higher levels but simply respond with the previous response.
|
|
||||||
|
|
||||||
transaction_id is from "GET /send/<tx_id>/"
|
|
||||||
|
|
||||||
It's also responsible for batching PDUs into single transaction for sending to remote destinations, so that we only ever have one transaction in flight to a given destination at any one time.
|
|
||||||
|
|
||||||
This is also responsible for answering requests for things after a given set of transactions, i.e., ask for everything after 'ver' X.
|
|
||||||
|
|
||||||
|
|
||||||
4. Transport Layer
|
|
||||||
This is responsible for starting a HTTP server and hitting the correct callbacks on the Transaction layer, as well as sending both data and requests for data.
|
|
||||||
|
|
||||||
|
|
||||||
== Persistence ==
|
|
||||||
|
|
||||||
We persist things in a single sqlite3 database. All database queries get run on a separate, dedicated thread. This that we only ever have one query running at a time, making it a lot easier to do things in a safe manner.
|
|
||||||
|
|
||||||
The queries are located in the synapse.persistence.transactions module, and the table information in the synapse.persistence.tables module.
|
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
# Registering an Application Service
|
||||||
|
|
||||||
|
The registration of new application services depends on the homeserver used.
|
||||||
|
In synapse, you need to create a new configuration file for your AS and add it
|
||||||
|
to the list specified under the `app_service_config_files` config
|
||||||
|
option in your synapse config.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
app_service_config_files:
|
||||||
|
- /home/matrix/.synapse/<your-AS>.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
The format of the AS configuration file is as follows:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
url: <base url of AS>
|
||||||
|
as_token: <token AS will add to requests to HS>
|
||||||
|
hs_token: <token HS will add to requests to AS>
|
||||||
|
sender_localpart: <localpart of AS user>
|
||||||
|
namespaces:
|
||||||
|
users: # List of users we're interested in
|
||||||
|
- exclusive: <bool>
|
||||||
|
regex: <regex>
|
||||||
|
- ...
|
||||||
|
aliases: [] # List of aliases we're interested in
|
||||||
|
rooms: [] # List of room ids we're interested in
|
||||||
|
```
|
||||||
|
|
||||||
|
See the [spec](https://matrix.org/docs/spec/application_service/unstable.html) for further details on how application services work.
|
|
@ -1,35 +0,0 @@
|
||||||
Registering an Application Service
|
|
||||||
==================================
|
|
||||||
|
|
||||||
The registration of new application services depends on the homeserver used.
|
|
||||||
In synapse, you need to create a new configuration file for your AS and add it
|
|
||||||
to the list specified under the ``app_service_config_files`` config
|
|
||||||
option in your synapse config.
|
|
||||||
|
|
||||||
For example:
|
|
||||||
|
|
||||||
.. code-block:: yaml
|
|
||||||
|
|
||||||
app_service_config_files:
|
|
||||||
- /home/matrix/.synapse/<your-AS>.yaml
|
|
||||||
|
|
||||||
|
|
||||||
The format of the AS configuration file is as follows:
|
|
||||||
|
|
||||||
.. code-block:: yaml
|
|
||||||
|
|
||||||
url: <base url of AS>
|
|
||||||
as_token: <token AS will add to requests to HS>
|
|
||||||
hs_token: <token HS will add to requests to AS>
|
|
||||||
sender_localpart: <localpart of AS user>
|
|
||||||
namespaces:
|
|
||||||
users: # List of users we're interested in
|
|
||||||
- exclusive: <bool>
|
|
||||||
regex: <regex>
|
|
||||||
- ...
|
|
||||||
aliases: [] # List of aliases we're interested in
|
|
||||||
rooms: [] # List of room ids we're interested in
|
|
||||||
|
|
||||||
See the spec_ for further details on how application services work.
|
|
||||||
|
|
||||||
.. _spec: https://matrix.org/docs/spec/application_service/unstable.html
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
# Synapse Architecture
|
||||||
|
|
||||||
|
As of the end of Oct 2014, Synapse's overall architecture looks like:
|
||||||
|
|
||||||
|
synapse
|
||||||
|
.-----------------------------------------------------.
|
||||||
|
| Notifier |
|
||||||
|
| ^ | |
|
||||||
|
| | | |
|
||||||
|
| .------------|------. |
|
||||||
|
| | handlers/ | | |
|
||||||
|
| | v | |
|
||||||
|
| | Event*Handler <--------> rest/* <=> Client
|
||||||
|
| | Rooms*Handler | |
|
||||||
|
HS <=> federation/* <==> FederationHandler | |
|
||||||
|
| | | PresenceHandler | |
|
||||||
|
| | | TypingHandler | |
|
||||||
|
| | '-------------------' |
|
||||||
|
| | | | |
|
||||||
|
| | state/* | |
|
||||||
|
| | | | |
|
||||||
|
| | v v |
|
||||||
|
| `--------------> storage/* |
|
||||||
|
| | |
|
||||||
|
'--------------------------|--------------------------'
|
||||||
|
v
|
||||||
|
.----.
|
||||||
|
| DB |
|
||||||
|
'----'
|
||||||
|
|
||||||
|
- Handlers: business logic of synapse itself. Follows a set contract of BaseHandler:
|
||||||
|
- BaseHandler gives us onNewRoomEvent which: (TODO: flesh this out and make it less cryptic):
|
||||||
|
- handle_state(event)
|
||||||
|
- auth(event)
|
||||||
|
- persist_event(event)
|
||||||
|
- notify notifier or federation(event)
|
||||||
|
- PresenceHandler: use distributor to get EDUs out of Federation.
|
||||||
|
Very lightweight logic built on the distributor
|
||||||
|
- TypingHandler: use distributor to get EDUs out of Federation.
|
||||||
|
Very lightweight logic built on the distributor
|
||||||
|
- EventsHandler: handles the events stream...
|
||||||
|
- FederationHandler: - gets PDU from Federation Layer; turns into
|
||||||
|
an event; follows basehandler functionality.
|
||||||
|
- RoomsHandler: does all the room logic, including members - lots
|
||||||
|
of classes in RoomsHandler.
|
||||||
|
- ProfileHandler: talks to the storage to store/retrieve profile
|
||||||
|
info.
|
||||||
|
- EventFactory: generates events of particular event types.
|
||||||
|
- Notifier: Backs the events handler
|
||||||
|
- REST: Interfaces handlers and events to the outside world via
|
||||||
|
HTTP/JSON. Converts events back and forth from JSON.
|
||||||
|
- Federation: holds the HTTP client & server to talk to other servers.
|
||||||
|
Does replication to make sure there's nothing missing in the graph.
|
||||||
|
Handles reliability. Handles txns.
|
||||||
|
- Distributor: generic event bus. used for presence & typing only
|
||||||
|
currently. Notifier could be implemented using Distributor - so far
|
||||||
|
we are only using for things which actually /require/ dynamic
|
||||||
|
pluggability however as it can obfuscate the actual flow of control.
|
||||||
|
- Auth: helper singleton to say whether a given event is allowed to do
|
||||||
|
a given thing (TODO: put this on the diagram)
|
||||||
|
- State: helper singleton: does state conflict resolution. You give it
|
||||||
|
an event and it tells you if it actually updates the state or not,
|
||||||
|
and annotates the event up properly and handles merge conflict
|
||||||
|
resolution.
|
||||||
|
- Storage: abstracts the storage engine.
|
|
@ -1,68 +0,0 @@
|
||||||
Synapse Architecture
|
|
||||||
====================
|
|
||||||
|
|
||||||
As of the end of Oct 2014, Synapse's overall architecture looks like::
|
|
||||||
|
|
||||||
synapse
|
|
||||||
.-----------------------------------------------------.
|
|
||||||
| Notifier |
|
|
||||||
| ^ | |
|
|
||||||
| | | |
|
|
||||||
| .------------|------. |
|
|
||||||
| | handlers/ | | |
|
|
||||||
| | v | |
|
|
||||||
| | Event*Handler <--------> rest/* <=> Client
|
|
||||||
| | Rooms*Handler | |
|
|
||||||
HSes <=> federation/* <==> FederationHandler | |
|
|
||||||
| | | PresenceHandler | |
|
|
||||||
| | | TypingHandler | |
|
|
||||||
| | '-------------------' |
|
|
||||||
| | | | |
|
|
||||||
| | state/* | |
|
|
||||||
| | | | |
|
|
||||||
| | v v |
|
|
||||||
| `--------------> storage/* |
|
|
||||||
| | |
|
|
||||||
'--------------------------|--------------------------'
|
|
||||||
v
|
|
||||||
.----.
|
|
||||||
| DB |
|
|
||||||
'----'
|
|
||||||
|
|
||||||
* Handlers: business logic of synapse itself. Follows a set contract of BaseHandler:
|
|
||||||
|
|
||||||
- BaseHandler gives us onNewRoomEvent which: (TODO: flesh this out and make it less cryptic):
|
|
||||||
|
|
||||||
+ handle_state(event)
|
|
||||||
+ auth(event)
|
|
||||||
+ persist_event(event)
|
|
||||||
+ notify notifier or federation(event)
|
|
||||||
|
|
||||||
- PresenceHandler: use distributor to get EDUs out of Federation. Very
|
|
||||||
lightweight logic built on the distributor
|
|
||||||
- TypingHandler: use distributor to get EDUs out of Federation. Very
|
|
||||||
lightweight logic built on the distributor
|
|
||||||
- EventsHandler: handles the events stream...
|
|
||||||
- FederationHandler: - gets PDU from Federation Layer; turns into an event;
|
|
||||||
follows basehandler functionality.
|
|
||||||
- RoomsHandler: does all the room logic, including members - lots of classes in
|
|
||||||
RoomsHandler.
|
|
||||||
- ProfileHandler: talks to the storage to store/retrieve profile info.
|
|
||||||
|
|
||||||
* EventFactory: generates events of particular event types.
|
|
||||||
* Notifier: Backs the events handler
|
|
||||||
* REST: Interfaces handlers and events to the outside world via HTTP/JSON.
|
|
||||||
Converts events back and forth from JSON.
|
|
||||||
* Federation: holds the HTTP client & server to talk to other servers. Does
|
|
||||||
replication to make sure there's nothing missing in the graph. Handles
|
|
||||||
reliability. Handles txns.
|
|
||||||
* Distributor: generic event bus. used for presence & typing only currently.
|
|
||||||
Notifier could be implemented using Distributor - so far we are only using for
|
|
||||||
things which actually /require/ dynamic pluggability however as it can
|
|
||||||
obfuscate the actual flow of control.
|
|
||||||
* Auth: helper singleton to say whether a given event is allowed to do a given
|
|
||||||
thing (TODO: put this on the diagram)
|
|
||||||
* State: helper singleton: does state conflict resolution. You give it an event
|
|
||||||
and it tells you if it actually updates the state or not, and annotates the
|
|
||||||
event up properly and handles merge conflict resolution.
|
|
||||||
* Storage: abstracts the storage engine.
|
|
|
@ -0,0 +1,169 @@
|
||||||
|
# Code Style
|
||||||
|
|
||||||
|
## Formatting tools
|
||||||
|
|
||||||
|
The Synapse codebase uses a number of code formatting tools in order to
|
||||||
|
quickly and automatically check for formatting (and sometimes logical)
|
||||||
|
errors in code.
|
||||||
|
|
||||||
|
The necessary tools are detailed below.
|
||||||
|
|
||||||
|
- **black**
|
||||||
|
|
||||||
|
The Synapse codebase uses [black](https://pypi.org/project/black/)
|
||||||
|
as an opinionated code formatter, ensuring all comitted code is
|
||||||
|
properly formatted.
|
||||||
|
|
||||||
|
First install `black` with:
|
||||||
|
|
||||||
|
pip install --upgrade black
|
||||||
|
|
||||||
|
Have `black` auto-format your code (it shouldn't change any
|
||||||
|
functionality) with:
|
||||||
|
|
||||||
|
black . --exclude="\.tox|build|env"
|
||||||
|
|
||||||
|
- **flake8**
|
||||||
|
|
||||||
|
`flake8` is a code checking tool. We require code to pass `flake8`
|
||||||
|
before being merged into the codebase.
|
||||||
|
|
||||||
|
Install `flake8` with:
|
||||||
|
|
||||||
|
pip install --upgrade flake8
|
||||||
|
|
||||||
|
Check all application and test code with:
|
||||||
|
|
||||||
|
flake8 synapse tests
|
||||||
|
|
||||||
|
- **isort**
|
||||||
|
|
||||||
|
`isort` ensures imports are nicely formatted, and can suggest and
|
||||||
|
auto-fix issues such as double-importing.
|
||||||
|
|
||||||
|
Install `isort` with:
|
||||||
|
|
||||||
|
pip install --upgrade isort
|
||||||
|
|
||||||
|
Auto-fix imports with:
|
||||||
|
|
||||||
|
isort -rc synapse tests
|
||||||
|
|
||||||
|
`-rc` means to recursively search the given directories.
|
||||||
|
|
||||||
|
It's worth noting that modern IDEs and text editors can run these tools
|
||||||
|
automatically on save. It may be worth looking into whether this
|
||||||
|
functionality is supported in your editor for a more convenient
|
||||||
|
development workflow. It is not, however, recommended to run `flake8` on
|
||||||
|
save as it takes a while and is very resource intensive.
|
||||||
|
|
||||||
|
## General rules
|
||||||
|
|
||||||
|
- **Naming**:
|
||||||
|
- Use camel case for class and type names
|
||||||
|
- Use underscores for functions and variables.
|
||||||
|
- **Docstrings**: should follow the [google code
|
||||||
|
style](https://google.github.io/styleguide/pyguide.html#38-comments-and-docstrings).
|
||||||
|
This is so that we can generate documentation with
|
||||||
|
[sphinx](http://sphinxcontrib-napoleon.readthedocs.org/en/latest/).
|
||||||
|
See the
|
||||||
|
[examples](http://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html)
|
||||||
|
in the sphinx documentation.
|
||||||
|
- **Imports**:
|
||||||
|
- Imports should be sorted by `isort` as described above.
|
||||||
|
- Prefer to import classes and functions rather than packages or
|
||||||
|
modules.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
from synapse.types import UserID
|
||||||
|
...
|
||||||
|
user_id = UserID(local, server)
|
||||||
|
|
||||||
|
is preferred over:
|
||||||
|
|
||||||
|
from synapse import types
|
||||||
|
...
|
||||||
|
user_id = types.UserID(local, server)
|
||||||
|
|
||||||
|
(or any other variant).
|
||||||
|
|
||||||
|
This goes against the advice in the Google style guide, but it
|
||||||
|
means that errors in the name are caught early (at import time).
|
||||||
|
|
||||||
|
- Avoid wildcard imports (`from synapse.types import *`) and
|
||||||
|
relative imports (`from .types import UserID`).
|
||||||
|
|
||||||
|
## Configuration file format
|
||||||
|
|
||||||
|
The [sample configuration file](./sample_config.yaml) acts as a
|
||||||
|
reference to Synapse's configuration options for server administrators.
|
||||||
|
Remember that many readers will be unfamiliar with YAML and server
|
||||||
|
administration in general, so that it is important that the file be as
|
||||||
|
easy to understand as possible, which includes following a consistent
|
||||||
|
format.
|
||||||
|
|
||||||
|
Some guidelines follow:
|
||||||
|
|
||||||
|
- Sections should be separated with a heading consisting of a single
|
||||||
|
line prefixed and suffixed with `##`. There should be **two** blank
|
||||||
|
lines before the section header, and **one** after.
|
||||||
|
- Each option should be listed in the file with the following format:
|
||||||
|
- A comment describing the setting. Each line of this comment
|
||||||
|
should be prefixed with a hash (`#`) and a space.
|
||||||
|
|
||||||
|
The comment should describe the default behaviour (ie, what
|
||||||
|
happens if the setting is omitted), as well as what the effect
|
||||||
|
will be if the setting is changed.
|
||||||
|
|
||||||
|
Often, the comment end with something like "uncomment the
|
||||||
|
following to <do action>".
|
||||||
|
|
||||||
|
- A line consisting of only `#`.
|
||||||
|
- A commented-out example setting, prefixed with only `#`.
|
||||||
|
|
||||||
|
For boolean (on/off) options, convention is that this example
|
||||||
|
should be the *opposite* to the default (so the comment will end
|
||||||
|
with "Uncomment the following to enable [or disable]
|
||||||
|
<feature>." For other options, the example should give some
|
||||||
|
non-default value which is likely to be useful to the reader.
|
||||||
|
|
||||||
|
- There should be a blank line between each option.
|
||||||
|
- Where several settings are grouped into a single dict, *avoid* the
|
||||||
|
convention where the whole block is commented out, resulting in
|
||||||
|
comment lines starting `# #`, as this is hard to read and confusing
|
||||||
|
to edit. Instead, leave the top-level config option uncommented, and
|
||||||
|
follow the conventions above for sub-options. Ensure that your code
|
||||||
|
correctly handles the top-level option being set to `None` (as it
|
||||||
|
will be if no sub-options are enabled).
|
||||||
|
- Lines should be wrapped at 80 characters.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
## Frobnication ##
|
||||||
|
|
||||||
|
# The frobnicator will ensure that all requests are fully frobnicated.
|
||||||
|
# To enable it, uncomment the following.
|
||||||
|
#
|
||||||
|
#frobnicator_enabled: true
|
||||||
|
|
||||||
|
# By default, the frobnicator will frobnicate with the default frobber.
|
||||||
|
# The following will make it use an alternative frobber.
|
||||||
|
#
|
||||||
|
#frobincator_frobber: special_frobber
|
||||||
|
|
||||||
|
# Settings for the frobber
|
||||||
|
#
|
||||||
|
frobber:
|
||||||
|
# frobbing speed. Defaults to 1.
|
||||||
|
#
|
||||||
|
#speed: 10
|
||||||
|
|
||||||
|
# frobbing distance. Defaults to 1000.
|
||||||
|
#
|
||||||
|
#distance: 100
|
||||||
|
|
||||||
|
Note that the sample configuration is generated from the synapse code
|
||||||
|
and is maintained by a script, `scripts-dev/generate_sample_config`.
|
||||||
|
Making sure that the output from this script matches the desired format
|
||||||
|
is left as an exercise for the reader!
|
|
@ -1,180 +0,0 @@
|
||||||
Code Style
|
|
||||||
==========
|
|
||||||
|
|
||||||
Formatting tools
|
|
||||||
----------------
|
|
||||||
|
|
||||||
The Synapse codebase uses a number of code formatting tools in order to
|
|
||||||
quickly and automatically check for formatting (and sometimes logical) errors
|
|
||||||
in code.
|
|
||||||
|
|
||||||
The necessary tools are detailed below.
|
|
||||||
|
|
||||||
- **black**
|
|
||||||
|
|
||||||
The Synapse codebase uses `black <https://pypi.org/project/black/>`_ as an
|
|
||||||
opinionated code formatter, ensuring all comitted code is properly
|
|
||||||
formatted.
|
|
||||||
|
|
||||||
First install ``black`` with::
|
|
||||||
|
|
||||||
pip install --upgrade black
|
|
||||||
|
|
||||||
Have ``black`` auto-format your code (it shouldn't change any functionality)
|
|
||||||
with::
|
|
||||||
|
|
||||||
black . --exclude="\.tox|build|env"
|
|
||||||
|
|
||||||
- **flake8**
|
|
||||||
|
|
||||||
``flake8`` is a code checking tool. We require code to pass ``flake8`` before being merged into the codebase.
|
|
||||||
|
|
||||||
Install ``flake8`` with::
|
|
||||||
|
|
||||||
pip install --upgrade flake8
|
|
||||||
|
|
||||||
Check all application and test code with::
|
|
||||||
|
|
||||||
flake8 synapse tests
|
|
||||||
|
|
||||||
- **isort**
|
|
||||||
|
|
||||||
``isort`` ensures imports are nicely formatted, and can suggest and
|
|
||||||
auto-fix issues such as double-importing.
|
|
||||||
|
|
||||||
Install ``isort`` with::
|
|
||||||
|
|
||||||
pip install --upgrade isort
|
|
||||||
|
|
||||||
Auto-fix imports with::
|
|
||||||
|
|
||||||
isort -rc synapse tests
|
|
||||||
|
|
||||||
``-rc`` means to recursively search the given directories.
|
|
||||||
|
|
||||||
It's worth noting that modern IDEs and text editors can run these tools
|
|
||||||
automatically on save. It may be worth looking into whether this
|
|
||||||
functionality is supported in your editor for a more convenient development
|
|
||||||
workflow. It is not, however, recommended to run ``flake8`` on save as it
|
|
||||||
takes a while and is very resource intensive.
|
|
||||||
|
|
||||||
General rules
|
|
||||||
-------------
|
|
||||||
|
|
||||||
- **Naming**:
|
|
||||||
|
|
||||||
- Use camel case for class and type names
|
|
||||||
- Use underscores for functions and variables.
|
|
||||||
|
|
||||||
- **Docstrings**: should follow the `google code style
|
|
||||||
<https://google.github.io/styleguide/pyguide.html#38-comments-and-docstrings>`_.
|
|
||||||
This is so that we can generate documentation with `sphinx
|
|
||||||
<http://sphinxcontrib-napoleon.readthedocs.org/en/latest/>`_. See the
|
|
||||||
`examples
|
|
||||||
<http://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html>`_
|
|
||||||
in the sphinx documentation.
|
|
||||||
|
|
||||||
- **Imports**:
|
|
||||||
|
|
||||||
- Imports should be sorted by ``isort`` as described above.
|
|
||||||
|
|
||||||
- Prefer to import classes and functions rather than packages or modules.
|
|
||||||
|
|
||||||
Example::
|
|
||||||
|
|
||||||
from synapse.types import UserID
|
|
||||||
...
|
|
||||||
user_id = UserID(local, server)
|
|
||||||
|
|
||||||
is preferred over::
|
|
||||||
|
|
||||||
from synapse import types
|
|
||||||
...
|
|
||||||
user_id = types.UserID(local, server)
|
|
||||||
|
|
||||||
(or any other variant).
|
|
||||||
|
|
||||||
This goes against the advice in the Google style guide, but it means that
|
|
||||||
errors in the name are caught early (at import time).
|
|
||||||
|
|
||||||
- Avoid wildcard imports (``from synapse.types import *``) and relative
|
|
||||||
imports (``from .types import UserID``).
|
|
||||||
|
|
||||||
Configuration file format
|
|
||||||
-------------------------
|
|
||||||
|
|
||||||
The `sample configuration file <./sample_config.yaml>`_ acts as a reference to
|
|
||||||
Synapse's configuration options for server administrators. Remember that many
|
|
||||||
readers will be unfamiliar with YAML and server administration in general, so
|
|
||||||
that it is important that the file be as easy to understand as possible, which
|
|
||||||
includes following a consistent format.
|
|
||||||
|
|
||||||
Some guidelines follow:
|
|
||||||
|
|
||||||
* Sections should be separated with a heading consisting of a single line
|
|
||||||
prefixed and suffixed with ``##``. There should be **two** blank lines
|
|
||||||
before the section header, and **one** after.
|
|
||||||
|
|
||||||
* Each option should be listed in the file with the following format:
|
|
||||||
|
|
||||||
* A comment describing the setting. Each line of this comment should be
|
|
||||||
prefixed with a hash (``#``) and a space.
|
|
||||||
|
|
||||||
The comment should describe the default behaviour (ie, what happens if
|
|
||||||
the setting is omitted), as well as what the effect will be if the
|
|
||||||
setting is changed.
|
|
||||||
|
|
||||||
Often, the comment end with something like "uncomment the
|
|
||||||
following to \<do action>".
|
|
||||||
|
|
||||||
* A line consisting of only ``#``.
|
|
||||||
|
|
||||||
* A commented-out example setting, prefixed with only ``#``.
|
|
||||||
|
|
||||||
For boolean (on/off) options, convention is that this example should be
|
|
||||||
the *opposite* to the default (so the comment will end with "Uncomment
|
|
||||||
the following to enable [or disable] \<feature\>." For other options,
|
|
||||||
the example should give some non-default value which is likely to be
|
|
||||||
useful to the reader.
|
|
||||||
|
|
||||||
* There should be a blank line between each option.
|
|
||||||
|
|
||||||
* Where several settings are grouped into a single dict, *avoid* the
|
|
||||||
convention where the whole block is commented out, resulting in comment
|
|
||||||
lines starting ``# #``, as this is hard to read and confusing to
|
|
||||||
edit. Instead, leave the top-level config option uncommented, and follow
|
|
||||||
the conventions above for sub-options. Ensure that your code correctly
|
|
||||||
handles the top-level option being set to ``None`` (as it will be if no
|
|
||||||
sub-options are enabled).
|
|
||||||
|
|
||||||
* Lines should be wrapped at 80 characters.
|
|
||||||
|
|
||||||
Example::
|
|
||||||
|
|
||||||
## Frobnication ##
|
|
||||||
|
|
||||||
# The frobnicator will ensure that all requests are fully frobnicated.
|
|
||||||
# To enable it, uncomment the following.
|
|
||||||
#
|
|
||||||
#frobnicator_enabled: true
|
|
||||||
|
|
||||||
# By default, the frobnicator will frobnicate with the default frobber.
|
|
||||||
# The following will make it use an alternative frobber.
|
|
||||||
#
|
|
||||||
#frobincator_frobber: special_frobber
|
|
||||||
|
|
||||||
# Settings for the frobber
|
|
||||||
#
|
|
||||||
frobber:
|
|
||||||
# frobbing speed. Defaults to 1.
|
|
||||||
#
|
|
||||||
#speed: 10
|
|
||||||
|
|
||||||
# frobbing distance. Defaults to 1000.
|
|
||||||
#
|
|
||||||
#distance: 100
|
|
||||||
|
|
||||||
Note that the sample configuration is generated from the synapse code and is
|
|
||||||
maintained by a script, ``scripts-dev/generate_sample_config``. Making sure
|
|
||||||
that the output from this script matches the desired format is left as an
|
|
||||||
exercise for the reader!
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
# How to test SAML as a developer without a server
|
||||||
|
|
||||||
|
https://capriza.github.io/samling/samling.html (https://github.com/capriza/samling) is a great
|
||||||
|
resource for being able to tinker with the SAML options within Synapse without needing to
|
||||||
|
deploy and configure a complicated software stack.
|
||||||
|
|
||||||
|
To make Synapse (and therefore Riot) use it:
|
||||||
|
|
||||||
|
1. Use the samling.html URL above or deploy your own and visit the IdP Metadata tab.
|
||||||
|
2. Copy the XML to your clipboard.
|
||||||
|
3. On your Synapse server, create a new file `samling.xml` next to your `homeserver.yaml` with
|
||||||
|
the XML from step 2 as the contents.
|
||||||
|
4. Edit your `homeserver.yaml` to include:
|
||||||
|
```yaml
|
||||||
|
saml2_config:
|
||||||
|
sp_config:
|
||||||
|
allow_unknown_attributes: true # Works around a bug with AVA Hashes: https://github.com/IdentityPython/pysaml2/issues/388
|
||||||
|
metadata:
|
||||||
|
local: ["samling.xml"]
|
||||||
|
```
|
||||||
|
5. Run `apt-get install xmlsec1` and `pip install --upgrade --force 'pysaml2>=4.5.0'` to ensure
|
||||||
|
the dependencies are installed and ready to go.
|
||||||
|
6. Restart Synapse.
|
||||||
|
|
||||||
|
Then in Riot:
|
||||||
|
|
||||||
|
1. Visit the login page with a Riot pointing at your homeserver.
|
||||||
|
2. Click the Single Sign-On button.
|
||||||
|
3. On the samling page, enter a Name Identifier and add a SAML Attribute for `uid=your_localpart`.
|
||||||
|
The response must also be signed.
|
||||||
|
4. Click "Next".
|
||||||
|
5. Click "Post Response" (change nothing).
|
||||||
|
6. You should be logged in.
|
||||||
|
|
||||||
|
If you try and repeat this process, you may be automatically logged in using the information you
|
||||||
|
gave previously. To fix this, open your developer console (`F12` or `Ctrl+Shift+I`) while on the
|
||||||
|
samling page and clear the site data. In Chrome, this will be a button on the Application tab.
|
|
@ -148,7 +148,7 @@ We no longer actively recommend against using a reverse proxy. Many admins will
|
||||||
find it easier to direct federation traffic to a reverse proxy and manage their
|
find it easier to direct federation traffic to a reverse proxy and manage their
|
||||||
own TLS certificates, and this is a supported configuration.
|
own TLS certificates, and this is a supported configuration.
|
||||||
|
|
||||||
See [reverse_proxy.rst](reverse_proxy.rst) for information on setting up a
|
See [reverse_proxy.md](reverse_proxy.md) for information on setting up a
|
||||||
reverse proxy.
|
reverse proxy.
|
||||||
|
|
||||||
#### Do I still need to give my TLS certificates to Synapse if I am using a reverse proxy?
|
#### Do I still need to give my TLS certificates to Synapse if I am using a reverse proxy?
|
||||||
|
@ -184,7 +184,7 @@ a complicated dance which requires connections in both directions).
|
||||||
|
|
||||||
Another common problem is that people on other servers can't join rooms that
|
Another common problem is that people on other servers can't join rooms that
|
||||||
you invite them to. This can be caused by an incorrectly-configured reverse
|
you invite them to. This can be caused by an incorrectly-configured reverse
|
||||||
proxy: see [reverse_proxy.rst](<reverse_proxy.rst>) for instructions on how to correctly
|
proxy: see [reverse_proxy.md](<reverse_proxy.md>) for instructions on how to correctly
|
||||||
configure a reverse proxy.
|
configure a reverse proxy.
|
||||||
|
|
||||||
## Running a Demo Federation of Synapses
|
## Running a Demo Federation of Synapses
|
||||||
|
|
|
@ -0,0 +1,494 @@
|
||||||
|
# Log Contexts
|
||||||
|
|
||||||
|
To help track the processing of individual requests, synapse uses a
|
||||||
|
'`log context`' to track which request it is handling at any given
|
||||||
|
moment. This is done via a thread-local variable; a `logging.Filter` is
|
||||||
|
then used to fish the information back out of the thread-local variable
|
||||||
|
and add it to each log record.
|
||||||
|
|
||||||
|
Logcontexts are also used for CPU and database accounting, so that we
|
||||||
|
can track which requests were responsible for high CPU use or database
|
||||||
|
activity.
|
||||||
|
|
||||||
|
The `synapse.logging.context` module provides a facilities for managing
|
||||||
|
the current log context (as well as providing the `LoggingContextFilter`
|
||||||
|
class).
|
||||||
|
|
||||||
|
Deferreds make the whole thing complicated, so this document describes
|
||||||
|
how it all works, and how to write code which follows the rules.
|
||||||
|
|
||||||
|
##Logcontexts without Deferreds
|
||||||
|
|
||||||
|
In the absence of any Deferred voodoo, things are simple enough. As with
|
||||||
|
any code of this nature, the rule is that our function should leave
|
||||||
|
things as it found them:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from synapse.logging import context # omitted from future snippets
|
||||||
|
|
||||||
|
def handle_request(request_id):
|
||||||
|
request_context = context.LoggingContext()
|
||||||
|
|
||||||
|
calling_context = context.LoggingContext.current_context()
|
||||||
|
context.LoggingContext.set_current_context(request_context)
|
||||||
|
try:
|
||||||
|
request_context.request = request_id
|
||||||
|
do_request_handling()
|
||||||
|
logger.debug("finished")
|
||||||
|
finally:
|
||||||
|
context.LoggingContext.set_current_context(calling_context)
|
||||||
|
|
||||||
|
def do_request_handling():
|
||||||
|
logger.debug("phew") # this will be logged against request_id
|
||||||
|
```
|
||||||
|
|
||||||
|
LoggingContext implements the context management methods, so the above
|
||||||
|
can be written much more succinctly as:
|
||||||
|
|
||||||
|
```python
|
||||||
|
def handle_request(request_id):
|
||||||
|
with context.LoggingContext() as request_context:
|
||||||
|
request_context.request = request_id
|
||||||
|
do_request_handling()
|
||||||
|
logger.debug("finished")
|
||||||
|
|
||||||
|
def do_request_handling():
|
||||||
|
logger.debug("phew")
|
||||||
|
```
|
||||||
|
|
||||||
|
## Using logcontexts with Deferreds
|
||||||
|
|
||||||
|
Deferreds --- and in particular, `defer.inlineCallbacks` --- break the
|
||||||
|
linear flow of code so that there is no longer a single entry point
|
||||||
|
where we should set the logcontext and a single exit point where we
|
||||||
|
should remove it.
|
||||||
|
|
||||||
|
Consider the example above, where `do_request_handling` needs to do some
|
||||||
|
blocking operation, and returns a deferred:
|
||||||
|
|
||||||
|
```python
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def handle_request(request_id):
|
||||||
|
with context.LoggingContext() as request_context:
|
||||||
|
request_context.request = request_id
|
||||||
|
yield do_request_handling()
|
||||||
|
logger.debug("finished")
|
||||||
|
```
|
||||||
|
|
||||||
|
In the above flow:
|
||||||
|
|
||||||
|
- The logcontext is set
|
||||||
|
- `do_request_handling` is called, and returns a deferred
|
||||||
|
- `handle_request` yields the deferred
|
||||||
|
- The `inlineCallbacks` wrapper of `handle_request` returns a deferred
|
||||||
|
|
||||||
|
So we have stopped processing the request (and will probably go on to
|
||||||
|
start processing the next), without clearing the logcontext.
|
||||||
|
|
||||||
|
To circumvent this problem, synapse code assumes that, wherever you have
|
||||||
|
a deferred, you will want to yield on it. To that end, whereever
|
||||||
|
functions return a deferred, we adopt the following conventions:
|
||||||
|
|
||||||
|
**Rules for functions returning deferreds:**
|
||||||
|
|
||||||
|
> - If the deferred is already complete, the function returns with the
|
||||||
|
> same logcontext it started with.
|
||||||
|
> - If the deferred is incomplete, the function clears the logcontext
|
||||||
|
> before returning; when the deferred completes, it restores the
|
||||||
|
> logcontext before running any callbacks.
|
||||||
|
|
||||||
|
That sounds complicated, but actually it means a lot of code (including
|
||||||
|
the example above) "just works". There are two cases:
|
||||||
|
|
||||||
|
- If `do_request_handling` returns a completed deferred, then the
|
||||||
|
logcontext will still be in place. In this case, execution will
|
||||||
|
continue immediately after the `yield`; the "finished" line will
|
||||||
|
be logged against the right context, and the `with` block restores
|
||||||
|
the original context before we return to the caller.
|
||||||
|
- If the returned deferred is incomplete, `do_request_handling` clears
|
||||||
|
the logcontext before returning. The logcontext is therefore clear
|
||||||
|
when `handle_request` yields the deferred. At that point, the
|
||||||
|
`inlineCallbacks` wrapper adds a callback to the deferred, and
|
||||||
|
returns another (incomplete) deferred to the caller, and it is safe
|
||||||
|
to begin processing the next request.
|
||||||
|
|
||||||
|
Once `do_request_handling`'s deferred completes, it will reinstate
|
||||||
|
the logcontext, before running the callback added by the
|
||||||
|
`inlineCallbacks` wrapper. That callback runs the second half of
|
||||||
|
`handle_request`, so again the "finished" line will be logged
|
||||||
|
against the right context, and the `with` block restores the
|
||||||
|
original context.
|
||||||
|
|
||||||
|
As an aside, it's worth noting that `handle_request` follows our rules
|
||||||
|
-though that only matters if the caller has its own logcontext which it
|
||||||
|
cares about.
|
||||||
|
|
||||||
|
The following sections describe pitfalls and helpful patterns when
|
||||||
|
implementing these rules.
|
||||||
|
|
||||||
|
Always yield your deferreds
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
Whenever you get a deferred back from a function, you should `yield` on
|
||||||
|
it as soon as possible. (Returning it directly to your caller is ok too,
|
||||||
|
if you're not doing `inlineCallbacks`.) Do not pass go; do not do any
|
||||||
|
logging; do not call any other functions.
|
||||||
|
|
||||||
|
```python
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def fun():
|
||||||
|
logger.debug("starting")
|
||||||
|
yield do_some_stuff() # just like this
|
||||||
|
|
||||||
|
d = more_stuff()
|
||||||
|
result = yield d # also fine, of course
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
def nonInlineCallbacksFun():
|
||||||
|
logger.debug("just a wrapper really")
|
||||||
|
return do_some_stuff() # this is ok too - the caller will yield on
|
||||||
|
# it anyway.
|
||||||
|
```
|
||||||
|
|
||||||
|
Provided this pattern is followed all the way back up to the callchain
|
||||||
|
to where the logcontext was set, this will make things work out ok:
|
||||||
|
provided `do_some_stuff` and `more_stuff` follow the rules above, then
|
||||||
|
so will `fun` (as wrapped by `inlineCallbacks`) and
|
||||||
|
`nonInlineCallbacksFun`.
|
||||||
|
|
||||||
|
It's all too easy to forget to `yield`: for instance if we forgot that
|
||||||
|
`do_some_stuff` returned a deferred, we might plough on regardless. This
|
||||||
|
leads to a mess; it will probably work itself out eventually, but not
|
||||||
|
before a load of stuff has been logged against the wrong context.
|
||||||
|
(Normally, other things will break, more obviously, if you forget to
|
||||||
|
`yield`, so this tends not to be a major problem in practice.)
|
||||||
|
|
||||||
|
Of course sometimes you need to do something a bit fancier with your
|
||||||
|
Deferreds - not all code follows the linear A-then-B-then-C pattern.
|
||||||
|
Notes on implementing more complex patterns are in later sections.
|
||||||
|
|
||||||
|
## Where you create a new Deferred, make it follow the rules
|
||||||
|
|
||||||
|
Most of the time, a Deferred comes from another synapse function.
|
||||||
|
Sometimes, though, we need to make up a new Deferred, or we get a
|
||||||
|
Deferred back from external code. We need to make it follow our rules.
|
||||||
|
|
||||||
|
The easy way to do it is with a combination of `defer.inlineCallbacks`,
|
||||||
|
and `context.PreserveLoggingContext`. Suppose we want to implement
|
||||||
|
`sleep`, which returns a deferred which will run its callbacks after a
|
||||||
|
given number of seconds. That might look like:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# not a logcontext-rules-compliant function
|
||||||
|
def get_sleep_deferred(seconds):
|
||||||
|
d = defer.Deferred()
|
||||||
|
reactor.callLater(seconds, d.callback, None)
|
||||||
|
return d
|
||||||
|
```
|
||||||
|
|
||||||
|
That doesn't follow the rules, but we can fix it by wrapping it with
|
||||||
|
`PreserveLoggingContext` and `yield` ing on it:
|
||||||
|
|
||||||
|
```python
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def sleep(seconds):
|
||||||
|
with PreserveLoggingContext():
|
||||||
|
yield get_sleep_deferred(seconds)
|
||||||
|
```
|
||||||
|
|
||||||
|
This technique works equally for external functions which return
|
||||||
|
deferreds, or deferreds we have made ourselves.
|
||||||
|
|
||||||
|
You can also use `context.make_deferred_yieldable`, which just does the
|
||||||
|
boilerplate for you, so the above could be written:
|
||||||
|
|
||||||
|
```python
|
||||||
|
def sleep(seconds):
|
||||||
|
return context.make_deferred_yieldable(get_sleep_deferred(seconds))
|
||||||
|
```
|
||||||
|
|
||||||
|
## Fire-and-forget
|
||||||
|
|
||||||
|
Sometimes you want to fire off a chain of execution, but not wait for
|
||||||
|
its result. That might look a bit like this:
|
||||||
|
|
||||||
|
```python
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def do_request_handling():
|
||||||
|
yield foreground_operation()
|
||||||
|
|
||||||
|
# *don't* do this
|
||||||
|
background_operation()
|
||||||
|
|
||||||
|
logger.debug("Request handling complete")
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def background_operation():
|
||||||
|
yield first_background_step()
|
||||||
|
logger.debug("Completed first step")
|
||||||
|
yield second_background_step()
|
||||||
|
logger.debug("Completed second step")
|
||||||
|
```
|
||||||
|
|
||||||
|
The above code does a couple of steps in the background after
|
||||||
|
`do_request_handling` has finished. The log lines are still logged
|
||||||
|
against the `request_context` logcontext, which may or may not be
|
||||||
|
desirable. There are two big problems with the above, however. The first
|
||||||
|
problem is that, if `background_operation` returns an incomplete
|
||||||
|
Deferred, it will expect its caller to `yield` immediately, so will have
|
||||||
|
cleared the logcontext. In this example, that means that 'Request
|
||||||
|
handling complete' will be logged without any context.
|
||||||
|
|
||||||
|
The second problem, which is potentially even worse, is that when the
|
||||||
|
Deferred returned by `background_operation` completes, it will restore
|
||||||
|
the original logcontext. There is nothing waiting on that Deferred, so
|
||||||
|
the logcontext will leak into the reactor and possibly get attached to
|
||||||
|
some arbitrary future operation.
|
||||||
|
|
||||||
|
There are two potential solutions to this.
|
||||||
|
|
||||||
|
One option is to surround the call to `background_operation` with a
|
||||||
|
`PreserveLoggingContext` call. That will reset the logcontext before
|
||||||
|
starting `background_operation` (so the context restored when the
|
||||||
|
deferred completes will be the empty logcontext), and will restore the
|
||||||
|
current logcontext before continuing the foreground process:
|
||||||
|
|
||||||
|
```python
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def do_request_handling():
|
||||||
|
yield foreground_operation()
|
||||||
|
|
||||||
|
# start background_operation off in the empty logcontext, to
|
||||||
|
# avoid leaking the current context into the reactor.
|
||||||
|
with PreserveLoggingContext():
|
||||||
|
background_operation()
|
||||||
|
|
||||||
|
# this will now be logged against the request context
|
||||||
|
logger.debug("Request handling complete")
|
||||||
|
```
|
||||||
|
|
||||||
|
Obviously that option means that the operations done in
|
||||||
|
`background_operation` would be not be logged against a logcontext
|
||||||
|
(though that might be fixed by setting a different logcontext via a
|
||||||
|
`with LoggingContext(...)` in `background_operation`).
|
||||||
|
|
||||||
|
The second option is to use `context.run_in_background`, which wraps a
|
||||||
|
function so that it doesn't reset the logcontext even when it returns
|
||||||
|
an incomplete deferred, and adds a callback to the returned deferred to
|
||||||
|
reset the logcontext. In other words, it turns a function that follows
|
||||||
|
the Synapse rules about logcontexts and Deferreds into one which behaves
|
||||||
|
more like an external function --- the opposite operation to that
|
||||||
|
described in the previous section. It can be used like this:
|
||||||
|
|
||||||
|
```python
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def do_request_handling():
|
||||||
|
yield foreground_operation()
|
||||||
|
|
||||||
|
context.run_in_background(background_operation)
|
||||||
|
|
||||||
|
# this will now be logged against the request context
|
||||||
|
logger.debug("Request handling complete")
|
||||||
|
```
|
||||||
|
|
||||||
|
## Passing synapse deferreds into third-party functions
|
||||||
|
|
||||||
|
A typical example of this is where we want to collect together two or
|
||||||
|
more deferred via `defer.gatherResults`:
|
||||||
|
|
||||||
|
```python
|
||||||
|
d1 = operation1()
|
||||||
|
d2 = operation2()
|
||||||
|
d3 = defer.gatherResults([d1, d2])
|
||||||
|
```
|
||||||
|
|
||||||
|
This is really a variation of the fire-and-forget problem above, in that
|
||||||
|
we are firing off `d1` and `d2` without yielding on them. The difference
|
||||||
|
is that we now have third-party code attached to their callbacks. Anyway
|
||||||
|
either technique given in the [Fire-and-forget](#fire-and-forget)
|
||||||
|
section will work.
|
||||||
|
|
||||||
|
Of course, the new Deferred returned by `gatherResults` needs to be
|
||||||
|
wrapped in order to make it follow the logcontext rules before we can
|
||||||
|
yield it, as described in [Where you create a new Deferred, make it
|
||||||
|
follow the
|
||||||
|
rules](#where-you-create-a-new-deferred-make-it-follow-the-rules).
|
||||||
|
|
||||||
|
So, option one: reset the logcontext before starting the operations to
|
||||||
|
be gathered:
|
||||||
|
|
||||||
|
```python
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def do_request_handling():
|
||||||
|
with PreserveLoggingContext():
|
||||||
|
d1 = operation1()
|
||||||
|
d2 = operation2()
|
||||||
|
result = yield defer.gatherResults([d1, d2])
|
||||||
|
```
|
||||||
|
|
||||||
|
In this case particularly, though, option two, of using
|
||||||
|
`context.preserve_fn` almost certainly makes more sense, so that
|
||||||
|
`operation1` and `operation2` are both logged against the original
|
||||||
|
logcontext. This looks like:
|
||||||
|
|
||||||
|
```python
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def do_request_handling():
|
||||||
|
d1 = context.preserve_fn(operation1)()
|
||||||
|
d2 = context.preserve_fn(operation2)()
|
||||||
|
|
||||||
|
with PreserveLoggingContext():
|
||||||
|
result = yield defer.gatherResults([d1, d2])
|
||||||
|
```
|
||||||
|
|
||||||
|
## Was all this really necessary?
|
||||||
|
|
||||||
|
The conventions used work fine for a linear flow where everything
|
||||||
|
happens in series via `defer.inlineCallbacks` and `yield`, but are
|
||||||
|
certainly tricky to follow for any more exotic flows. It's hard not to
|
||||||
|
wonder if we could have done something else.
|
||||||
|
|
||||||
|
We're not going to rewrite Synapse now, so the following is entirely of
|
||||||
|
academic interest, but I'd like to record some thoughts on an
|
||||||
|
alternative approach.
|
||||||
|
|
||||||
|
I briefly prototyped some code following an alternative set of rules. I
|
||||||
|
think it would work, but I certainly didn't get as far as thinking how
|
||||||
|
it would interact with concepts as complicated as the cache descriptors.
|
||||||
|
|
||||||
|
My alternative rules were:
|
||||||
|
|
||||||
|
- functions always preserve the logcontext of their caller, whether or
|
||||||
|
not they are returning a Deferred.
|
||||||
|
- Deferreds returned by synapse functions run their callbacks in the
|
||||||
|
same context as the function was orignally called in.
|
||||||
|
|
||||||
|
The main point of this scheme is that everywhere that sets the
|
||||||
|
logcontext is responsible for clearing it before returning control to
|
||||||
|
the reactor.
|
||||||
|
|
||||||
|
So, for example, if you were the function which started a
|
||||||
|
`with LoggingContext` block, you wouldn't `yield` within it --- instead
|
||||||
|
you'd start off the background process, and then leave the `with` block
|
||||||
|
to wait for it:
|
||||||
|
|
||||||
|
```python
|
||||||
|
def handle_request(request_id):
|
||||||
|
with context.LoggingContext() as request_context:
|
||||||
|
request_context.request = request_id
|
||||||
|
d = do_request_handling()
|
||||||
|
|
||||||
|
def cb(r):
|
||||||
|
logger.debug("finished")
|
||||||
|
|
||||||
|
d.addCallback(cb)
|
||||||
|
return d
|
||||||
|
```
|
||||||
|
|
||||||
|
(in general, mixing `with LoggingContext` blocks and
|
||||||
|
`defer.inlineCallbacks` in the same function leads to slighly
|
||||||
|
counter-intuitive code, under this scheme).
|
||||||
|
|
||||||
|
Because we leave the original `with` block as soon as the Deferred is
|
||||||
|
returned (as opposed to waiting for it to be resolved, as we do today),
|
||||||
|
the logcontext is cleared before control passes back to the reactor; so
|
||||||
|
if there is some code within `do_request_handling` which needs to wait
|
||||||
|
for a Deferred to complete, there is no need for it to worry about
|
||||||
|
clearing the logcontext before doing so:
|
||||||
|
|
||||||
|
```python
|
||||||
|
def handle_request():
|
||||||
|
r = do_some_stuff()
|
||||||
|
r.addCallback(do_some_more_stuff)
|
||||||
|
return r
|
||||||
|
```
|
||||||
|
|
||||||
|
--- and provided `do_some_stuff` follows the rules of returning a
|
||||||
|
Deferred which runs its callbacks in the original logcontext, all is
|
||||||
|
happy.
|
||||||
|
|
||||||
|
The business of a Deferred which runs its callbacks in the original
|
||||||
|
logcontext isn't hard to achieve --- we have it today, in the shape of
|
||||||
|
`context._PreservingContextDeferred`:
|
||||||
|
|
||||||
|
```python
|
||||||
|
def do_some_stuff():
|
||||||
|
deferred = do_some_io()
|
||||||
|
pcd = _PreservingContextDeferred(LoggingContext.current_context())
|
||||||
|
deferred.chainDeferred(pcd)
|
||||||
|
return pcd
|
||||||
|
```
|
||||||
|
|
||||||
|
It turns out that, thanks to the way that Deferreds chain together, we
|
||||||
|
automatically get the property of a context-preserving deferred with
|
||||||
|
`defer.inlineCallbacks`, provided the final Defered the function
|
||||||
|
`yields` on has that property. So we can just write:
|
||||||
|
|
||||||
|
```python
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def handle_request():
|
||||||
|
yield do_some_stuff()
|
||||||
|
yield do_some_more_stuff()
|
||||||
|
```
|
||||||
|
|
||||||
|
To conclude: I think this scheme would have worked equally well, with
|
||||||
|
less danger of messing it up, and probably made some more esoteric code
|
||||||
|
easier to write. But again --- changing the conventions of the entire
|
||||||
|
Synapse codebase is not a sensible option for the marginal improvement
|
||||||
|
offered.
|
||||||
|
|
||||||
|
## A note on garbage-collection of Deferred chains
|
||||||
|
|
||||||
|
It turns out that our logcontext rules do not play nicely with Deferred
|
||||||
|
chains which get orphaned and garbage-collected.
|
||||||
|
|
||||||
|
Imagine we have some code that looks like this:
|
||||||
|
|
||||||
|
```python
|
||||||
|
listener_queue = []
|
||||||
|
|
||||||
|
def on_something_interesting():
|
||||||
|
for d in listener_queue:
|
||||||
|
d.callback("foo")
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def await_something_interesting():
|
||||||
|
new_deferred = defer.Deferred()
|
||||||
|
listener_queue.append(new_deferred)
|
||||||
|
|
||||||
|
with PreserveLoggingContext():
|
||||||
|
yield new_deferred
|
||||||
|
```
|
||||||
|
|
||||||
|
Obviously, the idea here is that we have a bunch of things which are
|
||||||
|
waiting for an event. (It's just an example of the problem here, but a
|
||||||
|
relatively common one.)
|
||||||
|
|
||||||
|
Now let's imagine two further things happen. First of all, whatever was
|
||||||
|
waiting for the interesting thing goes away. (Perhaps the request times
|
||||||
|
out, or something *even more* interesting happens.)
|
||||||
|
|
||||||
|
Secondly, let's suppose that we decide that the interesting thing is
|
||||||
|
never going to happen, and we reset the listener queue:
|
||||||
|
|
||||||
|
```python
|
||||||
|
def reset_listener_queue():
|
||||||
|
listener_queue.clear()
|
||||||
|
```
|
||||||
|
|
||||||
|
So, both ends of the deferred chain have now dropped their references,
|
||||||
|
and the deferred chain is now orphaned, and will be garbage-collected at
|
||||||
|
some point. Note that `await_something_interesting` is a generator
|
||||||
|
function, and when Python garbage-collects generator functions, it gives
|
||||||
|
them a chance to clean up by making the `yield` raise a `GeneratorExit`
|
||||||
|
exception. In our case, that means that the `__exit__` handler of
|
||||||
|
`PreserveLoggingContext` will carefully restore the request context, but
|
||||||
|
there is now nothing waiting for its return, so the request context is
|
||||||
|
never cleared.
|
||||||
|
|
||||||
|
To reiterate, this problem only arises when *both* ends of a deferred
|
||||||
|
chain are dropped. Dropping the the reference to a deferred you're
|
||||||
|
supposed to be calling is probably bad practice, so this doesn't
|
||||||
|
actually happen too much. Unfortunately, when it does happen, it will
|
||||||
|
lead to leaked logcontexts which are incredibly hard to track down.
|
|
@ -1,498 +0,0 @@
|
||||||
Log Contexts
|
|
||||||
============
|
|
||||||
|
|
||||||
.. contents::
|
|
||||||
|
|
||||||
To help track the processing of individual requests, synapse uses a
|
|
||||||
'log context' to track which request it is handling at any given moment. This
|
|
||||||
is done via a thread-local variable; a ``logging.Filter`` is then used to fish
|
|
||||||
the information back out of the thread-local variable and add it to each log
|
|
||||||
record.
|
|
||||||
|
|
||||||
Logcontexts are also used for CPU and database accounting, so that we can track
|
|
||||||
which requests were responsible for high CPU use or database activity.
|
|
||||||
|
|
||||||
The ``synapse.logging.context`` module provides a facilities for managing the
|
|
||||||
current log context (as well as providing the ``LoggingContextFilter`` class).
|
|
||||||
|
|
||||||
Deferreds make the whole thing complicated, so this document describes how it
|
|
||||||
all works, and how to write code which follows the rules.
|
|
||||||
|
|
||||||
Logcontexts without Deferreds
|
|
||||||
-----------------------------
|
|
||||||
|
|
||||||
In the absence of any Deferred voodoo, things are simple enough. As with any
|
|
||||||
code of this nature, the rule is that our function should leave things as it
|
|
||||||
found them:
|
|
||||||
|
|
||||||
.. code:: python
|
|
||||||
|
|
||||||
from synapse.logging import context # omitted from future snippets
|
|
||||||
|
|
||||||
def handle_request(request_id):
|
|
||||||
request_context = context.LoggingContext()
|
|
||||||
|
|
||||||
calling_context = context.LoggingContext.current_context()
|
|
||||||
context.LoggingContext.set_current_context(request_context)
|
|
||||||
try:
|
|
||||||
request_context.request = request_id
|
|
||||||
do_request_handling()
|
|
||||||
logger.debug("finished")
|
|
||||||
finally:
|
|
||||||
context.LoggingContext.set_current_context(calling_context)
|
|
||||||
|
|
||||||
def do_request_handling():
|
|
||||||
logger.debug("phew") # this will be logged against request_id
|
|
||||||
|
|
||||||
|
|
||||||
LoggingContext implements the context management methods, so the above can be
|
|
||||||
written much more succinctly as:
|
|
||||||
|
|
||||||
.. code:: python
|
|
||||||
|
|
||||||
def handle_request(request_id):
|
|
||||||
with context.LoggingContext() as request_context:
|
|
||||||
request_context.request = request_id
|
|
||||||
do_request_handling()
|
|
||||||
logger.debug("finished")
|
|
||||||
|
|
||||||
def do_request_handling():
|
|
||||||
logger.debug("phew")
|
|
||||||
|
|
||||||
|
|
||||||
Using logcontexts with Deferreds
|
|
||||||
--------------------------------
|
|
||||||
|
|
||||||
Deferreds — and in particular, ``defer.inlineCallbacks`` — break
|
|
||||||
the linear flow of code so that there is no longer a single entry point where
|
|
||||||
we should set the logcontext and a single exit point where we should remove it.
|
|
||||||
|
|
||||||
Consider the example above, where ``do_request_handling`` needs to do some
|
|
||||||
blocking operation, and returns a deferred:
|
|
||||||
|
|
||||||
.. code:: python
|
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
|
||||||
def handle_request(request_id):
|
|
||||||
with context.LoggingContext() as request_context:
|
|
||||||
request_context.request = request_id
|
|
||||||
yield do_request_handling()
|
|
||||||
logger.debug("finished")
|
|
||||||
|
|
||||||
|
|
||||||
In the above flow:
|
|
||||||
|
|
||||||
* The logcontext is set
|
|
||||||
* ``do_request_handling`` is called, and returns a deferred
|
|
||||||
* ``handle_request`` yields the deferred
|
|
||||||
* The ``inlineCallbacks`` wrapper of ``handle_request`` returns a deferred
|
|
||||||
|
|
||||||
So we have stopped processing the request (and will probably go on to start
|
|
||||||
processing the next), without clearing the logcontext.
|
|
||||||
|
|
||||||
To circumvent this problem, synapse code assumes that, wherever you have a
|
|
||||||
deferred, you will want to yield on it. To that end, whereever functions return
|
|
||||||
a deferred, we adopt the following conventions:
|
|
||||||
|
|
||||||
**Rules for functions returning deferreds:**
|
|
||||||
|
|
||||||
* If the deferred is already complete, the function returns with the same
|
|
||||||
logcontext it started with.
|
|
||||||
* If the deferred is incomplete, the function clears the logcontext before
|
|
||||||
returning; when the deferred completes, it restores the logcontext before
|
|
||||||
running any callbacks.
|
|
||||||
|
|
||||||
That sounds complicated, but actually it means a lot of code (including the
|
|
||||||
example above) "just works". There are two cases:
|
|
||||||
|
|
||||||
* If ``do_request_handling`` returns a completed deferred, then the logcontext
|
|
||||||
will still be in place. In this case, execution will continue immediately
|
|
||||||
after the ``yield``; the "finished" line will be logged against the right
|
|
||||||
context, and the ``with`` block restores the original context before we
|
|
||||||
return to the caller.
|
|
||||||
|
|
||||||
* If the returned deferred is incomplete, ``do_request_handling`` clears the
|
|
||||||
logcontext before returning. The logcontext is therefore clear when
|
|
||||||
``handle_request`` yields the deferred. At that point, the ``inlineCallbacks``
|
|
||||||
wrapper adds a callback to the deferred, and returns another (incomplete)
|
|
||||||
deferred to the caller, and it is safe to begin processing the next request.
|
|
||||||
|
|
||||||
Once ``do_request_handling``'s deferred completes, it will reinstate the
|
|
||||||
logcontext, before running the callback added by the ``inlineCallbacks``
|
|
||||||
wrapper. That callback runs the second half of ``handle_request``, so again
|
|
||||||
the "finished" line will be logged against the right
|
|
||||||
context, and the ``with`` block restores the original context.
|
|
||||||
|
|
||||||
As an aside, it's worth noting that ``handle_request`` follows our rules -
|
|
||||||
though that only matters if the caller has its own logcontext which it cares
|
|
||||||
about.
|
|
||||||
|
|
||||||
The following sections describe pitfalls and helpful patterns when implementing
|
|
||||||
these rules.
|
|
||||||
|
|
||||||
Always yield your deferreds
|
|
||||||
---------------------------
|
|
||||||
|
|
||||||
Whenever you get a deferred back from a function, you should ``yield`` on it
|
|
||||||
as soon as possible. (Returning it directly to your caller is ok too, if you're
|
|
||||||
not doing ``inlineCallbacks``.) Do not pass go; do not do any logging; do not
|
|
||||||
call any other functions.
|
|
||||||
|
|
||||||
.. code:: python
|
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
|
||||||
def fun():
|
|
||||||
logger.debug("starting")
|
|
||||||
yield do_some_stuff() # just like this
|
|
||||||
|
|
||||||
d = more_stuff()
|
|
||||||
result = yield d # also fine, of course
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
def nonInlineCallbacksFun():
|
|
||||||
logger.debug("just a wrapper really")
|
|
||||||
return do_some_stuff() # this is ok too - the caller will yield on
|
|
||||||
# it anyway.
|
|
||||||
|
|
||||||
Provided this pattern is followed all the way back up to the callchain to where
|
|
||||||
the logcontext was set, this will make things work out ok: provided
|
|
||||||
``do_some_stuff`` and ``more_stuff`` follow the rules above, then so will
|
|
||||||
``fun`` (as wrapped by ``inlineCallbacks``) and ``nonInlineCallbacksFun``.
|
|
||||||
|
|
||||||
It's all too easy to forget to ``yield``: for instance if we forgot that
|
|
||||||
``do_some_stuff`` returned a deferred, we might plough on regardless. This
|
|
||||||
leads to a mess; it will probably work itself out eventually, but not before
|
|
||||||
a load of stuff has been logged against the wrong context. (Normally, other
|
|
||||||
things will break, more obviously, if you forget to ``yield``, so this tends
|
|
||||||
not to be a major problem in practice.)
|
|
||||||
|
|
||||||
Of course sometimes you need to do something a bit fancier with your Deferreds
|
|
||||||
- not all code follows the linear A-then-B-then-C pattern. Notes on
|
|
||||||
implementing more complex patterns are in later sections.
|
|
||||||
|
|
||||||
Where you create a new Deferred, make it follow the rules
|
|
||||||
---------------------------------------------------------
|
|
||||||
|
|
||||||
Most of the time, a Deferred comes from another synapse function. Sometimes,
|
|
||||||
though, we need to make up a new Deferred, or we get a Deferred back from
|
|
||||||
external code. We need to make it follow our rules.
|
|
||||||
|
|
||||||
The easy way to do it is with a combination of ``defer.inlineCallbacks``, and
|
|
||||||
``context.PreserveLoggingContext``. Suppose we want to implement ``sleep``,
|
|
||||||
which returns a deferred which will run its callbacks after a given number of
|
|
||||||
seconds. That might look like:
|
|
||||||
|
|
||||||
.. code:: python
|
|
||||||
|
|
||||||
# not a logcontext-rules-compliant function
|
|
||||||
def get_sleep_deferred(seconds):
|
|
||||||
d = defer.Deferred()
|
|
||||||
reactor.callLater(seconds, d.callback, None)
|
|
||||||
return d
|
|
||||||
|
|
||||||
That doesn't follow the rules, but we can fix it by wrapping it with
|
|
||||||
``PreserveLoggingContext`` and ``yield`` ing on it:
|
|
||||||
|
|
||||||
.. code:: python
|
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
|
||||||
def sleep(seconds):
|
|
||||||
with PreserveLoggingContext():
|
|
||||||
yield get_sleep_deferred(seconds)
|
|
||||||
|
|
||||||
This technique works equally for external functions which return deferreds,
|
|
||||||
or deferreds we have made ourselves.
|
|
||||||
|
|
||||||
You can also use ``context.make_deferred_yieldable``, which just does the
|
|
||||||
boilerplate for you, so the above could be written:
|
|
||||||
|
|
||||||
.. code:: python
|
|
||||||
|
|
||||||
def sleep(seconds):
|
|
||||||
return context.make_deferred_yieldable(get_sleep_deferred(seconds))
|
|
||||||
|
|
||||||
|
|
||||||
Fire-and-forget
|
|
||||||
---------------
|
|
||||||
|
|
||||||
Sometimes you want to fire off a chain of execution, but not wait for its
|
|
||||||
result. That might look a bit like this:
|
|
||||||
|
|
||||||
.. code:: python
|
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
|
||||||
def do_request_handling():
|
|
||||||
yield foreground_operation()
|
|
||||||
|
|
||||||
# *don't* do this
|
|
||||||
background_operation()
|
|
||||||
|
|
||||||
logger.debug("Request handling complete")
|
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
|
||||||
def background_operation():
|
|
||||||
yield first_background_step()
|
|
||||||
logger.debug("Completed first step")
|
|
||||||
yield second_background_step()
|
|
||||||
logger.debug("Completed second step")
|
|
||||||
|
|
||||||
The above code does a couple of steps in the background after
|
|
||||||
``do_request_handling`` has finished. The log lines are still logged against
|
|
||||||
the ``request_context`` logcontext, which may or may not be desirable. There
|
|
||||||
are two big problems with the above, however. The first problem is that, if
|
|
||||||
``background_operation`` returns an incomplete Deferred, it will expect its
|
|
||||||
caller to ``yield`` immediately, so will have cleared the logcontext. In this
|
|
||||||
example, that means that 'Request handling complete' will be logged without any
|
|
||||||
context.
|
|
||||||
|
|
||||||
The second problem, which is potentially even worse, is that when the Deferred
|
|
||||||
returned by ``background_operation`` completes, it will restore the original
|
|
||||||
logcontext. There is nothing waiting on that Deferred, so the logcontext will
|
|
||||||
leak into the reactor and possibly get attached to some arbitrary future
|
|
||||||
operation.
|
|
||||||
|
|
||||||
There are two potential solutions to this.
|
|
||||||
|
|
||||||
One option is to surround the call to ``background_operation`` with a
|
|
||||||
``PreserveLoggingContext`` call. That will reset the logcontext before
|
|
||||||
starting ``background_operation`` (so the context restored when the deferred
|
|
||||||
completes will be the empty logcontext), and will restore the current
|
|
||||||
logcontext before continuing the foreground process:
|
|
||||||
|
|
||||||
.. code:: python
|
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
|
||||||
def do_request_handling():
|
|
||||||
yield foreground_operation()
|
|
||||||
|
|
||||||
# start background_operation off in the empty logcontext, to
|
|
||||||
# avoid leaking the current context into the reactor.
|
|
||||||
with PreserveLoggingContext():
|
|
||||||
background_operation()
|
|
||||||
|
|
||||||
# this will now be logged against the request context
|
|
||||||
logger.debug("Request handling complete")
|
|
||||||
|
|
||||||
Obviously that option means that the operations done in
|
|
||||||
``background_operation`` would be not be logged against a logcontext (though
|
|
||||||
that might be fixed by setting a different logcontext via a ``with
|
|
||||||
LoggingContext(...)`` in ``background_operation``).
|
|
||||||
|
|
||||||
The second option is to use ``context.run_in_background``, which wraps a
|
|
||||||
function so that it doesn't reset the logcontext even when it returns an
|
|
||||||
incomplete deferred, and adds a callback to the returned deferred to reset the
|
|
||||||
logcontext. In other words, it turns a function that follows the Synapse rules
|
|
||||||
about logcontexts and Deferreds into one which behaves more like an external
|
|
||||||
function — the opposite operation to that described in the previous section.
|
|
||||||
It can be used like this:
|
|
||||||
|
|
||||||
.. code:: python
|
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
|
||||||
def do_request_handling():
|
|
||||||
yield foreground_operation()
|
|
||||||
|
|
||||||
context.run_in_background(background_operation)
|
|
||||||
|
|
||||||
# this will now be logged against the request context
|
|
||||||
logger.debug("Request handling complete")
|
|
||||||
|
|
||||||
Passing synapse deferreds into third-party functions
|
|
||||||
----------------------------------------------------
|
|
||||||
|
|
||||||
A typical example of this is where we want to collect together two or more
|
|
||||||
deferred via ``defer.gatherResults``:
|
|
||||||
|
|
||||||
.. code:: python
|
|
||||||
|
|
||||||
d1 = operation1()
|
|
||||||
d2 = operation2()
|
|
||||||
d3 = defer.gatherResults([d1, d2])
|
|
||||||
|
|
||||||
This is really a variation of the fire-and-forget problem above, in that we are
|
|
||||||
firing off ``d1`` and ``d2`` without yielding on them. The difference
|
|
||||||
is that we now have third-party code attached to their callbacks. Anyway either
|
|
||||||
technique given in the `Fire-and-forget`_ section will work.
|
|
||||||
|
|
||||||
Of course, the new Deferred returned by ``gatherResults`` needs to be wrapped
|
|
||||||
in order to make it follow the logcontext rules before we can yield it, as
|
|
||||||
described in `Where you create a new Deferred, make it follow the rules`_.
|
|
||||||
|
|
||||||
So, option one: reset the logcontext before starting the operations to be
|
|
||||||
gathered:
|
|
||||||
|
|
||||||
.. code:: python
|
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
|
||||||
def do_request_handling():
|
|
||||||
with PreserveLoggingContext():
|
|
||||||
d1 = operation1()
|
|
||||||
d2 = operation2()
|
|
||||||
result = yield defer.gatherResults([d1, d2])
|
|
||||||
|
|
||||||
In this case particularly, though, option two, of using
|
|
||||||
``context.preserve_fn`` almost certainly makes more sense, so that
|
|
||||||
``operation1`` and ``operation2`` are both logged against the original
|
|
||||||
logcontext. This looks like:
|
|
||||||
|
|
||||||
.. code:: python
|
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
|
||||||
def do_request_handling():
|
|
||||||
d1 = context.preserve_fn(operation1)()
|
|
||||||
d2 = context.preserve_fn(operation2)()
|
|
||||||
|
|
||||||
with PreserveLoggingContext():
|
|
||||||
result = yield defer.gatherResults([d1, d2])
|
|
||||||
|
|
||||||
|
|
||||||
Was all this really necessary?
|
|
||||||
------------------------------
|
|
||||||
|
|
||||||
The conventions used work fine for a linear flow where everything happens in
|
|
||||||
series via ``defer.inlineCallbacks`` and ``yield``, but are certainly tricky to
|
|
||||||
follow for any more exotic flows. It's hard not to wonder if we could have done
|
|
||||||
something else.
|
|
||||||
|
|
||||||
We're not going to rewrite Synapse now, so the following is entirely of
|
|
||||||
academic interest, but I'd like to record some thoughts on an alternative
|
|
||||||
approach.
|
|
||||||
|
|
||||||
I briefly prototyped some code following an alternative set of rules. I think
|
|
||||||
it would work, but I certainly didn't get as far as thinking how it would
|
|
||||||
interact with concepts as complicated as the cache descriptors.
|
|
||||||
|
|
||||||
My alternative rules were:
|
|
||||||
|
|
||||||
* functions always preserve the logcontext of their caller, whether or not they
|
|
||||||
are returning a Deferred.
|
|
||||||
|
|
||||||
* Deferreds returned by synapse functions run their callbacks in the same
|
|
||||||
context as the function was orignally called in.
|
|
||||||
|
|
||||||
The main point of this scheme is that everywhere that sets the logcontext is
|
|
||||||
responsible for clearing it before returning control to the reactor.
|
|
||||||
|
|
||||||
So, for example, if you were the function which started a ``with
|
|
||||||
LoggingContext`` block, you wouldn't ``yield`` within it — instead you'd start
|
|
||||||
off the background process, and then leave the ``with`` block to wait for it:
|
|
||||||
|
|
||||||
.. code:: python
|
|
||||||
|
|
||||||
def handle_request(request_id):
|
|
||||||
with context.LoggingContext() as request_context:
|
|
||||||
request_context.request = request_id
|
|
||||||
d = do_request_handling()
|
|
||||||
|
|
||||||
def cb(r):
|
|
||||||
logger.debug("finished")
|
|
||||||
|
|
||||||
d.addCallback(cb)
|
|
||||||
return d
|
|
||||||
|
|
||||||
(in general, mixing ``with LoggingContext`` blocks and
|
|
||||||
``defer.inlineCallbacks`` in the same function leads to slighly
|
|
||||||
counter-intuitive code, under this scheme).
|
|
||||||
|
|
||||||
Because we leave the original ``with`` block as soon as the Deferred is
|
|
||||||
returned (as opposed to waiting for it to be resolved, as we do today), the
|
|
||||||
logcontext is cleared before control passes back to the reactor; so if there is
|
|
||||||
some code within ``do_request_handling`` which needs to wait for a Deferred to
|
|
||||||
complete, there is no need for it to worry about clearing the logcontext before
|
|
||||||
doing so:
|
|
||||||
|
|
||||||
.. code:: python
|
|
||||||
|
|
||||||
def handle_request():
|
|
||||||
r = do_some_stuff()
|
|
||||||
r.addCallback(do_some_more_stuff)
|
|
||||||
return r
|
|
||||||
|
|
||||||
— and provided ``do_some_stuff`` follows the rules of returning a Deferred which
|
|
||||||
runs its callbacks in the original logcontext, all is happy.
|
|
||||||
|
|
||||||
The business of a Deferred which runs its callbacks in the original logcontext
|
|
||||||
isn't hard to achieve — we have it today, in the shape of
|
|
||||||
``context._PreservingContextDeferred``:
|
|
||||||
|
|
||||||
.. code:: python
|
|
||||||
|
|
||||||
def do_some_stuff():
|
|
||||||
deferred = do_some_io()
|
|
||||||
pcd = _PreservingContextDeferred(LoggingContext.current_context())
|
|
||||||
deferred.chainDeferred(pcd)
|
|
||||||
return pcd
|
|
||||||
|
|
||||||
It turns out that, thanks to the way that Deferreds chain together, we
|
|
||||||
automatically get the property of a context-preserving deferred with
|
|
||||||
``defer.inlineCallbacks``, provided the final Defered the function ``yields``
|
|
||||||
on has that property. So we can just write:
|
|
||||||
|
|
||||||
.. code:: python
|
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
|
||||||
def handle_request():
|
|
||||||
yield do_some_stuff()
|
|
||||||
yield do_some_more_stuff()
|
|
||||||
|
|
||||||
To conclude: I think this scheme would have worked equally well, with less
|
|
||||||
danger of messing it up, and probably made some more esoteric code easier to
|
|
||||||
write. But again — changing the conventions of the entire Synapse codebase is
|
|
||||||
not a sensible option for the marginal improvement offered.
|
|
||||||
|
|
||||||
|
|
||||||
A note on garbage-collection of Deferred chains
|
|
||||||
-----------------------------------------------
|
|
||||||
|
|
||||||
It turns out that our logcontext rules do not play nicely with Deferred
|
|
||||||
chains which get orphaned and garbage-collected.
|
|
||||||
|
|
||||||
Imagine we have some code that looks like this:
|
|
||||||
|
|
||||||
.. code:: python
|
|
||||||
|
|
||||||
listener_queue = []
|
|
||||||
|
|
||||||
def on_something_interesting():
|
|
||||||
for d in listener_queue:
|
|
||||||
d.callback("foo")
|
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
|
||||||
def await_something_interesting():
|
|
||||||
new_deferred = defer.Deferred()
|
|
||||||
listener_queue.append(new_deferred)
|
|
||||||
|
|
||||||
with PreserveLoggingContext():
|
|
||||||
yield new_deferred
|
|
||||||
|
|
||||||
Obviously, the idea here is that we have a bunch of things which are waiting
|
|
||||||
for an event. (It's just an example of the problem here, but a relatively
|
|
||||||
common one.)
|
|
||||||
|
|
||||||
Now let's imagine two further things happen. First of all, whatever was
|
|
||||||
waiting for the interesting thing goes away. (Perhaps the request times out,
|
|
||||||
or something *even more* interesting happens.)
|
|
||||||
|
|
||||||
Secondly, let's suppose that we decide that the interesting thing is never
|
|
||||||
going to happen, and we reset the listener queue:
|
|
||||||
|
|
||||||
.. code:: python
|
|
||||||
|
|
||||||
def reset_listener_queue():
|
|
||||||
listener_queue.clear()
|
|
||||||
|
|
||||||
So, both ends of the deferred chain have now dropped their references, and the
|
|
||||||
deferred chain is now orphaned, and will be garbage-collected at some point.
|
|
||||||
Note that ``await_something_interesting`` is a generator function, and when
|
|
||||||
Python garbage-collects generator functions, it gives them a chance to clean
|
|
||||||
up by making the ``yield`` raise a ``GeneratorExit`` exception. In our case,
|
|
||||||
that means that the ``__exit__`` handler of ``PreserveLoggingContext`` will
|
|
||||||
carefully restore the request context, but there is now nothing waiting for
|
|
||||||
its return, so the request context is never cleared.
|
|
||||||
|
|
||||||
To reiterate, this problem only arises when *both* ends of a deferred chain
|
|
||||||
are dropped. Dropping the the reference to a deferred you're supposed to be
|
|
||||||
calling is probably bad practice, so this doesn't actually happen too much.
|
|
||||||
Unfortunately, when it does happen, it will lead to leaked logcontexts which
|
|
||||||
are incredibly hard to track down.
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
# Media Repository
|
||||||
|
|
||||||
|
*Synapse implementation-specific details for the media repository*
|
||||||
|
|
||||||
|
The media repository is where attachments and avatar photos are stored.
|
||||||
|
It stores attachment content and thumbnails for media uploaded by local users.
|
||||||
|
It caches attachment content and thumbnails for media uploaded by remote users.
|
||||||
|
|
||||||
|
## Storage
|
||||||
|
|
||||||
|
Each item of media is assigned a `media_id` when it is uploaded.
|
||||||
|
The `media_id` is a randomly chosen, URL safe 24 character string.
|
||||||
|
|
||||||
|
Metadata such as the MIME type, upload time and length are stored in the
|
||||||
|
sqlite3 database indexed by `media_id`.
|
||||||
|
|
||||||
|
Content is stored on the filesystem under a `"local_content"` directory.
|
||||||
|
|
||||||
|
Thumbnails are stored under a `"local_thumbnails"` directory.
|
||||||
|
|
||||||
|
The item with `media_id` `"aabbccccccccdddddddddddd"` is stored under
|
||||||
|
`"local_content/aa/bb/ccccccccdddddddddddd"`. Its thumbnail with width
|
||||||
|
`128` and height `96` and type `"image/jpeg"` is stored under
|
||||||
|
`"local_thumbnails/aa/bb/ccccccccdddddddddddd/128-96-image-jpeg"`
|
||||||
|
|
||||||
|
Remote content is cached under `"remote_content"` directory. Each item of
|
||||||
|
remote content is assigned a local `"filesystem_id"` to ensure that the
|
||||||
|
directory structure `"remote_content/server_name/aa/bb/ccccccccdddddddddddd"`
|
||||||
|
is appropriate. Thumbnails for remote content are stored under
|
||||||
|
`"remote_thumbnails/server_name/..."`
|
|
@ -1,27 +0,0 @@
|
||||||
Media Repository
|
|
||||||
================
|
|
||||||
|
|
||||||
*Synapse implementation-specific details for the media repository*
|
|
||||||
|
|
||||||
The media repository is where attachments and avatar photos are stored.
|
|
||||||
It stores attachment content and thumbnails for media uploaded by local users.
|
|
||||||
It caches attachment content and thumbnails for media uploaded by remote users.
|
|
||||||
|
|
||||||
Storage
|
|
||||||
-------
|
|
||||||
|
|
||||||
Each item of media is assigned a ``media_id`` when it is uploaded.
|
|
||||||
The ``media_id`` is a randomly chosen, URL safe 24 character string.
|
|
||||||
Metadata such as the MIME type, upload time and length are stored in the
|
|
||||||
sqlite3 database indexed by ``media_id``.
|
|
||||||
Content is stored on the filesystem under a ``"local_content"`` directory.
|
|
||||||
Thumbnails are stored under a ``"local_thumbnails"`` directory.
|
|
||||||
The item with ``media_id`` ``"aabbccccccccdddddddddddd"`` is stored under
|
|
||||||
``"local_content/aa/bb/ccccccccdddddddddddd"``. Its thumbnail with width
|
|
||||||
``128`` and height ``96`` and type ``"image/jpeg"`` is stored under
|
|
||||||
``"local_thumbnails/aa/bb/ccccccccdddddddddddd/128-96-image-jpeg"``
|
|
||||||
Remote content is cached under ``"remote_content"`` directory. Each item of
|
|
||||||
remote content is assigned a local "``filesystem_id``" to ensure that the
|
|
||||||
directory structure ``"remote_content/server_name/aa/bb/ccccccccdddddddddddd"``
|
|
||||||
is appropriate. Thumbnails for remote content are stored under
|
|
||||||
``"remote_thumbnails/server_name/..."``
|
|
|
@ -0,0 +1,217 @@
|
||||||
|
# How to monitor Synapse metrics using Prometheus
|
||||||
|
|
||||||
|
1. Install Prometheus:
|
||||||
|
|
||||||
|
Follow instructions at
|
||||||
|
<http://prometheus.io/docs/introduction/install/>
|
||||||
|
|
||||||
|
1. Enable Synapse metrics:
|
||||||
|
|
||||||
|
There are two methods of enabling metrics in Synapse.
|
||||||
|
|
||||||
|
The first serves the metrics as a part of the usual web server and
|
||||||
|
can be enabled by adding the \"metrics\" resource to the existing
|
||||||
|
listener as such:
|
||||||
|
|
||||||
|
resources:
|
||||||
|
- names:
|
||||||
|
- client
|
||||||
|
- metrics
|
||||||
|
|
||||||
|
This provides a simple way of adding metrics to your Synapse
|
||||||
|
installation, and serves under `/_synapse/metrics`. If you do not
|
||||||
|
wish your metrics be publicly exposed, you will need to either
|
||||||
|
filter it out at your load balancer, or use the second method.
|
||||||
|
|
||||||
|
The second method runs the metrics server on a different port, in a
|
||||||
|
different thread to Synapse. This can make it more resilient to
|
||||||
|
heavy load meaning metrics cannot be retrieved, and can be exposed
|
||||||
|
to just internal networks easier. The served metrics are available
|
||||||
|
over HTTP only, and will be available at `/`.
|
||||||
|
|
||||||
|
Add a new listener to homeserver.yaml:
|
||||||
|
|
||||||
|
listeners:
|
||||||
|
- type: metrics
|
||||||
|
port: 9000
|
||||||
|
bind_addresses:
|
||||||
|
- '0.0.0.0'
|
||||||
|
|
||||||
|
For both options, you will need to ensure that `enable_metrics` is
|
||||||
|
set to `True`.
|
||||||
|
|
||||||
|
1. Restart Synapse.
|
||||||
|
|
||||||
|
1. Add a Prometheus target for Synapse.
|
||||||
|
|
||||||
|
It needs to set the `metrics_path` to a non-default value (under
|
||||||
|
`scrape_configs`):
|
||||||
|
|
||||||
|
- job_name: "synapse"
|
||||||
|
metrics_path: "/_synapse/metrics"
|
||||||
|
static_configs:
|
||||||
|
- targets: ["my.server.here:port"]
|
||||||
|
|
||||||
|
where `my.server.here` is the IP address of Synapse, and `port` is
|
||||||
|
the listener port configured with the `metrics` resource.
|
||||||
|
|
||||||
|
If your prometheus is older than 1.5.2, you will need to replace
|
||||||
|
`static_configs` in the above with `target_groups`.
|
||||||
|
|
||||||
|
1. Restart Prometheus.
|
||||||
|
|
||||||
|
## Renaming of metrics & deprecation of old names in 1.2
|
||||||
|
|
||||||
|
Synapse 1.2 updates the Prometheus metrics to match the naming
|
||||||
|
convention of the upstream `prometheus_client`. The old names are
|
||||||
|
considered deprecated and will be removed in a future version of
|
||||||
|
Synapse.
|
||||||
|
|
||||||
|
| New Name | Old Name |
|
||||||
|
| ---------------------------------------------------------------------------- | ---------------------------------------------------------------------- |
|
||||||
|
| python_gc_objects_collected_total | python_gc_objects_collected |
|
||||||
|
| python_gc_objects_uncollectable_total | python_gc_objects_uncollectable |
|
||||||
|
| python_gc_collections_total | python_gc_collections |
|
||||||
|
| process_cpu_seconds_total | process_cpu_seconds |
|
||||||
|
| synapse_federation_client_sent_transactions_total | synapse_federation_client_sent_transactions |
|
||||||
|
| synapse_federation_client_events_processed_total | synapse_federation_client_events_processed |
|
||||||
|
| synapse_event_processing_loop_count_total | synapse_event_processing_loop_count |
|
||||||
|
| synapse_event_processing_loop_room_count_total | synapse_event_processing_loop_room_count |
|
||||||
|
| synapse_util_metrics_block_count_total | synapse_util_metrics_block_count |
|
||||||
|
| synapse_util_metrics_block_time_seconds_total | synapse_util_metrics_block_time_seconds |
|
||||||
|
| synapse_util_metrics_block_ru_utime_seconds_total | synapse_util_metrics_block_ru_utime_seconds |
|
||||||
|
| synapse_util_metrics_block_ru_stime_seconds_total | synapse_util_metrics_block_ru_stime_seconds |
|
||||||
|
| synapse_util_metrics_block_db_txn_count_total | synapse_util_metrics_block_db_txn_count |
|
||||||
|
| synapse_util_metrics_block_db_txn_duration_seconds_total | synapse_util_metrics_block_db_txn_duration_seconds |
|
||||||
|
| synapse_util_metrics_block_db_sched_duration_seconds_total | synapse_util_metrics_block_db_sched_duration_seconds |
|
||||||
|
| synapse_background_process_start_count_total | synapse_background_process_start_count |
|
||||||
|
| synapse_background_process_ru_utime_seconds_total | synapse_background_process_ru_utime_seconds |
|
||||||
|
| synapse_background_process_ru_stime_seconds_total | synapse_background_process_ru_stime_seconds |
|
||||||
|
| synapse_background_process_db_txn_count_total | synapse_background_process_db_txn_count |
|
||||||
|
| synapse_background_process_db_txn_duration_seconds_total | synapse_background_process_db_txn_duration_seconds |
|
||||||
|
| synapse_background_process_db_sched_duration_seconds_total | synapse_background_process_db_sched_duration_seconds |
|
||||||
|
| synapse_storage_events_persisted_events_total | synapse_storage_events_persisted_events |
|
||||||
|
| synapse_storage_events_persisted_events_sep_total | synapse_storage_events_persisted_events_sep |
|
||||||
|
| synapse_storage_events_state_delta_total | synapse_storage_events_state_delta |
|
||||||
|
| synapse_storage_events_state_delta_single_event_total | synapse_storage_events_state_delta_single_event |
|
||||||
|
| synapse_storage_events_state_delta_reuse_delta_total | synapse_storage_events_state_delta_reuse_delta |
|
||||||
|
| synapse_federation_server_received_pdus_total | synapse_federation_server_received_pdus |
|
||||||
|
| synapse_federation_server_received_edus_total | synapse_federation_server_received_edus |
|
||||||
|
| synapse_handler_presence_notified_presence_total | synapse_handler_presence_notified_presence |
|
||||||
|
| synapse_handler_presence_federation_presence_out_total | synapse_handler_presence_federation_presence_out |
|
||||||
|
| synapse_handler_presence_presence_updates_total | synapse_handler_presence_presence_updates |
|
||||||
|
| synapse_handler_presence_timers_fired_total | synapse_handler_presence_timers_fired |
|
||||||
|
| synapse_handler_presence_federation_presence_total | synapse_handler_presence_federation_presence |
|
||||||
|
| synapse_handler_presence_bump_active_time_total | synapse_handler_presence_bump_active_time |
|
||||||
|
| synapse_federation_client_sent_edus_total | synapse_federation_client_sent_edus |
|
||||||
|
| synapse_federation_client_sent_pdu_destinations_count_total | synapse_federation_client_sent_pdu_destinations:count |
|
||||||
|
| synapse_federation_client_sent_pdu_destinations_total | synapse_federation_client_sent_pdu_destinations:total |
|
||||||
|
| synapse_handlers_appservice_events_processed_total | synapse_handlers_appservice_events_processed |
|
||||||
|
| synapse_notifier_notified_events_total | synapse_notifier_notified_events |
|
||||||
|
| synapse_push_bulk_push_rule_evaluator_push_rules_invalidation_counter_total | synapse_push_bulk_push_rule_evaluator_push_rules_invalidation_counter |
|
||||||
|
| synapse_push_bulk_push_rule_evaluator_push_rules_state_size_counter_total | synapse_push_bulk_push_rule_evaluator_push_rules_state_size_counter |
|
||||||
|
| synapse_http_httppusher_http_pushes_processed_total | synapse_http_httppusher_http_pushes_processed |
|
||||||
|
| synapse_http_httppusher_http_pushes_failed_total | synapse_http_httppusher_http_pushes_failed |
|
||||||
|
| synapse_http_httppusher_badge_updates_processed_total | synapse_http_httppusher_badge_updates_processed |
|
||||||
|
| synapse_http_httppusher_badge_updates_failed_total | synapse_http_httppusher_badge_updates_failed |
|
||||||
|
|
||||||
|
Removal of deprecated metrics & time based counters becoming histograms in 0.31.0
|
||||||
|
---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
The duplicated metrics deprecated in Synapse 0.27.0 have been removed.
|
||||||
|
|
||||||
|
All time duration-based metrics have been changed to be seconds. This
|
||||||
|
affects:
|
||||||
|
|
||||||
|
| msec -> sec metrics |
|
||||||
|
| -------------------------------------- |
|
||||||
|
| python_gc_time |
|
||||||
|
| python_twisted_reactor_tick_time |
|
||||||
|
| synapse_storage_query_time |
|
||||||
|
| synapse_storage_schedule_time |
|
||||||
|
| synapse_storage_transaction_time |
|
||||||
|
|
||||||
|
Several metrics have been changed to be histograms, which sort entries
|
||||||
|
into buckets and allow better analysis. The following metrics are now
|
||||||
|
histograms:
|
||||||
|
|
||||||
|
| Altered metrics |
|
||||||
|
| ------------------------------------------------ |
|
||||||
|
| python_gc_time |
|
||||||
|
| python_twisted_reactor_pending_calls |
|
||||||
|
| python_twisted_reactor_tick_time |
|
||||||
|
| synapse_http_server_response_time_seconds |
|
||||||
|
| synapse_storage_query_time |
|
||||||
|
| synapse_storage_schedule_time |
|
||||||
|
| synapse_storage_transaction_time |
|
||||||
|
|
||||||
|
Block and response metrics renamed for 0.27.0
|
||||||
|
---------------------------------------------
|
||||||
|
|
||||||
|
Synapse 0.27.0 begins the process of rationalising the duplicate
|
||||||
|
`*:count` metrics reported for the resource tracking for code blocks and
|
||||||
|
HTTP requests.
|
||||||
|
|
||||||
|
At the same time, the corresponding `*:total` metrics are being renamed,
|
||||||
|
as the `:total` suffix no longer makes sense in the absence of a
|
||||||
|
corresponding `:count` metric.
|
||||||
|
|
||||||
|
To enable a graceful migration path, this release just adds new names
|
||||||
|
for the metrics being renamed. A future release will remove the old
|
||||||
|
ones.
|
||||||
|
|
||||||
|
The following table shows the new metrics, and the old metrics which
|
||||||
|
they are replacing.
|
||||||
|
|
||||||
|
| New name | Old name |
|
||||||
|
| ------------------------------------------------------------- | ---------------------------------------------------------- |
|
||||||
|
| synapse_util_metrics_block_count | synapse_util_metrics_block_timer:count |
|
||||||
|
| synapse_util_metrics_block_count | synapse_util_metrics_block_ru_utime:count |
|
||||||
|
| synapse_util_metrics_block_count | synapse_util_metrics_block_ru_stime:count |
|
||||||
|
| synapse_util_metrics_block_count | synapse_util_metrics_block_db_txn_count:count |
|
||||||
|
| synapse_util_metrics_block_count | synapse_util_metrics_block_db_txn_duration:count |
|
||||||
|
| synapse_util_metrics_block_time_seconds | synapse_util_metrics_block_timer:total |
|
||||||
|
| synapse_util_metrics_block_ru_utime_seconds | synapse_util_metrics_block_ru_utime:total |
|
||||||
|
| synapse_util_metrics_block_ru_stime_seconds | synapse_util_metrics_block_ru_stime:total |
|
||||||
|
| synapse_util_metrics_block_db_txn_count | synapse_util_metrics_block_db_txn_count:total |
|
||||||
|
| synapse_util_metrics_block_db_txn_duration_seconds | synapse_util_metrics_block_db_txn_duration:total |
|
||||||
|
| synapse_http_server_response_count | synapse_http_server_requests |
|
||||||
|
| synapse_http_server_response_count | synapse_http_server_response_time:count |
|
||||||
|
| synapse_http_server_response_count | synapse_http_server_response_ru_utime:count |
|
||||||
|
| synapse_http_server_response_count | synapse_http_server_response_ru_stime:count |
|
||||||
|
| synapse_http_server_response_count | synapse_http_server_response_db_txn_count:count |
|
||||||
|
| synapse_http_server_response_count | synapse_http_server_response_db_txn_duration:count |
|
||||||
|
| synapse_http_server_response_time_seconds | synapse_http_server_response_time:total |
|
||||||
|
| synapse_http_server_response_ru_utime_seconds | synapse_http_server_response_ru_utime:total |
|
||||||
|
| synapse_http_server_response_ru_stime_seconds | synapse_http_server_response_ru_stime:total |
|
||||||
|
| synapse_http_server_response_db_txn_count | synapse_http_server_response_db_txn_count:total |
|
||||||
|
| synapse_http_server_response_db_txn_duration_seconds | synapse_http_server_response_db_txn_duration:total |
|
||||||
|
|
||||||
|
Standard Metric Names
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
As of synapse version 0.18.2, the format of the process-wide metrics has
|
||||||
|
been changed to fit prometheus standard naming conventions. Additionally
|
||||||
|
the units have been changed to seconds, from miliseconds.
|
||||||
|
|
||||||
|
| New name | Old name |
|
||||||
|
| ---------------------------------------- | --------------------------------- |
|
||||||
|
| process_cpu_user_seconds_total | process_resource_utime / 1000 |
|
||||||
|
| process_cpu_system_seconds_total | process_resource_stime / 1000 |
|
||||||
|
| process_open_fds (no \'type\' label) | process_fds |
|
||||||
|
|
||||||
|
The python-specific counts of garbage collector performance have been
|
||||||
|
renamed.
|
||||||
|
|
||||||
|
| New name | Old name |
|
||||||
|
| -------------------------------- | -------------------------- |
|
||||||
|
| python_gc_time | reactor_gc_time |
|
||||||
|
| python_gc_unreachable_total | reactor_gc_unreachable |
|
||||||
|
| python_gc_counts | reactor_gc_counts |
|
||||||
|
|
||||||
|
The twisted-specific reactor metrics have been renamed.
|
||||||
|
|
||||||
|
| New name | Old name |
|
||||||
|
| -------------------------------------- | ----------------------- |
|
||||||
|
| python_twisted_reactor_pending_calls | reactor_pending_calls |
|
||||||
|
| python_twisted_reactor_tick_time | reactor_tick_time |
|
|
@ -1,285 +0,0 @@
|
||||||
How to monitor Synapse metrics using Prometheus
|
|
||||||
===============================================
|
|
||||||
|
|
||||||
1. Install Prometheus:
|
|
||||||
|
|
||||||
Follow instructions at http://prometheus.io/docs/introduction/install/
|
|
||||||
|
|
||||||
2. Enable Synapse metrics:
|
|
||||||
|
|
||||||
There are two methods of enabling metrics in Synapse.
|
|
||||||
|
|
||||||
The first serves the metrics as a part of the usual web server and can be
|
|
||||||
enabled by adding the "metrics" resource to the existing listener as such::
|
|
||||||
|
|
||||||
resources:
|
|
||||||
- names:
|
|
||||||
- client
|
|
||||||
- metrics
|
|
||||||
|
|
||||||
This provides a simple way of adding metrics to your Synapse installation,
|
|
||||||
and serves under ``/_synapse/metrics``. If you do not wish your metrics be
|
|
||||||
publicly exposed, you will need to either filter it out at your load
|
|
||||||
balancer, or use the second method.
|
|
||||||
|
|
||||||
The second method runs the metrics server on a different port, in a
|
|
||||||
different thread to Synapse. This can make it more resilient to heavy load
|
|
||||||
meaning metrics cannot be retrieved, and can be exposed to just internal
|
|
||||||
networks easier. The served metrics are available over HTTP only, and will
|
|
||||||
be available at ``/``.
|
|
||||||
|
|
||||||
Add a new listener to homeserver.yaml::
|
|
||||||
|
|
||||||
listeners:
|
|
||||||
- type: metrics
|
|
||||||
port: 9000
|
|
||||||
bind_addresses:
|
|
||||||
- '0.0.0.0'
|
|
||||||
|
|
||||||
For both options, you will need to ensure that ``enable_metrics`` is set to
|
|
||||||
``True``.
|
|
||||||
|
|
||||||
Restart Synapse.
|
|
||||||
|
|
||||||
3. Add a Prometheus target for Synapse.
|
|
||||||
|
|
||||||
It needs to set the ``metrics_path`` to a non-default value (under ``scrape_configs``)::
|
|
||||||
|
|
||||||
- job_name: "synapse"
|
|
||||||
metrics_path: "/_synapse/metrics"
|
|
||||||
static_configs:
|
|
||||||
- targets: ["my.server.here:port"]
|
|
||||||
|
|
||||||
where ``my.server.here`` is the IP address of Synapse, and ``port`` is the listener port
|
|
||||||
configured with the ``metrics`` resource.
|
|
||||||
|
|
||||||
If your prometheus is older than 1.5.2, you will need to replace
|
|
||||||
``static_configs`` in the above with ``target_groups``.
|
|
||||||
|
|
||||||
Restart Prometheus.
|
|
||||||
|
|
||||||
|
|
||||||
Renaming of metrics & deprecation of old names in 1.2
|
|
||||||
-----------------------------------------------------
|
|
||||||
|
|
||||||
Synapse 1.2 updates the Prometheus metrics to match the naming convention of the
|
|
||||||
upstream ``prometheus_client``. The old names are considered deprecated and will
|
|
||||||
be removed in a future version of Synapse.
|
|
||||||
|
|
||||||
+-----------------------------------------------------------------------------+-----------------------------------------------------------------------+
|
|
||||||
| New Name | Old Name |
|
|
||||||
+=============================================================================+=======================================================================+
|
|
||||||
| python_gc_objects_collected_total | python_gc_objects_collected |
|
|
||||||
+-----------------------------------------------------------------------------+-----------------------------------------------------------------------+
|
|
||||||
| python_gc_objects_uncollectable_total | python_gc_objects_uncollectable |
|
|
||||||
+-----------------------------------------------------------------------------+-----------------------------------------------------------------------+
|
|
||||||
| python_gc_collections_total | python_gc_collections |
|
|
||||||
+-----------------------------------------------------------------------------+-----------------------------------------------------------------------+
|
|
||||||
| process_cpu_seconds_total | process_cpu_seconds |
|
|
||||||
+-----------------------------------------------------------------------------+-----------------------------------------------------------------------+
|
|
||||||
| synapse_federation_client_sent_transactions_total | synapse_federation_client_sent_transactions |
|
|
||||||
+-----------------------------------------------------------------------------+-----------------------------------------------------------------------+
|
|
||||||
| synapse_federation_client_events_processed_total | synapse_federation_client_events_processed |
|
|
||||||
+-----------------------------------------------------------------------------+-----------------------------------------------------------------------+
|
|
||||||
| synapse_event_processing_loop_count_total | synapse_event_processing_loop_count |
|
|
||||||
+-----------------------------------------------------------------------------+-----------------------------------------------------------------------+
|
|
||||||
| synapse_event_processing_loop_room_count_total | synapse_event_processing_loop_room_count |
|
|
||||||
+-----------------------------------------------------------------------------+-----------------------------------------------------------------------+
|
|
||||||
| synapse_util_metrics_block_count_total | synapse_util_metrics_block_count |
|
|
||||||
+-----------------------------------------------------------------------------+-----------------------------------------------------------------------+
|
|
||||||
| synapse_util_metrics_block_time_seconds_total | synapse_util_metrics_block_time_seconds |
|
|
||||||
+-----------------------------------------------------------------------------+-----------------------------------------------------------------------+
|
|
||||||
| synapse_util_metrics_block_ru_utime_seconds_total | synapse_util_metrics_block_ru_utime_seconds |
|
|
||||||
+-----------------------------------------------------------------------------+-----------------------------------------------------------------------+
|
|
||||||
| synapse_util_metrics_block_ru_stime_seconds_total | synapse_util_metrics_block_ru_stime_seconds |
|
|
||||||
+-----------------------------------------------------------------------------+-----------------------------------------------------------------------+
|
|
||||||
| synapse_util_metrics_block_db_txn_count_total | synapse_util_metrics_block_db_txn_count |
|
|
||||||
+-----------------------------------------------------------------------------+-----------------------------------------------------------------------+
|
|
||||||
| synapse_util_metrics_block_db_txn_duration_seconds_total | synapse_util_metrics_block_db_txn_duration_seconds |
|
|
||||||
+-----------------------------------------------------------------------------+-----------------------------------------------------------------------+
|
|
||||||
| synapse_util_metrics_block_db_sched_duration_seconds_total | synapse_util_metrics_block_db_sched_duration_seconds |
|
|
||||||
+-----------------------------------------------------------------------------+-----------------------------------------------------------------------+
|
|
||||||
| synapse_background_process_start_count_total | synapse_background_process_start_count |
|
|
||||||
+-----------------------------------------------------------------------------+-----------------------------------------------------------------------+
|
|
||||||
| synapse_background_process_ru_utime_seconds_total | synapse_background_process_ru_utime_seconds |
|
|
||||||
+-----------------------------------------------------------------------------+-----------------------------------------------------------------------+
|
|
||||||
| synapse_background_process_ru_stime_seconds_total | synapse_background_process_ru_stime_seconds |
|
|
||||||
+-----------------------------------------------------------------------------+-----------------------------------------------------------------------+
|
|
||||||
| synapse_background_process_db_txn_count_total | synapse_background_process_db_txn_count |
|
|
||||||
+-----------------------------------------------------------------------------+-----------------------------------------------------------------------+
|
|
||||||
| synapse_background_process_db_txn_duration_seconds_total | synapse_background_process_db_txn_duration_seconds |
|
|
||||||
+-----------------------------------------------------------------------------+-----------------------------------------------------------------------+
|
|
||||||
| synapse_background_process_db_sched_duration_seconds_total | synapse_background_process_db_sched_duration_seconds |
|
|
||||||
+-----------------------------------------------------------------------------+-----------------------------------------------------------------------+
|
|
||||||
| synapse_storage_events_persisted_events_total | synapse_storage_events_persisted_events |
|
|
||||||
+-----------------------------------------------------------------------------+-----------------------------------------------------------------------+
|
|
||||||
| synapse_storage_events_persisted_events_sep_total | synapse_storage_events_persisted_events_sep |
|
|
||||||
+-----------------------------------------------------------------------------+-----------------------------------------------------------------------+
|
|
||||||
| synapse_storage_events_state_delta_total | synapse_storage_events_state_delta |
|
|
||||||
+-----------------------------------------------------------------------------+-----------------------------------------------------------------------+
|
|
||||||
| synapse_storage_events_state_delta_single_event_total | synapse_storage_events_state_delta_single_event |
|
|
||||||
+-----------------------------------------------------------------------------+-----------------------------------------------------------------------+
|
|
||||||
| synapse_storage_events_state_delta_reuse_delta_total | synapse_storage_events_state_delta_reuse_delta |
|
|
||||||
+-----------------------------------------------------------------------------+-----------------------------------------------------------------------+
|
|
||||||
| synapse_federation_server_received_pdus_total | synapse_federation_server_received_pdus |
|
|
||||||
+-----------------------------------------------------------------------------+-----------------------------------------------------------------------+
|
|
||||||
| synapse_federation_server_received_edus_total | synapse_federation_server_received_edus |
|
|
||||||
+-----------------------------------------------------------------------------+-----------------------------------------------------------------------+
|
|
||||||
| synapse_handler_presence_notified_presence_total | synapse_handler_presence_notified_presence |
|
|
||||||
+-----------------------------------------------------------------------------+-----------------------------------------------------------------------+
|
|
||||||
| synapse_handler_presence_federation_presence_out_total | synapse_handler_presence_federation_presence_out |
|
|
||||||
+-----------------------------------------------------------------------------+-----------------------------------------------------------------------+
|
|
||||||
| synapse_handler_presence_presence_updates_total | synapse_handler_presence_presence_updates |
|
|
||||||
+-----------------------------------------------------------------------------+-----------------------------------------------------------------------+
|
|
||||||
| synapse_handler_presence_timers_fired_total | synapse_handler_presence_timers_fired |
|
|
||||||
+-----------------------------------------------------------------------------+-----------------------------------------------------------------------+
|
|
||||||
| synapse_handler_presence_federation_presence_total | synapse_handler_presence_federation_presence |
|
|
||||||
+-----------------------------------------------------------------------------+-----------------------------------------------------------------------+
|
|
||||||
| synapse_handler_presence_bump_active_time_total | synapse_handler_presence_bump_active_time |
|
|
||||||
+-----------------------------------------------------------------------------+-----------------------------------------------------------------------+
|
|
||||||
| synapse_federation_client_sent_edus_total | synapse_federation_client_sent_edus |
|
|
||||||
+-----------------------------------------------------------------------------+-----------------------------------------------------------------------+
|
|
||||||
| synapse_federation_client_sent_pdu_destinations_count_total | synapse_federation_client_sent_pdu_destinations:count |
|
|
||||||
+-----------------------------------------------------------------------------+-----------------------------------------------------------------------+
|
|
||||||
| synapse_federation_client_sent_pdu_destinations_total | synapse_federation_client_sent_pdu_destinations:total |
|
|
||||||
+-----------------------------------------------------------------------------+-----------------------------------------------------------------------+
|
|
||||||
| synapse_handlers_appservice_events_processed_total | synapse_handlers_appservice_events_processed |
|
|
||||||
+-----------------------------------------------------------------------------+-----------------------------------------------------------------------+
|
|
||||||
| synapse_notifier_notified_events_total | synapse_notifier_notified_events |
|
|
||||||
+-----------------------------------------------------------------------------+-----------------------------------------------------------------------+
|
|
||||||
| synapse_push_bulk_push_rule_evaluator_push_rules_invalidation_counter_total | synapse_push_bulk_push_rule_evaluator_push_rules_invalidation_counter |
|
|
||||||
+-----------------------------------------------------------------------------+-----------------------------------------------------------------------+
|
|
||||||
| synapse_push_bulk_push_rule_evaluator_push_rules_state_size_counter_total | synapse_push_bulk_push_rule_evaluator_push_rules_state_size_counter |
|
|
||||||
+-----------------------------------------------------------------------------+-----------------------------------------------------------------------+
|
|
||||||
| synapse_http_httppusher_http_pushes_processed_total | synapse_http_httppusher_http_pushes_processed |
|
|
||||||
+-----------------------------------------------------------------------------+-----------------------------------------------------------------------+
|
|
||||||
| synapse_http_httppusher_http_pushes_failed_total | synapse_http_httppusher_http_pushes_failed |
|
|
||||||
+-----------------------------------------------------------------------------+-----------------------------------------------------------------------+
|
|
||||||
| synapse_http_httppusher_badge_updates_processed_total | synapse_http_httppusher_badge_updates_processed |
|
|
||||||
+-----------------------------------------------------------------------------+-----------------------------------------------------------------------+
|
|
||||||
| synapse_http_httppusher_badge_updates_failed_total | synapse_http_httppusher_badge_updates_failed |
|
|
||||||
+-----------------------------------------------------------------------------+-----------------------------------------------------------------------+
|
|
||||||
|
|
||||||
|
|
||||||
Removal of deprecated metrics & time based counters becoming histograms in 0.31.0
|
|
||||||
---------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
The duplicated metrics deprecated in Synapse 0.27.0 have been removed.
|
|
||||||
|
|
||||||
All time duration-based metrics have been changed to be seconds. This affects:
|
|
||||||
|
|
||||||
+----------------------------------+
|
|
||||||
| msec -> sec metrics |
|
|
||||||
+==================================+
|
|
||||||
| python_gc_time |
|
|
||||||
+----------------------------------+
|
|
||||||
| python_twisted_reactor_tick_time |
|
|
||||||
+----------------------------------+
|
|
||||||
| synapse_storage_query_time |
|
|
||||||
+----------------------------------+
|
|
||||||
| synapse_storage_schedule_time |
|
|
||||||
+----------------------------------+
|
|
||||||
| synapse_storage_transaction_time |
|
|
||||||
+----------------------------------+
|
|
||||||
|
|
||||||
Several metrics have been changed to be histograms, which sort entries into
|
|
||||||
buckets and allow better analysis. The following metrics are now histograms:
|
|
||||||
|
|
||||||
+-------------------------------------------+
|
|
||||||
| Altered metrics |
|
|
||||||
+===========================================+
|
|
||||||
| python_gc_time |
|
|
||||||
+-------------------------------------------+
|
|
||||||
| python_twisted_reactor_pending_calls |
|
|
||||||
+-------------------------------------------+
|
|
||||||
| python_twisted_reactor_tick_time |
|
|
||||||
+-------------------------------------------+
|
|
||||||
| synapse_http_server_response_time_seconds |
|
|
||||||
+-------------------------------------------+
|
|
||||||
| synapse_storage_query_time |
|
|
||||||
+-------------------------------------------+
|
|
||||||
| synapse_storage_schedule_time |
|
|
||||||
+-------------------------------------------+
|
|
||||||
| synapse_storage_transaction_time |
|
|
||||||
+-------------------------------------------+
|
|
||||||
|
|
||||||
|
|
||||||
Block and response metrics renamed for 0.27.0
|
|
||||||
---------------------------------------------
|
|
||||||
|
|
||||||
Synapse 0.27.0 begins the process of rationalising the duplicate ``*:count``
|
|
||||||
metrics reported for the resource tracking for code blocks and HTTP requests.
|
|
||||||
|
|
||||||
At the same time, the corresponding ``*:total`` metrics are being renamed, as
|
|
||||||
the ``:total`` suffix no longer makes sense in the absence of a corresponding
|
|
||||||
``:count`` metric.
|
|
||||||
|
|
||||||
To enable a graceful migration path, this release just adds new names for the
|
|
||||||
metrics being renamed. A future release will remove the old ones.
|
|
||||||
|
|
||||||
The following table shows the new metrics, and the old metrics which they are
|
|
||||||
replacing.
|
|
||||||
|
|
||||||
==================================================== ===================================================
|
|
||||||
New name Old name
|
|
||||||
==================================================== ===================================================
|
|
||||||
synapse_util_metrics_block_count synapse_util_metrics_block_timer:count
|
|
||||||
synapse_util_metrics_block_count synapse_util_metrics_block_ru_utime:count
|
|
||||||
synapse_util_metrics_block_count synapse_util_metrics_block_ru_stime:count
|
|
||||||
synapse_util_metrics_block_count synapse_util_metrics_block_db_txn_count:count
|
|
||||||
synapse_util_metrics_block_count synapse_util_metrics_block_db_txn_duration:count
|
|
||||||
|
|
||||||
synapse_util_metrics_block_time_seconds synapse_util_metrics_block_timer:total
|
|
||||||
synapse_util_metrics_block_ru_utime_seconds synapse_util_metrics_block_ru_utime:total
|
|
||||||
synapse_util_metrics_block_ru_stime_seconds synapse_util_metrics_block_ru_stime:total
|
|
||||||
synapse_util_metrics_block_db_txn_count synapse_util_metrics_block_db_txn_count:total
|
|
||||||
synapse_util_metrics_block_db_txn_duration_seconds synapse_util_metrics_block_db_txn_duration:total
|
|
||||||
|
|
||||||
synapse_http_server_response_count synapse_http_server_requests
|
|
||||||
synapse_http_server_response_count synapse_http_server_response_time:count
|
|
||||||
synapse_http_server_response_count synapse_http_server_response_ru_utime:count
|
|
||||||
synapse_http_server_response_count synapse_http_server_response_ru_stime:count
|
|
||||||
synapse_http_server_response_count synapse_http_server_response_db_txn_count:count
|
|
||||||
synapse_http_server_response_count synapse_http_server_response_db_txn_duration:count
|
|
||||||
|
|
||||||
synapse_http_server_response_time_seconds synapse_http_server_response_time:total
|
|
||||||
synapse_http_server_response_ru_utime_seconds synapse_http_server_response_ru_utime:total
|
|
||||||
synapse_http_server_response_ru_stime_seconds synapse_http_server_response_ru_stime:total
|
|
||||||
synapse_http_server_response_db_txn_count synapse_http_server_response_db_txn_count:total
|
|
||||||
synapse_http_server_response_db_txn_duration_seconds synapse_http_server_response_db_txn_duration:total
|
|
||||||
==================================================== ===================================================
|
|
||||||
|
|
||||||
|
|
||||||
Standard Metric Names
|
|
||||||
---------------------
|
|
||||||
|
|
||||||
As of synapse version 0.18.2, the format of the process-wide metrics has been
|
|
||||||
changed to fit prometheus standard naming conventions. Additionally the units
|
|
||||||
have been changed to seconds, from miliseconds.
|
|
||||||
|
|
||||||
================================== =============================
|
|
||||||
New name Old name
|
|
||||||
================================== =============================
|
|
||||||
process_cpu_user_seconds_total process_resource_utime / 1000
|
|
||||||
process_cpu_system_seconds_total process_resource_stime / 1000
|
|
||||||
process_open_fds (no 'type' label) process_fds
|
|
||||||
================================== =============================
|
|
||||||
|
|
||||||
The python-specific counts of garbage collector performance have been renamed.
|
|
||||||
|
|
||||||
=========================== ======================
|
|
||||||
New name Old name
|
|
||||||
=========================== ======================
|
|
||||||
python_gc_time reactor_gc_time
|
|
||||||
python_gc_unreachable_total reactor_gc_unreachable
|
|
||||||
python_gc_counts reactor_gc_counts
|
|
||||||
=========================== ======================
|
|
||||||
|
|
||||||
The twisted-specific reactor metrics have been renamed.
|
|
||||||
|
|
||||||
==================================== =====================
|
|
||||||
New name Old name
|
|
||||||
==================================== =====================
|
|
||||||
python_twisted_reactor_pending_calls reactor_pending_calls
|
|
||||||
python_twisted_reactor_tick_time reactor_tick_time
|
|
||||||
==================================== =====================
|
|
|
@ -0,0 +1,93 @@
|
||||||
|
# OpenTracing
|
||||||
|
|
||||||
|
## Background
|
||||||
|
|
||||||
|
OpenTracing is a semi-standard being adopted by a number of distributed
|
||||||
|
tracing platforms. It is a common api for facilitating vendor-agnostic
|
||||||
|
tracing instrumentation. That is, we can use the OpenTracing api and
|
||||||
|
select one of a number of tracer implementations to do the heavy lifting
|
||||||
|
in the background. Our current selected implementation is Jaeger.
|
||||||
|
|
||||||
|
OpenTracing is a tool which gives an insight into the causal
|
||||||
|
relationship of work done in and between servers. The servers each track
|
||||||
|
events and report them to a centralised server - in Synapse's case:
|
||||||
|
Jaeger. The basic unit used to represent events is the span. The span
|
||||||
|
roughly represents a single piece of work that was done and the time at
|
||||||
|
which it occurred. A span can have child spans, meaning that the work of
|
||||||
|
the child had to be completed for the parent span to complete, or it can
|
||||||
|
have follow-on spans which represent work that is undertaken as a result
|
||||||
|
of the parent but is not depended on by the parent to in order to
|
||||||
|
finish.
|
||||||
|
|
||||||
|
Since this is undertaken in a distributed environment a request to
|
||||||
|
another server, such as an RPC or a simple GET, can be considered a span
|
||||||
|
(a unit or work) for the local server. This causal link is what
|
||||||
|
OpenTracing aims to capture and visualise. In order to do this metadata
|
||||||
|
about the local server's span, i.e the 'span context', needs to be
|
||||||
|
included with the request to the remote.
|
||||||
|
|
||||||
|
It is up to the remote server to decide what it does with the spans it
|
||||||
|
creates. This is called the sampling policy and it can be configured
|
||||||
|
through Jaeger's settings.
|
||||||
|
|
||||||
|
For OpenTracing concepts see
|
||||||
|
<https://opentracing.io/docs/overview/what-is-tracing/>.
|
||||||
|
|
||||||
|
For more information about Jaeger's implementation see
|
||||||
|
<https://www.jaegertracing.io/docs/>
|
||||||
|
|
||||||
|
## Setting up OpenTracing
|
||||||
|
|
||||||
|
To receive OpenTracing spans, start up a Jaeger server. This can be done
|
||||||
|
using docker like so:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
docker run -d --name jaeger
|
||||||
|
-p 6831:6831/udp \
|
||||||
|
-p 6832:6832/udp \
|
||||||
|
-p 5778:5778 \
|
||||||
|
-p 16686:16686 \
|
||||||
|
-p 14268:14268 \
|
||||||
|
jaegertracing/all-in-one:1.13
|
||||||
|
```
|
||||||
|
|
||||||
|
Latest documentation is probably at
|
||||||
|
<https://www.jaegertracing.io/docs/1.13/getting-started/>
|
||||||
|
|
||||||
|
## Enable OpenTracing in Synapse
|
||||||
|
|
||||||
|
OpenTracing is not enabled by default. It must be enabled in the
|
||||||
|
homeserver config by uncommenting the config options under `opentracing`
|
||||||
|
as shown in the [sample config](./sample_config.yaml). For example:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
opentracing:
|
||||||
|
tracer_enabled: true
|
||||||
|
homeserver_whitelist:
|
||||||
|
- "mytrustedhomeserver.org"
|
||||||
|
- "*.myotherhomeservers.com"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Homeserver whitelisting
|
||||||
|
|
||||||
|
The homeserver whitelist is configured using regular expressions. A list
|
||||||
|
of regular expressions can be given and their union will be compared
|
||||||
|
when propagating any spans contexts to another homeserver.
|
||||||
|
|
||||||
|
Though it's mostly safe to send and receive span contexts to and from
|
||||||
|
untrusted users since span contexts are usually opaque ids it can lead
|
||||||
|
to two problems, namely:
|
||||||
|
|
||||||
|
- If the span context is marked as sampled by the sending homeserver
|
||||||
|
the receiver will sample it. Therefore two homeservers with wildly
|
||||||
|
different sampling policies could incur higher sampling counts than
|
||||||
|
intended.
|
||||||
|
- Sending servers can attach arbitrary data to spans, known as
|
||||||
|
'baggage'. For safety this has been disabled in Synapse but that
|
||||||
|
doesn't prevent another server sending you baggage which will be
|
||||||
|
logged to OpenTracing's logs.
|
||||||
|
|
||||||
|
## Configuring Jaeger
|
||||||
|
|
||||||
|
Sampling strategies can be set as in this document:
|
||||||
|
<https://www.jaegertracing.io/docs/1.13/sampling/>
|
|
@ -1,100 +0,0 @@
|
||||||
===========
|
|
||||||
OpenTracing
|
|
||||||
===========
|
|
||||||
|
|
||||||
Background
|
|
||||||
----------
|
|
||||||
|
|
||||||
OpenTracing is a semi-standard being adopted by a number of distributed tracing
|
|
||||||
platforms. It is a common api for facilitating vendor-agnostic tracing
|
|
||||||
instrumentation. That is, we can use the OpenTracing api and select one of a
|
|
||||||
number of tracer implementations to do the heavy lifting in the background.
|
|
||||||
Our current selected implementation is Jaeger.
|
|
||||||
|
|
||||||
OpenTracing is a tool which gives an insight into the causal relationship of
|
|
||||||
work done in and between servers. The servers each track events and report them
|
|
||||||
to a centralised server - in Synapse's case: Jaeger. The basic unit used to
|
|
||||||
represent events is the span. The span roughly represents a single piece of work
|
|
||||||
that was done and the time at which it occurred. A span can have child spans,
|
|
||||||
meaning that the work of the child had to be completed for the parent span to
|
|
||||||
complete, or it can have follow-on spans which represent work that is undertaken
|
|
||||||
as a result of the parent but is not depended on by the parent to in order to
|
|
||||||
finish.
|
|
||||||
|
|
||||||
Since this is undertaken in a distributed environment a request to another
|
|
||||||
server, such as an RPC or a simple GET, can be considered a span (a unit or
|
|
||||||
work) for the local server. This causal link is what OpenTracing aims to
|
|
||||||
capture and visualise. In order to do this metadata about the local server's
|
|
||||||
span, i.e the 'span context', needs to be included with the request to the
|
|
||||||
remote.
|
|
||||||
|
|
||||||
It is up to the remote server to decide what it does with the spans
|
|
||||||
it creates. This is called the sampling policy and it can be configured
|
|
||||||
through Jaeger's settings.
|
|
||||||
|
|
||||||
For OpenTracing concepts see
|
|
||||||
https://opentracing.io/docs/overview/what-is-tracing/.
|
|
||||||
|
|
||||||
For more information about Jaeger's implementation see
|
|
||||||
https://www.jaegertracing.io/docs/
|
|
||||||
|
|
||||||
=====================
|
|
||||||
Seting up OpenTracing
|
|
||||||
=====================
|
|
||||||
|
|
||||||
To receive OpenTracing spans, start up a Jaeger server. This can be done
|
|
||||||
using docker like so:
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
docker run -d --name jaeger
|
|
||||||
-p 6831:6831/udp \
|
|
||||||
-p 6832:6832/udp \
|
|
||||||
-p 5778:5778 \
|
|
||||||
-p 16686:16686 \
|
|
||||||
-p 14268:14268 \
|
|
||||||
jaegertracing/all-in-one:1.13
|
|
||||||
|
|
||||||
Latest documentation is probably at
|
|
||||||
https://www.jaegertracing.io/docs/1.13/getting-started/
|
|
||||||
|
|
||||||
|
|
||||||
Enable OpenTracing in Synapse
|
|
||||||
-----------------------------
|
|
||||||
|
|
||||||
OpenTracing is not enabled by default. It must be enabled in the homeserver
|
|
||||||
config by uncommenting the config options under ``opentracing`` as shown in
|
|
||||||
the `sample config <./sample_config.yaml>`_. For example:
|
|
||||||
|
|
||||||
.. code-block:: yaml
|
|
||||||
|
|
||||||
opentracing:
|
|
||||||
tracer_enabled: true
|
|
||||||
homeserver_whitelist:
|
|
||||||
- "mytrustedhomeserver.org"
|
|
||||||
- "*.myotherhomeservers.com"
|
|
||||||
|
|
||||||
Homeserver whitelisting
|
|
||||||
-----------------------
|
|
||||||
|
|
||||||
The homeserver whitelist is configured using regular expressions. A list of regular
|
|
||||||
expressions can be given and their union will be compared when propagating any
|
|
||||||
spans contexts to another homeserver.
|
|
||||||
|
|
||||||
Though it's mostly safe to send and receive span contexts to and from
|
|
||||||
untrusted users since span contexts are usually opaque ids it can lead to
|
|
||||||
two problems, namely:
|
|
||||||
|
|
||||||
- If the span context is marked as sampled by the sending homeserver the receiver will
|
|
||||||
sample it. Therefore two homeservers with wildly different sampling policies
|
|
||||||
could incur higher sampling counts than intended.
|
|
||||||
- Sending servers can attach arbitrary data to spans, known as 'baggage'. For safety this has been disabled in Synapse
|
|
||||||
but that doesn't prevent another server sending you baggage which will be logged
|
|
||||||
to OpenTracing's logs.
|
|
||||||
|
|
||||||
==================
|
|
||||||
Configuring Jaeger
|
|
||||||
==================
|
|
||||||
|
|
||||||
Sampling strategies can be set as in this document:
|
|
||||||
https://www.jaegertracing.io/docs/1.13/sampling/
|
|
|
@ -0,0 +1,116 @@
|
||||||
|
# Password auth provider modules
|
||||||
|
|
||||||
|
Password auth providers offer a way for server administrators to
|
||||||
|
integrate their Synapse installation with an existing authentication
|
||||||
|
system.
|
||||||
|
|
||||||
|
A password auth provider is a Python class which is dynamically loaded
|
||||||
|
into Synapse, and provides a number of methods by which it can integrate
|
||||||
|
with the authentication system.
|
||||||
|
|
||||||
|
This document serves as a reference for those looking to implement their
|
||||||
|
own password auth providers.
|
||||||
|
|
||||||
|
## Required methods
|
||||||
|
|
||||||
|
Password auth provider classes must provide the following methods:
|
||||||
|
|
||||||
|
*class* `SomeProvider.parse_config`(*config*)
|
||||||
|
|
||||||
|
> This method is passed the `config` object for this module from the
|
||||||
|
> homeserver configuration file.
|
||||||
|
>
|
||||||
|
> It should perform any appropriate sanity checks on the provided
|
||||||
|
> configuration, and return an object which is then passed into
|
||||||
|
> `__init__`.
|
||||||
|
|
||||||
|
*class* `SomeProvider`(*config*, *account_handler*)
|
||||||
|
|
||||||
|
> The constructor is passed the config object returned by
|
||||||
|
> `parse_config`, and a `synapse.module_api.ModuleApi` object which
|
||||||
|
> allows the password provider to check if accounts exist and/or create
|
||||||
|
> new ones.
|
||||||
|
|
||||||
|
## Optional methods
|
||||||
|
|
||||||
|
Password auth provider classes may optionally provide the following
|
||||||
|
methods.
|
||||||
|
|
||||||
|
*class* `SomeProvider.get_db_schema_files`()
|
||||||
|
|
||||||
|
> This method, if implemented, should return an Iterable of
|
||||||
|
> `(name, stream)` pairs of database schema files. Each file is applied
|
||||||
|
> in turn at initialisation, and a record is then made in the database
|
||||||
|
> so that it is not re-applied on the next start.
|
||||||
|
|
||||||
|
`someprovider.get_supported_login_types`()
|
||||||
|
|
||||||
|
> This method, if implemented, should return a `dict` mapping from a
|
||||||
|
> login type identifier (such as `m.login.password`) to an iterable
|
||||||
|
> giving the fields which must be provided by the user in the submission
|
||||||
|
> to the `/login` api. These fields are passed in the `login_dict`
|
||||||
|
> dictionary to `check_auth`.
|
||||||
|
>
|
||||||
|
> For example, if a password auth provider wants to implement a custom
|
||||||
|
> login type of `com.example.custom_login`, where the client is expected
|
||||||
|
> to pass the fields `secret1` and `secret2`, the provider should
|
||||||
|
> implement this method and return the following dict:
|
||||||
|
>
|
||||||
|
> {"com.example.custom_login": ("secret1", "secret2")}
|
||||||
|
|
||||||
|
`someprovider.check_auth`(*username*, *login_type*, *login_dict*)
|
||||||
|
|
||||||
|
> This method is the one that does the real work. If implemented, it
|
||||||
|
> will be called for each login attempt where the login type matches one
|
||||||
|
> of the keys returned by `get_supported_login_types`.
|
||||||
|
>
|
||||||
|
> It is passed the (possibly UNqualified) `user` provided by the client,
|
||||||
|
> the login type, and a dictionary of login secrets passed by the
|
||||||
|
> client.
|
||||||
|
>
|
||||||
|
> The method should return a Twisted `Deferred` object, which resolves
|
||||||
|
> to the canonical `@localpart:domain` user id if authentication is
|
||||||
|
> successful, and `None` if not.
|
||||||
|
>
|
||||||
|
> Alternatively, the `Deferred` can resolve to a `(str, func)` tuple, in
|
||||||
|
> which case the second field is a callback which will be called with
|
||||||
|
> the result from the `/login` call (including `access_token`,
|
||||||
|
> `device_id`, etc.)
|
||||||
|
|
||||||
|
`someprovider.check_3pid_auth`(*medium*, *address*, *password*)
|
||||||
|
|
||||||
|
> This method, if implemented, is called when a user attempts to
|
||||||
|
> register or log in with a third party identifier, such as email. It is
|
||||||
|
> passed the medium (ex. "email"), an address (ex.
|
||||||
|
> "<jdoe@example.com>") and the user's password.
|
||||||
|
>
|
||||||
|
> The method should return a Twisted `Deferred` object, which resolves
|
||||||
|
> to a `str` containing the user's (canonical) User ID if
|
||||||
|
> authentication was successful, and `None` if not.
|
||||||
|
>
|
||||||
|
> As with `check_auth`, the `Deferred` may alternatively resolve to a
|
||||||
|
> `(user_id, callback)` tuple.
|
||||||
|
|
||||||
|
`someprovider.check_password`(*user_id*, *password*)
|
||||||
|
|
||||||
|
> This method provides a simpler interface than
|
||||||
|
> `get_supported_login_types` and `check_auth` for password auth
|
||||||
|
> providers that just want to provide a mechanism for validating
|
||||||
|
> `m.login.password` logins.
|
||||||
|
>
|
||||||
|
> Iif implemented, it will be called to check logins with an
|
||||||
|
> `m.login.password` login type. It is passed a qualified
|
||||||
|
> `@localpart:domain` user id, and the password provided by the user.
|
||||||
|
>
|
||||||
|
> The method should return a Twisted `Deferred` object, which resolves
|
||||||
|
> to `True` if authentication is successful, and `False` if not.
|
||||||
|
|
||||||
|
`someprovider.on_logged_out`(*user_id*, *device_id*, *access_token*)
|
||||||
|
|
||||||
|
> This method, if implemented, is called when a user logs out. It is
|
||||||
|
> passed the qualified user ID, the ID of the deactivated device (if
|
||||||
|
> any: access tokens are occasionally created without an associated
|
||||||
|
> device ID), and the (now deactivated) access token.
|
||||||
|
>
|
||||||
|
> It may return a Twisted `Deferred` object; the logout request will
|
||||||
|
> wait for the deferred to complete but the result is ignored.
|
|
@ -1,113 +0,0 @@
|
||||||
Password auth provider modules
|
|
||||||
==============================
|
|
||||||
|
|
||||||
Password auth providers offer a way for server administrators to integrate
|
|
||||||
their Synapse installation with an existing authentication system.
|
|
||||||
|
|
||||||
A password auth provider is a Python class which is dynamically loaded into
|
|
||||||
Synapse, and provides a number of methods by which it can integrate with the
|
|
||||||
authentication system.
|
|
||||||
|
|
||||||
This document serves as a reference for those looking to implement their own
|
|
||||||
password auth providers.
|
|
||||||
|
|
||||||
Required methods
|
|
||||||
----------------
|
|
||||||
|
|
||||||
Password auth provider classes must provide the following methods:
|
|
||||||
|
|
||||||
*class* ``SomeProvider.parse_config``\(*config*)
|
|
||||||
|
|
||||||
This method is passed the ``config`` object for this module from the
|
|
||||||
homeserver configuration file.
|
|
||||||
|
|
||||||
It should perform any appropriate sanity checks on the provided
|
|
||||||
configuration, and return an object which is then passed into ``__init__``.
|
|
||||||
|
|
||||||
*class* ``SomeProvider``\(*config*, *account_handler*)
|
|
||||||
|
|
||||||
The constructor is passed the config object returned by ``parse_config``,
|
|
||||||
and a ``synapse.module_api.ModuleApi`` object which allows the
|
|
||||||
password provider to check if accounts exist and/or create new ones.
|
|
||||||
|
|
||||||
Optional methods
|
|
||||||
----------------
|
|
||||||
|
|
||||||
Password auth provider classes may optionally provide the following methods.
|
|
||||||
|
|
||||||
*class* ``SomeProvider.get_db_schema_files``\()
|
|
||||||
|
|
||||||
This method, if implemented, should return an Iterable of ``(name,
|
|
||||||
stream)`` pairs of database schema files. Each file is applied in turn at
|
|
||||||
initialisation, and a record is then made in the database so that it is
|
|
||||||
not re-applied on the next start.
|
|
||||||
|
|
||||||
``someprovider.get_supported_login_types``\()
|
|
||||||
|
|
||||||
This method, if implemented, should return a ``dict`` mapping from a login
|
|
||||||
type identifier (such as ``m.login.password``) to an iterable giving the
|
|
||||||
fields which must be provided by the user in the submission to the
|
|
||||||
``/login`` api. These fields are passed in the ``login_dict`` dictionary
|
|
||||||
to ``check_auth``.
|
|
||||||
|
|
||||||
For example, if a password auth provider wants to implement a custom login
|
|
||||||
type of ``com.example.custom_login``, where the client is expected to pass
|
|
||||||
the fields ``secret1`` and ``secret2``, the provider should implement this
|
|
||||||
method and return the following dict::
|
|
||||||
|
|
||||||
{"com.example.custom_login": ("secret1", "secret2")}
|
|
||||||
|
|
||||||
``someprovider.check_auth``\(*username*, *login_type*, *login_dict*)
|
|
||||||
|
|
||||||
This method is the one that does the real work. If implemented, it will be
|
|
||||||
called for each login attempt where the login type matches one of the keys
|
|
||||||
returned by ``get_supported_login_types``.
|
|
||||||
|
|
||||||
It is passed the (possibly UNqualified) ``user`` provided by the client,
|
|
||||||
the login type, and a dictionary of login secrets passed by the client.
|
|
||||||
|
|
||||||
The method should return a Twisted ``Deferred`` object, which resolves to
|
|
||||||
the canonical ``@localpart:domain`` user id if authentication is successful,
|
|
||||||
and ``None`` if not.
|
|
||||||
|
|
||||||
Alternatively, the ``Deferred`` can resolve to a ``(str, func)`` tuple, in
|
|
||||||
which case the second field is a callback which will be called with the
|
|
||||||
result from the ``/login`` call (including ``access_token``, ``device_id``,
|
|
||||||
etc.)
|
|
||||||
|
|
||||||
``someprovider.check_3pid_auth``\(*medium*, *address*, *password*)
|
|
||||||
|
|
||||||
This method, if implemented, is called when a user attempts to register or
|
|
||||||
log in with a third party identifier, such as email. It is passed the
|
|
||||||
medium (ex. "email"), an address (ex. "jdoe@example.com") and the user's
|
|
||||||
password.
|
|
||||||
|
|
||||||
The method should return a Twisted ``Deferred`` object, which resolves to
|
|
||||||
a ``str`` containing the user's (canonical) User ID if authentication was
|
|
||||||
successful, and ``None`` if not.
|
|
||||||
|
|
||||||
As with ``check_auth``, the ``Deferred`` may alternatively resolve to a
|
|
||||||
``(user_id, callback)`` tuple.
|
|
||||||
|
|
||||||
``someprovider.check_password``\(*user_id*, *password*)
|
|
||||||
|
|
||||||
This method provides a simpler interface than ``get_supported_login_types``
|
|
||||||
and ``check_auth`` for password auth providers that just want to provide a
|
|
||||||
mechanism for validating ``m.login.password`` logins.
|
|
||||||
|
|
||||||
Iif implemented, it will be called to check logins with an
|
|
||||||
``m.login.password`` login type. It is passed a qualified
|
|
||||||
``@localpart:domain`` user id, and the password provided by the user.
|
|
||||||
|
|
||||||
The method should return a Twisted ``Deferred`` object, which resolves to
|
|
||||||
``True`` if authentication is successful, and ``False`` if not.
|
|
||||||
|
|
||||||
``someprovider.on_logged_out``\(*user_id*, *device_id*, *access_token*)
|
|
||||||
|
|
||||||
This method, if implemented, is called when a user logs out. It is passed
|
|
||||||
the qualified user ID, the ID of the deactivated device (if any: access
|
|
||||||
tokens are occasionally created without an associated device ID), and the
|
|
||||||
(now deactivated) access token.
|
|
||||||
|
|
||||||
It may return a Twisted ``Deferred`` object; the logout request will wait
|
|
||||||
for the deferred to complete but the result is ignored.
|
|
|
@ -0,0 +1,164 @@
|
||||||
|
# Using Postgres
|
||||||
|
|
||||||
|
Postgres version 9.5 or later is known to work.
|
||||||
|
|
||||||
|
## Install postgres client libraries
|
||||||
|
|
||||||
|
Synapse will require the python postgres client library in order to
|
||||||
|
connect to a postgres database.
|
||||||
|
|
||||||
|
- If you are using the [matrix.org debian/ubuntu
|
||||||
|
packages](../INSTALL.md#matrixorg-packages), the necessary python
|
||||||
|
library will already be installed, but you will need to ensure the
|
||||||
|
low-level postgres library is installed, which you can do with
|
||||||
|
`apt install libpq5`.
|
||||||
|
- For other pre-built packages, please consult the documentation from
|
||||||
|
the relevant package.
|
||||||
|
- If you installed synapse [in a
|
||||||
|
virtualenv](../INSTALL.md#installing-from-source), you can install
|
||||||
|
the library with:
|
||||||
|
|
||||||
|
~/synapse/env/bin/pip install matrix-synapse[postgres]
|
||||||
|
|
||||||
|
(substituting the path to your virtualenv for `~/synapse/env`, if
|
||||||
|
you used a different path). You will require the postgres
|
||||||
|
development files. These are in the `libpq-dev` package on
|
||||||
|
Debian-derived distributions.
|
||||||
|
|
||||||
|
## Set up database
|
||||||
|
|
||||||
|
Assuming your PostgreSQL database user is called `postgres`, create a
|
||||||
|
user `synapse_user` with:
|
||||||
|
|
||||||
|
su - postgres
|
||||||
|
createuser --pwprompt synapse_user
|
||||||
|
|
||||||
|
Before you can authenticate with the `synapse_user`, you must create a
|
||||||
|
database that it can access. To create a database, first connect to the
|
||||||
|
database with your database user:
|
||||||
|
|
||||||
|
su - postgres
|
||||||
|
psql
|
||||||
|
|
||||||
|
and then run:
|
||||||
|
|
||||||
|
CREATE DATABASE synapse
|
||||||
|
ENCODING 'UTF8'
|
||||||
|
LC_COLLATE='C'
|
||||||
|
LC_CTYPE='C'
|
||||||
|
template=template0
|
||||||
|
OWNER synapse_user;
|
||||||
|
|
||||||
|
This would create an appropriate database named `synapse` owned by the
|
||||||
|
`synapse_user` user (which must already have been created as above).
|
||||||
|
|
||||||
|
Note that the PostgreSQL database *must* have the correct encoding set
|
||||||
|
(as shown above), otherwise it will not be able to store UTF8 strings.
|
||||||
|
|
||||||
|
You may need to enable password authentication so `synapse_user` can
|
||||||
|
connect to the database. See
|
||||||
|
<https://www.postgresql.org/docs/11/auth-pg-hba-conf.html>.
|
||||||
|
|
||||||
|
## Tuning Postgres
|
||||||
|
|
||||||
|
The default settings should be fine for most deployments. For larger
|
||||||
|
scale deployments tuning some of the settings is recommended, details of
|
||||||
|
which can be found at
|
||||||
|
<https://wiki.postgresql.org/wiki/Tuning_Your_PostgreSQL_Server>.
|
||||||
|
|
||||||
|
In particular, we've found tuning the following values helpful for
|
||||||
|
performance:
|
||||||
|
|
||||||
|
- `shared_buffers`
|
||||||
|
- `effective_cache_size`
|
||||||
|
- `work_mem`
|
||||||
|
- `maintenance_work_mem`
|
||||||
|
- `autovacuum_work_mem`
|
||||||
|
|
||||||
|
Note that the appropriate values for those fields depend on the amount
|
||||||
|
of free memory the database host has available.
|
||||||
|
|
||||||
|
## Synapse config
|
||||||
|
|
||||||
|
When you are ready to start using PostgreSQL, edit the `database`
|
||||||
|
section in your config file to match the following lines:
|
||||||
|
|
||||||
|
database:
|
||||||
|
name: psycopg2
|
||||||
|
args:
|
||||||
|
user: <user>
|
||||||
|
password: <pass>
|
||||||
|
database: <db>
|
||||||
|
host: <host>
|
||||||
|
cp_min: 5
|
||||||
|
cp_max: 10
|
||||||
|
|
||||||
|
All key, values in `args` are passed to the `psycopg2.connect(..)`
|
||||||
|
function, except keys beginning with `cp_`, which are consumed by the
|
||||||
|
twisted adbapi connection pool.
|
||||||
|
|
||||||
|
## Porting from SQLite
|
||||||
|
|
||||||
|
### Overview
|
||||||
|
|
||||||
|
The script `synapse_port_db` allows porting an existing synapse server
|
||||||
|
backed by SQLite to using PostgreSQL. This is done in as a two phase
|
||||||
|
process:
|
||||||
|
|
||||||
|
1. Copy the existing SQLite database to a separate location (while the
|
||||||
|
server is down) and running the port script against that offline
|
||||||
|
database.
|
||||||
|
2. Shut down the server. Rerun the port script to port any data that
|
||||||
|
has come in since taking the first snapshot. Restart server against
|
||||||
|
the PostgreSQL database.
|
||||||
|
|
||||||
|
The port script is designed to be run repeatedly against newer snapshots
|
||||||
|
of the SQLite database file. This makes it safe to repeat step 1 if
|
||||||
|
there was a delay between taking the previous snapshot and being ready
|
||||||
|
to do step 2.
|
||||||
|
|
||||||
|
It is safe to at any time kill the port script and restart it.
|
||||||
|
|
||||||
|
### Using the port script
|
||||||
|
|
||||||
|
Firstly, shut down the currently running synapse server and copy its
|
||||||
|
database file (typically `homeserver.db`) to another location. Once the
|
||||||
|
copy is complete, restart synapse. For instance:
|
||||||
|
|
||||||
|
./synctl stop
|
||||||
|
cp homeserver.db homeserver.db.snapshot
|
||||||
|
./synctl start
|
||||||
|
|
||||||
|
Copy the old config file into a new config file:
|
||||||
|
|
||||||
|
cp homeserver.yaml homeserver-postgres.yaml
|
||||||
|
|
||||||
|
Edit the database section as described in the section *Synapse config*
|
||||||
|
above and with the SQLite snapshot located at `homeserver.db.snapshot`
|
||||||
|
simply run:
|
||||||
|
|
||||||
|
synapse_port_db --sqlite-database homeserver.db.snapshot \
|
||||||
|
--postgres-config homeserver-postgres.yaml
|
||||||
|
|
||||||
|
The flag `--curses` displays a coloured curses progress UI.
|
||||||
|
|
||||||
|
If the script took a long time to complete, or time has otherwise passed
|
||||||
|
since the original snapshot was taken, repeat the previous steps with a
|
||||||
|
newer snapshot.
|
||||||
|
|
||||||
|
To complete the conversion shut down the synapse server and run the port
|
||||||
|
script one last time, e.g. if the SQLite database is at `homeserver.db`
|
||||||
|
run:
|
||||||
|
|
||||||
|
synapse_port_db --sqlite-database homeserver.db \
|
||||||
|
--postgres-config homeserver-postgres.yaml
|
||||||
|
|
||||||
|
Once that has completed, change the synapse config to point at the
|
||||||
|
PostgreSQL database configuration file `homeserver-postgres.yaml`:
|
||||||
|
|
||||||
|
./synctl stop
|
||||||
|
mv homeserver.yaml homeserver-old-sqlite.yaml
|
||||||
|
mv homeserver-postgres.yaml homeserver.yaml
|
||||||
|
./synctl start
|
||||||
|
|
||||||
|
Synapse should now be running against PostgreSQL.
|
|
@ -1,166 +0,0 @@
|
||||||
Using Postgres
|
|
||||||
--------------
|
|
||||||
|
|
||||||
Postgres version 9.5 or later is known to work.
|
|
||||||
|
|
||||||
Install postgres client libraries
|
|
||||||
=================================
|
|
||||||
|
|
||||||
Synapse will require the python postgres client library in order to connect to
|
|
||||||
a postgres database.
|
|
||||||
|
|
||||||
* If you are using the `matrix.org debian/ubuntu
|
|
||||||
packages <../INSTALL.md#matrixorg-packages>`_,
|
|
||||||
the necessary python library will already be installed, but you will need to
|
|
||||||
ensure the low-level postgres library is installed, which you can do with
|
|
||||||
``apt install libpq5``.
|
|
||||||
|
|
||||||
* For other pre-built packages, please consult the documentation from the
|
|
||||||
relevant package.
|
|
||||||
|
|
||||||
* If you installed synapse `in a virtualenv
|
|
||||||
<../INSTALL.md#installing-from-source>`_, you can install the library with::
|
|
||||||
|
|
||||||
~/synapse/env/bin/pip install matrix-synapse[postgres]
|
|
||||||
|
|
||||||
(substituting the path to your virtualenv for ``~/synapse/env``, if you used a
|
|
||||||
different path). You will require the postgres development files. These are in
|
|
||||||
the ``libpq-dev`` package on Debian-derived distributions.
|
|
||||||
|
|
||||||
Set up database
|
|
||||||
===============
|
|
||||||
|
|
||||||
Assuming your PostgreSQL database user is called ``postgres``, create a user
|
|
||||||
``synapse_user`` with::
|
|
||||||
|
|
||||||
su - postgres
|
|
||||||
createuser --pwprompt synapse_user
|
|
||||||
|
|
||||||
Before you can authenticate with the ``synapse_user``, you must create a
|
|
||||||
database that it can access. To create a database, first connect to the database
|
|
||||||
with your database user::
|
|
||||||
|
|
||||||
su - postgres
|
|
||||||
psql
|
|
||||||
|
|
||||||
and then run::
|
|
||||||
|
|
||||||
CREATE DATABASE synapse
|
|
||||||
ENCODING 'UTF8'
|
|
||||||
LC_COLLATE='C'
|
|
||||||
LC_CTYPE='C'
|
|
||||||
template=template0
|
|
||||||
OWNER synapse_user;
|
|
||||||
|
|
||||||
This would create an appropriate database named ``synapse`` owned by the
|
|
||||||
``synapse_user`` user (which must already have been created as above).
|
|
||||||
|
|
||||||
Note that the PostgreSQL database *must* have the correct encoding set (as
|
|
||||||
shown above), otherwise it will not be able to store UTF8 strings.
|
|
||||||
|
|
||||||
You may need to enable password authentication so ``synapse_user`` can connect
|
|
||||||
to the database. See https://www.postgresql.org/docs/11/auth-pg-hba-conf.html.
|
|
||||||
|
|
||||||
Tuning Postgres
|
|
||||||
===============
|
|
||||||
|
|
||||||
The default settings should be fine for most deployments. For larger scale
|
|
||||||
deployments tuning some of the settings is recommended, details of which can be
|
|
||||||
found at https://wiki.postgresql.org/wiki/Tuning_Your_PostgreSQL_Server.
|
|
||||||
|
|
||||||
In particular, we've found tuning the following values helpful for performance:
|
|
||||||
|
|
||||||
- ``shared_buffers``
|
|
||||||
- ``effective_cache_size``
|
|
||||||
- ``work_mem``
|
|
||||||
- ``maintenance_work_mem``
|
|
||||||
- ``autovacuum_work_mem``
|
|
||||||
|
|
||||||
Note that the appropriate values for those fields depend on the amount of free
|
|
||||||
memory the database host has available.
|
|
||||||
|
|
||||||
Synapse config
|
|
||||||
==============
|
|
||||||
|
|
||||||
When you are ready to start using PostgreSQL, edit the ``database`` section in
|
|
||||||
your config file to match the following lines::
|
|
||||||
|
|
||||||
database:
|
|
||||||
name: psycopg2
|
|
||||||
args:
|
|
||||||
user: <user>
|
|
||||||
password: <pass>
|
|
||||||
database: <db>
|
|
||||||
host: <host>
|
|
||||||
cp_min: 5
|
|
||||||
cp_max: 10
|
|
||||||
|
|
||||||
All key, values in ``args`` are passed to the ``psycopg2.connect(..)``
|
|
||||||
function, except keys beginning with ``cp_``, which are consumed by the twisted
|
|
||||||
adbapi connection pool.
|
|
||||||
|
|
||||||
|
|
||||||
Porting from SQLite
|
|
||||||
===================
|
|
||||||
|
|
||||||
Overview
|
|
||||||
~~~~~~~~
|
|
||||||
|
|
||||||
The script ``synapse_port_db`` allows porting an existing synapse server
|
|
||||||
backed by SQLite to using PostgreSQL. This is done in as a two phase process:
|
|
||||||
|
|
||||||
1. Copy the existing SQLite database to a separate location (while the server
|
|
||||||
is down) and running the port script against that offline database.
|
|
||||||
2. Shut down the server. Rerun the port script to port any data that has come
|
|
||||||
in since taking the first snapshot. Restart server against the PostgreSQL
|
|
||||||
database.
|
|
||||||
|
|
||||||
The port script is designed to be run repeatedly against newer snapshots of the
|
|
||||||
SQLite database file. This makes it safe to repeat step 1 if there was a delay
|
|
||||||
between taking the previous snapshot and being ready to do step 2.
|
|
||||||
|
|
||||||
It is safe to at any time kill the port script and restart it.
|
|
||||||
|
|
||||||
Using the port script
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Firstly, shut down the currently running synapse server and copy its database
|
|
||||||
file (typically ``homeserver.db``) to another location. Once the copy is
|
|
||||||
complete, restart synapse. For instance::
|
|
||||||
|
|
||||||
./synctl stop
|
|
||||||
cp homeserver.db homeserver.db.snapshot
|
|
||||||
./synctl start
|
|
||||||
|
|
||||||
Copy the old config file into a new config file::
|
|
||||||
|
|
||||||
cp homeserver.yaml homeserver-postgres.yaml
|
|
||||||
|
|
||||||
Edit the database section as described in the section *Synapse config* above
|
|
||||||
and with the SQLite snapshot located at ``homeserver.db.snapshot`` simply run::
|
|
||||||
|
|
||||||
synapse_port_db --sqlite-database homeserver.db.snapshot \
|
|
||||||
--postgres-config homeserver-postgres.yaml
|
|
||||||
|
|
||||||
The flag ``--curses`` displays a coloured curses progress UI.
|
|
||||||
|
|
||||||
If the script took a long time to complete, or time has otherwise passed since
|
|
||||||
the original snapshot was taken, repeat the previous steps with a newer
|
|
||||||
snapshot.
|
|
||||||
|
|
||||||
To complete the conversion shut down the synapse server and run the port
|
|
||||||
script one last time, e.g. if the SQLite database is at ``homeserver.db``
|
|
||||||
run::
|
|
||||||
|
|
||||||
synapse_port_db --sqlite-database homeserver.db \
|
|
||||||
--postgres-config homeserver-postgres.yaml
|
|
||||||
|
|
||||||
Once that has completed, change the synapse config to point at the PostgreSQL
|
|
||||||
database configuration file ``homeserver-postgres.yaml``::
|
|
||||||
|
|
||||||
./synctl stop
|
|
||||||
mv homeserver.yaml homeserver-old-sqlite.yaml
|
|
||||||
mv homeserver-postgres.yaml homeserver.yaml
|
|
||||||
./synctl start
|
|
||||||
|
|
||||||
Synapse should now be running against PostgreSQL.
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
# Replication Architecture
|
||||||
|
|
||||||
|
## Motivation
|
||||||
|
|
||||||
|
We'd like to be able to split some of the work that synapse does into
|
||||||
|
multiple python processes. In theory multiple synapse processes could
|
||||||
|
share a single postgresql database and we\'d scale up by running more
|
||||||
|
synapse processes. However much of synapse assumes that only one process
|
||||||
|
is interacting with the database, both for assigning unique identifiers
|
||||||
|
when inserting into tables, notifying components about new updates, and
|
||||||
|
for invalidating its caches.
|
||||||
|
|
||||||
|
So running multiple copies of the current code isn't an option. One way
|
||||||
|
to run multiple processes would be to have a single writer process and
|
||||||
|
multiple reader processes connected to the same database. In order to do
|
||||||
|
this we'd need a way for the reader process to invalidate its in-memory
|
||||||
|
caches when an update happens on the writer. One way to do this is for
|
||||||
|
the writer to present an append-only log of updates which the readers
|
||||||
|
can consume to invalidate their caches and to push updates to listening
|
||||||
|
clients or pushers.
|
||||||
|
|
||||||
|
Synapse already stores much of its data as an append-only log so that it
|
||||||
|
can correctly respond to `/sync` requests so the amount of code changes
|
||||||
|
needed to expose the append-only log to the readers should be fairly
|
||||||
|
minimal.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
### The Replication Protocol
|
||||||
|
|
||||||
|
See [tcp_replication.md](tcp_replication.md)
|
||||||
|
|
||||||
|
### The Slaved DataStore
|
||||||
|
|
||||||
|
There are read-only version of the synapse storage layer in
|
||||||
|
`synapse/replication/slave/storage` that use the response of the
|
||||||
|
replication API to invalidate their caches.
|
|
@ -1,40 +0,0 @@
|
||||||
Replication Architecture
|
|
||||||
========================
|
|
||||||
|
|
||||||
Motivation
|
|
||||||
----------
|
|
||||||
|
|
||||||
We'd like to be able to split some of the work that synapse does into multiple
|
|
||||||
python processes. In theory multiple synapse processes could share a single
|
|
||||||
postgresql database and we'd scale up by running more synapse processes.
|
|
||||||
However much of synapse assumes that only one process is interacting with the
|
|
||||||
database, both for assigning unique identifiers when inserting into tables,
|
|
||||||
notifying components about new updates, and for invalidating its caches.
|
|
||||||
|
|
||||||
So running multiple copies of the current code isn't an option. One way to
|
|
||||||
run multiple processes would be to have a single writer process and multiple
|
|
||||||
reader processes connected to the same database. In order to do this we'd need
|
|
||||||
a way for the reader process to invalidate its in-memory caches when an update
|
|
||||||
happens on the writer. One way to do this is for the writer to present an
|
|
||||||
append-only log of updates which the readers can consume to invalidate their
|
|
||||||
caches and to push updates to listening clients or pushers.
|
|
||||||
|
|
||||||
Synapse already stores much of its data as an append-only log so that it can
|
|
||||||
correctly respond to /sync requests so the amount of code changes needed to
|
|
||||||
expose the append-only log to the readers should be fairly minimal.
|
|
||||||
|
|
||||||
Architecture
|
|
||||||
------------
|
|
||||||
|
|
||||||
The Replication Protocol
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
See ``tcp_replication.rst``
|
|
||||||
|
|
||||||
|
|
||||||
The Slaved DataStore
|
|
||||||
~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
There are read-only version of the synapse storage layer in
|
|
||||||
``synapse/replication/slave/storage`` that use the response of the replication
|
|
||||||
API to invalidate their caches.
|
|
|
@ -0,0 +1,123 @@
|
||||||
|
# Using a reverse proxy with Synapse
|
||||||
|
|
||||||
|
It is recommended to put a reverse proxy such as
|
||||||
|
[nginx](https://nginx.org/en/docs/http/ngx_http_proxy_module.html),
|
||||||
|
[Apache](https://httpd.apache.org/docs/current/mod/mod_proxy_http.html),
|
||||||
|
[Caddy](https://caddyserver.com/docs/proxy) or
|
||||||
|
[HAProxy](https://www.haproxy.org/) in front of Synapse. One advantage
|
||||||
|
of doing so is that it means that you can expose the default https port
|
||||||
|
(443) to Matrix clients without needing to run Synapse with root
|
||||||
|
privileges.
|
||||||
|
|
||||||
|
> **NOTE**: Your reverse proxy must not `canonicalise` or `normalise`
|
||||||
|
the requested URI in any way (for example, by decoding `%xx` escapes).
|
||||||
|
Beware that Apache *will* canonicalise URIs unless you specifify
|
||||||
|
`nocanon`.
|
||||||
|
|
||||||
|
When setting up a reverse proxy, remember that Matrix clients and other
|
||||||
|
Matrix servers do not necessarily need to connect to your server via the
|
||||||
|
same server name or port. Indeed, clients will use port 443 by default,
|
||||||
|
whereas servers default to port 8448. Where these are different, we
|
||||||
|
refer to the 'client port' and the \'federation port\'. See [Setting
|
||||||
|
up federation](federate.md) for more details of the algorithm used for
|
||||||
|
federation connections.
|
||||||
|
|
||||||
|
Let's assume that we expect clients to connect to our server at
|
||||||
|
`https://matrix.example.com`, and other servers to connect at
|
||||||
|
`https://example.com:8448`. The following sections detail the configuration of
|
||||||
|
the reverse proxy and the homeserver.
|
||||||
|
|
||||||
|
## Webserver configuration examples
|
||||||
|
|
||||||
|
> **NOTE**: You only need one of these.
|
||||||
|
|
||||||
|
### nginx
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 443 ssl;
|
||||||
|
listen [::]:443 ssl;
|
||||||
|
server_name matrix.example.com;
|
||||||
|
|
||||||
|
location /_matrix {
|
||||||
|
proxy_pass http://localhost:8008;
|
||||||
|
proxy_set_header X-Forwarded-For $remote_addr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 8448 ssl default_server;
|
||||||
|
listen [::]:8448 ssl default_server;
|
||||||
|
server_name example.com;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://localhost:8008;
|
||||||
|
proxy_set_header X-Forwarded-For $remote_addr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> **NOTE**: Do not add a `/` after the port in `proxy_pass`, otherwise nginx will
|
||||||
|
canonicalise/normalise the URI.
|
||||||
|
|
||||||
|
### Caddy
|
||||||
|
|
||||||
|
matrix.example.com {
|
||||||
|
proxy /_matrix http://localhost:8008 {
|
||||||
|
transparent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
example.com:8448 {
|
||||||
|
proxy / http://localhost:8008 {
|
||||||
|
transparent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
### Apache
|
||||||
|
|
||||||
|
<VirtualHost *:443>
|
||||||
|
SSLEngine on
|
||||||
|
ServerName matrix.example.com;
|
||||||
|
|
||||||
|
AllowEncodedSlashes NoDecode
|
||||||
|
ProxyPass /_matrix http://127.0.0.1:8008/_matrix nocanon
|
||||||
|
ProxyPassReverse /_matrix http://127.0.0.1:8008/_matrix
|
||||||
|
</VirtualHost>
|
||||||
|
|
||||||
|
<VirtualHost *:8448>
|
||||||
|
SSLEngine on
|
||||||
|
ServerName example.com;
|
||||||
|
|
||||||
|
AllowEncodedSlashes NoDecode
|
||||||
|
ProxyPass /_matrix http://127.0.0.1:8008/_matrix nocanon
|
||||||
|
ProxyPassReverse /_matrix http://127.0.0.1:8008/_matrix
|
||||||
|
</VirtualHost>
|
||||||
|
|
||||||
|
> **NOTE**: ensure the `nocanon` options are included.
|
||||||
|
|
||||||
|
### HAProxy
|
||||||
|
|
||||||
|
frontend https
|
||||||
|
bind :::443 v4v6 ssl crt /etc/ssl/haproxy/ strict-sni alpn h2,http/1.1
|
||||||
|
|
||||||
|
# Matrix client traffic
|
||||||
|
acl matrix-host hdr(host) -i matrix.example.com
|
||||||
|
acl matrix-path path_beg /_matrix
|
||||||
|
|
||||||
|
use_backend matrix if matrix-host matrix-path
|
||||||
|
|
||||||
|
frontend matrix-federation
|
||||||
|
bind :::8448 v4v6 ssl crt /etc/ssl/haproxy/synapse.pem alpn h2,http/1.1
|
||||||
|
default_backend matrix
|
||||||
|
|
||||||
|
backend matrix
|
||||||
|
server matrix 127.0.0.1:8008
|
||||||
|
|
||||||
|
## Homeserver Configuration
|
||||||
|
|
||||||
|
You will also want to set `bind_addresses: ['127.0.0.1']` and
|
||||||
|
`x_forwarded: true` for port 8008 in `homeserver.yaml` to ensure that
|
||||||
|
client IP addresses are recorded correctly.
|
||||||
|
|
||||||
|
Having done so, you can then use `https://matrix.example.com` (instead
|
||||||
|
of `https://matrix.example.com:8448`) as the "Custom server" when
|
||||||
|
connecting to Synapse from a client.
|
|
@ -1,112 +0,0 @@
|
||||||
Using a reverse proxy with Synapse
|
|
||||||
==================================
|
|
||||||
|
|
||||||
It is recommended to put a reverse proxy such as
|
|
||||||
`nginx <https://nginx.org/en/docs/http/ngx_http_proxy_module.html>`_,
|
|
||||||
`Apache <https://httpd.apache.org/docs/current/mod/mod_proxy_http.html>`_,
|
|
||||||
`Caddy <https://caddyserver.com/docs/proxy>`_ or
|
|
||||||
`HAProxy <https://www.haproxy.org/>`_ in front of Synapse. One advantage of
|
|
||||||
doing so is that it means that you can expose the default https port (443) to
|
|
||||||
Matrix clients without needing to run Synapse with root privileges.
|
|
||||||
|
|
||||||
**NOTE**: Your reverse proxy must not 'canonicalise' or 'normalise' the
|
|
||||||
requested URI in any way (for example, by decoding ``%xx`` escapes). Beware
|
|
||||||
that Apache *will* canonicalise URIs unless you specifify ``nocanon``.
|
|
||||||
|
|
||||||
When setting up a reverse proxy, remember that Matrix clients and other Matrix
|
|
||||||
servers do not necessarily need to connect to your server via the same server
|
|
||||||
name or port. Indeed, clients will use port 443 by default, whereas servers
|
|
||||||
default to port 8448. Where these are different, we refer to the 'client port'
|
|
||||||
and the 'federation port'. See `Setting up federation
|
|
||||||
<federate.md>`_ for more details of the algorithm used for
|
|
||||||
federation connections.
|
|
||||||
|
|
||||||
Let's assume that we expect clients to connect to our server at
|
|
||||||
``https://matrix.example.com``, and other servers to connect at
|
|
||||||
``https://example.com:8448``. Here are some example configurations:
|
|
||||||
|
|
||||||
* nginx::
|
|
||||||
|
|
||||||
server {
|
|
||||||
listen 443 ssl;
|
|
||||||
listen [::]:443 ssl;
|
|
||||||
server_name matrix.example.com;
|
|
||||||
|
|
||||||
location /_matrix {
|
|
||||||
proxy_pass http://localhost:8008;
|
|
||||||
proxy_set_header X-Forwarded-For $remote_addr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
server {
|
|
||||||
listen 8448 ssl default_server;
|
|
||||||
listen [::]:8448 ssl default_server;
|
|
||||||
server_name example.com;
|
|
||||||
|
|
||||||
location / {
|
|
||||||
proxy_pass http://localhost:8008;
|
|
||||||
proxy_set_header X-Forwarded-For $remote_addr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Do not add a `/` after the port in `proxy_pass`, otherwise nginx will canonicalise/normalise the URI.
|
|
||||||
|
|
||||||
* Caddy::
|
|
||||||
|
|
||||||
matrix.example.com {
|
|
||||||
proxy /_matrix http://localhost:8008 {
|
|
||||||
transparent
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
example.com:8448 {
|
|
||||||
proxy / http://localhost:8008 {
|
|
||||||
transparent
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
* Apache (note the ``nocanon`` options here!)::
|
|
||||||
|
|
||||||
<VirtualHost *:443>
|
|
||||||
SSLEngine on
|
|
||||||
ServerName matrix.example.com;
|
|
||||||
|
|
||||||
AllowEncodedSlashes NoDecode
|
|
||||||
ProxyPass /_matrix http://127.0.0.1:8008/_matrix nocanon
|
|
||||||
ProxyPassReverse /_matrix http://127.0.0.1:8008/_matrix
|
|
||||||
</VirtualHost>
|
|
||||||
|
|
||||||
<VirtualHost *:8448>
|
|
||||||
SSLEngine on
|
|
||||||
ServerName example.com;
|
|
||||||
|
|
||||||
AllowEncodedSlashes NoDecode
|
|
||||||
ProxyPass /_matrix http://127.0.0.1:8008/_matrix nocanon
|
|
||||||
ProxyPassReverse /_matrix http://127.0.0.1:8008/_matrix
|
|
||||||
</VirtualHost>
|
|
||||||
|
|
||||||
* HAProxy::
|
|
||||||
|
|
||||||
frontend https
|
|
||||||
bind :::443 v4v6 ssl crt /etc/ssl/haproxy/ strict-sni alpn h2,http/1.1
|
|
||||||
|
|
||||||
# Matrix client traffic
|
|
||||||
acl matrix-host hdr(host) -i matrix.example.com
|
|
||||||
acl matrix-path path_beg /_matrix
|
|
||||||
|
|
||||||
use_backend matrix if matrix-host matrix-path
|
|
||||||
|
|
||||||
frontend matrix-federation
|
|
||||||
bind :::8448 v4v6 ssl crt /etc/ssl/haproxy/synapse.pem alpn h2,http/1.1
|
|
||||||
default_backend matrix
|
|
||||||
|
|
||||||
backend matrix
|
|
||||||
server matrix 127.0.0.1:8008
|
|
||||||
|
|
||||||
You will also want to set ``bind_addresses: ['127.0.0.1']`` and ``x_forwarded: true``
|
|
||||||
for port 8008 in ``homeserver.yaml`` to ensure that client IP addresses are
|
|
||||||
recorded correctly.
|
|
||||||
|
|
||||||
Having done so, you can then use ``https://matrix.example.com`` (instead of
|
|
||||||
``https://matrix.example.com:8448``) as the "Custom server" when connecting to
|
|
||||||
Synapse from a client.
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
Room and User Statistics
|
||||||
|
========================
|
||||||
|
|
||||||
|
Synapse maintains room and user statistics (as well as a cache of room state),
|
||||||
|
in various tables. These can be used for administrative purposes but are also
|
||||||
|
used when generating the public room directory.
|
||||||
|
|
||||||
|
|
||||||
|
# Synapse Developer Documentation
|
||||||
|
|
||||||
|
## High-Level Concepts
|
||||||
|
|
||||||
|
### Definitions
|
||||||
|
|
||||||
|
* **subject**: Something we are tracking stats about – currently a room or user.
|
||||||
|
* **current row**: An entry for a subject in the appropriate current statistics
|
||||||
|
table. Each subject can have only one.
|
||||||
|
* **historical row**: An entry for a subject in the appropriate historical
|
||||||
|
statistics table. Each subject can have any number of these.
|
||||||
|
|
||||||
|
### Overview
|
||||||
|
|
||||||
|
Stats are maintained as time series. There are two kinds of column:
|
||||||
|
|
||||||
|
* absolute columns – where the value is correct for the time given by `end_ts`
|
||||||
|
in the stats row. (Imagine a line graph for these values)
|
||||||
|
* They can also be thought of as 'gauges' in Prometheus, if you are familiar.
|
||||||
|
* per-slice columns – where the value corresponds to how many of the occurrences
|
||||||
|
occurred within the time slice given by `(end_ts − bucket_size)…end_ts`
|
||||||
|
or `start_ts…end_ts`. (Imagine a histogram for these values)
|
||||||
|
|
||||||
|
Stats are maintained in two tables (for each type): current and historical.
|
||||||
|
|
||||||
|
Current stats correspond to the present values. Each subject can only have one
|
||||||
|
entry.
|
||||||
|
|
||||||
|
Historical stats correspond to values in the past. Subjects may have multiple
|
||||||
|
entries.
|
||||||
|
|
||||||
|
## Concepts around the management of stats
|
||||||
|
|
||||||
|
### Current rows
|
||||||
|
|
||||||
|
Current rows contain the most up-to-date statistics for a room.
|
||||||
|
They only contain absolute columns
|
||||||
|
|
||||||
|
### Historical rows
|
||||||
|
|
||||||
|
Historical rows can always be considered to be valid for the time slice and
|
||||||
|
end time specified.
|
||||||
|
|
||||||
|
* historical rows will not exist for every time slice – they will be omitted
|
||||||
|
if there were no changes. In this case, the following assumptions can be
|
||||||
|
made to interpolate/recreate missing rows:
|
||||||
|
- absolute fields have the same values as in the preceding row
|
||||||
|
- per-slice fields are zero (`0`)
|
||||||
|
* historical rows will not be retained forever – rows older than a configurable
|
||||||
|
time will be purged.
|
||||||
|
|
||||||
|
#### Purge
|
||||||
|
|
||||||
|
The purging of historical rows is not yet implemented.
|
|
@ -110,6 +110,9 @@ pid_file: DATADIR/homeserver.pid
|
||||||
# blacklist IP address CIDR ranges. If this option is not specified, or
|
# blacklist IP address CIDR ranges. If this option is not specified, or
|
||||||
# specified with an empty list, no ip range blacklist will be enforced.
|
# specified with an empty list, no ip range blacklist will be enforced.
|
||||||
#
|
#
|
||||||
|
# As of Synapse v1.4.0 this option also affects any outbound requests to identity
|
||||||
|
# servers provided by user input.
|
||||||
|
#
|
||||||
# (0.0.0.0 and :: are always blacklisted, whether or not they are explicitly
|
# (0.0.0.0 and :: are always blacklisted, whether or not they are explicitly
|
||||||
# listed here, since they correspond to unroutable addresses.)
|
# listed here, since they correspond to unroutable addresses.)
|
||||||
#
|
#
|
||||||
|
@ -136,8 +139,8 @@ federation_ip_range_blacklist:
|
||||||
#
|
#
|
||||||
# type: the type of listener. Normally 'http', but other valid options are:
|
# type: the type of listener. Normally 'http', but other valid options are:
|
||||||
# 'manhole' (see docs/manhole.md),
|
# 'manhole' (see docs/manhole.md),
|
||||||
# 'metrics' (see docs/metrics-howto.rst),
|
# 'metrics' (see docs/metrics-howto.md),
|
||||||
# 'replication' (see docs/workers.rst).
|
# 'replication' (see docs/workers.md).
|
||||||
#
|
#
|
||||||
# tls: set to true to enable TLS for this listener. Will use the TLS
|
# tls: set to true to enable TLS for this listener. Will use the TLS
|
||||||
# key/cert specified in tls_private_key_path / tls_certificate_path.
|
# key/cert specified in tls_private_key_path / tls_certificate_path.
|
||||||
|
@ -172,12 +175,12 @@ federation_ip_range_blacklist:
|
||||||
#
|
#
|
||||||
# media: the media API (/_matrix/media).
|
# media: the media API (/_matrix/media).
|
||||||
#
|
#
|
||||||
# metrics: the metrics interface. See docs/metrics-howto.rst.
|
# metrics: the metrics interface. See docs/metrics-howto.md.
|
||||||
#
|
#
|
||||||
# openid: OpenID authentication.
|
# openid: OpenID authentication.
|
||||||
#
|
#
|
||||||
# replication: the HTTP replication API (/_synapse/replication). See
|
# replication: the HTTP replication API (/_synapse/replication). See
|
||||||
# docs/workers.rst.
|
# docs/workers.md.
|
||||||
#
|
#
|
||||||
# static: static resources under synapse/static (/_matrix/static). (Mostly
|
# static: static resources under synapse/static (/_matrix/static). (Mostly
|
||||||
# useful for 'fallback authentication'.)
|
# useful for 'fallback authentication'.)
|
||||||
|
@ -201,13 +204,13 @@ listeners:
|
||||||
# that unwraps TLS.
|
# that unwraps TLS.
|
||||||
#
|
#
|
||||||
# If you plan to use a reverse proxy, please see
|
# If you plan to use a reverse proxy, please see
|
||||||
# https://github.com/matrix-org/synapse/blob/master/docs/reverse_proxy.rst.
|
# https://github.com/matrix-org/synapse/blob/master/docs/reverse_proxy.md.
|
||||||
#
|
#
|
||||||
- port: 8008
|
- port: 8008
|
||||||
tls: false
|
tls: false
|
||||||
bind_addresses: ['::1', '127.0.0.1']
|
|
||||||
type: http
|
type: http
|
||||||
x_forwarded: true
|
x_forwarded: true
|
||||||
|
bind_addresses: ['::1', '127.0.0.1']
|
||||||
|
|
||||||
resources:
|
resources:
|
||||||
- names: [client, federation]
|
- names: [client, federation]
|
||||||
|
@ -306,6 +309,19 @@ listeners:
|
||||||
#
|
#
|
||||||
#allow_per_room_profiles: false
|
#allow_per_room_profiles: false
|
||||||
|
|
||||||
|
# How long to keep redacted events in unredacted form in the database. After
|
||||||
|
# this period redacted events get replaced with their redacted form in the DB.
|
||||||
|
#
|
||||||
|
# Defaults to `7d`. Set to `null` to disable.
|
||||||
|
#
|
||||||
|
#redaction_retention_period: 28d
|
||||||
|
|
||||||
|
# How long to track users' last seen time and IPs in the database.
|
||||||
|
#
|
||||||
|
# Defaults to `28d`. Set to `null` to disable clearing out of old rows.
|
||||||
|
#
|
||||||
|
#user_ips_max_age: 14d
|
||||||
|
|
||||||
|
|
||||||
## TLS ##
|
## TLS ##
|
||||||
|
|
||||||
|
@ -392,10 +408,10 @@ listeners:
|
||||||
# permission to listen on port 80.
|
# permission to listen on port 80.
|
||||||
#
|
#
|
||||||
acme:
|
acme:
|
||||||
# ACME support is disabled by default. Uncomment the following line
|
# ACME support is disabled by default. Set this to `true` and uncomment
|
||||||
# (and tls_certificate_path and tls_private_key_path above) to enable it.
|
# tls_certificate_path and tls_private_key_path above to enable it.
|
||||||
#
|
#
|
||||||
#enabled: true
|
enabled: False
|
||||||
|
|
||||||
# Endpoint to use to request certificates. If you only want to test,
|
# Endpoint to use to request certificates. If you only want to test,
|
||||||
# use Let's Encrypt's staging url:
|
# use Let's Encrypt's staging url:
|
||||||
|
@ -406,17 +422,17 @@ acme:
|
||||||
# Port number to listen on for the HTTP-01 challenge. Change this if
|
# Port number to listen on for the HTTP-01 challenge. Change this if
|
||||||
# you are forwarding connections through Apache/Nginx/etc.
|
# you are forwarding connections through Apache/Nginx/etc.
|
||||||
#
|
#
|
||||||
#port: 80
|
port: 80
|
||||||
|
|
||||||
# Local addresses to listen on for incoming connections.
|
# Local addresses to listen on for incoming connections.
|
||||||
# Again, you may want to change this if you are forwarding connections
|
# Again, you may want to change this if you are forwarding connections
|
||||||
# through Apache/Nginx/etc.
|
# through Apache/Nginx/etc.
|
||||||
#
|
#
|
||||||
#bind_addresses: ['::', '0.0.0.0']
|
bind_addresses: ['::', '0.0.0.0']
|
||||||
|
|
||||||
# How many days remaining on a certificate before it is renewed.
|
# How many days remaining on a certificate before it is renewed.
|
||||||
#
|
#
|
||||||
#reprovision_threshold: 30
|
reprovision_threshold: 30
|
||||||
|
|
||||||
# The domain that the certificate should be for. Normally this
|
# The domain that the certificate should be for. Normally this
|
||||||
# should be the same as your Matrix domain (i.e., 'server_name'), but,
|
# should be the same as your Matrix domain (i.e., 'server_name'), but,
|
||||||
|
@ -430,7 +446,7 @@ acme:
|
||||||
#
|
#
|
||||||
# If not set, defaults to your 'server_name'.
|
# If not set, defaults to your 'server_name'.
|
||||||
#
|
#
|
||||||
#domain: matrix.example.com
|
domain: matrix.example.com
|
||||||
|
|
||||||
# file to use for the account key. This will be generated if it doesn't
|
# file to use for the account key. This will be generated if it doesn't
|
||||||
# exist.
|
# exist.
|
||||||
|
@ -485,7 +501,8 @@ database:
|
||||||
|
|
||||||
## Logging ##
|
## Logging ##
|
||||||
|
|
||||||
# A yaml python logging config file
|
# A yaml python logging config file as described by
|
||||||
|
# https://docs.python.org/3.7/library/logging.config.html#configuration-dictionary-schema
|
||||||
#
|
#
|
||||||
log_config: "CONFDIR/SERVERNAME.log.config"
|
log_config: "CONFDIR/SERVERNAME.log.config"
|
||||||
|
|
||||||
|
@ -510,6 +527,9 @@ log_config: "CONFDIR/SERVERNAME.log.config"
|
||||||
# - one for login that ratelimits login requests based on the account the
|
# - one for login that ratelimits login requests based on the account the
|
||||||
# client is attempting to log into, based on the amount of failed login
|
# client is attempting to log into, based on the amount of failed login
|
||||||
# attempts for this account.
|
# attempts for this account.
|
||||||
|
# - one for ratelimiting redactions by room admins. If this is not explicitly
|
||||||
|
# set then it uses the same ratelimiting as per rc_message. This is useful
|
||||||
|
# to allow room admins to deal with abuse quickly.
|
||||||
#
|
#
|
||||||
# The defaults are as shown below.
|
# The defaults are as shown below.
|
||||||
#
|
#
|
||||||
|
@ -531,6 +551,10 @@ log_config: "CONFDIR/SERVERNAME.log.config"
|
||||||
# failed_attempts:
|
# failed_attempts:
|
||||||
# per_second: 0.17
|
# per_second: 0.17
|
||||||
# burst_count: 3
|
# burst_count: 3
|
||||||
|
#
|
||||||
|
#rc_admin_redaction:
|
||||||
|
# per_second: 1
|
||||||
|
# burst_count: 50
|
||||||
|
|
||||||
|
|
||||||
# Ratelimiting settings for incoming federation
|
# Ratelimiting settings for incoming federation
|
||||||
|
@ -890,10 +914,44 @@ uploads_path: "DATADIR/uploads"
|
||||||
# Also defines the ID server which will be called when an account is
|
# Also defines the ID server which will be called when an account is
|
||||||
# deactivated (one will be picked arbitrarily).
|
# deactivated (one will be picked arbitrarily).
|
||||||
#
|
#
|
||||||
|
# Note: This option is deprecated. Since v0.99.4, Synapse has tracked which identity
|
||||||
|
# server a 3PID has been bound to. For 3PIDs bound before then, Synapse runs a
|
||||||
|
# background migration script, informing itself that the identity server all of its
|
||||||
|
# 3PIDs have been bound to is likely one of the below.
|
||||||
|
#
|
||||||
|
# As of Synapse v1.4.0, all other functionality of this option has been deprecated, and
|
||||||
|
# it is now solely used for the purposes of the background migration script, and can be
|
||||||
|
# removed once it has run.
|
||||||
#trusted_third_party_id_servers:
|
#trusted_third_party_id_servers:
|
||||||
# - matrix.org
|
# - matrix.org
|
||||||
# - vector.im
|
# - vector.im
|
||||||
|
|
||||||
|
# Handle threepid (email/phone etc) registration and password resets through a set of
|
||||||
|
# *trusted* identity servers. Note that this allows the configured identity server to
|
||||||
|
# reset passwords for accounts!
|
||||||
|
#
|
||||||
|
# Be aware that if `email` is not set, and SMTP options have not been
|
||||||
|
# configured in the email config block, registration and user password resets via
|
||||||
|
# email will be globally disabled.
|
||||||
|
#
|
||||||
|
# Additionally, if `msisdn` is not set, registration and password resets via msisdn
|
||||||
|
# will be disabled regardless. This is due to Synapse currently not supporting any
|
||||||
|
# method of sending SMS messages on its own.
|
||||||
|
#
|
||||||
|
# To enable using an identity server for operations regarding a particular third-party
|
||||||
|
# identifier type, set the value to the URL of that identity server as shown in the
|
||||||
|
# examples below.
|
||||||
|
#
|
||||||
|
# Servers handling the these requests must answer the `/requestToken` endpoints defined
|
||||||
|
# by the Matrix Identity Service API specification:
|
||||||
|
# https://matrix.org/docs/spec/identity_service/latest
|
||||||
|
#
|
||||||
|
# If a delegate is specified, the config option public_baseurl must also be filled out.
|
||||||
|
#
|
||||||
|
account_threepid_delegates:
|
||||||
|
#email: https://example.com # Delegate email sending to example.org
|
||||||
|
#msisdn: http://localhost:8090 # Delegate SMS sending to this local process
|
||||||
|
|
||||||
# Users who register on this homeserver will automatically be joined
|
# Users who register on this homeserver will automatically be joined
|
||||||
# to these rooms
|
# to these rooms
|
||||||
#
|
#
|
||||||
|
@ -925,9 +983,24 @@ uploads_path: "DATADIR/uploads"
|
||||||
#sentry:
|
#sentry:
|
||||||
# dsn: "..."
|
# dsn: "..."
|
||||||
|
|
||||||
|
# Flags to enable Prometheus metrics which are not suitable to be
|
||||||
|
# enabled by default, either for performance reasons or limited use.
|
||||||
|
#
|
||||||
|
metrics_flags:
|
||||||
|
# Publish synapse_federation_known_servers, a g auge of the number of
|
||||||
|
# servers this homeserver knows about, including itself. May cause
|
||||||
|
# performance problems on large homeservers.
|
||||||
|
#
|
||||||
|
#known_servers: true
|
||||||
|
|
||||||
# Whether or not to report anonymized homeserver usage statistics.
|
# Whether or not to report anonymized homeserver usage statistics.
|
||||||
# report_stats: true|false
|
# report_stats: true|false
|
||||||
|
|
||||||
|
# The endpoint to report the anonymized homeserver usage statistics to.
|
||||||
|
# Defaults to https://matrix.org/report-usage-stats/push
|
||||||
|
#
|
||||||
|
#report_stats_endpoint: https://example.com/report-usage-stats/push
|
||||||
|
|
||||||
|
|
||||||
## API Configuration ##
|
## API Configuration ##
|
||||||
|
|
||||||
|
@ -999,6 +1072,10 @@ signing_key_path: "CONFDIR/SERVERNAME.signing.key"
|
||||||
# This setting supercedes an older setting named `perspectives`. The old format
|
# This setting supercedes an older setting named `perspectives`. The old format
|
||||||
# is still supported for backwards-compatibility, but it is deprecated.
|
# is still supported for backwards-compatibility, but it is deprecated.
|
||||||
#
|
#
|
||||||
|
# 'trusted_key_servers' defaults to matrix.org, but using it will generate a
|
||||||
|
# warning on start-up. To suppress this warning, set
|
||||||
|
# 'suppress_key_server_warning' to true.
|
||||||
|
#
|
||||||
# Options for each entry in the list include:
|
# Options for each entry in the list include:
|
||||||
#
|
#
|
||||||
# server_name: the name of the server. required.
|
# server_name: the name of the server. required.
|
||||||
|
@ -1023,20 +1100,31 @@ signing_key_path: "CONFDIR/SERVERNAME.signing.key"
|
||||||
# "ed25519:auto": "abcdefghijklmnopqrstuvwxyzabcdefghijklmopqr"
|
# "ed25519:auto": "abcdefghijklmnopqrstuvwxyzabcdefghijklmopqr"
|
||||||
# - server_name: "my_other_trusted_server.example.com"
|
# - server_name: "my_other_trusted_server.example.com"
|
||||||
#
|
#
|
||||||
# The default configuration is:
|
trusted_key_servers:
|
||||||
|
- server_name: "matrix.org"
|
||||||
|
|
||||||
|
# Uncomment the following to disable the warning that is emitted when the
|
||||||
|
# trusted_key_servers include 'matrix.org'. See above.
|
||||||
#
|
#
|
||||||
#trusted_key_servers:
|
#suppress_key_server_warning: true
|
||||||
# - server_name: "matrix.org"
|
|
||||||
|
# The signing keys to use when acting as a trusted key server. If not specified
|
||||||
|
# defaults to the server signing key.
|
||||||
|
#
|
||||||
|
# Can contain multiple keys, one per line.
|
||||||
|
#
|
||||||
|
#key_server_signing_keys_path: "key_server_signing_keys.key"
|
||||||
|
|
||||||
|
|
||||||
# Enable SAML2 for registration and login. Uses pysaml2.
|
# Enable SAML2 for registration and login. Uses pysaml2.
|
||||||
#
|
#
|
||||||
# `sp_config` is the configuration for the pysaml2 Service Provider.
|
# At least one of `sp_config` or `config_path` must be set in this section to
|
||||||
# See pysaml2 docs for format of config.
|
# enable SAML login.
|
||||||
#
|
#
|
||||||
# Default values will be used for the 'entityid' and 'service' settings,
|
# (You will probably also want to set the following options to `false` to
|
||||||
# so it is not normally necessary to specify them unless you need to
|
# disable the regular login/registration flows:
|
||||||
# override them.
|
# * enable_registration
|
||||||
|
# * password_config.enabled
|
||||||
#
|
#
|
||||||
# Once SAML support is enabled, a metadata file will be exposed at
|
# Once SAML support is enabled, a metadata file will be exposed at
|
||||||
# https://<server>:<port>/_matrix/saml2/metadata.xml, which you may be able to
|
# https://<server>:<port>/_matrix/saml2/metadata.xml, which you may be able to
|
||||||
|
@ -1044,52 +1132,85 @@ signing_key_path: "CONFDIR/SERVERNAME.signing.key"
|
||||||
# the IdP to use an ACS location of
|
# the IdP to use an ACS location of
|
||||||
# https://<server>:<port>/_matrix/saml2/authn_response.
|
# https://<server>:<port>/_matrix/saml2/authn_response.
|
||||||
#
|
#
|
||||||
#saml2_config:
|
saml2_config:
|
||||||
# sp_config:
|
# `sp_config` is the configuration for the pysaml2 Service Provider.
|
||||||
# # point this to the IdP's metadata. You can use either a local file or
|
# See pysaml2 docs for format of config.
|
||||||
# # (preferably) a URL.
|
#
|
||||||
# metadata:
|
# Default values will be used for the 'entityid' and 'service' settings,
|
||||||
# #local: ["saml2/idp.xml"]
|
# so it is not normally necessary to specify them unless you need to
|
||||||
# remote:
|
# override them.
|
||||||
# - url: https://our_idp/metadata.xml
|
#
|
||||||
#
|
#sp_config:
|
||||||
# # By default, the user has to go to our login page first. If you'd like to
|
# # point this to the IdP's metadata. You can use either a local file or
|
||||||
# # allow IdP-initiated login, set 'allow_unsolicited: True' in a
|
# # (preferably) a URL.
|
||||||
# # 'service.sp' section:
|
# metadata:
|
||||||
# #
|
# #local: ["saml2/idp.xml"]
|
||||||
# #service:
|
# remote:
|
||||||
# # sp:
|
# - url: https://our_idp/metadata.xml
|
||||||
# # allow_unsolicited: True
|
#
|
||||||
#
|
# # By default, the user has to go to our login page first. If you'd like
|
||||||
# # The examples below are just used to generate our metadata xml, and you
|
# # to allow IdP-initiated login, set 'allow_unsolicited: True' in a
|
||||||
# # may well not need it, depending on your setup. Alternatively you
|
# # 'service.sp' section:
|
||||||
# # may need a whole lot more detail - see the pysaml2 docs!
|
# #
|
||||||
#
|
# #service:
|
||||||
# description: ["My awesome SP", "en"]
|
# # sp:
|
||||||
# name: ["Test SP", "en"]
|
# # allow_unsolicited: true
|
||||||
#
|
#
|
||||||
# organization:
|
# # The examples below are just used to generate our metadata xml, and you
|
||||||
# name: Example com
|
# # may well not need them, depending on your setup. Alternatively you
|
||||||
# display_name:
|
# # may need a whole lot more detail - see the pysaml2 docs!
|
||||||
# - ["Example co", "en"]
|
#
|
||||||
# url: "http://example.com"
|
# description: ["My awesome SP", "en"]
|
||||||
#
|
# name: ["Test SP", "en"]
|
||||||
# contact_person:
|
#
|
||||||
# - given_name: Bob
|
# organization:
|
||||||
# sur_name: "the Sysadmin"
|
# name: Example com
|
||||||
# email_address": ["admin@example.com"]
|
# display_name:
|
||||||
# contact_type": technical
|
# - ["Example co", "en"]
|
||||||
#
|
# url: "http://example.com"
|
||||||
# # Instead of putting the config inline as above, you can specify a
|
#
|
||||||
# # separate pysaml2 configuration file:
|
# contact_person:
|
||||||
# #
|
# - given_name: Bob
|
||||||
# config_path: "CONFDIR/sp_conf.py"
|
# sur_name: "the Sysadmin"
|
||||||
#
|
# email_address": ["admin@example.com"]
|
||||||
# # the lifetime of a SAML session. This defines how long a user has to
|
# contact_type": technical
|
||||||
# # complete the authentication process, if allow_unsolicited is unset.
|
|
||||||
# # The default is 5 minutes.
|
# Instead of putting the config inline as above, you can specify a
|
||||||
# #
|
# separate pysaml2 configuration file:
|
||||||
# # saml_session_lifetime: 5m
|
#
|
||||||
|
#config_path: "CONFDIR/sp_conf.py"
|
||||||
|
|
||||||
|
# the lifetime of a SAML session. This defines how long a user has to
|
||||||
|
# complete the authentication process, if allow_unsolicited is unset.
|
||||||
|
# The default is 5 minutes.
|
||||||
|
#
|
||||||
|
#saml_session_lifetime: 5m
|
||||||
|
|
||||||
|
# The SAML attribute (after mapping via the attribute maps) to use to derive
|
||||||
|
# the Matrix ID from. 'uid' by default.
|
||||||
|
#
|
||||||
|
#mxid_source_attribute: displayName
|
||||||
|
|
||||||
|
# The mapping system to use for mapping the saml attribute onto a matrix ID.
|
||||||
|
# Options include:
|
||||||
|
# * 'hexencode' (which maps unpermitted characters to '=xx')
|
||||||
|
# * 'dotreplace' (which replaces unpermitted characters with '.').
|
||||||
|
# The default is 'hexencode'.
|
||||||
|
#
|
||||||
|
#mxid_mapping: dotreplace
|
||||||
|
|
||||||
|
# In previous versions of synapse, the mapping from SAML attribute to MXID was
|
||||||
|
# always calculated dynamically rather than stored in a table. For backwards-
|
||||||
|
# compatibility, we will look for user_ids matching such a pattern before
|
||||||
|
# creating a new account.
|
||||||
|
#
|
||||||
|
# This setting controls the SAML attribute which will be used for this
|
||||||
|
# backwards-compatibility lookup. Typically it should be 'uid', but if the
|
||||||
|
# attribute maps are changed, it may be necessary to change it.
|
||||||
|
#
|
||||||
|
# The default is 'uid'.
|
||||||
|
#
|
||||||
|
#grandfathered_mxid_source_attribute: upn
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -1155,19 +1276,6 @@ password_config:
|
||||||
# #
|
# #
|
||||||
# riot_base_url: "http://localhost/riot"
|
# riot_base_url: "http://localhost/riot"
|
||||||
#
|
#
|
||||||
# # Enable sending password reset emails via the configured, trusted
|
|
||||||
# # identity servers
|
|
||||||
# #
|
|
||||||
# # IMPORTANT! This will give a malicious or overtaken identity server
|
|
||||||
# # the ability to reset passwords for your users! Make absolutely sure
|
|
||||||
# # that you want to do this! It is strongly recommended that password
|
|
||||||
# # reset emails be sent by the homeserver instead
|
|
||||||
# #
|
|
||||||
# # If this option is set to false and SMTP options have not been
|
|
||||||
# # configured, resetting user passwords via email will be disabled
|
|
||||||
# #
|
|
||||||
# #trust_identity_server_for_password_resets: false
|
|
||||||
#
|
|
||||||
# # Configure the time that a validation email or text message code
|
# # Configure the time that a validation email or text message code
|
||||||
# # will expire after sending
|
# # will expire after sending
|
||||||
# #
|
# #
|
||||||
|
@ -1199,11 +1307,34 @@ password_config:
|
||||||
# #password_reset_template_html: password_reset.html
|
# #password_reset_template_html: password_reset.html
|
||||||
# #password_reset_template_text: password_reset.txt
|
# #password_reset_template_text: password_reset.txt
|
||||||
#
|
#
|
||||||
|
# # Templates for registration emails sent by the homeserver
|
||||||
|
# #
|
||||||
|
# #registration_template_html: registration.html
|
||||||
|
# #registration_template_text: registration.txt
|
||||||
|
#
|
||||||
|
# # Templates for validation emails sent by the homeserver when adding an email to
|
||||||
|
# # your user account
|
||||||
|
# #
|
||||||
|
# #add_threepid_template_html: add_threepid.html
|
||||||
|
# #add_threepid_template_text: add_threepid.txt
|
||||||
|
#
|
||||||
# # Templates for password reset success and failure pages that a user
|
# # Templates for password reset success and failure pages that a user
|
||||||
# # will see after attempting to reset their password
|
# # will see after attempting to reset their password
|
||||||
# #
|
# #
|
||||||
# #password_reset_template_success_html: password_reset_success.html
|
# #password_reset_template_success_html: password_reset_success.html
|
||||||
# #password_reset_template_failure_html: password_reset_failure.html
|
# #password_reset_template_failure_html: password_reset_failure.html
|
||||||
|
#
|
||||||
|
# # Templates for registration success and failure pages that a user
|
||||||
|
# # will see after attempting to register using an email or phone
|
||||||
|
# #
|
||||||
|
# #registration_template_success_html: registration_success.html
|
||||||
|
# #registration_template_failure_html: registration_failure.html
|
||||||
|
#
|
||||||
|
# # Templates for success and failure pages that a user will see after attempting
|
||||||
|
# # to add an email or phone to their account
|
||||||
|
# #
|
||||||
|
# #add_threepid_success_html: add_threepid_success.html
|
||||||
|
# #add_threepid_failure_html: add_threepid_failure.html
|
||||||
|
|
||||||
|
|
||||||
#password_providers:
|
#password_providers:
|
||||||
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
# Structured Logging
|
||||||
|
|
||||||
|
A structured logging system can be useful when your logs are destined for a machine to parse and process. By maintaining its machine-readable characteristics, it enables more efficient searching and aggregations when consumed by software such as the "ELK stack".
|
||||||
|
|
||||||
|
Synapse's structured logging system is configured via the file that Synapse's `log_config` config option points to. The file must be YAML and contain `structured: true`. It must contain a list of "drains" (places where logs go to).
|
||||||
|
|
||||||
|
A structured logging configuration looks similar to the following:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
structured: true
|
||||||
|
|
||||||
|
loggers:
|
||||||
|
synapse:
|
||||||
|
level: INFO
|
||||||
|
synapse.storage.SQL:
|
||||||
|
level: WARNING
|
||||||
|
|
||||||
|
drains:
|
||||||
|
console:
|
||||||
|
type: console
|
||||||
|
location: stdout
|
||||||
|
file:
|
||||||
|
type: file_json
|
||||||
|
location: homeserver.log
|
||||||
|
```
|
||||||
|
|
||||||
|
The above logging config will set Synapse as 'INFO' logging level by default, with the SQL layer at 'WARNING', and will have two logging drains (to the console and to a file, stored as JSON).
|
||||||
|
|
||||||
|
## Drain Types
|
||||||
|
|
||||||
|
Drain types can be specified by the `type` key.
|
||||||
|
|
||||||
|
### `console`
|
||||||
|
|
||||||
|
Outputs human-readable logs to the console.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
|
||||||
|
- `location`: Either `stdout` or `stderr`.
|
||||||
|
|
||||||
|
### `console_json`
|
||||||
|
|
||||||
|
Outputs machine-readable JSON logs to the console.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
|
||||||
|
- `location`: Either `stdout` or `stderr`.
|
||||||
|
|
||||||
|
### `console_json_terse`
|
||||||
|
|
||||||
|
Outputs machine-readable JSON logs to the console, separated by newlines. This
|
||||||
|
format is not designed to be read and re-formatted into human-readable text, but
|
||||||
|
is optimal for a logging aggregation system.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
|
||||||
|
- `location`: Either `stdout` or `stderr`.
|
||||||
|
|
||||||
|
### `file`
|
||||||
|
|
||||||
|
Outputs human-readable logs to a file.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
|
||||||
|
- `location`: An absolute path to the file to log to.
|
||||||
|
|
||||||
|
### `file_json`
|
||||||
|
|
||||||
|
Outputs machine-readable logs to a file.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
|
||||||
|
- `location`: An absolute path to the file to log to.
|
||||||
|
|
||||||
|
### `network_json_terse`
|
||||||
|
|
||||||
|
Delivers machine-readable JSON logs to a log aggregator over TCP. This is
|
||||||
|
compatible with LogStash's TCP input with the codec set to `json_lines`.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
|
||||||
|
- `host`: Hostname or IP address of the log aggregator.
|
||||||
|
- `port`: Numerical port to contact on the host.
|
|
@ -0,0 +1,249 @@
|
||||||
|
# TCP Replication
|
||||||
|
|
||||||
|
## Motivation
|
||||||
|
|
||||||
|
Previously the workers used an HTTP long poll mechanism to get updates
|
||||||
|
from the master, which had the problem of causing a lot of duplicate
|
||||||
|
work on the server. This TCP protocol replaces those APIs with the aim
|
||||||
|
of increased efficiency.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The protocol is based on fire and forget, line based commands. An
|
||||||
|
example flow would be (where '>' indicates master to worker and
|
||||||
|
'<' worker to master flows):
|
||||||
|
|
||||||
|
> SERVER example.com
|
||||||
|
< REPLICATE events 53
|
||||||
|
> RDATA events 54 ["$foo1:bar.com", ...]
|
||||||
|
> RDATA events 55 ["$foo4:bar.com", ...]
|
||||||
|
|
||||||
|
The example shows the server accepting a new connection and sending its
|
||||||
|
identity with the `SERVER` command, followed by the client asking to
|
||||||
|
subscribe to the `events` stream from the token `53`. The server then
|
||||||
|
periodically sends `RDATA` commands which have the format
|
||||||
|
`RDATA <stream_name> <token> <row>`, where the format of `<row>` is
|
||||||
|
defined by the individual streams.
|
||||||
|
|
||||||
|
Error reporting happens by either the client or server sending an ERROR
|
||||||
|
command, and usually the connection will be closed.
|
||||||
|
|
||||||
|
Since the protocol is a simple line based, its possible to manually
|
||||||
|
connect to the server using a tool like netcat. A few things should be
|
||||||
|
noted when manually using the protocol:
|
||||||
|
|
||||||
|
- When subscribing to a stream using `REPLICATE`, the special token
|
||||||
|
`NOW` can be used to get all future updates. The special stream name
|
||||||
|
`ALL` can be used with `NOW` to subscribe to all available streams.
|
||||||
|
- The federation stream is only available if federation sending has
|
||||||
|
been disabled on the main process.
|
||||||
|
- The server will only time connections out that have sent a `PING`
|
||||||
|
command. If a ping is sent then the connection will be closed if no
|
||||||
|
further commands are receieved within 15s. Both the client and
|
||||||
|
server protocol implementations will send an initial PING on
|
||||||
|
connection and ensure at least one command every 5s is sent (not
|
||||||
|
necessarily `PING`).
|
||||||
|
- `RDATA` commands *usually* include a numeric token, however if the
|
||||||
|
stream has multiple rows to replicate per token the server will send
|
||||||
|
multiple `RDATA` commands, with all but the last having a token of
|
||||||
|
`batch`. See the documentation on `commands.RdataCommand` for
|
||||||
|
further details.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
The basic structure of the protocol is line based, where the initial
|
||||||
|
word of each line specifies the command. The rest of the line is parsed
|
||||||
|
based on the command. For example, the RDATA command is defined as:
|
||||||
|
|
||||||
|
RDATA <stream_name> <token> <row_json>
|
||||||
|
|
||||||
|
(Note that <row_json> may contains spaces, but cannot contain
|
||||||
|
newlines.)
|
||||||
|
|
||||||
|
Blank lines are ignored.
|
||||||
|
|
||||||
|
### Keep alives
|
||||||
|
|
||||||
|
Both sides are expected to send at least one command every 5s or so, and
|
||||||
|
should send a `PING` command if necessary. If either side do not receive
|
||||||
|
a command within e.g. 15s then the connection should be closed.
|
||||||
|
|
||||||
|
Because the server may be connected to manually using e.g. netcat, the
|
||||||
|
timeouts aren't enabled until an initial `PING` command is seen. Both
|
||||||
|
the client and server implementations below send a `PING` command
|
||||||
|
immediately on connection to ensure the timeouts are enabled.
|
||||||
|
|
||||||
|
This ensures that both sides can quickly realize if the tcp connection
|
||||||
|
has gone and handle the situation appropriately.
|
||||||
|
|
||||||
|
### Start up
|
||||||
|
|
||||||
|
When a new connection is made, the server:
|
||||||
|
|
||||||
|
- Sends a `SERVER` command, which includes the identity of the server,
|
||||||
|
allowing the client to detect if its connected to the expected
|
||||||
|
server
|
||||||
|
- Sends a `PING` command as above, to enable the client to time out
|
||||||
|
connections promptly.
|
||||||
|
|
||||||
|
The client:
|
||||||
|
|
||||||
|
- Sends a `NAME` command, allowing the server to associate a human
|
||||||
|
friendly name with the connection. This is optional.
|
||||||
|
- Sends a `PING` as above
|
||||||
|
- For each stream the client wishes to subscribe to it sends a
|
||||||
|
`REPLICATE` with the `stream_name` and token it wants to subscribe
|
||||||
|
from.
|
||||||
|
- On receipt of a `SERVER` command, checks that the server name
|
||||||
|
matches the expected server name.
|
||||||
|
|
||||||
|
### Error handling
|
||||||
|
|
||||||
|
If either side detects an error it can send an `ERROR` command and close
|
||||||
|
the connection.
|
||||||
|
|
||||||
|
If the client side loses the connection to the server it should
|
||||||
|
reconnect, following the steps above.
|
||||||
|
|
||||||
|
### Congestion
|
||||||
|
|
||||||
|
If the server sends messages faster than the client can consume them the
|
||||||
|
server will first buffer a (fairly large) number of commands and then
|
||||||
|
disconnect the client. This ensures that we don't queue up an unbounded
|
||||||
|
number of commands in memory and gives us a potential oppurtunity to
|
||||||
|
squawk loudly. When/if the client recovers it can reconnect to the
|
||||||
|
server and ask for missed messages.
|
||||||
|
|
||||||
|
### Reliability
|
||||||
|
|
||||||
|
In general the replication stream should be considered an unreliable
|
||||||
|
transport since e.g. commands are not resent if the connection
|
||||||
|
disappears.
|
||||||
|
|
||||||
|
The exception to that are the replication streams, i.e. RDATA commands,
|
||||||
|
since these include tokens which can be used to restart the stream on
|
||||||
|
connection errors.
|
||||||
|
|
||||||
|
The client should keep track of the token in the last RDATA command
|
||||||
|
received for each stream so that on reconneciton it can start streaming
|
||||||
|
from the correct place. Note: not all RDATA have valid tokens due to
|
||||||
|
batching. See `RdataCommand` for more details.
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
An example iteraction is shown below. Each line is prefixed with '>'
|
||||||
|
or '<' to indicate which side is sending, these are *not* included on
|
||||||
|
the wire:
|
||||||
|
|
||||||
|
* connection established *
|
||||||
|
> SERVER localhost:8823
|
||||||
|
> PING 1490197665618
|
||||||
|
< NAME synapse.app.appservice
|
||||||
|
< PING 1490197665618
|
||||||
|
< REPLICATE events 1
|
||||||
|
< REPLICATE backfill 1
|
||||||
|
< REPLICATE caches 1
|
||||||
|
> POSITION events 1
|
||||||
|
> POSITION backfill 1
|
||||||
|
> POSITION caches 1
|
||||||
|
> RDATA caches 2 ["get_user_by_id",["@01register-user:localhost:8823"],1490197670513]
|
||||||
|
> RDATA events 14 ["$149019767112vOHxz:localhost:8823",
|
||||||
|
"!AFDCvgApUmpdfVjIXm:localhost:8823","m.room.guest_access","",null]
|
||||||
|
< PING 1490197675618
|
||||||
|
> ERROR server stopping
|
||||||
|
* connection closed by server *
|
||||||
|
|
||||||
|
The `POSITION` command sent by the server is used to set the clients
|
||||||
|
position without needing to send data with the `RDATA` command.
|
||||||
|
|
||||||
|
An example of a batched set of `RDATA` is:
|
||||||
|
|
||||||
|
> RDATA caches batch ["get_user_by_id",["@test:localhost:8823"],1490197670513]
|
||||||
|
> RDATA caches batch ["get_user_by_id",["@test2:localhost:8823"],1490197670513]
|
||||||
|
> RDATA caches batch ["get_user_by_id",["@test3:localhost:8823"],1490197670513]
|
||||||
|
> RDATA caches 54 ["get_user_by_id",["@test4:localhost:8823"],1490197670513]
|
||||||
|
|
||||||
|
In this case the client shouldn't advance their caches token until it
|
||||||
|
sees the the last `RDATA`.
|
||||||
|
|
||||||
|
### List of commands
|
||||||
|
|
||||||
|
The list of valid commands, with which side can send it: server (S) or
|
||||||
|
client (C):
|
||||||
|
|
||||||
|
#### SERVER (S)
|
||||||
|
|
||||||
|
Sent at the start to identify which server the client is talking to
|
||||||
|
|
||||||
|
#### RDATA (S)
|
||||||
|
|
||||||
|
A single update in a stream
|
||||||
|
|
||||||
|
#### POSITION (S)
|
||||||
|
|
||||||
|
The position of the stream has been updated. Sent to the client
|
||||||
|
after all missing updates for a stream have been sent to the client
|
||||||
|
and they're now up to date.
|
||||||
|
|
||||||
|
#### ERROR (S, C)
|
||||||
|
|
||||||
|
There was an error
|
||||||
|
|
||||||
|
#### PING (S, C)
|
||||||
|
|
||||||
|
Sent periodically to ensure the connection is still alive
|
||||||
|
|
||||||
|
#### NAME (C)
|
||||||
|
|
||||||
|
Sent at the start by client to inform the server who they are
|
||||||
|
|
||||||
|
#### REPLICATE (C)
|
||||||
|
|
||||||
|
Asks the server to replicate a given stream
|
||||||
|
|
||||||
|
#### USER_SYNC (C)
|
||||||
|
|
||||||
|
A user has started or stopped syncing
|
||||||
|
|
||||||
|
#### FEDERATION_ACK (C)
|
||||||
|
|
||||||
|
Acknowledge receipt of some federation data
|
||||||
|
|
||||||
|
#### REMOVE_PUSHER (C)
|
||||||
|
|
||||||
|
Inform the server a pusher should be removed
|
||||||
|
|
||||||
|
#### INVALIDATE_CACHE (C)
|
||||||
|
|
||||||
|
Inform the server a cache should be invalidated
|
||||||
|
|
||||||
|
#### SYNC (S, C)
|
||||||
|
|
||||||
|
Used exclusively in tests
|
||||||
|
|
||||||
|
See `synapse/replication/tcp/commands.py` for a detailed description and
|
||||||
|
the format of each command.
|
||||||
|
|
||||||
|
### Cache Invalidation Stream
|
||||||
|
|
||||||
|
The cache invalidation stream is used to inform workers when they need
|
||||||
|
to invalidate any of their caches in the data store. This is done by
|
||||||
|
streaming all cache invalidations done on master down to the workers,
|
||||||
|
assuming that any caches on the workers also exist on the master.
|
||||||
|
|
||||||
|
Each individual cache invalidation results in a row being sent down
|
||||||
|
replication, which includes the cache name (the name of the function)
|
||||||
|
and they key to invalidate. For example:
|
||||||
|
|
||||||
|
> RDATA caches 550953771 ["get_user_by_id", ["@bob:example.com"], 1550574873251]
|
||||||
|
|
||||||
|
However, there are times when a number of caches need to be invalidated
|
||||||
|
at the same time with the same key. To reduce traffic we batch those
|
||||||
|
invalidations into a single poke by defining a special cache name that
|
||||||
|
workers understand to mean to expand to invalidate the correct caches.
|
||||||
|
|
||||||
|
Currently the special cache names are declared in
|
||||||
|
`synapse/storage/_base.py` and are:
|
||||||
|
|
||||||
|
1. `cs_cache_fake` ─ invalidates caches that depend on the current
|
||||||
|
state
|
|
@ -1,249 +0,0 @@
|
||||||
TCP Replication
|
|
||||||
===============
|
|
||||||
|
|
||||||
Motivation
|
|
||||||
----------
|
|
||||||
|
|
||||||
Previously the workers used an HTTP long poll mechanism to get updates from the
|
|
||||||
master, which had the problem of causing a lot of duplicate work on the server.
|
|
||||||
This TCP protocol replaces those APIs with the aim of increased efficiency.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Overview
|
|
||||||
--------
|
|
||||||
|
|
||||||
The protocol is based on fire and forget, line based commands. An example flow
|
|
||||||
would be (where '>' indicates master to worker and '<' worker to master flows)::
|
|
||||||
|
|
||||||
> SERVER example.com
|
|
||||||
< REPLICATE events 53
|
|
||||||
> RDATA events 54 ["$foo1:bar.com", ...]
|
|
||||||
> RDATA events 55 ["$foo4:bar.com", ...]
|
|
||||||
|
|
||||||
The example shows the server accepting a new connection and sending its identity
|
|
||||||
with the ``SERVER`` command, followed by the client asking to subscribe to the
|
|
||||||
``events`` stream from the token ``53``. The server then periodically sends ``RDATA``
|
|
||||||
commands which have the format ``RDATA <stream_name> <token> <row>``, where the
|
|
||||||
format of ``<row>`` is defined by the individual streams.
|
|
||||||
|
|
||||||
Error reporting happens by either the client or server sending an `ERROR`
|
|
||||||
command, and usually the connection will be closed.
|
|
||||||
|
|
||||||
|
|
||||||
Since the protocol is a simple line based, its possible to manually connect to
|
|
||||||
the server using a tool like netcat. A few things should be noted when manually
|
|
||||||
using the protocol:
|
|
||||||
|
|
||||||
* When subscribing to a stream using ``REPLICATE``, the special token ``NOW`` can
|
|
||||||
be used to get all future updates. The special stream name ``ALL`` can be used
|
|
||||||
with ``NOW`` to subscribe to all available streams.
|
|
||||||
* The federation stream is only available if federation sending has been
|
|
||||||
disabled on the main process.
|
|
||||||
* The server will only time connections out that have sent a ``PING`` command.
|
|
||||||
If a ping is sent then the connection will be closed if no further commands
|
|
||||||
are receieved within 15s. Both the client and server protocol implementations
|
|
||||||
will send an initial PING on connection and ensure at least one command every
|
|
||||||
5s is sent (not necessarily ``PING``).
|
|
||||||
* ``RDATA`` commands *usually* include a numeric token, however if the stream
|
|
||||||
has multiple rows to replicate per token the server will send multiple
|
|
||||||
``RDATA`` commands, with all but the last having a token of ``batch``. See
|
|
||||||
the documentation on ``commands.RdataCommand`` for further details.
|
|
||||||
|
|
||||||
|
|
||||||
Architecture
|
|
||||||
------------
|
|
||||||
|
|
||||||
The basic structure of the protocol is line based, where the initial word of
|
|
||||||
each line specifies the command. The rest of the line is parsed based on the
|
|
||||||
command. For example, the `RDATA` command is defined as::
|
|
||||||
|
|
||||||
RDATA <stream_name> <token> <row_json>
|
|
||||||
|
|
||||||
(Note that `<row_json>` may contains spaces, but cannot contain newlines.)
|
|
||||||
|
|
||||||
Blank lines are ignored.
|
|
||||||
|
|
||||||
|
|
||||||
Keep alives
|
|
||||||
~~~~~~~~~~~
|
|
||||||
|
|
||||||
Both sides are expected to send at least one command every 5s or so, and
|
|
||||||
should send a ``PING`` command if necessary. If either side do not receive a
|
|
||||||
command within e.g. 15s then the connection should be closed.
|
|
||||||
|
|
||||||
Because the server may be connected to manually using e.g. netcat, the timeouts
|
|
||||||
aren't enabled until an initial ``PING`` command is seen. Both the client and
|
|
||||||
server implementations below send a ``PING`` command immediately on connection to
|
|
||||||
ensure the timeouts are enabled.
|
|
||||||
|
|
||||||
This ensures that both sides can quickly realize if the tcp connection has gone
|
|
||||||
and handle the situation appropriately.
|
|
||||||
|
|
||||||
|
|
||||||
Start up
|
|
||||||
~~~~~~~~
|
|
||||||
|
|
||||||
When a new connection is made, the server:
|
|
||||||
|
|
||||||
* Sends a ``SERVER`` command, which includes the identity of the server, allowing
|
|
||||||
the client to detect if its connected to the expected server
|
|
||||||
* Sends a ``PING`` command as above, to enable the client to time out connections
|
|
||||||
promptly.
|
|
||||||
|
|
||||||
The client:
|
|
||||||
|
|
||||||
* Sends a ``NAME`` command, allowing the server to associate a human friendly
|
|
||||||
name with the connection. This is optional.
|
|
||||||
* Sends a ``PING`` as above
|
|
||||||
* For each stream the client wishes to subscribe to it sends a ``REPLICATE``
|
|
||||||
with the stream_name and token it wants to subscribe from.
|
|
||||||
* On receipt of a ``SERVER`` command, checks that the server name matches the
|
|
||||||
expected server name.
|
|
||||||
|
|
||||||
|
|
||||||
Error handling
|
|
||||||
~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
If either side detects an error it can send an ``ERROR`` command and close the
|
|
||||||
connection.
|
|
||||||
|
|
||||||
If the client side loses the connection to the server it should reconnect,
|
|
||||||
following the steps above.
|
|
||||||
|
|
||||||
|
|
||||||
Congestion
|
|
||||||
~~~~~~~~~~
|
|
||||||
|
|
||||||
If the server sends messages faster than the client can consume them the server
|
|
||||||
will first buffer a (fairly large) number of commands and then disconnect the
|
|
||||||
client. This ensures that we don't queue up an unbounded number of commands in
|
|
||||||
memory and gives us a potential oppurtunity to squawk loudly. When/if the client
|
|
||||||
recovers it can reconnect to the server and ask for missed messages.
|
|
||||||
|
|
||||||
|
|
||||||
Reliability
|
|
||||||
~~~~~~~~~~~
|
|
||||||
|
|
||||||
In general the replication stream should be considered an unreliable transport
|
|
||||||
since e.g. commands are not resent if the connection disappears.
|
|
||||||
|
|
||||||
The exception to that are the replication streams, i.e. RDATA commands, since
|
|
||||||
these include tokens which can be used to restart the stream on connection
|
|
||||||
errors.
|
|
||||||
|
|
||||||
The client should keep track of the token in the last RDATA command received
|
|
||||||
for each stream so that on reconneciton it can start streaming from the correct
|
|
||||||
place. Note: not all RDATA have valid tokens due to batching. See
|
|
||||||
``RdataCommand`` for more details.
|
|
||||||
|
|
||||||
Example
|
|
||||||
~~~~~~~
|
|
||||||
|
|
||||||
An example iteraction is shown below. Each line is prefixed with '>' or '<' to
|
|
||||||
indicate which side is sending, these are *not* included on the wire::
|
|
||||||
|
|
||||||
* connection established *
|
|
||||||
> SERVER localhost:8823
|
|
||||||
> PING 1490197665618
|
|
||||||
< NAME synapse.app.appservice
|
|
||||||
< PING 1490197665618
|
|
||||||
< REPLICATE events 1
|
|
||||||
< REPLICATE backfill 1
|
|
||||||
< REPLICATE caches 1
|
|
||||||
> POSITION events 1
|
|
||||||
> POSITION backfill 1
|
|
||||||
> POSITION caches 1
|
|
||||||
> RDATA caches 2 ["get_user_by_id",["@01register-user:localhost:8823"],1490197670513]
|
|
||||||
> RDATA events 14 ["$149019767112vOHxz:localhost:8823",
|
|
||||||
"!AFDCvgApUmpdfVjIXm:localhost:8823","m.room.guest_access","",null]
|
|
||||||
< PING 1490197675618
|
|
||||||
> ERROR server stopping
|
|
||||||
* connection closed by server *
|
|
||||||
|
|
||||||
The ``POSITION`` command sent by the server is used to set the clients position
|
|
||||||
without needing to send data with the ``RDATA`` command.
|
|
||||||
|
|
||||||
|
|
||||||
An example of a batched set of ``RDATA`` is::
|
|
||||||
|
|
||||||
> RDATA caches batch ["get_user_by_id",["@test:localhost:8823"],1490197670513]
|
|
||||||
> RDATA caches batch ["get_user_by_id",["@test2:localhost:8823"],1490197670513]
|
|
||||||
> RDATA caches batch ["get_user_by_id",["@test3:localhost:8823"],1490197670513]
|
|
||||||
> RDATA caches 54 ["get_user_by_id",["@test4:localhost:8823"],1490197670513]
|
|
||||||
|
|
||||||
In this case the client shouldn't advance their caches token until it sees the
|
|
||||||
the last ``RDATA``.
|
|
||||||
|
|
||||||
|
|
||||||
List of commands
|
|
||||||
~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
The list of valid commands, with which side can send it: server (S) or client (C):
|
|
||||||
|
|
||||||
SERVER (S)
|
|
||||||
Sent at the start to identify which server the client is talking to
|
|
||||||
|
|
||||||
RDATA (S)
|
|
||||||
A single update in a stream
|
|
||||||
|
|
||||||
POSITION (S)
|
|
||||||
The position of the stream has been updated. Sent to the client after all
|
|
||||||
missing updates for a stream have been sent to the client and they're now
|
|
||||||
up to date.
|
|
||||||
|
|
||||||
ERROR (S, C)
|
|
||||||
There was an error
|
|
||||||
|
|
||||||
PING (S, C)
|
|
||||||
Sent periodically to ensure the connection is still alive
|
|
||||||
|
|
||||||
NAME (C)
|
|
||||||
Sent at the start by client to inform the server who they are
|
|
||||||
|
|
||||||
REPLICATE (C)
|
|
||||||
Asks the server to replicate a given stream
|
|
||||||
|
|
||||||
USER_SYNC (C)
|
|
||||||
A user has started or stopped syncing
|
|
||||||
|
|
||||||
FEDERATION_ACK (C)
|
|
||||||
Acknowledge receipt of some federation data
|
|
||||||
|
|
||||||
REMOVE_PUSHER (C)
|
|
||||||
Inform the server a pusher should be removed
|
|
||||||
|
|
||||||
INVALIDATE_CACHE (C)
|
|
||||||
Inform the server a cache should be invalidated
|
|
||||||
|
|
||||||
SYNC (S, C)
|
|
||||||
Used exclusively in tests
|
|
||||||
|
|
||||||
|
|
||||||
See ``synapse/replication/tcp/commands.py`` for a detailed description and the
|
|
||||||
format of each command.
|
|
||||||
|
|
||||||
|
|
||||||
Cache Invalidation Stream
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
The cache invalidation stream is used to inform workers when they need to
|
|
||||||
invalidate any of their caches in the data store. This is done by streaming all
|
|
||||||
cache invalidations done on master down to the workers, assuming that any caches
|
|
||||||
on the workers also exist on the master.
|
|
||||||
|
|
||||||
Each individual cache invalidation results in a row being sent down replication,
|
|
||||||
which includes the cache name (the name of the function) and they key to
|
|
||||||
invalidate. For example::
|
|
||||||
|
|
||||||
> RDATA caches 550953771 ["get_user_by_id", ["@bob:example.com"], 1550574873251]
|
|
||||||
|
|
||||||
However, there are times when a number of caches need to be invalidated at the
|
|
||||||
same time with the same key. To reduce traffic we batch those invalidations into
|
|
||||||
a single poke by defining a special cache name that workers understand to mean
|
|
||||||
to expand to invalidate the correct caches.
|
|
||||||
|
|
||||||
Currently the special cache names are declared in ``synapse/storage/_base.py``
|
|
||||||
and are:
|
|
||||||
|
|
||||||
1. ``cs_cache_fake`` ─ invalidates caches that depend on the current state
|
|
|
@ -0,0 +1,123 @@
|
||||||
|
# Overview
|
||||||
|
|
||||||
|
This document explains how to enable VoIP relaying on your Home Server with
|
||||||
|
TURN.
|
||||||
|
|
||||||
|
The synapse Matrix Home Server supports integration with TURN server via the
|
||||||
|
[TURN server REST API](<http://tools.ietf.org/html/draft-uberti-behave-turn-rest-00>). This
|
||||||
|
allows the Home Server to generate credentials that are valid for use on the
|
||||||
|
TURN server through the use of a secret shared between the Home Server and the
|
||||||
|
TURN server.
|
||||||
|
|
||||||
|
The following sections describe how to install [coturn](<https://github.com/coturn/coturn>) (which implements the TURN REST API) and integrate it with synapse.
|
||||||
|
|
||||||
|
## `coturn` Setup
|
||||||
|
|
||||||
|
### Initial installation
|
||||||
|
|
||||||
|
The TURN daemon `coturn` is available from a variety of sources such as native package managers, or installation from source.
|
||||||
|
|
||||||
|
#### Debian installation
|
||||||
|
|
||||||
|
# apt install coturn
|
||||||
|
|
||||||
|
#### Source installation
|
||||||
|
|
||||||
|
1. Download the [latest release](https://github.com/coturn/coturn/releases/latest) from github. Unpack it and `cd` into the directory.
|
||||||
|
|
||||||
|
1. Configure it:
|
||||||
|
|
||||||
|
./configure
|
||||||
|
|
||||||
|
> You may need to install `libevent2`: if so, you should do so in
|
||||||
|
> the way recommended by your operating system. You can ignore
|
||||||
|
> warnings about lack of database support: a database is unnecessary
|
||||||
|
> for this purpose.
|
||||||
|
|
||||||
|
1. Build and install it:
|
||||||
|
|
||||||
|
make
|
||||||
|
make install
|
||||||
|
|
||||||
|
1. Create or edit the config file in `/etc/turnserver.conf`. The relevant
|
||||||
|
lines, with example values, are:
|
||||||
|
|
||||||
|
use-auth-secret
|
||||||
|
static-auth-secret=[your secret key here]
|
||||||
|
realm=turn.myserver.org
|
||||||
|
|
||||||
|
See `turnserver.conf` for explanations of the options. One way to generate
|
||||||
|
the `static-auth-secret` is with `pwgen`:
|
||||||
|
|
||||||
|
pwgen -s 64 1
|
||||||
|
|
||||||
|
1. Consider your security settings. TURN lets users request a relay which will
|
||||||
|
connect to arbitrary IP addresses and ports. The following configuration is
|
||||||
|
suggested as a minimum starting point:
|
||||||
|
|
||||||
|
# VoIP traffic is all UDP. There is no reason to let users connect to arbitrary TCP endpoints via the relay.
|
||||||
|
no-tcp-relay
|
||||||
|
|
||||||
|
# don't let the relay ever try to connect to private IP address ranges within your network (if any)
|
||||||
|
# given the turn server is likely behind your firewall, remember to include any privileged public IPs too.
|
||||||
|
denied-peer-ip=10.0.0.0-10.255.255.255
|
||||||
|
denied-peer-ip=192.168.0.0-192.168.255.255
|
||||||
|
denied-peer-ip=172.16.0.0-172.31.255.255
|
||||||
|
|
||||||
|
# special case the turn server itself so that client->TURN->TURN->client flows work
|
||||||
|
allowed-peer-ip=10.0.0.1
|
||||||
|
|
||||||
|
# consider whether you want to limit the quota of relayed streams per user (or total) to avoid risk of DoS.
|
||||||
|
user-quota=12 # 4 streams per video call, so 12 streams = 3 simultaneous relayed calls per user.
|
||||||
|
total-quota=1200
|
||||||
|
|
||||||
|
Ideally coturn should refuse to relay traffic which isn't SRTP; see
|
||||||
|
<https://github.com/matrix-org/synapse/issues/2009>
|
||||||
|
|
||||||
|
1. Ensure your firewall allows traffic into the TURN server on the ports
|
||||||
|
you've configured it to listen on (remember to allow both TCP and UDP TURN
|
||||||
|
traffic)
|
||||||
|
|
||||||
|
1. If you've configured coturn to support TLS/DTLS, generate or import your
|
||||||
|
private key and certificate.
|
||||||
|
|
||||||
|
1. Start the turn server:
|
||||||
|
|
||||||
|
bin/turnserver -o
|
||||||
|
|
||||||
|
## synapse Setup
|
||||||
|
|
||||||
|
Your home server configuration file needs the following extra keys:
|
||||||
|
|
||||||
|
1. "`turn_uris`": This needs to be a yaml list of public-facing URIs
|
||||||
|
for your TURN server to be given out to your clients. Add separate
|
||||||
|
entries for each transport your TURN server supports.
|
||||||
|
2. "`turn_shared_secret`": This is the secret shared between your
|
||||||
|
Home server and your TURN server, so you should set it to the same
|
||||||
|
string you used in turnserver.conf.
|
||||||
|
3. "`turn_user_lifetime`": This is the amount of time credentials
|
||||||
|
generated by your Home Server are valid for (in milliseconds).
|
||||||
|
Shorter times offer less potential for abuse at the expense of
|
||||||
|
increased traffic between web clients and your home server to
|
||||||
|
refresh credentials. The TURN REST API specification recommends
|
||||||
|
one day (86400000).
|
||||||
|
4. "`turn_allow_guests`": Whether to allow guest users to use the
|
||||||
|
TURN server. This is enabled by default, as otherwise VoIP will
|
||||||
|
not work reliably for guests. However, it does introduce a
|
||||||
|
security risk as it lets guests connect to arbitrary endpoints
|
||||||
|
without having gone through a CAPTCHA or similar to register a
|
||||||
|
real account.
|
||||||
|
|
||||||
|
As an example, here is the relevant section of the config file for matrix.org:
|
||||||
|
|
||||||
|
turn_uris: [ "turn:turn.matrix.org:3478?transport=udp", "turn:turn.matrix.org:3478?transport=tcp" ]
|
||||||
|
turn_shared_secret: n0t4ctuAllymatr1Xd0TorgSshar3d5ecret4obvIousreAsons
|
||||||
|
turn_user_lifetime: 86400000
|
||||||
|
turn_allow_guests: True
|
||||||
|
|
||||||
|
After updating the homeserver configuration, you must restart synapse:
|
||||||
|
|
||||||
|
cd /where/you/run/synapse
|
||||||
|
./synctl restart
|
||||||
|
|
||||||
|
..and your Home Server now supports VoIP relaying!
|
|
@ -1,127 +0,0 @@
|
||||||
How to enable VoIP relaying on your Home Server with TURN
|
|
||||||
|
|
||||||
Overview
|
|
||||||
--------
|
|
||||||
The synapse Matrix Home Server supports integration with TURN server via the
|
|
||||||
TURN server REST API
|
|
||||||
(http://tools.ietf.org/html/draft-uberti-behave-turn-rest-00). This allows
|
|
||||||
the Home Server to generate credentials that are valid for use on the TURN
|
|
||||||
server through the use of a secret shared between the Home Server and the
|
|
||||||
TURN server.
|
|
||||||
|
|
||||||
This document describes how to install coturn
|
|
||||||
(https://github.com/coturn/coturn) which also supports the TURN REST API,
|
|
||||||
and integrate it with synapse.
|
|
||||||
|
|
||||||
coturn Setup
|
|
||||||
============
|
|
||||||
|
|
||||||
You may be able to setup coturn via your package manager, or set it up manually using the usual ``configure, make, make install`` process.
|
|
||||||
|
|
||||||
1. Check out coturn::
|
|
||||||
|
|
||||||
git clone https://github.com/coturn/coturn.git coturn
|
|
||||||
cd coturn
|
|
||||||
|
|
||||||
2. Configure it::
|
|
||||||
|
|
||||||
./configure
|
|
||||||
|
|
||||||
You may need to install ``libevent2``: if so, you should do so
|
|
||||||
in the way recommended by your operating system.
|
|
||||||
You can ignore warnings about lack of database support: a
|
|
||||||
database is unnecessary for this purpose.
|
|
||||||
|
|
||||||
3. Build and install it::
|
|
||||||
|
|
||||||
make
|
|
||||||
make install
|
|
||||||
|
|
||||||
4. Create or edit the config file in ``/etc/turnserver.conf``. The relevant
|
|
||||||
lines, with example values, are::
|
|
||||||
|
|
||||||
use-auth-secret
|
|
||||||
static-auth-secret=[your secret key here]
|
|
||||||
realm=turn.myserver.org
|
|
||||||
|
|
||||||
See turnserver.conf for explanations of the options.
|
|
||||||
One way to generate the static-auth-secret is with pwgen::
|
|
||||||
|
|
||||||
pwgen -s 64 1
|
|
||||||
|
|
||||||
5. Consider your security settings. TURN lets users request a relay
|
|
||||||
which will connect to arbitrary IP addresses and ports. At the least
|
|
||||||
we recommend::
|
|
||||||
|
|
||||||
# VoIP traffic is all UDP. There is no reason to let users connect to arbitrary TCP endpoints via the relay.
|
|
||||||
no-tcp-relay
|
|
||||||
|
|
||||||
# don't let the relay ever try to connect to private IP address ranges within your network (if any)
|
|
||||||
# given the turn server is likely behind your firewall, remember to include any privileged public IPs too.
|
|
||||||
denied-peer-ip=10.0.0.0-10.255.255.255
|
|
||||||
denied-peer-ip=192.168.0.0-192.168.255.255
|
|
||||||
denied-peer-ip=172.16.0.0-172.31.255.255
|
|
||||||
|
|
||||||
# special case the turn server itself so that client->TURN->TURN->client flows work
|
|
||||||
allowed-peer-ip=10.0.0.1
|
|
||||||
|
|
||||||
# consider whether you want to limit the quota of relayed streams per user (or total) to avoid risk of DoS.
|
|
||||||
user-quota=12 # 4 streams per video call, so 12 streams = 3 simultaneous relayed calls per user.
|
|
||||||
total-quota=1200
|
|
||||||
|
|
||||||
Ideally coturn should refuse to relay traffic which isn't SRTP;
|
|
||||||
see https://github.com/matrix-org/synapse/issues/2009
|
|
||||||
|
|
||||||
6. Ensure your firewall allows traffic into the TURN server on
|
|
||||||
the ports you've configured it to listen on (remember to allow
|
|
||||||
both TCP and UDP TURN traffic)
|
|
||||||
|
|
||||||
7. If you've configured coturn to support TLS/DTLS, generate or
|
|
||||||
import your private key and certificate.
|
|
||||||
|
|
||||||
8. Start the turn server::
|
|
||||||
|
|
||||||
bin/turnserver -o
|
|
||||||
|
|
||||||
|
|
||||||
synapse Setup
|
|
||||||
=============
|
|
||||||
|
|
||||||
Your home server configuration file needs the following extra keys:
|
|
||||||
|
|
||||||
1. "turn_uris": This needs to be a yaml list
|
|
||||||
of public-facing URIs for your TURN server to be given out
|
|
||||||
to your clients. Add separate entries for each transport your
|
|
||||||
TURN server supports.
|
|
||||||
|
|
||||||
2. "turn_shared_secret": This is the secret shared between your Home
|
|
||||||
server and your TURN server, so you should set it to the same
|
|
||||||
string you used in turnserver.conf.
|
|
||||||
|
|
||||||
3. "turn_user_lifetime": This is the amount of time credentials
|
|
||||||
generated by your Home Server are valid for (in milliseconds).
|
|
||||||
Shorter times offer less potential for abuse at the expense
|
|
||||||
of increased traffic between web clients and your home server
|
|
||||||
to refresh credentials. The TURN REST API specification recommends
|
|
||||||
one day (86400000).
|
|
||||||
|
|
||||||
4. "turn_allow_guests": Whether to allow guest users to use the TURN
|
|
||||||
server. This is enabled by default, as otherwise VoIP will not
|
|
||||||
work reliably for guests. However, it does introduce a security risk
|
|
||||||
as it lets guests connect to arbitrary endpoints without having gone
|
|
||||||
through a CAPTCHA or similar to register a real account.
|
|
||||||
|
|
||||||
As an example, here is the relevant section of the config file for
|
|
||||||
matrix.org::
|
|
||||||
|
|
||||||
turn_uris: [ "turn:turn.matrix.org:3478?transport=udp", "turn:turn.matrix.org:3478?transport=tcp" ]
|
|
||||||
turn_shared_secret: n0t4ctuAllymatr1Xd0TorgSshar3d5ecret4obvIousreAsons
|
|
||||||
turn_user_lifetime: 86400000
|
|
||||||
turn_allow_guests: True
|
|
||||||
|
|
||||||
Now, restart synapse::
|
|
||||||
|
|
||||||
cd /where/you/run/synapse
|
|
||||||
./synctl restart
|
|
||||||
|
|
||||||
...and your Home Server now supports VoIP relaying!
|
|
|
@ -1,5 +1,4 @@
|
||||||
Scaling synapse via workers
|
# Scaling synapse via workers
|
||||||
===========================
|
|
||||||
|
|
||||||
Synapse has experimental support for splitting out functionality into
|
Synapse has experimental support for splitting out functionality into
|
||||||
multiple separate python processes, helping greatly with scalability. These
|
multiple separate python processes, helping greatly with scalability. These
|
||||||
|
@ -20,17 +19,16 @@ TCP protocol called 'replication' - analogous to MySQL or Postgres style
|
||||||
database replication; feeding a stream of relevant data to the workers so they
|
database replication; feeding a stream of relevant data to the workers so they
|
||||||
can be kept in sync with the main synapse process and database state.
|
can be kept in sync with the main synapse process and database state.
|
||||||
|
|
||||||
Configuration
|
## Configuration
|
||||||
-------------
|
|
||||||
|
|
||||||
To make effective use of the workers, you will need to configure an HTTP
|
To make effective use of the workers, you will need to configure an HTTP
|
||||||
reverse-proxy such as nginx or haproxy, which will direct incoming requests to
|
reverse-proxy such as nginx or haproxy, which will direct incoming requests to
|
||||||
the correct worker, or to the main synapse instance. Note that this includes
|
the correct worker, or to the main synapse instance. Note that this includes
|
||||||
requests made to the federation port. See `<reverse_proxy.rst>`_ for
|
requests made to the federation port. See [reverse_proxy.md](reverse_proxy.md)
|
||||||
information on setting up a reverse proxy.
|
for information on setting up a reverse proxy.
|
||||||
|
|
||||||
To enable workers, you need to add two replication listeners to the master
|
To enable workers, you need to add two replication listeners to the master
|
||||||
synapse, e.g.::
|
synapse, e.g.:
|
||||||
|
|
||||||
listeners:
|
listeners:
|
||||||
# The TCP replication port
|
# The TCP replication port
|
||||||
|
@ -56,7 +54,7 @@ You then create a set of configs for the various worker processes. These
|
||||||
should be worker configuration files, and should be stored in a dedicated
|
should be worker configuration files, and should be stored in a dedicated
|
||||||
subdirectory, to allow synctl to manipulate them. An additional configuration
|
subdirectory, to allow synctl to manipulate them. An additional configuration
|
||||||
for the master synapse process will need to be created because the process will
|
for the master synapse process will need to be created because the process will
|
||||||
not be started automatically. That configuration should look like this::
|
not be started automatically. That configuration should look like this:
|
||||||
|
|
||||||
worker_app: synapse.app.homeserver
|
worker_app: synapse.app.homeserver
|
||||||
daemonize: true
|
daemonize: true
|
||||||
|
@ -66,17 +64,17 @@ configuration file. You can then override configuration specific to that worker
|
||||||
e.g. the HTTP listener that it provides (if any); logging configuration; etc.
|
e.g. the HTTP listener that it provides (if any); logging configuration; etc.
|
||||||
You should minimise the number of overrides though to maintain a usable config.
|
You should minimise the number of overrides though to maintain a usable config.
|
||||||
|
|
||||||
You must specify the type of worker application (``worker_app``). The currently
|
You must specify the type of worker application (`worker_app`). The currently
|
||||||
available worker applications are listed below. You must also specify the
|
available worker applications are listed below. You must also specify the
|
||||||
replication endpoints that it's talking to on the main synapse process.
|
replication endpoints that it's talking to on the main synapse process.
|
||||||
``worker_replication_host`` should specify the host of the main synapse,
|
`worker_replication_host` should specify the host of the main synapse,
|
||||||
``worker_replication_port`` should point to the TCP replication listener port and
|
`worker_replication_port` should point to the TCP replication listener port and
|
||||||
``worker_replication_http_port`` should point to the HTTP replication port.
|
`worker_replication_http_port` should point to the HTTP replication port.
|
||||||
|
|
||||||
Currently, the ``event_creator`` and ``federation_reader`` workers require specifying
|
Currently, the `event_creator` and `federation_reader` workers require specifying
|
||||||
``worker_replication_http_port``.
|
`worker_replication_http_port`.
|
||||||
|
|
||||||
For instance::
|
For instance:
|
||||||
|
|
||||||
worker_app: synapse.app.synchrotron
|
worker_app: synapse.app.synchrotron
|
||||||
|
|
||||||
|
@ -97,15 +95,15 @@ For instance::
|
||||||
worker_log_config: /home/matrix/synapse/config/synchrotron_log_config.yaml
|
worker_log_config: /home/matrix/synapse/config/synchrotron_log_config.yaml
|
||||||
|
|
||||||
...is a full configuration for a synchrotron worker instance, which will expose a
|
...is a full configuration for a synchrotron worker instance, which will expose a
|
||||||
plain HTTP ``/sync`` endpoint on port 8083 separately from the ``/sync`` endpoint provided
|
plain HTTP `/sync` endpoint on port 8083 separately from the `/sync` endpoint provided
|
||||||
by the main synapse.
|
by the main synapse.
|
||||||
|
|
||||||
Obviously you should configure your reverse-proxy to route the relevant
|
Obviously you should configure your reverse-proxy to route the relevant
|
||||||
endpoints to the worker (``localhost:8083`` in the above example).
|
endpoints to the worker (`localhost:8083` in the above example).
|
||||||
|
|
||||||
Finally, to actually run your worker-based synapse, you must pass synctl the -a
|
Finally, to actually run your worker-based synapse, you must pass synctl the -a
|
||||||
commandline option to tell it to operate on all the worker configurations found
|
commandline option to tell it to operate on all the worker configurations found
|
||||||
in the given directory, e.g.::
|
in the given directory, e.g.:
|
||||||
|
|
||||||
synctl -a $CONFIG/workers start
|
synctl -a $CONFIG/workers start
|
||||||
|
|
||||||
|
@ -114,28 +112,24 @@ synapse, unless you explicitly know it's safe not to. For instance, restarting
|
||||||
synapse without restarting all the synchrotrons may result in broken typing
|
synapse without restarting all the synchrotrons may result in broken typing
|
||||||
notifications.
|
notifications.
|
||||||
|
|
||||||
To manipulate a specific worker, you pass the -w option to synctl::
|
To manipulate a specific worker, you pass the -w option to synctl:
|
||||||
|
|
||||||
synctl -w $CONFIG/workers/synchrotron.yaml restart
|
synctl -w $CONFIG/workers/synchrotron.yaml restart
|
||||||
|
|
||||||
|
## Available worker applications
|
||||||
|
|
||||||
Available worker applications
|
### `synapse.app.pusher`
|
||||||
-----------------------------
|
|
||||||
|
|
||||||
``synapse.app.pusher``
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Handles sending push notifications to sygnal and email. Doesn't handle any
|
Handles sending push notifications to sygnal and email. Doesn't handle any
|
||||||
REST endpoints itself, but you should set ``start_pushers: False`` in the
|
REST endpoints itself, but you should set `start_pushers: False` in the
|
||||||
shared configuration file to stop the main synapse sending these notifications.
|
shared configuration file to stop the main synapse sending these notifications.
|
||||||
|
|
||||||
Note this worker cannot be load-balanced: only one instance should be active.
|
Note this worker cannot be load-balanced: only one instance should be active.
|
||||||
|
|
||||||
``synapse.app.synchrotron``
|
### `synapse.app.synchrotron`
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
The synchrotron handles ``sync`` requests from clients. In particular, it can
|
The synchrotron handles `sync` requests from clients. In particular, it can
|
||||||
handle REST endpoints matching the following regular expressions::
|
handle REST endpoints matching the following regular expressions:
|
||||||
|
|
||||||
^/_matrix/client/(v2_alpha|r0)/sync$
|
^/_matrix/client/(v2_alpha|r0)/sync$
|
||||||
^/_matrix/client/(api/v1|v2_alpha|r0)/events$
|
^/_matrix/client/(api/v1|v2_alpha|r0)/events$
|
||||||
|
@ -151,20 +145,18 @@ load-balance across the instances, though it will be more efficient if all
|
||||||
requests from a particular user are routed to a single instance. Extracting
|
requests from a particular user are routed to a single instance. Extracting
|
||||||
a userid from the access token is currently left as an exercise for the reader.
|
a userid from the access token is currently left as an exercise for the reader.
|
||||||
|
|
||||||
``synapse.app.appservice``
|
### `synapse.app.appservice`
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Handles sending output traffic to Application Services. Doesn't handle any
|
Handles sending output traffic to Application Services. Doesn't handle any
|
||||||
REST endpoints itself, but you should set ``notify_appservices: False`` in the
|
REST endpoints itself, but you should set `notify_appservices: False` in the
|
||||||
shared configuration file to stop the main synapse sending these notifications.
|
shared configuration file to stop the main synapse sending these notifications.
|
||||||
|
|
||||||
Note this worker cannot be load-balanced: only one instance should be active.
|
Note this worker cannot be load-balanced: only one instance should be active.
|
||||||
|
|
||||||
``synapse.app.federation_reader``
|
### `synapse.app.federation_reader`
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Handles a subset of federation endpoints. In particular, it can handle REST
|
Handles a subset of federation endpoints. In particular, it can handle REST
|
||||||
endpoints matching the following regular expressions::
|
endpoints matching the following regular expressions:
|
||||||
|
|
||||||
^/_matrix/federation/v1/event/
|
^/_matrix/federation/v1/event/
|
||||||
^/_matrix/federation/v1/state/
|
^/_matrix/federation/v1/state/
|
||||||
|
@ -190,40 +182,36 @@ reverse-proxy configuration.
|
||||||
The `^/_matrix/federation/v1/send/` endpoint must only be handled by a single
|
The `^/_matrix/federation/v1/send/` endpoint must only be handled by a single
|
||||||
instance.
|
instance.
|
||||||
|
|
||||||
``synapse.app.federation_sender``
|
### `synapse.app.federation_sender`
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Handles sending federation traffic to other servers. Doesn't handle any
|
Handles sending federation traffic to other servers. Doesn't handle any
|
||||||
REST endpoints itself, but you should set ``send_federation: False`` in the
|
REST endpoints itself, but you should set `send_federation: False` in the
|
||||||
shared configuration file to stop the main synapse sending this traffic.
|
shared configuration file to stop the main synapse sending this traffic.
|
||||||
|
|
||||||
Note this worker cannot be load-balanced: only one instance should be active.
|
Note this worker cannot be load-balanced: only one instance should be active.
|
||||||
|
|
||||||
``synapse.app.media_repository``
|
### `synapse.app.media_repository`
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Handles the media repository. It can handle all endpoints starting with::
|
Handles the media repository. It can handle all endpoints starting with:
|
||||||
|
|
||||||
/_matrix/media/
|
/_matrix/media/
|
||||||
|
|
||||||
And the following regular expressions matching media-specific administration
|
And the following regular expressions matching media-specific administration APIs:
|
||||||
APIs::
|
|
||||||
|
|
||||||
^/_synapse/admin/v1/purge_media_cache$
|
^/_synapse/admin/v1/purge_media_cache$
|
||||||
^/_synapse/admin/v1/room/.*/media$
|
^/_synapse/admin/v1/room/.*/media$
|
||||||
^/_synapse/admin/v1/quarantine_media/.*$
|
^/_synapse/admin/v1/quarantine_media/.*$
|
||||||
|
|
||||||
You should also set ``enable_media_repo: False`` in the shared configuration
|
You should also set `enable_media_repo: False` in the shared configuration
|
||||||
file to stop the main synapse running background jobs related to managing the
|
file to stop the main synapse running background jobs related to managing the
|
||||||
media repository.
|
media repository.
|
||||||
|
|
||||||
Note this worker cannot be load-balanced: only one instance should be active.
|
Note this worker cannot be load-balanced: only one instance should be active.
|
||||||
|
|
||||||
``synapse.app.client_reader``
|
### `synapse.app.client_reader`
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Handles client API endpoints. It can handle REST endpoints matching the
|
Handles client API endpoints. It can handle REST endpoints matching the
|
||||||
following regular expressions::
|
following regular expressions:
|
||||||
|
|
||||||
^/_matrix/client/(api/v1|r0|unstable)/publicRooms$
|
^/_matrix/client/(api/v1|r0|unstable)/publicRooms$
|
||||||
^/_matrix/client/(api/v1|r0|unstable)/rooms/.*/joined_members$
|
^/_matrix/client/(api/v1|r0|unstable)/rooms/.*/joined_members$
|
||||||
|
@ -237,60 +225,55 @@ following regular expressions::
|
||||||
^/_matrix/client/versions$
|
^/_matrix/client/versions$
|
||||||
^/_matrix/client/(api/v1|r0|unstable)/voip/turnServer$
|
^/_matrix/client/(api/v1|r0|unstable)/voip/turnServer$
|
||||||
|
|
||||||
Additionally, the following REST endpoints can be handled for GET requests::
|
Additionally, the following REST endpoints can be handled for GET requests:
|
||||||
|
|
||||||
^/_matrix/client/(api/v1|r0|unstable)/pushrules/.*$
|
^/_matrix/client/(api/v1|r0|unstable)/pushrules/.*$
|
||||||
|
|
||||||
Additionally, the following REST endpoints can be handled, but all requests must
|
Additionally, the following REST endpoints can be handled, but all requests must
|
||||||
be routed to the same instance::
|
be routed to the same instance:
|
||||||
|
|
||||||
^/_matrix/client/(r0|unstable)/register$
|
^/_matrix/client/(r0|unstable)/register$
|
||||||
|
|
||||||
Pagination requests can also be handled, but all requests with the same path
|
Pagination requests can also be handled, but all requests with the same path
|
||||||
room must be routed to the same instance. Additionally, care must be taken to
|
room must be routed to the same instance. Additionally, care must be taken to
|
||||||
ensure that the purge history admin API is not used while pagination requests
|
ensure that the purge history admin API is not used while pagination requests
|
||||||
for the room are in flight::
|
for the room are in flight:
|
||||||
|
|
||||||
^/_matrix/client/(api/v1|r0|unstable)/rooms/.*/messages$
|
^/_matrix/client/(api/v1|r0|unstable)/rooms/.*/messages$
|
||||||
|
|
||||||
|
### `synapse.app.user_dir`
|
||||||
``synapse.app.user_dir``
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Handles searches in the user directory. It can handle REST endpoints matching
|
Handles searches in the user directory. It can handle REST endpoints matching
|
||||||
the following regular expressions::
|
the following regular expressions:
|
||||||
|
|
||||||
^/_matrix/client/(api/v1|r0|unstable)/user_directory/search$
|
^/_matrix/client/(api/v1|r0|unstable)/user_directory/search$
|
||||||
|
|
||||||
``synapse.app.frontend_proxy``
|
### `synapse.app.frontend_proxy`
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Proxies some frequently-requested client endpoints to add caching and remove
|
Proxies some frequently-requested client endpoints to add caching and remove
|
||||||
load from the main synapse. It can handle REST endpoints matching the following
|
load from the main synapse. It can handle REST endpoints matching the following
|
||||||
regular expressions::
|
regular expressions:
|
||||||
|
|
||||||
^/_matrix/client/(api/v1|r0|unstable)/keys/upload
|
^/_matrix/client/(api/v1|r0|unstable)/keys/upload
|
||||||
|
|
||||||
If ``use_presence`` is False in the homeserver config, it can also handle REST
|
If `use_presence` is False in the homeserver config, it can also handle REST
|
||||||
endpoints matching the following regular expressions::
|
endpoints matching the following regular expressions:
|
||||||
|
|
||||||
^/_matrix/client/(api/v1|r0|unstable)/presence/[^/]+/status
|
^/_matrix/client/(api/v1|r0|unstable)/presence/[^/]+/status
|
||||||
|
|
||||||
This "stub" presence handler will pass through ``GET`` request but make the
|
This "stub" presence handler will pass through `GET` request but make the
|
||||||
``PUT`` effectively a no-op.
|
`PUT` effectively a no-op.
|
||||||
|
|
||||||
It will proxy any requests it cannot handle to the main synapse instance. It
|
It will proxy any requests it cannot handle to the main synapse instance. It
|
||||||
must therefore be configured with the location of the main instance, via
|
must therefore be configured with the location of the main instance, via
|
||||||
the ``worker_main_http_uri`` setting in the frontend_proxy worker configuration
|
the `worker_main_http_uri` setting in the `frontend_proxy` worker configuration
|
||||||
file. For example::
|
file. For example:
|
||||||
|
|
||||||
worker_main_http_uri: http://127.0.0.1:8008
|
worker_main_http_uri: http://127.0.0.1:8008
|
||||||
|
|
||||||
|
### `synapse.app.event_creator`
|
||||||
|
|
||||||
``synapse.app.event_creator``
|
Handles some event creation. It can handle REST endpoints matching:
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Handles some event creation. It can handle REST endpoints matching::
|
|
||||||
|
|
||||||
^/_matrix/client/(api/v1|r0|unstable)/rooms/.*/send
|
^/_matrix/client/(api/v1|r0|unstable)/rooms/.*/send
|
||||||
^/_matrix/client/(api/v1|r0|unstable)/rooms/.*/(join|invite|leave|ban|unban|kick)$
|
^/_matrix/client/(api/v1|r0|unstable)/rooms/.*/(join|invite|leave|ban|unban|kick)$
|
|
@ -1,16 +0,0 @@
|
||||||
#! /bin/bash
|
|
||||||
|
|
||||||
set -eux
|
|
||||||
|
|
||||||
cd "`dirname $0`/.."
|
|
||||||
|
|
||||||
TOX_DIR=$WORKSPACE/.tox
|
|
||||||
|
|
||||||
mkdir -p $TOX_DIR
|
|
||||||
|
|
||||||
if ! [ $TOX_DIR -ef .tox ]; then
|
|
||||||
ln -s "$TOX_DIR" .tox
|
|
||||||
fi
|
|
||||||
|
|
||||||
# set up the virtualenv
|
|
||||||
tox -e py27 --notest -v
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
[mypy]
|
||||||
|
namespace_packages=True
|
||||||
|
plugins=mypy_zope:plugin
|
||||||
|
follow_imports=skip
|
||||||
|
mypy_path=stubs
|
||||||
|
|
||||||
|
[mypy-synapse.config.homeserver]
|
||||||
|
# this is a mess because of the metaclass shenanigans
|
||||||
|
ignore_errors = True
|
||||||
|
|
||||||
|
[mypy-zope]
|
||||||
|
ignore_missing_imports = True
|
||||||
|
|
||||||
|
[mypy-constantly]
|
||||||
|
ignore_missing_imports = True
|
||||||
|
|
||||||
|
[mypy-twisted.*]
|
||||||
|
ignore_missing_imports = True
|
||||||
|
|
||||||
|
[mypy-treq.*]
|
||||||
|
ignore_missing_imports = True
|
||||||
|
|
||||||
|
[mypy-hyperlink]
|
||||||
|
ignore_missing_imports = True
|
||||||
|
|
||||||
|
[mypy-h11]
|
||||||
|
ignore_missing_imports = True
|
||||||
|
|
||||||
|
[mypy-opentracing]
|
||||||
|
ignore_missing_imports = True
|
||||||
|
|
||||||
|
[mypy-OpenSSL]
|
||||||
|
ignore_missing_imports = True
|
||||||
|
|
||||||
|
[mypy-netaddr]
|
||||||
|
ignore_missing_imports = True
|
||||||
|
|
||||||
|
[mypy-saml2.*]
|
||||||
|
ignore_missing_imports = True
|
||||||
|
|
||||||
|
[mypy-unpaddedbase64]
|
||||||
|
ignore_missing_imports = True
|
||||||
|
|
||||||
|
[mypy-canonicaljson]
|
||||||
|
ignore_missing_imports = True
|
||||||
|
|
||||||
|
[mypy-jaeger_client]
|
||||||
|
ignore_missing_imports = True
|
||||||
|
|
||||||
|
[mypy-jsonschema]
|
||||||
|
ignore_missing_imports = True
|
||||||
|
|
||||||
|
[mypy-signedjson.*]
|
||||||
|
ignore_missing_imports = True
|
|
@ -35,4 +35,4 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
__version__ = "1.3.1"
|
__version__ = "1.4.0"
|
||||||
|
|
|
@ -22,9 +22,10 @@ from netaddr import IPAddress
|
||||||
|
|
||||||
from twisted.internet import defer
|
from twisted.internet import defer
|
||||||
|
|
||||||
|
import synapse.logging.opentracing as opentracing
|
||||||
import synapse.types
|
import synapse.types
|
||||||
from synapse import event_auth
|
from synapse import event_auth
|
||||||
from synapse.api.constants import EventTypes, JoinRules, Membership
|
from synapse.api.constants import EventTypes, JoinRules, Membership, UserTypes
|
||||||
from synapse.api.errors import (
|
from synapse.api.errors import (
|
||||||
AuthError,
|
AuthError,
|
||||||
Codes,
|
Codes,
|
||||||
|
@ -178,6 +179,7 @@ class Auth(object):
|
||||||
def get_public_keys(self, invite_event):
|
def get_public_keys(self, invite_event):
|
||||||
return event_auth.get_public_keys(invite_event)
|
return event_auth.get_public_keys(invite_event)
|
||||||
|
|
||||||
|
@opentracing.trace
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def get_user_by_req(
|
def get_user_by_req(
|
||||||
self, request, allow_guest=False, rights="access", allow_expired=False
|
self, request, allow_guest=False, rights="access", allow_expired=False
|
||||||
|
@ -209,6 +211,7 @@ class Auth(object):
|
||||||
user_id, app_service = yield self._get_appservice_user_id(request)
|
user_id, app_service = yield self._get_appservice_user_id(request)
|
||||||
if user_id:
|
if user_id:
|
||||||
request.authenticated_entity = user_id
|
request.authenticated_entity = user_id
|
||||||
|
opentracing.set_tag("authenticated_entity", user_id)
|
||||||
|
|
||||||
if ip_addr and self.hs.config.track_appservice_user_ips:
|
if ip_addr and self.hs.config.track_appservice_user_ips:
|
||||||
yield self.store.insert_client_ip(
|
yield self.store.insert_client_ip(
|
||||||
|
@ -259,6 +262,7 @@ class Auth(object):
|
||||||
)
|
)
|
||||||
|
|
||||||
request.authenticated_entity = user.to_string()
|
request.authenticated_entity = user.to_string()
|
||||||
|
opentracing.set_tag("authenticated_entity", user.to_string())
|
||||||
|
|
||||||
return synapse.types.create_requester(
|
return synapse.types.create_requester(
|
||||||
user, token_id, is_guest, device_id, app_service=app_service
|
user, token_id, is_guest, device_id, app_service=app_service
|
||||||
|
@ -272,25 +276,25 @@ class Auth(object):
|
||||||
self.get_access_token_from_request(request)
|
self.get_access_token_from_request(request)
|
||||||
)
|
)
|
||||||
if app_service is None:
|
if app_service is None:
|
||||||
return (None, None)
|
return None, None
|
||||||
|
|
||||||
if app_service.ip_range_whitelist:
|
if app_service.ip_range_whitelist:
|
||||||
ip_address = IPAddress(self.hs.get_ip_from_request(request))
|
ip_address = IPAddress(self.hs.get_ip_from_request(request))
|
||||||
if ip_address not in app_service.ip_range_whitelist:
|
if ip_address not in app_service.ip_range_whitelist:
|
||||||
return (None, None)
|
return None, None
|
||||||
|
|
||||||
if b"user_id" not in request.args:
|
if b"user_id" not in request.args:
|
||||||
return (app_service.sender, app_service)
|
return app_service.sender, app_service
|
||||||
|
|
||||||
user_id = request.args[b"user_id"][0].decode("utf8")
|
user_id = request.args[b"user_id"][0].decode("utf8")
|
||||||
if app_service.sender == user_id:
|
if app_service.sender == user_id:
|
||||||
return (app_service.sender, app_service)
|
return app_service.sender, app_service
|
||||||
|
|
||||||
if not app_service.is_interested_in_user(user_id):
|
if not app_service.is_interested_in_user(user_id):
|
||||||
raise AuthError(403, "Application service cannot masquerade as this user.")
|
raise AuthError(403, "Application service cannot masquerade as this user.")
|
||||||
if not (yield self.store.get_user_by_id(user_id)):
|
if not (yield self.store.get_user_by_id(user_id)):
|
||||||
raise AuthError(403, "Application service has not registered this user")
|
raise AuthError(403, "Application service has not registered this user")
|
||||||
return (user_id, app_service)
|
return user_id, app_service
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def get_user_by_access_token(self, token, rights="access"):
|
def get_user_by_access_token(self, token, rights="access"):
|
||||||
|
@ -690,7 +694,7 @@ class Auth(object):
|
||||||
# * The user is a guest user, and has joined the room
|
# * The user is a guest user, and has joined the room
|
||||||
# else it will throw.
|
# else it will throw.
|
||||||
member_event = yield self.check_user_was_in_room(room_id, user_id)
|
member_event = yield self.check_user_was_in_room(room_id, user_id)
|
||||||
return (member_event.membership, member_event.event_id)
|
return member_event.membership, member_event.event_id
|
||||||
except AuthError:
|
except AuthError:
|
||||||
visibility = yield self.state.get_current_state(
|
visibility = yield self.state.get_current_state(
|
||||||
room_id, EventTypes.RoomHistoryVisibility, ""
|
room_id, EventTypes.RoomHistoryVisibility, ""
|
||||||
|
@ -699,14 +703,13 @@ class Auth(object):
|
||||||
visibility
|
visibility
|
||||||
and visibility.content["history_visibility"] == "world_readable"
|
and visibility.content["history_visibility"] == "world_readable"
|
||||||
):
|
):
|
||||||
return (Membership.JOIN, None)
|
return Membership.JOIN, None
|
||||||
return
|
|
||||||
raise AuthError(
|
raise AuthError(
|
||||||
403, "Guest access not allowed", errcode=Codes.GUEST_ACCESS_FORBIDDEN
|
403, "Guest access not allowed", errcode=Codes.GUEST_ACCESS_FORBIDDEN
|
||||||
)
|
)
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def check_auth_blocking(self, user_id=None, threepid=None):
|
def check_auth_blocking(self, user_id=None, threepid=None, user_type=None):
|
||||||
"""Checks if the user should be rejected for some external reason,
|
"""Checks if the user should be rejected for some external reason,
|
||||||
such as monthly active user limiting or global disable flag
|
such as monthly active user limiting or global disable flag
|
||||||
|
|
||||||
|
@ -719,6 +722,9 @@ class Auth(object):
|
||||||
with a MAU blocked server, normally they would be rejected but their
|
with a MAU blocked server, normally they would be rejected but their
|
||||||
threepid is on the reserved list. user_id and
|
threepid is on the reserved list. user_id and
|
||||||
threepid should never be set at the same time.
|
threepid should never be set at the same time.
|
||||||
|
|
||||||
|
user_type(str|None): If present, is used to decide whether to check against
|
||||||
|
certain blocking reasons like MAU.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Never fail an auth check for the server notices users or support user
|
# Never fail an auth check for the server notices users or support user
|
||||||
|
@ -756,6 +762,10 @@ class Auth(object):
|
||||||
self.hs.config.mau_limits_reserved_threepids, threepid
|
self.hs.config.mau_limits_reserved_threepids, threepid
|
||||||
):
|
):
|
||||||
return
|
return
|
||||||
|
elif user_type == UserTypes.SUPPORT:
|
||||||
|
# If the user does not exist yet and is of type "support",
|
||||||
|
# allow registration. Support users are excluded from MAU checks.
|
||||||
|
return
|
||||||
# Else if there is no room in the MAU bucket, bail
|
# Else if there is no room in the MAU bucket, bail
|
||||||
current_mau = yield self.store.get_monthly_active_count()
|
current_mau = yield self.store.get_monthly_active_count()
|
||||||
if current_mau >= self.hs.config.max_mau_value:
|
if current_mau >= self.hs.config.max_mau_value:
|
||||||
|
|
|
@ -122,7 +122,8 @@ class UserTypes(object):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
SUPPORT = "support"
|
SUPPORT = "support"
|
||||||
ALL_USER_TYPES = (SUPPORT,)
|
BOT = "bot"
|
||||||
|
ALL_USER_TYPES = (SUPPORT, BOT)
|
||||||
|
|
||||||
|
|
||||||
class RelationTypes(object):
|
class RelationTypes(object):
|
||||||
|
|
|
@ -36,18 +36,20 @@ from synapse.util.versionstring import get_version_string
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# list of tuples of function, args list, kwargs dict
|
||||||
_sighup_callbacks = []
|
_sighup_callbacks = []
|
||||||
|
|
||||||
|
|
||||||
def register_sighup(func):
|
def register_sighup(func, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Register a function to be called when a SIGHUP occurs.
|
Register a function to be called when a SIGHUP occurs.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
func (function): Function to be called when sent a SIGHUP signal.
|
func (function): Function to be called when sent a SIGHUP signal.
|
||||||
Will be called with a single argument, the homeserver.
|
Will be called with a single default argument, the homeserver.
|
||||||
|
*args, **kwargs: args and kwargs to be passed to the target function.
|
||||||
"""
|
"""
|
||||||
_sighup_callbacks.append(func)
|
_sighup_callbacks.append((func, args, kwargs))
|
||||||
|
|
||||||
|
|
||||||
def start_worker_reactor(appname, config, run_command=reactor.run):
|
def start_worker_reactor(appname, config, run_command=reactor.run):
|
||||||
|
@ -248,8 +250,8 @@ def start(hs, listeners=None):
|
||||||
# we're not using systemd.
|
# we're not using systemd.
|
||||||
sdnotify(b"RELOADING=1")
|
sdnotify(b"RELOADING=1")
|
||||||
|
|
||||||
for i in _sighup_callbacks:
|
for i, args, kwargs in _sighup_callbacks:
|
||||||
i(hs)
|
i(hs, *args, **kwargs)
|
||||||
|
|
||||||
sdnotify(b"READY=1")
|
sdnotify(b"READY=1")
|
||||||
|
|
||||||
|
|
|
@ -227,8 +227,6 @@ def start(config_options):
|
||||||
config.start_pushers = False
|
config.start_pushers = False
|
||||||
config.send_federation = False
|
config.send_federation = False
|
||||||
|
|
||||||
setup_logging(config, use_worker_options=True)
|
|
||||||
|
|
||||||
synapse.events.USE_FROZEN_DICTS = config.use_frozen_dicts
|
synapse.events.USE_FROZEN_DICTS = config.use_frozen_dicts
|
||||||
|
|
||||||
database_engine = create_engine(config.database_config)
|
database_engine = create_engine(config.database_config)
|
||||||
|
@ -241,6 +239,8 @@ def start(config_options):
|
||||||
database_engine=database_engine,
|
database_engine=database_engine,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
setup_logging(ss, config, use_worker_options=True)
|
||||||
|
|
||||||
ss.setup()
|
ss.setup()
|
||||||
|
|
||||||
# We use task.react as the basic run command as it correctly handles tearing
|
# We use task.react as the basic run command as it correctly handles tearing
|
||||||
|
|
|
@ -141,8 +141,6 @@ def start(config_options):
|
||||||
|
|
||||||
assert config.worker_app == "synapse.app.appservice"
|
assert config.worker_app == "synapse.app.appservice"
|
||||||
|
|
||||||
setup_logging(config, use_worker_options=True)
|
|
||||||
|
|
||||||
events.USE_FROZEN_DICTS = config.use_frozen_dicts
|
events.USE_FROZEN_DICTS = config.use_frozen_dicts
|
||||||
|
|
||||||
database_engine = create_engine(config.database_config)
|
database_engine = create_engine(config.database_config)
|
||||||
|
@ -167,6 +165,8 @@ def start(config_options):
|
||||||
database_engine=database_engine,
|
database_engine=database_engine,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
setup_logging(ps, config, use_worker_options=True)
|
||||||
|
|
||||||
ps.setup()
|
ps.setup()
|
||||||
reactor.addSystemEventTrigger(
|
reactor.addSystemEventTrigger(
|
||||||
"before", "startup", _base.start, ps, config.worker_listeners
|
"before", "startup", _base.start, ps, config.worker_listeners
|
||||||
|
|
|
@ -119,7 +119,7 @@ class ClientReaderServer(HomeServer):
|
||||||
KeyChangesServlet(self).register(resource)
|
KeyChangesServlet(self).register(resource)
|
||||||
VoipRestServlet(self).register(resource)
|
VoipRestServlet(self).register(resource)
|
||||||
PushRuleRestServlet(self).register(resource)
|
PushRuleRestServlet(self).register(resource)
|
||||||
VersionsRestServlet().register(resource)
|
VersionsRestServlet(self).register(resource)
|
||||||
|
|
||||||
resources.update({"/_matrix/client": resource})
|
resources.update({"/_matrix/client": resource})
|
||||||
|
|
||||||
|
@ -179,8 +179,6 @@ def start(config_options):
|
||||||
|
|
||||||
assert config.worker_app == "synapse.app.client_reader"
|
assert config.worker_app == "synapse.app.client_reader"
|
||||||
|
|
||||||
setup_logging(config, use_worker_options=True)
|
|
||||||
|
|
||||||
events.USE_FROZEN_DICTS = config.use_frozen_dicts
|
events.USE_FROZEN_DICTS = config.use_frozen_dicts
|
||||||
|
|
||||||
database_engine = create_engine(config.database_config)
|
database_engine = create_engine(config.database_config)
|
||||||
|
@ -193,6 +191,8 @@ def start(config_options):
|
||||||
database_engine=database_engine,
|
database_engine=database_engine,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
setup_logging(ss, config, use_worker_options=True)
|
||||||
|
|
||||||
ss.setup()
|
ss.setup()
|
||||||
reactor.addSystemEventTrigger(
|
reactor.addSystemEventTrigger(
|
||||||
"before", "startup", _base.start, ss, config.worker_listeners
|
"before", "startup", _base.start, ss, config.worker_listeners
|
||||||
|
|
|
@ -175,8 +175,6 @@ def start(config_options):
|
||||||
|
|
||||||
assert config.worker_replication_http_port is not None
|
assert config.worker_replication_http_port is not None
|
||||||
|
|
||||||
setup_logging(config, use_worker_options=True)
|
|
||||||
|
|
||||||
# This should only be done on the user directory worker or the master
|
# This should only be done on the user directory worker or the master
|
||||||
config.update_user_directory = False
|
config.update_user_directory = False
|
||||||
|
|
||||||
|
@ -192,6 +190,8 @@ def start(config_options):
|
||||||
database_engine=database_engine,
|
database_engine=database_engine,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
setup_logging(ss, config, use_worker_options=True)
|
||||||
|
|
||||||
ss.setup()
|
ss.setup()
|
||||||
reactor.addSystemEventTrigger(
|
reactor.addSystemEventTrigger(
|
||||||
"before", "startup", _base.start, ss, config.worker_listeners
|
"before", "startup", _base.start, ss, config.worker_listeners
|
||||||
|
|
|
@ -160,8 +160,6 @@ def start(config_options):
|
||||||
|
|
||||||
assert config.worker_app == "synapse.app.federation_reader"
|
assert config.worker_app == "synapse.app.federation_reader"
|
||||||
|
|
||||||
setup_logging(config, use_worker_options=True)
|
|
||||||
|
|
||||||
events.USE_FROZEN_DICTS = config.use_frozen_dicts
|
events.USE_FROZEN_DICTS = config.use_frozen_dicts
|
||||||
|
|
||||||
database_engine = create_engine(config.database_config)
|
database_engine = create_engine(config.database_config)
|
||||||
|
@ -174,6 +172,8 @@ def start(config_options):
|
||||||
database_engine=database_engine,
|
database_engine=database_engine,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
setup_logging(ss, config, use_worker_options=True)
|
||||||
|
|
||||||
ss.setup()
|
ss.setup()
|
||||||
reactor.addSystemEventTrigger(
|
reactor.addSystemEventTrigger(
|
||||||
"before", "startup", _base.start, ss, config.worker_listeners
|
"before", "startup", _base.start, ss, config.worker_listeners
|
||||||
|
|
|
@ -171,8 +171,6 @@ def start(config_options):
|
||||||
|
|
||||||
assert config.worker_app == "synapse.app.federation_sender"
|
assert config.worker_app == "synapse.app.federation_sender"
|
||||||
|
|
||||||
setup_logging(config, use_worker_options=True)
|
|
||||||
|
|
||||||
events.USE_FROZEN_DICTS = config.use_frozen_dicts
|
events.USE_FROZEN_DICTS = config.use_frozen_dicts
|
||||||
|
|
||||||
database_engine = create_engine(config.database_config)
|
database_engine = create_engine(config.database_config)
|
||||||
|
@ -197,6 +195,8 @@ def start(config_options):
|
||||||
database_engine=database_engine,
|
database_engine=database_engine,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
setup_logging(ss, config, use_worker_options=True)
|
||||||
|
|
||||||
ss.setup()
|
ss.setup()
|
||||||
reactor.addSystemEventTrigger(
|
reactor.addSystemEventTrigger(
|
||||||
"before", "startup", _base.start, ss, config.worker_listeners
|
"before", "startup", _base.start, ss, config.worker_listeners
|
||||||
|
|
|
@ -70,12 +70,12 @@ class PresenceStatusStubServlet(RestServlet):
|
||||||
except HttpResponseException as e:
|
except HttpResponseException as e:
|
||||||
raise e.to_synapse_error()
|
raise e.to_synapse_error()
|
||||||
|
|
||||||
return (200, result)
|
return 200, result
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def on_PUT(self, request, user_id):
|
def on_PUT(self, request, user_id):
|
||||||
yield self.auth.get_user_by_req(request)
|
yield self.auth.get_user_by_req(request)
|
||||||
return (200, {})
|
return 200, {}
|
||||||
|
|
||||||
|
|
||||||
class KeyUploadServlet(RestServlet):
|
class KeyUploadServlet(RestServlet):
|
||||||
|
@ -126,11 +126,11 @@ class KeyUploadServlet(RestServlet):
|
||||||
self.main_uri + request.uri.decode("ascii"), body, headers=headers
|
self.main_uri + request.uri.decode("ascii"), body, headers=headers
|
||||||
)
|
)
|
||||||
|
|
||||||
return (200, result)
|
return 200, result
|
||||||
else:
|
else:
|
||||||
# Just interested in counts.
|
# Just interested in counts.
|
||||||
result = yield self.store.count_e2e_one_time_keys(user_id, device_id)
|
result = yield self.store.count_e2e_one_time_keys(user_id, device_id)
|
||||||
return (200, {"one_time_key_counts": result})
|
return 200, {"one_time_key_counts": result}
|
||||||
|
|
||||||
|
|
||||||
class FrontendProxySlavedStore(
|
class FrontendProxySlavedStore(
|
||||||
|
@ -232,8 +232,6 @@ def start(config_options):
|
||||||
|
|
||||||
assert config.worker_main_http_uri is not None
|
assert config.worker_main_http_uri is not None
|
||||||
|
|
||||||
setup_logging(config, use_worker_options=True)
|
|
||||||
|
|
||||||
events.USE_FROZEN_DICTS = config.use_frozen_dicts
|
events.USE_FROZEN_DICTS = config.use_frozen_dicts
|
||||||
|
|
||||||
database_engine = create_engine(config.database_config)
|
database_engine = create_engine(config.database_config)
|
||||||
|
@ -246,6 +244,8 @@ def start(config_options):
|
||||||
database_engine=database_engine,
|
database_engine=database_engine,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
setup_logging(ss, config, use_worker_options=True)
|
||||||
|
|
||||||
ss.setup()
|
ss.setup()
|
||||||
reactor.addSystemEventTrigger(
|
reactor.addSystemEventTrigger(
|
||||||
"before", "startup", _base.start, ss, config.worker_listeners
|
"before", "startup", _base.start, ss, config.worker_listeners
|
||||||
|
|
|
@ -341,8 +341,6 @@ def setup(config_options):
|
||||||
# generating config files and shouldn't try to continue.
|
# generating config files and shouldn't try to continue.
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
synapse.config.logger.setup_logging(config, use_worker_options=False)
|
|
||||||
|
|
||||||
events.USE_FROZEN_DICTS = config.use_frozen_dicts
|
events.USE_FROZEN_DICTS = config.use_frozen_dicts
|
||||||
|
|
||||||
database_engine = create_engine(config.database_config)
|
database_engine = create_engine(config.database_config)
|
||||||
|
@ -356,6 +354,8 @@ def setup(config_options):
|
||||||
database_engine=database_engine,
|
database_engine=database_engine,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
synapse.config.logger.setup_logging(hs, config, use_worker_options=False)
|
||||||
|
|
||||||
logger.info("Preparing database: %s...", config.database_config["name"])
|
logger.info("Preparing database: %s...", config.database_config["name"])
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -561,10 +561,12 @@ def run(hs):
|
||||||
|
|
||||||
stats["database_engine"] = hs.get_datastore().database_engine_name
|
stats["database_engine"] = hs.get_datastore().database_engine_name
|
||||||
stats["database_server_version"] = hs.get_datastore().get_server_version()
|
stats["database_server_version"] = hs.get_datastore().get_server_version()
|
||||||
logger.info("Reporting stats to matrix.org: %s" % (stats,))
|
logger.info(
|
||||||
|
"Reporting stats to %s: %s" % (hs.config.report_stats_endpoint, stats)
|
||||||
|
)
|
||||||
try:
|
try:
|
||||||
yield hs.get_simple_http_client().put_json(
|
yield hs.get_simple_http_client().put_json(
|
||||||
"https://matrix.org/report-usage-stats/push", stats
|
hs.config.report_stats_endpoint, stats
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warn("Error reporting stats: %s", e)
|
logger.warn("Error reporting stats: %s", e)
|
||||||
|
|
|
@ -155,8 +155,6 @@ def start(config_options):
|
||||||
"Please add ``enable_media_repo: false`` to the main config\n"
|
"Please add ``enable_media_repo: false`` to the main config\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
setup_logging(config, use_worker_options=True)
|
|
||||||
|
|
||||||
events.USE_FROZEN_DICTS = config.use_frozen_dicts
|
events.USE_FROZEN_DICTS = config.use_frozen_dicts
|
||||||
|
|
||||||
database_engine = create_engine(config.database_config)
|
database_engine = create_engine(config.database_config)
|
||||||
|
@ -169,6 +167,8 @@ def start(config_options):
|
||||||
database_engine=database_engine,
|
database_engine=database_engine,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
setup_logging(ss, config, use_worker_options=True)
|
||||||
|
|
||||||
ss.setup()
|
ss.setup()
|
||||||
reactor.addSystemEventTrigger(
|
reactor.addSystemEventTrigger(
|
||||||
"before", "startup", _base.start, ss, config.worker_listeners
|
"before", "startup", _base.start, ss, config.worker_listeners
|
||||||
|
|
|
@ -184,8 +184,6 @@ def start(config_options):
|
||||||
|
|
||||||
assert config.worker_app == "synapse.app.pusher"
|
assert config.worker_app == "synapse.app.pusher"
|
||||||
|
|
||||||
setup_logging(config, use_worker_options=True)
|
|
||||||
|
|
||||||
events.USE_FROZEN_DICTS = config.use_frozen_dicts
|
events.USE_FROZEN_DICTS = config.use_frozen_dicts
|
||||||
|
|
||||||
if config.start_pushers:
|
if config.start_pushers:
|
||||||
|
@ -210,6 +208,8 @@ def start(config_options):
|
||||||
database_engine=database_engine,
|
database_engine=database_engine,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
setup_logging(ps, config, use_worker_options=True)
|
||||||
|
|
||||||
ps.setup()
|
ps.setup()
|
||||||
|
|
||||||
def start():
|
def start():
|
||||||
|
|
|
@ -435,8 +435,6 @@ def start(config_options):
|
||||||
|
|
||||||
assert config.worker_app == "synapse.app.synchrotron"
|
assert config.worker_app == "synapse.app.synchrotron"
|
||||||
|
|
||||||
setup_logging(config, use_worker_options=True)
|
|
||||||
|
|
||||||
synapse.events.USE_FROZEN_DICTS = config.use_frozen_dicts
|
synapse.events.USE_FROZEN_DICTS = config.use_frozen_dicts
|
||||||
|
|
||||||
database_engine = create_engine(config.database_config)
|
database_engine = create_engine(config.database_config)
|
||||||
|
@ -450,6 +448,8 @@ def start(config_options):
|
||||||
application_service_handler=SynchrotronApplicationService(),
|
application_service_handler=SynchrotronApplicationService(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
setup_logging(ss, config, use_worker_options=True)
|
||||||
|
|
||||||
ss.setup()
|
ss.setup()
|
||||||
reactor.addSystemEventTrigger(
|
reactor.addSystemEventTrigger(
|
||||||
"before", "startup", _base.start, ss, config.worker_listeners
|
"before", "startup", _base.start, ss, config.worker_listeners
|
||||||
|
|
|
@ -197,8 +197,6 @@ def start(config_options):
|
||||||
|
|
||||||
assert config.worker_app == "synapse.app.user_dir"
|
assert config.worker_app == "synapse.app.user_dir"
|
||||||
|
|
||||||
setup_logging(config, use_worker_options=True)
|
|
||||||
|
|
||||||
events.USE_FROZEN_DICTS = config.use_frozen_dicts
|
events.USE_FROZEN_DICTS = config.use_frozen_dicts
|
||||||
|
|
||||||
database_engine = create_engine(config.database_config)
|
database_engine = create_engine(config.database_config)
|
||||||
|
@ -223,6 +221,8 @@ def start(config_options):
|
||||||
database_engine=database_engine,
|
database_engine=database_engine,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
setup_logging(ss, config, use_worker_options=True)
|
||||||
|
|
||||||
ss.setup()
|
ss.setup()
|
||||||
reactor.addSystemEventTrigger(
|
reactor.addSystemEventTrigger(
|
||||||
"before", "startup", _base.start, ss, config.worker_listeners
|
"before", "startup", _base.start, ss, config.worker_listeners
|
||||||
|
|
|
@ -107,7 +107,6 @@ class ApplicationServiceApi(SimpleHttpClient):
|
||||||
except CodeMessageException as e:
|
except CodeMessageException as e:
|
||||||
if e.code == 404:
|
if e.code == 404:
|
||||||
return False
|
return False
|
||||||
return
|
|
||||||
logger.warning("query_user to %s received %s", uri, e.code)
|
logger.warning("query_user to %s received %s", uri, e.code)
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
logger.warning("query_user to %s threw exception %s", uri, ex)
|
logger.warning("query_user to %s threw exception %s", uri, ex)
|
||||||
|
@ -127,7 +126,6 @@ class ApplicationServiceApi(SimpleHttpClient):
|
||||||
logger.warning("query_alias to %s received %s", uri, e.code)
|
logger.warning("query_alias to %s received %s", uri, e.code)
|
||||||
if e.code == 404:
|
if e.code == 404:
|
||||||
return False
|
return False
|
||||||
return
|
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
logger.warning("query_alias to %s threw exception %s", uri, ex)
|
logger.warning("query_alias to %s threw exception %s", uri, ex)
|
||||||
return False
|
return False
|
||||||
|
@ -230,7 +228,6 @@ class ApplicationServiceApi(SimpleHttpClient):
|
||||||
sent_transactions_counter.labels(service.id).inc()
|
sent_transactions_counter.labels(service.id).inc()
|
||||||
sent_events_counter.labels(service.id).inc(len(events))
|
sent_events_counter.labels(service.id).inc(len(events))
|
||||||
return True
|
return True
|
||||||
return
|
|
||||||
except CodeMessageException as e:
|
except CodeMessageException as e:
|
||||||
logger.warning("push_bulk to %s received %s", uri, e.code)
|
logger.warning("push_bulk to %s received %s", uri, e.code)
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
|
|
|
@ -70,35 +70,37 @@ class ApplicationServiceScheduler(object):
|
||||||
self.store = hs.get_datastore()
|
self.store = hs.get_datastore()
|
||||||
self.as_api = hs.get_application_service_api()
|
self.as_api = hs.get_application_service_api()
|
||||||
|
|
||||||
def create_recoverer(service, callback):
|
self.txn_ctrl = _TransactionController(self.clock, self.store, self.as_api)
|
||||||
return _Recoverer(self.clock, self.store, self.as_api, service, callback)
|
|
||||||
|
|
||||||
self.txn_ctrl = _TransactionController(
|
|
||||||
self.clock, self.store, self.as_api, create_recoverer
|
|
||||||
)
|
|
||||||
self.queuer = _ServiceQueuer(self.txn_ctrl, self.clock)
|
self.queuer = _ServiceQueuer(self.txn_ctrl, self.clock)
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def start(self):
|
def start(self):
|
||||||
logger.info("Starting appservice scheduler")
|
logger.info("Starting appservice scheduler")
|
||||||
|
|
||||||
# check for any DOWN ASes and start recoverers for them.
|
# check for any DOWN ASes and start recoverers for them.
|
||||||
recoverers = yield _Recoverer.start(
|
services = yield self.store.get_appservices_by_state(
|
||||||
self.clock, self.store, self.as_api, self.txn_ctrl.on_recovered
|
ApplicationServiceState.DOWN
|
||||||
)
|
)
|
||||||
self.txn_ctrl.add_recoverers(recoverers)
|
|
||||||
|
for service in services:
|
||||||
|
self.txn_ctrl.start_recoverer(service)
|
||||||
|
|
||||||
def submit_event_for_as(self, service, event):
|
def submit_event_for_as(self, service, event):
|
||||||
self.queuer.enqueue(service, event)
|
self.queuer.enqueue(service, event)
|
||||||
|
|
||||||
|
|
||||||
class _ServiceQueuer(object):
|
class _ServiceQueuer(object):
|
||||||
"""Queues events for the same application service together, sending
|
"""Queue of events waiting to be sent to appservices.
|
||||||
transactions as soon as possible. Once a transaction is sent successfully,
|
|
||||||
this schedules any other events in the queue to run.
|
Groups events into transactions per-appservice, and sends them on to the
|
||||||
|
TransactionController. Makes sure that we only have one transaction in flight per
|
||||||
|
appservice at a given time.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, txn_ctrl, clock):
|
def __init__(self, txn_ctrl, clock):
|
||||||
self.queued_events = {} # dict of {service_id: [events]}
|
self.queued_events = {} # dict of {service_id: [events]}
|
||||||
|
|
||||||
|
# the appservices which currently have a transaction in flight
|
||||||
self.requests_in_flight = set()
|
self.requests_in_flight = set()
|
||||||
self.txn_ctrl = txn_ctrl
|
self.txn_ctrl = txn_ctrl
|
||||||
self.clock = clock
|
self.clock = clock
|
||||||
|
@ -136,13 +138,29 @@ class _ServiceQueuer(object):
|
||||||
|
|
||||||
|
|
||||||
class _TransactionController(object):
|
class _TransactionController(object):
|
||||||
def __init__(self, clock, store, as_api, recoverer_fn):
|
"""Transaction manager.
|
||||||
|
|
||||||
|
Builds AppServiceTransactions and runs their lifecycle. Also starts a Recoverer
|
||||||
|
if a transaction fails.
|
||||||
|
|
||||||
|
(Note we have only have one of these in the homeserver.)
|
||||||
|
|
||||||
|
Args:
|
||||||
|
clock (synapse.util.Clock):
|
||||||
|
store (synapse.storage.DataStore):
|
||||||
|
as_api (synapse.appservice.api.ApplicationServiceApi):
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, clock, store, as_api):
|
||||||
self.clock = clock
|
self.clock = clock
|
||||||
self.store = store
|
self.store = store
|
||||||
self.as_api = as_api
|
self.as_api = as_api
|
||||||
self.recoverer_fn = recoverer_fn
|
|
||||||
# keep track of how many recoverers there are
|
# map from service id to recoverer instance
|
||||||
self.recoverers = []
|
self.recoverers = {}
|
||||||
|
|
||||||
|
# for UTs
|
||||||
|
self.RECOVERER_CLASS = _Recoverer
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def send(self, service, events):
|
def send(self, service, events):
|
||||||
|
@ -154,42 +172,45 @@ class _TransactionController(object):
|
||||||
if sent:
|
if sent:
|
||||||
yield txn.complete(self.store)
|
yield txn.complete(self.store)
|
||||||
else:
|
else:
|
||||||
run_in_background(self._start_recoverer, service)
|
run_in_background(self._on_txn_fail, service)
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception("Error creating appservice transaction")
|
logger.exception("Error creating appservice transaction")
|
||||||
run_in_background(self._start_recoverer, service)
|
run_in_background(self._on_txn_fail, service)
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def on_recovered(self, recoverer):
|
def on_recovered(self, recoverer):
|
||||||
self.recoverers.remove(recoverer)
|
|
||||||
logger.info(
|
logger.info(
|
||||||
"Successfully recovered application service AS ID %s", recoverer.service.id
|
"Successfully recovered application service AS ID %s", recoverer.service.id
|
||||||
)
|
)
|
||||||
|
self.recoverers.pop(recoverer.service.id)
|
||||||
logger.info("Remaining active recoverers: %s", len(self.recoverers))
|
logger.info("Remaining active recoverers: %s", len(self.recoverers))
|
||||||
yield self.store.set_appservice_state(
|
yield self.store.set_appservice_state(
|
||||||
recoverer.service, ApplicationServiceState.UP
|
recoverer.service, ApplicationServiceState.UP
|
||||||
)
|
)
|
||||||
|
|
||||||
def add_recoverers(self, recoverers):
|
|
||||||
for r in recoverers:
|
|
||||||
self.recoverers.append(r)
|
|
||||||
if len(recoverers) > 0:
|
|
||||||
logger.info("New active recoverers: %s", len(self.recoverers))
|
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def _start_recoverer(self, service):
|
def _on_txn_fail(self, service):
|
||||||
try:
|
try:
|
||||||
yield self.store.set_appservice_state(service, ApplicationServiceState.DOWN)
|
yield self.store.set_appservice_state(service, ApplicationServiceState.DOWN)
|
||||||
logger.info(
|
self.start_recoverer(service)
|
||||||
"Application service falling behind. Starting recoverer. AS ID %s",
|
|
||||||
service.id,
|
|
||||||
)
|
|
||||||
recoverer = self.recoverer_fn(service, self.on_recovered)
|
|
||||||
self.add_recoverers([recoverer])
|
|
||||||
recoverer.recover()
|
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception("Error starting AS recoverer")
|
logger.exception("Error starting AS recoverer")
|
||||||
|
|
||||||
|
def start_recoverer(self, service):
|
||||||
|
"""Start a Recoverer for the given service
|
||||||
|
|
||||||
|
Args:
|
||||||
|
service (synapse.appservice.ApplicationService):
|
||||||
|
"""
|
||||||
|
logger.info("Starting recoverer for AS ID %s", service.id)
|
||||||
|
assert service.id not in self.recoverers
|
||||||
|
recoverer = self.RECOVERER_CLASS(
|
||||||
|
self.clock, self.store, self.as_api, service, self.on_recovered
|
||||||
|
)
|
||||||
|
self.recoverers[service.id] = recoverer
|
||||||
|
recoverer.recover()
|
||||||
|
logger.info("Now %i active recoverers", len(self.recoverers))
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def _is_service_up(self, service):
|
def _is_service_up(self, service):
|
||||||
state = yield self.store.get_appservice_state(service)
|
state = yield self.store.get_appservice_state(service)
|
||||||
|
@ -197,18 +218,17 @@ class _TransactionController(object):
|
||||||
|
|
||||||
|
|
||||||
class _Recoverer(object):
|
class _Recoverer(object):
|
||||||
@staticmethod
|
"""Manages retries and backoff for a DOWN appservice.
|
||||||
@defer.inlineCallbacks
|
|
||||||
def start(clock, store, as_api, callback):
|
We have one of these for each appservice which is currently considered DOWN.
|
||||||
services = yield store.get_appservices_by_state(ApplicationServiceState.DOWN)
|
|
||||||
recoverers = [_Recoverer(clock, store, as_api, s, callback) for s in services]
|
Args:
|
||||||
for r in recoverers:
|
clock (synapse.util.Clock):
|
||||||
logger.info(
|
store (synapse.storage.DataStore):
|
||||||
"Starting recoverer for AS ID %s which was marked as " "DOWN",
|
as_api (synapse.appservice.api.ApplicationServiceApi):
|
||||||
r.service.id,
|
service (synapse.appservice.ApplicationService): the service we are managing
|
||||||
)
|
callback (callable[_Recoverer]): called once the service recovers.
|
||||||
r.recover()
|
"""
|
||||||
return recoverers
|
|
||||||
|
|
||||||
def __init__(self, clock, store, as_api, service, callback):
|
def __init__(self, clock, store, as_api, service, callback):
|
||||||
self.clock = clock
|
self.clock = clock
|
||||||
|
@ -224,7 +244,9 @@ class _Recoverer(object):
|
||||||
"as-recoverer-%s" % (self.service.id,), self.retry
|
"as-recoverer-%s" % (self.service.id,), self.retry
|
||||||
)
|
)
|
||||||
|
|
||||||
self.clock.call_later((2 ** self.backoff_counter), _retry)
|
delay = 2 ** self.backoff_counter
|
||||||
|
logger.info("Scheduling retries on %s in %fs", self.service.id, delay)
|
||||||
|
self.clock.call_later(delay, _retry)
|
||||||
|
|
||||||
def _backoff(self):
|
def _backoff(self):
|
||||||
# cap the backoff to be around 8.5min => (2^9) = 512 secs
|
# cap the backoff to be around 8.5min => (2^9) = 512 secs
|
||||||
|
@ -234,25 +256,30 @@ class _Recoverer(object):
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def retry(self):
|
def retry(self):
|
||||||
|
logger.info("Starting retries on %s", self.service.id)
|
||||||
try:
|
try:
|
||||||
txn = yield self.store.get_oldest_unsent_txn(self.service)
|
while True:
|
||||||
if txn:
|
txn = yield self.store.get_oldest_unsent_txn(self.service)
|
||||||
|
if not txn:
|
||||||
|
# nothing left: we're done!
|
||||||
|
self.callback(self)
|
||||||
|
return
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
"Retrying transaction %s for AS ID %s", txn.id, txn.service.id
|
"Retrying transaction %s for AS ID %s", txn.id, txn.service.id
|
||||||
)
|
)
|
||||||
sent = yield txn.send(self.as_api)
|
sent = yield txn.send(self.as_api)
|
||||||
if sent:
|
if not sent:
|
||||||
yield txn.complete(self.store)
|
break
|
||||||
# reset the backoff counter and retry immediately
|
|
||||||
self.backoff_counter = 1
|
|
||||||
yield self.retry()
|
|
||||||
else:
|
|
||||||
self._backoff()
|
|
||||||
else:
|
|
||||||
self._set_service_recovered()
|
|
||||||
except Exception as e:
|
|
||||||
logger.exception(e)
|
|
||||||
self._backoff()
|
|
||||||
|
|
||||||
def _set_service_recovered(self):
|
yield txn.complete(self.store)
|
||||||
self.callback(self)
|
|
||||||
|
# reset the backoff counter and then process the next transaction
|
||||||
|
self.backoff_counter = 1
|
||||||
|
|
||||||
|
except Exception:
|
||||||
|
logger.exception("Unexpected error running retries")
|
||||||
|
|
||||||
|
# we didn't manage to send all of the transactions before we got an error of
|
||||||
|
# some flavour: reschedule the next retry.
|
||||||
|
self._backoff()
|
||||||
|
|
|
@ -13,8 +13,9 @@
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
from ._base import ConfigError
|
from ._base import ConfigError, find_config_files
|
||||||
|
|
||||||
# export ConfigError if somebody does import *
|
# export ConfigError and find_config_files if somebody does
|
||||||
|
# import *
|
||||||
# this is largely a fudge to stop PEP8 moaning about the import
|
# this is largely a fudge to stop PEP8 moaning about the import
|
||||||
__all__ = ["ConfigError"]
|
__all__ = ["ConfigError", "find_config_files"]
|
||||||
|
|
|
@ -181,6 +181,11 @@ class Config(object):
|
||||||
generate_secrets=False,
|
generate_secrets=False,
|
||||||
report_stats=None,
|
report_stats=None,
|
||||||
open_private_ports=False,
|
open_private_ports=False,
|
||||||
|
listeners=None,
|
||||||
|
database_conf=None,
|
||||||
|
tls_certificate_path=None,
|
||||||
|
tls_private_key_path=None,
|
||||||
|
acme_domain=None,
|
||||||
):
|
):
|
||||||
"""Build a default configuration file
|
"""Build a default configuration file
|
||||||
|
|
||||||
|
@ -207,6 +212,33 @@ class Config(object):
|
||||||
open_private_ports (bool): True to leave private ports (such as the non-TLS
|
open_private_ports (bool): True to leave private ports (such as the non-TLS
|
||||||
HTTP listener) open to the internet.
|
HTTP listener) open to the internet.
|
||||||
|
|
||||||
|
listeners (list(dict)|None): A list of descriptions of the listeners
|
||||||
|
synapse should start with each of which specifies a port (str), a list of
|
||||||
|
resources (list(str)), tls (bool) and type (str). For example:
|
||||||
|
[{
|
||||||
|
"port": 8448,
|
||||||
|
"resources": [{"names": ["federation"]}],
|
||||||
|
"tls": True,
|
||||||
|
"type": "http",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"port": 443,
|
||||||
|
"resources": [{"names": ["client"]}],
|
||||||
|
"tls": False,
|
||||||
|
"type": "http",
|
||||||
|
}],
|
||||||
|
|
||||||
|
|
||||||
|
database (str|None): The database type to configure, either `psycog2`
|
||||||
|
or `sqlite3`.
|
||||||
|
|
||||||
|
tls_certificate_path (str|None): The path to the tls certificate.
|
||||||
|
|
||||||
|
tls_private_key_path (str|None): The path to the tls private key.
|
||||||
|
|
||||||
|
acme_domain (str|None): The domain acme will try to validate. If
|
||||||
|
specified acme will be enabled.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str: the yaml config file
|
str: the yaml config file
|
||||||
"""
|
"""
|
||||||
|
@ -220,6 +252,11 @@ class Config(object):
|
||||||
generate_secrets=generate_secrets,
|
generate_secrets=generate_secrets,
|
||||||
report_stats=report_stats,
|
report_stats=report_stats,
|
||||||
open_private_ports=open_private_ports,
|
open_private_ports=open_private_ports,
|
||||||
|
listeners=listeners,
|
||||||
|
database_conf=database_conf,
|
||||||
|
tls_certificate_path=tls_certificate_path,
|
||||||
|
tls_private_key_path=tls_private_key_path,
|
||||||
|
acme_domain=acme_domain,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,9 @@
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
import os
|
import os
|
||||||
|
from textwrap import indent
|
||||||
|
|
||||||
|
import yaml
|
||||||
|
|
||||||
from ._base import Config
|
from ._base import Config
|
||||||
|
|
||||||
|
@ -38,20 +41,28 @@ class DatabaseConfig(Config):
|
||||||
|
|
||||||
self.set_databasepath(config.get("database_path"))
|
self.set_databasepath(config.get("database_path"))
|
||||||
|
|
||||||
def generate_config_section(self, data_dir_path, **kwargs):
|
def generate_config_section(self, data_dir_path, database_conf, **kwargs):
|
||||||
database_path = os.path.join(data_dir_path, "homeserver.db")
|
if not database_conf:
|
||||||
return (
|
database_path = os.path.join(data_dir_path, "homeserver.db")
|
||||||
"""\
|
database_conf = (
|
||||||
## Database ##
|
"""# The database engine name
|
||||||
|
|
||||||
database:
|
|
||||||
# The database engine name
|
|
||||||
name: "sqlite3"
|
name: "sqlite3"
|
||||||
# Arguments to pass to the engine
|
# Arguments to pass to the engine
|
||||||
args:
|
args:
|
||||||
# Path to the database
|
# Path to the database
|
||||||
database: "%(database_path)s"
|
database: "%(database_path)s"
|
||||||
|
"""
|
||||||
|
% locals()
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
database_conf = indent(yaml.dump(database_conf), " " * 10).lstrip()
|
||||||
|
|
||||||
|
return (
|
||||||
|
"""\
|
||||||
|
## Database ##
|
||||||
|
|
||||||
|
database:
|
||||||
|
%(database_conf)s
|
||||||
# Number of events to cache in memory.
|
# Number of events to cache in memory.
|
||||||
#
|
#
|
||||||
#event_cache_size: 10K
|
#event_cache_size: 10K
|
||||||
|
|
|
@ -20,6 +20,7 @@ from __future__ import print_function
|
||||||
# This file can't be called email.py because if it is, we cannot:
|
# This file can't be called email.py because if it is, we cannot:
|
||||||
import email.utils
|
import email.utils
|
||||||
import os
|
import os
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
import pkg_resources
|
import pkg_resources
|
||||||
|
|
||||||
|
@ -74,19 +75,48 @@ class EmailConfig(Config):
|
||||||
"renew_at"
|
"renew_at"
|
||||||
)
|
)
|
||||||
|
|
||||||
email_trust_identity_server_for_password_resets = email_config.get(
|
self.threepid_behaviour_email = (
|
||||||
"trust_identity_server_for_password_resets", False
|
# Have Synapse handle the email sending if account_threepid_delegates.email
|
||||||
|
# is not defined
|
||||||
|
# msisdn is currently always remote while Synapse does not support any method of
|
||||||
|
# sending SMS messages
|
||||||
|
ThreepidBehaviour.REMOTE
|
||||||
|
if self.account_threepid_delegate_email
|
||||||
|
else ThreepidBehaviour.LOCAL
|
||||||
)
|
)
|
||||||
self.email_password_reset_behaviour = (
|
# Prior to Synapse v1.4.0, there was another option that defined whether Synapse would
|
||||||
"remote" if email_trust_identity_server_for_password_resets else "local"
|
# use an identity server to password reset tokens on its behalf. We now warn the user
|
||||||
)
|
# if they have this set and tell them to use the updated option, while using a default
|
||||||
self.password_resets_were_disabled_due_to_email_config = False
|
# identity server in the process.
|
||||||
if self.email_password_reset_behaviour == "local" and email_config == {}:
|
self.using_identity_server_from_trusted_list = False
|
||||||
|
if (
|
||||||
|
not self.account_threepid_delegate_email
|
||||||
|
and config.get("trust_identity_server_for_password_resets", False) is True
|
||||||
|
):
|
||||||
|
# Use the first entry in self.trusted_third_party_id_servers instead
|
||||||
|
if self.trusted_third_party_id_servers:
|
||||||
|
# XXX: It's a little confusing that account_threepid_delegate_email is modified
|
||||||
|
# both in RegistrationConfig and here. We should factor this bit out
|
||||||
|
self.account_threepid_delegate_email = self.trusted_third_party_id_servers[
|
||||||
|
0
|
||||||
|
]
|
||||||
|
self.using_identity_server_from_trusted_list = True
|
||||||
|
else:
|
||||||
|
raise ConfigError(
|
||||||
|
"Attempted to use an identity server from"
|
||||||
|
'"trusted_third_party_id_servers" but it is empty.'
|
||||||
|
)
|
||||||
|
|
||||||
|
self.local_threepid_handling_disabled_due_to_email_config = False
|
||||||
|
if (
|
||||||
|
self.threepid_behaviour_email == ThreepidBehaviour.LOCAL
|
||||||
|
and email_config == {}
|
||||||
|
):
|
||||||
# We cannot warn the user this has happened here
|
# We cannot warn the user this has happened here
|
||||||
# Instead do so when a user attempts to reset their password
|
# Instead do so when a user attempts to reset their password
|
||||||
self.password_resets_were_disabled_due_to_email_config = True
|
self.local_threepid_handling_disabled_due_to_email_config = True
|
||||||
|
|
||||||
self.email_password_reset_behaviour = "off"
|
self.threepid_behaviour_email = ThreepidBehaviour.OFF
|
||||||
|
|
||||||
# Get lifetime of a validation token in milliseconds
|
# Get lifetime of a validation token in milliseconds
|
||||||
self.email_validation_token_lifetime = self.parse_duration(
|
self.email_validation_token_lifetime = self.parse_duration(
|
||||||
|
@ -96,7 +126,7 @@ class EmailConfig(Config):
|
||||||
if (
|
if (
|
||||||
self.email_enable_notifs
|
self.email_enable_notifs
|
||||||
or account_validity_renewal_enabled
|
or account_validity_renewal_enabled
|
||||||
or self.email_password_reset_behaviour == "local"
|
or self.threepid_behaviour_email == ThreepidBehaviour.LOCAL
|
||||||
):
|
):
|
||||||
# make sure we can import the required deps
|
# make sure we can import the required deps
|
||||||
import jinja2
|
import jinja2
|
||||||
|
@ -106,7 +136,7 @@ class EmailConfig(Config):
|
||||||
jinja2
|
jinja2
|
||||||
bleach
|
bleach
|
||||||
|
|
||||||
if self.email_password_reset_behaviour == "local":
|
if self.threepid_behaviour_email == ThreepidBehaviour.LOCAL:
|
||||||
required = ["smtp_host", "smtp_port", "notif_from"]
|
required = ["smtp_host", "smtp_port", "notif_from"]
|
||||||
|
|
||||||
missing = []
|
missing = []
|
||||||
|
@ -115,7 +145,7 @@ class EmailConfig(Config):
|
||||||
missing.append("email." + k)
|
missing.append("email." + k)
|
||||||
|
|
||||||
if config.get("public_baseurl") is None:
|
if config.get("public_baseurl") is None:
|
||||||
missing.append("public_base_url")
|
missing.append("public_baseurl")
|
||||||
|
|
||||||
if len(missing) > 0:
|
if len(missing) > 0:
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
|
@ -125,28 +155,63 @@ class EmailConfig(Config):
|
||||||
% (", ".join(missing),)
|
% (", ".join(missing),)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Templates for password reset emails
|
# These email templates have placeholders in them, and thus must be
|
||||||
|
# parsed using a templating engine during a request
|
||||||
self.email_password_reset_template_html = email_config.get(
|
self.email_password_reset_template_html = email_config.get(
|
||||||
"password_reset_template_html", "password_reset.html"
|
"password_reset_template_html", "password_reset.html"
|
||||||
)
|
)
|
||||||
self.email_password_reset_template_text = email_config.get(
|
self.email_password_reset_template_text = email_config.get(
|
||||||
"password_reset_template_text", "password_reset.txt"
|
"password_reset_template_text", "password_reset.txt"
|
||||||
)
|
)
|
||||||
self.email_password_reset_failure_template = email_config.get(
|
self.email_registration_template_html = email_config.get(
|
||||||
"password_reset_failure_template", "password_reset_failure.html"
|
"registration_template_html", "registration.html"
|
||||||
)
|
)
|
||||||
# This template does not support any replaceable variables, so we will
|
self.email_registration_template_text = email_config.get(
|
||||||
# read it from the disk once during setup
|
"registration_template_text", "registration.txt"
|
||||||
email_password_reset_success_template = email_config.get(
|
)
|
||||||
"password_reset_success_template", "password_reset_success.html"
|
self.email_add_threepid_template_html = email_config.get(
|
||||||
|
"add_threepid_template_html", "add_threepid.html"
|
||||||
|
)
|
||||||
|
self.email_add_threepid_template_text = email_config.get(
|
||||||
|
"add_threepid_template_text", "add_threepid.txt"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.email_password_reset_template_failure_html = email_config.get(
|
||||||
|
"password_reset_template_failure_html", "password_reset_failure.html"
|
||||||
|
)
|
||||||
|
self.email_registration_template_failure_html = email_config.get(
|
||||||
|
"registration_template_failure_html", "registration_failure.html"
|
||||||
|
)
|
||||||
|
self.email_add_threepid_template_failure_html = email_config.get(
|
||||||
|
"add_threepid_template_failure_html", "add_threepid_failure.html"
|
||||||
|
)
|
||||||
|
|
||||||
|
# These templates do not support any placeholder variables, so we
|
||||||
|
# will read them from disk once during setup
|
||||||
|
email_password_reset_template_success_html = email_config.get(
|
||||||
|
"password_reset_template_success_html", "password_reset_success.html"
|
||||||
|
)
|
||||||
|
email_registration_template_success_html = email_config.get(
|
||||||
|
"registration_template_success_html", "registration_success.html"
|
||||||
|
)
|
||||||
|
email_add_threepid_template_success_html = email_config.get(
|
||||||
|
"add_threepid_template_success_html", "add_threepid_success.html"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Check templates exist
|
# Check templates exist
|
||||||
for f in [
|
for f in [
|
||||||
self.email_password_reset_template_html,
|
self.email_password_reset_template_html,
|
||||||
self.email_password_reset_template_text,
|
self.email_password_reset_template_text,
|
||||||
self.email_password_reset_failure_template,
|
self.email_registration_template_html,
|
||||||
email_password_reset_success_template,
|
self.email_registration_template_text,
|
||||||
|
self.email_add_threepid_template_html,
|
||||||
|
self.email_add_threepid_template_text,
|
||||||
|
self.email_password_reset_template_failure_html,
|
||||||
|
self.email_registration_template_failure_html,
|
||||||
|
self.email_add_threepid_template_failure_html,
|
||||||
|
email_password_reset_template_success_html,
|
||||||
|
email_registration_template_success_html,
|
||||||
|
email_add_threepid_template_success_html,
|
||||||
]:
|
]:
|
||||||
p = os.path.join(self.email_template_dir, f)
|
p = os.path.join(self.email_template_dir, f)
|
||||||
if not os.path.isfile(p):
|
if not os.path.isfile(p):
|
||||||
|
@ -154,11 +219,23 @@ class EmailConfig(Config):
|
||||||
|
|
||||||
# Retrieve content of web templates
|
# Retrieve content of web templates
|
||||||
filepath = os.path.join(
|
filepath = os.path.join(
|
||||||
self.email_template_dir, email_password_reset_success_template
|
self.email_template_dir, email_password_reset_template_success_html
|
||||||
)
|
)
|
||||||
self.email_password_reset_success_html_content = self.read_file(
|
self.email_password_reset_template_success_html = self.read_file(
|
||||||
filepath, "email.password_reset_template_success_html"
|
filepath, "email.password_reset_template_success_html"
|
||||||
)
|
)
|
||||||
|
filepath = os.path.join(
|
||||||
|
self.email_template_dir, email_registration_template_success_html
|
||||||
|
)
|
||||||
|
self.email_registration_template_success_html_content = self.read_file(
|
||||||
|
filepath, "email.registration_template_success_html"
|
||||||
|
)
|
||||||
|
filepath = os.path.join(
|
||||||
|
self.email_template_dir, email_add_threepid_template_success_html
|
||||||
|
)
|
||||||
|
self.email_add_threepid_template_success_html_content = self.read_file(
|
||||||
|
filepath, "email.add_threepid_template_success_html"
|
||||||
|
)
|
||||||
|
|
||||||
if self.email_enable_notifs:
|
if self.email_enable_notifs:
|
||||||
required = [
|
required = [
|
||||||
|
@ -239,19 +316,6 @@ class EmailConfig(Config):
|
||||||
# #
|
# #
|
||||||
# riot_base_url: "http://localhost/riot"
|
# riot_base_url: "http://localhost/riot"
|
||||||
#
|
#
|
||||||
# # Enable sending password reset emails via the configured, trusted
|
|
||||||
# # identity servers
|
|
||||||
# #
|
|
||||||
# # IMPORTANT! This will give a malicious or overtaken identity server
|
|
||||||
# # the ability to reset passwords for your users! Make absolutely sure
|
|
||||||
# # that you want to do this! It is strongly recommended that password
|
|
||||||
# # reset emails be sent by the homeserver instead
|
|
||||||
# #
|
|
||||||
# # If this option is set to false and SMTP options have not been
|
|
||||||
# # configured, resetting user passwords via email will be disabled
|
|
||||||
# #
|
|
||||||
# #trust_identity_server_for_password_resets: false
|
|
||||||
#
|
|
||||||
# # Configure the time that a validation email or text message code
|
# # Configure the time that a validation email or text message code
|
||||||
# # will expire after sending
|
# # will expire after sending
|
||||||
# #
|
# #
|
||||||
|
@ -283,9 +347,47 @@ class EmailConfig(Config):
|
||||||
# #password_reset_template_html: password_reset.html
|
# #password_reset_template_html: password_reset.html
|
||||||
# #password_reset_template_text: password_reset.txt
|
# #password_reset_template_text: password_reset.txt
|
||||||
#
|
#
|
||||||
|
# # Templates for registration emails sent by the homeserver
|
||||||
|
# #
|
||||||
|
# #registration_template_html: registration.html
|
||||||
|
# #registration_template_text: registration.txt
|
||||||
|
#
|
||||||
|
# # Templates for validation emails sent by the homeserver when adding an email to
|
||||||
|
# # your user account
|
||||||
|
# #
|
||||||
|
# #add_threepid_template_html: add_threepid.html
|
||||||
|
# #add_threepid_template_text: add_threepid.txt
|
||||||
|
#
|
||||||
# # Templates for password reset success and failure pages that a user
|
# # Templates for password reset success and failure pages that a user
|
||||||
# # will see after attempting to reset their password
|
# # will see after attempting to reset their password
|
||||||
# #
|
# #
|
||||||
# #password_reset_template_success_html: password_reset_success.html
|
# #password_reset_template_success_html: password_reset_success.html
|
||||||
# #password_reset_template_failure_html: password_reset_failure.html
|
# #password_reset_template_failure_html: password_reset_failure.html
|
||||||
|
#
|
||||||
|
# # Templates for registration success and failure pages that a user
|
||||||
|
# # will see after attempting to register using an email or phone
|
||||||
|
# #
|
||||||
|
# #registration_template_success_html: registration_success.html
|
||||||
|
# #registration_template_failure_html: registration_failure.html
|
||||||
|
#
|
||||||
|
# # Templates for success and failure pages that a user will see after attempting
|
||||||
|
# # to add an email or phone to their account
|
||||||
|
# #
|
||||||
|
# #add_threepid_success_html: add_threepid_success.html
|
||||||
|
# #add_threepid_failure_html: add_threepid_failure.html
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class ThreepidBehaviour(Enum):
|
||||||
|
"""
|
||||||
|
Enum to define the behaviour of Synapse with regards to when it contacts an identity
|
||||||
|
server for 3pid registration and password resets
|
||||||
|
|
||||||
|
REMOTE = use an external server to send tokens
|
||||||
|
LOCAL = send tokens ourselves
|
||||||
|
OFF = disable registration via 3pid and password resets
|
||||||
|
"""
|
||||||
|
|
||||||
|
REMOTE = "remote"
|
||||||
|
LOCAL = "local"
|
||||||
|
OFF = "off"
|
||||||
|
|
|
@ -50,6 +50,33 @@ and you should enable 'federation_verify_certificates' in your configuration.
|
||||||
If you are *sure* you want to do this, set 'accept_keys_insecurely' on the
|
If you are *sure* you want to do this, set 'accept_keys_insecurely' on the
|
||||||
trusted_key_server configuration."""
|
trusted_key_server configuration."""
|
||||||
|
|
||||||
|
TRUSTED_KEY_SERVER_NOT_CONFIGURED_WARN = """\
|
||||||
|
Synapse requires that a list of trusted key servers are specified in order to
|
||||||
|
provide signing keys for other servers in the federation.
|
||||||
|
|
||||||
|
This homeserver does not have a trusted key server configured in
|
||||||
|
homeserver.yaml and will fall back to the default of 'matrix.org'.
|
||||||
|
|
||||||
|
Trusted key servers should be long-lived and stable which makes matrix.org a
|
||||||
|
good choice for many admins, but some admins may wish to choose another. To
|
||||||
|
suppress this warning, the admin should set 'trusted_key_servers' in
|
||||||
|
homeserver.yaml to their desired key server and 'suppress_key_server_warning'
|
||||||
|
to 'true'.
|
||||||
|
|
||||||
|
In a future release the software-defined default will be removed entirely and
|
||||||
|
the trusted key server will be defined exclusively by the value of
|
||||||
|
'trusted_key_servers'.
|
||||||
|
--------------------------------------------------------------------------------"""
|
||||||
|
|
||||||
|
TRUSTED_KEY_SERVER_CONFIGURED_AS_M_ORG_WARN = """\
|
||||||
|
This server is configured to use 'matrix.org' as its trusted key server via the
|
||||||
|
'trusted_key_servers' config option. 'matrix.org' is a good choice for a key
|
||||||
|
server since it is long-lived, stable and trusted. However, some admins may
|
||||||
|
wish to use another server for this purpose.
|
||||||
|
|
||||||
|
To suppress this warning and continue using 'matrix.org', admins should set
|
||||||
|
'suppress_key_server_warning' to 'true' in homeserver.yaml.
|
||||||
|
--------------------------------------------------------------------------------"""
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -76,7 +103,7 @@ class KeyConfig(Config):
|
||||||
config_dir_path, config["server_name"] + ".signing.key"
|
config_dir_path, config["server_name"] + ".signing.key"
|
||||||
)
|
)
|
||||||
|
|
||||||
self.signing_key = self.read_signing_key(signing_key_path)
|
self.signing_key = self.read_signing_keys(signing_key_path, "signing_key")
|
||||||
|
|
||||||
self.old_signing_keys = self.read_old_signing_keys(
|
self.old_signing_keys = self.read_old_signing_keys(
|
||||||
config.get("old_signing_keys", {})
|
config.get("old_signing_keys", {})
|
||||||
|
@ -85,8 +112,18 @@ class KeyConfig(Config):
|
||||||
config.get("key_refresh_interval", "1d")
|
config.get("key_refresh_interval", "1d")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
suppress_key_server_warning = config.get("suppress_key_server_warning", False)
|
||||||
|
key_server_signing_keys_path = config.get("key_server_signing_keys_path")
|
||||||
|
if key_server_signing_keys_path:
|
||||||
|
self.key_server_signing_keys = self.read_signing_keys(
|
||||||
|
key_server_signing_keys_path, "key_server_signing_keys_path"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.key_server_signing_keys = list(self.signing_key)
|
||||||
|
|
||||||
# if neither trusted_key_servers nor perspectives are given, use the default.
|
# if neither trusted_key_servers nor perspectives are given, use the default.
|
||||||
if "perspectives" not in config and "trusted_key_servers" not in config:
|
if "perspectives" not in config and "trusted_key_servers" not in config:
|
||||||
|
logger.warn(TRUSTED_KEY_SERVER_NOT_CONFIGURED_WARN)
|
||||||
key_servers = [{"server_name": "matrix.org"}]
|
key_servers = [{"server_name": "matrix.org"}]
|
||||||
else:
|
else:
|
||||||
key_servers = config.get("trusted_key_servers", [])
|
key_servers = config.get("trusted_key_servers", [])
|
||||||
|
@ -100,6 +137,11 @@ class KeyConfig(Config):
|
||||||
# merge the 'perspectives' config into the 'trusted_key_servers' config.
|
# merge the 'perspectives' config into the 'trusted_key_servers' config.
|
||||||
key_servers.extend(_perspectives_to_key_servers(config))
|
key_servers.extend(_perspectives_to_key_servers(config))
|
||||||
|
|
||||||
|
if not suppress_key_server_warning and "matrix.org" in (
|
||||||
|
s["server_name"] for s in key_servers
|
||||||
|
):
|
||||||
|
logger.warning(TRUSTED_KEY_SERVER_CONFIGURED_AS_M_ORG_WARN)
|
||||||
|
|
||||||
# list of TrustedKeyServer objects
|
# list of TrustedKeyServer objects
|
||||||
self.key_servers = list(
|
self.key_servers = list(
|
||||||
_parse_key_servers(key_servers, self.federation_verify_certificates)
|
_parse_key_servers(key_servers, self.federation_verify_certificates)
|
||||||
|
@ -182,6 +224,10 @@ class KeyConfig(Config):
|
||||||
# This setting supercedes an older setting named `perspectives`. The old format
|
# This setting supercedes an older setting named `perspectives`. The old format
|
||||||
# is still supported for backwards-compatibility, but it is deprecated.
|
# is still supported for backwards-compatibility, but it is deprecated.
|
||||||
#
|
#
|
||||||
|
# 'trusted_key_servers' defaults to matrix.org, but using it will generate a
|
||||||
|
# warning on start-up. To suppress this warning, set
|
||||||
|
# 'suppress_key_server_warning' to true.
|
||||||
|
#
|
||||||
# Options for each entry in the list include:
|
# Options for each entry in the list include:
|
||||||
#
|
#
|
||||||
# server_name: the name of the server. required.
|
# server_name: the name of the server. required.
|
||||||
|
@ -206,20 +252,40 @@ class KeyConfig(Config):
|
||||||
# "ed25519:auto": "abcdefghijklmnopqrstuvwxyzabcdefghijklmopqr"
|
# "ed25519:auto": "abcdefghijklmnopqrstuvwxyzabcdefghijklmopqr"
|
||||||
# - server_name: "my_other_trusted_server.example.com"
|
# - server_name: "my_other_trusted_server.example.com"
|
||||||
#
|
#
|
||||||
# The default configuration is:
|
trusted_key_servers:
|
||||||
|
- server_name: "matrix.org"
|
||||||
|
|
||||||
|
# Uncomment the following to disable the warning that is emitted when the
|
||||||
|
# trusted_key_servers include 'matrix.org'. See above.
|
||||||
#
|
#
|
||||||
#trusted_key_servers:
|
#suppress_key_server_warning: true
|
||||||
# - server_name: "matrix.org"
|
|
||||||
|
# The signing keys to use when acting as a trusted key server. If not specified
|
||||||
|
# defaults to the server signing key.
|
||||||
|
#
|
||||||
|
# Can contain multiple keys, one per line.
|
||||||
|
#
|
||||||
|
#key_server_signing_keys_path: "key_server_signing_keys.key"
|
||||||
"""
|
"""
|
||||||
% locals()
|
% locals()
|
||||||
)
|
)
|
||||||
|
|
||||||
def read_signing_key(self, signing_key_path):
|
def read_signing_keys(self, signing_key_path, name):
|
||||||
signing_keys = self.read_file(signing_key_path, "signing_key")
|
"""Read the signing keys in the given path.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
signing_key_path (str)
|
||||||
|
name (str): Associated config key name
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list[SigningKey]
|
||||||
|
"""
|
||||||
|
|
||||||
|
signing_keys = self.read_file(signing_key_path, name)
|
||||||
try:
|
try:
|
||||||
return read_signing_keys(signing_keys.splitlines(True))
|
return read_signing_keys(signing_keys.splitlines(True))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise ConfigError("Error reading signing_key: %s" % (str(e)))
|
raise ConfigError("Error reading %s: %s" % (name, str(e)))
|
||||||
|
|
||||||
def read_old_signing_keys(self, old_signing_keys):
|
def read_old_signing_keys(self, old_signing_keys):
|
||||||
keys = {}
|
keys = {}
|
||||||
|
|
|
@ -21,10 +21,19 @@ from string import Template
|
||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
from twisted.logger import STDLibLogObserver, globalLogBeginner
|
from twisted.logger import (
|
||||||
|
ILogObserver,
|
||||||
|
LogBeginner,
|
||||||
|
STDLibLogObserver,
|
||||||
|
globalLogBeginner,
|
||||||
|
)
|
||||||
|
|
||||||
import synapse
|
import synapse
|
||||||
from synapse.app import _base as appbase
|
from synapse.app import _base as appbase
|
||||||
|
from synapse.logging._structured import (
|
||||||
|
reload_structured_logging,
|
||||||
|
setup_structured_logging,
|
||||||
|
)
|
||||||
from synapse.logging.context import LoggingContextFilter
|
from synapse.logging.context import LoggingContextFilter
|
||||||
from synapse.util.versionstring import get_version_string
|
from synapse.util.versionstring import get_version_string
|
||||||
|
|
||||||
|
@ -85,7 +94,8 @@ class LoggingConfig(Config):
|
||||||
"""\
|
"""\
|
||||||
## Logging ##
|
## Logging ##
|
||||||
|
|
||||||
# A yaml python logging config file
|
# A yaml python logging config file as described by
|
||||||
|
# https://docs.python.org/3.7/library/logging.config.html#configuration-dictionary-schema
|
||||||
#
|
#
|
||||||
log_config: "%(log_config)s"
|
log_config: "%(log_config)s"
|
||||||
"""
|
"""
|
||||||
|
@ -119,21 +129,10 @@ class LoggingConfig(Config):
|
||||||
log_config_file.write(DEFAULT_LOG_CONFIG.substitute(log_file=log_file))
|
log_config_file.write(DEFAULT_LOG_CONFIG.substitute(log_file=log_file))
|
||||||
|
|
||||||
|
|
||||||
def setup_logging(config, use_worker_options=False):
|
def _setup_stdlib_logging(config, log_config, logBeginner: LogBeginner):
|
||||||
""" Set up python logging
|
"""
|
||||||
|
Set up Python stdlib logging.
|
||||||
Args:
|
|
||||||
config (LoggingConfig | synapse.config.workers.WorkerConfig):
|
|
||||||
configuration data
|
|
||||||
|
|
||||||
use_worker_options (bool): True to use the 'worker_log_config' option
|
|
||||||
instead of 'log_config'.
|
|
||||||
|
|
||||||
register_sighup (func | None): Function to call to register a
|
|
||||||
sighup handler.
|
|
||||||
"""
|
"""
|
||||||
log_config = config.worker_log_config if use_worker_options else config.log_config
|
|
||||||
|
|
||||||
if log_config is None:
|
if log_config is None:
|
||||||
log_format = (
|
log_format = (
|
||||||
"%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(request)s"
|
"%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(request)s"
|
||||||
|
@ -151,35 +150,10 @@ def setup_logging(config, use_worker_options=False):
|
||||||
handler.addFilter(LoggingContextFilter(request=""))
|
handler.addFilter(LoggingContextFilter(request=""))
|
||||||
logger.addHandler(handler)
|
logger.addHandler(handler)
|
||||||
else:
|
else:
|
||||||
|
logging.config.dictConfig(log_config)
|
||||||
|
|
||||||
def load_log_config():
|
# Route Twisted's native logging through to the standard library logging
|
||||||
with open(log_config, "r") as f:
|
# system.
|
||||||
logging.config.dictConfig(yaml.safe_load(f))
|
|
||||||
|
|
||||||
def sighup(*args):
|
|
||||||
# it might be better to use a file watcher or something for this.
|
|
||||||
load_log_config()
|
|
||||||
logging.info("Reloaded log config from %s due to SIGHUP", log_config)
|
|
||||||
|
|
||||||
load_log_config()
|
|
||||||
appbase.register_sighup(sighup)
|
|
||||||
|
|
||||||
# make sure that the first thing we log is a thing we can grep backwards
|
|
||||||
# for
|
|
||||||
logging.warn("***** STARTING SERVER *****")
|
|
||||||
logging.warn("Server %s version %s", sys.argv[0], get_version_string(synapse))
|
|
||||||
logging.info("Server hostname: %s", config.server_name)
|
|
||||||
|
|
||||||
# It's critical to point twisted's internal logging somewhere, otherwise it
|
|
||||||
# stacks up and leaks kup to 64K object;
|
|
||||||
# see: https://twistedmatrix.com/trac/ticket/8164
|
|
||||||
#
|
|
||||||
# Routing to the python logging framework could be a performance problem if
|
|
||||||
# the handlers blocked for a long time as python.logging is a blocking API
|
|
||||||
# see https://twistedmatrix.com/documents/current/core/howto/logger.html
|
|
||||||
# filed as https://github.com/matrix-org/synapse/issues/1727
|
|
||||||
#
|
|
||||||
# However this may not be too much of a problem if we are just writing to a file.
|
|
||||||
observer = STDLibLogObserver()
|
observer = STDLibLogObserver()
|
||||||
|
|
||||||
def _log(event):
|
def _log(event):
|
||||||
|
@ -196,8 +170,71 @@ def setup_logging(config, use_worker_options=False):
|
||||||
|
|
||||||
return observer(event)
|
return observer(event)
|
||||||
|
|
||||||
globalLogBeginner.beginLoggingTo(
|
logBeginner.beginLoggingTo([_log], redirectStandardIO=not config.no_redirect_stdio)
|
||||||
[_log], redirectStandardIO=not config.no_redirect_stdio
|
|
||||||
)
|
|
||||||
if not config.no_redirect_stdio:
|
if not config.no_redirect_stdio:
|
||||||
print("Redirected stdout/stderr to logs")
|
print("Redirected stdout/stderr to logs")
|
||||||
|
|
||||||
|
return observer
|
||||||
|
|
||||||
|
|
||||||
|
def _reload_stdlib_logging(*args, log_config=None):
|
||||||
|
logger = logging.getLogger("")
|
||||||
|
|
||||||
|
if not log_config:
|
||||||
|
logger.warn("Reloaded a blank config?")
|
||||||
|
|
||||||
|
logging.config.dictConfig(log_config)
|
||||||
|
|
||||||
|
|
||||||
|
def setup_logging(
|
||||||
|
hs, config, use_worker_options=False, logBeginner: LogBeginner = globalLogBeginner
|
||||||
|
) -> ILogObserver:
|
||||||
|
"""
|
||||||
|
Set up the logging subsystem.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
config (LoggingConfig | synapse.config.workers.WorkerConfig):
|
||||||
|
configuration data
|
||||||
|
|
||||||
|
use_worker_options (bool): True to use the 'worker_log_config' option
|
||||||
|
instead of 'log_config'.
|
||||||
|
|
||||||
|
logBeginner: The Twisted logBeginner to use.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The "root" Twisted Logger observer, suitable for sending logs to from a
|
||||||
|
Logger instance.
|
||||||
|
"""
|
||||||
|
log_config = config.worker_log_config if use_worker_options else config.log_config
|
||||||
|
|
||||||
|
def read_config(*args, callback=None):
|
||||||
|
if log_config is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
with open(log_config, "rb") as f:
|
||||||
|
log_config_body = yaml.safe_load(f.read())
|
||||||
|
|
||||||
|
if callback:
|
||||||
|
callback(log_config=log_config_body)
|
||||||
|
logging.info("Reloaded log config from %s due to SIGHUP", log_config)
|
||||||
|
|
||||||
|
return log_config_body
|
||||||
|
|
||||||
|
log_config_body = read_config()
|
||||||
|
|
||||||
|
if log_config_body and log_config_body.get("structured") is True:
|
||||||
|
logger = setup_structured_logging(
|
||||||
|
hs, config, log_config_body, logBeginner=logBeginner
|
||||||
|
)
|
||||||
|
appbase.register_sighup(read_config, callback=reload_structured_logging)
|
||||||
|
else:
|
||||||
|
logger = _setup_stdlib_logging(config, log_config_body, logBeginner=logBeginner)
|
||||||
|
appbase.register_sighup(read_config, callback=_reload_stdlib_logging)
|
||||||
|
|
||||||
|
# make sure that the first thing we log is a thing we can grep backwards
|
||||||
|
# for
|
||||||
|
logging.warn("***** STARTING SERVER *****")
|
||||||
|
logging.warn("Server %s version %s", sys.argv[0], get_version_string(synapse))
|
||||||
|
logging.info("Server hostname: %s", config.server_name)
|
||||||
|
|
||||||
|
return logger
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Copyright 2015, 2016 OpenMarket Ltd
|
# Copyright 2015, 2016 OpenMarket Ltd
|
||||||
|
# Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -13,26 +14,47 @@
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
|
import attr
|
||||||
|
|
||||||
|
from synapse.python_dependencies import DependencyException, check_requirements
|
||||||
|
|
||||||
from ._base import Config, ConfigError
|
from ._base import Config, ConfigError
|
||||||
|
|
||||||
MISSING_SENTRY = """Missing sentry-sdk library. This is required to enable sentry
|
|
||||||
integration.
|
@attr.s
|
||||||
"""
|
class MetricsFlags(object):
|
||||||
|
known_servers = attr.ib(default=False, validator=attr.validators.instance_of(bool))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def all_off(cls):
|
||||||
|
"""
|
||||||
|
Instantiate the flags with all options set to off.
|
||||||
|
"""
|
||||||
|
return cls(**{x.name: False for x in attr.fields(cls)})
|
||||||
|
|
||||||
|
|
||||||
class MetricsConfig(Config):
|
class MetricsConfig(Config):
|
||||||
def read_config(self, config, **kwargs):
|
def read_config(self, config, **kwargs):
|
||||||
self.enable_metrics = config.get("enable_metrics", False)
|
self.enable_metrics = config.get("enable_metrics", False)
|
||||||
self.report_stats = config.get("report_stats", None)
|
self.report_stats = config.get("report_stats", None)
|
||||||
|
self.report_stats_endpoint = config.get(
|
||||||
|
"report_stats_endpoint", "https://matrix.org/report-usage-stats/push"
|
||||||
|
)
|
||||||
self.metrics_port = config.get("metrics_port")
|
self.metrics_port = config.get("metrics_port")
|
||||||
self.metrics_bind_host = config.get("metrics_bind_host", "127.0.0.1")
|
self.metrics_bind_host = config.get("metrics_bind_host", "127.0.0.1")
|
||||||
|
|
||||||
|
if self.enable_metrics:
|
||||||
|
_metrics_config = config.get("metrics_flags") or {}
|
||||||
|
self.metrics_flags = MetricsFlags(**_metrics_config)
|
||||||
|
else:
|
||||||
|
self.metrics_flags = MetricsFlags.all_off()
|
||||||
|
|
||||||
self.sentry_enabled = "sentry" in config
|
self.sentry_enabled = "sentry" in config
|
||||||
if self.sentry_enabled:
|
if self.sentry_enabled:
|
||||||
try:
|
try:
|
||||||
import sentry_sdk # noqa F401
|
check_requirements("sentry")
|
||||||
except ImportError:
|
except DependencyException as e:
|
||||||
raise ConfigError(MISSING_SENTRY)
|
raise ConfigError(e.message)
|
||||||
|
|
||||||
self.sentry_dsn = config["sentry"].get("dsn")
|
self.sentry_dsn = config["sentry"].get("dsn")
|
||||||
if not self.sentry_dsn:
|
if not self.sentry_dsn:
|
||||||
|
@ -58,6 +80,16 @@ class MetricsConfig(Config):
|
||||||
#sentry:
|
#sentry:
|
||||||
# dsn: "..."
|
# dsn: "..."
|
||||||
|
|
||||||
|
# Flags to enable Prometheus metrics which are not suitable to be
|
||||||
|
# enabled by default, either for performance reasons or limited use.
|
||||||
|
#
|
||||||
|
metrics_flags:
|
||||||
|
# Publish synapse_federation_known_servers, a g auge of the number of
|
||||||
|
# servers this homeserver knows about, including itself. May cause
|
||||||
|
# performance problems on large homeservers.
|
||||||
|
#
|
||||||
|
#known_servers: true
|
||||||
|
|
||||||
# Whether or not to report anonymized homeserver usage statistics.
|
# Whether or not to report anonymized homeserver usage statistics.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -66,4 +98,10 @@ class MetricsConfig(Config):
|
||||||
else:
|
else:
|
||||||
res += "report_stats: %s\n" % ("true" if report_stats else "false")
|
res += "report_stats: %s\n" % ("true" if report_stats else "false")
|
||||||
|
|
||||||
|
res += """
|
||||||
|
# The endpoint to report the anonymized homeserver usage statistics to.
|
||||||
|
# Defaults to https://matrix.org/report-usage-stats/push
|
||||||
|
#
|
||||||
|
#report_stats_endpoint: https://example.com/report-usage-stats/push
|
||||||
|
"""
|
||||||
return res
|
return res
|
||||||
|
|
|
@ -80,6 +80,12 @@ class RatelimitConfig(Config):
|
||||||
"federation_rr_transactions_per_room_per_second", 50
|
"federation_rr_transactions_per_room_per_second", 50
|
||||||
)
|
)
|
||||||
|
|
||||||
|
rc_admin_redaction = config.get("rc_admin_redaction")
|
||||||
|
if rc_admin_redaction:
|
||||||
|
self.rc_admin_redaction = RateLimitConfig(rc_admin_redaction)
|
||||||
|
else:
|
||||||
|
self.rc_admin_redaction = None
|
||||||
|
|
||||||
def generate_config_section(self, **kwargs):
|
def generate_config_section(self, **kwargs):
|
||||||
return """\
|
return """\
|
||||||
## Ratelimiting ##
|
## Ratelimiting ##
|
||||||
|
@ -102,6 +108,9 @@ class RatelimitConfig(Config):
|
||||||
# - one for login that ratelimits login requests based on the account the
|
# - one for login that ratelimits login requests based on the account the
|
||||||
# client is attempting to log into, based on the amount of failed login
|
# client is attempting to log into, based on the amount of failed login
|
||||||
# attempts for this account.
|
# attempts for this account.
|
||||||
|
# - one for ratelimiting redactions by room admins. If this is not explicitly
|
||||||
|
# set then it uses the same ratelimiting as per rc_message. This is useful
|
||||||
|
# to allow room admins to deal with abuse quickly.
|
||||||
#
|
#
|
||||||
# The defaults are as shown below.
|
# The defaults are as shown below.
|
||||||
#
|
#
|
||||||
|
@ -123,6 +132,10 @@ class RatelimitConfig(Config):
|
||||||
# failed_attempts:
|
# failed_attempts:
|
||||||
# per_second: 0.17
|
# per_second: 0.17
|
||||||
# burst_count: 3
|
# burst_count: 3
|
||||||
|
#
|
||||||
|
#rc_admin_redaction:
|
||||||
|
# per_second: 1
|
||||||
|
# burst_count: 50
|
||||||
|
|
||||||
|
|
||||||
# Ratelimiting settings for incoming federation
|
# Ratelimiting settings for incoming federation
|
||||||
|
|
|
@ -99,6 +99,10 @@ class RegistrationConfig(Config):
|
||||||
self.trusted_third_party_id_servers = config.get(
|
self.trusted_third_party_id_servers = config.get(
|
||||||
"trusted_third_party_id_servers", ["matrix.org", "vector.im"]
|
"trusted_third_party_id_servers", ["matrix.org", "vector.im"]
|
||||||
)
|
)
|
||||||
|
account_threepid_delegates = config.get("account_threepid_delegates") or {}
|
||||||
|
self.account_threepid_delegate_email = account_threepid_delegates.get("email")
|
||||||
|
self.account_threepid_delegate_msisdn = account_threepid_delegates.get("msisdn")
|
||||||
|
|
||||||
self.default_identity_server = config.get("default_identity_server")
|
self.default_identity_server = config.get("default_identity_server")
|
||||||
self.allow_guest_access = config.get("allow_guest_access", False)
|
self.allow_guest_access = config.get("allow_guest_access", False)
|
||||||
|
|
||||||
|
@ -257,10 +261,44 @@ class RegistrationConfig(Config):
|
||||||
# Also defines the ID server which will be called when an account is
|
# Also defines the ID server which will be called when an account is
|
||||||
# deactivated (one will be picked arbitrarily).
|
# deactivated (one will be picked arbitrarily).
|
||||||
#
|
#
|
||||||
|
# Note: This option is deprecated. Since v0.99.4, Synapse has tracked which identity
|
||||||
|
# server a 3PID has been bound to. For 3PIDs bound before then, Synapse runs a
|
||||||
|
# background migration script, informing itself that the identity server all of its
|
||||||
|
# 3PIDs have been bound to is likely one of the below.
|
||||||
|
#
|
||||||
|
# As of Synapse v1.4.0, all other functionality of this option has been deprecated, and
|
||||||
|
# it is now solely used for the purposes of the background migration script, and can be
|
||||||
|
# removed once it has run.
|
||||||
#trusted_third_party_id_servers:
|
#trusted_third_party_id_servers:
|
||||||
# - matrix.org
|
# - matrix.org
|
||||||
# - vector.im
|
# - vector.im
|
||||||
|
|
||||||
|
# Handle threepid (email/phone etc) registration and password resets through a set of
|
||||||
|
# *trusted* identity servers. Note that this allows the configured identity server to
|
||||||
|
# reset passwords for accounts!
|
||||||
|
#
|
||||||
|
# Be aware that if `email` is not set, and SMTP options have not been
|
||||||
|
# configured in the email config block, registration and user password resets via
|
||||||
|
# email will be globally disabled.
|
||||||
|
#
|
||||||
|
# Additionally, if `msisdn` is not set, registration and password resets via msisdn
|
||||||
|
# will be disabled regardless. This is due to Synapse currently not supporting any
|
||||||
|
# method of sending SMS messages on its own.
|
||||||
|
#
|
||||||
|
# To enable using an identity server for operations regarding a particular third-party
|
||||||
|
# identifier type, set the value to the URL of that identity server as shown in the
|
||||||
|
# examples below.
|
||||||
|
#
|
||||||
|
# Servers handling the these requests must answer the `/requestToken` endpoints defined
|
||||||
|
# by the Matrix Identity Service API specification:
|
||||||
|
# https://matrix.org/docs/spec/identity_service/latest
|
||||||
|
#
|
||||||
|
# If a delegate is specified, the config option public_baseurl must also be filled out.
|
||||||
|
#
|
||||||
|
account_threepid_delegates:
|
||||||
|
#email: https://example.com # Delegate email sending to example.org
|
||||||
|
#msisdn: http://localhost:8090 # Delegate SMS sending to this local process
|
||||||
|
|
||||||
# Users who register on this homeserver will automatically be joined
|
# Users who register on this homeserver will automatically be joined
|
||||||
# to these rooms
|
# to these rooms
|
||||||
#
|
#
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Copyright 2014, 2015 matrix.org
|
# Copyright 2014, 2015 OpenMarket Ltd
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -16,6 +16,7 @@
|
||||||
import os
|
import os
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
|
|
||||||
|
from synapse.python_dependencies import DependencyException, check_requirements
|
||||||
from synapse.util.module_loader import load_module
|
from synapse.util.module_loader import load_module
|
||||||
|
|
||||||
from ._base import Config, ConfigError
|
from ._base import Config, ConfigError
|
||||||
|
@ -34,17 +35,6 @@ THUMBNAIL_SIZE_YAML = """\
|
||||||
# method: %(method)s
|
# method: %(method)s
|
||||||
"""
|
"""
|
||||||
|
|
||||||
MISSING_NETADDR = "Missing netaddr library. This is required for URL preview API."
|
|
||||||
|
|
||||||
MISSING_LXML = """Missing lxml library. This is required for URL preview API.
|
|
||||||
|
|
||||||
Install by running:
|
|
||||||
pip install lxml
|
|
||||||
|
|
||||||
Requires libxslt1-dev system package.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
ThumbnailRequirement = namedtuple(
|
ThumbnailRequirement = namedtuple(
|
||||||
"ThumbnailRequirement", ["width", "height", "method", "media_type"]
|
"ThumbnailRequirement", ["width", "height", "method", "media_type"]
|
||||||
)
|
)
|
||||||
|
@ -171,16 +161,10 @@ class ContentRepositoryConfig(Config):
|
||||||
self.url_preview_enabled = config.get("url_preview_enabled", False)
|
self.url_preview_enabled = config.get("url_preview_enabled", False)
|
||||||
if self.url_preview_enabled:
|
if self.url_preview_enabled:
|
||||||
try:
|
try:
|
||||||
import lxml
|
check_requirements("url_preview")
|
||||||
|
|
||||||
lxml # To stop unused lint.
|
except DependencyException as e:
|
||||||
except ImportError:
|
raise ConfigError(e.message)
|
||||||
raise ConfigError(MISSING_LXML)
|
|
||||||
|
|
||||||
try:
|
|
||||||
from netaddr import IPSet
|
|
||||||
except ImportError:
|
|
||||||
raise ConfigError(MISSING_NETADDR)
|
|
||||||
|
|
||||||
if "url_preview_ip_range_blacklist" not in config:
|
if "url_preview_ip_range_blacklist" not in config:
|
||||||
raise ConfigError(
|
raise ConfigError(
|
||||||
|
@ -189,6 +173,9 @@ class ContentRepositoryConfig(Config):
|
||||||
"to work"
|
"to work"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# netaddr is a dependency for url_preview
|
||||||
|
from netaddr import IPSet
|
||||||
|
|
||||||
self.url_preview_ip_range_blacklist = IPSet(
|
self.url_preview_ip_range_blacklist = IPSet(
|
||||||
config["url_preview_ip_range_blacklist"]
|
config["url_preview_ip_range_blacklist"]
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Copyright 2018 New Vector Ltd
|
# Copyright 2018 New Vector Ltd
|
||||||
|
# Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -12,11 +13,47 @@
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
from synapse.python_dependencies import DependencyException, check_requirements
|
from synapse.python_dependencies import DependencyException, check_requirements
|
||||||
|
from synapse.types import (
|
||||||
|
map_username_to_mxid_localpart,
|
||||||
|
mxid_localpart_allowed_characters,
|
||||||
|
)
|
||||||
|
from synapse.util.module_loader import load_python_module
|
||||||
|
|
||||||
from ._base import Config, ConfigError
|
from ._base import Config, ConfigError
|
||||||
|
|
||||||
|
|
||||||
|
def _dict_merge(merge_dict, into_dict):
|
||||||
|
"""Do a deep merge of two dicts
|
||||||
|
|
||||||
|
Recursively merges `merge_dict` into `into_dict`:
|
||||||
|
* For keys where both `merge_dict` and `into_dict` have a dict value, the values
|
||||||
|
are recursively merged
|
||||||
|
* For all other keys, the values in `into_dict` (if any) are overwritten with
|
||||||
|
the value from `merge_dict`.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
merge_dict (dict): dict to merge
|
||||||
|
into_dict (dict): target dict
|
||||||
|
"""
|
||||||
|
for k, v in merge_dict.items():
|
||||||
|
if k not in into_dict:
|
||||||
|
into_dict[k] = v
|
||||||
|
continue
|
||||||
|
|
||||||
|
current_val = into_dict[k]
|
||||||
|
|
||||||
|
if isinstance(v, dict) and isinstance(current_val, dict):
|
||||||
|
_dict_merge(v, current_val)
|
||||||
|
continue
|
||||||
|
|
||||||
|
# otherwise we just overwrite
|
||||||
|
into_dict[k] = v
|
||||||
|
|
||||||
|
|
||||||
class SAML2Config(Config):
|
class SAML2Config(Config):
|
||||||
def read_config(self, config, **kwargs):
|
def read_config(self, config, **kwargs):
|
||||||
self.saml2_enabled = False
|
self.saml2_enabled = False
|
||||||
|
@ -26,6 +63,9 @@ class SAML2Config(Config):
|
||||||
if not saml2_config or not saml2_config.get("enabled", True):
|
if not saml2_config or not saml2_config.get("enabled", True):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if not saml2_config.get("sp_config") and not saml2_config.get("config_path"):
|
||||||
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
check_requirements("saml2")
|
check_requirements("saml2")
|
||||||
except DependencyException as e:
|
except DependencyException as e:
|
||||||
|
@ -33,21 +73,40 @@ class SAML2Config(Config):
|
||||||
|
|
||||||
self.saml2_enabled = True
|
self.saml2_enabled = True
|
||||||
|
|
||||||
import saml2.config
|
self.saml2_mxid_source_attribute = saml2_config.get(
|
||||||
|
"mxid_source_attribute", "uid"
|
||||||
|
)
|
||||||
|
|
||||||
self.saml2_sp_config = saml2.config.SPConfig()
|
self.saml2_grandfathered_mxid_source_attribute = saml2_config.get(
|
||||||
self.saml2_sp_config.load(self._default_saml_config_dict())
|
"grandfathered_mxid_source_attribute", "uid"
|
||||||
self.saml2_sp_config.load(saml2_config.get("sp_config", {}))
|
)
|
||||||
|
|
||||||
|
saml2_config_dict = self._default_saml_config_dict()
|
||||||
|
_dict_merge(
|
||||||
|
merge_dict=saml2_config.get("sp_config", {}), into_dict=saml2_config_dict
|
||||||
|
)
|
||||||
|
|
||||||
config_path = saml2_config.get("config_path", None)
|
config_path = saml2_config.get("config_path", None)
|
||||||
if config_path is not None:
|
if config_path is not None:
|
||||||
self.saml2_sp_config.load_file(config_path)
|
mod = load_python_module(config_path)
|
||||||
|
_dict_merge(merge_dict=mod.CONFIG, into_dict=saml2_config_dict)
|
||||||
|
|
||||||
|
import saml2.config
|
||||||
|
|
||||||
|
self.saml2_sp_config = saml2.config.SPConfig()
|
||||||
|
self.saml2_sp_config.load(saml2_config_dict)
|
||||||
|
|
||||||
# session lifetime: in milliseconds
|
# session lifetime: in milliseconds
|
||||||
self.saml2_session_lifetime = self.parse_duration(
|
self.saml2_session_lifetime = self.parse_duration(
|
||||||
saml2_config.get("saml_session_lifetime", "5m")
|
saml2_config.get("saml_session_lifetime", "5m")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
mapping = saml2_config.get("mxid_mapping", "hexencode")
|
||||||
|
try:
|
||||||
|
self.saml2_mxid_mapper = MXID_MAPPER_MAP[mapping]
|
||||||
|
except KeyError:
|
||||||
|
raise ConfigError("%s is not a known mxid_mapping" % (mapping,))
|
||||||
|
|
||||||
def _default_saml_config_dict(self):
|
def _default_saml_config_dict(self):
|
||||||
import saml2
|
import saml2
|
||||||
|
|
||||||
|
@ -55,6 +114,13 @@ class SAML2Config(Config):
|
||||||
if public_baseurl is None:
|
if public_baseurl is None:
|
||||||
raise ConfigError("saml2_config requires a public_baseurl to be set")
|
raise ConfigError("saml2_config requires a public_baseurl to be set")
|
||||||
|
|
||||||
|
required_attributes = {"uid", self.saml2_mxid_source_attribute}
|
||||||
|
|
||||||
|
optional_attributes = {"displayName"}
|
||||||
|
if self.saml2_grandfathered_mxid_source_attribute:
|
||||||
|
optional_attributes.add(self.saml2_grandfathered_mxid_source_attribute)
|
||||||
|
optional_attributes -= required_attributes
|
||||||
|
|
||||||
metadata_url = public_baseurl + "_matrix/saml2/metadata.xml"
|
metadata_url = public_baseurl + "_matrix/saml2/metadata.xml"
|
||||||
response_url = public_baseurl + "_matrix/saml2/authn_response"
|
response_url = public_baseurl + "_matrix/saml2/authn_response"
|
||||||
return {
|
return {
|
||||||
|
@ -66,8 +132,9 @@ class SAML2Config(Config):
|
||||||
(response_url, saml2.BINDING_HTTP_POST)
|
(response_url, saml2.BINDING_HTTP_POST)
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"required_attributes": ["uid"],
|
"required_attributes": list(required_attributes),
|
||||||
"optional_attributes": ["mail", "surname", "givenname"],
|
"optional_attributes": list(optional_attributes),
|
||||||
|
# "name_id_format": saml2.saml.NAMEID_FORMAT_PERSISTENT,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -76,12 +143,13 @@ class SAML2Config(Config):
|
||||||
return """\
|
return """\
|
||||||
# Enable SAML2 for registration and login. Uses pysaml2.
|
# Enable SAML2 for registration and login. Uses pysaml2.
|
||||||
#
|
#
|
||||||
# `sp_config` is the configuration for the pysaml2 Service Provider.
|
# At least one of `sp_config` or `config_path` must be set in this section to
|
||||||
# See pysaml2 docs for format of config.
|
# enable SAML login.
|
||||||
#
|
#
|
||||||
# Default values will be used for the 'entityid' and 'service' settings,
|
# (You will probably also want to set the following options to `false` to
|
||||||
# so it is not normally necessary to specify them unless you need to
|
# disable the regular login/registration flows:
|
||||||
# override them.
|
# * enable_registration
|
||||||
|
# * password_config.enabled
|
||||||
#
|
#
|
||||||
# Once SAML support is enabled, a metadata file will be exposed at
|
# Once SAML support is enabled, a metadata file will be exposed at
|
||||||
# https://<server>:<port>/_matrix/saml2/metadata.xml, which you may be able to
|
# https://<server>:<port>/_matrix/saml2/metadata.xml, which you may be able to
|
||||||
|
@ -89,52 +157,105 @@ class SAML2Config(Config):
|
||||||
# the IdP to use an ACS location of
|
# the IdP to use an ACS location of
|
||||||
# https://<server>:<port>/_matrix/saml2/authn_response.
|
# https://<server>:<port>/_matrix/saml2/authn_response.
|
||||||
#
|
#
|
||||||
#saml2_config:
|
saml2_config:
|
||||||
# sp_config:
|
# `sp_config` is the configuration for the pysaml2 Service Provider.
|
||||||
# # point this to the IdP's metadata. You can use either a local file or
|
# See pysaml2 docs for format of config.
|
||||||
# # (preferably) a URL.
|
#
|
||||||
# metadata:
|
# Default values will be used for the 'entityid' and 'service' settings,
|
||||||
# #local: ["saml2/idp.xml"]
|
# so it is not normally necessary to specify them unless you need to
|
||||||
# remote:
|
# override them.
|
||||||
# - url: https://our_idp/metadata.xml
|
#
|
||||||
#
|
#sp_config:
|
||||||
# # By default, the user has to go to our login page first. If you'd like to
|
# # point this to the IdP's metadata. You can use either a local file or
|
||||||
# # allow IdP-initiated login, set 'allow_unsolicited: True' in a
|
# # (preferably) a URL.
|
||||||
# # 'service.sp' section:
|
# metadata:
|
||||||
# #
|
# #local: ["saml2/idp.xml"]
|
||||||
# #service:
|
# remote:
|
||||||
# # sp:
|
# - url: https://our_idp/metadata.xml
|
||||||
# # allow_unsolicited: True
|
#
|
||||||
#
|
# # By default, the user has to go to our login page first. If you'd like
|
||||||
# # The examples below are just used to generate our metadata xml, and you
|
# # to allow IdP-initiated login, set 'allow_unsolicited: True' in a
|
||||||
# # may well not need it, depending on your setup. Alternatively you
|
# # 'service.sp' section:
|
||||||
# # may need a whole lot more detail - see the pysaml2 docs!
|
# #
|
||||||
#
|
# #service:
|
||||||
# description: ["My awesome SP", "en"]
|
# # sp:
|
||||||
# name: ["Test SP", "en"]
|
# # allow_unsolicited: true
|
||||||
#
|
#
|
||||||
# organization:
|
# # The examples below are just used to generate our metadata xml, and you
|
||||||
# name: Example com
|
# # may well not need them, depending on your setup. Alternatively you
|
||||||
# display_name:
|
# # may need a whole lot more detail - see the pysaml2 docs!
|
||||||
# - ["Example co", "en"]
|
#
|
||||||
# url: "http://example.com"
|
# description: ["My awesome SP", "en"]
|
||||||
#
|
# name: ["Test SP", "en"]
|
||||||
# contact_person:
|
#
|
||||||
# - given_name: Bob
|
# organization:
|
||||||
# sur_name: "the Sysadmin"
|
# name: Example com
|
||||||
# email_address": ["admin@example.com"]
|
# display_name:
|
||||||
# contact_type": technical
|
# - ["Example co", "en"]
|
||||||
#
|
# url: "http://example.com"
|
||||||
# # Instead of putting the config inline as above, you can specify a
|
#
|
||||||
# # separate pysaml2 configuration file:
|
# contact_person:
|
||||||
# #
|
# - given_name: Bob
|
||||||
# config_path: "%(config_dir_path)s/sp_conf.py"
|
# sur_name: "the Sysadmin"
|
||||||
#
|
# email_address": ["admin@example.com"]
|
||||||
# # the lifetime of a SAML session. This defines how long a user has to
|
# contact_type": technical
|
||||||
# # complete the authentication process, if allow_unsolicited is unset.
|
|
||||||
# # The default is 5 minutes.
|
# Instead of putting the config inline as above, you can specify a
|
||||||
# #
|
# separate pysaml2 configuration file:
|
||||||
# # saml_session_lifetime: 5m
|
#
|
||||||
|
#config_path: "%(config_dir_path)s/sp_conf.py"
|
||||||
|
|
||||||
|
# the lifetime of a SAML session. This defines how long a user has to
|
||||||
|
# complete the authentication process, if allow_unsolicited is unset.
|
||||||
|
# The default is 5 minutes.
|
||||||
|
#
|
||||||
|
#saml_session_lifetime: 5m
|
||||||
|
|
||||||
|
# The SAML attribute (after mapping via the attribute maps) to use to derive
|
||||||
|
# the Matrix ID from. 'uid' by default.
|
||||||
|
#
|
||||||
|
#mxid_source_attribute: displayName
|
||||||
|
|
||||||
|
# The mapping system to use for mapping the saml attribute onto a matrix ID.
|
||||||
|
# Options include:
|
||||||
|
# * 'hexencode' (which maps unpermitted characters to '=xx')
|
||||||
|
# * 'dotreplace' (which replaces unpermitted characters with '.').
|
||||||
|
# The default is 'hexencode'.
|
||||||
|
#
|
||||||
|
#mxid_mapping: dotreplace
|
||||||
|
|
||||||
|
# In previous versions of synapse, the mapping from SAML attribute to MXID was
|
||||||
|
# always calculated dynamically rather than stored in a table. For backwards-
|
||||||
|
# compatibility, we will look for user_ids matching such a pattern before
|
||||||
|
# creating a new account.
|
||||||
|
#
|
||||||
|
# This setting controls the SAML attribute which will be used for this
|
||||||
|
# backwards-compatibility lookup. Typically it should be 'uid', but if the
|
||||||
|
# attribute maps are changed, it may be necessary to change it.
|
||||||
|
#
|
||||||
|
# The default is 'uid'.
|
||||||
|
#
|
||||||
|
#grandfathered_mxid_source_attribute: upn
|
||||||
""" % {
|
""" % {
|
||||||
"config_dir_path": config_dir_path
|
"config_dir_path": config_dir_path
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DOT_REPLACE_PATTERN = re.compile(
|
||||||
|
("[^%s]" % (re.escape("".join(mxid_localpart_allowed_characters)),))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def dot_replace_for_mxid(username: str) -> str:
|
||||||
|
username = username.lower()
|
||||||
|
username = DOT_REPLACE_PATTERN.sub(".", username)
|
||||||
|
|
||||||
|
# regular mxids aren't allowed to start with an underscore either
|
||||||
|
username = re.sub("^_", "", username)
|
||||||
|
return username
|
||||||
|
|
||||||
|
|
||||||
|
MXID_MAPPER_MAP = {
|
||||||
|
"hexencode": map_username_to_mxid_localpart,
|
||||||
|
"dotreplace": dot_replace_for_mxid,
|
||||||
|
}
|
||||||
|
|
|
@ -17,8 +17,11 @@
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import os.path
|
import os.path
|
||||||
|
import re
|
||||||
|
from textwrap import indent
|
||||||
|
|
||||||
import attr
|
import attr
|
||||||
|
import yaml
|
||||||
from netaddr import IPSet
|
from netaddr import IPSet
|
||||||
|
|
||||||
from synapse.api.room_versions import KNOWN_ROOM_VERSIONS
|
from synapse.api.room_versions import KNOWN_ROOM_VERSIONS
|
||||||
|
@ -45,6 +48,13 @@ ROOM_COMPLEXITY_TOO_GREAT = (
|
||||||
"to join this room."
|
"to join this room."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
METRICS_PORT_WARNING = """\
|
||||||
|
The metrics_port configuration option is deprecated in Synapse 0.31 in favour of
|
||||||
|
a listener. Please see
|
||||||
|
https://github.com/matrix-org/synapse/blob/master/docs/metrics-howto.md
|
||||||
|
on how to configure the new listener.
|
||||||
|
--------------------------------------------------------------------------------"""
|
||||||
|
|
||||||
|
|
||||||
class ServerConfig(Config):
|
class ServerConfig(Config):
|
||||||
def read_config(self, config, **kwargs):
|
def read_config(self, config, **kwargs):
|
||||||
|
@ -159,6 +169,23 @@ class ServerConfig(Config):
|
||||||
|
|
||||||
self.mau_trial_days = config.get("mau_trial_days", 0)
|
self.mau_trial_days = config.get("mau_trial_days", 0)
|
||||||
|
|
||||||
|
# How long to keep redacted events in the database in unredacted form
|
||||||
|
# before redacting them.
|
||||||
|
redaction_retention_period = config.get("redaction_retention_period", "7d")
|
||||||
|
if redaction_retention_period is not None:
|
||||||
|
self.redaction_retention_period = self.parse_duration(
|
||||||
|
redaction_retention_period
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.redaction_retention_period = None
|
||||||
|
|
||||||
|
# How long to keep entries in the `users_ips` table.
|
||||||
|
user_ips_max_age = config.get("user_ips_max_age", "28d")
|
||||||
|
if user_ips_max_age is not None:
|
||||||
|
self.user_ips_max_age = self.parse_duration(user_ips_max_age)
|
||||||
|
else:
|
||||||
|
self.user_ips_max_age = None
|
||||||
|
|
||||||
# Options to disable HS
|
# Options to disable HS
|
||||||
self.hs_disabled = config.get("hs_disabled", False)
|
self.hs_disabled = config.get("hs_disabled", False)
|
||||||
self.hs_disabled_message = config.get("hs_disabled_message", "")
|
self.hs_disabled_message = config.get("hs_disabled_message", "")
|
||||||
|
@ -321,14 +348,7 @@ class ServerConfig(Config):
|
||||||
|
|
||||||
metrics_port = config.get("metrics_port")
|
metrics_port = config.get("metrics_port")
|
||||||
if metrics_port:
|
if metrics_port:
|
||||||
logger.warn(
|
logger.warning(METRICS_PORT_WARNING)
|
||||||
(
|
|
||||||
"The metrics_port configuration option is deprecated in Synapse 0.31 "
|
|
||||||
"in favour of a listener. Please see "
|
|
||||||
"http://github.com/matrix-org/synapse/blob/master/docs/metrics-howto.rst"
|
|
||||||
" on how to configure the new listener."
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
self.listeners.append(
|
self.listeners.append(
|
||||||
{
|
{
|
||||||
|
@ -342,17 +362,15 @@ class ServerConfig(Config):
|
||||||
|
|
||||||
_check_resource_config(self.listeners)
|
_check_resource_config(self.listeners)
|
||||||
|
|
||||||
# An experimental option to try and periodically clean up extremities
|
|
||||||
# by sending dummy events.
|
|
||||||
self.cleanup_extremities_with_dummy_events = config.get(
|
self.cleanup_extremities_with_dummy_events = config.get(
|
||||||
"cleanup_extremities_with_dummy_events", False
|
"cleanup_extremities_with_dummy_events", True
|
||||||
)
|
)
|
||||||
|
|
||||||
def has_tls_listener(self):
|
def has_tls_listener(self):
|
||||||
return any(l["tls"] for l in self.listeners)
|
return any(l["tls"] for l in self.listeners)
|
||||||
|
|
||||||
def generate_config_section(
|
def generate_config_section(
|
||||||
self, server_name, data_dir_path, open_private_ports, **kwargs
|
self, server_name, data_dir_path, open_private_ports, listeners, **kwargs
|
||||||
):
|
):
|
||||||
_, bind_port = parse_and_validate_server_name(server_name)
|
_, bind_port = parse_and_validate_server_name(server_name)
|
||||||
if bind_port is not None:
|
if bind_port is not None:
|
||||||
|
@ -366,11 +384,68 @@ class ServerConfig(Config):
|
||||||
# Bring DEFAULT_ROOM_VERSION into the local-scope for use in the
|
# Bring DEFAULT_ROOM_VERSION into the local-scope for use in the
|
||||||
# default config string
|
# default config string
|
||||||
default_room_version = DEFAULT_ROOM_VERSION
|
default_room_version = DEFAULT_ROOM_VERSION
|
||||||
|
secure_listeners = []
|
||||||
|
unsecure_listeners = []
|
||||||
|
private_addresses = ["::1", "127.0.0.1"]
|
||||||
|
if listeners:
|
||||||
|
for listener in listeners:
|
||||||
|
if listener["tls"]:
|
||||||
|
secure_listeners.append(listener)
|
||||||
|
else:
|
||||||
|
# If we don't want open ports we need to bind the listeners
|
||||||
|
# to some address other than 0.0.0.0. Here we chose to use
|
||||||
|
# localhost.
|
||||||
|
# If the addresses are already bound we won't overwrite them
|
||||||
|
# however.
|
||||||
|
if not open_private_ports:
|
||||||
|
listener.setdefault("bind_addresses", private_addresses)
|
||||||
|
|
||||||
unsecure_http_binding = "port: %i\n tls: false" % (unsecure_port,)
|
unsecure_listeners.append(listener)
|
||||||
if not open_private_ports:
|
|
||||||
unsecure_http_binding += (
|
secure_http_bindings = indent(
|
||||||
"\n bind_addresses: ['::1', '127.0.0.1']"
|
yaml.dump(secure_listeners), " " * 10
|
||||||
|
).lstrip()
|
||||||
|
|
||||||
|
unsecure_http_bindings = indent(
|
||||||
|
yaml.dump(unsecure_listeners), " " * 10
|
||||||
|
).lstrip()
|
||||||
|
|
||||||
|
if not unsecure_listeners:
|
||||||
|
unsecure_http_bindings = (
|
||||||
|
"""- port: %(unsecure_port)s
|
||||||
|
tls: false
|
||||||
|
type: http
|
||||||
|
x_forwarded: true"""
|
||||||
|
% locals()
|
||||||
|
)
|
||||||
|
|
||||||
|
if not open_private_ports:
|
||||||
|
unsecure_http_bindings += (
|
||||||
|
"\n bind_addresses: ['::1', '127.0.0.1']"
|
||||||
|
)
|
||||||
|
|
||||||
|
unsecure_http_bindings += """
|
||||||
|
|
||||||
|
resources:
|
||||||
|
- names: [client, federation]
|
||||||
|
compress: false"""
|
||||||
|
|
||||||
|
if listeners:
|
||||||
|
# comment out this block
|
||||||
|
unsecure_http_bindings = "#" + re.sub(
|
||||||
|
"\n {10}",
|
||||||
|
lambda match: match.group(0) + "#",
|
||||||
|
unsecure_http_bindings,
|
||||||
|
)
|
||||||
|
|
||||||
|
if not secure_listeners:
|
||||||
|
secure_http_bindings = (
|
||||||
|
"""#- port: %(bind_port)s
|
||||||
|
# type: http
|
||||||
|
# tls: true
|
||||||
|
# resources:
|
||||||
|
# - names: [client, federation]"""
|
||||||
|
% locals()
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -475,6 +550,9 @@ class ServerConfig(Config):
|
||||||
# blacklist IP address CIDR ranges. If this option is not specified, or
|
# blacklist IP address CIDR ranges. If this option is not specified, or
|
||||||
# specified with an empty list, no ip range blacklist will be enforced.
|
# specified with an empty list, no ip range blacklist will be enforced.
|
||||||
#
|
#
|
||||||
|
# As of Synapse v1.4.0 this option also affects any outbound requests to identity
|
||||||
|
# servers provided by user input.
|
||||||
|
#
|
||||||
# (0.0.0.0 and :: are always blacklisted, whether or not they are explicitly
|
# (0.0.0.0 and :: are always blacklisted, whether or not they are explicitly
|
||||||
# listed here, since they correspond to unroutable addresses.)
|
# listed here, since they correspond to unroutable addresses.)
|
||||||
#
|
#
|
||||||
|
@ -501,8 +579,8 @@ class ServerConfig(Config):
|
||||||
#
|
#
|
||||||
# type: the type of listener. Normally 'http', but other valid options are:
|
# type: the type of listener. Normally 'http', but other valid options are:
|
||||||
# 'manhole' (see docs/manhole.md),
|
# 'manhole' (see docs/manhole.md),
|
||||||
# 'metrics' (see docs/metrics-howto.rst),
|
# 'metrics' (see docs/metrics-howto.md),
|
||||||
# 'replication' (see docs/workers.rst).
|
# 'replication' (see docs/workers.md).
|
||||||
#
|
#
|
||||||
# tls: set to true to enable TLS for this listener. Will use the TLS
|
# tls: set to true to enable TLS for this listener. Will use the TLS
|
||||||
# key/cert specified in tls_private_key_path / tls_certificate_path.
|
# key/cert specified in tls_private_key_path / tls_certificate_path.
|
||||||
|
@ -537,12 +615,12 @@ class ServerConfig(Config):
|
||||||
#
|
#
|
||||||
# media: the media API (/_matrix/media).
|
# media: the media API (/_matrix/media).
|
||||||
#
|
#
|
||||||
# metrics: the metrics interface. See docs/metrics-howto.rst.
|
# metrics: the metrics interface. See docs/metrics-howto.md.
|
||||||
#
|
#
|
||||||
# openid: OpenID authentication.
|
# openid: OpenID authentication.
|
||||||
#
|
#
|
||||||
# replication: the HTTP replication API (/_synapse/replication). See
|
# replication: the HTTP replication API (/_synapse/replication). See
|
||||||
# docs/workers.rst.
|
# docs/workers.md.
|
||||||
#
|
#
|
||||||
# static: static resources under synapse/static (/_matrix/static). (Mostly
|
# static: static resources under synapse/static (/_matrix/static). (Mostly
|
||||||
# useful for 'fallback authentication'.)
|
# useful for 'fallback authentication'.)
|
||||||
|
@ -556,25 +634,15 @@ class ServerConfig(Config):
|
||||||
# will also need to give Synapse a TLS key and certificate: see the TLS section
|
# will also need to give Synapse a TLS key and certificate: see the TLS section
|
||||||
# below.)
|
# below.)
|
||||||
#
|
#
|
||||||
#- port: %(bind_port)s
|
%(secure_http_bindings)s
|
||||||
# type: http
|
|
||||||
# tls: true
|
|
||||||
# resources:
|
|
||||||
# - names: [client, federation]
|
|
||||||
|
|
||||||
# Unsecure HTTP listener: for when matrix traffic passes through a reverse proxy
|
# Unsecure HTTP listener: for when matrix traffic passes through a reverse proxy
|
||||||
# that unwraps TLS.
|
# that unwraps TLS.
|
||||||
#
|
#
|
||||||
# If you plan to use a reverse proxy, please see
|
# If you plan to use a reverse proxy, please see
|
||||||
# https://github.com/matrix-org/synapse/blob/master/docs/reverse_proxy.rst.
|
# https://github.com/matrix-org/synapse/blob/master/docs/reverse_proxy.md.
|
||||||
#
|
#
|
||||||
- %(unsecure_http_binding)s
|
%(unsecure_http_bindings)s
|
||||||
type: http
|
|
||||||
x_forwarded: true
|
|
||||||
|
|
||||||
resources:
|
|
||||||
- names: [client, federation]
|
|
||||||
compress: false
|
|
||||||
|
|
||||||
# example additional_resources:
|
# example additional_resources:
|
||||||
#
|
#
|
||||||
|
@ -668,6 +736,19 @@ class ServerConfig(Config):
|
||||||
# Defaults to 'true'.
|
# Defaults to 'true'.
|
||||||
#
|
#
|
||||||
#allow_per_room_profiles: false
|
#allow_per_room_profiles: false
|
||||||
|
|
||||||
|
# How long to keep redacted events in unredacted form in the database. After
|
||||||
|
# this period redacted events get replaced with their redacted form in the DB.
|
||||||
|
#
|
||||||
|
# Defaults to `7d`. Set to `null` to disable.
|
||||||
|
#
|
||||||
|
#redaction_retention_period: 28d
|
||||||
|
|
||||||
|
# How long to track users' last seen time and IPs in the database.
|
||||||
|
#
|
||||||
|
# Defaults to `28d`. Set to `null` to disable clearing out of old rows.
|
||||||
|
#
|
||||||
|
#user_ips_max_age: 14d
|
||||||
"""
|
"""
|
||||||
% locals()
|
% locals()
|
||||||
)
|
)
|
||||||
|
|
|
@ -27,19 +27,16 @@ class StatsConfig(Config):
|
||||||
|
|
||||||
def read_config(self, config, **kwargs):
|
def read_config(self, config, **kwargs):
|
||||||
self.stats_enabled = True
|
self.stats_enabled = True
|
||||||
self.stats_bucket_size = 86400
|
self.stats_bucket_size = 86400 * 1000
|
||||||
self.stats_retention = sys.maxsize
|
self.stats_retention = sys.maxsize
|
||||||
stats_config = config.get("stats", None)
|
stats_config = config.get("stats", None)
|
||||||
if stats_config:
|
if stats_config:
|
||||||
self.stats_enabled = stats_config.get("enabled", self.stats_enabled)
|
self.stats_enabled = stats_config.get("enabled", self.stats_enabled)
|
||||||
self.stats_bucket_size = (
|
self.stats_bucket_size = self.parse_duration(
|
||||||
self.parse_duration(stats_config.get("bucket_size", "1d")) / 1000
|
stats_config.get("bucket_size", "1d")
|
||||||
)
|
)
|
||||||
self.stats_retention = (
|
self.stats_retention = self.parse_duration(
|
||||||
self.parse_duration(
|
stats_config.get("retention", "%ds" % (sys.maxsize,))
|
||||||
stats_config.get("retention", "%ds" % (sys.maxsize,))
|
|
||||||
)
|
|
||||||
/ 1000
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def generate_config_section(self, config_dir_path, server_name, **kwargs):
|
def generate_config_section(self, config_dir_path, server_name, **kwargs):
|
||||||
|
|
|
@ -110,8 +110,15 @@ class TlsConfig(Config):
|
||||||
# Support globs (*) in whitelist values
|
# Support globs (*) in whitelist values
|
||||||
self.federation_certificate_verification_whitelist = []
|
self.federation_certificate_verification_whitelist = []
|
||||||
for entry in fed_whitelist_entries:
|
for entry in fed_whitelist_entries:
|
||||||
|
try:
|
||||||
|
entry_regex = glob_to_regex(entry.encode("ascii").decode("ascii"))
|
||||||
|
except UnicodeEncodeError:
|
||||||
|
raise ConfigError(
|
||||||
|
"IDNA domain names are not allowed in the "
|
||||||
|
"federation_certificate_verification_whitelist: %s" % (entry,)
|
||||||
|
)
|
||||||
|
|
||||||
# Convert globs to regex
|
# Convert globs to regex
|
||||||
entry_regex = glob_to_regex(entry)
|
|
||||||
self.federation_certificate_verification_whitelist.append(entry_regex)
|
self.federation_certificate_verification_whitelist.append(entry_regex)
|
||||||
|
|
||||||
# List of custom certificate authorities for federation traffic validation
|
# List of custom certificate authorities for federation traffic validation
|
||||||
|
@ -239,12 +246,38 @@ class TlsConfig(Config):
|
||||||
self.tls_fingerprints.append({"sha256": sha256_fingerprint})
|
self.tls_fingerprints.append({"sha256": sha256_fingerprint})
|
||||||
|
|
||||||
def generate_config_section(
|
def generate_config_section(
|
||||||
self, config_dir_path, server_name, data_dir_path, **kwargs
|
self,
|
||||||
|
config_dir_path,
|
||||||
|
server_name,
|
||||||
|
data_dir_path,
|
||||||
|
tls_certificate_path,
|
||||||
|
tls_private_key_path,
|
||||||
|
acme_domain,
|
||||||
|
**kwargs
|
||||||
):
|
):
|
||||||
|
"""If the acme_domain is specified acme will be enabled.
|
||||||
|
If the TLS paths are not specified the default will be certs in the
|
||||||
|
config directory"""
|
||||||
|
|
||||||
base_key_name = os.path.join(config_dir_path, server_name)
|
base_key_name = os.path.join(config_dir_path, server_name)
|
||||||
|
|
||||||
tls_certificate_path = base_key_name + ".tls.crt"
|
if bool(tls_certificate_path) != bool(tls_private_key_path):
|
||||||
tls_private_key_path = base_key_name + ".tls.key"
|
raise ConfigError(
|
||||||
|
"Please specify both a cert path and a key path or neither."
|
||||||
|
)
|
||||||
|
|
||||||
|
tls_enabled = (
|
||||||
|
"" if tls_certificate_path and tls_private_key_path or acme_domain else "#"
|
||||||
|
)
|
||||||
|
|
||||||
|
if not tls_certificate_path:
|
||||||
|
tls_certificate_path = base_key_name + ".tls.crt"
|
||||||
|
if not tls_private_key_path:
|
||||||
|
tls_private_key_path = base_key_name + ".tls.key"
|
||||||
|
|
||||||
|
acme_enabled = bool(acme_domain)
|
||||||
|
acme_domain = "matrix.example.com"
|
||||||
|
|
||||||
default_acme_account_file = os.path.join(data_dir_path, "acme_account.key")
|
default_acme_account_file = os.path.join(data_dir_path, "acme_account.key")
|
||||||
|
|
||||||
# this is to avoid the max line length. Sorrynotsorry
|
# this is to avoid the max line length. Sorrynotsorry
|
||||||
|
@ -269,11 +302,11 @@ class TlsConfig(Config):
|
||||||
# instance, if using certbot, use `fullchain.pem` as your certificate,
|
# instance, if using certbot, use `fullchain.pem` as your certificate,
|
||||||
# not `cert.pem`).
|
# not `cert.pem`).
|
||||||
#
|
#
|
||||||
#tls_certificate_path: "%(tls_certificate_path)s"
|
%(tls_enabled)stls_certificate_path: "%(tls_certificate_path)s"
|
||||||
|
|
||||||
# PEM-encoded private key for TLS
|
# PEM-encoded private key for TLS
|
||||||
#
|
#
|
||||||
#tls_private_key_path: "%(tls_private_key_path)s"
|
%(tls_enabled)stls_private_key_path: "%(tls_private_key_path)s"
|
||||||
|
|
||||||
# Whether to verify TLS server certificates for outbound federation requests.
|
# Whether to verify TLS server certificates for outbound federation requests.
|
||||||
#
|
#
|
||||||
|
@ -340,10 +373,10 @@ class TlsConfig(Config):
|
||||||
# permission to listen on port 80.
|
# permission to listen on port 80.
|
||||||
#
|
#
|
||||||
acme:
|
acme:
|
||||||
# ACME support is disabled by default. Uncomment the following line
|
# ACME support is disabled by default. Set this to `true` and uncomment
|
||||||
# (and tls_certificate_path and tls_private_key_path above) to enable it.
|
# tls_certificate_path and tls_private_key_path above to enable it.
|
||||||
#
|
#
|
||||||
#enabled: true
|
enabled: %(acme_enabled)s
|
||||||
|
|
||||||
# Endpoint to use to request certificates. If you only want to test,
|
# Endpoint to use to request certificates. If you only want to test,
|
||||||
# use Let's Encrypt's staging url:
|
# use Let's Encrypt's staging url:
|
||||||
|
@ -354,17 +387,17 @@ class TlsConfig(Config):
|
||||||
# Port number to listen on for the HTTP-01 challenge. Change this if
|
# Port number to listen on for the HTTP-01 challenge. Change this if
|
||||||
# you are forwarding connections through Apache/Nginx/etc.
|
# you are forwarding connections through Apache/Nginx/etc.
|
||||||
#
|
#
|
||||||
#port: 80
|
port: 80
|
||||||
|
|
||||||
# Local addresses to listen on for incoming connections.
|
# Local addresses to listen on for incoming connections.
|
||||||
# Again, you may want to change this if you are forwarding connections
|
# Again, you may want to change this if you are forwarding connections
|
||||||
# through Apache/Nginx/etc.
|
# through Apache/Nginx/etc.
|
||||||
#
|
#
|
||||||
#bind_addresses: ['::', '0.0.0.0']
|
bind_addresses: ['::', '0.0.0.0']
|
||||||
|
|
||||||
# How many days remaining on a certificate before it is renewed.
|
# How many days remaining on a certificate before it is renewed.
|
||||||
#
|
#
|
||||||
#reprovision_threshold: 30
|
reprovision_threshold: 30
|
||||||
|
|
||||||
# The domain that the certificate should be for. Normally this
|
# The domain that the certificate should be for. Normally this
|
||||||
# should be the same as your Matrix domain (i.e., 'server_name'), but,
|
# should be the same as your Matrix domain (i.e., 'server_name'), but,
|
||||||
|
@ -378,7 +411,7 @@ class TlsConfig(Config):
|
||||||
#
|
#
|
||||||
# If not set, defaults to your 'server_name'.
|
# If not set, defaults to your 'server_name'.
|
||||||
#
|
#
|
||||||
#domain: matrix.example.com
|
domain: %(acme_domain)s
|
||||||
|
|
||||||
# file to use for the account key. This will be generated if it doesn't
|
# file to use for the account key. This will be generated if it doesn't
|
||||||
# exist.
|
# exist.
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
|
from synapse.python_dependencies import DependencyException, check_requirements
|
||||||
|
|
||||||
from ._base import Config, ConfigError
|
from ._base import Config, ConfigError
|
||||||
|
|
||||||
|
|
||||||
|
@ -32,6 +34,11 @@ class TracerConfig(Config):
|
||||||
if not self.opentracer_enabled:
|
if not self.opentracer_enabled:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
check_requirements("opentracing")
|
||||||
|
except DependencyException as e:
|
||||||
|
raise ConfigError(e.message)
|
||||||
|
|
||||||
# The tracer is enabled so sanitize the config
|
# The tracer is enabled so sanitize the config
|
||||||
|
|
||||||
self.opentracer_whitelist = opentracing_config.get("homeserver_whitelist", [])
|
self.opentracer_whitelist = opentracing_config.get("homeserver_whitelist", [])
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Copyright 2016 matrix.org
|
# Copyright 2016 OpenMarket Ltd
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import idna
|
|
||||||
from service_identity import VerificationError
|
from service_identity import VerificationError
|
||||||
from service_identity.pyopenssl import verify_hostname, verify_ip_address
|
from service_identity.pyopenssl import verify_hostname, verify_ip_address
|
||||||
from zope.interface import implementer
|
from zope.interface import implementer
|
||||||
|
@ -114,14 +113,20 @@ class ClientTLSOptionsFactory(object):
|
||||||
self._no_verify_ssl_context = self._no_verify_ssl.getContext()
|
self._no_verify_ssl_context = self._no_verify_ssl.getContext()
|
||||||
self._no_verify_ssl_context.set_info_callback(self._context_info_cb)
|
self._no_verify_ssl_context.set_info_callback(self._context_info_cb)
|
||||||
|
|
||||||
def get_options(self, host):
|
def get_options(self, host: bytes):
|
||||||
|
|
||||||
|
# IPolicyForHTTPS.get_options takes bytes, but we want to compare
|
||||||
|
# against the str whitelist. The hostnames in the whitelist are already
|
||||||
|
# IDNA-encoded like the hosts will be here.
|
||||||
|
ascii_host = host.decode("ascii")
|
||||||
|
|
||||||
# Check if certificate verification has been enabled
|
# Check if certificate verification has been enabled
|
||||||
should_verify = self._config.federation_verify_certificates
|
should_verify = self._config.federation_verify_certificates
|
||||||
|
|
||||||
# Check if we've disabled certificate verification for this host
|
# Check if we've disabled certificate verification for this host
|
||||||
if should_verify:
|
if should_verify:
|
||||||
for regex in self._config.federation_certificate_verification_whitelist:
|
for regex in self._config.federation_certificate_verification_whitelist:
|
||||||
if regex.match(host):
|
if regex.match(ascii_host):
|
||||||
should_verify = False
|
should_verify = False
|
||||||
break
|
break
|
||||||
|
|
||||||
|
@ -162,7 +167,7 @@ class SSLClientConnectionCreator(object):
|
||||||
Replaces twisted.internet.ssl.ClientTLSOptions
|
Replaces twisted.internet.ssl.ClientTLSOptions
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, hostname, ctx, verify_certs):
|
def __init__(self, hostname: bytes, ctx, verify_certs: bool):
|
||||||
self._ctx = ctx
|
self._ctx = ctx
|
||||||
self._verifier = ConnectionVerifier(hostname, verify_certs)
|
self._verifier = ConnectionVerifier(hostname, verify_certs)
|
||||||
|
|
||||||
|
@ -190,21 +195,16 @@ class ConnectionVerifier(object):
|
||||||
|
|
||||||
# This code is based on twisted.internet.ssl.ClientTLSOptions.
|
# This code is based on twisted.internet.ssl.ClientTLSOptions.
|
||||||
|
|
||||||
def __init__(self, hostname, verify_certs):
|
def __init__(self, hostname: bytes, verify_certs):
|
||||||
self._verify_certs = verify_certs
|
self._verify_certs = verify_certs
|
||||||
|
|
||||||
if isIPAddress(hostname) or isIPv6Address(hostname):
|
_decoded = hostname.decode("ascii")
|
||||||
self._hostnameBytes = hostname.encode("ascii")
|
if isIPAddress(_decoded) or isIPv6Address(_decoded):
|
||||||
self._is_ip_address = True
|
self._is_ip_address = True
|
||||||
else:
|
else:
|
||||||
# twisted's ClientTLSOptions falls back to the stdlib impl here if
|
|
||||||
# idna is not installed, but points out that lacks support for
|
|
||||||
# IDNA2008 (http://bugs.python.org/issue17305).
|
|
||||||
#
|
|
||||||
# We can rely on having idna.
|
|
||||||
self._hostnameBytes = idna.encode(hostname)
|
|
||||||
self._is_ip_address = False
|
self._is_ip_address = False
|
||||||
|
|
||||||
|
self._hostnameBytes = hostname
|
||||||
self._hostnameASCII = self._hostnameBytes.decode("ascii")
|
self._hostnameASCII = self._hostnameBytes.decode("ascii")
|
||||||
|
|
||||||
def verify_context_info_cb(self, ssl_connection, where):
|
def verify_context_info_cb(self, ssl_connection, where):
|
||||||
|
|
|
@ -83,7 +83,7 @@ def compute_content_hash(event_dict, hash_algorithm):
|
||||||
event_json_bytes = encode_canonical_json(event_dict)
|
event_json_bytes = encode_canonical_json(event_dict)
|
||||||
|
|
||||||
hashed = hash_algorithm(event_json_bytes)
|
hashed = hash_algorithm(event_json_bytes)
|
||||||
return (hashed.name, hashed.digest())
|
return hashed.name, hashed.digest()
|
||||||
|
|
||||||
|
|
||||||
def compute_event_reference_hash(event, hash_algorithm=hashlib.sha256):
|
def compute_event_reference_hash(event, hash_algorithm=hashlib.sha256):
|
||||||
|
@ -106,7 +106,7 @@ def compute_event_reference_hash(event, hash_algorithm=hashlib.sha256):
|
||||||
event_dict.pop("unsigned", None)
|
event_dict.pop("unsigned", None)
|
||||||
event_json_bytes = encode_canonical_json(event_dict)
|
event_json_bytes = encode_canonical_json(event_dict)
|
||||||
hashed = hash_algorithm(event_json_bytes)
|
hashed = hash_algorithm(event_json_bytes)
|
||||||
return (hashed.name, hashed.digest())
|
return hashed.name, hashed.digest()
|
||||||
|
|
||||||
|
|
||||||
def compute_event_signature(event_dict, signature_name, signing_key):
|
def compute_event_signature(event_dict, signature_name, signing_key):
|
||||||
|
|
|
@ -18,7 +18,6 @@ import logging
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
|
||||||
import six
|
import six
|
||||||
from six import raise_from
|
|
||||||
from six.moves import urllib
|
from six.moves import urllib
|
||||||
|
|
||||||
import attr
|
import attr
|
||||||
|
@ -30,7 +29,6 @@ from signedjson.key import (
|
||||||
from signedjson.sign import (
|
from signedjson.sign import (
|
||||||
SignatureVerifyException,
|
SignatureVerifyException,
|
||||||
encode_canonical_json,
|
encode_canonical_json,
|
||||||
sign_json,
|
|
||||||
signature_ids,
|
signature_ids,
|
||||||
verify_signed_json,
|
verify_signed_json,
|
||||||
)
|
)
|
||||||
|
@ -540,13 +538,7 @@ class BaseV2KeyFetcher(object):
|
||||||
verify_key=verify_key, valid_until_ts=key_data["expired_ts"]
|
verify_key=verify_key, valid_until_ts=key_data["expired_ts"]
|
||||||
)
|
)
|
||||||
|
|
||||||
# re-sign the json with our own key, so that it is ready if we are asked to
|
key_json_bytes = encode_canonical_json(response_json)
|
||||||
# give it out as a notary server
|
|
||||||
signed_key_json = sign_json(
|
|
||||||
response_json, self.config.server_name, self.config.signing_key[0]
|
|
||||||
)
|
|
||||||
|
|
||||||
signed_key_json_bytes = encode_canonical_json(signed_key_json)
|
|
||||||
|
|
||||||
yield make_deferred_yieldable(
|
yield make_deferred_yieldable(
|
||||||
defer.gatherResults(
|
defer.gatherResults(
|
||||||
|
@ -558,7 +550,7 @@ class BaseV2KeyFetcher(object):
|
||||||
from_server=from_server,
|
from_server=from_server,
|
||||||
ts_now_ms=time_added_ms,
|
ts_now_ms=time_added_ms,
|
||||||
ts_expires_ms=ts_valid_until_ms,
|
ts_expires_ms=ts_valid_until_ms,
|
||||||
key_json_bytes=signed_key_json_bytes,
|
key_json_bytes=key_json_bytes,
|
||||||
)
|
)
|
||||||
for key_id in verify_keys
|
for key_id in verify_keys
|
||||||
],
|
],
|
||||||
|
@ -657,9 +649,10 @@ class PerspectivesKeyFetcher(BaseV2KeyFetcher):
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
except (NotRetryingDestination, RequestSendFailed) as e:
|
except (NotRetryingDestination, RequestSendFailed) as e:
|
||||||
raise_from(KeyLookupError("Failed to connect to remote server"), e)
|
# these both have str() representations which we can't really improve upon
|
||||||
|
raise KeyLookupError(str(e))
|
||||||
except HttpResponseException as e:
|
except HttpResponseException as e:
|
||||||
raise_from(KeyLookupError("Remote server returned an error"), e)
|
raise KeyLookupError("Remote server returned an error: %s" % (e,))
|
||||||
|
|
||||||
keys = {}
|
keys = {}
|
||||||
added_keys = []
|
added_keys = []
|
||||||
|
@ -821,9 +814,11 @@ class ServerKeyFetcher(BaseV2KeyFetcher):
|
||||||
timeout=10000,
|
timeout=10000,
|
||||||
)
|
)
|
||||||
except (NotRetryingDestination, RequestSendFailed) as e:
|
except (NotRetryingDestination, RequestSendFailed) as e:
|
||||||
raise_from(KeyLookupError("Failed to connect to remote server"), e)
|
# these both have str() representations which we can't really improve
|
||||||
|
# upon
|
||||||
|
raise KeyLookupError(str(e))
|
||||||
except HttpResponseException as e:
|
except HttpResponseException as e:
|
||||||
raise_from(KeyLookupError("Remote server returned an error"), e)
|
raise KeyLookupError("Remote server returned an error: %s" % (e,))
|
||||||
|
|
||||||
if response["server_name"] != server_name:
|
if response["server_name"] != server_name:
|
||||||
raise KeyLookupError(
|
raise KeyLookupError(
|
||||||
|
|
|
@ -637,11 +637,11 @@ def auth_types_for_event(event):
|
||||||
if event.type == EventTypes.Create:
|
if event.type == EventTypes.Create:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
auth_types = []
|
auth_types = [
|
||||||
|
(EventTypes.PowerLevels, ""),
|
||||||
auth_types.append((EventTypes.PowerLevels, ""))
|
(EventTypes.Member, event.sender),
|
||||||
auth_types.append((EventTypes.Member, event.sender))
|
(EventTypes.Create, ""),
|
||||||
auth_types.append((EventTypes.Create, ""))
|
]
|
||||||
|
|
||||||
if event.type == EventTypes.Member:
|
if event.type == EventTypes.Member:
|
||||||
membership = event.content["membership"]
|
membership = event.content["membership"]
|
||||||
|
|
|
@ -355,7 +355,7 @@ class FederationClient(FederationBase):
|
||||||
|
|
||||||
auth_chain.sort(key=lambda e: e.depth)
|
auth_chain.sort(key=lambda e: e.depth)
|
||||||
|
|
||||||
return (pdus, auth_chain)
|
return pdus, auth_chain
|
||||||
except HttpResponseException as e:
|
except HttpResponseException as e:
|
||||||
if e.code == 400 or e.code == 404:
|
if e.code == 400 or e.code == 404:
|
||||||
logger.info("Failed to use get_room_state_ids API, falling back")
|
logger.info("Failed to use get_room_state_ids API, falling back")
|
||||||
|
@ -404,7 +404,7 @@ class FederationClient(FederationBase):
|
||||||
|
|
||||||
signed_auth.sort(key=lambda e: e.depth)
|
signed_auth.sort(key=lambda e: e.depth)
|
||||||
|
|
||||||
return (signed_pdus, signed_auth)
|
return signed_pdus, signed_auth
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def get_events_from_store_or_dest(self, destination, room_id, event_ids):
|
def get_events_from_store_or_dest(self, destination, room_id, event_ids):
|
||||||
|
@ -429,7 +429,7 @@ class FederationClient(FederationBase):
|
||||||
missing_events.discard(k)
|
missing_events.discard(k)
|
||||||
|
|
||||||
if not missing_events:
|
if not missing_events:
|
||||||
return (signed_events, failed_to_fetch)
|
return signed_events, failed_to_fetch
|
||||||
|
|
||||||
logger.debug(
|
logger.debug(
|
||||||
"Fetching unknown state/auth events %s for room %s",
|
"Fetching unknown state/auth events %s for room %s",
|
||||||
|
@ -465,7 +465,7 @@ class FederationClient(FederationBase):
|
||||||
# We removed all events we successfully fetched from `batch`
|
# We removed all events we successfully fetched from `batch`
|
||||||
failed_to_fetch.update(batch)
|
failed_to_fetch.update(batch)
|
||||||
|
|
||||||
return (signed_events, failed_to_fetch)
|
return signed_events, failed_to_fetch
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
@log_function
|
@log_function
|
||||||
|
|
|
@ -43,6 +43,7 @@ from synapse.federation.persistence import TransactionActions
|
||||||
from synapse.federation.units import Edu, Transaction
|
from synapse.federation.units import Edu, Transaction
|
||||||
from synapse.http.endpoint import parse_server_name
|
from synapse.http.endpoint import parse_server_name
|
||||||
from synapse.logging.context import nested_logging_context
|
from synapse.logging.context import nested_logging_context
|
||||||
|
from synapse.logging.opentracing import log_kv, start_active_span_from_edu, trace
|
||||||
from synapse.logging.utils import log_function
|
from synapse.logging.utils import log_function
|
||||||
from synapse.replication.http.federation import (
|
from synapse.replication.http.federation import (
|
||||||
ReplicationFederationSendEduRestServlet,
|
ReplicationFederationSendEduRestServlet,
|
||||||
|
@ -99,7 +100,7 @@ class FederationServer(FederationBase):
|
||||||
|
|
||||||
res = self._transaction_from_pdus(pdus).get_dict()
|
res = self._transaction_from_pdus(pdus).get_dict()
|
||||||
|
|
||||||
return (200, res)
|
return 200, res
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
@log_function
|
@log_function
|
||||||
|
@ -162,7 +163,7 @@ class FederationServer(FederationBase):
|
||||||
yield self.transaction_actions.set_response(
|
yield self.transaction_actions.set_response(
|
||||||
origin, transaction, 400, response
|
origin, transaction, 400, response
|
||||||
)
|
)
|
||||||
return (400, response)
|
return 400, response
|
||||||
|
|
||||||
received_pdus_counter.inc(len(transaction.pdus))
|
received_pdus_counter.inc(len(transaction.pdus))
|
||||||
|
|
||||||
|
@ -264,7 +265,7 @@ class FederationServer(FederationBase):
|
||||||
logger.debug("Returning: %s", str(response))
|
logger.debug("Returning: %s", str(response))
|
||||||
|
|
||||||
yield self.transaction_actions.set_response(origin, transaction, 200, response)
|
yield self.transaction_actions.set_response(origin, transaction, 200, response)
|
||||||
return (200, response)
|
return 200, response
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def received_edu(self, origin, edu_type, content):
|
def received_edu(self, origin, edu_type, content):
|
||||||
|
@ -297,7 +298,7 @@ class FederationServer(FederationBase):
|
||||||
event_id,
|
event_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
return (200, resp)
|
return 200, resp
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def on_state_ids_request(self, origin, room_id, event_id):
|
def on_state_ids_request(self, origin, room_id, event_id):
|
||||||
|
@ -314,7 +315,7 @@ class FederationServer(FederationBase):
|
||||||
state_ids = yield self.handler.get_state_ids_for_pdu(room_id, event_id)
|
state_ids = yield self.handler.get_state_ids_for_pdu(room_id, event_id)
|
||||||
auth_chain_ids = yield self.store.get_auth_chain_ids(state_ids)
|
auth_chain_ids = yield self.store.get_auth_chain_ids(state_ids)
|
||||||
|
|
||||||
return (200, {"pdu_ids": state_ids, "auth_chain_ids": auth_chain_ids})
|
return 200, {"pdu_ids": state_ids, "auth_chain_ids": auth_chain_ids}
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def _on_context_state_request_compute(self, room_id, event_id):
|
def _on_context_state_request_compute(self, room_id, event_id):
|
||||||
|
@ -344,15 +345,15 @@ class FederationServer(FederationBase):
|
||||||
pdu = yield self.handler.get_persisted_pdu(origin, event_id)
|
pdu = yield self.handler.get_persisted_pdu(origin, event_id)
|
||||||
|
|
||||||
if pdu:
|
if pdu:
|
||||||
return (200, self._transaction_from_pdus([pdu]).get_dict())
|
return 200, self._transaction_from_pdus([pdu]).get_dict()
|
||||||
else:
|
else:
|
||||||
return (404, "")
|
return 404, ""
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def on_query_request(self, query_type, args):
|
def on_query_request(self, query_type, args):
|
||||||
received_queries_counter.labels(query_type).inc()
|
received_queries_counter.labels(query_type).inc()
|
||||||
resp = yield self.registry.on_query(query_type, args)
|
resp = yield self.registry.on_query(query_type, args)
|
||||||
return (200, resp)
|
return 200, resp
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def on_make_join_request(self, origin, room_id, user_id, supported_versions):
|
def on_make_join_request(self, origin, room_id, user_id, supported_versions):
|
||||||
|
@ -434,7 +435,7 @@ class FederationServer(FederationBase):
|
||||||
|
|
||||||
logger.debug("on_send_leave_request: pdu sigs: %s", pdu.signatures)
|
logger.debug("on_send_leave_request: pdu sigs: %s", pdu.signatures)
|
||||||
yield self.handler.on_send_leave_request(origin, pdu)
|
yield self.handler.on_send_leave_request(origin, pdu)
|
||||||
return (200, {})
|
return 200, {}
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def on_event_auth(self, origin, room_id, event_id):
|
def on_event_auth(self, origin, room_id, event_id):
|
||||||
|
@ -445,7 +446,7 @@ class FederationServer(FederationBase):
|
||||||
time_now = self._clock.time_msec()
|
time_now = self._clock.time_msec()
|
||||||
auth_pdus = yield self.handler.on_event_auth(event_id)
|
auth_pdus = yield self.handler.on_event_auth(event_id)
|
||||||
res = {"auth_chain": [a.get_pdu_json(time_now) for a in auth_pdus]}
|
res = {"auth_chain": [a.get_pdu_json(time_now) for a in auth_pdus]}
|
||||||
return (200, res)
|
return 200, res
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def on_query_auth_request(self, origin, content, room_id, event_id):
|
def on_query_auth_request(self, origin, content, room_id, event_id):
|
||||||
|
@ -498,7 +499,7 @@ class FederationServer(FederationBase):
|
||||||
"missing": ret.get("missing", []),
|
"missing": ret.get("missing", []),
|
||||||
}
|
}
|
||||||
|
|
||||||
return (200, send_content)
|
return 200, send_content
|
||||||
|
|
||||||
@log_function
|
@log_function
|
||||||
def on_query_client_keys(self, origin, content):
|
def on_query_client_keys(self, origin, content):
|
||||||
|
@ -507,6 +508,7 @@ class FederationServer(FederationBase):
|
||||||
def on_query_user_devices(self, origin, user_id):
|
def on_query_user_devices(self, origin, user_id):
|
||||||
return self.on_query_request("user_devices", user_id)
|
return self.on_query_request("user_devices", user_id)
|
||||||
|
|
||||||
|
@trace
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
@log_function
|
@log_function
|
||||||
def on_claim_client_keys(self, origin, content):
|
def on_claim_client_keys(self, origin, content):
|
||||||
|
@ -515,6 +517,7 @@ class FederationServer(FederationBase):
|
||||||
for device_id, algorithm in device_keys.items():
|
for device_id, algorithm in device_keys.items():
|
||||||
query.append((user_id, device_id, algorithm))
|
query.append((user_id, device_id, algorithm))
|
||||||
|
|
||||||
|
log_kv({"message": "Claiming one time keys.", "user, device pairs": query})
|
||||||
results = yield self.store.claim_e2e_one_time_keys(query)
|
results = yield self.store.claim_e2e_one_time_keys(query)
|
||||||
|
|
||||||
json_result = {}
|
json_result = {}
|
||||||
|
@ -666,9 +669,9 @@ class FederationServer(FederationBase):
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def on_exchange_third_party_invite_request(self, origin, room_id, event_dict):
|
def on_exchange_third_party_invite_request(self, room_id, event_dict):
|
||||||
ret = yield self.handler.on_exchange_third_party_invite_request(
|
ret = yield self.handler.on_exchange_third_party_invite_request(
|
||||||
origin, room_id, event_dict
|
room_id, event_dict
|
||||||
)
|
)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
@ -808,12 +811,13 @@ class FederationHandlerRegistry(object):
|
||||||
if not handler:
|
if not handler:
|
||||||
logger.warn("No handler registered for EDU type %s", edu_type)
|
logger.warn("No handler registered for EDU type %s", edu_type)
|
||||||
|
|
||||||
try:
|
with start_active_span_from_edu(content, "handle_edu"):
|
||||||
yield handler(origin, content)
|
try:
|
||||||
except SynapseError as e:
|
yield handler(origin, content)
|
||||||
logger.info("Failed to handle edu %r: %r", edu_type, e)
|
except SynapseError as e:
|
||||||
except Exception:
|
logger.info("Failed to handle edu %r: %r", edu_type, e)
|
||||||
logger.exception("Failed to handle edu %r", edu_type)
|
except Exception:
|
||||||
|
logger.exception("Failed to handle edu %r", edu_type)
|
||||||
|
|
||||||
def on_query(self, query_type, args):
|
def on_query(self, query_type, args):
|
||||||
handler = self.query_handlers.get(query_type)
|
handler = self.query_handlers.get(query_type)
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue