From ac958c04f0159a55c45fc99c05dfd8b7eb14d5c1 Mon Sep 17 00:00:00 2001 From: irl Date: Wed, 7 Jan 2026 16:52:10 +0000 Subject: [PATCH] feat: initial import --- .gitignore | 210 +++++++++++++++++++++++++++++++++++ Justfile | 3 + README.md | 70 ++++++++++++ bootbridge/__init__.py | 0 bootbridge/__main__.py | 11 ++ bootbridge/app.py | 64 +++++++++++ bootbridge/apt.py | 95 ++++++++++++++++ bootbridge/configure.py | 110 ++++++++++++++++++ bootbridge/console.py | 3 + bootbridge/httpserver.py | 39 +++++++ bootbridge/settings.py | 96 ++++++++++++++++ bootbridge/systemd.py | 44 ++++++++ bootbridge/tor.py | 73 ++++++++++++ bootbridge/utils.py | 121 ++++++++++++++++++++ bootbridge/webtunnel.py | 162 +++++++++++++++++++++++++++ bootbridge/zerossl.py | 234 +++++++++++++++++++++++++++++++++++++++ config.toml.example | 7 ++ docs/concept.jpg | Bin 0 -> 70881 bytes install.sh | 14 +++ pyproject.toml | 42 +++++++ 20 files changed, 1398 insertions(+) create mode 100644 .gitignore create mode 100644 Justfile create mode 100644 README.md create mode 100644 bootbridge/__init__.py create mode 100644 bootbridge/__main__.py create mode 100644 bootbridge/app.py create mode 100644 bootbridge/apt.py create mode 100644 bootbridge/configure.py create mode 100644 bootbridge/console.py create mode 100644 bootbridge/httpserver.py create mode 100644 bootbridge/settings.py create mode 100644 bootbridge/systemd.py create mode 100644 bootbridge/tor.py create mode 100644 bootbridge/utils.py create mode 100644 bootbridge/webtunnel.py create mode 100644 bootbridge/zerossl.py create mode 100644 config.toml.example create mode 100644 docs/concept.jpg create mode 100644 install.sh create mode 100644 pyproject.toml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8cba423 --- /dev/null +++ b/.gitignore @@ -0,0 +1,210 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[codz] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py.cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# UV +# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +#uv.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock +#poetry.toml + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python. +# https://pdm-project.org/en/latest/usage/project/#working-with-version-control +#pdm.lock +#pdm.toml +.pdm-python +.pdm-build/ + +# pixi +# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control. +#pixi.lock +# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one +# in the .venv directory. It is recommended not to include this directory in version control. +.pixi + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.envrc +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +.idea/ + +# Abstra +# Abstra is an AI-powered process automation framework. +# Ignore directories containing user credentials, local state, and settings. +# Learn more at https://abstra.io/docs +.abstra/ + +# Visual Studio Code +# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore +# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore +# and can be added to the global gitignore or merged into this file. However, if you prefer, +# you could uncomment the following to ignore the entire vscode folder +# .vscode/ + +# Ruff stuff: +.ruff_cache/ + +# PyPI configuration file +.pypirc + +# Cursor +# Cursor is an AI-powered code editor. `.cursorignore` specifies files/directories to +# exclude from AI features like autocomplete and code analysis. Recommended for sensitive data +# refer to https://docs.cursor.com/context/ignore-files +.cursorignore +.cursorindexingignore + +# Marimo +marimo/_static/ +marimo/_lsp/ +__marimo__/ + +# Streamlit +.streamlit/secrets.toml diff --git a/Justfile b/Justfile new file mode 100644 index 0000000..66eede3 --- /dev/null +++ b/Justfile @@ -0,0 +1,3 @@ +build: + rm -rf dist + hatch build -t sdist diff --git a/README.md b/README.md new file mode 100644 index 0000000..5650b0c --- /dev/null +++ b/README.md @@ -0,0 +1,70 @@ + +

+ Guardian Project +

+ +

bootbridge

+

+A command line tool to bootstrap a Tor bridge +(not to be confused with bridgestrap). + +

+

+Language: Python + + Licence: BSD 2-Clause + + Lifecycle: Experimental +
+ + Issues + + + Open Collective backers and sponsors + +

+ +--- + +### Purpose + +You want to run many Tor bridges and would like to quickly bootstrap them from a fresh Debian installation. + +### Requirements + +* Debian stable VM fresh out of the box that won't be used for any other purpose +* Either: + * SSH access to that box + * Ability to give it cloud-init user data + +### Caveats + +* Many cloud providers now implement network firewalls outside the control of the operating system. + This is good for security in that if the virtual machine is compromised, it's not possible to change the firewall + rules, but it also means that you can't deliberately change the firewall rules from inside the virtual machine. + If your cloud has such a feature, it'll be up to you to configure that separately, perhaps with + [OpenTofu](https://opentofu.org/). + +### Concept + +![original concept](./docs/concept.jpg) + +### Copyright + +Copyright © 2022-2025 SR2 Communications Limited. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/bootbridge/__init__.py b/bootbridge/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/bootbridge/__main__.py b/bootbridge/__main__.py new file mode 100644 index 0000000..50f9d0a --- /dev/null +++ b/bootbridge/__main__.py @@ -0,0 +1,11 @@ +import os +import sys + +if __name__ == "__main__": + if not __package__: + package_source_path = os.path.dirname(os.path.dirname(__file__)) + sys.path.insert(0, package_source_path) + + from bootbridge.app import app + + app() diff --git a/bootbridge/app.py b/bootbridge/app.py new file mode 100644 index 0000000..523b350 --- /dev/null +++ b/bootbridge/app.py @@ -0,0 +1,64 @@ +import os +import sys + +import typer +from typing_extensions import Annotated + +from bootbridge.settings import validate_settings, settings +from bootbridge.configure import ConfigureCommand +from bootbridge.console import console +from bootbridge.webtunnel import up_webtunnel + +if not __package__: + package_source_path = os.path.dirname(os.path.dirname(__file__)) + sys.path.insert(0, package_source_path) + +app = typer.Typer( + help="Bootstrap a Tor bridge", + no_args_is_help=True, +) + + +@app.command(rich_help_panel="Configure") +def configure(): + """ + Interactively define configuration for a Tor bridge. + """ + ConfigureCommand().run() + + + +@app.command(rich_help_panel="Provision") +def up( + force_tls_renewal: Annotated[ + bool, typer.Option(help="Force renewal of TLS certificate(s), if any.") + ] = False, +): + """ + Bootstrap a Tor bridge. + """ + up_functions = { + "webtunnel": up_webtunnel, + } + console.rule("[bold]bootbridge up") + validate_settings() + try: + up_functions[settings.bridge.transport]( + settings.bridge, force_tls_renewal=force_tls_renewal + ) + except KeyError: + raise NotImplementedError() + raise typer.Exit() + + +@app.command(rich_help_panel="Utilities") +def validate(): + """ + Validate configuration. + """ + validate_settings() + raise typer.Exit() + + +if __name__ == "__main__": + app() diff --git a/bootbridge/apt.py b/bootbridge/apt.py new file mode 100644 index 0000000..201d6a0 --- /dev/null +++ b/bootbridge/apt.py @@ -0,0 +1,95 @@ +import base64 +import subprocess +from functools import cache + +import typer + +from bootbridge.console import console +from bootbridge.utils import run_command, requests_session, ensure_file + + +@cache +def get_codename() -> str: + return subprocess.check_output(["lsb_release", "-cs"], text=True).strip() + + +def setup_deb_tpo() -> None: + with console.status("Configuring Tor Project Debian repository..."): + s = requests_session() + r = s.get( + "https://deb.torproject.org/torproject.org/A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89.asc" + ) + r.raise_for_status() + if r.headers["content-type"] == "application/pgp-keys": + key = r.content + else: + key = dearmor_gpg_key(r.content) + ensure_file( + "/usr/share/keyrings/tor-archive-keyring.gpg", + key, + owner=0, + group=0, + mode=0o644, + ) + ensure_file( + "/etc/apt/sources.list.d/tor.list", + f"""deb [signed-by=/usr/share/keyrings/tor-archive-keyring.gpg] https://deb.torproject.org/torproject.org {get_codename()} main + deb-src [signed-by=/usr/share/keyrings/tor-archive-keyring.gpg] https://deb.torproject.org/torproject.org {get_codename()} main""", + owner=0, + group=0, + mode=0o644, + ) + + +def apt_update(upgrade: bool = True) -> None: + for action in [ + ("Updating apt package lists", "update"), + ("Upgrading apt packages", "upgrade"), + ]: + with console.status(action[0] + "..."): + try: + command = ["apt", "-y", action[1]] + for output in run_command(command): + console.log("[gray30]" + output) + except subprocess.CalledProcessError: + console.log(f"[red]An error occurred while {action[0].lower()}. :boom:") + console.log( + f"[blue]To debug, you can try running '{' '.join(command)}' yourself." + ) + raise typer.Exit(1) + console.log( + f":white_check_mark: Updated the apt package lists{' and upgraded packages' if upgrade else ''}." + ) + + +def apt_install(pkgs: str | list[str]) -> None: + with console.status(f"Installing apt packages ({', '.join(pkgs)})..."): + try: + if isinstance(pkgs, str): + pkgs = [pkgs] + command = ["apt", "install", "-y"] + pkgs + for output in run_command(command): + console.log("[gray30]" + output) + except subprocess.CalledProcessError: + console.log( + "[red]An error occurred while installing packages with apt. :boom:" + ) + console.log( + f"[blue]To debug, you can try running 'apt install {' '.join(pkgs)}' yourself." + ) + raise typer.Exit(1) + console.log(f":white_check_mark: Installed apt packages ({', '.join(pkgs)}).") + + +def dearmor_gpg_key(ascii_key: bytes) -> bytes: + lines = [line.strip() for line in ascii_key.strip().splitlines()] + if ( + lines[0] != b"-----BEGIN PGP PUBLIC KEY BLOCK-----" + or lines[-1] != b"-----END PGP PUBLIC KEY BLOCK-----" + ): + raise ValueError("Invalid PGP key format") + start_line = lines.index(b"") + body = lines[start_line:-1] + key_data = b"".join(body) + binary_key = base64.b64decode(key_data) + return binary_key diff --git a/bootbridge/configure.py b/bootbridge/configure.py new file mode 100644 index 0000000..057903d --- /dev/null +++ b/bootbridge/configure.py @@ -0,0 +1,110 @@ +import os +import random +import string +from typing import Any + +import petname +import typer +import yaml +from rich.panel import Panel +from rich.prompt import Confirm, Prompt + +from bootbridge.settings import CONFIG_PATH, validate_settings +from bootbridge.console import console +from bootbridge.utils import ensure_file + + +class ConfigureCommand: + settings: dict[str, Any] + old_settings: None | dict[str, Any] = None + + def load_old_settings(self): + if os.path.exists(CONFIG_PATH): + if Confirm.ask("Would you like to load existing settings?"): + with open(CONFIG_PATH, "r") as config_file: + self.old_settings = yaml.safe_load(config_file) + + def old_setting( + self, key: str, default: Any, current: None | dict[str, Any] = None + ) -> Any: + if current is None: + if not (current := self.old_settings): + return default + key_parts = key.split(".") + if len(key_parts) == 1: + try: + return current[key_parts[0]] + except KeyError: + return default + else: + try: + return self.old_setting( + ".".join(key_parts[1:]), default, current[key_parts[0]] + ) + except KeyError: + return default + + def ask_webtunnel(self): + self.ask( + "Enter a URL path", + "bridge.webtunnel.path", + "".join( + random.choice(string.ascii_letters + string.digits) + for _ in range(random.randint(30, 50)) + ), + ) + self.ask( + "Choose a certificate method", + "bridge.webtunnel.certificate", + "zerossl-ip", + choices=["zerossl-ip"], + ) + if self.settings["bridge"]["webtunnel"]["certificate"] == "zerossl-ip": + self.ask("Enter your ZeroSSL API token", "zerossl.token", None) + + def ask( + self, prompt: str, key: str, default: str | None, choices: list[str] | None = None + ) -> None: + result = Prompt.ask( + prompt, default=self.old_setting(key, default), choices=choices + ) + key_parts = key.split(".") + current = self.settings + while len(key_parts) > 1: + head = key_parts.pop(0) + if head not in current: + current[head] = {} + current = current[head] + current[key_parts[0]] = result + + def save(self): + ensure_file( + CONFIG_PATH, + yaml.dump(self.settings, indent=2), + owner=0, + group=0, + mode=0o600, + ) + + def run(self): + console.rule("bootbridge configure") + self.load_old_settings() + self.settings = {} + self.ask( + "Enter a nickname", + "bridge.nickname", + petname.Generate(3).title().replace("-", ""), + ) + self.ask("Enter contact details", "bridge.contact", "n/a") + self.ask("Choose a transport", "bridge.transport", "webtunnel", ["webtunnel"]) + if self.settings["bridge"]["transport"] == "webtunnel": + self.ask_webtunnel() + self.save() + validate_settings() + console.print( + Panel( + ":floppy_disk: Configuration saved.\n\n" + ":toolbox: Run [bold]bootbridge up[/bold] to bootstrap this bridge." + ) + ) + raise typer.Exit() diff --git a/bootbridge/console.py b/bootbridge/console.py new file mode 100644 index 0000000..a9463af --- /dev/null +++ b/bootbridge/console.py @@ -0,0 +1,3 @@ +from rich.console import Console + +console = Console() diff --git a/bootbridge/httpserver.py b/bootbridge/httpserver.py new file mode 100644 index 0000000..3857b30 --- /dev/null +++ b/bootbridge/httpserver.py @@ -0,0 +1,39 @@ +import threading +import time +from http.server import BaseHTTPRequestHandler, HTTPServer + + +def bootstrap_http_handler(content: str): + class BootstrapHTTPHandler(BaseHTTPRequestHandler): + def do_GET(self) -> None: + self.send_response(200) + self.send_header("Content-type", "text/plain") + self.end_headers() + self.wfile.write(content.encode("utf-8")) + + return BootstrapHTTPHandler + + +server_instance: HTTPServer | None = None + + +def _start_bootstrap_server(content: str) -> None: + global server_instance + server_address = ("", 80) + server_instance = HTTPServer(server_address, bootstrap_http_handler(content)) + server_instance.serve_forever() + + +def start_bootstrap_server(content: str) -> None: + server_thread = threading.Thread(target=_start_bootstrap_server, args=(content,)) + server_thread.daemon = True + server_thread.start() + time.sleep(1) # Give the server a second to start + + +def stop_bootstrap_server() -> None: + global server_instance + if server_instance: + server_instance.shutdown() + server_instance.server_close() + server_instance = None diff --git a/bootbridge/settings.py b/bootbridge/settings.py new file mode 100644 index 0000000..aedad8d --- /dev/null +++ b/bootbridge/settings.py @@ -0,0 +1,96 @@ +import os +import time +from typing import Literal, Type, Any + +import typer +from pydantic import BaseModel, ValidationError +from pydantic_settings import ( + BaseSettings, + PydanticBaseSettingsSource, + SettingsConfigDict, + YamlConfigSettingsSource, +) + +from bootbridge.console import console + +CONFIG_PATH = os.getenv("BOOTBRIDGE_CONFIG", "/etc/bootbridge.yaml") + + +class WebtunnelSettings(BaseModel): + certificate: Literal["zerossl-ip"] + path: str + + +class KeysSettings(BaseModel): + ed25519_master_id_public_key: str + ed25519_master_id_secret_key: str + secret_id_key: str + + +class BridgeSettings(BaseModel): + nickname: str # TODO: string constraints + contact: str # TODO: string constraints + transport: Literal["webtunnel"] + webtunnel: WebtunnelSettings | None = None + keys: KeysSettings | None = None + + +class ZerosslSettings(BaseModel): + token: str # TODO: string constraints? + + +class Settings(BaseSettings): + model_config = SettingsConfigDict(yaml_file=CONFIG_PATH) + + bridge: BridgeSettings + zerossl: ZerosslSettings | None = None + + @classmethod + def settings_customise_sources( + cls, + settings_cls: Type[BaseSettings], + init_settings: PydanticBaseSettingsSource, + env_settings: PydanticBaseSettingsSource, + dotenv_settings: PydanticBaseSettingsSource, + file_secret_settings: PydanticBaseSettingsSource, + ) -> tuple[PydanticBaseSettingsSource, ...]: + return (YamlConfigSettingsSource(settings_cls),) + + +settings: Settings + + +def ensure_setting(key: str, current: dict[str, Any] | None = None) -> None: + if current is None: + current = settings + if current is None: + console.log(":boom: No settings available.") + raise typer.Exit(1) + key_parts = key.split(".") + head = key_parts.pop(0) + return ensure_setting(head, current) + + +def validate_settings(): + global settings + with console.status("Validating settings..."): + time.sleep(0.5) + try: + settings = Settings() + except ValidationError as exc: + console.log(str(exc)) + raise typer.Exit(1) + bridge = settings.bridge + if bridge.transport == "webtunnel": + if bridge.webtunnel is None: + console.log( + ":boom: [red]Missing webtunnel section in configuration file." + ) + raise typer.Exit(1) + if bridge.webtunnel.certificate == "zerossl-ip": + if settings.zerossl is None: + console.log( + ":boom: [red]Missing ZeroSSL section in configuration file." + ) + typer.Exit(1) + console.log(":white_check_mark: Settings validated") diff --git a/bootbridge/systemd.py b/bootbridge/systemd.py new file mode 100644 index 0000000..2543eee --- /dev/null +++ b/bootbridge/systemd.py @@ -0,0 +1,44 @@ +import subprocess + +import typer + +from bootbridge.console import console +from bootbridge.utils import run_command + + +def start_service(service: str, enable: bool = False, restart: bool = False) -> None: + actions = ["restart" if restart else "start"] + if enable: + actions = ["enable"] + actions + with console.status(f"Starting {service}..."): + for action in actions: + try: + command = ["systemctl", action, service] + for output in run_command(command): + console.log("[gray30]" + output) + except subprocess.CalledProcessError: + console.log(f"[red]An error occurred while starting {service}. :boom:") + console.log( + f"[blue]To debug, you can try running '{' '.join(command)}' yourself." + ) + raise typer.Exit(1) + console.log(f":white_check_mark: {'Res' if restart else 'S'}tarted {service}.") + + +def stop_service(service: str, disable: bool = False) -> None: + actions = ["stop"] + if disable: + actions = actions + ["disable"] + with console.status(f"Stopping {service}..."): + for action in actions: + try: + command = ["systemctl", action, service] + for output in run_command(command): + console.log("[gray30]" + output) + except subprocess.CalledProcessError: + console.log(f"[red]An error occurred while stopping {service}. :boom:") + console.log( + f"[blue]To debug, you can try running '{' '.join(command)}' yourself." + ) + raise typer.Exit(1) + console.log(f":white_check_mark: Stopped {service}.") diff --git a/bootbridge/tor.py b/bootbridge/tor.py new file mode 100644 index 0000000..ca57ff5 --- /dev/null +++ b/bootbridge/tor.py @@ -0,0 +1,73 @@ +import base64 +import os +import time + +from bootbridge.settings import KeysSettings +from bootbridge.console import console +from bootbridge.utils import ensure_file, gid, uid, ensure_dir + + +def get_tor_data_file(filename: str) -> str: # TODO: add timeout + with console.status(f"Waiting for {filename.replace('-', ' ')}..."): + while not os.path.exists(f"/var/lib/tor/{filename}"): + time.sleep(1) + with open(f"/var/lib/tor/{filename}") as data_file: + return data_file.read() + + +def get_fingerprint(): + return get_tor_data_file("fingerprint").split(" ")[1].strip() + + +def get_hashed_fingerprint(): + return get_tor_data_file("hashed-fingerprint").split(" ")[1].strip() + + +def render_torrc( + nickname: str, + contact: str, + extra_config=list[str], +): + return "\n".join( + [ + "# -- This file managed by bridgeboot. Don't edit it. --", + "DataDirectory /var/lib/tor", + "KeyDirectory /var/lib/tor/bootbridge_keys", + f"Nickname {nickname}", + f"ContactInfo {contact}", + "BridgeRelay 1", + "AssumeReachable 1", + "ORPort 127.0.0.1:auto", + "ExtORPort auto", + "SocksPort 0", + "Log notice file /var/log/tor/bootbridge.log" "", + "# -- Transport specific configuration below --", + ] + + extra_config + + [""] + ) + + +def write_keys(keys: KeysSettings): + os.makedirs("/var/lib/tor/bootbridge_keys", exist_ok=True) + ensure_dir( + "/var/lib/tor/bootbridge_keys", + owner=uid("debian-tor"), + group=gid("debian-tor"), + mode=0o2700, + ) + ensure_file( + "/var/lib/tor/bootbridge_keys/secret_id_key", + keys.secret_id_key, + uid("debian-tor"), + gid("debian-tor"), + 0o600, + ) + for key in ["ed25519_master_id_secret_key", "ed25519_master_id_public_key"]: + ensure_file( + f"/var/lib/tor/bootbridge_keys/{key}", + base64.b64decode(getattr(keys, key)), + uid("debian-tor"), + gid("debian-tor"), + 0o600, + ) diff --git a/bootbridge/utils.py b/bootbridge/utils.py new file mode 100644 index 0000000..aac69c6 --- /dev/null +++ b/bootbridge/utils.py @@ -0,0 +1,121 @@ +import grp +import os +import pwd +import select +import subprocess +from functools import cache +from typing import Generator + +import requests +from requests.adapters import HTTPAdapter +from rich.progress import Progress, TextColumn, BarColumn +from urllib3 import Retry + +from bootbridge.console import console + + +def requests_session() -> requests.Session: + s = requests.Session() + retries = Retry(total=5, backoff_factor=1, status_forcelist=[500, 502, 503, 504]) + s.mount("http://", HTTPAdapter(max_retries=retries)) + s.mount("https://", HTTPAdapter(max_retries=retries)) + return s + + +def download_file( + description: str, url: str, path: str, owner: int, group: int, mode: int = 0o644 +) -> None: + response = requests.get(url, stream=True) + total_size = int(response.headers.get("content-length", 0)) + with Progress( + TextColumn("[bold blue]{task.description}"), + BarColumn(), + TextColumn("[bold green]{task.completed}/{task.total}"), + transient=True, + ) as progress: + task = progress.add_task(f"Downloading {description}...", total=total_size) + with open(path, "wb") as file: + for data in response.iter_content(chunk_size=1024): + file.write(data) + progress.update(task, advance=len(data)) + console.log(f":floppy_disk: Saved {os.path.abspath(path)}.") + ensure_file(path, None, owner=owner, group=group, mode=mode) + + +@cache +def get_public_ip() -> str: + r = requests_session().get("https://ipv4.icanhazip.com/") + return r.text.strip() + + +@cache +def uid(user: str) -> int: + return pwd.getpwnam("debian-tor").pw_uid + + +@cache +def gid(group: str) -> int: + return grp.getgrnam("debian-tor").gr_gid + + +def ensure_dir(path: str, owner: int, group: int, mode: int = 0o755): + parts = path.split(os.sep) + current_path = "" + for part in parts: + if part: # Skip empty parts (e.g., leading separators) + current_path = os.path.join(current_path, part) + if not os.path.exists(current_path): + os.mkdir(current_path) + os.chown(current_path, owner, group) + os.chmod(current_path, mode) + console.log(f":floppy_disk: Created directory {current_path}.") + + +def ensure_file( + path: str, + content: None | str | bytes, + owner: int = 0, + group: int = 0, + mode: int = 0o640, +): + if content: + new_content = False + with open(path, "r" if isinstance(content, str) else "rb") as file: + new_content = file.read() != content + if new_content: + open_mode = "w" if isinstance(content, str) else "wb" + with open(path, open_mode) as file: + file.write(content) + console.log(f":floppy_disk: Saved {os.path.abspath(path)}.") + else: + console.log(f":floppy_disk: Already up to date {os.path.abspath(path)}.") + stat = os.stat(path) + if stat.st_uid != owner or stat.st_gid != group: + os.chown(path, owner, group) + console.log(f":floppy_disk: Changed ownership of {os.path.abspath(path)}.") + if stat.st_mode & 0o7777 != mode: + os.chmod(path, mode) + console.log(f":floppy_disk: Changed permissions of {os.path.abspath(path)}.") + + +def run_command(command: list[str]) -> Generator[str, None, None]: + process = subprocess.Popen( + command, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + env={"DEBIAN_FRONTEND": "noninteractive"}, + ) + while True: + reads = [process.stdout, process.stderr] + ready, _, _ = select.select(reads, [], []) + for stream in ready: + line = stream.readline() + if line: + yield line.strip() + else: + break + if process.poll() is not None: + break + if process.returncode != 0: + raise subprocess.CalledProcessError(process.returncode, command) diff --git a/bootbridge/webtunnel.py b/bootbridge/webtunnel.py new file mode 100644 index 0000000..5a3bdc0 --- /dev/null +++ b/bootbridge/webtunnel.py @@ -0,0 +1,162 @@ +import os +import subprocess + +from rich.panel import Panel + +from bootbridge.apt import apt_install, apt_update, setup_deb_tpo +from bootbridge.settings import BridgeSettings +from bootbridge.console import console +from bootbridge.systemd import start_service, stop_service +from bootbridge.tor import ( + render_torrc, + get_hashed_fingerprint, + get_fingerprint, + write_keys, +) +from bootbridge.utils import download_file, ensure_file +import bootbridge.zerossl as zerossl +from bootbridge.utils import get_public_ip + +NGINX_VIRTUALHOST_CONFIG = """ +server { + listen 80 default_server; + listen [::]:80 default_server; + + listen [::]:443 ssl http2 default_server; + listen 443 ssl http2 default_server; + server_name _; + + server_tokens off; + + ssl_certificate /etc/ssl/private/combined.pem; + ssl_certificate_key /etc/ssl/private/key.pem; + + ssl_session_timeout 15m; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_prefer_server_ciphers off; + ssl_session_tickets off; + + add_header Strict-Transport-Security "max-age=63072000" always; + + root /var/www/html; + + location / { + try_files $uri $uri/ =403; + } + + location = /!WEBTUNNEL_PATH! { + proxy_pass http://127.0.0.1:15000; + proxy_http_version 1.1; + + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + + proxy_set_header Accept-Encoding ""; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + add_header Front-End-Https on; + + proxy_redirect off; + access_log off; + error_log off; + } +} +""" + + +def download_webtunnel() -> None: + binary_path = "/usr/local/bin/webtunnel_server" + if not os.path.exists(binary_path): + download_file( + "Webtunnel server binary", + "https://guardianproject.dev/api/packages/irl/generic/webtunnel/202507080/webtunnel_server_amd64", + binary_path, + owner=0, + group=0, + mode=0o755, + ) + console.log(":white_check_mark: Webtunnel server binary downloaded.") + else: + console.log(":white_check_mark: Webtunnel server binary already present.") + + +def write_nginx_vhost_config(path: str): + with console.status("Writing nginx virtual host configuration file..."): + ensure_file( + "/etc/nginx/sites-available/default", + NGINX_VIRTUALHOST_CONFIG.replace("!WEBTUNNEL_PATH!", path), + owner=0, + group=0, + mode=0o644, + ) + + +def configure_tor(settings: BridgeSettings): + with console.status("Writing Tor configuration file..."): + torrc_extra_lines = [ + "ServerTransportPlugin webtunnel exec /usr/local/bin/webtunnel_server", + "ServerTransportListenAddr webtunnel 127.0.0.1:15000", + f"ServerTransportOptions webtunnel url=https://{get_public_ip()}/{settings.webtunnel.path}", + ] + ensure_file( + "/etc/tor/torrc", + render_torrc( + settings.nickname, + settings.contact, + extra_config=torrc_extra_lines, + ), + owner=0, + group=0, + mode=0o644, + ) + if settings.keys: + with console.status("Writing Tor keys..."): + write_keys(settings.keys) + + +def update_apparmor(): + with console.status("Configure apparmor profile override..."): + ensure_file( + "/etc/apparmor.d/local/system_tor", + "/usr/local/bin/webtunnel_server ix,", + owner=0, + group=0, + mode=0o644, + ) + subprocess.check_output(["apparmor_parser", "-r", "/etc/apparmor.d/system_tor"]) + console.log(":white_check_mark: Apparmor profile override updated.") + + +def up_webtunnel(settings: BridgeSettings, force_tls_renewal: bool = False): + console.rule("Install system packages") + setup_deb_tpo() + apt_update() + apt_install(["tor", "nginx"]) + download_webtunnel() + console.rule("Provision TLS certificate") + if not os.path.exists("/etc/ssl/private/combined.pem"): + stop_service("nginx") + else: + start_service("nginx") + if settings.webtunnel.certificate == "zerossl-ip": + zerossl.zerossl_ip_certificate(force_renew=force_tls_renewal) + console.rule("Configure nginx") + write_nginx_vhost_config(settings.webtunnel.path) + start_service("nginx", enable=True, restart=True) + console.rule("Configure Tor") + configure_tor(settings) + update_apparmor() + start_service("tor", enable=True, restart=True) + console.print( + Panel( + ":thumbsup: Your Tor bridge is up.\n\n" + ":globe_with_meridians: Connect to your bridge with the following bridgeline:\n\n" + f" webtunnel 10.0.0.2:443 {get_fingerprint()} url=https://{get_public_ip()}/{settings.webtunnel.path}\n\n" + ":bar_chart: Check out your bridge metrics on the Tor Metrics website (may take a while to appear):\n\n" + f" https://metrics.torproject.org/rs.html#details/{get_hashed_fingerprint()}\n\n" + ":vertical_traffic_light: Check the status of your bridge (may take a while to appear):\n\n" + f" https://bridges.torproject.org/status?id={get_hashed_fingerprint()}" + ) + ) diff --git a/bootbridge/zerossl.py b/bootbridge/zerossl.py new file mode 100644 index 0000000..1b1845b --- /dev/null +++ b/bootbridge/zerossl.py @@ -0,0 +1,234 @@ +import os +import sys +import time +from ipaddress import IPv4Address +from typing import Any + +import typer +from cryptography import x509 +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import hashes, serialization +from cryptography.hazmat.primitives.asymmetric import rsa + +from bootbridge.console import console +from bootbridge.httpserver import start_bootstrap_server, stop_bootstrap_server +from bootbridge.settings import settings +from bootbridge.utils import get_public_ip, requests_session, ensure_file, ensure_dir + +ID_PATH = "/etc/ssl/private/certificate.id" +KEY_PATH = "/etc/ssl/private/key.pem" +CERTIFICATE_PATH = "/etc/ssl/private/certificate.pem" +BUNDLE_PATH = "/etc/ssl/private/bundle.pem" +COMBINED_PATH = "/etc/ssl/private/combined.pem" + +WEBROOT_PATH = "/var/www/html" +VALIDATION_PATH = "/.well-known/pki-validation" + +def stage_validation(url: str, content: str) -> None: + validation_dir = os.path.join(WEBROOT_PATH, VALIDATION_PATH) + filename = os.path.basename(url) + ensure_dir(validation_dir, owner=0, group=0, mode=0o755) + ensure_file( + os.path.join(validation_dir, filename), + "\n".join(content), + owner=0, + group=0, + mode=0o644, + ) + +def get_certificate_id() -> str | None: + if os.path.exists(ID_PATH): + with open(ID_PATH, "r") as id_file: + return id_file.read() + return None + +def generate_tls_key() -> rsa.RSAPrivateKey: + private_key = rsa.generate_private_key( + public_exponent=65537, key_size=2048, backend=default_backend() + ) + return private_key + +def write_tls_key(private_key: rsa.RSAPrivateKey) -> None: + ensure_file( + KEY_PATH, + private_key.private_bytes( + encoding=serialization.Encoding.PEM, + encryption_algorithm=serialization.NoEncryption(), + format=serialization.PrivateFormat.TraditionalOpenSSL, + ), + owner=0, + group=0, + mode=0o600, + ) + +def read_tls_key() -> rsa.RSAPrivateKey: + with open(KEY_PATH, "rb") as key_file: + key = serialization.load_pem_private_key( + key_file.read(), password=None, backend=default_backend() + ) + assert isinstance(key, rsa.RSAPrivateKey) + return key + +def generate_tls_csr(ip, private_key: rsa.RSAPrivateKey) -> str: + subject = x509.Name([x509.NameAttribute(x509.NameOID.COMMON_NAME, ip)]) + alt_names = x509.SubjectAlternativeName([x509.IPAddress(IPv4Address(ip))]) + csr = ( + x509.CertificateSigningRequestBuilder() + .subject_name(subject) + .add_extension(alt_names, critical=False) + .sign(private_key, hashes.SHA256(), default_backend()) + ) + return csr.public_bytes(serialization.Encoding.PEM).decode("utf-8") + +def write_certificate(cert: str, bundle: str) -> None: + ensure_file(CERTIFICATE_PATH, cert, owner=0, group=0, mode=0o644) + ensure_file(BUNDLE_PATH, bundle, owner=0, group=0, mode=0o644) + ensure_file(COMBINED_PATH, cert + "\n" + bundle, owner=0, group=0, mode=0o644) + +def save_certificate_id(id: str): + with open(ID_PATH, "w") as id_file: + id_file.write(id) + +class ZeroSSLManager: + def __init__(self, token: str): + self.token = token + + def create_certificate(self, ip: str, csr: str) -> dict[str, Any]: + data = { + "certificate_domains": ip, + "certificate_csr": csr, + } + response = requests_session().post( + "https://api.zerossl.com/certificates", + json=data, + params={"access_key": self.token}, + ) + response.raise_for_status() + return response.json() + + def validate_certificate(self, id: str) -> dict[str, Any]: + data = { + "validation_method": "HTTP_CSR_HASH", + } + response = requests_session().post( + f"https://api.zerossl.com/certificates/{id}/challenges", + json=data, + params={"access_key": self.token}, + ) + response.raise_for_status() + return response.json() + + def get_certificate(self, id: str) -> dict[str, Any]: + response = requests_session().get( + f"https://api.zerossl.com/certificates/{id}", + params={"access_key": self.token}, + ) + response.raise_for_status() + return response.json() + + def download_certificate(self, id: str) -> dict[str, Any]: + response = requests_session().get( + f"https://api.zerossl.com/certificates/{id}/download/return", + params={"access_key": self.token}, + ) + response.raise_for_status() + return response.json() + + def revoke_certificate(self, id: str, reason: str = "Superseded"): + data = { + "reason": reason, + } + response = requests_session().post( + f"https://api.zerossl.com/certificates/{id}/revoke", + json=data, + params={"access_key": self.token}, + ) + response.raise_for_status() + return response.json() + + +def zerossl_ip_certificate(force_renew: bool = False): + bootstrap = True + ip = get_public_ip() + console.log( + f":globe_with_meridians: [blue]Your public IP is {ip}. This will be used for the TLS certificate." + ) + if settings.zerossl is None: + console.log(":boom: No ZeroSSL token in configuration.") + raise typer.Exit(1) + z = ZeroSSLManager(settings.zerossl.token) + if os.path.exists(KEY_PATH): + console.log( + ":locked_with_key: [blue]Found an existing TLS private key, so not generating a new one." + ) + key = read_tls_key() + else: + with console.status("Generating TLS private key..."): + key = generate_tls_key() + with console.status("Saving TLS private key..."): + write_tls_key(key) + console.log(":white_check_mark: TLS private key generated and saved.") + if os.path.exists(CERTIFICATE_PATH): + bootstrap = False # TODO: determine based on anything listening on port 80 + days_since_modification = (time.time() - os.stat(CERTIFICATE_PATH).st_mtime) / ( + 60 * 60 * 24 + ) + console.log( + f":locked_with_key: [blue]Found an existing TLS certificate, which was last modified {int(days_since_modification)} days ago." + ) + # TODO: Actually check the certificate expiry + if days_since_modification > 80: + console.log(":clock9: [blue]Certificate is due for renewal.") + elif force_renew: + console.log(":clock12: [orange3]Certificate renewal is forced.") + else: + console.log(":white_check_mark: Certificate is still fine.") + return + with console.status("Generating TLS certificate signing request (CSR)..."): + csr = generate_tls_csr(ip, key) + console.log(":white_check_mark: TLS CSR generated.") + with console.status("Requesting to create certificate..."): + cert = z.create_certificate(ip, csr) + console.log(f":white_check_mark: Certificate created with ID {cert['id']}.") + if bootstrap: + with console.status("Starting bootstrap server... (background)"): + start_bootstrap_server( + "\n".join( + cert["validation"]["other_methods"][ip]["file_validation_content"] + ) + ) + console.log( + f":white_check_mark: Started bootstrap server listening at http://{ip}/." + ) + else: + with console.status("Writing validation file to webroot..."): + stage_validation( + cert["validation"]["other_methods"][ip]["file_validation_url_http"], + cert["validation"]["other_methods"][ip]["file_validation_content"], + ) + console.log(":white_check_mark: Wrote validation file to webroot.") + with console.status("Starting validation process..."): + cert = z.validate_certificate(cert["id"]) + while cert["status"] == "pending_validation": + time.sleep(1) + cert = z.get_certificate(cert["id"]) + if cert["status"] != "issued": + console.log( + ":boom: Invalid certificate state, something went wrong so I give up." + ) + sys.exit(1) + console.log(":white_check_mark: Certificate has been issued.") + stop_bootstrap_server() + with console.status("Downloading certificate..."): + dl = z.download_certificate(cert["id"]) + with console.status("Saving certificate..."): + write_certificate(dl["certificate.crt"], dl["ca_bundle.crt"]) + console.log(":white_check_mark: TLS certificate downloaded and saved.") + if old_id := get_certificate_id(): + console.log(f"[blue]Found ID for the previous certificate {old_id}.") + with console.status("Revoking old certificate..."): + z.revoke_certificate(old_id) # TODO: check return status + console.log(":white_check_mark: Old certificate revoked.") + with console.status("Saving new certificate ID..."): + save_certificate_id(cert["id"]) + console.log(":white_check_mark: New certificate ID saved.") diff --git a/config.toml.example b/config.toml.example new file mode 100644 index 0000000..2f834e2 --- /dev/null +++ b/config.toml.example @@ -0,0 +1,7 @@ +[bridge] +nickname = "VastlyClearFrog" +transport = "webtunnel" + +[bridge.webtunnel] +path = "mA8gsdsnJKSJGea4zF8BxVnzNCb75obO2f6KmzDHYeVkD5" +certificate = "zerossl-ip" diff --git a/docs/concept.jpg b/docs/concept.jpg new file mode 100644 index 0000000000000000000000000000000000000000..04cb1422bedfff058d21d156a04021bb7ae905c2 GIT binary patch literal 70881 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