Skip to content
Snippets Groups Projects
Unverified Commit 8d133a84 authored by Richard van der Hoff's avatar Richard van der Hoff Committed by GitHub
Browse files

Fixes to `federation_client` dev script (#14479)

* Attempt to fix federation-client devscript handling of .well-known

The script was setting the wrong value in the Host header

* Fix TLS verification

Turns out that actually doing TLS verification isn't that hard. Let's enable
it.
parent e1b15f25
No related branches found
No related tags found
No related merge requests found
`scripts-dev/federation_client`: Fix routing on servers with `.well-known` files.
\ No newline at end of file
...@@ -46,11 +46,12 @@ import signedjson.key ...@@ -46,11 +46,12 @@ import signedjson.key
import signedjson.types import signedjson.types
import srvlookup import srvlookup
import yaml import yaml
from requests import PreparedRequest, Response
from requests.adapters import HTTPAdapter from requests.adapters import HTTPAdapter
from urllib3 import HTTPConnectionPool from urllib3 import HTTPConnectionPool
# uncomment the following to enable debug logging of http requests # uncomment the following to enable debug logging of http requests
# from httplib import HTTPConnection # from http.client import HTTPConnection
# HTTPConnection.debuglevel = 1 # HTTPConnection.debuglevel = 1
...@@ -103,6 +104,7 @@ def request( ...@@ -103,6 +104,7 @@ def request(
destination: str, destination: str,
path: str, path: str,
content: Optional[str], content: Optional[str],
verify_tls: bool,
) -> requests.Response: ) -> requests.Response:
if method is None: if method is None:
if content is None: if content is None:
...@@ -141,7 +143,6 @@ def request( ...@@ -141,7 +143,6 @@ def request(
s.mount("matrix://", MatrixConnectionAdapter()) s.mount("matrix://", MatrixConnectionAdapter())
headers: Dict[str, str] = { headers: Dict[str, str] = {
"Host": destination,
"Authorization": authorization_headers[0], "Authorization": authorization_headers[0],
} }
...@@ -152,7 +153,7 @@ def request( ...@@ -152,7 +153,7 @@ def request(
method=method, method=method,
url=dest, url=dest,
headers=headers, headers=headers,
verify=False, verify=verify_tls,
data=content, data=content,
stream=True, stream=True,
) )
...@@ -202,6 +203,12 @@ def main() -> None: ...@@ -202,6 +203,12 @@ def main() -> None:
parser.add_argument("--body", help="Data to send as the body of the HTTP request") parser.add_argument("--body", help="Data to send as the body of the HTTP request")
parser.add_argument(
"--insecure",
action="store_true",
help="Disable TLS certificate verification",
)
parser.add_argument( parser.add_argument(
"path", help="request path, including the '/_matrix/federation/...' prefix." "path", help="request path, including the '/_matrix/federation/...' prefix."
) )
...@@ -227,6 +234,7 @@ def main() -> None: ...@@ -227,6 +234,7 @@ def main() -> None:
args.destination, args.destination,
args.path, args.path,
content=args.body, content=args.body,
verify_tls=not args.insecure,
) )
sys.stderr.write("Status Code: %d\n" % (result.status_code,)) sys.stderr.write("Status Code: %d\n" % (result.status_code,))
...@@ -254,36 +262,93 @@ def read_args_from_config(args: argparse.Namespace) -> None: ...@@ -254,36 +262,93 @@ def read_args_from_config(args: argparse.Namespace) -> None:
class MatrixConnectionAdapter(HTTPAdapter): class MatrixConnectionAdapter(HTTPAdapter):
def send(
self,
request: PreparedRequest,
*args: Any,
**kwargs: Any,
) -> Response:
# overrides the send() method in the base class.
# We need to look for .well-known redirects before passing the request up to
# HTTPAdapter.send().
assert isinstance(request.url, str)
parsed = urlparse.urlsplit(request.url)
server_name = parsed.netloc
well_known = self._get_well_known(parsed.netloc)
if well_known:
server_name = well_known
# replace the scheme in the uri with https, so that cert verification is done
# also replace the hostname if we got a .well-known result
request.url = urlparse.urlunsplit(
("https", server_name, parsed.path, parsed.query, parsed.fragment)
)
# at this point we also add the host header (otherwise urllib will add one
# based on the `host` from the connection returned by `get_connection`,
# which will be wrong if there is an SRV record).
request.headers["Host"] = server_name
return super().send(request, *args, **kwargs)
def get_connection(
self, url: str, proxies: Optional[Dict[str, str]] = None
) -> HTTPConnectionPool:
# overrides the get_connection() method in the base class
parsed = urlparse.urlsplit(url)
(host, port, ssl_server_name) = self._lookup(parsed.netloc)
print(
f"Connecting to {host}:{port} with SNI {ssl_server_name}", file=sys.stderr
)
return self.poolmanager.connection_from_host(
host,
port=port,
scheme="https",
pool_kwargs={"server_hostname": ssl_server_name},
)
@staticmethod @staticmethod
def lookup(s: str, skip_well_known: bool = False) -> Tuple[str, int]: def _lookup(server_name: str) -> Tuple[str, int, str]:
if s[-1] == "]": """
Do an SRV lookup on a server name and return the host:port to connect to
Given the server_name (after any .well-known lookup), return the host, port and
the ssl server name
"""
if server_name[-1] == "]":
# ipv6 literal (with no port) # ipv6 literal (with no port)
return s, 8448 return server_name, 8448, server_name
if ":" in s: if ":" in server_name:
out = s.rsplit(":", 1) # explicit port
out = server_name.rsplit(":", 1)
try: try:
port = int(out[1]) port = int(out[1])
except ValueError: except ValueError:
raise ValueError("Invalid host:port '%s'" % s) raise ValueError("Invalid host:port '%s'" % (server_name,))
return out[0], port return out[0], port, out[0]
# try a .well-known lookup
if not skip_well_known:
well_known = MatrixConnectionAdapter.get_well_known(s)
if well_known:
return MatrixConnectionAdapter.lookup(well_known, skip_well_known=True)
try: try:
srv = srvlookup.lookup("matrix", "tcp", s)[0] srv = srvlookup.lookup("matrix", "tcp", server_name)[0]
return srv.host, srv.port print(
f"SRV lookup on _matrix._tcp.{server_name} gave {srv}",
file=sys.stderr,
)
return srv.host, srv.port, server_name
except Exception: except Exception:
return s, 8448 return server_name, 8448, server_name
@staticmethod @staticmethod
def get_well_known(server_name: str) -> Optional[str]: def _get_well_known(server_name: str) -> Optional[str]:
uri = "https://%s/.well-known/matrix/server" % (server_name,) if ":" in server_name:
print("fetching %s" % (uri,), file=sys.stderr) # explicit port, or ipv6 literal. Either way, no .well-known
return None
# TODO: check for ipv4 literals
uri = f"https://{server_name}/.well-known/matrix/server"
print(f"fetching {uri}", file=sys.stderr)
try: try:
resp = requests.get(uri) resp = requests.get(uri)
...@@ -304,19 +369,6 @@ class MatrixConnectionAdapter(HTTPAdapter): ...@@ -304,19 +369,6 @@ class MatrixConnectionAdapter(HTTPAdapter):
print("Invalid response from %s: %s" % (uri, e), file=sys.stderr) print("Invalid response from %s: %s" % (uri, e), file=sys.stderr)
return None return None
def get_connection(
self, url: str, proxies: Optional[Dict[str, str]] = None
) -> HTTPConnectionPool:
parsed = urlparse.urlparse(url)
(host, port) = self.lookup(parsed.netloc)
netloc = "%s:%d" % (host, port)
print("Connecting to %s" % (netloc,), file=sys.stderr)
url = urlparse.urlunparse(
("https", netloc, parsed.path, parsed.params, parsed.query, parsed.fragment)
)
return super().get_connection(url, proxies)
if __name__ == "__main__": if __name__ == "__main__":
main() main()
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment