Skip to content

Commit

Permalink
add example running relay as tor service
Browse files Browse the repository at this point in the history
  • Loading branch information
monty committed May 22, 2024
1 parent 1e984a1 commit a7642df
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 4 deletions.
40 changes: 40 additions & 0 deletions examples/run_relay_tor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import asyncio
import logging
from monstr.relay.relay import Relay
from monstr.relay.tor import TORService

"""
runs a relay available over tor - this won't be storing any events so all queries to it'll will return empty
This should run with the torbrowser running.
To use with the controller (without the browser up and running), make changes to
/etc/torr/torrc - file maybe somewhere else depending on system
should contain something like ....
#ControlPort 9051
## If you enable the controlport, be sure to enable one of these
## authentication methods, to prevent attackers from accessing it.
#HashedControlPassword 16:775E7AE128CD63126062B2C548F5D2E515E4D15212A43288D7B32112BC
#CookieAuthentication 1
uncomment ControlPort and enable HashedControlPassword
generate the password using: tor --hash-password PASSWORD
uncomment HashedControlPassword and set password to the output of above
restart the tor service: sudo systemctl restart tor
now you should be able to run without the torbrowser open if you pass the password(unhashed) in here
"""
async def run_relay():
port = 8081

with TORService(relay_port=port,
service_dir=None,
password=None,
is_ssl=False,
empheral=True):
r = Relay()
await r.start(port=port)

if __name__ == '__main__':
logging.getLogger().setLevel(logging.DEBUG)
asyncio.run(run_relay())
2 changes: 1 addition & 1 deletion src/monstr/__about__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__='0.1.4'
__version__='0.1.6'
97 changes: 97 additions & 0 deletions src/monstr/relay/tor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import os
from monstr.util import ConfigError
import shutil

try:
from stem.control import Controller
except Exception as e:
pass


class TORService:

def __init__(self, relay_port,
service_dir=None,
password: str = None,
is_ssl: bool = False,
empheral: bool = True):
try:
if Controller:
pass
except NameError as ne:
raise ConfigError(f'No Controller class - try pip install stem')

# the relays actual port if we were to it normally
self._relay_port = relay_port

# password used to auth with controller
# best to supply - but without and if tor_browser is running we still might be ok
# (don't quite understand the exact way this is working)
self._password = password

# the tor service will either be on port 80 http or 443 https
self._service_port = 80
if is_ssl:
self._service_port = 443

# create the tor service as empheral
self._empheral = empheral

# if not empheral then this is the directory where the hidden service will be created
# just give the actual dir, the full path is worked out using the controller class
# for example something like/home/monty/tor-browser/Browser/TorBrowser/Data/[service_dir]
self._hidden_service_dir = service_dir
if self._hidden_service_dir is None:
self._hidden_service_dir = 'monstr_relay'

def __enter__(self):
# this will be default port probably 9051
self._controller = Controller.from_port()
if self._password is None:
self._controller.authenticate()
else:
self._controller.authenticate(password=self._password)

# address of service when we have it
onion_addr = None

# we'll get a new onion address each time
if self._empheral:
result = self._controller.create_ephemeral_hidden_service({self._service_port: self._relay_port},
await_publication=True)

onion_addr = result.service_id + '.onion'
# after first create the onion address will be the same
else:
base_dir = self._controller.get_conf('DataDirectory', '/tmp')
actual_dir = os.path.join(base_dir, self._hidden_service_dir)

print(f' * Creating our hidden service {self._hidden_service_dir} in {base_dir}')
result = self._controller.create_hidden_service(actual_dir,
self._service_port,
target_port=self._relay_port)

onion_addr = None
if result:
onion_addr = result.hostname
else:
# probably the service already exists, try open the service_dir/hostname file
# not sure why create_hidden_services doesn't just return that for us anyway?
try:
f = open(os.path.join(actual_dir, 'hostname'), "r")
lines = f.readlines()
onion_addr = lines[0]
except Exception as e:
pass

if onion_addr:
print(f" hidden service is available at {onion_addr}")
else:
print(
f" Unable to determine our service's hostname, probably due to being unable to read the hidden service directory")

def __exit__(self, exc_type, exc_val, exc_tb):
print(" * Shutting down our hidden service")
self._controller.close()
self._controller.remove_hidden_service(self._hidden_service_dir)
shutil.rmtree(self._hidden_service_dir)
6 changes: 3 additions & 3 deletions src/monstr/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,15 +93,15 @@ def fix_path_str(the_str):
def create_sqlite_store(db_file):
from monstr.event.persist_sqlite import RelaySQLiteEventStore
from monstr.ident.persist import SQLiteProfileStore
from monstr.channels.persist import SQLiteSQLChannelStore
# from monstr.channels.persist import SQLiteSQLChannelStore
from monstr.settings.persist import SQLiteSettingsStore
my_events = RelaySQLiteEventStore(db_file)
if not my_events.exists():
my_events.create()
my_profiles = SQLiteProfileStore(db_file)
my_profiles.create()
my_channels = SQLiteSQLChannelStore(db_file)
my_channels.create()
# my_channels = SQLiteSQLChannelStore(db_file)
# my_channels.create()
db = SQLiteDatabase(db_file)
my_settings = SQLiteSettingsStore(db_file)
my_settings.create()
Expand Down

0 comments on commit a7642df

Please sign in to comment.