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
|
||||
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
|
||||
|
||||
To use this method, you need to be able to alter the
|
||||
`server_name` 's https server to serve the `/.well-known/matrix/server`
|
||||
URL. Having an active server (with a valid TLS certificate) serving your
|
||||
`server_name` domain is out of the scope of this documentation.
|
||||
To use this method, you need to be able to configure the server at
|
||||
`https://<server_name>` to serve a file at
|
||||
`https://<server_name>/.well-known/matrix/server`. There are two ways to do this, shown below.
|
||||
|
||||
The URL `https://<server_name>/.well-known/matrix/server` should
|
||||
return a JSON structure containing the key `m.server` like so:
|
||||
Note that the `.well-known` file is hosted on the default port for `https` (port 443).
|
||||
|
||||
### 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
|
||||
{
|
||||
|
@ -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`
|
||||
should return:
|
||||
In our example (where we want federation traffic to be routed to
|
||||
`https://synapse.example.com`, on port 443), this would mean that
|
||||
`https://example.com/.well-known/matrix/server` should return:
|
||||
|
||||
```json
|
||||
{
|
||||
|
@ -38,16 +51,29 @@ should return:
|
|||
Note, specifying a port is optional. If no port is specified, then it defaults
|
||||
to 8448.
|
||||
|
||||
With .well-known delegation, federating servers will check for a valid TLS
|
||||
certificate for the delegated hostname (in our example: `synapse.example.com`).
|
||||
### Serving a `.well-known/matrix/server` file with Synapse
|
||||
|
||||
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
|
||||
|
||||
It is also possible to do delegation using a SRV DNS record. However, that is
|
||||
considered an advanced topic since it's a bit complex to set up, and `.well-known`
|
||||
delegation is already enough in most cases.
|
||||
It is also possible to do delegation using a SRV DNS record. However, that is generally
|
||||
not recommended, as it can be difficult to configure the TLS certificates correctly in
|
||||
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
|
||||
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
|
||||
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
|
||||
find it easier to direct federation traffic to a reverse proxy and manage their
|
||||
own TLS certificates, and this is a supported configuration.
|
||||
|
||||
See [the reverse proxy documentation](reverse_proxy.md) for information on setting up a
|
||||
Generally, using a reverse proxy for both the federation and client traffic is a good
|
||||
idea, since it saves handling TLS traffic in Synapse. See
|
||||
[the reverse proxy documentation](reverse_proxy.md) for information on setting up a
|
||||
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/
|
||||
|
||||
# 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
|
||||
# Zero is used to indicate synapse should set the soft limit to the
|
||||
# hard limit.
|
||||
|
|
|
@ -100,6 +100,7 @@ from synapse.rest.client.register import (
|
|||
from synapse.rest.health import HealthResource
|
||||
from synapse.rest.key.v2 import KeyApiV2Resource
|
||||
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.storage.databases.main.censor_events import CensorEventsStore
|
||||
from synapse.storage.databases.main.client_ips import ClientIpWorkerStore
|
||||
|
@ -318,6 +319,8 @@ class GenericWorkerServer(HomeServer):
|
|||
resources.update({CLIENT_API_PREFIX: resource})
|
||||
|
||||
resources.update(build_synapse_client_resource_tree(self))
|
||||
resources.update({"/.well-known": well_known_resource(self)})
|
||||
|
||||
elif name == "federation":
|
||||
resources.update({FEDERATION_PREFIX: TransportLayerServer(self)})
|
||||
elif name == "media":
|
||||
|
|
|
@ -66,7 +66,7 @@ from synapse.rest.admin import AdminRestResource
|
|||
from synapse.rest.health import HealthResource
|
||||
from synapse.rest.key.v2 import KeyApiV2Resource
|
||||
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.storage import DataStore
|
||||
from synapse.util.httpresourcetree import create_resource_tree
|
||||
|
@ -189,7 +189,7 @@ class SynapseHomeServer(HomeServer):
|
|||
"/_matrix/client/unstable": client_resource,
|
||||
"/_matrix/client/v2_alpha": client_resource,
|
||||
"/_matrix/client/versions": client_resource,
|
||||
"/.well-known/matrix/client": WellKnownResource(self),
|
||||
"/.well-known": well_known_resource(self),
|
||||
"/_synapse/admin": AdminRestResource(self),
|
||||
**build_synapse_client_resource_tree(self),
|
||||
}
|
||||
|
|
|
@ -262,6 +262,7 @@ class ServerConfig(Config):
|
|||
self.print_pidfile = config.get("print_pidfile")
|
||||
self.user_agent_suffix = config.get("user_agent_suffix")
|
||||
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")
|
||||
if self.public_baseurl is not None:
|
||||
|
@ -774,6 +775,24 @@ class ServerConfig(Config):
|
|||
#
|
||||
#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
|
||||
# Zero is used to indicate synapse should set the soft limit to the
|
||||
# hard limit.
|
||||
|
|
|
@ -21,6 +21,7 @@ from twisted.web.server import Request
|
|||
from synapse.http.server import set_cors_headers
|
||||
from synapse.types import JsonDict
|
||||
from synapse.util import json_encoder
|
||||
from synapse.util.stringutils import parse_server_name
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from synapse.server import HomeServer
|
||||
|
@ -47,8 +48,8 @@ class WellKnownBuilder:
|
|||
return result
|
||||
|
||||
|
||||
class WellKnownResource(Resource):
|
||||
"""A Twisted web resource which renders the .well-known file"""
|
||||
class ClientWellKnownResource(Resource):
|
||||
"""A Twisted web resource which renders the .well-known/matrix/client file"""
|
||||
|
||||
isLeaf = 1
|
||||
|
||||
|
@ -67,3 +68,45 @@ class WellKnownResource(Resource):
|
|||
logger.debug("returning: %s", r)
|
||||
request.setHeader(b"Content-Type", b"application/json")
|
||||
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.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
from twisted.web.resource import Resource
|
||||
|
||||
|
||||
from synapse.rest.well_known import WellKnownResource
|
||||
from synapse.rest.well_known import well_known_resource
|
||||
|
||||
from tests import unittest
|
||||
|
||||
|
||||
class WellKnownTests(unittest.HomeserverTestCase):
|
||||
def create_test_resource(self):
|
||||
# replace the JsonResource with a WellKnownResource
|
||||
return WellKnownResource(self.hs)
|
||||
# replace the JsonResource with a Resource wrapping the WellKnownResource
|
||||
res = Resource()
|
||||
res.putChild(b".well-known", well_known_resource(self.hs))
|
||||
return res
|
||||
|
||||
@unittest.override_config(
|
||||
{
|
||||
|
@ -29,7 +31,7 @@ class WellKnownTests(unittest.HomeserverTestCase):
|
|||
"default_identity_server": "https://testis",
|
||||
}
|
||||
)
|
||||
def test_well_known(self):
|
||||
def test_client_well_known(self):
|
||||
channel = self.make_request(
|
||||
"GET", "/.well-known/matrix/client", shorthand=False
|
||||
)
|
||||
|
@ -48,9 +50,27 @@ class WellKnownTests(unittest.HomeserverTestCase):
|
|||
"public_baseurl": None,
|
||||
}
|
||||
)
|
||||
def test_well_known_no_public_baseurl(self):
|
||||
def test_client_well_known_no_public_baseurl(self):
|
||||
channel = self.make_request(
|
||||
"GET", "/.well-known/matrix/client", shorthand=False
|
||||
)
|
||||
|
||||
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