tor-container/assets/onions/onions/Onions.py
2016-09-27 18:54:59 +02:00

188 lines
5.7 KiB
Python

#!/usr/bin/env python3
import os
from json import dumps
from re import match
from pyentrypoint import DockerLinks
import argparse
from jinja2 import Environment
from jinja2 import FileSystemLoader
import socket
from Crypto.PublicKey import RSA
from hashlib import sha1
from base64 import b32encode
class Setup(object):
hidden_service_dir = "/var/lib/tor/hidden_service/"
torrc = '/etc/tor/torrc'
torrc_template = '/var/local/tor/torrc.tpl'
def onion_url_gen(self, key):
"Get onion url from private key"
# Convert private RSA to public DER
priv = RSA.importKey(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())
def _add_host(self, host):
if host not in self.setup:
self.setup[host] = {}
def _get_ports(self, host, ports):
self._add_host(host)
if 'ports' not in self.setup[host]:
self.setup[host]['ports'] = []
port = [int(p) for p in ports.split(':')]
assert len(port) == 2
if port not in self.setup[host]['ports']:
self.setup[host]['ports'].append(port)
def _get_ip(self):
for host in self.setup:
self.setup[host]['ip'] = str(socket.gethostbyname(host))
def _get_key(self, host, key):
self._add_host(host)
assert len(key) > 800
self.setup[host]['key'] = key
def _get_setup_from_env(self):
match_map = (
(r'([A-Z0-9]*)_PORTS', self._get_ports),
(r'([A-Z0-9]*)_KEY', self._get_key),
)
for key, val in os.environ.items():
for reg, call in match_map:
m = match(reg, key)
if m:
call(m.groups()[0].lower(), val)
def _get_setup_from_links(self):
containers = DockerLinks().to_containers()
if not containers:
return
for container in containers:
host = container.names[0]
self._add_host(host)
for link in container.links:
if link.protocol != 'tcp':
continue
port_map = os.environ.get('PORT_MAP')
self._get_ports(host, '{exposed}:{internal}'.format(
exposed=port_map or link.port,
internal=link.port,
))
def _set_keys(self):
for link, conf in self.setup.items():
if 'key' in conf:
serv_dir = os.path.join(self.hidden_service_dir, link)
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(conf['key'])
os.fchmod(f.fileno(), 0o600)
with open(os.path.join(serv_dir, 'hostname'), 'w') as f:
f.write(self.onion_url_gen(conf['key']))
def _set_conf(self):
env = Environment(loader=FileSystemLoader('/'))
temp = env.get_template(self.torrc_template)
with open(self.torrc, mode='w') as f:
f.write(temp.render(setup=self.setup,
env=os.environ))
def setup_hosts(self):
self.setup = {}
try:
self._get_setup_from_env()
self._get_setup_from_links()
self._get_ip()
self._set_keys()
self._set_conf()
except:
raise Exception('Something wrongs with setup')
class Onions(Setup):
"""Onions"""
def __init__(self):
if 'HIDDEN_SERVICE_DIR' in os.environ:
self.hidden_service_dir = os.environ['HIDDEN_SERVICE_DIR']
def _get_port_from_service(self, service, filename):
with open(filename, 'r') as hostfile:
onion = str(hostfile.read()).strip()
with open(self.torrc, 'r') as torfile:
self.onions[service] = []
for line in torfile.readlines():
find = '# PORT {name}'.format(name=service)
if line.startswith(find):
self.onions[service].append(
'{onion}:{port}'.format(
onion=onion,
port=line[len(find):].strip()
)
)
def get_onions(self):
self.onions = {}
for root, dirs, _ in os.walk(self.hidden_service_dir,
topdown=False):
for service in dirs:
filename = "{root}{service}/hostname".format(
service=service,
root=root
)
self._get_port_from_service(service, filename)
def __str__(self):
if not self.onions:
return 'No onion site'
return '\n'.join(['%s: %s' % (service, ', '.join(onion))
for (service, onion) in self.onions.items()])
def to_json(self):
return dumps(self.onions)
def main():
parser = argparse.ArgumentParser(description='Display onion sites',
prog='onions')
parser.add_argument('--json', dest='json', action='store_true',
help='serialize to json')
parser.add_argument('--setup-hosts', dest='setup', action='store_true',
help='Setup hosts')
args = parser.parse_args()
onions = Onions()
if args.setup:
onions.setup_hosts()
return
onions.get_onions()
if args.json:
print(onions.to_json())
else:
print(onions)
if __name__ == '__main__':
main()