From 31bbef583c159e0b34b047dfc0b8bef5bbf3f7c5 Mon Sep 17 00:00:00 2001 From: "black.box (Unzoner) team@belodetek.io" Date: Fri, 1 Dec 2023 15:09:41 -0800 Subject: [PATCH 1/2] fix: reset loop if any tunnel interface is not passing traffic --- unzoner/.balena/balena.yml | 6 +- unzoner/requirements.txt | 1 - unzoner/src/main.py | 416 ++----- unzoner/src/tests/requirements.txt | 1 - unzoner/src/utils.py | 41 +- unzoner/src/vpn.py | 1644 +++++++++++++--------------- 6 files changed, 908 insertions(+), 1201 deletions(-) diff --git a/unzoner/.balena/balena.yml b/unzoner/.balena/balena.yml index 8e8bba7..6a16100 100644 --- a/unzoner/.balena/balena.yml +++ b/unzoner/.balena/balena.yml @@ -7,14 +7,14 @@ build-variables: - BUILD_BIRD=0 # https://github.com/openssl/openssl - BUILD_OPENSSL_VERSION=3.0.5 - - BUILD_OPENSSL=1 + - BUILD_OPENSSL=0 # https://gcc.gnu.org/onlinedocs/gcc/AArch64-Options.html - AARCH64_OPTIMISE_FLAGS=-O3 -mtune=cortex-a72 -march=armv8-a # https://gcc.gnu.org/onlinedocs/gcc/ARM-Options.html - ARM_OPTIMISE_FLAGS=-O3 -mtune=cortex-a53 -mcpu=cortex-a53+crypto -mfpu=crypto-neon-fp-armv8 # https://github.com/OpenVPN/openvpn - BUILD_OPENVPN_VERSION=2.5.7 - - BUILD_OPENVPN=1 + - BUILD_OPENVPN=0 # FIXME: https://sources.debian.org/patches/sniproxy/0.6.0-2/ - BUILD_SNIPROXY_VERSION=0.6.0 - BUILD_SNIPROXY=0 @@ -22,7 +22,7 @@ build-variables: - BUILD_WANPROXY_VERSION=0.8.0 - BUILD_WANPROXY=0 # https://nuitka.net/ - - COMPILE_CODE=1 + - COMPILE_CODE=0 # (e.g.) dig +short us.{{ DNS_SUB_DOMAIN }}.{{ DNS_DOMAIN }} - DNS_SUB_DOMAIN=blackbox diff --git a/unzoner/requirements.txt b/unzoner/requirements.txt index 2497877..710d699 100644 --- a/unzoner/requirements.txt +++ b/unzoner/requirements.txt @@ -2,6 +2,5 @@ Pyjwt pyasn ipaddr requests[security] -pingparsing dnspython passlib diff --git a/unzoner/src/main.py b/unzoner/src/main.py index cdbce1f..348cd08 100755 --- a/unzoner/src/main.py +++ b/unzoner/src/main.py @@ -23,8 +23,7 @@ def main(): kill_thread = Thread() connected = False connecting = False - c_gwip = None - c_loss = 0 + magic_lines = ['Initialization Sequence Completed'] c_proc = None c_proto = None c_pid = None @@ -50,8 +49,6 @@ def main(): w_clnts = 0 started = [False, False] # [udp, tcp] starting = [False, False] - s_local = [None, None] - s_loss = [0, 0] s_conns = [0, 0] s_proc = [None, None] s_pid = [None, None] @@ -61,6 +58,7 @@ def main(): s_lineout = [None, None] s_lineerr = [None, None] s_status_line = None + this = stack()[0][3] if SUPPRESS_TS: p1 = re.compile('^(.*)$') @@ -70,42 +68,28 @@ def main(): ) group = p1.groups - 1 - if bool(re.search('^2\.3\.', OPENVPN_VERSION)): - p2 = re.compile( - '^.* dev ([\w\d]+) local ([\d]+\.[\d]+\.[\d]+\.[\d]+) peer ([\d]+\.[\d]+\.[\d]+\.[\d]+)$' - ) - - if bool(re.search('^2\.[4-5]\.', OPENVPN_VERSION)): - p2 = re.compile( - '^.*ifconfig ([\w\d]+) ([\d]+\.[\d]+\.[\d]+\.[\d]+) pointopoint ([\d]+\.[\d]+\.[\d]+\.[\d]+) mtu [\d]+$' - ) - - if DEVICE_TYPE == 5: - p2 = re.compile( - '^.*get_ping_host: route_network_[0-9]+=([\d]+\.[\d]+\.[\d]+\.[\d]+)$' - ) - p3 = re.compile('^.*remote=(.*) country=(.*)$') + p2 = re.compile('^.*remote=(.*) country=(.*)$') # hdparm tests - p4 = re.compile( + p3 = re.compile( '^\s+(.*):\s+(\d+\s+.*)\s+in\s+([\d\.]+\s+.*)\s+=\s+(.*)\n$' ) # dd write - p5 = re.compile( + p4 = re.compile( '^(\d+\s+.*)\s+\((.*),.*\)\s+.*,\s+(.*),\s+(.*)$' ) mgmt_ipaddr = None if TUN_MGMT: mgmt_ipaddr = get_ip_address(MGMT_IFACE) + if DEBUG: print('os.environ: {}'.format(os.environ)) + while True: if DEVICE_TYPE == 0: - log('{}: device={}'.format(stack()[0][3], GUID)) + log('{}: device={}'.format(this, GUID)) sys.exit(0) - if DEBUG: print('os.environ: {}'.format(os.environ)) - for i in range(1, LOOP_CYCLE + 1): ########################### # server mode(s) or mixed # @@ -119,9 +103,7 @@ def main(): s_lineerr = [None, None] if s_stderrq[idx]: try: - s_lineerr[idx] = s_stderrq[idx].get( - timeout=LOOP_TIMER - ).decode() + s_lineerr[idx] = s_stderrq[idx].get(timeout=LOOP_TIMER).decode() log('{}: {}'.format(proto, s_lineerr[idx])) except Empty: pass @@ -132,9 +114,7 @@ def main(): if s_stdoutq[idx]: try: - s_lineout[idx] = s_stdoutq[idx].get( - timeout=LOOP_TIMER - ).decode() + s_lineout[idx] = s_stdoutq[idx].get(timeout=LOOP_TIMER).decode() log('{}: {}'.format(proto, s_lineout[idx])) except Empty: pass @@ -144,84 +124,43 @@ def main(): except (IndexError, TypeError, AttributeError): pass - if s_msg[idx] in ['Initialization Sequence Completed']: + if s_msg[idx] in magic_lines: started[idx] = True starting[idx] = False try: log_server_stats(status=started) except: + print('exception-handler in {}: {}'.format(this, repr(e))) if DEBUG: print_exc() - try: - m2 = p2.search(s_msg[idx]).groups() - dev = m2[0] - s_local[idx] = m2[1] - s_peer = m2[2] - log( - '{}: iface={} local={} peer={} proto={}'.format( - stack()[0][3], - dev, - s_local[idx], - s_peer, - proto - ) - ) - except (IndexError, TypeError, AttributeError): - pass - - if not started[idx] and starting[idx]: - if i % LOOP_CYCLE == 0 and not started[idx]: - log( - '{}: cycle={} started={} starting={} proto={} s_pid={}'.format( - stack()[0][3], - i, - started[idx], - starting[idx], - proto, - s_pid[idx] - ) - ) - started[idx] = False - starting[idx] = False - s_proc[idx].terminate() - s_pid[idx] = None if not started[idx] and not starting[idx]: if i == 1: # once per loop (start) starting[idx] = True - log( - '{}: cycle={} starting={} proto={}'.format( - stack()[0][3], - i, - starting[idx], - proto - ) - ) + log('{}: cycle={} starting={} proto={}'.format( + this, + i, + starting[idx], + proto + )) try: - (s_stdoutq[idx], s_stderrq[idx], s_proc[idx]) = start_server( - proto=proto - ) + (s_stdoutq[idx], s_stderrq[idx], s_proc[idx]) = start_server(proto=proto) s_pid[idx] = s_proc[idx].pid except Exception as e: starting[idx] = False - print(repr(e)) + print('exception-handler in {}: {}'.format(this, repr(e))) if DEBUG: print_exc() if started[idx] and not starting[idx]: if not kill_thread.is_alive(): - kill_thread = Thread( - target=disconnect_clients, - args=() - ) + kill_thread = Thread(target=disconnect_clients, args=()) kill_thread.daemon = True kill_thread.start() - log( - 'disconnect_clients: name={} alive={} daemon={}'.format( - kill_thread.name, - kill_thread.is_alive(), - kill_thread.daemon - ) - ) + log('disconnect_clients: name={} alive={} daemon={}'.format( + kill_thread.name, + kill_thread.is_alive(), + kill_thread.daemon + )) if not auth_thread.is_alive(): if BITCOIN_PAYMENT_CHECK: @@ -231,49 +170,19 @@ def main(): ) auth_thread.daemon = True auth_thread.start() - log( - 'reauthenticate_clients: name={} alive={} daemon={}'.format( - auth_thread.name, - auth_thread.is_alive(), - auth_thread.daemon - ) - ) + log('(re)authenticate_clients: name={} alive={} daemon={}'.format( + auth_thread.name, + auth_thread.is_alive(), + auth_thread.daemon + )) if i == 1 or i % LOOP_CYCLE == 0: # twice per loop (start and end) - if DEBUG: log( - 'get_status: cycle={} proto={} status={}'.format( - i, - proto, - get_status(proto=proto) - ) - ) - s_conns[idx] = int( - get_server_conns_by( - proto=proto - ) - ) - - if i % POLL_FREQ == 0 and s_local[idx]: # every x cycles per loop - s_loss[idx] = 100 - try: - s_loss[idx] = ping_host(host=s_local[idx]) - except AssertionError as e: - print(repr(e)) - if DEBUG: print_exc() - log( - '{}: cycle={} proto={} s_local={} s_loss={}'.format( - i, - stack()[0][3], - proto, - s_local[idx], - s_loss[idx] - ) - ) - started[idx] = False - starting[idx] = False - s_proc[idx].terminate() - s_loss[idx] = 0 - break + if DEBUG: log('event-loop-status: cycle={} proto={} status={}'.format( + i, + proto, + get_status(proto=proto) + )) + s_conns[idx] = int(get_server_conns_by(proto=proto)) # flush the stderr queue if not debugging if not DEBUG and s_stderrq[idx]: @@ -281,58 +190,33 @@ def main(): s_stderrq[idx].queue.clear() if i % LOOP_CYCLE == 0: - if DEVICE_TYPE == 3: # test if double-vpn is up + if DEVICE_TYPE == 3 and connected: # test if double-vpn is up try: - shell_check_output_cmd( - 'ip link | grep %s' % TUN_IFACE - ) - gwip = shell_check_output_cmd( - "ip route | grep %s | grep -E '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+ via [0-9]+\.[0-9]+\.[0-9]+\.[0-9]+ dev %s' | awk '{print $1}'" % ( - TUN_IFACE, - TUN_IFACE - ) - ) - log( - 'iface={} gwip={}'.format( - TUN_IFACE, - gwip - ) - ) - ping_host( - host=gwip.strip('\n'), - timeout=1, - count=3 - ) - try: - # server up only if double-vpn is up - log_server_stats(status=started) - except: - if DEBUG: print_exc() + shell_check_output_cmd('ip link | grep {}'.format(TUN_IFACE)) + geo_result = get_geo_location() + assert geo_result, '{}: double-vpn not ready'.format(this) + print('double-vpn: {}'.format(geo_result)) + log_server_stats(status=started) # server up only if double-vpn is up except: + print('exception-handler in {}: {}'.format(this, repr(e))) if DEBUG: print_exc() - log( - '{}: double-vpn not ready'.format( - stack()[0][3] - ) - ) + try: - # force server down if double-vpn is down - log_server_stats() + log_server_stats() # force server down if double-vpn is down except: + print('exception-handler in {}: {}'.format(this, repr(e))) if DEBUG: print_exc() else: try: log_server_stats(status=started) except: + print('exception-handler in {}: {}'.format(this, repr(e))) if DEBUG: print_exc() - s_status_line = '{}: cycle={} started={} starting={} ip={} loss={} pid={} conns={} mgmt_tun={} af={} hostapd={} upnp={}'.format( - stack()[0][3], + s_status_line = 'server-status: cycle={} started={} starting={} s_pid={} s_conns={} mgmt_ipaddr={} AF={} hostAPd={} UPNP={}'.format( i, started, starting, - s_local, - s_loss, s_pid, s_conns, mgmt_ipaddr, @@ -348,13 +232,11 @@ def main(): if DEVICE_TYPE in [2, 3, 5]: # iperf timeout if c_stp and now - c_stimer > (LOOP_TIMER * LOOP_CYCLE * 5): - log( - '{}: pid={} elapsed={}'.format( - stack()[0][3], - c_stp.pid, - now - c_stimer - ) - ) + log('{}: pid={} elapsed={}'.format( + this, + c_stp.pid, + now - c_stimer + )) c_stp.terminate() c_stp = None c_stq = None @@ -371,19 +253,17 @@ def main(): res = update_speedtest(data=result) log('update_speedtest: {}'.format(res)) except Exception as e: - print(repr(e)) + print('exception-handler in {}: {}'.format(this, repr(e))) if DEBUG: print_exc() # hdparm or dd timeout if c_iotp and now - c_iotimer > (LOOP_TIMER * LOOP_CYCLE * 3): - log( - '{}: pid={} elapsed={} test={}'.format( - stack()[0][3], - c_iotp.pid, - now - c_iotimer, - c_iott - ) - ) + log('{}: pid={} elapsed={} test={}'.format( + this, + c_iotp.pid, + now - c_iotimer, + c_iott + )) try: result = { 'status': 1, @@ -392,14 +272,12 @@ def main(): } log('run_iotest: result={}'.format(result)) res = update_iotest(data=result) - log( - 'update_iotest({}): {}'.format( - res[0], - res[1] - ) - ) + log('update_iotest({}): {}'.format( + res[0], + res[1] + )) except Exception as e: - print(repr(e)) + print('exception-handler in {}: {}'.format(this, repr(e))) if DEBUG: print_exc() c_iotp.terminate() c_iotp = None @@ -461,7 +339,7 @@ def main(): res = update_speedtest(data=result) log('update_speedtest: {}'.format(res)) except Exception as e: - print(repr(e)) + print('exception-handler in {}: {}'.format(this, repr(e))) if DEBUG: print_exc() c_str = list() @@ -475,7 +353,7 @@ def main(): try: c_iotl = c_iotq.get(timeout=LOOP_TIMER).decode() log(c_iotl) - if p4.search(c_iotl) or p5.search(c_iotl): + if p3.search(c_iotl) or p4.search(c_iotl): result = { 'status': 0, 'test': c_iott, @@ -484,14 +362,9 @@ def main(): log('run_iotest: result={}'.format(result)) try: res = update_iotest(data=result) - log( - 'update_iotest({}): {}'.format( - res[0], - res[1] - ) - ) + log('update_iotest({}): {}'.format(res[0], res[1])) except Exception as e: - print(repr(e)) + print('exception-handler in {}: {}'.format(this, repr(e))) if DEBUG: print_exc() c_iotimer = 0 c_iott = None @@ -507,92 +380,37 @@ def main(): except (IndexError, TypeError, AttributeError): pass - if c_msg in [ - 'SIGTERM[soft,tls-error] received, process exiting', - 'SIGUSR1[soft,init_instance] received, process restarting', - 'SIGUSR1[soft,connection-reset] received, process restarting', - 'SIGTERM[hard,] received, process exiting', - 'Exiting due to fatal error' - ]: - log('terminate: c_pid={}'.format(c_pid)) - connected = False - connecting = False - c_proc.terminate() - c_pid = None - - if c_msg in ['Initialization Sequence Completed']: + if c_msg in magic_lines: connected = True connecting = False try: - log_client_stats( - status=connected, - country=c_country - ) + log_client_stats(status=connected, country=c_country) except: + print('exception-handler in {}: {}'.format(this, repr(e))) if DEBUG: print_exc() - try: - m2 = p2.search(c_msg).groups() - dev = m2[0] - c_local = m2[1] - c_peer = m2[2] - c_gwip = c_peer.split('.') - c_gwip[3] = '1' - c_gwip = '.'.join(c_gwip) - log( - '{}: iface={} local={} peer={} gwip={} family={}'.format( - stack()[0][3], - dev, - c_local, - c_peer, - c_gwip, - AF - ) - ) - except (IndexError, TypeError, AttributeError): - pass if DEVICE_TYPE == 5: try: - m3 = p3.search(c_msg).groups() + m3 = p2.search(c_msg).groups() c_remote = m3[0] c_country = m3[1] - log( - '{}: remote={} country={}'.format( - stack()[0][3], - c_remote, - c_country - ) - ) + log('{}: remote={} country={}'.format( + this, + c_remote, + c_country + )) except (IndexError, TypeError, AttributeError): pass - if not connected and connecting: - if i % LOOP_CYCLE == 0 and not connected: - log( - '{}: cycle={} connected={} connecting={} c_pid={}'.format( - stack()[0][3], - i, - connected, - connecting, - c_pid - ) - ) - connected = False - connecting = False - c_proc.terminate() - c_pid = None - if not connected and not connecting: if i == 1: connecting = True - log( - '{}: cycle={} connecting={} family={}'.format( - stack()[0][3], - i, - connecting, - AF - ) - ) + log('{}: cycle={} connecting={} family={}'.format( + this, + i, + connecting, + AF + )) try: (c_stdoutq, c_stderrq, c_proc, c_proto) = connect_node( family=AF @@ -600,7 +418,7 @@ def main(): c_pid = c_proc.pid except AssertionError as e: connecting = False - print(repr(e)) + print('exception-handler in {}: {}'.format(this, repr(e))) if DEBUG: print_exc() if connected and not connecting: @@ -612,16 +430,14 @@ def main(): c_str = list() (c_stq, c_stp) = run_speedtest() except Exception as e: - print(repr(e)) + print('exception-handler in {}: {}'.format(this, repr(e))) if DEBUG: print_exc() if c_stimer: - log( - 'run_speedtest: pid={} elapsed={} results={}'.format( - c_stp.pid, - now - c_stimer, - len(c_str) - ) - ) + log('run_speedtest: pid={} elapsed={} results={}'.format( + c_stp.pid, + now - c_stimer, + len(c_str) + )) # run hdparm or dd try: @@ -635,57 +451,43 @@ def main(): c_iotimer = now (c_iotq, c_iotp) = run_iotest(test=c_iott) except Exception as e: - print(repr(e)) + print('exception-handler in {}: {}'.format(this, repr(e))) if DEBUG: print_exc() if c_iotimer: - log( - 'run_iotest: pid={} elapsed={} test={} result={}'.format( - c_iotp.pid, - now - c_iotimer, - c_iott, - c_iotl - ) - ) - - if i % POLL_FREQ == 0 and c_gwip: - c_loss = 100 + log('run_iotest: pid={} elapsed={} test={} result={}'.format( + c_iotp.pid, + now - c_iotimer, + c_iott, + c_iotl + )) + + if i % POLL_FREQ == 0: # every x cycles per loop try: - c_loss = ping_host(host=c_gwip) + shell_check_output_cmd('ip link | grep {}'.format(TUN_IFACE)) + geo_result = get_geo_location() + assert geo_result, '{}: client tunnel down'.format(this) + print('client-vpn: {}'.format(geo_result)) except AssertionError as e: - print(repr(e)) + print('exception-handler in {}: {}'.format(this, repr(e))) if DEBUG: print_exc() - log( - '{}: c_gwip={} c_loss={}'.format( - stack()[0][3], - c_gwip, - c_loss - ) - ) connected = False connecting = False c_proc.terminate() - c_loss = 0 c_pid = None - break if i % LOOP_CYCLE == 0: try: - log_client_stats( - status=connected, - country=c_country - ) + log_client_stats(status=connected, country=c_country) except: + print('exception-handler in {}: {}'.format(this, repr(e))) if DEBUG: print_exc() w_clnts = get_stations() - c_status_line = '{}: cycle={} connected={} connecting={} proto={} ip={} loss={} pid={} clients={} mgmt_tun={} af={} hostapd={} upnp={}'.format( - stack()[0][3], + c_status_line = 'client-status: cycle={} connected={} connecting={} c_proto={} c_pid={} w_clnts={} mgmt_ipaddr={} AF={} hostAPd={} UPNP={}'.format( i, connected, connecting, c_proto, - c_gwip, - c_loss, c_pid, w_clnts, mgmt_ipaddr, diff --git a/unzoner/src/tests/requirements.txt b/unzoner/src/tests/requirements.txt index ec67947..ac7c18c 100644 --- a/unzoner/src/tests/requirements.txt +++ b/unzoner/src/tests/requirements.txt @@ -2,7 +2,6 @@ Pyjwt pyasn ipaddr requests[security] -pingparsing dnspython passlib diff --git a/unzoner/src/utils.py b/unzoner/src/utils.py index fb63124..38c9fd1 100755 --- a/unzoner/src/utils.py +++ b/unzoner/src/utils.py @@ -9,7 +9,6 @@ import json import jwt import dns.resolver -import pingparsing from time import time, sleep from inspect import stack @@ -22,26 +21,6 @@ from config import * -def ping_host(host='localhost', timeout=PING_TIMEOUT, count=PING_COUNT): - ping_parser = pingparsing.PingParsing() - transmitter = pingparsing.PingTransmitter() - transmitter.destination = host - transmitter.count = count - result = transmitter.ping() - ping_stats = ping_parser.parse(result).as_dict() - if DEBUG: print( - '{}: ping_stats={}'.format( - stack()[0][3], - ping_stats - ) - ) - assert ping_stats['packet_loss_rate'] < THRESHOLD, '{}: timeout={}'.format( - stack()[0][3], - host - ) - return ping_stats['packet_loss_rate'] - - def shell_check_output_cmd(cmd): if DEBUG: print('{}: cmd={}'.format(stack()[0][3], cmd)) return check_output( @@ -153,7 +132,7 @@ def get_hostname(): hostname = 'localhost' try: hostname = socket.gethostname() - except Exception: + except: pass return hostname @@ -172,9 +151,7 @@ def get_geo_location(family=AF): # get the VPN server location, not the location of the device if DEVICE_TYPE in [3, 5]: try: - shell_check_output_cmd( - 'ip link | grep %s' % TUN_IFACE - ) + shell_check_output_cmd('ip link | grep {}'.format(TUN_IFACE)) cmd.append('--interface') cmd.append(TUN_IFACE) except: @@ -186,14 +163,12 @@ def get_geo_location(family=AF): assert result[0] == 0 data = json.loads(result[1]) except Exception as e: - print( - '{}: family={} data={} e={}'.format( - stack()[0][3], - family, - data, - repr(e) - ) - ) + print('{}: family={} data={} e={}'.format( + stack()[0][3], + family, + data, + repr(e) + )) return data diff --git a/unzoner/src/vpn.py b/unzoner/src/vpn.py index f772f5e..8ce9662 100755 --- a/unzoner/src/vpn.py +++ b/unzoner/src/vpn.py @@ -11,7 +11,7 @@ from time import sleep from inspect import stack from traceback import print_exc -from threading import Thread +from threading import Thread from subprocess import Popen, PIPE from queue import Queue @@ -38,953 +38,885 @@ @retry(Exception, cdata='method={}'.format(stack()[0][3])) def run_openvpn_mgmt_cmd(host=VPN_HOST, port=VPN_UDP_MGMT_PORT, cmd='status 2'): - s = None - data = str() - header = None - - try: - for res in socket.getaddrinfo(host, port, socket.AF_UNSPEC, socket.SOCK_STREAM): - af, socktype, proto, canonname, sa = res - if DEBUG: log( - 'getaddrinfo: res={} af={} socktype={} proto={} canonname={} sa={}'.format( - res, - af, - socktype, - proto, - canonname, - sa - ) - ) - try: - s = socket.socket(af, socktype, proto) - except socket.error as msg: - log( - 'socket: msg={} af={} socktype={} proto={} canonname={} sa={}'.format( - msg, - af, - socktype, - proto, - canonname, - sa - ) - ) - s = None - continue - try: - s.connect(sa) - except socket.error as msg: - s.close() - s = None - continue - break - - header = recv_with_timeout(s) - if DEBUG: print(header) - s.sendall(b'%s\n' % cmd) - data = recv_with_timeout(s) - if DEBUG: print(data) - s.sendall(b'quit\n') - s.close() - except: - pass - return data.split('\r\n') + s = None + data = str() + header = None + + try: + for res in socket.getaddrinfo(host, port, socket.AF_UNSPEC, socket.SOCK_STREAM): + af, socktype, proto, canonname, sa = res + if DEBUG: log('getaddrinfo: res={} af={} socktype={} proto={} canonname={} sa={}'.format( + res, + af, + socktype, + proto, + canonname, + sa + )) + try: + s = socket.socket(af, socktype, proto) + except socket.error as msg: + log('socket: msg={} af={} socktype={} proto={} canonname={} sa={}'.format( + msg, + af, + socktype, + proto, + canonname, + sa + )) + s = None + continue + try: + s.connect(sa) + except socket.error as msg: + s.close() + s = None + continue + break + + header = recv_with_timeout(s) + if DEBUG: print('header: {}', format(header)) + s.sendall(b'{}\n'.format(cmd)) + data = recv_with_timeout(s) + if DEBUG: print('data: {}', format(data)) + s.sendall(b'quit\n') + s.close() + except: + pass + return data.split('\r\n') @retry(Exception, cdata='method={}'.format(stack()[0][3])) def get_openvpn_version(default='2.3.10'): - version = default - try: - version = run_shell_cmd(['/usr/bin/env', 'openvpn', '--version'])[1].split()[1] - except: - pass - return version + version = default + try: + version = run_shell_cmd(['/usr/bin/env', 'openvpn', '--version'])[1].split()[1] + except: + pass + return version @retry(Exception, cdata='method={}'.format(stack()[0][3])) def get_openvpn_binary(default='/usr/sbin/openvpn'): - binary = default - try: - binary = run_shell_cmd(['/usr/bin/which', 'openvpn'])[1].split()[0] - except: - pass - return binary + binary = default + try: + binary = run_shell_cmd(['/usr/bin/which', 'openvpn'])[1].split()[0] + except: + pass + return binary @retry(Exception, cdata='method={}'.format(stack()[0][3])) def connect_node(family=AF): - global PROTOS - global c_proto - global c_wpid - global s_wpid - global s_wport - global ipaddr - global country - global host - global qtype - - try: - log('os.kill: {}'.format(c_wpid)) - os.kill(int(c_wpid), signal.SIGKILL) - except: - pass - - c_wpid = None - - try: - log( - 'kill_remote_pid: {}'.format( - kill_remote_pid( - ipaddr=ipaddr, - family=family, - pid=s_wpid - ) - ) - ) - except: - pass - - s_wpid = None - s_wport = None - ipaddr = None - - if len(PROTOS) <= 0: PROTOS = list(TUN_PROTO) # start again - c_proto = PROTOS.pop() - tun_proto = c_proto - - env = os.environ.copy() - if DEBUG: print(env) - - qtype = 'A' - if family == 6: - if get_geo_location(family=6): - qtype = 'AAAA' - else: - family = 4 - - cmd = [OPENVPN_BINARY, '--config', CLIENT_CONFIG, '--dev', TUN_IFACE] - port = OPENVPN_PORT - - ############ - # VPN mode # - ############ - if DEVICE_TYPE == 5 or ( - DEVICE_TYPE == 3 - and VPN_PROVIDER - and VPN_LOCATION_GROUP - and VPN_LOCATION - and VPN_USERNAME - and VPN_PASSWD - ): - log( - '{}: mode={} provider={} group={} location={} username={} password={}'.format( - stack()[0][3], - DEVICE_TYPE, - VPN_PROVIDER, - VPN_LOCATION_GROUP, - VPN_LOCATION, - VPN_USERNAME, - VPN_PASSWD - ) - ) - - assert ( - VPN_PROVIDER - and VPN_LOCATION_GROUP - and VPN_LOCATION - and VPN_USERNAME - and VPN_PASSWD - ) - - else: - ######################### - # pairing mode override # - ######################### - if PAIRED_DEVICE_GUID: - try: - ipaddr = get_node_by_guid(family=family) - log( - 'get_node_by_guid: ipaddr={} af={} guid={}'.format( - ipaddr, - family, - PAIRED_DEVICE_GUID - ) - ) - assert ipaddr - except: - if DEBUG: print_exc() - try: - assert family == 6 # fall-back to IPv4 if not already - ipaddr = get_node_by_guid(family=4) - log( - 'get_node_by_guid: ipaddr={} af={} guid={}'.format( - ipaddr, - 4, - PAIRED_DEVICE_GUID - ) - ) - assert ipaddr - except AssertionError as e: - if DEBUG: print_exc() - log( - 'get_node_by_guid: e={} af={}'.format( - repr(e), - family - ) - ) - - ################### - # unblocking mode # - ################### - else: - if family == 6 and not SKIP_DNS: - try: - # try DNS resolution (IPv6) - country = get_alpha(TARGET_COUNTRY).lower() - host = '{}.{}'.format(country, DNS_HOST) - ipaddr = resolve_dns(host=host, record=qtype, family=family) - assert ipaddr - except Exception as e: - log( - 'resolve_dns: e={} qtype={} family={}'.format( - repr(e), - qtype, - family - ) - ) - family = 4 - qtype = 'A' - if DEBUG: print_exc() - - if family == 4 and not SKIP_DNS: - try: - # try DNS resolution (IPv4) - country = get_alpha(TARGET_COUNTRY).lower() - host = '{}.{}'.format(country, DNS_HOST) - ipaddr = resolve_dns(host=host, record=qtype, family=family) - assert ipaddr - except Exception as e: - log( - 'resolve_dns: e={} qtype={} family={}'.format( - repr(e), - qtype, - family - ) - ) - if DEBUG: print_exc() - - log( - '{}: qtype={} country={} host={} ipaddr={}'.format( - stack()[0][3], - qtype, - country, - host, - ipaddr - ) - ) - - if not ipaddr: - if family == 6: - try: - # fall-back to API (IPv6) - ipaddr = get_node_by_country(family=family) - log( - 'get_node_by_country: ipaddr={} af={}'.format( - stack()[0][3], - ipaddr, - family - ) - ) - except AssertionError as e: - log( - 'get_node_by_country: e={} af={}'.format( - repr(e), - family - ) - ) - if DEBUG: print_exc() - family = 4 - qtype = 'A' - - if family == 4: - try: - # fall-back to API (IPv4) - ipaddr = get_node_by_country(family=family) - log( - 'get_node_by_country: ipaddr={} af={}'.format( - ipaddr, - family - ) - ) - except AssertionError as e: - log( - 'get_node_by_country: e={} af={}'.format( - repr(e), - family - ) - ) - if DEBUG: print_exc() - - assert ipaddr - - if family == 6: tun_proto = '{}{}'.format(c_proto, family) - log( - '{}: mode={} remote={} port={} proto={} protos={} cipher={} auth={}'.format( - stack()[0][3], - DEVICE_TYPE, - ipaddr, - port, - tun_proto, - TUN_PROTO, - CIPHER, - AUTH - ) - ) - - ######################################## - # stunnel override for unblocking mode # - ######################################## - if STUNNEL: - result = conf_stunnel_client(node=ipaddr) - if DEBUG: print(result) - - result = restart_stunnel_client() - if DEBUG: print(result) - - if result[0] == 0: - ipaddr = 'localhost' - tun_proto = 'tcp' - if family == 6: tun_proto = '{}{}'.format(tun_proto, family) - log( - '{}: stunnel={} proto={} ipaddr={}'.format( - stack()[0][3], - STUNNEL, - tun_proto, - ipaddr - ) - ) - - ############################## - # (legacy) WANProxy override # - ############################## - if WANPROXY: - (c_wpid, s_wpid, s_wport) = start_wanproxy_server( - ipaddr=ipaddr, - family=family, - local_pid=c_wpid, - remote_pid=s_wpid, - port=s_wport - ) - - log( - 'start_wanproxy_server: ipaddr={} af={} local_pid={} remote_pid={} remote_port={} transport={}'.format( - ipaddr, - family, - c_wpid, - s_wpid, - s_wport, - WANPROXY - ) - ) - - port = WANPROXY_PORT - ipaddr = 'localhost' - tun_proto = 'tcp' - if family == 6: tun_proto = '{}{}'.format(tun_proto, family) - log( - '{}: wan_proxy={} proto={} ipaddr={} port={}'.format( - stack()[0][3], - WANPROXY, - tun_proto, - ipaddr, - port - ) - ) - - if FRAGMENT and tun_proto == 'udp': - cmd.append('--tun-mtu') - cmd.append(TUN_MTU) - cmd.append('--mssfix') - cmd.append('--fragment') - cmd.append(FRAGMENT) - - if EXPLICIT_EXIT_NOTIFY in [1, 2] and tun_proto == 'udp': - cmd.append('--explicit-exit-notify') - cmd.append(str(EXPLICIT_EXIT_NOTIFY)) - - cmd.append('--remote') - cmd.append(ipaddr) - cmd.append(port) - cmd.append(tun_proto) - cmd.append('--cipher') - cmd.append(CIPHER) - cmd.append('--auth') - cmd.append(AUTH) - - #################################### - # stunnel override for mixed modes # - #################################### - if DEVICE_TYPE in [5, 3]: - if STUNNEL: - try: - ipaddr = REMOTE_OVERRIDE.split(' ')[1] - port = REMOTE_OVERRIDE.split(' ')[2] - tun_proto = REMOTE_OVERRIDE.split(' ')[3] - - log('{}: stunnel={} proto={} ipaddr={} port={}'.format( - stack()[0][3], - STUNNEL, - tun_proto, - ipaddr, - port - )) - - result = conf_stunnel_client(node=ipaddr, port=port) - if DEBUG: print(result) - - result = openvpn_remote_override() - if DEBUG: print(result) - - result = restart_stunnel_client() - if DEBUG: print(result) - except: - if DEBUG: print_exc() - - log( - '{}: ipaddr={} af={} qtype={} proto={} port={} country={} guid={} provider={} group={} location={} username={}'.format( - stack()[0][3], - ipaddr, - family, - qtype, - tun_proto, - port, - TARGET_COUNTRY, - PAIRED_DEVICE_GUID, - VPN_PROVIDER, - VPN_LOCATION_GROUP, - VPN_LOCATION, - VPN_USERNAME - ) - ) - - p = run_shell_cmd_nowait(cmd) - stdoutq = Queue() - stderrq = Queue() - stdoutt = Thread(target=enqueue_output, args=(p.stdout, stdoutq)) - stderrt = Thread(target=enqueue_output, args=(p.stderr, stderrq)) - stdoutt.daemon = True - stdoutt.start() - stderrt.daemon = True - stderrt.start() - return stdoutq, stderrq, p, c_proto + global PROTOS + global c_proto + global c_wpid + global s_wpid + global s_wport + global ipaddr + global country + global host + global qtype + + try: + log('os.kill: {}'.format(c_wpid)) + os.kill(int(c_wpid), signal.SIGKILL) + except: + pass + + c_wpid = None + + try: + log('kill_remote_pid: {}'.format( + kill_remote_pid( + ipaddr=ipaddr, + family=family, + pid=s_wpid + ) + )) + except: + pass + + s_wpid = None + s_wport = None + ipaddr = None + + if len(PROTOS) <= 0: PROTOS = list(TUN_PROTO) # start again + c_proto = PROTOS.pop() + tun_proto = c_proto + + env = os.environ.copy() + if DEBUG: print(env) + + qtype = 'A' + if family == 6: + if get_geo_location(family=6): + qtype = 'AAAA' + else: + family = 4 + + cmd = [OPENVPN_BINARY, '--config', CLIENT_CONFIG, '--dev', TUN_IFACE] + port = OPENVPN_PORT + + ############ + # VPN mode # + ############ + if DEVICE_TYPE == 5 or ( + DEVICE_TYPE == 3 + and VPN_PROVIDER + and VPN_LOCATION_GROUP + and VPN_LOCATION + and VPN_USERNAME + and VPN_PASSWD + ): + log('{}: mode={} provider={} group={} location={} username={} password={}'.format( + stack()[0][3], + DEVICE_TYPE, + VPN_PROVIDER, + VPN_LOCATION_GROUP, + VPN_LOCATION, + VPN_USERNAME, + VPN_PASSWD + )) + + assert ( + VPN_PROVIDER + and VPN_LOCATION_GROUP + and VPN_LOCATION + and VPN_USERNAME + and VPN_PASSWD + ) + + else: + ######################### + # pairing mode override # + ######################### + if PAIRED_DEVICE_GUID: + try: + ipaddr = get_node_by_guid(family=family) + log('get_node_by_guid: ipaddr={} af={} guid={}'.format( + ipaddr, + family, + PAIRED_DEVICE_GUID + )) + assert ipaddr + except: + if DEBUG: print_exc() + try: + assert family == 6 # fall-back to IPv4 if not already + ipaddr = get_node_by_guid(family=4) + log('get_node_by_guid: ipaddr={} af={} guid={}'.format( + ipaddr, + 4, + PAIRED_DEVICE_GUID + )) + assert ipaddr + except AssertionError as e: + if DEBUG: print_exc() + log('get_node_by_guid: e={} af={}'.format( + repr(e), + family + )) + + ################### + # unblocking mode # + ################### + else: + if family == 6 and not SKIP_DNS: + try: + # try DNS resolution (IPv6) + country = get_alpha(TARGET_COUNTRY).lower() + host = '{}.{}'.format(country, DNS_HOST) + ipaddr = resolve_dns(host=host, record=qtype, family=family) + assert ipaddr + except Exception as e: + log('resolve_dns: e={} qtype={} family={}'.format( + repr(e), + qtype, + family + )) + family = 4 + qtype = 'A' + if DEBUG: print_exc() + + if family == 4 and not SKIP_DNS: + try: + # try DNS resolution (IPv4) + country = get_alpha(TARGET_COUNTRY).lower() + host = '{}.{}'.format(country, DNS_HOST) + ipaddr = resolve_dns(host=host, record=qtype, family=family) + assert ipaddr + except Exception as e: + log('resolve_dns: e={} qtype={} family={}'.format( + repr(e), + qtype, + family + )) + if DEBUG: print_exc() + + log('{}: qtype={} country={} host={} ipaddr={}'.format( + stack()[0][3], + qtype, + country, + host, + ipaddr + )) + + if not ipaddr: + if family == 6: + try: + # fall-back to API (IPv6) + ipaddr = get_node_by_country(family=family) + log('get_node_by_country: ipaddr={} af={}'.format( + stack()[0][3], + ipaddr, + family + )) + except AssertionError as e: + log('get_node_by_country: e={} af={}'.format( + repr(e), + family + )) + if DEBUG: print_exc() + family = 4 + qtype = 'A' + + if family == 4: + try: + # fall-back to API (IPv4) + ipaddr = get_node_by_country(family=family) + log('get_node_by_country: ipaddr={} af={}'.format( + ipaddr, + family + )) + except AssertionError as e: + log('get_node_by_country: e={} af={}'.format( + repr(e), + family + )) + if DEBUG: print_exc() + + assert ipaddr + + if family == 6: tun_proto = '{}{}'.format(c_proto, family) + log('{}: mode={} remote={} port={} proto={} protos={} cipher={} auth={}'.format( + stack()[0][3], + DEVICE_TYPE, + ipaddr, + port, + tun_proto, + TUN_PROTO, + CIPHER, + AUTH + )) + + ######################################## + # stunnel override for unblocking mode # + ######################################## + if STUNNEL: + result = conf_stunnel_client(node=ipaddr) + if DEBUG: print(result) + + result = restart_stunnel_client() + if DEBUG: print(result) + + if result[0] == 0: + ipaddr = 'localhost' + tun_proto = 'tcp' + if family == 6: tun_proto = '{}{}'.format(tun_proto, family) + log('{}: stunnel={} proto={} ipaddr={}'.format( + stack()[0][3], + STUNNEL, + tun_proto, + ipaddr + )) + + ############################## + # (legacy) WANProxy override # + ############################## + if WANPROXY: + (c_wpid, s_wpid, s_wport) = start_wanproxy_server( + ipaddr=ipaddr, + family=family, + local_pid=c_wpid, + remote_pid=s_wpid, + port=s_wport + ) + + log('start_wanproxy_server: ipaddr={} af={} local_pid={} remote_pid={} remote_port={} transport={}'.format( + ipaddr, + family, + c_wpid, + s_wpid, + s_wport, + WANPROXY + )) + + port = WANPROXY_PORT + ipaddr = 'localhost' + tun_proto = 'tcp' + if family == 6: tun_proto = '{}{}'.format(tun_proto, family) + log('{}: wan_proxy={} proto={} ipaddr={} port={}'.format( + stack()[0][3], + WANPROXY, + tun_proto, + ipaddr, + port + )) + + if FRAGMENT and tun_proto == 'udp': + cmd.append('--tun-mtu') + cmd.append(TUN_MTU) + cmd.append('--mssfix') + cmd.append('--fragment') + cmd.append(FRAGMENT) + + if EXPLICIT_EXIT_NOTIFY in [1, 2] and tun_proto == 'udp': + cmd.append('--explicit-exit-notify') + cmd.append(str(EXPLICIT_EXIT_NOTIFY)) + + cmd.append('--remote') + cmd.append(ipaddr) + cmd.append(port) + cmd.append(tun_proto) + cmd.append('--cipher') + cmd.append(CIPHER) + cmd.append('--auth') + cmd.append(AUTH) + + #################################### + # stunnel override for mixed modes # + #################################### + if DEVICE_TYPE in [5, 3]: + if STUNNEL: + try: + ipaddr = REMOTE_OVERRIDE.split(' ')[1] + port = REMOTE_OVERRIDE.split(' ')[2] + tun_proto = REMOTE_OVERRIDE.split(' ')[3] + + log('{}: stunnel={} proto={} ipaddr={} port={}'.format( + stack()[0][3], + STUNNEL, + tun_proto, + ipaddr, + port + )) + + result = conf_stunnel_client(node=ipaddr, port=port) + if DEBUG: print(result) + + result = openvpn_remote_override() + if DEBUG: print(result) + + result = restart_stunnel_client() + if DEBUG: print(result) + except: + if DEBUG: print_exc() + + log('{}: ipaddr={} af={} qtype={} proto={} port={} country={} guid={} provider={} group={} location={} username={}'.format( + stack()[0][3], + ipaddr, + family, + qtype, + tun_proto, + port, + TARGET_COUNTRY, + PAIRED_DEVICE_GUID, + VPN_PROVIDER, + VPN_LOCATION_GROUP, + VPN_LOCATION, + VPN_USERNAME + )) + + p = run_shell_cmd_nowait(cmd) + stdoutq = Queue() + stderrq = Queue() + stdoutt = Thread(target=enqueue_output, args=(p.stdout, stdoutq)) + stderrt = Thread(target=enqueue_output, args=(p.stderr, stderrq)) + stdoutt.daemon = True + stdoutt.start() + stderrt.daemon = True + stderrt.start() + return stdoutq, stderrq, p, c_proto @retry(Exception, cdata='method={}'.format(stack()[0][3])) def start_server(proto='udp'): - iface = TUN_IFACE_UDP - if proto == 'tcp': iface = TUN_IFACE_TCP - - config = '{}/openvpn/{}_server.conf'.format(WORKDIR, proto) - - cmd = [ - OPENVPN_BINARY, - '--config', config, - '--dev', iface, - '--proto', '{}6'.format(proto), '--port', OPENVPN_PORT, - '--cipher', CIPHER, - '--auth', AUTH - ] - - if FRAGMENT and proto == 'udp': - cmd.append('--tun-mtu') - cmd.append(TUN_MTU) - cmd.append('--mssfix') - cmd.append('--fragment') - cmd.append(FRAGMENT) - - if EXPLICIT_EXIT_NOTIFY in [1, 2]\ - and proto == 'udp'\ - and bool(re.search('^2\.[4-5]\.', OPENVPN_VERSION)): - cmd.append('--explicit-exit-notify') - cmd.append(str(EXPLICIT_EXIT_NOTIFY)) - - p = run_shell_cmd_nowait(cmd) - stdoutq = Queue() - stderrq = Queue() - stdoutt = Thread(target=enqueue_output, args=(p.stdout, stdoutq)) - stderrt = Thread(target=enqueue_output, args=(p.stderr, stderrq)) - stdoutt.daemon = True - stdoutt.start() - stderrt.daemon = True - stderrt.start() - return stdoutq, stderrq, p + iface = TUN_IFACE_UDP + if proto == 'tcp': iface = TUN_IFACE_TCP + + config = '{}/openvpn/{}_server.conf'.format(WORKDIR, proto) + + cmd = [ + OPENVPN_BINARY, + '--config', config, + '--dev', iface, + '--proto', '{}6'.format(proto), '--port', OPENVPN_PORT, + '--cipher', CIPHER, + '--auth', AUTH + ] + + if FRAGMENT and proto == 'udp': + cmd.append('--tun-mtu') + cmd.append(TUN_MTU) + cmd.append('--mssfix') + cmd.append('--fragment') + cmd.append(FRAGMENT) + + if EXPLICIT_EXIT_NOTIFY in [1, 2]\ + and proto == 'udp'\ + and bool(re.search('^2\.[4-5]\.', OPENVPN_VERSION)): + cmd.append('--explicit-exit-notify') + cmd.append(str(EXPLICIT_EXIT_NOTIFY)) + + p = run_shell_cmd_nowait(cmd) + stdoutq = Queue() + stderrq = Queue() + stdoutt = Thread(target=enqueue_output, args=(p.stdout, stdoutq)) + stderrt = Thread(target=enqueue_output, args=(p.stderr, stderrq)) + stdoutt.daemon = True + stdoutt.start() + stderrt.daemon = True + stderrt.start() + return stdoutq, stderrq, p @retry(Exception, cdata='method={}'.format(stack()[0][3])) def _get_client_conns(user=GUID, proto='udp'): - conns = 0 - stats = open( - '{}/openvpn.{}.status'.format( - DATADIR, - proto - ) - ).read().split('\n') - for lines in stats: - line = lines.split(',') - if 'CLIENT_LIST' in line and line[0] in ['CLIENT_LIST'] and line[-1] and line[1] == user: - conns = conns + 1 - return conns + conns = 0 + stats = open('{}/openvpn.{}.status'.format( + DATADIR, + proto + )).read().split('\n') + for lines in stats: + line = lines.split(',') + if 'CLIENT_LIST' in line and line[0] in ['CLIENT_LIST'] and line[-1] and line[1] == user: + conns = conns + 1 + return conns @retry(Exception, cdata='method={}'.format(stack()[0][3])) def get_client_conns(user=GUID): - total_conns = 0 - for proto in TUN_PROTO: - try: - proto_conns = _get_client_conns(user=user, proto=proto) - total_conns = total_conns + proto_conns - except: - proto_conns = 0 - return total_conns + total_conns = 0 + for proto in TUN_PROTO: + try: + proto_conns = _get_client_conns(user=user, proto=proto) + total_conns = total_conns + proto_conns + except: + proto_conns = 0 + return total_conns @retry(Exception, cdata='method={}'.format(stack()[0][3])) def get_server_conns_by(proto='udp'): - conns = 0 - stats = open( - '{}/openvpn.{}.status'.format( - DATADIR, - proto - ) - ).read().split('\n') - for lines in stats: - line = lines.split(',') - if 'CLIENT_LIST' in line and line[0] in ['CLIENT_LIST'] and line[-1]: conns = conns + 1 - return int(conns) + conns = 0 + stats = open('{}/openvpn.{}.status'.format( + DATADIR, + proto + )).read().split('\n') + for lines in stats: + line = lines.split(',') + if 'CLIENT_LIST' in line and line[0] in ['CLIENT_LIST'] and line[-1]: conns = conns + 1 + return int(conns) @retry(Exception, cdata='method={}'.format(stack()[0][3])) def get_server_conns(status=[True, True]): - total_conns = 0 - for proto in TUN_PROTO: - try: - proto_conns = int(get_server_conns_by(proto=proto)) - total_conns = total_conns + proto_conns - except: - proto_conns = 0 - return int(total_conns) + total_conns = 0 + for proto in TUN_PROTO: + try: + proto_conns = int(get_server_conns_by(proto=proto)) + total_conns = total_conns + proto_conns + except: + proto_conns = 0 + return int(total_conns) @retry(Exception, cdata='method={}'.format(stack()[0][3])) def get_load_stats(host=VPN_HOST): - total_stats = (0, 0, 0) - for port in [VPN_UDP_MGMT_PORT, VPN_TCP_MGMT_PORT]: - try: - load_stats = run_openvpn_mgmt_cmd(host=host, port=port, cmd='load-stats') - p = re.compile('^SUCCESS: nclients=([\d]+),bytesin=([\d]+),bytesout=([\d]+)$') - m = p.search(load_stats[0]) - try: - port_stats = m.groups() - except: - port_stats = (0, 0, 0) - except: - port_stats = (0, 0, 0) - total_stats = list(map(lambda x,y:int(x)+int(y), total_stats, port_stats)) - return total_stats + total_stats = (0, 0, 0) + for port in [VPN_UDP_MGMT_PORT, VPN_TCP_MGMT_PORT]: + try: + load_stats = run_openvpn_mgmt_cmd(host=host, port=port, cmd='load-stats') + p = re.compile('^SUCCESS: nclients=([\d]+),bytesin=([\d]+),bytesout=([\d]+)$') + m = p.search(load_stats[0]) + try: + port_stats = m.groups() + except: + port_stats = (0, 0, 0) + except: + port_stats = (0, 0, 0) + total_stats = list(map(lambda x,y:int(x)+int(y), total_stats, port_stats)) + return total_stats @retry(Exception, cdata='method={}'.format(stack()[0][3])) def get_status(user=GUID, proto='udp'): - status = list() - status = open( - '{}/openvpn.{}.status'.format( - DATADIR, - proto - ) - ).read().split('\n') - return status + status = list() + status = open('{}/openvpn.{}.status'.format( + DATADIR, + proto + )).read().split('\n') + return status @retry(Exception, cdata='method={}'.format(stack()[0][3])) def _get_status(host=VPN_HOST, port=VPN_UDP_MGMT_PORT): - status = list() - status = run_openvpn_mgmt_cmd(host=host, port=port) - return status + status = list() + status = run_openvpn_mgmt_cmd(host=host, port=port) + return status @retry(Exception, cdata='method={}'.format(stack()[0][3])) def get_client_status(): - bytesin = 0 - bytesout = 0 - try: - status = open( - '{}/client.status'.format( - TEMPDIR - ) - ).read().split('\n') - bytesin = [line.split(',')[1] for line in status if line.split(',')[0] == 'TCP/UDP read bytes'][0] - bytesout = [line.split(',')[1] for line in status if line.split(',')[0] == 'TCP/UDP write bytes'][0] - except IndexError: - pass - return (bytesin, bytesout) + bytesin = 0 + bytesout = 0 + try: + status = open('{}/client.status'.format( + TEMPDIR + )).read().split('\n') + bytesin = [line.split(',')[1] for line in status if line.split(',')[0] == 'TCP/UDP read bytes'][0] + bytesout = [line.split(',')[1] for line in status if line.split(',')[0] == 'TCP/UDP write bytes'][0] + except IndexError: + pass + return (bytesin, bytesout) @retry(Exception, cdata='method={}'.format(stack()[0][3])) def get_clients(): - clients = [] - for proto in TUN_PROTO: - try: - status_lines = get_status(proto=proto) - for lines in status_lines: - line = lines.split(',') - if 'CLIENT_LIST' in line and line[0] in ['CLIENT_LIST'] and line[-1]: - clients.append(line[1]) - except: - status_lines = [] - return clients + clients = [] + for proto in TUN_PROTO: + try: + status_lines = get_status(proto=proto) + for lines in status_lines: + line = lines.split(',') + if 'CLIENT_LIST' in line and line[0] in ['CLIENT_LIST'] and line[-1]: + clients.append(line[1]) + except: + status_lines = [] + return clients @retry(Exception, cdata='method={}'.format(stack()[0][3])) def reauthenticate_clients(): - count = 0 - while True: - clients = get_clients() - killed = list() - print( - '{}: count={} clients={}'.format( - stack()[0][3], - count, - len(clients) - ) - ) - for client in clients: - # password check (resin.io Data API) - jwtoken = None - try: - password = get_device_env_by_name(guid=client, name='TUN_PASSWD')[:16] - assert password - except Exception as e: - # authenticate against JWT recorded against PayPal billing agreement - try: - jwtoken = decode_jwt_payload(encoded=get_jwt_payload_from_paypal(baid=client)) - password = jwtoken['p'][:16] - assert password - except: - password = None - - if not password: break - - result = auth.authenticate(username=client, password=password) - print( - 'get_device_env_by_name: client={} password={}'.format( - client, - password - ) - ) - if not result and client != 'UNDEF': - for port in [VPN_UDP_MGMT_PORT, VPN_TCP_MGMT_PORT]: - try: - print( - 'run_openvpn_mgmt_cmd: port={} cmd=kill client={} result={}'.format( - port, - client, - result - ) - ) - print( - run_openvpn_mgmt_cmd( - port=port, - cmd='kill {}'.format( - client - ) - ) - ) - killed.append(client) - except: - pass - print( - '{}: count={} killed={}'.format( - stack()[0][3], - count, - len(killed) - ) - ) - sleep(3600) - count = count + 1 + count = 0 + while True: + clients = get_clients() + killed = list() + print('{}: count={} clients={}'.format( + stack()[0][3], + count, + len(clients) + )) + for client in clients: + # password check (resin.io Data API) + jwtoken = None + try: + password = get_device_env_by_name(guid=client, name='TUN_PASSWD')[:16] + assert password + except Exception as e: + # authenticate against JWT recorded against PayPal billing agreement + try: + jwtoken = decode_jwt_payload(encoded=get_jwt_payload_from_paypal(baid=client)) + password = jwtoken['p'][:16] + assert password + except: + password = None + + if not password: break + + result = auth.authenticate(username=client, password=password) + print('get_device_env_by_name: client={} password={}'.format( + client, + password + )) + if not result and client != 'UNDEF': + for port in [VPN_UDP_MGMT_PORT, VPN_TCP_MGMT_PORT]: + try: + print('run_openvpn_mgmt_cmd: port={} cmd=kill client={} result={}'.format( + port, + client, + result + )) + print(run_openvpn_mgmt_cmd( + port=port, + cmd='kill {}'.format(client) + )) + killed.append(client) + except: + pass + print('{}: count={} killed={}'.format( + stack()[0][3], + count, + len(killed) + )) + sleep(3600) + count = count + 1 def log_client_stats(status=False, country=TARGET_COUNTRY): - if LOG_CLIENT_STATS: - for family in AF_INETS: - data = get_geo_location(family=family) - if not data: break + if LOG_CLIENT_STATS: + for family in AF_INETS: + data = get_geo_location(family=family) + if not data: break - # 1 = up - # 0 = down - data['status'] = sum([int(el) for el in [status]]) + # 1 = up + # 0 = down + data['status'] = sum([int(el) for el in [status]]) - data['bytesin'] = 0 - data['bytesout'] = 0 - data['conns'] = 0 - data['hostapd'] = AP - data['weight'] = MAX_CONNS_CLIENT + data['bytesin'] = 0 + data['bytesout'] = 0 + data['conns'] = 0 + data['hostapd'] = AP + data['weight'] = MAX_CONNS_CLIENT - if DEVICE_TYPE == 5: - data['country'] = country + if DEVICE_TYPE == 5: + data['country'] = country - if COUNTRY_OVERRIDE: - log('{}: country={}'.format(stack()[0][3], COUNTRY_OVERRIDE)) - data['country'] = COUNTRY_OVERRIDE + if COUNTRY_OVERRIDE: + log('{}: country={}'.format(stack()[0][3], COUNTRY_OVERRIDE)) + data['country'] = COUNTRY_OVERRIDE - if GEOIP_OVERRIDE: - log('{}: ip={}'.format(stack()[0][3], GEOIP_OVERRIDE)) - data['ip'] = GEOIP_OVERRIDE + if GEOIP_OVERRIDE: + log('{}: ip={}'.format(stack()[0][3], GEOIP_OVERRIDE)) + data['ip'] = GEOIP_OVERRIDE - if DEVICE_TYPE in CLIENT_DEVICE_TYPES: - try: - data['conns'] = get_stations() # wireless client count - data['bytesin'] = get_client_status()[0] - data['bytesout'] = get_client_status()[1] - except: - pass + if DEVICE_TYPE in CLIENT_DEVICE_TYPES: + try: + data['conns'] = get_stations() # wireless client count + data['bytesin'] = get_client_status()[0] + data['bytesout'] = get_client_status()[1] + except: + pass - log('{}: af={} data={}'.format(stack()[0][3], family, data)) - print(put_device(family=family, data=data)) + log('{}: af={} data={}'.format(stack()[0][3], family, data)) + print(put_device(family=family, data=data)) - # client logging plug-in(s) - log('{}: plugin={}'.format(stack()[0][3], DNS_SUB_DOMAIN)) - if 'log_plugin_client' in dir(plugin): - result = plugin.log_plugin_client(status=status) + # client logging plug-in(s) + log('{}: plugin={}'.format(stack()[0][3], DNS_SUB_DOMAIN)) + if 'log_plugin_client' in dir(plugin): + result = plugin.log_plugin_client(status=status) def log_server_stats(status=[False, False]): - if LOG_SERVER_STATS: - for family in AF_INETS: - data = get_geo_location(family=family) - if not data: break - - # 2 = udp+tcp; 1 = udp or tcp; 0 = down - data['status'] = sum([int(el) for el in status]) - data['conns'] = 0 - data['bytesin'] = 0 - data['bytesout'] = 0 - data['cipher'] = CIPHER - data['auth'] = AUTH - data['upnp'] = UPNP - data['weight'] = MAX_CONNS_SERVER - - if COUNTRY_OVERRIDE: - log('{}: country={}'.format(stack()[0][3], COUNTRY_OVERRIDE)) - data['country'] = COUNTRY_OVERRIDE - - if GEOIP_OVERRIDE: - log('{}: ip={}'.format(stack()[0][3], GEOIP_OVERRIDE)) - data['ip'] = GEOIP_OVERRIDE - - if DEVICE_TYPE in SERVER_DEVICE_TYPES: - data['conns'] = int(get_server_conns()) - data['bytesin'] = int(get_load_stats()[1]) - data['bytesout'] = int(get_load_stats()[2]) - - log('{}: af={} data={}'.format(stack()[0][3], family, data)) - print(put_device(family=family, data=data)) - - # additional server logging plug-in(s) - log('{}: plugin={}'.format(stack()[0][3], DNS_SUB_DOMAIN)) - if 'log_plugin_server' in dir(plugin): - result = plugin.log_plugin_server(status=status) + if LOG_SERVER_STATS: + for family in AF_INETS: + data = get_geo_location(family=family) + if not data: break + + # 2 = udp+tcp; 1 = udp or tcp; 0 = down + data['status'] = sum([int(el) for el in status]) + data['conns'] = 0 + data['bytesin'] = 0 + data['bytesout'] = 0 + data['cipher'] = CIPHER + data['auth'] = AUTH + data['upnp'] = UPNP + data['weight'] = MAX_CONNS_SERVER + + if COUNTRY_OVERRIDE: + log('{}: country={}'.format(stack()[0][3], COUNTRY_OVERRIDE)) + data['country'] = COUNTRY_OVERRIDE + + if GEOIP_OVERRIDE: + log('{}: ip={}'.format(stack()[0][3], GEOIP_OVERRIDE)) + data['ip'] = GEOIP_OVERRIDE + + if DEVICE_TYPE in SERVER_DEVICE_TYPES: + data['conns'] = int(get_server_conns()) + data['bytesin'] = int(get_load_stats()[1]) + data['bytesout'] = int(get_load_stats()[2]) + + log('{}: af={} data={}'.format(stack()[0][3], family, data)) + print(put_device(family=family, data=data)) + + # additional server logging plug-in(s) + log('{}: plugin={}'.format(stack()[0][3], DNS_SUB_DOMAIN)) + if 'log_plugin_server' in dir(plugin): + result = plugin.log_plugin_server(status=status) def openvpn_remote_override(conf='/mnt/{}/client.ovpn'.format(DNS_SUB_DOMAIN)): - f = open(conf, 'r') - text = f.read() - f.close() - p = re.compile('remote\s+(.*)\s+(.*)\s+([tcpud46]+)') - m = p.search(text) - try: - groups = m.groups() - host = groups[0] - port = groups[1] - proto = groups[2] - search = 'remote {} {} {}'.format(host, port, proto) - replace = 'remote {} {} {}'.format('localhost', port, proto) - text = text.replace(search, replace) - f = open(conf, 'w') - f.write(text) - f.close() - except: - if DEBUG: print_exc() - - return text + f = open(conf, 'r') + text = f.read() + f.close() + p = re.compile('remote\s+(.*)\s+(.*)\s+([tcpud46]+)') + m = p.search(text) + try: + groups = m.groups() + host = groups[0] + port = groups[1] + proto = groups[2] + search = 'remote {} {} {}'.format(host, port, proto) + replace = 'remote {} {} {}'.format('localhost', port, proto) + text = text.replace(search, replace) + f = open(conf, 'w') + f.write(text) + f.close() + except: + if DEBUG: print_exc() + + return text def conf_stunnel_client( - node=None, - port=OPENVPN_PORT, - conf='/etc/stunnel/stunnel-client.conf', - template='/etc/stunnel/stunnel-client.template.conf' + node=None, + port=OPENVPN_PORT, + conf='/etc/stunnel/stunnel-client.conf', + template='/etc/stunnel/stunnel-client.template.conf' ): - if not node: return None - f = open(template, 'r') - template = f.read() - f.close() - template = template.replace('{{OPENVPN_SERVER}}', str(node)) - template = template.replace('{{OPENVPN_PORT}}', str(port)) - f = open(conf, 'w') - f.write(template) - f.close() - return template + if not node: return None + f = open(template, 'r') + template = f.read() + f.close() + template = template.replace('{{OPENVPN_SERVER}}', str(node)) + template = template.replace('{{OPENVPN_PORT}}', str(port)) + f = open(conf, 'w') + f.write(template) + f.close() + return template def restart_stunnel_client( - stunnel_bin='/usr/bin/stunnel', - conf='/etc/stunnel/stunnel-client.conf' + stunnel_bin='/usr/bin/stunnel', + conf='/etc/stunnel/stunnel-client.conf' ): - print( - run_shell_cmd( - [ - '/usr/bin/pkill', - '-f', - '%s %s' % (stunnel_bin, conf) - ] - ) - ) - return run_shell_cmd(['%s' % stunnel_bin, '%s' % conf]) + print(run_shell_cmd( + [ + '/usr/bin/pkill', + '-f', + '%s %s' % (stunnel_bin, conf) + ] + )) + return run_shell_cmd(['%s' % stunnel_bin, '%s' % conf]) def start_wanproxy_server( - ipaddr=None, - family=4, - local_pid=None, - remote_pid=None, - port=None, - ssh_port=DOCKER_SSH_PORT + ipaddr=None, + family=4, + local_pid=None, + remote_pid=None, + port=None, + ssh_port=DOCKER_SSH_PORT ): - if WANPROXY == 'SSH': - remote_guid = get_guid_by_public_ipaddr(ipaddr=ipaddr, family=family) - assert remote_guid - if DEBUG: print( - 'get_guid_by_public_ipaddr: ipaddr={} family={} remote_guid={}'.format( - ipaddr, - family, - remote_guid - ) - ) - - if not (port and remote_pid): - if WANPROXY == 'SSH': - # start WANProxy server instance and get remote port for forwarding - result = run_shell_cmd( - [ - 'ssh', '-i', '%s/id_rsa' % WORKDIR, - '-o StrictHostKeyChecking=no', - 'root@%s' % ipaddr, - '-p', '%s' % str(ssh_port), - 'bash', - '-c', - '"source %s/functions; source %s/.env; start_wanproxy_server"' % ( - WORKDIR, - TEMPDIR - ) - ] - ) - if DEBUG: print(result) - remote_pid = result[1].split()[0] - port = result[1].split()[1] - - if not local_pid: - if WANPROXY == 'SSH': - # start SSH tunnel - result = run_background_shell_cmd( - [ - 'ssh', '-i', '%s/id_rsa' % WORKDIR, - '-fN','-o StrictHostKeyChecking=no', - '-L 3301:localhost:%s' % str(port), - '-p', '%s' % str(ssh_port), - 'root@%s' % ipaddr - ] - ) - if DEBUG: print(result.__dict__) - local_pid = str(int(result.pid) + 1) - - if WANPROXY == 'SOCAT': - # start SOCAT local listener - result = run_shell_cmd_nowait( - [ - '/usr/bin/socat', - 'TCP%s-LISTEN:3301,bind=localhost,su=nobody,fork,reuseaddr' % str( - family - ), - 'TCP%s:%s:%s' % (str(family), ipaddr, SOCAT_PORT) - ] - ) - if DEBUG: print(result.__dict__) - local_pid = str(result.pid) - - return (local_pid, remote_pid, port) + if WANPROXY == 'SSH': + remote_guid = get_guid_by_public_ipaddr(ipaddr=ipaddr, family=family) + assert remote_guid + if DEBUG: print( 'get_guid_by_public_ipaddr: ipaddr={} family={} remote_guid={}'.format( + ipaddr, + family, + remote_guid + )) + + if not (port and remote_pid): + if WANPROXY == 'SSH': + # start WANProxy server instance and get remote port for forwarding + result = run_shell_cmd( + [ + 'ssh', '-i', '%s/id_rsa' % WORKDIR, + '-o StrictHostKeyChecking=no', + 'root@%s' % ipaddr, + '-p', '%s' % str(ssh_port), + 'bash', + '-c', + '"source %s/functions; source %s/.env; start_wanproxy_server"' % ( + WORKDIR, + TEMPDIR + ) + ] + ) + if DEBUG: print(result) + remote_pid = result[1].split()[0] + port = result[1].split()[1] + + if not local_pid: + if WANPROXY == 'SSH': + # start SSH tunnel + result = run_background_shell_cmd( + [ + 'ssh', '-i', '%s/id_rsa' % WORKDIR, + '-fN','-o StrictHostKeyChecking=no', + '-L 3301:localhost:%s' % str(port), + '-p', '%s' % str(ssh_port), + 'root@%s' % ipaddr + ] + ) + if DEBUG: print(result.__dict__) + local_pid = str(int(result.pid) + 1) + + if WANPROXY == 'SOCAT': + # start SOCAT local listener + result = run_shell_cmd_nowait( + [ + '/usr/bin/socat', + 'TCP%s-LISTEN:3301,bind=localhost,su=nobody,fork,reuseaddr' % str( + family + ), + 'TCP%s:%s:%s' % (str(family), ipaddr, SOCAT_PORT) + ] + ) + if DEBUG: print(result.__dict__) + local_pid = str(result.pid) + + return (local_pid, remote_pid, port) def kill_remote_pid(ipaddr=None, family=4, pid=None, ssh_port=DOCKER_SSH_PORT): - if WANPROXY == 'SSH': - remote_guid = get_guid_by_public_ipaddr(ipaddr=ipaddr, family=family) - assert remote_guid - if DEBUG: print( - 'get_guid_by_public_ipaddr: ipaddr={} family={} remote_guid={}'.format( - ipaddr, - family, - remote_guid - ) - ) - - result = None - if pid and WANPROXY == 'SSH': - # kill remote pid - result = run_shell_cmd( - [ - 'ssh', '-i', '%s/id_rsa' % WORKDIR, - '-o StrictHostKeyChecking=no', - 'root@%s' % ipaddr, - '-p', '%s' % str(ssh_port), - 'bash', '-c', '"kill -9 %s"' % str(pid) - ] - ) - if DEBUG: print(result) - return result + if WANPROXY == 'SSH': + remote_guid = get_guid_by_public_ipaddr(ipaddr=ipaddr, family=family) + assert remote_guid + if DEBUG: print('get_guid_by_public_ipaddr: ipaddr={} family={} remote_guid={}'.format( + ipaddr, + family, + remote_guid + )) + + result = None + if pid and WANPROXY == 'SSH': + # kill remote pid + result = run_shell_cmd( + [ + 'ssh', '-i', '%s/id_rsa' % WORKDIR, + '-o StrictHostKeyChecking=no', + 'root@%s' % ipaddr, + '-p', '%s' % str(ssh_port), + 'bash', '-c', '"kill -9 %s"' % str(pid) + ] + ) + if DEBUG: print(result) + return result @retry(Exception, cdata='method={}'.format(stack()[0][3])) def disconnect_clients(): - disconnected = list() - while True: - if os.path.exists('{}/disconnect_clients'.format(DATADIR)): - if not 'client_disconnect' in dir(plugin): break - clients = get_clients() - print('{}: clients={}'.format(stack()[0][3], len(clients))) - for client in clients: - if client not in ['UNDEF']: - try: - result = plugin.client_disconnect(client) - disconnected.append(client) - print( - 'client_disconnect: client={} result={}'.format( - client, - result - ) - ) - except: - pass - - if os.path.exists('{}/disconnect_clients'.format(DATADIR)): - os.remove('{}/disconnect_clients'.format(DATADIR)) - - print( - '{}: plugin={} disconnected={}'.format( - stack()[0][3], - DNS_SUB_DOMAIN, - len(disconnected) - ) - ) - sleep(1) - sleep(3600) + disconnected = list() + while True: + if os.path.exists('{}/disconnect_clients'.format(DATADIR)): + if not 'client_disconnect' in dir(plugin): break + clients = get_clients() + print('{}: clients={}'.format(stack()[0][3], len(clients))) + for client in clients: + if client not in ['UNDEF']: + try: + result = plugin.client_disconnect(client) + disconnected.append(client) + print('client_disconnect: client={} result={}'.format( + client, + result + )) + except: + pass + + if os.path.exists('{}/disconnect_clients'.format(DATADIR)): + os.remove('{}/disconnect_clients'.format(DATADIR)) + + print('{}: plugin={} disconnected={}'.format( + stack()[0][3], + DNS_SUB_DOMAIN, + len(disconnected) + )) + sleep(1) + sleep(3600) if not OPENVPN_VERSION: OPENVPN_VERSION = get_openvpn_version() From fa1452e663a6f53dcc8bf7095e3002f1aca3d889 Mon Sep 17 00:00:00 2001 From: "black.box (Unzoner) team@belodetek.io" Date: Sat, 2 Dec 2023 18:14:00 -0800 Subject: [PATCH 2/2] feat: remove ping tests and refactor mixed mode status reporting * re-encrypt secrets * remove GeoIP * bump OpenSSL to 3.0.12 * bump OpenVPN to 2.6.8 * bump sniproxy to 0.6.1 * deprecate WANPROXY * despace-entab --- .github/workflows/balena.yml | 5 + .gitsecret/paths/mapping.cfg | 2 +- unzoner/.balena/balena.yml | 19 +- unzoner/.balena/secrets/env.secret | Bin 1267 -> 1171 bytes unzoner/Dockerfile.template | 292 ++++++++++++++--------------- unzoner/functions | 19 +- unzoner/id_rsa.secret | Bin 2295 -> 2292 bytes unzoner/mgmt.ovpn.secret | Bin 4501 -> 4499 bytes unzoner/openvpn/client.key.secret | Bin 2310 -> 2308 bytes unzoner/openvpn/dh2048.pem.secret | Bin 1345 -> 1344 bytes unzoner/openvpn/server.key.secret | Bin 2316 -> 2314 bytes unzoner/openvpn/ta.key.secret | Bin 1381 -> 1380 bytes unzoner/src/config.py | 4 - unzoner/src/main.py | 145 ++++++-------- unzoner/src/tests/run | 2 +- unzoner/src/vpn.py | 160 +--------------- unzoner/start | 75 -------- 17 files changed, 221 insertions(+), 502 deletions(-) diff --git a/.github/workflows/balena.yml b/.github/workflows/balena.yml index 4cd8954..ae94396 100644 --- a/.github/workflows/balena.yml +++ b/.github/workflows/balena.yml @@ -8,6 +8,11 @@ on: branches: - master +concurrency: + group: ${{ github.workflow }}-${{ github.event.number || github.ref }} + # cancel jobs in progress for updated PRs, but not merge or tag events + cancel-in-progress: ${{ github.event.action == 'synchronize' }} + env: ENVIRONMENT: balena-cloud.com BLOCK_PREFIX: belodetek/unzoner diff --git a/.gitsecret/paths/mapping.cfg b/.gitsecret/paths/mapping.cfg index fa32711..5284212 100644 --- a/.gitsecret/paths/mapping.cfg +++ b/.gitsecret/paths/mapping.cfg @@ -2,6 +2,6 @@ unzoner/openvpn/client.key:1f2e908d2eac66568e4d4cc2c37967684c0da9cb32a24c970ee82 unzoner/openvpn/server.key:0817ed6b2ae19fdf1ddab8b507d70e7cbea29fa00674687eb845514539ad2c64 unzoner/openvpn/ta.key:61a616ec66587848c1a43cc92aa21ad0305e804581f9c2a437929713c4f4edb6 unzoner/id_rsa:7efaea18eedb4a1b9f9298e0f3d92ceb3f2adc4c29a5e818009a7d88f847296f -unzoner/.balena/secrets/env:b581220016ab9a0e9bb870baa31c1407caac3ba674ad785d3bffc9cb407f756a +unzoner/.balena/secrets/env:57af61f0f7fdb6afa4d15d7399358c00957b4358de192eb0d7b048f575e345a1 unzoner/mgmt.ovpn:b81b41c4cf71cdd490feecadf4bb35c31ed8396ed0fdd61808d4efd73c3031de unzoner/openvpn/dh2048.pem:7afe4bea0e57bc35bcd7cdd9e393692c43a88facc64c309a176dc69a66d5a029 diff --git a/unzoner/.balena/balena.yml b/unzoner/.balena/balena.yml index 6a16100..7f9adba 100644 --- a/unzoner/.balena/balena.yml +++ b/unzoner/.balena/balena.yml @@ -6,23 +6,22 @@ build-variables: # https://en.wikipedia.org/wiki/Bird_Internet_routing_daemon - BUILD_BIRD=0 # https://github.com/openssl/openssl - - BUILD_OPENSSL_VERSION=3.0.5 - - BUILD_OPENSSL=0 + # Setting up openssh-server (1:8.9p1-3ubuntu0.4) ... + # OpenSSL version mismatch. Built against 30000020, you have 30200000 + - BUILD_OPENSSL_VERSION=3.0.12 + - BUILD_OPENSSL=1 # https://gcc.gnu.org/onlinedocs/gcc/AArch64-Options.html - AARCH64_OPTIMISE_FLAGS=-O3 -mtune=cortex-a72 -march=armv8-a # https://gcc.gnu.org/onlinedocs/gcc/ARM-Options.html - ARM_OPTIMISE_FLAGS=-O3 -mtune=cortex-a53 -mcpu=cortex-a53+crypto -mfpu=crypto-neon-fp-armv8 # https://github.com/OpenVPN/openvpn - - BUILD_OPENVPN_VERSION=2.5.7 - - BUILD_OPENVPN=0 + - BUILD_OPENVPN_VERSION=2.6.8 + - BUILD_OPENVPN=1 # FIXME: https://sources.debian.org/patches/sniproxy/0.6.0-2/ - - BUILD_SNIPROXY_VERSION=0.6.0 - - BUILD_SNIPROXY=0 - # project abandoned http://wanproxy.org/) - - BUILD_WANPROXY_VERSION=0.8.0 - - BUILD_WANPROXY=0 + - BUILD_SNIPROXY_VERSION=0.6.1 + - BUILD_SNIPROXY=1 # https://nuitka.net/ - - COMPILE_CODE=0 + - COMPILE_CODE=1 # (e.g.) dig +short us.{{ DNS_SUB_DOMAIN }}.{{ DNS_DOMAIN }} - DNS_SUB_DOMAIN=blackbox diff --git a/unzoner/.balena/secrets/env.secret b/unzoner/.balena/secrets/env.secret index adc913cd4aa10b0565dee71b2682cffec75050c1..3292a17eb741d32d240b3e992e35343eebf6d157 100644 GIT binary patch literal 1171 zcmV;E1Z?|-0t^F2<0md9g;;U{5B@mhhJo-(o3NA|939c4R8*aH3+;t_6P7^Jaiy5% zCge!QQ1e$yL%l-bWS5Z!m=4VKGjz&U{K!)9%mFThH$ZU@pRv2dFa7X_K4WCJGxOof zR{0zyLh;qLWV%fHb=`yVQ3FGGhXEsG%vdbZFJSY@#&e?9pz24^Cl| zkhadZ)+TP22W($gFmiD8Bg#CsBJ0)XOvKB$O5-VI1T5g9bgsj!r_qs=M-{~rE5Pxe zd!Pt(x7kxQOx*8D!Oz)r0+e>a>fhHwWaQQ_NUiA1j9s-3o;Pi~~yUUU_9b7~ug6{vfRINXN(dAbN1?G#hwQ4nC*hPJV(C zqhPcEYt3u$tfJcG9FUjAMm=v=6VmSCqYE*N*gYlG$viX4tU9ORz{VMqa82tG!@45wF&~JIMd|Z7_=6#xK6JK)O2eo52 zgj5yJ--FNkECaE(zEQ3ZFYiBZDN+?_*RVHVO7kNbDL}Y!+|eGra?ygd)BD+?%YeTx zZa35EMh^T&EsHmk)47mIp~x2*BO2U*ZdW>X=aG5Hm!TqO@Gud*qmU=TbW7I z^U4Z;fI!{6oeb-T7AT3PeWv2>XR4GK;Fx<>rh3^lX{txW^Anz|p9N=sz5| llTi3Y>V&_GG11Szo2-vxJa0lfAQLE})wt?v zns3s*^fJd(Wb8b(Me>W7i1-9Y4W4Qzo?B|Ji)=1w+pbwNi5!>0oL^AUUWndb zBKnzVP98$RtFkB_>Q*no{p?IZsd3Y$>rzJuug+Aadv5nqiWG5o1HlM&7}L{a_KXHd zk!DsWfv`GUnBi4^$Ohf`hxZt8Y$Gn1DW)7c3xuc|iPynllvS<-4!<7YACEN_RmF-@^_F z{!I|VutP7d3F8>}>PJI_F^_9#K$SsJ!BS9%Rx8O3#Rose9xyx5s(2UtPz&v?T~up& zoiX^*#CcG$Hh82e&*i3y(961GRRA17Cv_eOe~ynqz6LinuqN!04euksPp_F)*8&1b zp_dQeH@1i55rCh^3?R1YI1Vu8o*DpZ*``R&q=6>5XME2D7{(fH7$R{1Sg--Y#Sy$p zwKv{>BDQ%<6{|TG92wosZ}Hl*`G{PY6;mm3=qh7+TpLC7XL?qe;sjx{fMzCdiL4B@xw>L7&yi~~yUUU_9b7~ug6{T;huq$Agc^~Nfu`DE>xv{(nEEuFYr z7JnT}I1WTW0E-`~omvIi(7x4>0R#Rk7|}!+?BIJ0&4*c`=Lj7V8zPTo$uQReYWKNE z{I>LWI!@-C7?1I_dJRxze6VcIHnykWPtW?|XQ%DljEB1MZdmv_yRjKNB>n(T%2sx;AzE^}~C=&zIl5_YmcTk0Eec zE?BzIJ=Lgsw0v7x@b*Kw?{^*;3eVAq=K?q6&-vTJi$YMTdeM1lz;qqE@uOJv%OZXB zA~_-uFo{TlMzoUF7T9`i@#%yvdu-uIH|KA;=egGFf@yzB_%LoTizymoHbIAB-=Vo$e)H8W^i!4pCBKd4<5U)fZ9G124MjW_&a$rOGE?vxJNGKBibH~}y^0xS^`T(0 zcn9Gp_mXF=k}PHYPvOZ4_E3`BA`&vj`&@cGzR_KJ!tCFmZ-52&rOnh;7rhtOFW)`& zgn=3V;Kpb?4$4Pz%7Ta&7@098dhS|irW34*z~D@H?W#(KCXYWAVEVwq^AYRxkN zUfcsH&^KL}V6wFEf7h*A5iByvpkXxAs+nE(aP3W diff --git a/unzoner/Dockerfile.template b/unzoner/Dockerfile.template index c71497e..56e7b6c 100644 --- a/unzoner/Dockerfile.template +++ b/unzoner/Dockerfile.template @@ -14,44 +14,48 @@ ARG BUILD_OPENVPN ARG BUILD_OPENVPN_VERSION ARG BUILD_SNIPROXY ARG BUILD_SNIPROXY_VERSION -ARG BUILD_WANPROXY -ARG BUILD_WANPROXY_VERSION ARG COMPILE_CODE ARG DNS_SUB_DOMAIN ENV DEBIAN_FRONTEND noninteractive RUN install_packages \ - automake \ - bison \ - ca-certificates \ - cmake \ - curl \ - fakeroot \ - flex \ - gawk \ - gettext \ - libev-dev \ - liblz4-dev \ - liblzo2-dev \ - libpam-dev \ - libssl-dev \ - libtool \ - libudns-dev \ - openssl \ - python3-venv + automake \ + bison \ + ca-certificates \ + cmake \ + curl \ + fakeroot \ + flex \ + gawk \ + gettext \ + libcap-ng-dev \ + libev-dev \ + liblz4-dev \ + liblzo2-dev \ + libnl-genl-3-dev \ + libpam0g-dev \ + libssl-dev \ + libtool \ + libudns-dev \ + openssl \ + python3-venv WORKDIR /data RUN useradd openssl && (passwd -d openssl || true) +ENV LD_LIBRARY_PATH="/usr/local/ssl/lib64:$LD_LIBRARY_PATH" + +RUN update-ca-certificates + RUN if [ "$BUILD_OPENSSL" = '1' ]; then \ - set -x; git clone --single-branch --branch openssl-$BUILD_OPENSSL_VERSION https://github.com/openssl/openssl \ - && cd openssl \ - && chown -hR openssl:openssl . \ - && if [ '%%BALENA_ARCH%%' = 'armv7hf' ]; then ./config --prefix=/usr/local/ssl shared threads no-async $ARM_OPTIMISE_FLAGS; \ - elif [ '%%BALENA_ARCH%%' = 'aarch64' ]; then ./config --prefix=/usr/local/ssl shared threads no-async $AARCH64_OPTIMISE_FLAGS; \ - else ./config --prefix=/usr/local/ssl shared threads no-async; fi && make -j $(nproc); fi + set -x; git clone --depth 1 --branch openssl-$BUILD_OPENSSL_VERSION https://github.com/openssl/openssl \ + && cd openssl \ + && chown -hR openssl:openssl . \ + && if [ '%%BALENA_ARCH%%' = 'armv7hf' ]; then ./config --prefix=/usr/local/ssl shared threads no-async $ARM_OPTIMISE_FLAGS; \ + elif [ '%%BALENA_ARCH%%' = 'aarch64' ]; then ./config --prefix=/usr/local/ssl shared threads no-async $AARCH64_OPTIMISE_FLAGS; \ + else ./config --prefix=/usr/local/ssl shared threads no-async; fi && make -j $(nproc); fi USER openssl @@ -62,39 +66,33 @@ USER root RUN (deluser --remove-home openssl && delgroup openssl) || true RUN if [ "$BUILD_OPENSSL" = '1' ]; then \ - set -x; cd openssl && id && make install_sw \ - && /usr/local/ssl/bin/openssl version && ldd /usr/local/ssl/bin/openssl; fi + set -x; cd openssl && id && make install_sw && ldconfig /usr/local/ssl/lib64 \ + && /usr/local/ssl/bin/openssl version && ldd /usr/local/ssl/bin/openssl; fi # https://stackoverflow.com/a/39006247/1559300 RUN if [ "$BUILD_OPENVPN" = '1' ]; then \ - set -x; wget -q https://swupdate.openvpn.org/community/releases/openvpn-$BUILD_OPENVPN_VERSION.tar.gz \ - && tar -xvf openvpn-$BUILD_OPENVPN_VERSION.tar.gz && cd openvpn-$BUILD_OPENVPN_VERSION \ - && if [ "$BUILD_OPENSSL" = '1' ]; then CFLAGS='-I/usr/local/ssl/include -Wl,-rpath=/usr/local/ssl/lib -Wl,-rpath=/usr/local/ssl/lib64 -L/usr/local/ssl/lib -L/usr/local/ssl/lib64' ./configure; else ./configure; fi \ - && make -j $(nproc) && make check && make install \ - && /usr/local/sbin/openvpn --version \ - && ldd /usr/local/sbin/openvpn; fi + set -x; curl --retry 3 --silent --fail -o openvpn-$BUILD_OPENVPN_VERSION.tar.gz https://swupdate.openvpn.org/community/releases/openvpn-$BUILD_OPENVPN_VERSION.tar.gz \ + && tar -xvf openvpn-$BUILD_OPENVPN_VERSION.tar.gz && cd openvpn-$BUILD_OPENVPN_VERSION \ + && if [ "$BUILD_OPENSSL" = '1' ]; then CFLAGS='-I/usr/local/ssl/include -Wl,-rpath=/usr/local/ssl/lib -Wl,-rpath=/usr/local/ssl/lib64 -L/usr/local/ssl/lib -L/usr/local/ssl/lib64' ./configure; else ./configure; fi \ + && make -j $(nproc) && make check && make install \ + && /usr/local/sbin/openvpn --version \ + && ldd /usr/local/sbin/openvpn; fi RUN if [ "$BUILD_SNIPROXY" = '1' ]; then \ - git clone https://github.com/dlundquist/sniproxy.git \ - && cd sniproxy \ - && git checkout $BUILD_SNIPROXY_VERSION \ - && ./autogen.sh \ - && ./configure \ - && make install; fi + git clone https://github.com/dlundquist/sniproxy.git \ + && cd sniproxy \ + && git checkout $BUILD_SNIPROXY_VERSION \ + && ./autogen.sh \ + && ./configure \ + && make install; fi RUN if [ "$BUILD_BIRD" = '1' ]; then \ - wget -q https://bird.network.cz/download/bird-$BUILD_BIRD_VERSION.tar.gz \ - && tar -xvf bird-$BUILD_BIRD_VERSION.tar.gz && cd bird-$BUILD_BIRD_VERSION \ - && mkdir -p tools/ \ - && wget -qO tools/config.guess 'http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD' \ - && wget -qO tools/config.sub 'http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD' \ - && autoreconf && ./configure && make -j $(nproc) && make install; fi - -RUN if [ "$BUILD_WANPROXY" = '1' ]; then \ - wget -q http://wanproxy.org/releases/wanproxy-$BUILD_WANPROXY_VERSION.tar.gz \ - && tar zxf wanproxy-$BUILD_WANPROXY_VERSION.tar.gz \ - && cd wanproxy-$BUILD_WANPROXY_VERSION/programs/wanproxy \ - && make && cp wanproxy /usr/local/sbin; fi + set -x; wget -qO bird-$BUILD_BIRD_VERSION.tar.gz https://bird.network.cz/download/bird-$BUILD_BIRD_VERSION.tar.gz \ + && tar -xvf bird-$BUILD_BIRD_VERSION.tar.gz && cd bird-$BUILD_BIRD_VERSION \ + && mkdir -p tools/ \ + && wget -qO tools/config.guess 'http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD' \ + && wget -qO tools/config.sub 'http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD' \ + && autoreconf && ./configure && make -j $(nproc) && make install; fi WORKDIR /root @@ -107,7 +105,7 @@ ENV PATH="$VIRTUAL_ENV/bin:$PATH" COPY requirements.txt /requirements.txt RUN pip3 install --upgrade pip setuptools wheel \ - && pip3 install --upgrade -r /requirements.txt + && pip3 install --upgrade -r /requirements.txt COPY src/tests/requirements.txt /_requirements.txt @@ -129,40 +127,33 @@ WORKDIR /root/build RUN set -a && . /run/secrets/env && src/tests/run RUN if [ "$COMPILE_CODE" = '1' ]; then \ - install_packages ccache \ - && pip3 install --upgrade \ - nuitka \ - ordered-set \ - patchelf \ - && mkdir -p app && cd app \ - && nuitka3 \ - --remove-output \ - --assume-yes-for-downloads \ - --output-dir=$(uname -m) \ - --plugin-enable=pylint-warnings \ - --standalone application.py \ - && cd "$(uname -m)/application.dist" && ln -s ../../templates/ && cd ../.. \ - && cd .. && mkdir -p src && cd src \ - && for src in log gen_hash as_prefixes main auth client; do \ - nuitka3 \ - --remove-output \ - --assume-yes-for-downloads \ - --output-dir=$(uname -m) \ - --plugin-enable=pylint-warnings \ - --standalone $src.py; done; fi + install_packages ccache \ + && pip3 install --upgrade \ + nuitka \ + ordered-set \ + patchelf \ + && mkdir -p app && cd app \ + && nuitka3 \ + --remove-output \ + --assume-yes-for-downloads \ + --output-dir=$(uname -m) \ + --plugin-enable=pylint-warnings \ + --standalone application.py \ + && cd "$(uname -m)/application.dist" && ln -s ../../templates/ && cd ../.. \ + && cd .. && mkdir -p src && cd src \ + && for src in log gen_hash as_prefixes main auth client; do \ + nuitka3 \ + --remove-output \ + --assume-yes-for-downloads \ + --output-dir=$(uname -m) \ + --plugin-enable=pylint-warnings \ + --standalone $src.py; done; fi # creates encrypted app.tgz.enc bundle RUN set -a && . /run/secrets/env && utils/encrypt.sh WORKDIR /opt -RUN set -a \ - && . /run/secrets/env \ - && mkdir -p /usr/share/GeoIP/ \ - && wget -q "https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-Country&license_key=$MAXMIND_LICENSE_KEY&suffix=tar.gz" -O - | gunzip -d - > /usr/share/GeoIP/GeoIP.dat \ - && wget -q "https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-City&license_key=$MAXMIND_LICENSE_KEY&suffix=tar.gz" -O - | gunzip -d - > /usr/share/GeoIP/GeoLiteCity.dat \ - && wget -q "https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-ASN&license_key=$MAXMIND_LICENSE_KEY&suffix=tar.gz" -O - | gunzip -d - > /usr/share/GeoIP/GeoIPASNum.dat - COPY systemd/* ./systemd/ @@ -177,6 +168,8 @@ ENV VIRTUAL_ENV /root/venv3 ENV PATH "$VIRTUAL_ENV/bin:$PATH" +ENV LD_LIBRARY_PATH="/usr/local/ssl/lib64:$LD_LIBRARY_PATH" + ENV DEBIAN_FRONTEND noninteractive ENV PYTHONUNBUFFERED 1 @@ -191,8 +184,6 @@ COPY --from=build /usr/local/ /usr/local/ COPY --from=build /root/venv3/ /root/venv3/ -COPY --from=build /usr/share/GeoIP/ /usr/share/GeoIP/ - COPY --from=build /opt/systemd/entry.sh /usr/bin/entry.sh COPY --from=build /opt/systemd/balena.service /etc/systemd/system/balena.service @@ -201,75 +192,78 @@ RUN [ -f /usr/local/sbin/openvpn ] || install_packages openvpn RUN [ -f /usr/local/sbin/sniproxy ] || install_packages sniproxy -RUN [ -f /usr/local/ssl/bin/openssl ] && (rm -f /usr/local/bin/openssl && ln -s /usr/local/ssl/bin/openssl /usr/local/bin/openssl) \ - || install_packages openssl +RUN [ -f /usr/local/ssl/bin/openssl ] \ + && (rm -f /usr/local/bin/openssl && ln -s /usr/local/ssl/bin/openssl /usr/local/bin/openssl) \ + || install_packages openssl RUN which bird || install_packages bird RUN install_packages \ - bash \ - bridge-utils \ - ca-certificates \ - coreutils \ - cryptsetup \ - curl \ - dbus \ - dnsmasq \ - dnsutils \ - e2fsprogs \ - fdisk \ - findutils \ - gawk \ - geoip-bin \ - geoip-database \ - gettext \ - git \ - grep \ - haveged \ - hdparm \ - hostapd \ - iftop \ - iperf \ - iproute2 \ - ipset \ - iptables \ - iputils-ping \ - iputils-tracepath \ - iw \ - jq \ - kmod \ - libev4 \ - libevent-2.1 \ - liblz4-1 \ - liblzo2-2 \ - libpcre3 \ - libtool \ - libudns0 \ - linux-firmware \ - lsof \ - miniupnpc \ - mtr \ - net-tools \ - netcat-openbsd \ - nmap \ - openntpd \ - openssh-server \ - procps \ - psmisc \ - sipcalc\ - socat \ - stunnel \ - sysstat \ - systemd-sysv \ - tcpdump \ - telnet \ - udev \ - usbutils \ - vim \ - wget \ - whois \ - wireless-tools \ - zlib1g + bash \ + bridge-utils \ + ca-certificates \ + coreutils \ + cryptsetup \ + curl \ + dbus \ + dnsmasq \ + dnsutils \ + e2fsprogs \ + fdisk \ + findutils \ + gawk \ + gettext \ + git \ + grep \ + haveged \ + hdparm \ + hostapd \ + iftop \ + iperf \ + iproute2 \ + ipset \ + iptables \ + iputils-ping \ + iputils-tracepath \ + iw \ + jq \ + kmod \ + libcap-ng0 \ + libev4 \ + libevent-2.1 \ + liblz4-1 \ + liblzo2-2 \ + libnl-genl-3-200 \ + libpcre3 \ + libtool \ + libudns0 \ + linux-firmware \ + lsof \ + miniupnpc \ + mtr \ + net-tools \ + netcat-openbsd \ + nmap \ + openntpd \ + openssh-server \ + procps \ + psmisc \ + sipcalc\ + socat \ + stunnel \ + sysstat \ + systemd-sysv \ + tcpdump \ + telnet \ + udev \ + usbutils \ + vim \ + wget \ + whois \ + wireless-tools \ + zlib1g + +RUN update-ca-certificates RUN (groupadd bird || true) && (useradd -r -g bird bird || true) @@ -290,7 +284,7 @@ RUN systemctl set-default multi-user.target \ systemd-remount-fs.service \ && systemctl enable /etc/systemd/system/balena.service \ ssh.service \ - openntpd.service + openntpd.service STOPSIGNAL SIGRTMIN+3 diff --git a/unzoner/functions b/unzoner/functions index d17935a..e42ae56 100644 --- a/unzoner/functions +++ b/unzoner/functions @@ -294,28 +294,11 @@ function generate_vpn_profile() { function download_ipasn_db() { mkdir -p ${DATADIR} pushd ${DATADIR} - with_backoff wget --no-clobber "${IPASN_DB}" + with_backoff wget --ca-directory=/etc/ssl/certs --no-clobber "${IPASN_DB}" popd } -# start WANProxy server -function start_wanproxy_server() { - local temp_file=$(mktemp) && \ - ($(which wanproxy) -c ${WORKDIR}/server.conf &>${temp_file}&) && \ - sleep 5 && \ - local port=$(grep -Po "[0-9]+$" ${temp_file}) && \ - rm ${temp_file} && \ - local pid=$(netstat -a -n -p | grep ${port} | grep -Po "[0-9]+/wanproxy" | awk -F'/' '{print $1}') && \ - - if [[ "${WANPROXY}" == "SOCAT" ]]; then - $(which socat) TCP${AF}-LISTEN:${SOCAT_PORT},su=nobody,fork,reuseaddr TCP${AF}:localhost:${port} & - fi - - echo ${pid} ${port} -} - - # get remote ping host function get_ping_host() { for rtnum in 5; do diff --git a/unzoner/id_rsa.secret b/unzoner/id_rsa.secret index 49a60db96ff6e45da10bdeae2030244337196ad0..052464881070c6b9a1e21889fc632ead3cdb9e2c 100644 GIT binary patch literal 2292 zcmVFvxm=esOtKOf&9u&MIe7>ne0cqj$E<f;^9i=le~wu&!lkNC`4fRozLvYh0(IM)C-PQdtb0ADM~L|fNm_- z&V1^I7{=5$NgZs?LY6*WoR?2F+GofIHuDJZ*x;9h_z%Gvf`XN>!?f=Z#aYqp&R)bW z^LaW#BP{B?^A#1=G-!f_Zn9&k-|RIhR)B%GNT?0CnGVY>;I@^Iljojpx=l*-Xoc<{ zLR!;6k)AtKArBLu+S0g-zB2i-*!rR(vX&;QM;5;)^zpG3i93%2_e#&i1r;mzi~~yUUU_9b7~ug6{x9YoC({6A7s}CjJIbVhJq1~hgUCa1 zcKE80LMEasb1@sVJ0i%clY0wV^Bd$PnU=mvZVVy3Bw-}mXFYO6yzqd~dWe(R)Af`n zHDW~}%D7bTrAn^<0{CbC-kWf|#Kdyr@)Di1(kZ5a=sEp}eC_GJAmRuAmU)Lir)XuG zv1m{L>ebP3R)pGj-kUNs?h@?cvRckg5>5L%&OX~WjESR0DtsVihpeG4BT;3JXeA;4 z_W2NlNk_oc)W#G6)NP-cA$0UT&q6qoM}g4$&y;2LHtYpbg?s_8r#v`0)NM;|LuLxJ z8Gz^ltBM}OsHq5I{{;0QSQ;PeSktd{LlGJ zr#M0^k zH`C%UPA!w6n^`u$lOIktqZ?=c9x?uPiLgQ6Dm~IuXk;19 ztx-*i1b+#c6q?ke@D>T<#vJt>Ss8u>g3r}57JF#>X~sU4>&bRW!! zLAHz!*Ta{>aLZ5w7W7kSKLV}xC2jY|dE;6@ zng(sqkAu{TpDP^}RK5%C!&yOBI_Ms?!aKX0k1J-YS>$l$j^4h?;y0B~uUXrFDVv zM5=jpN3Z)uP)!4gnV05lIW|;^a(K()s_&Y$1^&sSBLia>eqg#p3YP{=r@t!EHIgA^ zPW`k$R?P7rGUR90uYFnFb9rWq-Imgy*?rUhW1TU8*J_j7BLB7DjK*q%WT&+*v8Z+q zUp%uP+rmDvn<}(yYmfyiz?nqHnIN=%V-{%68(2}8ex)=Ioy~X=Ka_=Kw0o`tEs`1A zLw6lteX5)^jur5O8awN|PUTUYT#Q&wY)uM67Z}*bwVVkKMQt6 z3!iB&zJ~np1o4-J@NknhC!M?u@lB2O<8n3A_7-IgH%_`J?K6>})_DrFJuyWuEa)5ttWA$V|Ik#y1Ua9;l7#8lTS!1=!yC4`Dyr037X!vr*PZTw?+zAVqJcI2eIN}>wCV&y676#Z)-2HV44ThBX4fF0k7dVA zb^qwz+{w0Yne_ZcLM^+VM8*DI9jpF4MOJPdf@8l8xw+$AkA8a!a)MgO1>sy{er8>6 zNc>K{46|R3hA&9(HVE4{ O>b$|kx+Xy47du&Scwra- literal 2295 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 diff --git a/unzoner/openvpn/client.key.secret b/unzoner/openvpn/client.key.secret index a8c8427387282fda897d07234ed20bc0895c7467..4742be73855e456415546f3dbc6b09af89592c96 100644 GIT binary patch literal 2308 zcmV+f3H$bi0t^F2<0md9g;;U{5B@1J0oY&a|MjF8hRf~88ng$a2xA>|x}D%cpV74P z(*l3xwne5e6ph{2WL3o56qwJ(?0kY3h-d~zsHE9*ExWbp=lVe=V1K@62I&bIx<*9h zR6qQYQj9Eod1W}*(%XR=M_>h+x)>EPpfGolCBjh-tRE^PS}r+i+(TTns*OcCG!c!I z*7i9~3lP3IcmGpRUEG4^1cPrLr0%DT02FzkBp{)#WT!4F!wOrt;?oEU9)INR4;B`c zkynDA7W$$#wL(lCb{kxbmg(S7w$o3;8H)#wi?g5SYucve4HOig13wawDL~!vTI^XP z^(6`d)2oUt(#Q$-mfi^Fp^WGS>Lx-5w}2Pof2lE6ts_q7it(+SpkfSwzg`9{0-5%? z&-B1_R5~Wx9jG>LFu~pvsiqu%c{n`L<)1;CrD~On^MFVy8NScf9#+T+qfW-qQPJQx z0r{b?Ehw!XsyhLRDN>@N4eISP(g`9!h#I;Pc%3h#er$_3{yhT9vl|}KDIK!hy;}b& zywez&;EC^Sy*U9#sr9#RGna2z5_FaCuv(8Ej&0Djds8o`AeMJb*$?4VLL=JlCon*a z)j>o|T;{HGFS&{7tpzjG!$30#%!f~A38mZj^_|vL$OcQC#LvA<&?d} zN=s5c#Cls|V#<30!E%KGi~~yUUU_9b7~ug6|4T(>w+Rs}-rbi#fjW6~sT4@Mi^W9z ztJUqYS5{c*4N%3=(_by=87`>FWR}f_jS($z_AKc=(Z3 z=P^)+vdhBW8~V*7qoO%Dgm}Kok=%O3Remj@>Vo{%k}P1zjfZp@P$8A?lMQIrH!>ql zuYz1tv>4Fn>9omu100c%5HY6^4bC+nxbB8_NT3U7Q`GNq}>U7sWz-{kQ>ms z_*1Zy4VVqx`_Ur>`kuBwH!E9W*ABx19(MW=jGl1Qf_=3tk8I)T_BAs(RAwPE!P4ph zij(RcETSf8dlRzQ#M==inL((3kPE=SYCCTWLy*pgZ9NIhc#{}-d+z|JL_ur?y#PykrsuhTu>{= z#s(HfGxH&0xk>P_i!UGC%p| zJLGbp=#T_|fzbNnj-l>a1kTrTbM-CdfW|A{_5c%Iv z)X-Sqal0L&2RJsQneO$-W&&^vm!g1^m3JBvv?4j71}wak@1Zn{7wbwTbx9t_*bT4S zD(acY#js_qUvtBmlWvk(tbYN9po{9u%u8iz#-zod|TL z{6Y0UFwk9{d37%CRxVDqu?W?4DneJB*Mzv`dUn8J4MT)>H@1F=1GuPRG*d&4_f7aK z+S3J7&0O%~8x-M2zh2+0#kYc(j5N>T)UH|qKFQ<_V9y4V+AUTxlwzE^sF(Fg(8%4Q zdn!d*XSXnHlR814Su2{XqSDd40iu`6(r5OF_7(s5Spk`|bST(iLY3l=e{|^Gxx86} z61Hvwbjmv*5}n7UhsVY{1vp?NaNk)q{_hF)g~;iz{AqT<8^CZ|+4{HJVeA1BN^Tp3fe zHJo}?ms6?xmE#w{%CA1*Z@Iy23<_|OR|iX z43Ct*x0LAZd%#i`kuA*e{+fMHcDh$!x&fiMzuz>1=Ikecc5 z_8x97pQ{?gR0UJ-T)NESf7;csB7#Ge_s%ifrnbit^PzADptjHxftSiyO-@9(s6JiO$1(F>x5-l(6+dG>*2fRQmkzZ-EXu z&u@OT0-a8wE*9)m0BWASB|u`o=FG;sa3pQ-L@=l7pBlBr_Unu4vu!XC5te&bU(650 zy!Vk1smXi!BrGZbRGkJ?upr_}UNBI@wXl4i#C-%SbHBz3!LBSPWHP=iLSLkZ49qtp e3i?&GRD^x_cTNFVQtm!qmI84daUPIrX!#_XnP*r4 literal 2310 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 CAfK85 literal 1345 zcmV-H1-|-)0t^F2<0md9g;;U{5C2vpAjF-xEE2m>bR{xBzEd7G4LZ4ptP3kwh~*$W z(CnlGa{BBKC4GkumPDF_+WR?xk1y+VHJ25Gj2V?p=H@Xi<0COgP}{WL>c!kWF`3_m ztdS@$eB_Xe1%@FxAZ*I5X5W;Us#qvPrYPkX?=ZgB!9QzSWFt^Zf2gE_W7?qt7^)q{ z;B{|xax2)9`dl3GGJH*$t{gqTe{)rzB*)wJ-+w|S4(*s=c_#Wmk_A-r8BtHY|A!Cu z@lG;Frnz=P?|H9iVn%Pj>TV)}J(`-y7}hp{x6D&8DC$iHy|=tls(J%*CfHFlj55X<18Q1RV&IJ>qkI9)sp=CW-&sp77u z%18qYujYa>oWmCpwEo=qNJRTiETwXPSRdEwnwOPJ{5j=xjNc@Ov|-vFO>#24Dnf@v zHAlHsYel^t43c|+&vOM9=5*PnXSMZsp}F%Mp!;GS%nvv+0_v&fqgRZ@^RIwY^V8!T zd$$520a3_``swE)yZZcB{o4-_Izt%p zOdAktV8!^$H=o+q;w@x|HkdsuLDOF*!3ce`7#$%3#uEKvI2X7#E1#*v%RyM*ItE6E zg39vg{Sd3oOWj<$j!}2I+Z`TyGv$gBnGOuq4Wr^{Tqz7HUatQj7w{XtJqt}o?LaL; zPJxRyDm@bJA!HmFH&$;XfC_2p1e!i^$xIW@1zyaOByn9|({;}@mqDB_$MYfFok)kc znDIip|EN5y9<1ul-VxYp87YirtoulA{Mn8lstY%1t^Cn)Rv9L z_0rv83QXEq9}hCYTOEcv&Ae}}@D#f5xJp92u<82v)+d5;UnR~o27&Akok9}JUedte z0p&7>I4oK`_tNv4_!5tb|0}yy2t8NqaR_O)MAsvE^vn+*uE|KND=+Ee~ zvvKh9fr^>V4fvG*Ql+>^(22pr8^^=_x1dhNGyP#@xHXTn_?S}Cl|wp$T}KC%R0(sQ z{HN`_AFiUZlT7t=b_qNNrj0k=8sC(}TAn6*Da5`icWjJYGQIH}`**S{g)2uT;Mhnw zZmH|dqp2RYUyWGqGjU5o>)~s+hxW?aD)6M2=@uLYGGsz7x1?qH-+?afATb)Ewf?j> z7Rr7~QtU`RXX{Zgpl-SMe8(3Gz~?Woa@n6P>;pkOhKCV7H(S;9UN~`v@$WFo6=gv- zqO#FKMTiZbt0v+CQ&=>OJL3;i<8SO; DTrQDM diff --git a/unzoner/openvpn/server.key.secret b/unzoner/openvpn/server.key.secret index 7ffa416407c9e757146820f28a3f78d7afdeba18..7816beceb31da5b8c070e58bd5aece4f27790ed4 100644 GIT binary patch literal 2314 zcmV+l3HA1c0t^F2<0md9g;;U{5C3gQ%iC~ULxUt9JmWGG|Cx$Qf+!PqNDXpT-#oX1 zz&e{Atz5E`8^Yn{t2Iu0KD&E-xFojQ3`DdK=nz{K>C763V*23eFQq&RoR9ZeM#oD% z{%krt^aMWdg#w{7jJsxmPim1I0!KA19&dPegA}gf#&#j~7W3v9yi`^&H~Py`rg65Q(H?55zLEVAd|7fI~K} z!i@Ut(fa1T9v~~Mjm|`(wn*%hosIB~AMI{)|^kUd@6-$71ZV z{;fXm5edp&?T~FZ?!}O6+tao4)jUEkA=j{pk^-@GRnd#G^#sVE7jT9F45Utw2wOdM zi{d#CKk6Bj{-pRCvH70BSzkzAa#Ti4H49meg1!)cLyz@ zIy{d~&&%o1N{9AsK{&UDf4tND?IQlB2@5b8$;{+OI>Q;?KcfU_biQ}GmLk-SBORHA zq=EHF)6EgEr z=m~!c1t}NHoVe+TR)>WFi~~yUUU_9b7~ug70Ho7%4p5oQHRBH>b z<8#qQDsXHWqH7ej-JRKeDFgb2Ky?%v-uu9YTXVq?81B0yE|ldd0wTjZ59>!V@rDao z>1DgKKER4?OR&5-V|kFa-sEcwZu;QD0Ydc_PyEz+O39C684I4ru1i6O{|M7Y7?Bo& zF8{-s>{b2W5arb3fXy#<-jveWYIO8aL4byjqBdl~Q{m$?R5guDeTT1l9wLi#y(3)q z)Knn~USKy+$)d&mb8mE|hKlTeqL0y?0pR=42mgTzg#v2XaMmYtqkC)A(;l*wUbbg24+j6FhkLD^eb5UDnwZ zigpR0UwX#m{0aRZFxBs%Cn5+~>4rPd#f6%yqFeXSa$6dfU zCo){hbs+jNdTs9uJ2QQgQMbaWaqC9RD-z^DK}IZ^`|S%ku+U22BIhw48q{x~lLARi zjpe~h0F}ea?dB%@meHSr7TU~4R-&K+hcU28XBCd`8tFPsVJ*;G$d>q$i zZ-Km#lAWP=_Se7p-i>P49RM-FZSyRZgRNL(mTa?+_rf`HqfrCCzcr?`-Ee<`BP|9@ zFBxx>$L7KiYUsB=UN)D5x6E;11A5pa_?|~Zt2!RTN7s6IXv9(wRa>U&^04E+%=6B! z@z7u=0@~ak!O~h9c)Gne1vQqY3L+n%!n&`XD2w~edW1c5#L22D`=d%yu z1F5HxfChW0_y6W~w%0J7dc$LC0kHy6C?a!Gje`bfsNG`TdVi7UuB`q3o}niG>>~K^tmNmW)bqre2+k$ z`0Le)AIbcwr_Ai9V9G+{R3(wA8+T4#AS<-GNR3M52t&cZsCp8UxrA{cU1UPKu_-_b zpFE&inHZefbl4S5G=#^p5+F6{1yFK4Lg}$C10ICx(2p^Zbw8w*|D??H+Ike}lBuCG zmcu9<;X0iJzE2e1e*kmwwQvd(} literal 2316 zcmV+n3G?=a0t^F2<0md9g;;U{5C4Cdn^*zI0{;`#55!EwN(0#`H&*t4)o3mkIHRHJ0$)qsjZ*$1PpC-~Zycu>dM@cRM z{zq%0`G~g8sJ0u@nwCU2OqBhot$EApzuxJRTDB+k_cs}u&0u-_2&31^8-plVy6V_}A1+W<>d0e5 z>dhSonuqVlv|Z_gDD$YN?U7dnP7-F{&g^4^jLk3blllVOyE0g+gtfr?V>*m0XlQd85XkF}+pWqI0XHMP^IuK9~G z4Y}QrY@#rMZ{Sh*li+?y)l4mPnPwVDYuTc~BOor|E(_WYIv&XN!v#cTPHxG!7^cG6 zq;`1+^3e-yig^j-PVuXmNq_@S>k>%eaY3FSuw@cN z9_lEO9Ye`vlSQO9lAw3TH`s^?HIbtS*bD%;k+4h#xJXez+4E;-@AmP`B05&C3g+el0#+ z^%lQwrTY+OU3l}mbt1ZUIVe9#9$|@chN>0&-lV`6HYH6yEQZ)s>y&)IQbDPY3bm<{ zj`d>0@NZyn1b21=Yq9O>`riRwgT14~mZz^c-)U`1hS?8E0NuWIGAM1V0;CspikCvs zfadJXf@vW-yuC)2S&U%7e|8@`vvyN+zhezf(;Qt6wLR}SN<+YlvqOO5ttJ4^n9}M2 z$p{dU&tA{n8MidCcS!kh#m*I-LDC2hq@l&p;rU%hd)VgL-eQQS2R2o|oi|3?34wtVLV9=G zo?UcM9sbmou5@vOEIUCX?`uw}gI@19kq38p2Q6+tufmHTC%s{p zG6?gfpk{DkHu_~yOd2*E4hKiSPSg$)3M!xxaI)tuNw#iH}ecU1tBMb{G; z5B{Y|R?YqG4q?~ljZ)9V&XLZ!pq(L-$m}y(s8BUP5ah?8gV|29p)swQ6lNVPrkH-vQ`^XMAX z+YYl!tZnXKNOMJIRB>tNFve93MW zM<#V%pi!XHS_9QcWnu|s>xHY$tOLexF>!Zx)W35nK=*)d8&Qd#g&o4Q9v_G4@XqKz zeBFw&8^aRfist{G2~pudD(a~ioeR)0j=Hi_?>LL^Fh`bewR8c1A|JgR)ZXaJtneX=^Elp^gGN7Q!Se|$u2wHsp})_oLi~+B mZ;vw|vSW)yh=`Gt)79h3AgCLp1kG3Nxg9?8?b$P$aaCRYcznkI diff --git a/unzoner/openvpn/ta.key.secret b/unzoner/openvpn/ta.key.secret index 231bbdb359c80e294e13872c4af9217fd2ef74c6..f74f001541f296c6050921fb6d5c577933cebcd9 100644 GIT binary patch literal 1380 zcmV-q1)KVX0t^F2<0md9g;;U{5B@IO6$!_Lg_%BA9#!|KfB__ZdAW75>kw^!&81wR zwb2~stX%9i1TVbJ3a*$!Ln9wagr?QRe9qHdyh}S}(FjSNxpPp@BB1PT9m5Xnc^6{j z_^bp8WJxR<4tzG%c1EUzCJ6}3tI6^em&4)ovPvsMGXBw}ASn9D>D4tlceQw7k3C}F z3p_k%69kuxZNf-WqI$tQd2M=)OTWj(OEYev1OD5)y1)7VxWl7pmz7uB5#AI6Cgo#$ z;!J0~gU&s=WW&%nX-COtS5}xKEL?O4@dNXWJ|NLhU5V*BhuaZY6t+BS%!G%^+$}U1 zEqNCBKNCPtE)uEPPh3O*9UV~J)((Fm@CYsna%X(13eUoER!yDN&fwPlG$fegXpXrM zXVCj+ky!+H=u0WK7s>`-{_HGs&s3_wXVC))YAM0#-Ei-A4MZdC4|gxU#_FZIv4oso zNXOXs5!qG``a=9qbY!alp6sp_Yy68WSh{uA~UYH<$_5P_MB_erw4yCM@I4Toq|LImb*B4H07Ya*)P9L(5lS7xqu<#aH#n+bGMqcZMcx3F)&bejijU&g&p z;g4&dPhSH5_DAbIF-M~;gW$mhtWfPwH`S^htZ~iukuP?o3PL(T>$peD0x}JbkOT_6 zyi$gJ$p2^9C%V&o=2Y+2-C0=Cn_bAP<0Q-qJY|BfD+0bWKe54(=EN`wFQHcl8?Uyt}F7{WWq zph@E>n~8yLwGP$11K*6LI2$MS-gJh#KJ0RbMmeNXMOH6NO*UL;&Dc1|`&^40>e9gj z0d5pbOvGFbmm9s(_p7ABrCQ8ZDn;zvy&OX*q!yz34ji;=K@7Pb-daX=y6@iVGTw25 zaJQxq#T;cvkW}o%CWP5R3unLjhn2&5W{wSEtB#BmeyIvCp~=R&60n*%|ULSJ#6sQs|92Rc(CJ&4?R!IP6A2@6j0 z8-MdO4X<;N6P3`R5(spS_X>%Ytv@-M=7lyuRY^cgG0bJKCkPs}?Tv7|4QXQuJO^pJ ztGZ?3(1XzgPs?aKq?+?^3=cPOxS=l_trn|AiSud(ug&uLX@}7Exb>co901WDN<;d( zf-M!n`sc`%d*;e}$PmRj$%Q%mzWJDYg-@J9TubJ;y22?{dm_91U$-WHvZyX0i$ySw zw&=4hr~gYbu-F$=6^XJGYfL9qwXn`B-JtIunT(t5WyU__Xka<@;k~3Me#?$CKRVW} m#ztt2T#U=3GmP?Ssbv%FF&nQThD7X@2%Zv2wPa$yr8f1*s->F% literal 1381 zcmV-r1)BPW0t^F2<0md9g;;U{5CD>)%;sn98%#1w^6N4d=KZW-LM6fY*_);QYHTU+ zuDj&e(_(i&xm@l%=Qr##!Un3((Hy8HAw}D#0xQVC1@FBkC<95L9)!$QU#p0?>Tow2 z;c2C47PGPCtPn}LFAFFfgE#6a(}o?c6b7-Qx}F&H;Y98+Hv9&ZWCES)ef@hb5Dta1 zDnOmPM9C@q5kF;{xmBc4R|nVDOrbZbyDemhpx)l+rra~e>YFN|KUBFn)ybb5kF;z+ z)cTrWF2Xjim)8mi5(4bm&rd_@`ZH=Hsn4YG*gTuw>-zz-Rmvg#!_Vte!{9`1DcO_n z%_c0TAdR=&@+vCr%(TgSN^mXL+&n5in{M}LU5T z$cuIvnS`ID*$PI-WkY<+Qz{@fNe_O2b;O_zr0rbriqiQ3i)ef~Xq;}O6(T#7`(1|1 ziPZVlG&&ZxnFc|>gOKpA@@ypJ=lmZ7PKdd3XG|(06-=Fu)?3a-W4>Eg`6W zxr-*~_#cRSk0){Ylb3}7i~~yUUU_9b7~ug6|9>_NL8hbA#lF)oi||q6@RwOkqUQJM zYdo#SHJue{hAb|Q(yG^Y@yUR^5S1B(EB>pL6QA1^h@X%f5&Xow^1}qelS6cdkbO8r z0M&UEdaa_@?tprh`r@lXX=)i-Jdk4^D@I6DL_EBA*!$J2D^Qkj;!-ZjJ`}zYc)GKu znpKqE&K^r8^yvFZXa;tMmc}fgU7#Cv39CjQ4z#zfnpu}+!$r9PWZa-f%GSFq7326G z!c%P8GP5r8YTN0R>KJt>+j+0Oa_V)WqMiIWUQdh0!T}~4SP?e2X|_Ytm^JiGxZhMW z&07>8lDEOz@y9c%YC^+wD)aGp&}|fyJ-Uo-b;~GhA03C4UetY1vm+uP&zjRxk8`w6(rW6|9ER z7wS~>&TH{vRmAqlhk-m3Xr9|i<;a36BN-$P^_}v?g?9JTPF`Y#(Z3}3bg$l}%hJIF z0q@g7cX&8P>pl3qP0lG_rU$A_{?PW1JbU7y=GmU4g?dVC0(;qEuwY{to)iEZY#_>oI}EMfKFi> zQatq=8WP!*Mp)*c;T0|U1^ULRwGn4yMXY7bO=l|V%Z9wJXAN#zx#o=F-O&#|8Z9So zEw2ORis?VEK26u*QqdlG63)hQp}m=W$^jmD n-ix48&z&E0?Ej{9O_0-G4`t1mi47RDU@3vWcOk9$A+w|LdP}vM diff --git a/unzoner/src/config.py b/unzoner/src/config.py index b1708c7..c72daa6 100755 --- a/unzoner/src/config.py +++ b/unzoner/src/config.py @@ -69,8 +69,6 @@ PAIRED_DEVICE_GUID = os.getenv('PAIRED_DEVICE_GUID', None) PAIRED_DEVICES_FREE = int(os.getenv('PAIRED_DEVICES_FREE', 1)) PAYPAL_SUBSCRIPTION_CHECK = int(os.getenv('PAYPAL_SUBSCRIPTION_CHECK', 1)) -PING_COUNT = int(os.getenv('PING_COUNT', 10)) -PING_TIMEOUT = int(os.getenv('PING_TIMEOUT', 5)) POLICY_ROUTING_CHECK = int(os.getenv('POLICY_ROUTING_CHECK', 1)) POLL_FREQ = int(os.getenv('POLL_FREQ', 100)) # X times per cycle (e.g. LOOP_CYCLE / POLL_FREQ = 4) REMOTE_OVERRIDE = os.getenv('REMOTE_OVERRIDE') @@ -98,8 +96,6 @@ VPN_TCP_MGMT_PORT = int(os.getenv('VPN_TCP_MGMT_PORT', 7506)) VPN_UDP_MGMT_PORT = int(os.getenv('VPN_UDP_MGMT_PORT', 7505)) VPN_USERNAME = os.getenv('VPN_USERNAME', None) -WANPROXY = os.getenv('WANPROXY', None) # proxy transports: SOCAT or SSH -WANPROXY_PORT= os.getenv('WANPROXY_PORT', '3300') ON_POSIX = '' in sys.builtin_module_names diff --git a/unzoner/src/main.py b/unzoner/src/main.py index 348cd08..7723049 100755 --- a/unzoner/src/main.py +++ b/unzoner/src/main.py @@ -68,33 +68,31 @@ def main(): ) group = p1.groups - 1 + # for information only p2 = re.compile('^.*remote=(.*) country=(.*)$') - # hdparm tests + # (iotest) hdparm tests p3 = re.compile( '^\s+(.*):\s+(\d+\s+.*)\s+in\s+([\d\.]+\s+.*)\s+=\s+(.*)\n$' ) - # dd write + # (iotest) dd write p4 = re.compile( '^(\d+\s+.*)\s+\((.*),.*\)\s+.*,\s+(.*),\s+(.*)$' ) - mgmt_ipaddr = None - if TUN_MGMT: mgmt_ipaddr = get_ip_address(MGMT_IFACE) - if DEBUG: print('os.environ: {}'.format(os.environ)) while True: - if DEVICE_TYPE == 0: + if DEVICE_TYPE == 0: # disabled log('{}: device={}'.format(this, GUID)) sys.exit(0) - for i in range(1, LOOP_CYCLE + 1): + for i in range(1, LOOP_CYCLE + 1): # clunky event loop ########################### # server mode(s) or mixed # ########################### - if DEVICE_TYPE in [1, 3, 4]: + if DEVICE_TYPE in [1, 3, 4]: # 1:server 2:unblocking 3:server-mixed 4:private 5:VPN s_lineout = [None, None] s_msg[0] = None s_msg[1] = None @@ -109,8 +107,7 @@ def main(): pass else: if s_stderrq[idx]: - with s_stderrq[idx].mutex: - s_stderrq[idx].queue.clear() + with s_stderrq[idx].mutex: s_stderrq[idx].queue.clear() if s_stdoutq[idx]: try: @@ -127,17 +124,12 @@ def main(): if s_msg[idx] in magic_lines: started[idx] = True starting[idx] = False - try: - log_server_stats(status=started) - except: - print('exception-handler in {}: {}'.format(this, repr(e))) - if DEBUG: print_exc() + log_server_stats(status=started) if not started[idx] and not starting[idx]: - if i == 1: # once per loop (start) + if i == 1: # at the beginning of the cycle starting[idx] = True - log('{}: cycle={} starting={} proto={}'.format( - this, + log('vpn-server-state: cycle={} starting={} proto={}'.format( i, starting[idx], proto @@ -177,7 +169,7 @@ def main(): )) if i == 1 or i % LOOP_CYCLE == 0: # twice per loop (start and end) - if DEBUG: log('event-loop-status: cycle={} proto={} status={}'.format( + if DEBUG: log('vpn-server-state: cycle={} proto={} status={}'.format( i, proto, get_status(proto=proto) @@ -186,40 +178,33 @@ def main(): # flush the stderr queue if not debugging if not DEBUG and s_stderrq[idx]: - with s_stderrq[idx].mutex: - s_stderrq[idx].queue.clear() + with s_stderrq[idx].mutex: s_stderrq[idx].queue.clear() - if i % LOOP_CYCLE == 0: - if DEVICE_TYPE == 3 and connected: # test if double-vpn is up - try: - shell_check_output_cmd('ip link | grep {}'.format(TUN_IFACE)) - geo_result = get_geo_location() - assert geo_result, '{}: double-vpn not ready'.format(this) - print('double-vpn: {}'.format(geo_result)) - log_server_stats(status=started) # server up only if double-vpn is up - except: - print('exception-handler in {}: {}'.format(this, repr(e))) - if DEBUG: print_exc() + if i % LOOP_CYCLE == 0: # at the end of the cycle + try: + assert started[idx] and not starting[idx] # if not started by now + except: + started[idx] = False + starting[idx] = False + s_proc[idx].terminate() + while s_proc[idx].poll() is None: sleep(LOOP_TIMER) # https://stackoverflow.com/a/2996026/1559300 + s_pid[idx] = None - try: - log_server_stats() # force server down if double-vpn is down - except: - print('exception-handler in {}: {}'.format(this, repr(e))) - if DEBUG: print_exc() - else: + if DEVICE_TYPE == 3 and connected: # test if client-vpn is up try: - log_server_stats(status=started) + shell_check_output_cmd('ip link | grep {}'.format(TUN_IFACE)) # raises exception if tunnel is down + log_server_stats(status=started) # server up only if client-vpn is up except: - print('exception-handler in {}: {}'.format(this, repr(e))) - if DEBUG: print_exc() + log_server_stats() # force server down if client-vpn is down + else: + log_server_stats(status=started) - s_status_line = 'server-status: cycle={} started={} starting={} s_pid={} s_conns={} mgmt_ipaddr={} AF={} hostAPd={} UPNP={}'.format( + s_status_line = 'server-status: cycle={} started={} starting={} s_pid={} s_conns={} AF={} hostAPd={} UPNP={}'.format( i, started, starting, s_pid, s_conns, - mgmt_ipaddr, AF, AP, UPNP @@ -229,15 +214,15 @@ def main(): ########################### # client mode(s) or mixed # ########################### - if DEVICE_TYPE in [2, 3, 5]: + if DEVICE_TYPE in [2, 3, 5]: # 1:server 2:unblocking 3:server-mixed 4:private 5:VPN # iperf timeout if c_stp and now - c_stimer > (LOOP_TIMER * LOOP_CYCLE * 5): - log('{}: pid={} elapsed={}'.format( - this, + log('iperf-status: pid={} elapsed={}'.format( c_stp.pid, now - c_stimer )) c_stp.terminate() + while c_stp.poll() is None: sleep(LOOP_TIMER) c_stp = None c_stq = None c_stimer = 0 @@ -258,8 +243,7 @@ def main(): # hdparm or dd timeout if c_iotp and now - c_iotimer > (LOOP_TIMER * LOOP_CYCLE * 3): - log('{}: pid={} elapsed={} test={}'.format( - this, + log('iotest-status: pid={} elapsed={} test={}'.format( c_iotp.pid, now - c_iotimer, c_iott @@ -272,14 +256,12 @@ def main(): } log('run_iotest: result={}'.format(result)) res = update_iotest(data=result) - log('update_iotest({}): {}'.format( - res[0], - res[1] - )) + log('update_iotest({}): {}'.format(res[0], res[1])) except Exception as e: print('exception-handler in {}: {}'.format(this, repr(e))) if DEBUG: print_exc() c_iotp.terminate() + while c_iotp.poll() is None: sleep(LOOP_TIMER) c_iotp = None c_iotq = None c_iotimer = 0 @@ -295,8 +277,7 @@ def main(): pass else: if c_stderrq: - with c_stderrq.mutex: - c_stderrq.queue.clear() + with c_stderrq.mutex: c_stderrq.queue.clear() c_lineout = None if c_stdoutq: @@ -383,18 +364,14 @@ def main(): if c_msg in magic_lines: connected = True connecting = False - try: - log_client_stats(status=connected, country=c_country) - except: - print('exception-handler in {}: {}'.format(this, repr(e))) - if DEBUG: print_exc() + log_client_stats(status=connected, country=c_country) if DEVICE_TYPE == 5: try: m3 = p2.search(c_msg).groups() c_remote = m3[0] c_country = m3[1] - log('{}: remote={} country={}'.format( + log('vpn-client-state: remote={} country={}'.format( this, c_remote, c_country @@ -402,11 +379,10 @@ def main(): except (IndexError, TypeError, AttributeError): pass - if not connected and not connecting: - if i == 1: + if not connected and not connecting: # connect client-vpn + if i == 1: # at the beginning of the cycle connecting = True - log('{}: cycle={} connecting={} family={}'.format( - this, + log('vpn-client-state: cycle={} connecting={} family={}'.format( i, connecting, AF @@ -416,13 +392,16 @@ def main(): family=AF ) c_pid = c_proc.pid + connected = True + connecting = False except AssertionError as e: + connected = False connecting = False print('exception-handler in {}: {}'.format(this, repr(e))) if DEBUG: print_exc() if connected and not connecting: - if i % LOOP_CYCLE == 0: + if i % LOOP_CYCLE == 0: # at the end of the cycle # run speedtest try: if not c_stimer and dequeue_speedtest(): @@ -461,36 +440,32 @@ def main(): c_iotl )) - if i % POLL_FREQ == 0: # every x cycles per loop - try: - shell_check_output_cmd('ip link | grep {}'.format(TUN_IFACE)) - geo_result = get_geo_location() - assert geo_result, '{}: client tunnel down'.format(this) - print('client-vpn: {}'.format(geo_result)) - except AssertionError as e: - print('exception-handler in {}: {}'.format(this, repr(e))) - if DEBUG: print_exc() - connected = False - connecting = False - c_proc.terminate() - c_pid = None - - if i % LOOP_CYCLE == 0: + if i % LOOP_CYCLE == 0: # at the end of the cycle try: - log_client_stats(status=connected, country=c_country) - except: + shell_check_output_cmd('ip link | grep {}'.format(TUN_IFACE)) # raises exception if tunnel is down + geo_result = get_geo_location() + assert geo_result, '{}: client tunnel down'.format(this) + print('client-vpn-geo: {}'.format(geo_result)) + if DEVICE_TYPE == 3: log_server_stats(status=started) # server up only if client-vpn is up + except Exception as e: print('exception-handler in {}: {}'.format(this, repr(e))) if DEBUG: print_exc() - + connected = False + connecting = False + c_proc.terminate() + while c_proc.poll() is None: sleep(LOOP_TIMER) + c_pid = None + if DEVICE_TYPE == 3: log_server_stats() # force server down if clienv-vpn is down + + log_client_stats(status=connected, country=c_country) w_clnts = get_stations() - c_status_line = 'client-status: cycle={} connected={} connecting={} c_proto={} c_pid={} w_clnts={} mgmt_ipaddr={} AF={} hostAPd={} UPNP={}'.format( + c_status_line = 'client-status: cycle={} connected={} connecting={} c_proto={} c_pid={} w_clnts={} AF={} hostAPd={} UPNP={}'.format( i, connected, connecting, c_proto, c_pid, w_clnts, - mgmt_ipaddr, AF, AP, UPNP diff --git a/unzoner/src/tests/run b/unzoner/src/tests/run index b78f87c..41302c1 100755 --- a/unzoner/src/tests/run +++ b/unzoner/src/tests/run @@ -16,7 +16,7 @@ function finish() { trap finish EXIT function install_venv() { - python3.9 -m venv src/tests/venv + python3 -m venv src/tests/venv export PATH="$(pwd)/src/tests/venv/bin:${PATH}" diff --git a/unzoner/src/vpn.py b/unzoner/src/vpn.py index 8ce9662..31592cf 100755 --- a/unzoner/src/vpn.py +++ b/unzoner/src/vpn.py @@ -31,9 +31,6 @@ ipaddr = None qtype = None c_proto = None -c_wpid = None -s_wpid = None -s_wport = None @retry(Exception, cdata='method={}'.format(stack()[0][3])) @@ -110,35 +107,11 @@ def get_openvpn_binary(default='/usr/sbin/openvpn'): def connect_node(family=AF): global PROTOS global c_proto - global c_wpid - global s_wpid - global s_wport global ipaddr global country global host global qtype - try: - log('os.kill: {}'.format(c_wpid)) - os.kill(int(c_wpid), signal.SIGKILL) - except: - pass - - c_wpid = None - - try: - log('kill_remote_pid: {}'.format( - kill_remote_pid( - ipaddr=ipaddr, - family=family, - pid=s_wpid - ) - )) - except: - pass - - s_wpid = None - s_wport = None ipaddr = None if len(PROTOS) <= 0: PROTOS = list(TUN_PROTO) # start again @@ -331,39 +304,6 @@ def connect_node(family=AF): ipaddr )) - ############################## - # (legacy) WANProxy override # - ############################## - if WANPROXY: - (c_wpid, s_wpid, s_wport) = start_wanproxy_server( - ipaddr=ipaddr, - family=family, - local_pid=c_wpid, - remote_pid=s_wpid, - port=s_wport - ) - - log('start_wanproxy_server: ipaddr={} af={} local_pid={} remote_pid={} remote_port={} transport={}'.format( - ipaddr, - family, - c_wpid, - s_wpid, - s_wport, - WANPROXY - )) - - port = WANPROXY_PORT - ipaddr = 'localhost' - tun_proto = 'tcp' - if family == 6: tun_proto = '{}{}'.format(tun_proto, family) - log('{}: wan_proxy={} proto={} ipaddr={} port={}'.format( - stack()[0][3], - WANPROXY, - tun_proto, - ipaddr, - port - )) - if FRAGMENT and tun_proto == 'udp': cmd.append('--tun-mtu') cmd.append(TUN_MTU) @@ -572,9 +512,7 @@ def get_client_status(): bytesin = 0 bytesout = 0 try: - status = open('{}/client.status'.format( - TEMPDIR - )).read().split('\n') + status = open('{}/client.status'.format(TEMPDIR)).read().split('\n') bytesin = [line.split(',')[1] for line in status if line.split(',')[0] == 'TCP/UDP read bytes'][0] bytesout = [line.split(',')[1] for line in status if line.split(',')[0] == 'TCP/UDP write bytes'][0] except IndexError: @@ -791,102 +729,6 @@ def restart_stunnel_client( return run_shell_cmd(['%s' % stunnel_bin, '%s' % conf]) -def start_wanproxy_server( - ipaddr=None, - family=4, - local_pid=None, - remote_pid=None, - port=None, - ssh_port=DOCKER_SSH_PORT -): - if WANPROXY == 'SSH': - remote_guid = get_guid_by_public_ipaddr(ipaddr=ipaddr, family=family) - assert remote_guid - if DEBUG: print( 'get_guid_by_public_ipaddr: ipaddr={} family={} remote_guid={}'.format( - ipaddr, - family, - remote_guid - )) - - if not (port and remote_pid): - if WANPROXY == 'SSH': - # start WANProxy server instance and get remote port for forwarding - result = run_shell_cmd( - [ - 'ssh', '-i', '%s/id_rsa' % WORKDIR, - '-o StrictHostKeyChecking=no', - 'root@%s' % ipaddr, - '-p', '%s' % str(ssh_port), - 'bash', - '-c', - '"source %s/functions; source %s/.env; start_wanproxy_server"' % ( - WORKDIR, - TEMPDIR - ) - ] - ) - if DEBUG: print(result) - remote_pid = result[1].split()[0] - port = result[1].split()[1] - - if not local_pid: - if WANPROXY == 'SSH': - # start SSH tunnel - result = run_background_shell_cmd( - [ - 'ssh', '-i', '%s/id_rsa' % WORKDIR, - '-fN','-o StrictHostKeyChecking=no', - '-L 3301:localhost:%s' % str(port), - '-p', '%s' % str(ssh_port), - 'root@%s' % ipaddr - ] - ) - if DEBUG: print(result.__dict__) - local_pid = str(int(result.pid) + 1) - - if WANPROXY == 'SOCAT': - # start SOCAT local listener - result = run_shell_cmd_nowait( - [ - '/usr/bin/socat', - 'TCP%s-LISTEN:3301,bind=localhost,su=nobody,fork,reuseaddr' % str( - family - ), - 'TCP%s:%s:%s' % (str(family), ipaddr, SOCAT_PORT) - ] - ) - if DEBUG: print(result.__dict__) - local_pid = str(result.pid) - - return (local_pid, remote_pid, port) - - -def kill_remote_pid(ipaddr=None, family=4, pid=None, ssh_port=DOCKER_SSH_PORT): - if WANPROXY == 'SSH': - remote_guid = get_guid_by_public_ipaddr(ipaddr=ipaddr, family=family) - assert remote_guid - if DEBUG: print('get_guid_by_public_ipaddr: ipaddr={} family={} remote_guid={}'.format( - ipaddr, - family, - remote_guid - )) - - result = None - if pid and WANPROXY == 'SSH': - # kill remote pid - result = run_shell_cmd( - [ - 'ssh', '-i', '%s/id_rsa' % WORKDIR, - '-o StrictHostKeyChecking=no', - 'root@%s' % ipaddr, - '-p', '%s' % str(ssh_port), - 'bash', '-c', '"kill -9 %s"' % str(pid) - ] - ) - if DEBUG: print(result) - return result - - @retry(Exception, cdata='method={}'.format(stack()[0][3])) def disconnect_clients(): disconnected = list() diff --git a/unzoner/start b/unzoner/start index ac976bf..a996cfd 100755 --- a/unzoner/start +++ b/unzoner/start @@ -77,8 +77,6 @@ declare -x TUN_PASSWD=${TUN_PASSWD:-$(${WORKDIR}/scripts/pyboot.sh gen_hash)} declare -x REQUESTS_CA_BUNDLE=${REQUESTS_CA_BUNDLE:-${DATADIR}/cacert.pem} declare -x STUNNEL=${STUNNEL:-0} declare -x STUNNEL_PORT=${STUNNEL_PORT:-443} -declare -x WANPROXY=${WANPROXY} -declare -x WANPROXY_PORT=${WANPROXY_PORT:-3300} declare -x SOCAT_PORT=${SOCAT_PORT:-3302} declare -x UPNP_TCP_PORT_FORWARD=${UPNP_TCP_PORT_FORWARD:-${OPENVPN_PORT} ${STUNNEL_PORT} ${SOCAT_PORT}} declare -x UPNP_UDP_PORT_FORWARD=${UPNP_UDP_PORT_FORWARD:-${OPENVPN_PORT}} @@ -831,79 +829,6 @@ EOF fi fi -if [[ ${WANPROXY} ]]; then - if [[ "${DEVICE_TYPE}" == "1" ]] || [[ "${DEVICE_TYPE}" == "3" ]] || [[ "${DEVICE_TYPE}" == "4" ]]; then - log 'generating WANProxy server config...' - cat << EOF > ${WORKDIR}/server.conf -create codec codec0 -set codec0.codec XCodec -set codec0.compressor zlib -set codec0.compressor_level 6 -activate codec0 - -create interface $(get_iface) -set $(get_iface).family IPv${AF} -set $(get_iface).host "localhost" -set $(get_iface).port "0" -activate $(get_iface) - -create peer peer0 -set peer0.family IPv${AF} -set peer0.host "localhost" -set peer0.port "${OPENVPN_PORT}" -activate peer0 - -create proxy proxy0 -set proxy0.interface $(get_iface) -set proxy0.interface_codec codec0 -set proxy0.peer peer0 -set proxy0.peer_codec None -activate proxy0 -EOF - - if [[ "${WANPROXY}" == "SOCAT" ]]; then - ipt_add_rule flter I "INPUT -i ${EXT_IFACE} -p tcp -m tcp --dport ${SOCAT_PORT} -j ACCEPT" - if [[ "${AF}" == "6" ]] && [[ $(ip6tables -t nat -L) ]]; then - ip6t_add_rule filter I "INPUT -i ${EXT_IFACE} -p tcp -m tcp --dport ${SOCAT_PORT}" - fi - start_wanproxy_server - fi - fi - - if [[ "${DEVICE_TYPE}" == "2" ]] || [[ "${DEVICE_TYPE}" == "3" ]]; then - log 'generating WANProxy client config...' - cat << EOF > ${WORKDIR}/client.conf -create codec codec0 -set codec0.codec XCodec -set codec0.compressor zlib -set codec0.compressor_level 6 -activate codec0 - -create interface $(get_iface) -set $(get_iface).family IPv${AF} -set $(get_iface).host "localhost" -set $(get_iface).port "${WANPROXY_PORT}" -activate $(get_iface) - -create peer peer0 -set peer0.family IPv${AF} -set peer0.host "localhost" -set peer0.port "3301" -activate peer0 - -create proxy proxy0 -set proxy0.interface $(get_iface) -set proxy0.interface_codec None -set proxy0.peer peer0 -set proxy0.peer_codec codec0 -activate proxy0 -EOF - - log 'starting WANProxy client...' - (nohup $(which wanproxy) -c ${WORKDIR}/client.conf) & - cat "${WORKDIR}/id_rsa.pub" >> "${HOME}/.ssh/authorized_keys" - fi -fi if [[ ${VPN_PROVIDER} ]] && [[ ${VPN_LOCATION_GROUP} ]] && [[ ${VPN_LOCATION} ]]; then log 'generating custom VPN client configuration...'