Hook up NURL generation to the new Foolscap/HTTPS protocol switch.

This commit is contained in:
Itamar Turner-Trauring
2022-07-20 15:12:00 -04:00
parent 5e0c32708b
commit 11f4ebc0d9
4 changed files with 52 additions and 51 deletions

View File

@ -37,6 +37,7 @@ import allmydata
from allmydata.crypto import rsa, ed25519 from allmydata.crypto import rsa, ed25519
from allmydata.crypto.util import remove_prefix from allmydata.crypto.util import remove_prefix
from allmydata.storage.server import StorageServer, FoolscapStorageServer from allmydata.storage.server import StorageServer, FoolscapStorageServer
from allmydata.storage.http_server import build_nurl
from allmydata import storage_client from allmydata import storage_client
from allmydata.immutable.upload import Uploader from allmydata.immutable.upload import Uploader
from allmydata.immutable.offloaded import Helper from allmydata.immutable.offloaded import Helper
@ -658,6 +659,12 @@ class _Client(node.Node, pollmixin.PollMixin):
if webport: if webport:
self.init_web(webport) # strports string self.init_web(webport) # strports string
# TODO this may be the wrong location for now? but as temporary measure
# it allows us to get NURLs for testing in test_istorageserver.py Will
# eventually get fixed one way or another in
# https://tahoe-lafs.org/trac/tahoe-lafs/ticket/3901
self.storage_nurls = []
def init_stats_provider(self): def init_stats_provider(self):
self.stats_provider = StatsProvider(self) self.stats_provider = StatsProvider(self)
self.stats_provider.setServiceParent(self) self.stats_provider.setServiceParent(self)
@ -820,6 +827,15 @@ class _Client(node.Node, pollmixin.PollMixin):
furl = self.tub.registerReference(FoolscapStorageServer(ss), furlFile=furl_file) furl = self.tub.registerReference(FoolscapStorageServer(ss), furlFile=furl_file)
(_, _, swissnum) = furl.rpartition("/") (_, _, swissnum) = furl.rpartition("/")
self.tub.negotiationClass.add_storage_server(ss, swissnum.encode("ascii")) self.tub.negotiationClass.add_storage_server(ss, swissnum.encode("ascii"))
for location_hint in self.tub.locationHints:
if location_hint.startswith("tcp:"):
_, hostname, port = location_hint.split(":")
port = int(port)
self.storage_nurls.append(
build_nurl(
hostname, port, swissnum, self.tub.myCertificate.original.to_cryptography()
)
)
announcement["anonymous-storage-FURL"] = furl announcement["anonymous-storage-FURL"] = furl

View File

@ -66,14 +66,6 @@ class _FoolscapOrHttps(Protocol, metaclass=_PretendToBeNegotiation):
Add the various storage server-related attributes needed by a Add the various storage server-related attributes needed by a
``Tub``-specific ``_FoolscapOrHttps`` subclass. ``Tub``-specific ``_FoolscapOrHttps`` subclass.
""" """
# TODO tub.locationHints will be in the format ["tcp:hostname:port"]
# (and maybe some other things we can ignore for now). We also have
# access to the certificate. Together, this should be sufficient to
# construct NURLs, one per hint. The code for NURls should be
# refactored out of http_server.py's build_nurl; that code might want
# to skip around for the future when we don't do foolscap, but for now
# this module will be main way we set up HTTPS.
# Tub.myCertificate is a twisted.internet.ssl.PrivateCertificate # Tub.myCertificate is a twisted.internet.ssl.PrivateCertificate
# instance. # instance.
certificate_options = CertificateOptions( certificate_options = CertificateOptions(

View File

@ -10,6 +10,7 @@ from base64 import b64decode
import binascii import binascii
from tempfile import TemporaryFile from tempfile import TemporaryFile
from cryptography.x509 import Certificate
from zope.interface import implementer from zope.interface import implementer
from klein import Klein from klein import Klein
from twisted.web import http from twisted.web import http
@ -843,6 +844,29 @@ class _TLSEndpointWrapper(object):
) )
def build_nurl(
hostname: str, port: int, swissnum: str, certificate: Certificate
) -> DecodedURL:
"""
Construct a HTTPS NURL, given the hostname, port, server swissnum, and x509
certificate for the server. Clients can then connect to the server using
this NURL.
"""
return DecodedURL().replace(
fragment="v=1", # how we know this NURL is HTTP-based (i.e. not Foolscap)
host=hostname,
port=port,
path=(swissnum,),
userinfo=(
str(
get_spki_hash(certificate),
"ascii",
),
),
scheme="pb",
)
def listen_tls( def listen_tls(
server: HTTPServer, server: HTTPServer,
hostname: str, hostname: str,
@ -862,22 +886,14 @@ def listen_tls(
""" """
endpoint = _TLSEndpointWrapper.from_paths(endpoint, private_key_path, cert_path) endpoint = _TLSEndpointWrapper.from_paths(endpoint, private_key_path, cert_path)
def build_nurl(listening_port: IListeningPort) -> DecodedURL: def get_nurl(listening_port: IListeningPort) -> DecodedURL:
nurl = DecodedURL().replace( return build_nurl(
fragment="v=1", # how we know this NURL is HTTP-based (i.e. not Foolscap) hostname,
host=hostname, listening_port.getHost().port,
port=listening_port.getHost().port, str(server._swissnum, "ascii"),
path=(str(server._swissnum, "ascii"),), load_pem_x509_certificate(cert_path.getContent()),
userinfo=(
str(
get_spki_hash(load_pem_x509_certificate(cert_path.getContent())),
"ascii",
),
),
scheme="pb",
) )
return nurl
return endpoint.listen(Site(server.get_resource())).addCallback( return endpoint.listen(Site(server.get_resource())).addCallback(
lambda listening_port: (build_nurl(listening_port), listening_port) lambda listening_port: (get_nurl(listening_port), listening_port)
) )

View File

@ -1084,40 +1084,17 @@ class _FoolscapMixin(_SharedMixin):
class _HTTPMixin(_SharedMixin): class _HTTPMixin(_SharedMixin):
"""Run tests on the HTTP version of ``IStorageServer``.""" """Run tests on the HTTP version of ``IStorageServer``."""
def setUp(self):
self._port_assigner = SameProcessStreamEndpointAssigner()
self._port_assigner.setUp()
self.addCleanup(self._port_assigner.tearDown)
return _SharedMixin.setUp(self)
@inlineCallbacks
def _get_istorage_server(self): def _get_istorage_server(self):
swissnum = b"1234" nurl = self.clients[0].storage_nurls[0]
http_storage_server = HTTPServer(self.server, swissnum)
# Listen on randomly assigned port, using self-signed cert:
private_key = generate_private_key()
certificate = generate_certificate(private_key)
_, endpoint_string = self._port_assigner.assign(reactor)
nurl, listening_port = yield listen_tls(
http_storage_server,
"127.0.0.1",
serverFromString(reactor, endpoint_string),
private_key_to_file(FilePath(self.mktemp()), private_key),
cert_to_file(FilePath(self.mktemp()), certificate),
)
self.addCleanup(listening_port.stopListening)
# Create HTTP client with non-persistent connections, so we don't leak # Create HTTP client with non-persistent connections, so we don't leak
# state across tests: # state across tests:
returnValue( client: IStorageServer = _HTTPStorageServer.from_http_client(
_HTTPStorageServer.from_http_client(
StorageClient.from_nurl(nurl, reactor, persistent=False) StorageClient.from_nurl(nurl, reactor, persistent=False)
) )
) self.assertTrue(IStorageServer.providedBy(client))
# Eventually should also: return succeed(client)
# self.assertTrue(IStorageServer.providedBy(client))
class FoolscapSharedAPIsTests( class FoolscapSharedAPIsTests(