def run(reactor, cfg, tor, ports): def info(msg): if 'Service descriptor (v2) stored' in msg: got_upload.callback(None) got_upload = Deferred() tor.protocol.add_event_listener('INFO', info) hs = txtorcon.EphemeralHiddenService(ports) yield hs.add_to_tor(tor.protocol) print("Created HS", hs.hostname) def remove(): print("removing hidden-service") return hs.remove_from_tor(tor.protocol) reactor.addSystemEventTrigger('before', 'shutdown', remove) print("...waiting for descriptor upload") yield got_upload print("Got one.", time.asctime()) # we never callback() on this, so we serve forever d = Deferred() yield d
def run(self, options, mainoptions, proto): "ICarmlCommand API" def info(msg): if 'Service descriptor (v2) stored' in msg: got_upload.callback(None) got_upload = Deferred() proto.add_event_listener('INFO', info) hs = txtorcon.EphemeralHiddenService(options['ports']) yield hs.add_to_tor(proto) print("Created HS", hs.hostname) def remove(): print("removing hidden-service") return hs.remove_from_tor(proto) reactor.addSystemEventTrigger('before', 'shutdown', remove) print("...waiting for descriptor upload") yield got_upload print("Got one.", time.asctime()) # we never callback() on this, so we serve forever d = Deferred() yield d
def listen(self, proto_factory): # we don't care which local TCP port we listen on, but # we do need to know it local_ep = TCP4ServerEndpoint(reactor, 0, interface=u"127.0.0.1") target_port = yield local_ep.listen(proto_factory) tor = yield txtorcon.connect( reactor, tor_control_ep, ) # create and add the service hs = txtorcon.EphemeralHiddenService( ports=[ "{} 127.0.0.1:{}".format(port, target_port.getHost().port) ], key_blob_or_type=private_key if private_key else "NEW:BEST", ) log.info("Uploading descriptors can take more than 30s") yield hs.add_to_tor(tor.protocol) # if it's new, store our private key # XXX better "if private_key is None"? if not exists(private_key_fname): with open(private_key_fname, 'w') as f: f.write(hs.private_key) log.info("Wrote private key to '{fname}'", fname=private_key_fname) addr = txtorcon.TorOnionAddress(hs.hostname, port) log.info( "Listening on Tor onion service {addr.onion_uri}:{addr.onion_port}" " with local port {local_port}", addr=addr, local_port=target_port.getHost().port, ) defer.returnValue(addr)
def main(reactor): ep = TCP4ClientEndpoint(reactor, "localhost", 9251) tor_protocol = yield txtorcon.build_tor_connection(ep, build_state=False) print "Connected to Tor" hs_public_port = 80 hs_port = yield txtorcon.util.available_tcp_port(reactor) hs_string = '%s 127.0.0.1:%d' % (hs_public_port, hs_port) print "Adding ephemeral service", hs_string print "(this can take some time; please be patient)" hs = txtorcon.EphemeralHiddenService([hs_string]) yield hs.add_to_tor(tor_protocol) print "Added ephemeral HS to Tor:", hs.hostname print "Starting site" site = server.Site(Simple()) hs_endpoint = TCP4ServerEndpoint(reactor, hs_port, interface='127.0.0.1') yield hs_endpoint.listen(site) # in 5 seconds, remove the hidden service -- obviously this is # where you'd do your "real work" or whatever. d = defer.Deferred() @defer.inlineCallbacks def remove(): print "Removing the hiddenservice. Private key was" print hs.private_key yield hs.remove_from_tor(tor_protocol) d.callback(None) if False: reactor.callLater(5, remove) print "waiting 5 seconds" else: print "waiting forever" yield d
def create_config(reactor, cli_config): txtorcon = _import_txtorcon() if not txtorcon: raise ValueError("Cannot create onion without txtorcon. " "Please 'pip install tahoe-lafs[tor]' to fix this.") tahoe_config_tor = {} # written into tahoe.cfg:[tor] private_dir = os.path.abspath( os.path.join(cli_config["basedir"], "private")) stdout = cli_config.stdout if cli_config["tor-launch"]: tahoe_config_tor["launch"] = "true" tor_executable = cli_config["tor-executable"] if tor_executable: tahoe_config_tor["tor.executable"] = tor_executable print("launching Tor (to allocate .onion address)..", file=stdout) (_, tor_control_proto) = yield _launch_tor(reactor, tor_executable, private_dir, txtorcon) print("Tor launched", file=stdout) else: print("connecting to Tor (to allocate .onion address)..", file=stdout) (port, tor_control_proto) = yield _connect_to_tor(reactor, cli_config, txtorcon) print("Tor connection established", file=stdout) tahoe_config_tor["control.port"] = port external_port = 3457 # TODO: pick this randomly? there's no contention. local_port = allocate_tcp_port() ehs = txtorcon.EphemeralHiddenService("%d 127.0.0.1:%d" % (external_port, local_port)) print("allocating .onion address (takes ~40s)..", file=stdout) yield ehs.add_to_tor(tor_control_proto) print(".onion address allocated", file=stdout) tor_port = "tcp:%d:interface=127.0.0.1" % local_port tor_location = "tor:%s:%d" % (ehs.hostname, external_port) privkey = ehs.private_key yield ehs.remove_from_tor(tor_control_proto) # in addition to the "how to launch/connect-to tor" keys above, we also # record information about the onion service into tahoe.cfg. # * "local_port" is a server endpont string, which should match # "tor_port" (which will be added to tahoe.cfg [node] tub.port) # * "external_port" is the random "public onion port" (integer), which # (when combined with the .onion address) should match "tor_location" # (which will be added to tub.location) # * "private_key_file" points to the on-disk copy of the private key # material (although we always write it to the same place) tahoe_config_tor["onion"] = "true" tahoe_config_tor["onion.local_port"] = str(local_port) tahoe_config_tor["onion.external_port"] = str(external_port) assert privkey tahoe_config_tor["onion.private_key_file"] = os.path.join( "private", "tor_onion.privkey") privkeyfile = os.path.join(private_dir, "tor_onion.privkey") with open(privkeyfile, "wb") as f: f.write(privkey) # tahoe_config_tor: this is a dictionary of keys/values to add to the # "[tor]" section of tahoe.cfg, which tells the new node how to launch # Tor in the right way. # tor_port: a server endpoint string, it will be added to tub.port= # tor_location: a foolscap connection hint, "tor:ONION:EXTERNAL_PORT" # We assume/require that the Node gives us the same data_directory= # at both create-node and startup time. The data directory is not # recorded in tahoe.cfg returnValue((tahoe_config_tor, tor_port, tor_location))