Support for serving server well-known files (#11211)
Fixes https://github.com/matrix-org/synapse/issues/8308pull/11226/head
parent
2014098d01
commit
71f9966f27
|
@ -0,0 +1 @@
|
||||||
|
Add support for serving `/.well-known/matrix/server` files, to redirect federation traffic to port 443.
|
|
@ -1,4 +1,8 @@
|
||||||
# Delegation
|
# Delegation of incoming federation traffic
|
||||||
|
|
||||||
|
In the following documentation, we use the term `server_name` to refer to that setting
|
||||||
|
in your homeserver configuration file. It appears at the ends of user ids, and tells
|
||||||
|
other homeservers where they can find your server.
|
||||||
|
|
||||||
By default, other homeservers will expect to be able to reach yours via
|
By default, other homeservers will expect to be able to reach yours via
|
||||||
your `server_name`, on port 8448. For example, if you set your `server_name`
|
your `server_name`, on port 8448. For example, if you set your `server_name`
|
||||||
|
@ -12,13 +16,21 @@ to a different server and/or port (e.g. `synapse.example.com:443`).
|
||||||
|
|
||||||
## .well-known delegation
|
## .well-known delegation
|
||||||
|
|
||||||
To use this method, you need to be able to alter the
|
To use this method, you need to be able to configure the server at
|
||||||
`server_name` 's https server to serve the `/.well-known/matrix/server`
|
`https://<server_name>` to serve a file at
|
||||||
URL. Having an active server (with a valid TLS certificate) serving your
|
`https://<server_name>/.well-known/matrix/server`. There are two ways to do this, shown below.
|
||||||
`server_name` domain is out of the scope of this documentation.
|
|
||||||
|
|
||||||
The URL `https://<server_name>/.well-known/matrix/server` should
|
Note that the `.well-known` file is hosted on the default port for `https` (port 443).
|
||||||
return a JSON structure containing the key `m.server` like so:
|
|
||||||
|
### External server
|
||||||
|
|
||||||
|
For maximum flexibility, you need to configure an external server such as nginx, Apache
|
||||||
|
or HAProxy to serve the `https://<server_name>/.well-known/matrix/server` file. Setting
|
||||||
|
up such a server is out of the scope of this documentation, but note that it is often
|
||||||
|
possible to configure your [reverse proxy](reverse_proxy.md) for this.
|
||||||
|
|
||||||
|
The URL `https://<server_name>/.well-known/matrix/server` should be configured
|
||||||
|
return a JSON structure containing the key `m.server` like this:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
@ -26,8 +38,9 @@ return a JSON structure containing the key `m.server` like so:
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
In our example, this would mean that URL `https://example.com/.well-known/matrix/server`
|
In our example (where we want federation traffic to be routed to
|
||||||
should return:
|
`https://synapse.example.com`, on port 443), this would mean that
|
||||||
|
`https://example.com/.well-known/matrix/server` should return:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
@ -38,16 +51,29 @@ should return:
|
||||||
Note, specifying a port is optional. If no port is specified, then it defaults
|
Note, specifying a port is optional. If no port is specified, then it defaults
|
||||||
to 8448.
|
to 8448.
|
||||||
|
|
||||||
With .well-known delegation, federating servers will check for a valid TLS
|
### Serving a `.well-known/matrix/server` file with Synapse
|
||||||
certificate for the delegated hostname (in our example: `synapse.example.com`).
|
|
||||||
|
If you are able to set up your domain so that `https://<server_name>` is routed to
|
||||||
|
Synapse (i.e., the only change needed is to direct federation traffic to port 443
|
||||||
|
instead of port 8448), then it is possible to configure Synapse to serve a suitable
|
||||||
|
`.well-known/matrix/server` file. To do so, add the following to your `homeserver.yaml`
|
||||||
|
file:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
serve_server_wellknown: true
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note**: this *only* works if `https://<server_name>` is routed to Synapse, so is
|
||||||
|
generally not suitable if Synapse is hosted at a subdomain such as
|
||||||
|
`https://synapse.example.com`.
|
||||||
|
|
||||||
## SRV DNS record delegation
|
## SRV DNS record delegation
|
||||||
|
|
||||||
It is also possible to do delegation using a SRV DNS record. However, that is
|
It is also possible to do delegation using a SRV DNS record. However, that is generally
|
||||||
considered an advanced topic since it's a bit complex to set up, and `.well-known`
|
not recommended, as it can be difficult to configure the TLS certificates correctly in
|
||||||
delegation is already enough in most cases.
|
this case, and it offers little advantage over `.well-known` delegation.
|
||||||
|
|
||||||
However, if you really need it, you can find some documentation on how such a
|
However, if you really need it, you can find some documentation on what such a
|
||||||
record should look like and how Synapse will use it in [the Matrix
|
record should look like and how Synapse will use it in [the Matrix
|
||||||
specification](https://matrix.org/docs/spec/server_server/latest#resolving-server-names).
|
specification](https://matrix.org/docs/spec/server_server/latest#resolving-server-names).
|
||||||
|
|
||||||
|
@ -68,27 +94,9 @@ wouldn't need any delegation set up.
|
||||||
domain `server_name` points to, you will need to let other servers know how to
|
domain `server_name` points to, you will need to let other servers know how to
|
||||||
find it using delegation.
|
find it using delegation.
|
||||||
|
|
||||||
### Do you still recommend against using a reverse proxy on the federation port?
|
### Should I use a reverse proxy for federation traffic?
|
||||||
|
|
||||||
We no longer actively recommend against using a reverse proxy. Many admins will
|
Generally, using a reverse proxy for both the federation and client traffic is a good
|
||||||
find it easier to direct federation traffic to a reverse proxy and manage their
|
idea, since it saves handling TLS traffic in Synapse. See
|
||||||
own TLS certificates, and this is a supported configuration.
|
[the reverse proxy documentation](reverse_proxy.md) for information on setting up a
|
||||||
|
|
||||||
See [the reverse proxy documentation](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?
|
|
||||||
|
|
||||||
This is no longer necessary. If you are using a reverse proxy for all of your
|
|
||||||
TLS traffic, then you can set `no_tls: True` in the Synapse config.
|
|
||||||
|
|
||||||
In that case, the only reason Synapse needs the certificate is to populate a legacy
|
|
||||||
`tls_fingerprints` field in the federation API. This is ignored by Synapse 0.99.0
|
|
||||||
and later, and the only time pre-0.99 Synapses will check it is when attempting to
|
|
||||||
fetch the server keys - and generally this is delegated via `matrix.org`, which
|
|
||||||
is running a modern version of Synapse.
|
|
||||||
|
|
||||||
### Do I need the same certificate for the client and federation port?
|
|
||||||
|
|
||||||
No. There is nothing stopping you from using different certificates,
|
|
||||||
particularly if you are using a reverse proxy.
|
|
||||||
|
|
|
@ -93,6 +93,24 @@ pid_file: DATADIR/homeserver.pid
|
||||||
#
|
#
|
||||||
#public_baseurl: https://example.com/
|
#public_baseurl: https://example.com/
|
||||||
|
|
||||||
|
# Uncomment the following to tell other servers to send federation traffic on
|
||||||
|
# port 443.
|
||||||
|
#
|
||||||
|
# By default, other servers will try to reach our server on port 8448, which can
|
||||||
|
# be inconvenient in some environments.
|
||||||
|
#
|
||||||
|
# Provided 'https://<server_name>/' on port 443 is routed to Synapse, this
|
||||||
|
# option configures Synapse to serve a file at
|
||||||
|
# 'https://<server_name>/.well-known/matrix/server'. This will tell other
|
||||||
|
# servers to send traffic to port 443 instead.
|
||||||
|
#
|
||||||
|
# See https://matrix-org.github.io/synapse/latest/delegate.html for more
|
||||||
|
# information.
|
||||||
|
#
|
||||||
|
# Defaults to 'false'.
|
||||||
|
#
|
||||||
|
#serve_server_wellknown: true
|
||||||
|
|
||||||
# Set the soft limit on the number of file descriptors synapse can use
|
# Set the soft limit on the number of file descriptors synapse can use
|
||||||
# Zero is used to indicate synapse should set the soft limit to the
|
# Zero is used to indicate synapse should set the soft limit to the
|
||||||
# hard limit.
|
# hard limit.
|
||||||
|
|
|
@ -100,6 +100,7 @@ from synapse.rest.client.register import (
|
||||||
from synapse.rest.health import HealthResource
|
from synapse.rest.health import HealthResource
|
||||||
from synapse.rest.key.v2 import KeyApiV2Resource
|
from synapse.rest.key.v2 import KeyApiV2Resource
|
||||||
from synapse.rest.synapse.client import build_synapse_client_resource_tree
|
from synapse.rest.synapse.client import build_synapse_client_resource_tree
|
||||||
|
from synapse.rest.well_known import well_known_resource
|
||||||
from synapse.server import HomeServer
|
from synapse.server import HomeServer
|
||||||
from synapse.storage.databases.main.censor_events import CensorEventsStore
|
from synapse.storage.databases.main.censor_events import CensorEventsStore
|
||||||
from synapse.storage.databases.main.client_ips import ClientIpWorkerStore
|
from synapse.storage.databases.main.client_ips import ClientIpWorkerStore
|
||||||
|
@ -318,6 +319,8 @@ class GenericWorkerServer(HomeServer):
|
||||||
resources.update({CLIENT_API_PREFIX: resource})
|
resources.update({CLIENT_API_PREFIX: resource})
|
||||||
|
|
||||||
resources.update(build_synapse_client_resource_tree(self))
|
resources.update(build_synapse_client_resource_tree(self))
|
||||||
|
resources.update({"/.well-known": well_known_resource(self)})
|
||||||
|
|
||||||
elif name == "federation":
|
elif name == "federation":
|
||||||
resources.update({FEDERATION_PREFIX: TransportLayerServer(self)})
|
resources.update({FEDERATION_PREFIX: TransportLayerServer(self)})
|
||||||
elif name == "media":
|
elif name == "media":
|
||||||
|
|
|
@ -66,7 +66,7 @@ from synapse.rest.admin import AdminRestResource
|
||||||
from synapse.rest.health import HealthResource
|
from synapse.rest.health import HealthResource
|
||||||
from synapse.rest.key.v2 import KeyApiV2Resource
|
from synapse.rest.key.v2 import KeyApiV2Resource
|
||||||
from synapse.rest.synapse.client import build_synapse_client_resource_tree
|
from synapse.rest.synapse.client import build_synapse_client_resource_tree
|
||||||
from synapse.rest.well_known import WellKnownResource
|
from synapse.rest.well_known import well_known_resource
|
||||||
from synapse.server import HomeServer
|
from synapse.server import HomeServer
|
||||||
from synapse.storage import DataStore
|
from synapse.storage import DataStore
|
||||||
from synapse.util.httpresourcetree import create_resource_tree
|
from synapse.util.httpresourcetree import create_resource_tree
|
||||||
|
@ -189,7 +189,7 @@ class SynapseHomeServer(HomeServer):
|
||||||
"/_matrix/client/unstable": client_resource,
|
"/_matrix/client/unstable": client_resource,
|
||||||
"/_matrix/client/v2_alpha": client_resource,
|
"/_matrix/client/v2_alpha": client_resource,
|
||||||
"/_matrix/client/versions": client_resource,
|
"/_matrix/client/versions": client_resource,
|
||||||
"/.well-known/matrix/client": WellKnownResource(self),
|
"/.well-known": well_known_resource(self),
|
||||||
"/_synapse/admin": AdminRestResource(self),
|
"/_synapse/admin": AdminRestResource(self),
|
||||||
**build_synapse_client_resource_tree(self),
|
**build_synapse_client_resource_tree(self),
|
||||||
}
|
}
|
||||||
|
|
|
@ -262,6 +262,7 @@ class ServerConfig(Config):
|
||||||
self.print_pidfile = config.get("print_pidfile")
|
self.print_pidfile = config.get("print_pidfile")
|
||||||
self.user_agent_suffix = config.get("user_agent_suffix")
|
self.user_agent_suffix = config.get("user_agent_suffix")
|
||||||
self.use_frozen_dicts = config.get("use_frozen_dicts", False)
|
self.use_frozen_dicts = config.get("use_frozen_dicts", False)
|
||||||
|
self.serve_server_wellknown = config.get("serve_server_wellknown", False)
|
||||||
|
|
||||||
self.public_baseurl = config.get("public_baseurl")
|
self.public_baseurl = config.get("public_baseurl")
|
||||||
if self.public_baseurl is not None:
|
if self.public_baseurl is not None:
|
||||||
|
@ -774,6 +775,24 @@ class ServerConfig(Config):
|
||||||
#
|
#
|
||||||
#public_baseurl: https://example.com/
|
#public_baseurl: https://example.com/
|
||||||
|
|
||||||
|
# Uncomment the following to tell other servers to send federation traffic on
|
||||||
|
# port 443.
|
||||||
|
#
|
||||||
|
# By default, other servers will try to reach our server on port 8448, which can
|
||||||
|
# be inconvenient in some environments.
|
||||||
|
#
|
||||||
|
# Provided 'https://<server_name>/' on port 443 is routed to Synapse, this
|
||||||
|
# option configures Synapse to serve a file at
|
||||||
|
# 'https://<server_name>/.well-known/matrix/server'. This will tell other
|
||||||
|
# servers to send traffic to port 443 instead.
|
||||||
|
#
|
||||||
|
# See https://matrix-org.github.io/synapse/latest/delegate.html for more
|
||||||
|
# information.
|
||||||
|
#
|
||||||
|
# Defaults to 'false'.
|
||||||
|
#
|
||||||
|
#serve_server_wellknown: true
|
||||||
|
|
||||||
# Set the soft limit on the number of file descriptors synapse can use
|
# Set the soft limit on the number of file descriptors synapse can use
|
||||||
# Zero is used to indicate synapse should set the soft limit to the
|
# Zero is used to indicate synapse should set the soft limit to the
|
||||||
# hard limit.
|
# hard limit.
|
||||||
|
|
|
@ -21,6 +21,7 @@ from twisted.web.server import Request
|
||||||
from synapse.http.server import set_cors_headers
|
from synapse.http.server import set_cors_headers
|
||||||
from synapse.types import JsonDict
|
from synapse.types import JsonDict
|
||||||
from synapse.util import json_encoder
|
from synapse.util import json_encoder
|
||||||
|
from synapse.util.stringutils import parse_server_name
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from synapse.server import HomeServer
|
from synapse.server import HomeServer
|
||||||
|
@ -47,8 +48,8 @@ class WellKnownBuilder:
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
class WellKnownResource(Resource):
|
class ClientWellKnownResource(Resource):
|
||||||
"""A Twisted web resource which renders the .well-known file"""
|
"""A Twisted web resource which renders the .well-known/matrix/client file"""
|
||||||
|
|
||||||
isLeaf = 1
|
isLeaf = 1
|
||||||
|
|
||||||
|
@ -67,3 +68,45 @@ class WellKnownResource(Resource):
|
||||||
logger.debug("returning: %s", r)
|
logger.debug("returning: %s", r)
|
||||||
request.setHeader(b"Content-Type", b"application/json")
|
request.setHeader(b"Content-Type", b"application/json")
|
||||||
return json_encoder.encode(r).encode("utf-8")
|
return json_encoder.encode(r).encode("utf-8")
|
||||||
|
|
||||||
|
|
||||||
|
class ServerWellKnownResource(Resource):
|
||||||
|
"""Resource for .well-known/matrix/server, redirecting to port 443"""
|
||||||
|
|
||||||
|
isLeaf = 1
|
||||||
|
|
||||||
|
def __init__(self, hs: "HomeServer"):
|
||||||
|
super().__init__()
|
||||||
|
self._serve_server_wellknown = hs.config.server.serve_server_wellknown
|
||||||
|
|
||||||
|
host, port = parse_server_name(hs.config.server.server_name)
|
||||||
|
|
||||||
|
# If we've got this far, then https://<server_name>/ must route to us, so
|
||||||
|
# we just redirect the traffic to port 443 instead of 8448.
|
||||||
|
if port is None:
|
||||||
|
port = 443
|
||||||
|
|
||||||
|
self._response = json_encoder.encode({"m.server": f"{host}:{port}"}).encode(
|
||||||
|
"utf-8"
|
||||||
|
)
|
||||||
|
|
||||||
|
def render_GET(self, request: Request) -> bytes:
|
||||||
|
if not self._serve_server_wellknown:
|
||||||
|
request.setResponseCode(404)
|
||||||
|
request.setHeader(b"Content-Type", b"text/plain")
|
||||||
|
return b"404. Is anything ever truly *well* known?\n"
|
||||||
|
|
||||||
|
request.setHeader(b"Content-Type", b"application/json")
|
||||||
|
return self._response
|
||||||
|
|
||||||
|
|
||||||
|
def well_known_resource(hs: "HomeServer") -> Resource:
|
||||||
|
"""Returns a Twisted web resource which handles '.well-known' requests"""
|
||||||
|
res = Resource()
|
||||||
|
matrix_resource = Resource()
|
||||||
|
res.putChild(b"matrix", matrix_resource)
|
||||||
|
|
||||||
|
matrix_resource.putChild(b"server", ServerWellKnownResource(hs))
|
||||||
|
matrix_resource.putChild(b"client", ClientWellKnownResource(hs))
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
|
@ -11,17 +11,19 @@
|
||||||
# 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.
|
||||||
|
from twisted.web.resource import Resource
|
||||||
|
|
||||||
|
from synapse.rest.well_known import well_known_resource
|
||||||
from synapse.rest.well_known import WellKnownResource
|
|
||||||
|
|
||||||
from tests import unittest
|
from tests import unittest
|
||||||
|
|
||||||
|
|
||||||
class WellKnownTests(unittest.HomeserverTestCase):
|
class WellKnownTests(unittest.HomeserverTestCase):
|
||||||
def create_test_resource(self):
|
def create_test_resource(self):
|
||||||
# replace the JsonResource with a WellKnownResource
|
# replace the JsonResource with a Resource wrapping the WellKnownResource
|
||||||
return WellKnownResource(self.hs)
|
res = Resource()
|
||||||
|
res.putChild(b".well-known", well_known_resource(self.hs))
|
||||||
|
return res
|
||||||
|
|
||||||
@unittest.override_config(
|
@unittest.override_config(
|
||||||
{
|
{
|
||||||
|
@ -29,7 +31,7 @@ class WellKnownTests(unittest.HomeserverTestCase):
|
||||||
"default_identity_server": "https://testis",
|
"default_identity_server": "https://testis",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
def test_well_known(self):
|
def test_client_well_known(self):
|
||||||
channel = self.make_request(
|
channel = self.make_request(
|
||||||
"GET", "/.well-known/matrix/client", shorthand=False
|
"GET", "/.well-known/matrix/client", shorthand=False
|
||||||
)
|
)
|
||||||
|
@ -48,9 +50,27 @@ class WellKnownTests(unittest.HomeserverTestCase):
|
||||||
"public_baseurl": None,
|
"public_baseurl": None,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
def test_well_known_no_public_baseurl(self):
|
def test_client_well_known_no_public_baseurl(self):
|
||||||
channel = self.make_request(
|
channel = self.make_request(
|
||||||
"GET", "/.well-known/matrix/client", shorthand=False
|
"GET", "/.well-known/matrix/client", shorthand=False
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(channel.code, 404)
|
self.assertEqual(channel.code, 404)
|
||||||
|
|
||||||
|
@unittest.override_config({"serve_server_wellknown": True})
|
||||||
|
def test_server_well_known(self):
|
||||||
|
channel = self.make_request(
|
||||||
|
"GET", "/.well-known/matrix/server", shorthand=False
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(channel.code, 200)
|
||||||
|
self.assertEqual(
|
||||||
|
channel.json_body,
|
||||||
|
{"m.server": "test:443"},
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_server_well_known_disabled(self):
|
||||||
|
channel = self.make_request(
|
||||||
|
"GET", "/.well-known/matrix/server", shorthand=False
|
||||||
|
)
|
||||||
|
self.assertEqual(channel.code, 404)
|
||||||
|
|
Loading…
Reference in New Issue