diff --git a/actions/tor b/actions/tor
index cd1bd4347..b03b71b62 100755
--- a/actions/tor
+++ b/actions/tor
@@ -45,7 +45,12 @@ def parse_arguments():
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest='subcommand', help='Sub command')
- subparsers.add_parser('setup', help='Setup Tor configuration')
+ setup_parser = subparsers.add_parser('setup',
+ help='Setup Tor configuration')
+ setup_parser.add_argument(
+ '--old-version', type=int, required=True, help=
+ 'Version number being upgraded from or None if setting up first time.')
+
subparsers.add_parser('get-status', help='Get Tor status in JSON format')
configure = subparsers.add_parser('configure', help='Configure Tor')
@@ -72,9 +77,17 @@ def parse_arguments():
return parser.parse_args()
-def subcommand_setup(_):
+def subcommand_setup(arguments):
"""Setup Tor configuration after installing it."""
+ if arguments.old_version and arguments.old_version <= 2:
+ _upgrade_orport_value()
+ return
+ _first_time_setup()
+
+
+def _first_time_setup():
+ """Setup Tor configuration for the first time setting defaults."""
# Disable default tor service. We will use tor@plinth instance
# instead.
_disable_apt_transport_tor()
@@ -129,6 +142,37 @@ def subcommand_setup(_):
time.sleep(10)
+def _upgrade_orport_value():
+ """Change ORPort value from auto to 9001.
+
+ When ORPort is set to 'auto', Tor automatically allocates a port for it.
+ During it's first run, we able to extract the port number and open the
+ firewall port. However, unlike for pluggable transports, Tor does not seem
+ to store this port for future reuse in the state file. It hence opens a new
+ port every time it is started. This leads to a new port being assigned on
+ next Tor startup and leads to relay functionality not being reachable from
+ outside.
+
+ According to the documentation, only possible values for ORPort are a fixed
+ number or 0 (disable) or auto (above behavior). Choose 9001 as this is
+ the commonly used port number for ORPort. The recommended port number of
+ 443 is not possible in FreedomBox due it is use for other purposes.
+
+ """
+ aug = augeas_load()
+
+ if _is_relay_enabled(aug):
+ aug.set(TOR_CONFIG + '/ORPort', '9001')
+
+ aug.save()
+
+ action_utils.service_try_restart('tor@plinth')
+
+ # Tor may not be running, don't try to read/update all ports
+ _update_port('orport', 9001)
+ action_utils.service_restart('firewalld')
+
+
def subcommand_get_status(_):
"""Get Tor status in JSON format."""
print(json.dumps(get_status()))
@@ -359,7 +403,7 @@ def _enable_relay(relay=None, bridge=None, aug=None):
use_upstream_bridges = _are_upstream_bridges_enabled(aug)
if relay == 'enable' and not use_upstream_bridges:
- aug.set(TOR_CONFIG + '/ORPort', 'auto')
+ aug.set(TOR_CONFIG + '/ORPort', '9001')
elif relay == 'disable':
aug.remove(TOR_CONFIG + '/ORPort')
@@ -440,6 +484,21 @@ def _disable_apt_transport_tor():
aug.save()
+def _update_port(name, number):
+ """Update firewall service information for single port."""
+ lines = """
+
+ Tor - {0}
+
+
+"""
+ try:
+ with open(SERVICE_FILE.format(name), 'w') as service_file:
+ service_file.writelines(lines.format(name, number))
+ except FileNotFoundError:
+ return
+
+
def _update_ports():
"""Update firewall service information."""
ready = False
@@ -458,18 +517,8 @@ def _update_ports():
time.sleep(10)
- lines = """
-
- Tor - {0}
-
-
-"""
for name, number in ports.items():
- try:
- with open(SERVICE_FILE.format(name), 'w') as service_file:
- service_file.writelines(lines.format(name, number))
- except FileNotFoundError:
- return
+ _update_port(name, number)
# XXX: We should ideally do firewalld reload instead. However,
# firewalld seems to fail to successfully reload sometimes.
diff --git a/plinth/modules/tor/__init__.py b/plinth/modules/tor/__init__.py
index 07aeedb37..60f628518 100644
--- a/plinth/modules/tor/__init__.py
+++ b/plinth/modules/tor/__init__.py
@@ -31,7 +31,7 @@ from plinth.signals import domain_added, domain_removed
from . import utils
from .manifest import backup, clients
-version = 2
+version = 3
depends = ['names']
@@ -106,9 +106,12 @@ def init():
def setup(helper, old_version=None):
"""Install and configure the module."""
helper.install(managed_packages)
- helper.call('post', actions.superuser_run, 'tor', ['setup'])
- helper.call('post', actions.superuser_run, 'tor',
- ['configure', '--apt-transport-tor', 'enable'])
+ helper.call(
+ 'post', actions.superuser_run, 'tor',
+ ['setup', '--old-version', str(old_version)])
+ if not old_version:
+ helper.call('post', actions.superuser_run, 'tor',
+ ['configure', '--apt-transport-tor', 'enable'])
global socks_service
if socks_service is None:
@@ -116,7 +119,9 @@ def setup(helper, old_version=None):
'tor-socks', _('Tor Anonymity Network'), ports=['tor-socks'],
is_external=False, is_enabled=utils.is_enabled,
is_running=utils.is_running)
- helper.call('post', socks_service.notify_enabled, None, True)
+
+ if not old_version:
+ helper.call('post', socks_service.notify_enabled, None, True)
global bridge_service
if bridge_service is None:
@@ -124,7 +129,9 @@ def setup(helper, old_version=None):
'tor-bridge', _('Tor Bridge Relay'),
ports=['tor-orport', 'tor-obfs3', 'tor-obfs4'], is_external=True,
is_enabled=utils.is_enabled, is_running=utils.is_running)
- helper.call('post', bridge_service.notify_enabled, None, True)
+
+ if not old_version:
+ helper.call('post', bridge_service.notify_enabled, None, True)
helper.call('post', update_hidden_service_domain)