From 79b260db495e94430d499906f321dc022c68fec1 Mon Sep 17 00:00:00 2001 From: ficus Date: Tue, 25 Sep 2012 15:59:18 +0200 Subject: basic tor control (needs testing) --- torouterui/sysstatus.py | 15 ++++++-- torouterui/templates/home.html | 8 ++-- torouterui/templates/logs.html | 1 + torouterui/templates/tor.html | 29 ++++++++++++-- torouterui/tor.py | 86 ++++++++++++++++++++++++++++++++++++++++-- torouterui/util.py | 11 ++++++ torouterui/views.py | 36 ++++++++++++++++-- 7 files changed, 168 insertions(+), 18 deletions(-) diff --git a/torouterui/sysstatus.py b/torouterui/sysstatus.py index 2d19458..3887724 100644 --- a/torouterui/sysstatus.py +++ b/torouterui/sysstatus.py @@ -41,10 +41,10 @@ def get_resources_status(): d['disk_avail'] = disk_info[1] d['disk_percent'] = int(disk_info[4][:-1]) - ram_info = cli_read_lines('free -m')[1].split() - d['ram_used'] = "%sMB" % ram_info[2] - d['ram_avail'] = "%sMB" % ram_info[1] - d['ram_percent'] = int(float(ram_info[2])/float(ram_info[1]) * 100.0) + ram_info = cli_read_lines('free -m') + d['ram_used'] = "%sMB" % ram_info[2].split()[2] + d['ram_avail'] = "%sMB" % ram_info[1].split()[1] + d['ram_percent'] = int(float(d['ram_used'].strip('MB'))/float(d['ram_avail'].strip('MB')) * 100.0) d['cpu_cores'] = 1 for l in open('/proc/cpuinfo', 'r'): @@ -76,6 +76,13 @@ def get_syslog(): except IOError: return None +def get_torlog(): + try: + with open('/var/log/tor/notices.log') as f: + return ''.join(f.readlines()) + except IOError: + return None + def get_process_list(): """ Example ``ps aux`` output: diff --git a/torouterui/templates/home.html b/torouterui/templates/home.html index 58ed2f2..fa9900c 100644 --- a/torouterui/templates/home.html +++ b/torouterui/templates/home.html @@ -71,11 +71,11 @@ Version {% if status.tor.version %}{{ status.tor.version }} ({{ status.tor.version_current }}){% endif %} - Traffic TX - {% if status.tor.traffic_written_bytes %}{{ status.tor.traffic_written_bytes }} bytes{% endif %} + Total TX Data + {% if status.tor.traffic_written_bytes != None %}{{ status.tor.traffic_written_bytes }} bytes{% endif %} - Traffic RX - {% if status.tor.traffic_read_bytes %}{{ status.tor.traffic_read_bytes }} bytes{% endif %} + Total RX Data + {% if status.tor.traffic_read_bytes != None %}{{ status.tor.traffic_read_bytes }} bytes{% endif %} diff --git a/torouterui/templates/logs.html b/torouterui/templates/logs.html index c5ea52a..118c0e0 100644 --- a/torouterui/templates/logs.html +++ b/torouterui/templates/logs.html @@ -5,5 +5,6 @@ {{ lib.logbox("dmesg", logs.dmesg) }} {{ lib.logbox("auth.log", logs.authlog) }} {{ lib.logbox("syslog", logs.syslog) }} +{{ lib.logbox("tor", logs.tor) }} {% endblock %} diff --git a/torouterui/templates/tor.html b/torouterui/templates/tor.html index a1f30dc..327ac59 100644 --- a/torouterui/templates/tor.html +++ b/torouterui/templates/tor.html @@ -5,9 +5,32 @@
Tor Network Configuration -{{ lib.formcheckbox(form, formerr, 'torenable', 'Enable Tor daemon', 'true') }} -{{ lib.formcheckbox(form, formerr, 'torrelayenable', 'Enable Tor Relay', 'true') }} -{{ lib.formcheckbox(form, formerr, 'torbridgeenable', 'Enable Tor Bridge', 'true') }} +{{ lib.formcheckbox(form, formerr, 'tor_enable', 'Enable Tor daemon', 'true') }} + +{{ lib.forminput(form, formerr, 'tor_sockslistenaddress', 'SOCKS Proxy Listen Address', '127.0.0.1:9050') }} +{{ lib.forminput(form, formerr, 'tor_virtualaddrnetwork', 'Virtual Address Network', '192.168.0.0/16') }} +{{ lib.forminput(form, formerr, 'tor_translistenaddress', 'Trans Listen Address', '127.0.0.1:9040') }} +{{ lib.forminput(form, formerr, 'tor_dnslistenaddress', 'DNS Listen Address', '127.0.0.1:5353') }} + +Exit Node Configuration +{{ lib.formcheckbox(form, formerr, 'tor_exitnodeenable', 'Enable Exit Node', 'true') }} + +Relay Configuration + + + + +{{ lib.forminput(form, formerr, 'tor_relaybandwidthrateKBps', 'Relay Node Bandwidth (KB/sec)', '50') }} +{{ lib.forminput(form, formerr, 'tor_relaybandwidthburstKBps', 'Relay Node Burst Bandwidth (KB/sec)', '75') }}
diff --git a/torouterui/tor.py b/torouterui/tor.py index cf18ce2..a44aea4 100644 --- a/torouterui/tor.py +++ b/torouterui/tor.py @@ -2,12 +2,18 @@ Helper code for interacting with Tor and modifying the Tor system configuration. """ +import os +import time from TorCtl import TorCtl +import util def tor_getinfo(conn, key): return conn.get_info(key)[key] +def tor_getoption(conn, key): + return conn.get_option(key)[0][1] + def get_tor_status(): """Ask for status over torctl pipe""" d = dict() @@ -35,6 +41,7 @@ def get_tor_status(): d['version_current'] = tor_getinfo(conn, 'status/version/current') d['circuit_established'] = bool( tor_getinfo(conn, 'status/circuit-established')) + d['is_alive'] = conn.is_live() if d['circuit_established']: d['state'] = 'ESTABLISHED' else: @@ -46,9 +53,82 @@ def get_tor_status(): def get_tor_settings(): """Ask for settings over torctl pipe; don't use augeas""" d = dict() - return d + conn = TorCtl.connect() + if not conn: + # couldn't connect; will need to display error to user + return None + d['tor_relaybandwidthrateKBps'] = \ + int(int(tor_getoption(conn, 'RelayBandwidthRate'))/1024.) + d['tor_relaybandwidthburstKBps'] = \ + int(int(tor_getoption(conn, 'RelayBandwidthBurst'))/1024.) + + d['tor_sockslistenaddress'] = tor_getoption(conn, 'SocksListenAddress') + d['tor_virtualaddrnetwork'] = tor_getoption(conn, 'VirtualAddrNetwork') + d['tor_translistenaddress'] = tor_getoption(conn, 'TransListenAddress') + d['tor_dnslistenaddress'] = tor_getoption(conn, 'DNSListenAddress') + d['tor_enable'] = (not bool(int(tor_getoption(conn, 'DisableNetwork')))) and 'true' + if tor_getoption(conn, 'ExitPolicy') == 'reject *:*': + d['tor_exitnodeenable'] = False + else: + d['tor_exitnodeenable'] = 'true' + + orport = tor_getoption(conn, 'ORPort') + bridgerelay = tor_getoption(conn, 'BridgeRelay') + if orport == 'auto' and bridgerelay == '0': + d['tor_relaytype'] = 'relay' + elif orport == 'auto' and bridgerelay == '1': + d['tor_relaytype'] = 'bridge' + elif orport == '0': + d['tor_relaytype'] = 'none' + else: + print "WARNING: unknown tor_relaytype state" + d['tor_relaytype'] = 'unknown' + + conn.close() + return d -def save_tor_settings(): +def save_tor_settings(form): """Commit settings through torctl pipe, then send SAVECONF""" - pass + conn = TorCtl.connect() + if not conn: + print "Warning: couldn't connect to tor daemon; need to boot up tor daemon in disabled state" + os.system('echo "DisableNetwork 1" >> /etc/tor/torrc') + # tor daemon should always be running, even if disabled + util.enable_service('tor') + time.sleep(3) + conn = TorCtl.connect() + if not conn: + raise Exception("Could not start tor daemon!") + + conn.set_option('RelayBandwidthRate', + "%dKB" % int(form['tor_relaybandwidthrateKBps'])) + conn.set_option('RelayBandwidthBurst', + "%dKB" % int(form['tor_relaybandwidthburstKBps'])) + + conn.set_option('DNSListenAddress', form['tor_dnslistenaddress']) + conn.set_option('SocksListenAddress', form['tor_sockslistenaddress']) + conn.set_option('VirtualAddrNetwork', form['tor_virtualaddrnetwork']) + conn.set_options([('TransListenAddress', form['tor_translistenaddress']), + ('TransPort', form['tor_translistenport'])]) + + if form.get('tor_enable') == 'true': + conn.set_option('DisableNetwork', '0') + else: + conn.set_option('DisableNetwork', '1') + + if form.get('tor_exitnodeenable') == 'true': + conn.set_option('ExitPolicy', '') + else: + conn.set_option('ExitPolicy', 'reject *:*') + + if form['tor_relaytype'] == 'relay': + conn.set_options([('ORPort', 'auto'), ('BridgeRelay', '0')]) + elif form['tor_relaytype'] == 'bridge': + conn.set_options([('ORPort', 'auto'), ('BridgeRelay', '1')]) + else: # type = 'none' + conn.set_options([('ORPort', '0'), ('BridgeRelay', '0')]) + + conn.save_conf() + conn.close() + diff --git a/torouterui/util.py b/torouterui/util.py index dc3b6c0..c459fa0 100644 --- a/torouterui/util.py +++ b/torouterui/util.py @@ -17,6 +17,17 @@ def fs_read(path): with open(path, 'r') as f: return ''.join(f.readlines()) +def enable_service(name): + os.system('update-rc.d %s defaults &' % name) + # safe to "restart" most services if they are already running + os.system('/etc/init.d/%s start &' % name) + +def disable_service(name): + """Currently, this is never actually called""" + os.system('update-rc.d %s remove &' % name) + # safe to "restart" most services if they are already running + os.system('/etc/init.d/%s stop &' % name) + def prefix_to_ipv4_mask(prefixlen): assert(prefixlen >= 0) assert(prefixlen <= 32) diff --git a/torouterui/views.py b/torouterui/views.py index af17be8..12efb1d 100644 --- a/torouterui/views.py +++ b/torouterui/views.py @@ -59,7 +59,7 @@ def wanpage(): form = netif.get_wan_settings() return render_template('wan.html', form=form, status=status, formerr=None) - # Got this far, need to validated form + # Got this far, need to validate form formerr = dict() if request.form['ipv4method'] == 'disabled': pass # no further validation @@ -106,7 +106,7 @@ def lanpage(): form = netif.get_lan_settings() return render_template('lan.html', form=form, status=status, formerr=None) - # Got this far, need to validated form + # Got this far, need to validate form formerr = dict() if request.form['ipv4method'] == 'disabled': pass # no further validation @@ -151,7 +151,7 @@ def wifipage(): form = netif.get_wifi_settings() return render_template('wifi.html', form=form, status=status, formerr=None) - # Got this far, need to validated form + # Got this far, need to validate form formerr = dict() if request.form['ipv4method'] == 'disabled': pass # no further validation @@ -185,8 +185,35 @@ def wifipage(): @app.route('/tor/', methods=['GET', 'POST']) def torpage(): - # TODO: unimplemented msg = list() + status = dict() + status['tor'] = tor.get_tor_status() + if request.method == 'GET': + if status['tor']['state'] == 'DISABLED': + msg.append(("warning", + "Could not connect to Tor daemon for control. Will try to restart daemon if settings are saved from this page."),) + form = None + else: + form = tor.get_tor_settings() + print form + return render_template('tor.html', form=form, status=status, + formerr=None, messages=msg) + # Got this far, need to validate form + formerr = dict() + # TODO: form validation + if len(formerr.keys()) > 0: + msg.append(("error", + "Please correct the validation issues below"),) + else: + # Ok, we have a valid form, now to commit it + try: + tor.save_tor_settings(request.form) + msg.append(("success", + "Configuration saved! Check logs for any errors"),) + except Exception, err: + msg.append(("error", + "Was unable to commit changes...\"%s\"" + % err)) return render_template('tor.html', settings=None, status=None, form=request.form, formerr=None, messages=msg) @@ -196,6 +223,7 @@ def logspage(): logs['dmesg'] = sysstatus.get_dmesg() logs['syslog'] = sysstatus.get_syslog() logs['authlog'] = sysstatus.get_authlog() + logs['tor'] = sysstatus.get_torlog() return render_template('logs.html', logs=logs) @app.route('/processes/', methods=['GET']) -- cgit v1.2.3