mirror of
https://github.com/cmehay/docker-tor-hidden-service.git
synced 2025-04-20 13:59:12 +00:00
769 lines
24 KiB
Python
769 lines
24 KiB
Python
import configparser
|
|
import json
|
|
import os
|
|
import re
|
|
from base64 import b32encode
|
|
from base64 import b64decode
|
|
from hashlib import sha1
|
|
|
|
import pytest
|
|
from Crypto.PublicKey import RSA
|
|
|
|
from onions import Onions
|
|
|
|
|
|
def get_key_and_onion(version=2):
|
|
key = {}
|
|
key[
|
|
2
|
|
] = """
|
|
-----BEGIN RSA PRIVATE KEY-----
|
|
MIICXAIBAAKBgQCsMP4gl6g1Q313miPhb1GnDr56ZxIWGsO2PwHM1infkbhlBakR
|
|
6DGQfpE31L1ZKTUxY0OexKbW088v8qCOfjD9Zk1i80JP4xzfWQcwFZ5yM/0fkhm3
|
|
zLXqXdEahvRthmFsS8OWusRs/04U247ryTm4k5S0Ch5OTBuvMLzQ8W0yDwIDAQAB
|
|
AoGAAZr3U5B2ZgC6E7phKUHjbf5KMlPxrDkVqAZQWvuIKmhuYqq518vlYmZ7rhyS
|
|
o1kqAMrfH4TP1WLmJJlLe+ibRk2aonR4e0GbW4x151wcJdT1V3vdWAsVSzG3+dqX
|
|
PiGT//DIe0OPSH6ecI8ftFRLODd6f5iGkF4gsUSTcVzAFgkCQQDTY67dRpOD9Ozw
|
|
oYH48xe0B9NQCw7g4NSH85jPurJXnpn6lZ6bcl8x8ioAdgLyomR7fO/dJFYLw6uV
|
|
LZLqZsVbAkEA0Iei3QcpsJnYgcQG7l5I26Sq3LwoiGRDFKRI6k0e+en9JQJgA3Ay
|
|
tsLpyCHv9jQ762F6AVXFru5DmZX40F6AXQJBAIHoKac8Xx1h4FaEuo4WPkPZ50ey
|
|
dANIx/OAhTFrp3vnMPNpDV60K8JS8vLzkx4vJBcrkXDSirqSFhkIN9grLi8CQEO2
|
|
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
|
|
] = """
|
|
PT0gZWQyNTUxOXYxLXNlY3JldDogdHlwZTAgPT0AAACArobDQYyZAWXei4QZwr++j96H1X/gq14N
|
|
wLRZ2O5DXuL0EzYKkdhZSILY85q+kfwZH8z4ceqe7u1F+0pQi/sM
|
|
"""
|
|
|
|
pub[
|
|
3
|
|
] = """
|
|
PT0gZWQyNTUxOXYxLXB1YmxpYzogdHlwZTAgPT0AAAC9kzftiea/kb+TWlCEVNpfUJLVk+rFIoMG
|
|
m9/hW13isA==
|
|
"""
|
|
|
|
onion[3] = "xwjtp3mj427zdp4tljiiivg2l5ijfvmt5lcsfaygtpp6cw254kykvpyd.onion"
|
|
|
|
return key[version].strip(), onion[version]
|
|
|
|
|
|
def get_torrc_template():
|
|
return r"""
|
|
{% for service_group in onion.services %}
|
|
HiddenServiceDir {{service_group.hidden_service_dir}}
|
|
{% if service_group.version == 3 %}
|
|
HiddenServiceVersion 3
|
|
{% endif %}
|
|
{% for service in service_group.services %}
|
|
{% for port in service.ports %}
|
|
{% if port.is_socket %}
|
|
HiddenServicePort {{port.port_from}} {{port.dest}}
|
|
{% endif %}
|
|
{% if not port.is_socket %}
|
|
HiddenServicePort {{port.port_from}} {{service.host}}:{{port.dest}}
|
|
{% endif %}
|
|
{% endfor %}
|
|
{% endfor %}
|
|
{% endfor %}
|
|
DataDirectory {{ onion.data_directory }}
|
|
{% if 'TOR_SOCKS_PORT' in env %}
|
|
SocksPort {{env['TOR_SOCKS_PORT']}}
|
|
{% else %}
|
|
SocksPort 0
|
|
{% endif %}
|
|
|
|
{% if envtobool('TOR_EXIT_RELAY', False) %}
|
|
ExitRelay 1
|
|
{% else %}
|
|
ExitRelay 0
|
|
{% endif %}
|
|
|
|
{% if onion.enable_control_port %}
|
|
{% if onion.control_socket %}
|
|
ControlPort {{onion.control_socket}}
|
|
{% endif %}
|
|
{% if not onion.control_socket %}
|
|
{% if onion.control_ip_binding.version() == 4 %}
|
|
ControlPort {{onion.control_ip_binding}}:{{ onion.control_port }}
|
|
{% endif %}
|
|
{% if onion.control_ip_binding.version() == 6 %}
|
|
ControlPort [{{onion.control_ip_binding}}]:{{ onion.control_port }}
|
|
{% endif %}
|
|
{% endif %}
|
|
{% if onion.control_hashed_password %}
|
|
HashedControlPassword {{ onion.control_hashed_password }}
|
|
{% endif %}
|
|
{% endif %}
|
|
|
|
|
|
{% if 'TOR_EXTRA_OPTIONS' in env %}
|
|
{{env['TOR_EXTRA_OPTIONS']}}
|
|
{% endif %}
|
|
|
|
# useless line for Jinja bug
|
|
|
|
|
|
# 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",
|
|
}
|
|
|
|
monkeypatch.setattr(os, "environ", env)
|
|
|
|
onion = Onions()
|
|
onion._get_setup_from_env()
|
|
assert len(os.environ) == 3
|
|
assert len(onion.services) == 3
|
|
check = 0
|
|
for service_group in onion.services:
|
|
assert len(service_group.services) == 1
|
|
service = service_group.services[0]
|
|
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":
|
|
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":
|
|
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].is_socket
|
|
|
|
assert check == 10
|
|
|
|
|
|
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",
|
|
}
|
|
|
|
etc_host = """
|
|
127.0.0.1 localhost
|
|
::1 localhost ip6-localhost ip6-loopback
|
|
fe00::0 ip6-localnet
|
|
ff00::0 ip6-mcastprefix
|
|
ff02::1 ip6-allnodes
|
|
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()
|
|
|
|
fs.create_file("/etc/hosts", contents=etc_host)
|
|
|
|
monkeypatch.setattr(os, "environ", env)
|
|
|
|
onion = Onions()
|
|
onion._get_setup_from_links()
|
|
|
|
assert len(onion.services) == 1
|
|
group = onion.services[0]
|
|
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)]
|
|
)
|
|
|
|
|
|
def test_key(monkeypatch):
|
|
|
|
key, onion_url = get_key_and_onion()
|
|
env = {"SERVICE1_KEY": key}
|
|
|
|
monkeypatch.setattr(os, "environ", env)
|
|
|
|
onion = Onions()
|
|
onion._get_setup_from_env()
|
|
|
|
assert len(os.environ) == 1
|
|
assert len(onion.services) == 1
|
|
|
|
assert onion.services[0].onion_url == onion_url
|
|
|
|
|
|
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,
|
|
},
|
|
]
|
|
|
|
for env in envs:
|
|
monkeypatch.setattr(os, "environ", env)
|
|
|
|
onion = Onions()
|
|
onion._get_setup_from_env()
|
|
onion._load_keys_in_services()
|
|
|
|
assert len(os.environ) == len(env)
|
|
assert len(onion.services) == 1
|
|
|
|
assert onion.services[0].onion_url == onion_url
|
|
|
|
|
|
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,
|
|
}
|
|
|
|
monkeypatch.setattr(os, "environ", env)
|
|
|
|
onion = Onions()
|
|
onion._get_setup_from_env()
|
|
onion._load_keys_in_services()
|
|
|
|
assert len(os.environ) == 3
|
|
assert len(onion.services) == 1
|
|
|
|
assert onion.services[0].onion_url == onion_url
|
|
|
|
|
|
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",
|
|
}
|
|
|
|
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))
|
|
|
|
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")
|
|
|
|
assert group1.onion_url == onion_url_v2
|
|
assert group2.onion_url not in [onion_url_v2, onion_url_v3]
|
|
assert group3.onion_url == onion_url_v3
|
|
|
|
|
|
def test_configuration(fs, monkeypatch, tmpdir):
|
|
extra_options = """
|
|
HiddenServiceNonAnonymousMode 1
|
|
HiddenServiceSingleHopMode 1
|
|
""".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,
|
|
}
|
|
|
|
hidden_dir = "/var/lib/tor/hidden_service"
|
|
|
|
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_dir(hidden_dir)
|
|
|
|
onion = Onions()
|
|
onion._get_setup_from_env()
|
|
onion._load_keys_in_services()
|
|
onion.apply_conf()
|
|
|
|
onions_urls = {}
|
|
for dir in os.listdir(hidden_dir):
|
|
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:
|
|
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 "ControlPort" not in torrc
|
|
|
|
# Check parser
|
|
onion2 = Onions()
|
|
onion2.torrc_parser()
|
|
|
|
assert len(onion2.services) == 6
|
|
|
|
assert set(
|
|
group.name
|
|
for group in onion2.services
|
|
# ) == set(['group1', 'group2'])
|
|
) == set(["group1", "group2", "group3", "group4", "group3v3", "service5"])
|
|
|
|
for group in onion2.services:
|
|
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"]
|
|
)
|
|
for service in group.services:
|
|
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":
|
|
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":
|
|
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"]
|
|
)
|
|
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")])
|
|
|
|
if group.name in ["group3", "group3v3"]:
|
|
assert len(group.services) == 2
|
|
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"]
|
|
)
|
|
for service in group.services:
|
|
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":
|
|
assert len(service.ports) == 1
|
|
assert set(
|
|
(port.port_from, port.dest) for port in service.ports
|
|
) == set([(81, 8080)])
|
|
|
|
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"]
|
|
)
|
|
for service in group.services:
|
|
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")])
|
|
|
|
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"]
|
|
)
|
|
for service in group.services:
|
|
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",
|
|
}
|
|
|
|
monkeypatch.setattr(os, "environ", env)
|
|
|
|
onion = Onions()
|
|
onion._get_setup_from_env()
|
|
|
|
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"]
|
|
)
|
|
|
|
for group in onion.services:
|
|
if group.name == "group1":
|
|
assert len(group.services) == 2
|
|
assert set(service.host for service in group.services) == set(
|
|
["service1", "service2"]
|
|
)
|
|
|
|
if group.name == "group2":
|
|
assert len(group.services) == 1
|
|
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",
|
|
}
|
|
|
|
monkeypatch.setattr(os, "environ", env)
|
|
|
|
onion = Onions()
|
|
onion._get_setup_from_env()
|
|
onion.check_services()
|
|
|
|
jsn = json.loads(onion.to_json())
|
|
|
|
assert len(jsn) == 2
|
|
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",
|
|
}
|
|
|
|
monkeypatch.setattr(os, "environ", env)
|
|
|
|
onion = Onions()
|
|
onion._get_setup_from_env()
|
|
|
|
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",
|
|
}
|
|
|
|
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)
|
|
|
|
|
|
def test_not_valid_no_services(monkeypatch):
|
|
env = {
|
|
"SERVICE1_SERVICE_NAME": "group1",
|
|
"SERVICE2_SERVICE_NAME": "group1",
|
|
"SERVICE3_SERVICE_NAME": "group2",
|
|
}
|
|
|
|
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)
|
|
|
|
|
|
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"
|
|
)
|