mirror of
https://github.com/cmehay/docker-tor-hidden-service.git
synced 2025-05-27 04:07:13 +00:00
Add tor v3 addresses support
This commit is contained in:
parent
8d562ed2e6
commit
d3252e276e
24 changed files with 1086 additions and 225 deletions
|
@ -7,11 +7,13 @@ secret_env:
|
|||
- '*_KEY'
|
||||
- '*_PORTS'
|
||||
- '*_SERVICE_NAME'
|
||||
- '*_TOR_SERVICE_*'
|
||||
|
||||
pre_conf_commands:
|
||||
- onions --setup-hosts
|
||||
|
||||
post_conf_commands:
|
||||
- chmod -R 700 $HOME
|
||||
- chown -R tor:tor $HOME
|
||||
|
||||
reload:
|
||||
|
|
|
@ -3,6 +3,7 @@ import argparse
|
|||
import logging
|
||||
import os
|
||||
import sys
|
||||
from base64 import b64decode
|
||||
from json import dumps
|
||||
from re import match
|
||||
|
||||
|
@ -46,6 +47,10 @@ class Setup(object):
|
|||
assert len(key) > 800
|
||||
self.setup[host]['key'] = key
|
||||
|
||||
def _load_keys_in_services(self):
|
||||
for service in self.services:
|
||||
service.load_key()
|
||||
|
||||
def _get_service(self, host, service):
|
||||
self._add_host(host)
|
||||
self.setup[host]['service'] = service
|
||||
|
@ -66,30 +71,39 @@ class Setup(object):
|
|||
if service:
|
||||
return service
|
||||
|
||||
def add_empty_group(self, name):
|
||||
def add_empty_group(self, name, version=None):
|
||||
if self.find_group_by_name(name):
|
||||
raise Exception('Group {name} already exists'.format(name=name))
|
||||
group = ServicesGroup(name=name)
|
||||
group = ServicesGroup(name=name, version=version)
|
||||
self.services.append(group)
|
||||
return group
|
||||
|
||||
def add_new_service(self, host, name=None, ports=None, key=None):
|
||||
def add_new_service(self,
|
||||
host,
|
||||
name=None,
|
||||
ports=None,
|
||||
key=None):
|
||||
group = self.find_group_by_name(name)
|
||||
service = self.find_service_by_host(host)
|
||||
if group:
|
||||
service = group.get_service_by_host(host)
|
||||
else:
|
||||
service = self.find_service_by_host(host)
|
||||
if not service:
|
||||
service = Service(host=host)
|
||||
if not group:
|
||||
group = ServicesGroup(
|
||||
service=service,
|
||||
name=name,
|
||||
hidden_service_dir=self.hidden_service_dir
|
||||
hidden_service_dir=self.hidden_service_dir,
|
||||
)
|
||||
else:
|
||||
group.add_service(service)
|
||||
if group not in self.services:
|
||||
self.services.append(group)
|
||||
elif group and service not in group.services:
|
||||
group.add_service(service)
|
||||
else:
|
||||
group = self.find_group_by_service(service)
|
||||
self.find_group_by_service(service)
|
||||
if key:
|
||||
group.add_key(key)
|
||||
if ports:
|
||||
|
@ -108,22 +122,69 @@ class Setup(object):
|
|||
self.add_new_service(host=host, ports=ports)
|
||||
|
||||
def _set_key(self, host, key):
|
||||
self.add_new_service(host=host, key=key)
|
||||
self.add_new_service(host=host, key=key.encode())
|
||||
|
||||
def _setup_from_env(self):
|
||||
match_map = (
|
||||
(r'([A-Z0-9]*)_PORTS', self._set_ports),
|
||||
(r'([A-Z0-9]*)_KEY', self._set_key),
|
||||
)
|
||||
for key, val in os.environ.items():
|
||||
for reg, call in match_map:
|
||||
def _setup_from_env(self, match_map):
|
||||
for reg, call in match_map:
|
||||
for key, val in os.environ.items():
|
||||
m = match(reg, key)
|
||||
if m:
|
||||
call(m.groups()[0].lower(), val)
|
||||
|
||||
def _setup_keys_and_ports_from_env(self):
|
||||
self._setup_from_env(
|
||||
(
|
||||
(r'([A-Z0-9]+)_PORTS', self._set_ports),
|
||||
(r'([A-Z0-9]+)_KEY', self._set_key),
|
||||
)
|
||||
)
|
||||
|
||||
def get_or_create_empty_group(self, name, version=None):
|
||||
group = self.find_group_by_name(name)
|
||||
if group:
|
||||
if version:
|
||||
group.set_version(version)
|
||||
return group
|
||||
return self.add_empty_group(name, version)
|
||||
|
||||
def _set_group_version(self, name, version):
|
||||
'Setup groups with version'
|
||||
group = self.get_or_create_empty_group(name, version=version)
|
||||
group.set_version(version)
|
||||
|
||||
def _set_group_key(self, name, key):
|
||||
'Set key for service group'
|
||||
group = self.get_or_create_empty_group(name)
|
||||
if group.version == 3:
|
||||
group.add_key(b64decode(key))
|
||||
else:
|
||||
group.add_key(key)
|
||||
|
||||
def _set_group_hosts(self, name, hosts):
|
||||
'Set services for service groups'
|
||||
self.get_or_create_empty_group(name)
|
||||
for host_map in hosts.split(','):
|
||||
host_map = host_map.strip()
|
||||
port_from, host, port_dest = host_map.split(':', 2)
|
||||
if host == 'unix' and port_dest.startswith('/'):
|
||||
self.add_new_service(host=name, name=name, ports=host_map)
|
||||
else:
|
||||
ports = '{frm}:{dst}'.format(frm=port_from, dst=port_dest)
|
||||
self.add_new_service(host=host, name=name, ports=ports)
|
||||
|
||||
def _setup_services_from_env(self):
|
||||
self._setup_from_env(
|
||||
(
|
||||
(r'([A-Z0-9]+)_TOR_SERVICE_VERSION', self._set_group_version),
|
||||
(r'([A-Z0-9]+)_TOR_SERVICE_KEY', self._set_group_key),
|
||||
(r'([A-Z0-9]+)_TOR_SERVICE_HOSTS', self._set_group_hosts),
|
||||
)
|
||||
)
|
||||
|
||||
def _get_setup_from_env(self):
|
||||
self._set_service_names()
|
||||
self._setup_from_env()
|
||||
self._setup_keys_and_ports_from_env()
|
||||
self._setup_services_from_env()
|
||||
|
||||
def _get_setup_from_links(self):
|
||||
containers = DockerLinks().to_containers()
|
||||
|
@ -162,6 +223,7 @@ class Setup(object):
|
|||
self.setup = {}
|
||||
self._get_setup_from_env()
|
||||
self._get_setup_from_links()
|
||||
self._load_keys_in_services()
|
||||
self.check_services()
|
||||
self.apply_conf()
|
||||
|
||||
|
@ -201,38 +263,65 @@ class Onions(Setup):
|
|||
|
||||
def torrc_parser(self):
|
||||
|
||||
self.torrc_dict = {}
|
||||
|
||||
def parse_dir(line):
|
||||
_, path = line.split()
|
||||
group_name = os.path.basename(path)
|
||||
group = (self.find_group_by_name(group_name)
|
||||
or self.add_empty_group(group_name))
|
||||
return group
|
||||
self.torrc_dict[group_name] = {
|
||||
'services': [],
|
||||
}
|
||||
return group_name
|
||||
|
||||
def parse_port(line, service_group):
|
||||
def parse_port(line, name):
|
||||
_, port_from, dest = line.split()
|
||||
service_host, port = dest.split(':')
|
||||
ports_str = '{port_from}:{dest}'
|
||||
name = service_host
|
||||
ports_param = ports_str.format(port_from=port_from,
|
||||
dest=port)
|
||||
if port.startswith('/'):
|
||||
name = service_group.name
|
||||
service_host = name
|
||||
ports_param = ports_str.format(port_from=port_from,
|
||||
dest=dest)
|
||||
service = (service_group.get_service_by_host(name)
|
||||
or Service(name))
|
||||
service.add_ports(ports_param)
|
||||
if service not in service_group.services:
|
||||
service_group.add_service(service)
|
||||
self.torrc_dict[name]['services'].append({
|
||||
'host': service_host,
|
||||
'ports': ports_param,
|
||||
})
|
||||
|
||||
def parse_version(line, name):
|
||||
_, version = line.split()
|
||||
self.torrc_dict[name]['version'] = int(version)
|
||||
|
||||
def setup_services():
|
||||
for name, setup in self.torrc_dict.items():
|
||||
version = setup.get('version', 2)
|
||||
group = (self.find_group_by_name(name)
|
||||
or self.add_empty_group(name, version=version))
|
||||
for service_dict in setup.get('services', []):
|
||||
host = service_dict['host']
|
||||
service = (group.get_service_by_host(host)
|
||||
or Service(host))
|
||||
service.add_ports(service_dict['ports'])
|
||||
if service not in group.services:
|
||||
group.add_service(service)
|
||||
self._load_keys_in_services()
|
||||
|
||||
if not os.path.exists(self.torrc):
|
||||
return
|
||||
with open(self.torrc, 'r') as f:
|
||||
for line in f.readlines():
|
||||
if line.startswith('HiddenServiceDir'):
|
||||
service_group = parse_dir(line)
|
||||
if line.startswith('HiddenServicePort'):
|
||||
parse_port(line, service_group)
|
||||
try:
|
||||
with open(self.torrc, 'r') as f:
|
||||
for line in f.readlines():
|
||||
if line.startswith('HiddenServiceDir'):
|
||||
name = parse_dir(line)
|
||||
if line.startswith('HiddenServicePort'):
|
||||
parse_port(line, name)
|
||||
if line.startswith('HiddenServiceVersion'):
|
||||
parse_version(line, name)
|
||||
except BaseException:
|
||||
raise Exception(
|
||||
'Fail to parse torrc file. Please check the file'
|
||||
)
|
||||
setup_services()
|
||||
|
||||
def __str__(self):
|
||||
if not self.services:
|
||||
|
@ -257,6 +346,7 @@ def main():
|
|||
help='Setup hosts')
|
||||
|
||||
args = parser.parse_args()
|
||||
logging.getLogger().setLevel(logging.WARNING)
|
||||
try:
|
||||
onions = Onions()
|
||||
if args.setup:
|
||||
|
@ -264,6 +354,7 @@ def main():
|
|||
else:
|
||||
onions.torrc_parser()
|
||||
except BaseException as e:
|
||||
logging.exception(e)
|
||||
error_msg = str(e)
|
||||
else:
|
||||
error_msg = None
|
||||
|
@ -271,7 +362,6 @@ def main():
|
|||
if error_msg:
|
||||
print(dumps({'error': error_msg}))
|
||||
sys.exit(1)
|
||||
logging.getLogger().setLevel(logging.ERROR)
|
||||
print(onions.to_json())
|
||||
else:
|
||||
if error_msg:
|
||||
|
|
|
@ -1,42 +1,67 @@
|
|||
'This class define a service link'
|
||||
import base64
|
||||
import binascii
|
||||
import logging
|
||||
import os
|
||||
import pathlib
|
||||
import re
|
||||
from base64 import b32encode
|
||||
from hashlib import sha1
|
||||
|
||||
from Crypto.PublicKey import RSA
|
||||
from pytor import OnionV2
|
||||
from pytor import OnionV3
|
||||
|
||||
|
||||
class ServicesGroup(object):
|
||||
|
||||
name = None
|
||||
_priv_key = None
|
||||
_key_in_secrets = False
|
||||
version = None
|
||||
imported_key = False
|
||||
_default_version = 2
|
||||
_imported_key = False
|
||||
_onion = None
|
||||
_hidden_service_dir = "/var/lib/tor/hidden_service/"
|
||||
|
||||
hidden_service_dir = "/var/lib/tor/hidden_service/"
|
||||
|
||||
def __init__(self, name=None, service=None, hidden_service_dir=None):
|
||||
def __init__(self,
|
||||
name=None,
|
||||
service=None,
|
||||
version=None,
|
||||
hidden_service_dir=None):
|
||||
|
||||
name_regex = r'^[a-zA-Z0-9-_]+$'
|
||||
|
||||
self.hidden_service_dir = hidden_service_dir or self.hidden_service_dir
|
||||
self.onion_map = {
|
||||
2: OnionV2,
|
||||
3: OnionV3,
|
||||
}
|
||||
|
||||
if not name and not service:
|
||||
raise Exception(
|
||||
'Init service group with a name or service at least'
|
||||
)
|
||||
self.services = []
|
||||
self.name = name or service.host
|
||||
if hidden_service_dir:
|
||||
self._hidden_service_dir = hidden_service_dir
|
||||
if not re.match(name_regex, self.name):
|
||||
raise Exception(
|
||||
'Group {name} has invalid name'.format(name=self.name)
|
||||
)
|
||||
if service:
|
||||
self.add_service(service)
|
||||
self.set_version(version or self._default_version)
|
||||
self.gen_key()
|
||||
|
||||
self.load_key()
|
||||
if not self._priv_key:
|
||||
self.gen_key()
|
||||
def set_version(self, version):
|
||||
version = int(version)
|
||||
if version not in self.onion_map:
|
||||
raise Exception(
|
||||
'Url version {version} is not supported'.format(version)
|
||||
)
|
||||
self.version = version
|
||||
self._onion = self.onion_map[version]()
|
||||
|
||||
@property
|
||||
def hidden_service_dir(self):
|
||||
return os.path.join(self._hidden_service_dir, self.name)
|
||||
|
||||
def add_service(self, service):
|
||||
if service not in self.services:
|
||||
|
@ -50,15 +75,22 @@ class ServicesGroup(object):
|
|||
return service
|
||||
|
||||
def add_key(self, key):
|
||||
if self._key_in_secrets:
|
||||
if self._imported_key:
|
||||
logging.warning('Secret key already set, overriding')
|
||||
self._priv_key = key
|
||||
self._key_in_secrets = False
|
||||
# Try to decode key from base64 encoding
|
||||
# import the raw data if the input cannot be decoded as base64
|
||||
try:
|
||||
key = base64.b64decode(key)
|
||||
except binascii.Error:
|
||||
pass
|
||||
self._onion.set_private_key(key)
|
||||
self._imported_key = True
|
||||
|
||||
def __iter__(self):
|
||||
yield 'name', self.name
|
||||
yield 'onion', self.onion_url
|
||||
yield 'urls', list(self.urls)
|
||||
yield 'version', self.version
|
||||
|
||||
def __str__(self):
|
||||
return '{name}: {urls}'.format(name=self.name,
|
||||
|
@ -66,16 +98,7 @@ class ServicesGroup(object):
|
|||
|
||||
@property
|
||||
def onion_url(self):
|
||||
"Get onion url from private key"
|
||||
|
||||
# Convert private RSA to public DER
|
||||
priv = RSA.importKey(self._priv_key.strip())
|
||||
der = priv.publickey().exportKey("DER")
|
||||
|
||||
# hash key, keep first half of sha1, base32 encode
|
||||
onion = b32encode(sha1(der[22:]).digest()[:10])
|
||||
|
||||
return '{onion}.onion'.format(onion=onion.decode().lower())
|
||||
return self._onion.onion_hostname
|
||||
|
||||
@property
|
||||
def urls(self):
|
||||
|
@ -88,30 +111,17 @@ class ServicesGroup(object):
|
|||
'Write key on disk and set tor service'
|
||||
if not hidden_service_dir:
|
||||
hidden_service_dir = self.hidden_service_dir
|
||||
serv_dir = os.path.join(hidden_service_dir, self.name)
|
||||
os.makedirs(serv_dir, exist_ok=True)
|
||||
os.chmod(serv_dir, 0o700)
|
||||
with open(os.path.join(serv_dir, 'private_key'), 'w') as f:
|
||||
f.write(self._priv_key)
|
||||
os.fchmod(f.fileno(), 0o600)
|
||||
with open(os.path.join(serv_dir, 'hostname'), 'w') as f:
|
||||
f.write(self.onion_url)
|
||||
if not os.path.isdir(hidden_service_dir):
|
||||
pathlib.Path(hidden_service_dir).mkdir(parents=True)
|
||||
self._onion.write_hidden_service(hidden_service_dir, force=True)
|
||||
|
||||
def _load_key(self, key_file):
|
||||
if os.path.exists(key_file):
|
||||
with open(key_file, 'r') as f:
|
||||
key = f.read().encode()
|
||||
if not len(key):
|
||||
return
|
||||
try:
|
||||
rsa = RSA.importKey(key)
|
||||
self._priv_key = rsa.exportKey("PEM").decode()
|
||||
except BaseException:
|
||||
raise('Fail to load key for {name} services'.format(
|
||||
name=self.name
|
||||
))
|
||||
with open(key_file, 'rb') as f:
|
||||
self._onion.set_private_key_from_file(f)
|
||||
|
||||
def load_key(self):
|
||||
def load_key(self, override=False):
|
||||
if self._imported_key and not override:
|
||||
return
|
||||
self.load_key_from_secrets()
|
||||
self.load_key_from_conf()
|
||||
|
||||
|
@ -122,8 +132,9 @@ class ServicesGroup(object):
|
|||
return
|
||||
try:
|
||||
self._load_key(secret_file)
|
||||
self._key_in_secrets = True
|
||||
except BaseException:
|
||||
self._imported_key = True
|
||||
except BaseException as e:
|
||||
logging.exception(e)
|
||||
logging.warning('Fail to load key from secret, '
|
||||
'check the key or secret name collision')
|
||||
|
||||
|
@ -131,16 +142,17 @@ class ServicesGroup(object):
|
|||
'Load key from disk if exists'
|
||||
if not hidden_service_dir:
|
||||
hidden_service_dir = self.hidden_service_dir
|
||||
key_file = os.path.join(hidden_service_dir,
|
||||
self.name,
|
||||
'private_key')
|
||||
self._load_key(key_file)
|
||||
if not os.path.isdir(hidden_service_dir):
|
||||
return
|
||||
self._onion.load_hidden_service(hidden_service_dir)
|
||||
|
||||
def gen_key(self):
|
||||
'Generate new 1024 bits RSA key for hidden service'
|
||||
self._priv_key = RSA.generate(
|
||||
bits=1024,
|
||||
).exportKey("PEM").decode()
|
||||
self.imported_key = False
|
||||
return self._onion.gen_new_private_key()
|
||||
|
||||
@property
|
||||
def _priv_key(self):
|
||||
return self._onion.get_private_key()
|
||||
|
||||
|
||||
class Ports:
|
||||
|
|
|
@ -6,7 +6,7 @@ from setuptools import setup
|
|||
setup(
|
||||
name='onions',
|
||||
|
||||
version='0.4.1',
|
||||
version='0.5.0',
|
||||
|
||||
packages=find_packages(),
|
||||
|
||||
|
@ -31,10 +31,9 @@ setup(
|
|||
"Topic :: System :: Installation/Setup",
|
||||
],
|
||||
|
||||
install_requires=['pyentrypoint==0.5.1',
|
||||
'Jinja2>=2.8',
|
||||
'pycrypto', ],
|
||||
|
||||
install_requires=['pyentrypoint==0.5.2',
|
||||
'Jinja2>=2.10',
|
||||
'pytor>=0.1.2'],
|
||||
entry_points={
|
||||
'console_scripts': [
|
||||
'onions = onions:main',
|
||||
|
|
|
@ -2,6 +2,7 @@ import json
|
|||
import os
|
||||
import re
|
||||
from base64 import b32encode
|
||||
from base64 import b64decode
|
||||
from hashlib import sha1
|
||||
|
||||
import pytest
|
||||
|
@ -9,8 +10,9 @@ from Crypto.PublicKey import RSA
|
|||
from onions import Onions
|
||||
|
||||
|
||||
def get_key_and_onion():
|
||||
key = '''
|
||||
def get_key_and_onion(version=2):
|
||||
key = {}
|
||||
key[2] = '''
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIICXAIBAAKBgQCsMP4gl6g1Q313miPhb1GnDr56ZxIWGsO2PwHM1infkbhlBakR
|
||||
6DGQfpE31L1ZKTUxY0OexKbW088v8qCOfjD9Zk1i80JP4xzfWQcwFZ5yM/0fkhm3
|
||||
|
@ -27,24 +29,40 @@ La/7Syrnobngsh/vX90CQB+PSSBqiPSsK2yPz6Gsd6OLCQ9sdy2oRwFTasH8sZyl
|
|||
bhJ3M9WzP/EMkAzyW8mVs1moFp3hRcfQlZHl6g1U9D8=
|
||||
-----END RSA PRIVATE KEY-----
|
||||
'''
|
||||
|
||||
onion = b32encode(
|
||||
onion = {}
|
||||
pub = {}
|
||||
onion[2] = b32encode(
|
||||
sha1(
|
||||
RSA.importKey(
|
||||
key.strip()
|
||||
key[2].strip()
|
||||
).publickey().exportKey(
|
||||
"DER"
|
||||
)[22:]
|
||||
).digest()[:10]
|
||||
).decode().lower() + '.onion'
|
||||
|
||||
return key.strip(), 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 services %}
|
||||
HiddenServiceDir /var/lib/tor/hidden_service/{{service_group.name}}
|
||||
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 %}
|
||||
|
@ -155,7 +173,7 @@ ff02::2 ip6-allrouters
|
|||
172.17.0.2 compose_service1_1 bf447f22cdba
|
||||
'''.strip()
|
||||
|
||||
fs.CreateFile('/etc/hosts', contents=etc_host)
|
||||
fs.create_file('/etc/hosts', contents=etc_host)
|
||||
|
||||
monkeypatch.setattr(os, 'environ', env)
|
||||
|
||||
|
@ -190,34 +208,57 @@ def test_key(monkeypatch):
|
|||
assert onion.services[0].onion_url == onion_url
|
||||
|
||||
|
||||
def test_key_in_secret(fs, monkeypatch):
|
||||
def test_key_v3(monkeypatch):
|
||||
key, onion_url = get_key_and_onion(version=3)
|
||||
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',
|
||||
'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)
|
||||
|
||||
key, onion_url = get_key_and_onion()
|
||||
onion = Onions()
|
||||
onion._get_setup_from_env()
|
||||
onion._load_keys_in_services()
|
||||
|
||||
fs.CreateFile('/run/secrets/group1', contents=key)
|
||||
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 group._priv_key == key
|
||||
assert group1.onion_url == onion_url
|
||||
assert group2.onion_url != onion_url
|
||||
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):
|
||||
def test_configuration(fs, monkeypatch, tmpdir):
|
||||
|
||||
env = {
|
||||
'SERVICE1_SERVICE_NAME': 'group1',
|
||||
'SERVICE2_SERVICE_NAME': 'group1',
|
||||
|
@ -225,44 +266,72 @@ def test_configuration(fs, monkeypatch):
|
|||
'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'
|
||||
}
|
||||
|
||||
hidden_dir = '/var/lib/tor/hidden_service'
|
||||
|
||||
monkeypatch.setattr(os, 'environ', env)
|
||||
monkeypatch.setattr(os, 'fchmod', lambda x, y: None)
|
||||
|
||||
key, onion_url = get_key_and_onion()
|
||||
torrc_tpl = get_torrc_template()
|
||||
|
||||
fs.CreateFile('/var/local/tor/torrc.tpl', contents=torrc_tpl)
|
||||
fs.CreateFile('/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()
|
||||
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
|
||||
|
||||
# Check parser
|
||||
onion2 = Onions()
|
||||
onion2.torrc_parser()
|
||||
|
||||
assert len(onion2.services) == 2
|
||||
assert len(onion2.services) == 6
|
||||
|
||||
assert set(
|
||||
group.name for group in onion2.services
|
||||
) == set(['group1', 'group2'])
|
||||
# ) == 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'])
|
||||
|
@ -279,6 +348,8 @@ def test_configuration(fs, monkeypatch):
|
|||
) == 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'])
|
||||
|
@ -288,6 +359,53 @@ def test_configuration(fs, monkeypatch):
|
|||
(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)])
|
||||
|
||||
|
||||
def test_groups(monkeypatch):
|
||||
env = {
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
{% for service_group in services %}
|
||||
HiddenServiceDir /var/lib/tor/hidden_service/{{service_group.name}}
|
||||
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 %}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue