From 5b6e87c6e36adf99f079fd5dac7f44155793dcba Mon Sep 17 00:00:00 2001 From: Christophe Mehay Date: Sun, 14 Jun 2020 23:29:49 +0200 Subject: [PATCH] Add tests for Vanguards setup --- Dockerfile | 2 - README.md | 19 +- assets/torrc | 2 +- assets/vanguards.conf.tpl | 38 +- docker-compose.vanguards-network.yml | 6 + docker-compose.vanguards.yml | 8 + onions/Onions.py | 6 +- poetry.lock | 93 ++-- pyproject.toml | 4 +- tests/onions_test.py | 609 +++++++++++++++++---------- 10 files changed, 503 insertions(+), 284 deletions(-) diff --git a/Dockerfile b/Dockerfile index cd0546a..6d60600 100644 --- a/Dockerfile +++ b/Dockerfile @@ -45,8 +45,6 @@ COPY assets/vanguards.conf.tpl /var/local/tor/vanguards.conf.tpl ENV VANGUARDS_CONFIG /etc/tor/vanguards.conf -RUN apk add socat - VOLUME ["/var/lib/tor/hidden_service/"] ENTRYPOINT ["pyentrypoint"] diff --git a/README.md b/README.md index 4148f6a..accaf9a 100644 --- a/README.md +++ b/README.md @@ -192,9 +192,6 @@ Use these environment variables to enable control port For critical hidden services, it's possible to increase security with [`Vanguards`](https://github.com/mikeperry-tor/vanguards) tool. -#### Settings - -It's not possible yet to custom all the settings using environment variable, but it's possible to mount configuration file to `/etc/tor/vanguards.conf` to custom `vanguards` settings. ### Run in the same container @@ -213,6 +210,22 @@ Use the same environment variable as `tor` to configure `vangards` (see upper). * `TOR_CONTROL_PORT` * `TOR_CONTROL_PASSWORD` +##### more settings + +Use `VANGUARDS_EXTRA_OPTIONS` environment variable to change any settings. + +The following settings cannot me changer with this variable: + - `control_ip`: + - use `TOR_CONTROL_PORT` + - `control_port`: + - use `TOR_CONTROL_PORT` + - `control_socket`: + - use `TOR_CONTROL_PORT` + - `control_pass`: + - use `TOR_CONTROL_PASSWORD` + - `state_file`: + - use `VANGUARDS_STATE_FILE` + # Legacy deprecated doc > **WARNING**: ALL THE DOC BELLOW IS LEGACY, IT'S STILL WORKING BUT IT'S NOT RECOMMENDED ANYMORE AND COULD BE DROPPED IN FUTURE RELEASES. diff --git a/assets/torrc b/assets/torrc index f829733..54083be 100644 --- a/assets/torrc +++ b/assets/torrc @@ -29,7 +29,7 @@ ExitRelay 0 {% if onion.enable_control_port %} {% if onion.control_socket %} -ControlPort unix:{{onion.control_socket}} +ControlPort {{onion.control_socket}} {% endif %} {% if not onion.control_socket %} {% if onion.control_ip_binding.version() == 4 %} diff --git a/assets/vanguards.conf.tpl b/assets/vanguards.conf.tpl index 14edc94..9155816 100644 --- a/assets/vanguards.conf.tpl +++ b/assets/vanguards.conf.tpl @@ -1,12 +1,7 @@ -## Example vanguards configuration file -# -# The values in this file are the defaults. You do not need to specify -# options in your config file unless you wish to change the defaults. - ## Global options [Global] -{% if env.get('TOR_CONTROL_PORT', '').startswith('unix:') %} +{% if (env.get('TOR_CONTROL_PORT', '')).startswith('unix:') %} {% set _, unix_path = env['TOR_CONTROL_PORT'].split(':', 1) %} {% elif ':' in env.get('TOR_CONTROL_PORT', '') %} {% set host, port = env['TOR_CONTROL_PORT'].split(':', 1) %} @@ -14,18 +9,34 @@ {% set host = env.get('TOR_CONTROL_PORT') %} {% endif %} -# IP address that the Tor control port is listening on: control_ip = {{ host or '' }} -# TCP port the control port is listening on: control_port = {{ port or 9051 }} -# If set, use this filesystem control socket instead of IP+Port: control_socket = {{ unix_path or '' }} -# If set, use this as the control port password: control_pass = {{ env.get('TOR_CONTROL_PASSWORD', '') }} +state_file = {{ env.get('VANGUARDS_STATE_FILE', '/run/tor/data/vanguards.state') }} + + +{% if 'VANGUARDS_EXTRA_OPTIONS' in env %} +{% set extra_conf = ConfigParser().read_string(env['VANGUARDS_EXTRA_OPTIONS']) %} +{% if 'Global' in extra_conf %} +{% for key, val in extra_conf['Global'].items() %} +{{key}} = {{val}} +{% endfor %} +{% set _ = extra_conf.pop('Global') %} +{% endif %} +{{ extra_conf.to_string() }} +{% endif %} + +{# +## Example vanguards configuration file +# +# All values below are default values and won't appear in final config file +# Original here: https://github.com/mikeperry-tor/vanguards/blob/master/vanguards-example.conf +# # Enable/disable active vanguard update of layer2 and layer3 guards enable_vanguards = True @@ -33,7 +44,7 @@ enable_vanguards = True enable_bandguards = True # Enable/disable circuit build timeout analysis (informational only): -enable_cbtverify = True +enable_cbtverify = False # Enable/disable checks on Rendezvous Point overuse attacks: enable_rendguard = True @@ -51,10 +62,6 @@ loglevel = NOTICE # If specified, log to this file instead of stdout: logfile = -# Name of state file (with absolute path, or relative to current directory): -state_file = {{ env.get('VANGUARDS_STATE_FILE', '/run/tor/data/vanguards.state') }} - - ## Vanguards: layer1, layer2, and layer3 rotation params. [Vanguards] @@ -135,3 +142,4 @@ rend_use_relay_start_count = 100 # Divide all relay counts by two once the total circuit count hits this many: rend_use_scale_at_count = 20000 +#} diff --git a/docker-compose.vanguards-network.yml b/docker-compose.vanguards-network.yml index 1f86ab2..a4db8fe 100644 --- a/docker-compose.vanguards-network.yml +++ b/docker-compose.vanguards-network.yml @@ -12,6 +12,12 @@ services: # Set controle port password (optionnal) TOR_CONTROL_PASSWORD: something_secret + # You can change any options here, excepted control_* ones and state_file + VANGUARDS_EXTRA_OPTIONS: | + [Global] + enable_cbtverify = True + loglevel = DEBUG + HELLO_TOR_SERVICE_HOSTS: '80:hello:80' HELLO_TOR_SERVICE_VERSION: '3' diff --git a/docker-compose.vanguards.yml b/docker-compose.vanguards.yml index f45c60a..f2850aa 100644 --- a/docker-compose.vanguards.yml +++ b/docker-compose.vanguards.yml @@ -6,7 +6,15 @@ services: tor: image: goldy/tor-hidden-service:$CUR_TAG environment: + # Enable Vanguards like this TOR_ENABLE_VANGUARDS: 'true' + + # You can change any options here, excepted control_* ones + VANGUARDS_EXTRA_OPTIONS: | + [Global] + enable_cbtverify = True + loglevel = DEBUG + HELLO_TOR_SERVICE_HOSTS: '80:hello:80' HELLO_TOR_SERVICE_VERSION: '3' diff --git a/onions/Onions.py b/onions/Onions.py index 17c77bf..32cb8a0 100644 --- a/onions/Onions.py +++ b/onions/Onions.py @@ -14,6 +14,7 @@ from jinja2 import Environment from jinja2 import FileSystemLoader from pyentrypoint import DockerLinks from pyentrypoint.config import envtobool +from pyentrypoint.configparser import ConfigParser from .Service import Service from .Service import ServicesGroup @@ -64,7 +65,7 @@ class Setup(object): control_port = os.environ['TOR_CONTROL_PORT'] try: if control_port.startswith('unix:'): - _, self.control_socket = control_port.split(':') + self.control_socket = control_port return self.control_socket = None if ':' in control_port: @@ -97,7 +98,7 @@ class Setup(object): return self.enable_control_port = True self.enable_vanguards = True - os.environ['TOR_CONTROL_PORT'] = self.control_socket + os.environ.setdefault('TOR_CONTROL_PORT', self.control_socket) self.kill_tor_on_vanguard_exit = envtobool( 'VANGUARD_KILL_TOR_ON_EXIT', True @@ -300,6 +301,7 @@ class Setup(object): temp = env.get_template(self.vanguards_template) with open(self.vanguards_conf, mode='w') as f: f.write(temp.render(env=os.environ, + ConfigParser=ConfigParser, envtobool=envtobool)) def run_vanguards(self): diff --git a/poetry.lock b/poetry.lock index 3932972..8b6df0e 100644 --- a/poetry.lock +++ b/poetry.lock @@ -12,7 +12,7 @@ description = "An abstract syntax tree for Python with inference support." name = "astroid" optional = false python-versions = ">=3.5" -version = "2.4.1" +version = "2.4.2" [package.dependencies] lazy-object-proxy = ">=1.4.0,<1.5.0" @@ -275,7 +275,7 @@ description = "More routines for operating on iterables, beyond itertools" name = "more-itertools" optional = false python-versions = ">=3.5" -version = "8.3.0" +version = "8.4.0" [[package]] category = "dev" @@ -346,7 +346,7 @@ description = "A framework for managing and maintaining multi-language pre-commi name = "pre-commit" optional = false python-versions = ">=3.6.1" -version = "2.4.0" +version = "2.5.1" [package.dependencies] cfgv = ">=2.0.0" @@ -426,13 +426,14 @@ description = "pyentrypoint manages entrypoints in Docker containers." name = "pyentrypoint" optional = false python-versions = ">=3.6.1,<4.0.0" -version = "0.7.3" +version = "0.7.4" [package.dependencies] colorlog = ">=4.1,<5.0" jinja2 = ">=2.11,<3.0" pyyaml = ">=5.3,<6.0" six = ">=1.14,<2.0" +toml = ">=0.10.1,<0.11.0" watchdog = ">=0.10,<0.11" [[package]] @@ -457,7 +458,7 @@ description = "python code static checker" name = "pylint" optional = false python-versions = ">=3.5.*" -version = "2.5.2" +version = "2.5.3" [package.dependencies] astroid = ">=2.4.0,<=2.5" @@ -525,7 +526,7 @@ description = "Alternative regular expression module, to replace re." name = "regex" optional = false python-versions = "*" -version = "2020.6.7" +version = "2020.6.8" [[package]] category = "main" @@ -544,7 +545,7 @@ python-versions = "*" version = "1.8.0" [[package]] -category = "dev" +category = "main" description = "Python Library for Tom's Obvious, Minimal Language" name = "toml" optional = false @@ -604,7 +605,7 @@ description = "Virtual Python Environment builder" name = "virtualenv" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" -version = "20.0.21" +version = "20.0.23" [package.dependencies] appdirs = ">=1.4.3,<2" @@ -618,7 +619,7 @@ version = ">=0.12,<2" [package.extras] docs = ["sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=19.9.0rc1)", "proselint (>=0.10.2)"] -testing = ["pytest (>=4)", "coverage (>=5)", "coverage-enable-subprocess (>=1)", "pytest-xdist (>=1.31.0)", "pytest-mock (>=2)", "pytest-env (>=0.6.2)", "pytest-randomly (>=1)", "pytest-timeout", "packaging (>=20.0)", "xonsh (>=0.9.16)"] +testing = ["pytest (>=4)", "coverage (>=5)", "coverage-enable-subprocess (>=1)", "pytest-xdist (>=1.31.0)", "pytest-mock (>=2)", "pytest-env (>=0.6.2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "flaky (>=3)", "packaging (>=20.0)", "xonsh (>=0.9.16)"] [[package]] category = "main" @@ -640,7 +641,7 @@ description = "Measures the displayed width of unicode strings in a terminal" name = "wcwidth" optional = false python-versions = "*" -version = "0.2.3" +version = "0.2.4" [[package]] category = "dev" @@ -663,7 +664,7 @@ docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] testing = ["jaraco.itertools", "func-timeout"] [metadata] -content-hash = "097415d9c723c691882ffa440af8e248746a278f758745d2ce75ffab1bdac90d" +content-hash = "044c9aa2965d6dc97970237a0a1e5e02fdd37609ff58cf58f2e68b0a5edecf2a" python-versions = ">= 3.7, < 3.8" [metadata.files] @@ -672,8 +673,8 @@ appdirs = [ {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, ] astroid = [ - {file = "astroid-2.4.1-py3-none-any.whl", hash = "sha256:d8506842a3faf734b81599c8b98dcc423de863adcc1999248480b18bd31a0f38"}, - {file = "astroid-2.4.1.tar.gz", hash = "sha256:4c17cea3e592c21b6e222f673868961bad77e1f985cb1694ed077475a89229c1"}, + {file = "astroid-2.4.2-py3-none-any.whl", hash = "sha256:bc58d83eb610252fd8de6363e39d4f1d0619c894b0ed24603b881c02e64c7386"}, + {file = "astroid-2.4.2.tar.gz", hash = "sha256:2f4078c2a41bf377eea06d71c9d2ba4eb8f6b1af2135bec27bbbb7d8f12bb703"}, ] atomicwrites = [ {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, @@ -854,8 +855,8 @@ mccabe = [ {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, ] more-itertools = [ - {file = "more-itertools-8.3.0.tar.gz", hash = "sha256:558bb897a2232f5e4f8e2399089e35aecb746e1f9191b6584a151647e89267be"}, - {file = "more_itertools-8.3.0-py3-none-any.whl", hash = "sha256:7818f596b1e87be009031c7653d01acc46ed422e6656b394b0f765ce66ed4982"}, + {file = "more-itertools-8.4.0.tar.gz", hash = "sha256:68c70cc7167bdf5c7c9d8f6954a7837089c6a36bf565383919bb595efb8a17e5"}, + {file = "more_itertools-8.4.0-py3-none-any.whl", hash = "sha256:b78134b2063dd214000685165d81c154522c3ee0a1c0d4d113c80361c234c5a2"}, ] nodeenv = [ {file = "nodeenv-1.4.0-py2.py3-none-any.whl", hash = "sha256:4b0b77afa3ba9b54f4b6396e60b0c83f59eaeb2d63dc3cc7a70f7f4af96c82bc"}, @@ -880,8 +881,8 @@ pluggy = [ {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, ] pre-commit = [ - {file = "pre_commit-2.4.0-py2.py3-none-any.whl", hash = "sha256:5559e09afcac7808933951ffaf4ff9aac524f31efbc3f24d021540b6c579813c"}, - {file = "pre_commit-2.4.0.tar.gz", hash = "sha256:703e2e34cbe0eedb0d319eff9f7b83e2022bb5a3ab5289a6a8841441076514d0"}, + {file = "pre_commit-2.5.1-py2.py3-none-any.whl", hash = "sha256:c5c8fd4d0e1c363723aaf0a8f9cba0f434c160b48c4028f4bae6d219177945b3"}, + {file = "pre_commit-2.5.1.tar.gz", hash = "sha256:da463cf8f0e257f9af49047ba514f6b90dbd9b4f92f4c8847a3ccd36834874c7"}, ] prompt-toolkit = [ {file = "prompt_toolkit-3.0.5-py3-none-any.whl", hash = "sha256:df7e9e63aea609b1da3a65641ceaf5bc7d05e0a04de5bd45d05dbeffbabf9e04"}, @@ -938,8 +939,8 @@ pycryptodome = [ {file = "pycryptodome-3.9.4.tar.gz", hash = "sha256:a168e73879619b467072509a223282a02c8047d932a48b74fbd498f27224aa04"}, ] pyentrypoint = [ - {file = "pyentrypoint-0.7.3-py3-none-any.whl", hash = "sha256:aa4b3016466b398d379a974e01c363ed12618652adbe6a193ced43ed0fcefacc"}, - {file = "pyentrypoint-0.7.3.tar.gz", hash = "sha256:59c02957ec82e9ca53997e66f75d0f375f1a1b39373074d790307d6a159a1e06"}, + {file = "pyentrypoint-0.7.4-py3-none-any.whl", hash = "sha256:90dd03052f8e2a7fe4cf7781b6a16711d9ad247607e02d861c5870eff917ad58"}, + {file = "pyentrypoint-0.7.4.tar.gz", hash = "sha256:ec1d974dc7ca9cc77ffde31937121c04830060825081a9cfbf25712459c844d9"}, ] pyfakefs = [ {file = "pyfakefs-4.0.2-py3-none-any.whl", hash = "sha256:42cf165adc821fc9e205d3fc14033d45e0b8224e1d2fea4f67b487c6b7b3230e"}, @@ -950,8 +951,8 @@ pygments = [ {file = "Pygments-2.6.1.tar.gz", hash = "sha256:647344a061c249a3b74e230c739f434d7ea4d8b1d5f3721bc0f3558049b38f44"}, ] pylint = [ - {file = "pylint-2.5.2-py3-none-any.whl", hash = "sha256:dd506acce0427e9e08fb87274bcaa953d38b50a58207170dbf5b36cf3e16957b"}, - {file = "pylint-2.5.2.tar.gz", hash = "sha256:b95e31850f3af163c2283ed40432f053acbc8fc6eba6a069cb518d9dbf71848c"}, + {file = "pylint-2.5.3-py3-none-any.whl", hash = "sha256:d0ece7d223fe422088b0e8f13fa0a1e8eb745ebffcb8ed53d3e95394b6101a1c"}, + {file = "pylint-2.5.3.tar.gz", hash = "sha256:7dd78437f2d8d019717dbf287772d0b2dbdfd13fc016aa7faa08d67bccc46adc"}, ] pyparsing = [ {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, @@ -978,27 +979,27 @@ pyyaml = [ {file = "PyYAML-5.3.1.tar.gz", hash = "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d"}, ] regex = [ - {file = "regex-2020.6.7-cp27-cp27m-win32.whl", hash = "sha256:8d9bb2d90e23c51aacbc58c1a11320f49b335cd67a91986cdbebcc3e843e4de8"}, - {file = "regex-2020.6.7-cp27-cp27m-win_amd64.whl", hash = "sha256:dcda6d4e1bbfc939b177c237aee41c9678eaaf71df482688f8986e8251e12345"}, - {file = "regex-2020.6.7-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:af7209b2fcc79ee2b0ad4ea080d70bb748450ec4f282cc9e864861e469b1072e"}, - {file = "regex-2020.6.7-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:5735f26cacdb50b3d6d35ebf8fdeb504bd8b381e2d079d2d9f12ce534fc14ecd"}, - {file = "regex-2020.6.7-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:f6c8c3f56fef719180464855346e6e80971b86dfd9e5a0e356664b5baca53072"}, - {file = "regex-2020.6.7-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:21fc17cb868c4264f0813f992f46f9ae6fc8c309d4741091de4153bd1f6a6176"}, - {file = "regex-2020.6.7-cp36-cp36m-win32.whl", hash = "sha256:150125da109fccdcc8fec3b0b386b2a5d6ca7cff076f8b622486d1ca868b0c10"}, - {file = "regex-2020.6.7-cp36-cp36m-win_amd64.whl", hash = "sha256:c0849b0864ff451f04c8afb5fc28e9ed592262e03debdd227cf0f53e04a55dcd"}, - {file = "regex-2020.6.7-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:8d1ee3796795e609ef7a3a5a35eaf4728038d986aa12c06b3fd1b92ee81911f4"}, - {file = "regex-2020.6.7-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:7606dba82435429641efe4fbc580574942f89cf2b9c5c1f8bc1eab2bacbf7e8b"}, - {file = "regex-2020.6.7-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:6edc5c190248d3b612f2cca45448cf8ebc3621d41afcd1c5708853cbb1dbb3b3"}, - {file = "regex-2020.6.7-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:2c928bc8e0c453d73dffa3193a6e37ee752ea36df0dd4601e21024d98274dfad"}, - {file = "regex-2020.6.7-cp37-cp37m-win32.whl", hash = "sha256:97d414c41f19fd2362e493810caa8445c05e0a2d63a14081c972aad66284a8d2"}, - {file = "regex-2020.6.7-cp37-cp37m-win_amd64.whl", hash = "sha256:9e37502817225ee99d91d8418f5119e98c380b00e772d06915690c05290f32ee"}, - {file = "regex-2020.6.7-cp38-cp38-manylinux1_i686.whl", hash = "sha256:c4ac9215650688e78dea29b46adbdafb7b85058eebe92ef6ea848e14466c915f"}, - {file = "regex-2020.6.7-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:20c513893ff80bdbe4b4ce11ea2e93d49481f05b270595d82af69ffc402010a6"}, - {file = "regex-2020.6.7-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:163bc0805e46acfa098dfc8c0b07f371577d505f603e48afc425ff475cdac3a5"}, - {file = "regex-2020.6.7-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:2d9beca70e36f9c60d679e108c5fe49f3d4da79d13a13f91e5e759443bd954f9"}, - {file = "regex-2020.6.7-cp38-cp38-win32.whl", hash = "sha256:ec0e509ed1877ff1cbc6f0864689bb60384a303502c4d72d9a635f8a4676fd3f"}, - {file = "regex-2020.6.7-cp38-cp38-win_amd64.whl", hash = "sha256:dd8501b8d9ea1aba53c4bc7d47bc72933f9b4213d534cf400f16c1431f51c8ba"}, - {file = "regex-2020.6.7.tar.gz", hash = "sha256:ffd4f80602490a309064cf2b203e220d581c51660e01055c64bf5da450485ee6"}, + {file = "regex-2020.6.8-cp27-cp27m-win32.whl", hash = "sha256:fbff901c54c22425a5b809b914a3bfaf4b9570eee0e5ce8186ac71eb2025191c"}, + {file = "regex-2020.6.8-cp27-cp27m-win_amd64.whl", hash = "sha256:112e34adf95e45158c597feea65d06a8124898bdeac975c9087fe71b572bd938"}, + {file = "regex-2020.6.8-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:92d8a043a4241a710c1cf7593f5577fbb832cf6c3a00ff3fc1ff2052aff5dd89"}, + {file = "regex-2020.6.8-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:bae83f2a56ab30d5353b47f9b2a33e4aac4de9401fb582b55c42b132a8ac3868"}, + {file = "regex-2020.6.8-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:b2ba0f78b3ef375114856cbdaa30559914d081c416b431f2437f83ce4f8b7f2f"}, + {file = "regex-2020.6.8-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:95fa7726d073c87141f7bbfb04c284901f8328e2d430eeb71b8ffdd5742a5ded"}, + {file = "regex-2020.6.8-cp36-cp36m-win32.whl", hash = "sha256:e3cdc9423808f7e1bb9c2e0bdb1c9dc37b0607b30d646ff6faf0d4e41ee8fee3"}, + {file = "regex-2020.6.8-cp36-cp36m-win_amd64.whl", hash = "sha256:c78e66a922de1c95a208e4ec02e2e5cf0bb83a36ceececc10a72841e53fbf2bd"}, + {file = "regex-2020.6.8-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:08997a37b221a3e27d68ffb601e45abfb0093d39ee770e4257bd2f5115e8cb0a"}, + {file = "regex-2020.6.8-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:2f6f211633ee8d3f7706953e9d3edc7ce63a1d6aad0be5dcee1ece127eea13ae"}, + {file = "regex-2020.6.8-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:55b4c25cbb3b29f8d5e63aeed27b49fa0f8476b0d4e1b3171d85db891938cc3a"}, + {file = "regex-2020.6.8-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:89cda1a5d3e33ec9e231ece7307afc101b5217523d55ef4dc7fb2abd6de71ba3"}, + {file = "regex-2020.6.8-cp37-cp37m-win32.whl", hash = "sha256:690f858d9a94d903cf5cada62ce069b5d93b313d7d05456dbcd99420856562d9"}, + {file = "regex-2020.6.8-cp37-cp37m-win_amd64.whl", hash = "sha256:1700419d8a18c26ff396b3b06ace315b5f2a6e780dad387e4c48717a12a22c29"}, + {file = "regex-2020.6.8-cp38-cp38-manylinux1_i686.whl", hash = "sha256:654cb773b2792e50151f0e22be0f2b6e1c3a04c5328ff1d9d59c0398d37ef610"}, + {file = "regex-2020.6.8-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:52e1b4bef02f4040b2fd547357a170fc1146e60ab310cdbdd098db86e929b387"}, + {file = "regex-2020.6.8-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:cf59bbf282b627130f5ba68b7fa3abdb96372b24b66bdf72a4920e8153fc7910"}, + {file = "regex-2020.6.8-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:5aaa5928b039ae440d775acea11d01e42ff26e1561c0ffcd3d805750973c6baf"}, + {file = "regex-2020.6.8-cp38-cp38-win32.whl", hash = "sha256:97712e0d0af05febd8ab63d2ef0ab2d0cd9deddf4476f7aa153f76feef4b2754"}, + {file = "regex-2020.6.8-cp38-cp38-win_amd64.whl", hash = "sha256:6ad8663c17db4c5ef438141f99e291c4d4edfeaacc0ce28b5bba2b0bf273d9b5"}, + {file = "regex-2020.6.8.tar.gz", hash = "sha256:e9b64e609d37438f7d6e68c2546d2cb8062f3adb27e6336bc129b51be20773ac"}, ] six = [ {file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"}, @@ -1043,15 +1044,15 @@ vanguards = [ {file = "vanguards-0.3.1.tar.gz", hash = "sha256:04049fafd433bb747fbe27b404413ce09b441d5e0e6cc5d81debaac2192567b7"}, ] virtualenv = [ - {file = "virtualenv-20.0.21-py2.py3-none-any.whl", hash = "sha256:a730548b27366c5e6cbdf6f97406d861cccece2e22275e8e1a757aeff5e00c70"}, - {file = "virtualenv-20.0.21.tar.gz", hash = "sha256:a116629d4e7f4d03433b8afa27f43deba09d48bc48f5ecefa4f015a178efb6cf"}, + {file = "virtualenv-20.0.23-py2.py3-none-any.whl", hash = "sha256:ccfb8e1e05a1174f7bd4c163700277ba730496094fe1a58bea9d4ac140a207c8"}, + {file = "virtualenv-20.0.23.tar.gz", hash = "sha256:5102fbf1ec57e80671ef40ed98a84e980a71194cedf30c87c2b25c3a9e0b0107"}, ] watchdog = [ {file = "watchdog-0.10.2.tar.gz", hash = "sha256:c560efb643faed5ef28784b2245cf8874f939569717a4a12826a173ac644456b"}, ] wcwidth = [ - {file = "wcwidth-0.2.3-py2.py3-none-any.whl", hash = "sha256:980fbf4f3c196c0f329cdcd1e84c554d6a211f18e252e525a0cf4223154a41d6"}, - {file = "wcwidth-0.2.3.tar.gz", hash = "sha256:edbc2b718b4db6cdf393eefe3a420183947d6aa312505ce6754516f458ff8830"}, + {file = "wcwidth-0.2.4-py2.py3-none-any.whl", hash = "sha256:79375666b9954d4a1a10739315816324c3e73110af9d0e102d906fdb0aec009f"}, + {file = "wcwidth-0.2.4.tar.gz", hash = "sha256:8c6b5b6ee1360b842645f336d9e5d68c55817c26d3050f46b235ef2bc650e48f"}, ] wrapt = [ {file = "wrapt-1.12.1.tar.gz", hash = "sha256:b62ffa81fb85f4332a4f609cab4ac40709470da05643a082ec1eb88e6d9b97d7"}, diff --git a/pyproject.toml b/pyproject.toml index 1a0d8b4..71cbfbb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "docker-tor-hidden-service" -version = "0.6.0" +version = "0.6.1" description = "Display onion sites hosted" authors = ["Christophe Mehay "] license = "WTFPL" @@ -26,7 +26,7 @@ onions = "onions:main" python = ">= 3.7, < 3.8" pytor = "^0.1.5" Jinja2 = "^2.10" -pyentrypoint = "^0.7.3" +pyentrypoint = "^0.7.4" importlib_metadata = "^1.6.0" vanguards = "^0.3.1" ipy = "^1.00" diff --git a/tests/onions_test.py b/tests/onions_test.py index 1936afc..03474c9 100644 --- a/tests/onions_test.py +++ b/tests/onions_test.py @@ -1,3 +1,4 @@ +import configparser import json import os import re @@ -13,7 +14,9 @@ from onions import Onions def get_key_and_onion(version=2): key = {} - key[2] = ''' + key[ + 2 + ] = """ -----BEGIN RSA PRIVATE KEY----- MIICXAIBAAKBgQCsMP4gl6g1Q313miPhb1GnDr56ZxIWGsO2PwHM1infkbhlBakR 6DGQfpE31L1ZKTUxY0OexKbW088v8qCOfjD9Zk1i80JP4xzfWQcwFZ5yM/0fkhm3 @@ -29,36 +32,41 @@ l5MQPWBkRKK2pc2Hfj8cdIMi8kJ/1CyCwE6c5l8etR3sbIMRTtZ76nAbXRFkmsRv La/7Syrnobngsh/vX90CQB+PSSBqiPSsK2yPz6Gsd6OLCQ9sdy2oRwFTasH8sZyl bhJ3M9WzP/EMkAzyW8mVs1moFp3hRcfQlZHl6g1U9D8= -----END RSA PRIVATE KEY----- - ''' + """ onion = {} pub = {} - onion[2] = b32encode( - sha1( - RSA.importKey( - key[2].strip() - ).publickey().exportKey( - "DER" - )[22:] - ).digest()[:10] - ).decode().lower() + '.onion' - - key[3] = ''' + onion[2] = ( + b32encode( + sha1( + RSA.importKey(key[2].strip()).publickey().exportKey("DER")[22:] + ).digest()[:10] + ) + .decode() + .lower() + + ".onion" + ) + + key[ + 3 + ] = """ PT0gZWQyNTUxOXYxLXNlY3JldDogdHlwZTAgPT0AAACArobDQYyZAWXei4QZwr++j96H1X/gq14N wLRZ2O5DXuL0EzYKkdhZSILY85q+kfwZH8z4ceqe7u1F+0pQi/sM - ''' + """ - pub[3] = ''' + pub[ + 3 + ] = """ PT0gZWQyNTUxOXYxLXB1YmxpYzogdHlwZTAgPT0AAAC9kzftiea/kb+TWlCEVNpfUJLVk+rFIoMG m9/hW13isA== - ''' + """ - onion[3] = 'xwjtp3mj427zdp4tljiiivg2l5ijfvmt5lcsfaygtpp6cw254kykvpyd.onion' + onion[3] = "xwjtp3mj427zdp4tljiiivg2l5ijfvmt5lcsfaygtpp6cw254kykvpyd.onion" return key[version].strip(), onion[version] def get_torrc_template(): - return r''' + return r""" {% for service_group in onion.services %} HiddenServiceDir {{service_group.hidden_service_dir}} {% if service_group.version == 3 %} @@ -90,7 +98,7 @@ ExitRelay 0 {% if onion.enable_control_port %} {% if onion.control_socket %} -ControlPort unix:{{onion.control_socket}} +ControlPort {{onion.control_socket}} {% endif %} {% if not onion.control_socket %} {% if onion.control_ip_binding.version() == 4 %} @@ -111,17 +119,20 @@ HashedControlPassword {{ onion.control_hashed_password }} {% endif %} # useless line for Jinja bug - '''.strip() + + +# useless line for Jinja bug + """.strip() def test_ports(monkeypatch): env = { - 'SERVICE1_PORTS': '80:80', - 'SERVICE2_PORTS': '80:80,81:8000', - 'SERVICE3_PORTS': '80:unix://unix.socket', + "SERVICE1_PORTS": "80:80", + "SERVICE2_PORTS": "80:80,81:8000", + "SERVICE3_PORTS": "80:unix://unix.socket", } - monkeypatch.setattr(os, 'environ', env) + monkeypatch.setattr(os, "environ", env) onion = Onions() onion._get_setup_from_env() @@ -131,24 +142,24 @@ def test_ports(monkeypatch): for service_group in onion.services: assert len(service_group.services) == 1 service = service_group.services[0] - if service.host == 'service1': + if service.host == "service1": check += 1 assert len(service.ports) == 1 assert service.ports[0].port_from == 80 assert service.ports[0].dest == 80 assert not service.ports[0].is_socket - if service.host == 'service2': + if service.host == "service2": check += 3 assert len(service.ports) == 2 assert service.ports[0].port_from == 80 assert service.ports[0].dest == 80 assert service.ports[1].port_from == 81 assert service.ports[1].dest == 8000 - if service.host == 'service3': + if service.host == "service3": check += 6 assert len(service.ports) == 1 assert service.ports[0].port_from == 80 - assert service.ports[0].dest == 'unix://unix.socket' + assert service.ports[0].dest == "unix://unix.socket" assert service.ports[0].is_socket assert check == 10 @@ -157,40 +168,40 @@ def test_ports(monkeypatch): def test_docker_links(fs, monkeypatch): env = { - 'HOSTNAME': 'test_env', - 'COMPOSE_SERVICE1_1_PORT': 'tcp://172.17.0.2:80', - 'COMPOSE_SERVICE1_1_PORT_80_TCP': 'tcp://172.17.0.2:80', - 'COMPOSE_SERVICE1_1_PORT_80_TCP_ADDR': '172.17.0.2', - 'COMPOSE_SERVICE1_1_PORT_80_TCP_PORT': '80', - 'COMPOSE_SERVICE1_1_PORT_80_TCP_PROTO': 'tcp', - 'COMPOSE_SERVICE1_1_PORT_8000_TCP': 'tcp://172.17.0.2:8000', - 'COMPOSE_SERVICE1_1_PORT_8000_TCP_ADDR': '172.17.0.2', - 'COMPOSE_SERVICE1_1_PORT_8000_TCP_PORT': '8000', - 'COMPOSE_SERVICE1_1_PORT_8000_TCP_PROTO': 'tcp', - 'COMPOSE_SERVICE1_1_NAME': '/compose_env_1/compose_service1_1', - 'SERVICE1_PORT': 'tcp://172.17.0.2:80', - 'SERVICE1_PORT_80_TCP': 'tcp://172.17.0.2:80', - 'SERVICE1_PORT_80_TCP_ADDR': '172.17.0.2', - 'SERVICE1_PORT_80_TCP_PORT': '80', - 'SERVICE1_PORT_80_TCP_PROTO': 'tcp', - 'SERVICE1_PORT_8000_TCP': 'tcp://172.17.0.2:8000', - 'SERVICE1_PORT_8000_TCP_ADDR': '172.17.0.2', - 'SERVICE1_PORT_8000_TCP_PORT': '8000', - 'SERVICE1_PORT_8000_TCP_PROTO': 'tcp', - 'SERVICE1_NAME': '/compose_env_1/service1', - 'SERVICE1_1_PORT': 'tcp://172.17.0.2:80', - 'SERVICE1_1_PORT_80_TCP': 'tcp://172.17.0.2:80', - 'SERVICE1_1_PORT_80_TCP_ADDR': '172.17.0.2', - 'SERVICE1_1_PORT_80_TCP_PORT': '80', - 'SERVICE1_1_PORT_80_TCP_PROTO': 'tcp', - 'SERVICE1_1_PORT_8000_TCP': 'tcp://172.17.0.2:8000', - 'SERVICE1_1_PORT_8000_TCP_ADDR': '172.17.0.2', - 'SERVICE1_1_PORT_8000_TCP_PORT': '8000', - 'SERVICE1_1_PORT_8000_TCP_PROTO': 'tcp', - 'SERVICE1_1_NAME': '/compose_env_1/service1_1', + "HOSTNAME": "test_env", + "COMPOSE_SERVICE1_1_PORT": "tcp://172.17.0.2:80", + "COMPOSE_SERVICE1_1_PORT_80_TCP": "tcp://172.17.0.2:80", + "COMPOSE_SERVICE1_1_PORT_80_TCP_ADDR": "172.17.0.2", + "COMPOSE_SERVICE1_1_PORT_80_TCP_PORT": "80", + "COMPOSE_SERVICE1_1_PORT_80_TCP_PROTO": "tcp", + "COMPOSE_SERVICE1_1_PORT_8000_TCP": "tcp://172.17.0.2:8000", + "COMPOSE_SERVICE1_1_PORT_8000_TCP_ADDR": "172.17.0.2", + "COMPOSE_SERVICE1_1_PORT_8000_TCP_PORT": "8000", + "COMPOSE_SERVICE1_1_PORT_8000_TCP_PROTO": "tcp", + "COMPOSE_SERVICE1_1_NAME": "/compose_env_1/compose_service1_1", + "SERVICE1_PORT": "tcp://172.17.0.2:80", + "SERVICE1_PORT_80_TCP": "tcp://172.17.0.2:80", + "SERVICE1_PORT_80_TCP_ADDR": "172.17.0.2", + "SERVICE1_PORT_80_TCP_PORT": "80", + "SERVICE1_PORT_80_TCP_PROTO": "tcp", + "SERVICE1_PORT_8000_TCP": "tcp://172.17.0.2:8000", + "SERVICE1_PORT_8000_TCP_ADDR": "172.17.0.2", + "SERVICE1_PORT_8000_TCP_PORT": "8000", + "SERVICE1_PORT_8000_TCP_PROTO": "tcp", + "SERVICE1_NAME": "/compose_env_1/service1", + "SERVICE1_1_PORT": "tcp://172.17.0.2:80", + "SERVICE1_1_PORT_80_TCP": "tcp://172.17.0.2:80", + "SERVICE1_1_PORT_80_TCP_ADDR": "172.17.0.2", + "SERVICE1_1_PORT_80_TCP_PORT": "80", + "SERVICE1_1_PORT_80_TCP_PROTO": "tcp", + "SERVICE1_1_PORT_8000_TCP": "tcp://172.17.0.2:8000", + "SERVICE1_1_PORT_8000_TCP_ADDR": "172.17.0.2", + "SERVICE1_1_PORT_8000_TCP_PORT": "8000", + "SERVICE1_1_PORT_8000_TCP_PROTO": "tcp", + "SERVICE1_1_NAME": "/compose_env_1/service1_1", } - etc_host = ''' + etc_host = """ 127.0.0.1 localhost ::1 localhost ip6-localhost ip6-loopback fe00::0 ip6-localnet @@ -200,11 +211,11 @@ ff02::2 ip6-allrouters 172.17.0.2 service1 bf447f22cdba compose_service1_1 172.17.0.2 service1_1 bf447f22cdba compose_service1_1 172.17.0.2 compose_service1_1 bf447f22cdba - '''.strip() + """.strip() - fs.create_file('/etc/hosts', contents=etc_host) + fs.create_file("/etc/hosts", contents=etc_host) - monkeypatch.setattr(os, 'environ', env) + monkeypatch.setattr(os, "environ", env) onion = Onions() onion._get_setup_from_links() @@ -214,19 +225,17 @@ ff02::2 ip6-allrouters assert len(group.services) == 1 service = group.services[0] assert len(service.ports) == 2 - assert set( - (port.port_from, port.dest) for port in service.ports - ) == set([(80, 80), (8000, 8000)]) + assert set((port.port_from, port.dest) for port in service.ports) == set( + [(80, 80), (8000, 8000)] + ) def test_key(monkeypatch): key, onion_url = get_key_and_onion() - env = { - 'SERVICE1_KEY': key - } + env = {"SERVICE1_KEY": key} - monkeypatch.setattr(os, 'environ', env) + monkeypatch.setattr(os, "environ", env) onion = Onions() onion._get_setup_from_env() @@ -239,17 +248,20 @@ def test_key(monkeypatch): def test_key_v2(monkeypatch): key, onion_url = get_key_and_onion(version=2) - envs = [{ - 'GROUP1_TOR_SERVICE_HOSTS': '80:service1:80,81:service2:80', - 'GROUP1_TOR_SERVICE_VERSION': '2', - 'GROUP1_TOR_SERVICE_KEY': key, - }, { - 'GROUP1_TOR_SERVICE_HOSTS': '80:service1:80,81:service2:80', - 'GROUP1_TOR_SERVICE_KEY': key, - }] + envs = [ + { + "GROUP1_TOR_SERVICE_HOSTS": "80:service1:80,81:service2:80", + "GROUP1_TOR_SERVICE_VERSION": "2", + "GROUP1_TOR_SERVICE_KEY": key, + }, + { + "GROUP1_TOR_SERVICE_HOSTS": "80:service1:80,81:service2:80", + "GROUP1_TOR_SERVICE_KEY": key, + }, + ] for env in envs: - monkeypatch.setattr(os, 'environ', env) + monkeypatch.setattr(os, "environ", env) onion = Onions() onion._get_setup_from_env() @@ -264,12 +276,12 @@ def test_key_v2(monkeypatch): def test_key_v3(monkeypatch): key, onion_url = get_key_and_onion(version=3) env = { - 'GROUP1_TOR_SERVICE_HOSTS': '80:service1:80,81:service2:80', - 'GROUP1_TOR_SERVICE_VERSION': '3', - 'GROUP1_TOR_SERVICE_KEY': key, + "GROUP1_TOR_SERVICE_HOSTS": "80:service1:80,81:service2:80", + "GROUP1_TOR_SERVICE_VERSION": "3", + "GROUP1_TOR_SERVICE_KEY": key, } - monkeypatch.setattr(os, 'environ', env) + monkeypatch.setattr(os, "environ", env) onion = Onions() onion._get_setup_from_env() @@ -283,27 +295,27 @@ def test_key_v3(monkeypatch): def test_key_in_secret(fs, monkeypatch): env = { - 'GROUP1_TOR_SERVICE_HOSTS': '80:service1:80', - 'GROUP2_TOR_SERVICE_HOSTS': '80:service2:80', - 'GROUP3_TOR_SERVICE_HOSTS': '80:service3:80', - 'GROUP3_TOR_SERVICE_VERSION': '3', + "GROUP1_TOR_SERVICE_HOSTS": "80:service1:80", + "GROUP2_TOR_SERVICE_HOSTS": "80:service2:80", + "GROUP3_TOR_SERVICE_HOSTS": "80:service3:80", + "GROUP3_TOR_SERVICE_VERSION": "3", } - monkeypatch.setattr(os, 'environ', env) + monkeypatch.setattr(os, "environ", env) key_v2, onion_url_v2 = get_key_and_onion() key_v3, onion_url_v3 = get_key_and_onion(version=3) - fs.create_file('/run/secrets/group1', contents=key_v2) - fs.create_file('/run/secrets/group3', contents=b64decode(key_v3)) + fs.create_file("/run/secrets/group1", contents=key_v2) + fs.create_file("/run/secrets/group3", contents=b64decode(key_v3)) onion = Onions() onion._get_setup_from_env() onion._load_keys_in_services() - group1 = onion.find_group_by_name('group1') - group2 = onion.find_group_by_name('group2') - group3 = onion.find_group_by_name('group3') + group1 = onion.find_group_by_name("group1") + group2 = onion.find_group_by_name("group2") + group3 = onion.find_group_by_name("group3") assert group1.onion_url == onion_url_v2 assert group2.onion_url not in [onion_url_v2, onion_url_v3] @@ -311,37 +323,37 @@ def test_key_in_secret(fs, monkeypatch): def test_configuration(fs, monkeypatch, tmpdir): - extra_options = ''' + extra_options = """ HiddenServiceNonAnonymousMode 1 HiddenServiceSingleHopMode 1 - '''.strip() + """.strip() env = { - 'SERVICE1_SERVICE_NAME': 'group1', - 'SERVICE2_SERVICE_NAME': 'group1', - 'SERVICE3_SERVICE_NAME': 'group2', - 'SERVICE1_PORTS': '80:80', - 'SERVICE2_PORTS': '81:80,82:8000', - 'SERVICE3_PORTS': '80:unix://unix.socket', - 'GROUP3_TOR_SERVICE_VERSION': '2', - 'GROUP3_TOR_SERVICE_HOSTS': '80:service4:888,81:service5:8080', - 'GROUP4_TOR_SERVICE_VERSION': '3', - 'GROUP4_TOR_SERVICE_HOSTS': '81:unix://unix2.sock', - 'GROUP3V3_TOR_SERVICE_VERSION': '3', - 'GROUP3V3_TOR_SERVICE_HOSTS': '80:service4:888,81:service5:8080', - 'SERVICE5_TOR_SERVICE_HOSTS': '80:service5:80', - 'TOR_EXTRA_OPTIONS': extra_options, + "SERVICE1_SERVICE_NAME": "group1", + "SERVICE2_SERVICE_NAME": "group1", + "SERVICE3_SERVICE_NAME": "group2", + "SERVICE1_PORTS": "80:80", + "SERVICE2_PORTS": "81:80,82:8000", + "SERVICE3_PORTS": "80:unix://unix.socket", + "GROUP3_TOR_SERVICE_VERSION": "2", + "GROUP3_TOR_SERVICE_HOSTS": "80:service4:888,81:service5:8080", + "GROUP4_TOR_SERVICE_VERSION": "3", + "GROUP4_TOR_SERVICE_HOSTS": "81:unix://unix2.sock", + "GROUP3V3_TOR_SERVICE_VERSION": "3", + "GROUP3V3_TOR_SERVICE_HOSTS": "80:service4:888,81:service5:8080", + "SERVICE5_TOR_SERVICE_HOSTS": "80:service5:80", + "TOR_EXTRA_OPTIONS": extra_options, } - hidden_dir = '/var/lib/tor/hidden_service' + hidden_dir = "/var/lib/tor/hidden_service" - monkeypatch.setattr(os, 'environ', env) - monkeypatch.setattr(os, 'fchmod', lambda x, y: None) + monkeypatch.setattr(os, "environ", env) + monkeypatch.setattr(os, "fchmod", lambda x, y: None) torrc_tpl = get_torrc_template() - fs.create_file('/var/local/tor/torrc.tpl', contents=torrc_tpl) - fs.create_file('/etc/tor/torrc') + fs.create_file("/var/local/tor/torrc.tpl", contents=torrc_tpl) + fs.create_file("/etc/tor/torrc") fs.create_dir(hidden_dir) onion = Onions() @@ -351,30 +363,31 @@ HiddenServiceSingleHopMode 1 onions_urls = {} for dir in os.listdir(hidden_dir): - with open(os.path.join(hidden_dir, dir, 'hostname'), 'r') as f: + with open(os.path.join(hidden_dir, dir, "hostname"), "r") as f: onions_urls[dir] = f.read().strip() - with open('/etc/tor/torrc', 'r') as f: + with open("/etc/tor/torrc", "r") as f: torrc = f.read() print(torrc) - assert 'HiddenServiceDir /var/lib/tor/hidden_service/group1' in torrc - assert 'HiddenServicePort 80 service1:80' in torrc - assert 'HiddenServicePort 81 service2:80' in torrc - assert 'HiddenServicePort 82 service2:8000' in torrc - assert 'HiddenServiceDir /var/lib/tor/hidden_service/group2' in torrc - assert 'HiddenServicePort 80 unix://unix.socket' in torrc - assert 'HiddenServiceDir /var/lib/tor/hidden_service/group3' in torrc - assert 'HiddenServiceDir /var/lib/tor/hidden_service/group4' in torrc - assert 'HiddenServiceDir /var/lib/tor/hidden_service/group3v3' in torrc - assert 'HiddenServiceDir /var/lib/tor/hidden_service/service5' in torrc - assert torrc.count('HiddenServicePort 80 service4:888') == 2 - assert torrc.count('HiddenServicePort 81 service5:8080') == 2 - assert torrc.count('HiddenServicePort 80 service5:80') == 1 - assert torrc.count('HiddenServicePort 81 unix://unix2.sock') == 1 - assert torrc.count('HiddenServiceVersion 3') == 2 - assert 'HiddenServiceNonAnonymousMode 1\n' in torrc - assert 'HiddenServiceSingleHopMode 1\n' in torrc + assert "HiddenServiceDir /var/lib/tor/hidden_service/group1" in torrc + assert "HiddenServicePort 80 service1:80" in torrc + assert "HiddenServicePort 81 service2:80" in torrc + assert "HiddenServicePort 82 service2:8000" in torrc + assert "HiddenServiceDir /var/lib/tor/hidden_service/group2" in torrc + assert "HiddenServicePort 80 unix://unix.socket" in torrc + assert "HiddenServiceDir /var/lib/tor/hidden_service/group3" in torrc + assert "HiddenServiceDir /var/lib/tor/hidden_service/group4" in torrc + assert "HiddenServiceDir /var/lib/tor/hidden_service/group3v3" in torrc + assert "HiddenServiceDir /var/lib/tor/hidden_service/service5" in torrc + assert torrc.count("HiddenServicePort 80 service4:888") == 2 + assert torrc.count("HiddenServicePort 81 service5:8080") == 2 + assert torrc.count("HiddenServicePort 80 service5:80") == 1 + assert torrc.count("HiddenServicePort 81 unix://unix2.sock") == 1 + assert torrc.count("HiddenServiceVersion 3") == 2 + assert "HiddenServiceNonAnonymousMode 1\n" in torrc + assert "HiddenServiceSingleHopMode 1\n" in torrc + assert "ControlPort" not in torrc # Check parser onion2 = Onions() @@ -383,141 +396,184 @@ HiddenServiceSingleHopMode 1 assert len(onion2.services) == 6 assert set( - group.name for group in onion2.services + group.name + for group in onion2.services # ) == set(['group1', 'group2']) - ) == set(['group1', 'group2', 'group3', 'group4', 'group3v3', 'service5']) + ) == set(["group1", "group2", "group3", "group4", "group3v3", "service5"]) for group in onion2.services: - if group.name == 'group1': + if group.name == "group1": assert len(group.services) == 2 assert group.version == 2 assert group.onion_url == onions_urls[group.name] - assert set( - service.host for service in group.services - ) == set(['service1', 'service2']) + assert set(service.host for service in group.services) == set( + ["service1", "service2"] + ) for service in group.services: - if service.host == 'service1': + if service.host == "service1": assert len(service.ports) == 1 assert set( (port.port_from, port.dest) for port in service.ports ) == set([(80, 80)]) - if service.host == 'service2': + if service.host == "service2": assert len(service.ports) == 2 assert set( (port.port_from, port.dest) for port in service.ports ) == set([(81, 80), (82, 8000)]) - if group.name == 'group2': + if group.name == "group2": assert len(group.services) == 1 assert group.version == 2 assert group.onion_url == onions_urls[group.name] - assert set( - service.host for service in group.services - ) == set(['group2']) + assert set(service.host for service in group.services) == set( + ["group2"] + ) service = group.services[0] assert len(service.ports) == 1 assert set( (port.port_from, port.dest) for port in service.ports - ) == set([(80, 'unix://unix.socket')]) + ) == set([(80, "unix://unix.socket")]) - if group.name in ['group3', 'group3v3']: + if group.name in ["group3", "group3v3"]: assert len(group.services) == 2 - assert group.version == 2 if group.name == 'group3' else 3 + assert group.version == 2 if group.name == "group3" else 3 assert group.onion_url == onions_urls[group.name] - assert set( - service.host for service in group.services - ) == set(['service4', 'service5']) + assert set(service.host for service in group.services) == set( + ["service4", "service5"] + ) for service in group.services: - if service.host == 'service4': + if service.host == "service4": assert len(service.ports) == 1 assert set( (port.port_from, port.dest) for port in service.ports ) == set([(80, 888)]) - if service.host == 'service5': + if service.host == "service5": assert len(service.ports) == 1 assert set( (port.port_from, port.dest) for port in service.ports ) == set([(81, 8080)]) - if group.name == 'group4': + if group.name == "group4": assert len(group.services) == 1 assert group.version == 3 assert group.onion_url == onions_urls[group.name] - assert set( - service.host for service in group.services - ) == set(['group4']) + assert set(service.host for service in group.services) == set( + ["group4"] + ) for service in group.services: - assert service.host == 'group4' + assert service.host == "group4" assert len(service.ports) == 1 assert set( (port.port_from, port.dest) for port in service.ports - ) == set([(81, 'unix://unix2.sock')]) + ) == set([(81, "unix://unix2.sock")]) - if group.name == 'service5': + if group.name == "service5": assert len(group.services) == 1 assert group.version == 2 assert group.onion_url == onions_urls[group.name] - assert set( - service.host for service in group.services - ) == set(['service5']) + assert set(service.host for service in group.services) == set( + ["service5"] + ) for service in group.services: - assert service.host == 'service5' + assert service.host == "service5" assert len(service.ports) == 1 assert set( (port.port_from, port.dest) for port in service.ports ) == set([(80, 80)]) + # bug with fakefs, test everything in the same function + + env = { + "TOR_CONTROL_PORT": "172.0.1.0:7867", + "TOR_CONTROL_PASSWORD": "secret", + } + + def mock_hash(self, password): + self.control_hashed_password = "myhashedpassword" + + monkeypatch.setattr(os, "environ", env) + monkeypatch.setattr(Onions, "_hash_control_port_password", mock_hash) + + onion = Onions() + onion._setup_control_port() + onion.apply_conf() + + with open("/etc/tor/torrc", "r") as f: + torrc = f.read() + + print(torrc) + assert "ControlPort 172.0.1.0:7867" in torrc + assert f"HashedControlPassword {onion.control_hashed_password}" in torrc + + env = { + "TOR_CONTROL_PORT": "unix:/path/to.socket", + } + + monkeypatch.setattr(os, "environ", env) + + torrc_tpl = get_torrc_template() + + onion = Onions() + onion._setup_control_port() + onion.apply_conf() + + with open("/etc/tor/torrc", "r") as f: + torrc = f.read() + + print(torrc) + assert "ControlPort unix:/path/to.socket" in torrc + def test_groups(monkeypatch): env = { - 'SERVICE1_SERVICE_NAME': 'group1', - 'SERVICE2_SERVICE_NAME': 'group1', - 'SERVICE3_SERVICE_NAME': 'group2', - 'SERVICE1_PORTS': '80:80', - 'SERVICE2_PORTS': '81:80,82:8000', - 'SERVICE3_PORTS': '80:unix://unix.socket', + "SERVICE1_SERVICE_NAME": "group1", + "SERVICE2_SERVICE_NAME": "group1", + "SERVICE3_SERVICE_NAME": "group2", + "SERVICE1_PORTS": "80:80", + "SERVICE2_PORTS": "81:80,82:8000", + "SERVICE3_PORTS": "80:unix://unix.socket", } - monkeypatch.setattr(os, 'environ', env) + monkeypatch.setattr(os, "environ", env) onion = Onions() onion._get_setup_from_env() - onion_match = r'^[a-z2-7]{16}.onion$' + onion_match = r"^[a-z2-7]{16}.onion$" assert len(os.environ) == 6 assert len(onion.services) == 2 - assert set( - group.name for group in onion.services - ) == set(['group1', 'group2']) + assert set(group.name for group in onion.services) == set( + ["group1", "group2"] + ) for group in onion.services: - if group.name == 'group1': + if group.name == "group1": assert len(group.services) == 2 - assert set( - service.host for service in group.services - ) == set(['service1', 'service2']) + assert set(service.host for service in group.services) == set( + ["service1", "service2"] + ) - if group.name == 'group2': + if group.name == "group2": assert len(group.services) == 1 - assert set( - service.host for service in group.services - ) == set(['service3']) + assert set(service.host for service in group.services) == set( + ["service3"] + ) assert re.match(onion_match, group.onion_url) def test_json(monkeypatch): env = { - 'SERVICE1_SERVICE_NAME': 'group1', - 'SERVICE2_SERVICE_NAME': 'group1', - 'SERVICE3_SERVICE_NAME': 'group2', - 'SERVICE1_PORTS': '80:80', - 'SERVICE2_PORTS': '81:80,82:8000', - 'SERVICE3_PORTS': '80:unix://unix.socket', + "SERVICE1_SERVICE_NAME": "group1", + "SERVICE2_SERVICE_NAME": "group1", + "SERVICE3_SERVICE_NAME": "group2", + "SERVICE1_PORTS": "80:80", + "SERVICE2_PORTS": "81:80,82:8000", + "SERVICE3_PORTS": "80:unix://unix.socket", } - monkeypatch.setattr(os, 'environ', env) + monkeypatch.setattr(os, "environ", env) onion = Onions() onion._get_setup_from_env() @@ -526,61 +582,188 @@ def test_json(monkeypatch): jsn = json.loads(onion.to_json()) assert len(jsn) == 2 - assert len(jsn['group1']) == 3 - assert len(jsn['group2']) == 1 + assert len(jsn["group1"]) == 3 + assert len(jsn["group2"]) == 1 def test_output(monkeypatch): env = { - 'SERVICE1_SERVICE_NAME': 'group1', - 'SERVICE2_SERVICE_NAME': 'group1', - 'SERVICE3_SERVICE_NAME': 'group2', - 'SERVICE1_PORTS': '80:80', - 'SERVICE2_PORTS': '81:80,82:8000', - 'SERVICE3_PORTS': '80:unix://unix.socket', + "SERVICE1_SERVICE_NAME": "group1", + "SERVICE2_SERVICE_NAME": "group1", + "SERVICE3_SERVICE_NAME": "group2", + "SERVICE1_PORTS": "80:80", + "SERVICE2_PORTS": "81:80,82:8000", + "SERVICE3_PORTS": "80:unix://unix.socket", } - monkeypatch.setattr(os, 'environ', env) + monkeypatch.setattr(os, "environ", env) onion = Onions() onion._get_setup_from_env() - for item in ['group1', 'group2', '.onion', ',']: + for item in ["group1", "group2", ".onion", ","]: assert item in str(onion) def test_not_valid_share_port(monkeypatch): env = { - 'SERVICE1_SERVICE_NAME': 'group1', - 'SERVICE2_SERVICE_NAME': 'group1', - 'SERVICE3_SERVICE_NAME': 'group2', - 'SERVICE1_PORTS': '80:80', - 'SERVICE2_PORTS': '80:80,82:8000', - 'SERVICE3_PORTS': '80:unix://unix.socket', + "SERVICE1_SERVICE_NAME": "group1", + "SERVICE2_SERVICE_NAME": "group1", + "SERVICE3_SERVICE_NAME": "group2", + "SERVICE1_PORTS": "80:80", + "SERVICE2_PORTS": "80:80,82:8000", + "SERVICE3_PORTS": "80:unix://unix.socket", } - monkeypatch.setattr(os, 'environ', env) + monkeypatch.setattr(os, "environ", env) onion = Onions() onion._get_setup_from_env() with pytest.raises(Exception) as excinfo: onion.check_services() - assert 'Same port for multiple services' in str(excinfo.value) + assert "Same port for multiple services" in str(excinfo.value) def test_not_valid_no_services(monkeypatch): env = { - 'SERVICE1_SERVICE_NAME': 'group1', - 'SERVICE2_SERVICE_NAME': 'group1', - 'SERVICE3_SERVICE_NAME': 'group2', + "SERVICE1_SERVICE_NAME": "group1", + "SERVICE2_SERVICE_NAME": "group1", + "SERVICE3_SERVICE_NAME": "group2", } - monkeypatch.setattr(os, 'environ', env) + monkeypatch.setattr(os, "environ", env) onion = Onions() onion._get_setup_from_env() with pytest.raises(Exception) as excinfo: onion.check_services() - assert 'has not ports set' in str(excinfo.value) + assert "has not ports set" in str(excinfo.value) + + +def get_vanguards_template(): + return r""" +## Global options +[Global] + +{% if env.get('TOR_CONTROL_PORT', '').startswith('unix:') %} +{% set _, unix_path = env['TOR_CONTROL_PORT'].split(':', 1) %} +{% elif ':' in env.get('TOR_CONTROL_PORT', '') %} +{% set host, port = env['TOR_CONTROL_PORT'].split(':', 1) %} +{% else %} +{% set host = env.get('TOR_CONTROL_PORT') %} +{% endif %} + +control_ip = {{ host or '' }} + +control_port = {{ port or 9051 }} + +control_socket = {{ unix_path or '' }} + +control_pass = {{ env.get('TOR_CONTROL_PASSWORD', '') }} + +state_file = {{ env.get('VANGUARDS_STATE_FILE', '/run/tor/data/vanguards.state') }} + + +{% if 'VANGUARDS_EXTRA_OPTIONS' in env %} +{% set extra_conf = ConfigParser().read_string(env['VANGUARDS_EXTRA_OPTIONS']) %} +{% if 'Global' in extra_conf %} +{% for key, val in extra_conf['Global'].items() %} +{{key}} = {{val}} +{% endfor %} +{% set _ = extra_conf.pop('Global') %} +{% endif %} +{{ extra_conf.to_string() }} +{% endif %} + + """.strip() # noqa + + +def test_vanguards_configuration_sock(fs, monkeypatch): + extra_options = """ +[Global] +enable_cbtverify = True +loglevel = DEBUG + +[Rendguard] +rend_use_max_use_to_bw_ratio = 4.0 + """.strip() + + env = { + "TOR_ENABLE_VANGUARDS": "true", + "TOR_CONTROL_PORT": "unix:/path/to/sock", + "VANGUARDS_EXTRA_OPTIONS": extra_options, + } + + monkeypatch.setattr(os, "environ", env) + monkeypatch.setattr(os, "fchmod", lambda x, y: None) + + torrc_tpl = get_vanguards_template() + + fs.create_file("/var/local/tor/vanguards.conf.tpl", contents=torrc_tpl) + fs.create_file("/etc/tor/vanguards.conf") + + onion = Onions() + onion.resolve_control_port() + onion._setup_vanguards() + onion._write_vanguards_conf() + + vanguard_conf = configparser.ConfigParser() + + with open("/etc/tor/vanguards.conf", "r") as f: + print(f.read()) + + vanguard_conf.read("/etc/tor/vanguards.conf") + + assert vanguard_conf["Global"] + assert not vanguard_conf["Global"]["control_ip"] + assert vanguard_conf["Global"]["control_port"] == "9051" + assert vanguard_conf["Global"]["control_socket"] == "/path/to/sock" + assert not vanguard_conf["Global"]["control_pass"] + assert ( + vanguard_conf["Global"]["state_file"] + == "/run/tor/data/vanguards.state" + ) + assert vanguard_conf["Global"]["enable_cbtverify"] + assert vanguard_conf["Global"]["loglevel"] == "DEBUG" + assert vanguard_conf["Rendguard"]["rend_use_max_use_to_bw_ratio"] == "4.0" + + +def test_vanguards_configuration_ip(fs, monkeypatch): + + env = { + "TOR_ENABLE_VANGUARDS": "true", + "TOR_CONTROL_PORT": "127.0.0.1:7864", + "TOR_CONTROL_PASSWORD": "secret", + } + + monkeypatch.setattr(os, "environ", env) + monkeypatch.setattr(os, "fchmod", lambda x, y: None) + + torrc_tpl = get_vanguards_template() + + fs.create_file("/var/local/tor/vanguards.conf.tpl", contents=torrc_tpl) + fs.create_file("/etc/tor/vanguards.conf") + + onion = Onions() + onion.resolve_control_port() + onion._setup_vanguards() + onion._write_vanguards_conf() + + vanguard_conf = configparser.ConfigParser() + + with open("/etc/tor/vanguards.conf", "r") as f: + print(f.read()) + + vanguard_conf.read("/etc/tor/vanguards.conf") + + assert vanguard_conf["Global"] + assert vanguard_conf["Global"]["control_ip"] == "127.0.0.1" + assert vanguard_conf["Global"]["control_port"] == "7864" + assert not vanguard_conf["Global"]["control_socket"] + assert vanguard_conf["Global"]["control_pass"] == "secret" + assert ( + vanguard_conf["Global"]["state_file"] + == "/run/tor/data/vanguards.state" + )