From 863afa34ce53323a6341e293928a745897f0568b Mon Sep 17 00:00:00 2001 From: nbenedek Date: Sun, 30 Oct 2022 15:51:30 +0100 Subject: [PATCH] tor: Add onion location to apache - When hidden service is enabled create and enable an apache site with the proper configurations. This will let visitors using the Tor browser that a hidden version of the website is available. - Disable apache site when hidden service is disabled - Create a backup of the apache site - Hidden service won't be advertised when the user visits mediawiki, wordpress or tt-rss. These sites don't work (well) with a hidden service when a normal domain is already set up. Tests: - Functional tests pass. - With fresh install of Tor app, onion location header apache configuration is enabled and header is served in HTTP requests. - When Onion services are enabled/disabled, header is enabled/disabled due to webserver configuration changes. - When Tor app is enabled/disabled, header is enabled/disabled due to webserver configuration being enabled/disabled. - When Tor app is upgraded from earlier version while app is enabled, onion service is enabled, Tor app remains enabled. Onion location is enabled. - FAILED: When Tor app is upgraded from earlier version while app is enabled, onion service is disabled, Tor app remains enabled. Onion location is disabled. - FAILED: When Tor app is upgraded from earlier version while app is disabled, onion service is enabled, Tor app remains disabled. Onion location is disabled. - FAILED: When Tor app is upgraded from earlier version while app is disabled, onion service is disabled, Tor app remains disabled. Onion location is disabled. Signed-off-by: nbenedek [sunil: Ensure that enabling/disabling app enables/disables onion location] [sunil: Ensure that upgrading from old version does not enable the app] [sunil: Ensure that upgrading from old version enables/disables onion location] [sunil: Apache file should be a 'config' and not 'site'] Signed-off-by: Sunil Mohan Adapa Reviewed-by: James Valleroy --- plinth/modules/tor/__init__.py | 24 ++++++++++++++++++++---- plinth/modules/tor/manifest.py | 5 ++++- plinth/modules/tor/privileged.py | 24 ++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 5 deletions(-) diff --git a/plinth/modules/tor/__init__.py b/plinth/modules/tor/__init__.py index cae907cde..122d8fe57 100644 --- a/plinth/modules/tor/__init__.py +++ b/plinth/modules/tor/__init__.py @@ -8,7 +8,7 @@ from plinth import app as app_module from plinth import cfg, menu from plinth.daemon import (Daemon, app_is_running, diagnose_netcat, diagnose_port_listening) -from plinth.modules.apache.components import diagnose_url +from plinth.modules.apache.components import Webserver, diagnose_url from plinth.modules.backups.components import BackupRestore from plinth.modules.firewall.components import Firewall from plinth.modules.names.components import DomainType @@ -37,7 +37,7 @@ class TorApp(app_module.App): app_id = 'tor' - _version = 5 + _version = 6 def __init__(self): """Create components for the app.""" @@ -81,6 +81,10 @@ class TorApp(app_module.App): (9040, 'tcp6'), (9053, 'udp4'), (9053, 'udp6')]) self.add(daemon) + webserver = Webserver('webserver-onion-location', + 'onion-location-freedombox') + self.add(webserver) + users_and_groups = UsersAndGroups('users-and-groups-tor', reserved_usernames=['debian-tor']) self.add(users_and_groups) @@ -168,8 +172,20 @@ class TorApp(app_module.App): if not old_version: privileged.configure(apt_transport_tor=True) - update_hidden_service_domain() - self.enable() + update_hidden_service_domain(utils.get_status()) + + # Enable/disable Onion-Location component based on app status. + # Component was introduced in version 6. + if old_version and old_version < 6: + daemon_component = self.get_component('daemon-tor') + component = self.get_component('webserver-onion-location') + if daemon_component.is_enabled(): + component.enable() + else: + component.disable() + + if not old_version: + self.enable() def update_hidden_service_domain(status=None): diff --git a/plinth/modules/tor/manifest.py b/plinth/modules/tor/manifest.py index a26fbeac2..0657b6c06 100644 --- a/plinth/modules/tor/manifest.py +++ b/plinth/modules/tor/manifest.py @@ -4,6 +4,8 @@ from django.utils.translation import gettext_lazy as _ from plinth.clients import store_url +from . import privileged + _orbot_package_id = 'org.torproject.android' _tor_browser_download_url = \ 'https://www.torproject.org/download/download-easy.html' @@ -42,7 +44,8 @@ clients = [{ backup = { 'config': { - 'directories': ['/etc/tor/'] + 'directories': ['/etc/tor/'], + 'files': [str(privileged.TOR_APACHE_SITE)] }, 'secrets': { 'directories': ['/var/lib/tor/', '/var/lib/tor-instances/'] diff --git a/plinth/modules/tor/privileged.py b/plinth/modules/tor/privileged.py index 75b572001..d799e4739 100644 --- a/plinth/modules/tor/privileged.py +++ b/plinth/modules/tor/privileged.py @@ -3,6 +3,7 @@ import codecs import os +import pathlib import re import socket import subprocess @@ -20,6 +21,7 @@ SERVICE_FILE = '/etc/firewalld/services/tor-{0}.xml' TOR_CONFIG = '/files/etc/tor/instances/plinth/torrc' TOR_STATE_FILE = '/var/lib/tor-instances/plinth/state' TOR_AUTH_COOKIE = '/var/run/tor-instances/plinth/control.authcookie' +TOR_APACHE_SITE = '/etc/apache2/conf-available/onion-location-freedombox.conf' @privileged @@ -30,6 +32,7 @@ def setup(old_version: int): return _first_time_setup() + _set_onion_header(_get_hidden_service(aug=None)) def _first_time_setup(): @@ -391,6 +394,7 @@ def _enable_hs(aug=None): aug.set(TOR_CONFIG + '/HiddenServicePort[2]', '80 127.0.0.1:80') aug.set(TOR_CONFIG + '/HiddenServicePort[3]', '443 127.0.0.1:443') aug.save() + _set_onion_header(_get_hidden_service(aug)) def _disable_hs(aug=None): @@ -404,6 +408,7 @@ def _disable_hs(aug=None): aug.remove(TOR_CONFIG + '/HiddenServiceDir') aug.remove(TOR_CONFIG + '/HiddenServicePort') aug.save() + _set_onion_header(None) def _enable_apt_transport_tor(): @@ -486,3 +491,22 @@ def augeas_load(): '/etc/tor/instances/plinth/torrc') aug.load() return aug + + +def _set_onion_header(hidden_service): + """Set Apache configuration for the Onion-Location header.""" + config_file = pathlib.Path(TOR_APACHE_SITE) + if hidden_service and hidden_service['enabled']: + # https://community.torproject.org/onion-services/advanced/onion-location/ + hostname = hidden_service['hostname'] + config_contents = f'''# This file is managed by FreedomBox + + Header set Onion-Location "http://{hostname}%{{REQUEST_URI}}s" + +''' + config_file.write_text(config_contents, encoding='utf-8') + else: + config_file.write_text('# This file is managed by FreedomBox\n', + encoding='utf-8') + + action_utils.service_reload('apache2')