From a003e13cec2c44160b46047d0fee8d52dfc6253f Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Thu, 8 Jun 2017 15:36:37 -0700 Subject: [bug] make openvpn and firewall able to launch with these fixes, I'm able to finally launch openvpn and firewall on osx. :) all that's left for a minimum vpn release is packaging and installing all the helpers in the proper place. --- src/leap/bitmask/vpn/_control.py | 10 +++++-- src/leap/bitmask/vpn/fw/firewall.py | 47 +++++++++++++++++++++++------- src/leap/bitmask/vpn/fw/osx/bitmask-helper | 19 +++++++----- src/leap/bitmask/vpn/launcher.py | 27 +++++------------ src/leap/bitmask/vpn/launchers/darwin.py | 7 +++-- src/leap/bitmask/vpn/process.py | 20 +++++++++++-- 6 files changed, 85 insertions(+), 45 deletions(-) (limited to 'src') diff --git a/src/leap/bitmask/vpn/_control.py b/src/leap/bitmask/vpn/_control.py index 74182a9e..bf33b1bc 100644 --- a/src/leap/bitmask/vpn/_control.py +++ b/src/leap/bitmask/vpn/_control.py @@ -54,7 +54,7 @@ class VPNControl(object): args = [self._vpnconfig, self._providerconfig, self._host, self._port] - kwargs = {'openvpn_verb': 7, 'remotes': self._remotes, + kwargs = {'openvpn_verb': 4, 'remotes': self._remotes, 'restartfun': self.restart} vpnproc = VPNProcess(*args, **kwargs) @@ -62,6 +62,11 @@ class VPNControl(object): log.info('Another vpn process is running. Will try to stop it.') vpnproc.stop_if_already_running() + try: + vpnproc.preUp() + except Exception as e: + log.error('Error on vpn pre-up {0!r}'.format(e)) + raise try: cmd = vpnproc.getCommand() except Exception as e: @@ -70,7 +75,8 @@ class VPNControl(object): env = os.environ - reactor.spawnProcess(vpnproc, cmd[0], cmd, env) + runningproc = reactor.spawnProcess(vpnproc, cmd[0], cmd, env) + vpnproc.pid = runningproc.pid self._vpnproc = vpnproc # add pollers for status and state diff --git a/src/leap/bitmask/vpn/fw/firewall.py b/src/leap/bitmask/vpn/fw/firewall.py index 23bdbd91..dcd956d4 100644 --- a/src/leap/bitmask/vpn/fw/firewall.py +++ b/src/leap/bitmask/vpn/fw/firewall.py @@ -25,14 +25,12 @@ import subprocess from twisted.logger import Logger -from leap.bitmask.vpn.constants import IS_MAC +from leap.bitmask.vpn.constants import IS_MAC, IS_LINUX from leap.common.events import catalog, emit_async -log = Logger() - - -# TODO -- subclass it for osx/windows, not only for linux. +from leap.bitmask.vpn.launchers import darwin +log = Logger() # A regular user should not run bitmask as root, but we contemplate # this case for tests inside docker. @@ -46,7 +44,34 @@ def check_root(cmd): return cmd -class FirewallManager(object): +class _OSXFirewallManager(object): + def __init__(self, remotes): + self._remotes = list(remotes) + self._helper = darwin.HelperCommand() + + def start(self, restart=False): + gateways = [gateway for gateway, port in self._remotes] + cmd = 'firewall_start %s' % (' '.join(gateways),) + self._helper.send(cmd) + # TODO parse OK from result + return True + + def stop(self): + cmd = 'firewall_stop' + self._helper.send(cmd) + return True + + def is_up(self): + # TODO implement!!! + return True + + @property + def status(self): + # TODO implement!!! -- factor out, too + return {'status': 'on', 'error': None} + + +class _LinuxFirewallManager(object): """ Firewall manager that blocks/unblocks all the internet traffic with some @@ -100,10 +125,6 @@ class FirewallManager(object): """ Tear the firewall down using the privileged wrapper. """ - # We don't support Mac so far - if IS_MAC: - return True - cmd = [self.BITMASK_ROOT, "firewall", "stop"] cmd = check_root(cmd) exitCode = subprocess.call(cmd) @@ -133,3 +154,9 @@ class FirewallManager(object): status = 'on' return {'status': status, 'error': None} + + +if IS_LINUX: + FirewallManager = _LinuxFirewallManager +elif IS_MAC: + FirewallManager = _OSXFirewallManager diff --git a/src/leap/bitmask/vpn/fw/osx/bitmask-helper b/src/leap/bitmask/vpn/fw/osx/bitmask-helper index 68be7dbb..2990219f 100755 --- a/src/leap/bitmask/vpn/fw/osx/bitmask-helper +++ b/src/leap/bitmask/vpn/fw/osx/bitmask-helper @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # # Author: Kali Kaneko -# Copyright (C) 2015-2016 LEAP Encryption Access Project +# Copyright (C) 2015-2017 LEAP Encryption Access Project # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -42,11 +42,10 @@ To see the loaded rules: To test the commands, you can write directly to the unix socket. Remember to terminate the command properly: - echo 'firewall_stop/CMD' | socat - UNIX-CONNECT:/tmp/bitmask-helper.socket + echo 'firewall_stop/CMD' | socat - UNIX-CONNECT:/tmp/bitmask-helper.socket """ import os -import re import socket import signal import subprocess @@ -75,7 +74,6 @@ FIXED_FLAGS = [ "--setenv", "LEAPOPENVPN", "1", "--nobind", "--client", - "--dev", "tun", "--tls-client", "--remote-cert-tls", "server", "--management-signal", @@ -243,14 +241,19 @@ def openvpn_start(*args): opts += ['--dhcp-option', 'DNS', '10.42.0.1', '--up', RESOURCES_PATH + 'client.up.sh', '--down', RESOURCES_PATH + 'client.down.sh'] + opts += ["--dev", "tun"] binary = [RESOURCES_PATH + 'openvpn.leap'] - - syslog.syslog(syslog.LOG_WARNING, ' '.join(binary + opts)) + cmd = binary + opts + #syslog.syslog(syslog.LOG_WARNING, 'LAUNCHING VPN: ' + ' '.join(cmd)) # TODO sanitize options global openvpn_proc - openvpn_proc = subprocess.Popen(binary + opts, shell=False) - syslog.syslog(syslog.LOG_WARNING, "OpenVPN PID: %s" % str(openvpn_proc.pid)) + openvpn_proc = subprocess.Popen(cmd, shell=False, bufsize=-1) + #try: + # result = subprocess.check_output(cmd, shell=False, stderr=subprocess.STDOUT) + #except Exception as exc: + # syslog.syslog(syslog.LOG_WARNING, exc.output) + #syslog.syslog(syslog.LOG_WARNING, "OpenVPN PID: %s" % str(openvpn_proc.pid)) def openvpn_stop(sig='TERM'): diff --git a/src/leap/bitmask/vpn/launcher.py b/src/leap/bitmask/vpn/launcher.py index f412acef..8ce2660f 100644 --- a/src/leap/bitmask/vpn/launcher.py +++ b/src/leap/bitmask/vpn/launcher.py @@ -29,7 +29,7 @@ from twisted.logger import Logger from abc import ABCMeta, abstractmethod from functools import partial -from leap.bitmask.vpn.constants import IS_LINUX +from leap.bitmask.vpn.constants import IS_LINUX, IS_MAC from leap.bitmask.vpn.utils import force_eval @@ -186,16 +186,6 @@ class VPNLauncher(object): :return: A VPN command ready to be launched. :rtype: list """ - # leap_assert_type(vpnconfig, VPNConfig) - # leap_assert_type(providerconfig, ProviderConfig) - - # XXX this still has to be changed on osx and windows accordingly - # kwargs = {} - # openvpn_possibilities = which(kls.OPENVPN_BIN, **kwargs) - # if not openvpn_possibilities: - # raise OpenVPNNotFoundException() - # openvpn = first(openvpn_possibilities) - # ----------------------------------------- openvpn_path = force_eval(kls.OPENVPN_BIN_PATH) if not os.path.isfile(openvpn_path): @@ -215,7 +205,6 @@ class VPNLauncher(object): if openvpn_verb is not None: args += ['--verb', '%d' % (openvpn_verb,)] - # gateways = kls.get_gateways(vpnconfig, providerconfig) gateways = remotes for ip, port in gateways: @@ -224,7 +213,6 @@ class VPNLauncher(object): args += [ '--client', '--dev', ' tun', - '--persist-key', '--tls-client', '--remote-cert-tls', 'server' @@ -269,13 +257,12 @@ class VPNLauncher(object): '--ca', providerconfig.get_ca_cert_path() ] - args += [ - '--ping', '5', - '--ping-restart', '10'] - - args += [ - '--persist-key', - '--persist-local-ip', '--persist-remote-ip'] + if not IS_MAC: + args += [ + '--ping', '5', + '--ping-restart', '10', + '--persist-key', + '--persist-local-ip', '--persist-remote-ip'] command_and_args = [openvpn_path] + args return command_and_args diff --git a/src/leap/bitmask/vpn/launchers/darwin.py b/src/leap/bitmask/vpn/launchers/darwin.py index 3d64c857..80cacc4b 100644 --- a/src/leap/bitmask/vpn/launchers/darwin.py +++ b/src/leap/bitmask/vpn/launchers/darwin.py @@ -119,8 +119,8 @@ class DarwinVPNLauncher(VPNLauncher): return os.path.join(resources_path, "bitmask.tiff") @classmethod - def get_vpn_command(kls, eipconfig, providerconfig, socket_host, - socket_port="unix", openvpn_verb=1): + def get_vpn_command(kls, vpnconfig, providerconfig, socket_host, + remotes, socket_port="unix", openvpn_verb=1): """ Returns the OSX implementation for the vpn launching command. @@ -149,7 +149,8 @@ class DarwinVPNLauncher(VPNLauncher): # we use `super` in order to send the class to use command = super(DarwinVPNLauncher, kls).get_vpn_command( - eipconfig, providerconfig, socket_host, socket_port, openvpn_verb) + vpnconfig, providerconfig, socket_host, socket_port, remotes, + openvpn_verb) command.extend(['--setenv', "LEAPUSER", getpass.getuser()]) return command diff --git a/src/leap/bitmask/vpn/process.py b/src/leap/bitmask/vpn/process.py index 895cd0a2..844cd0ff 100644 --- a/src/leap/bitmask/vpn/process.py +++ b/src/leap/bitmask/vpn/process.py @@ -50,6 +50,10 @@ class _VPNProcess(protocol.ProcessProtocol, _management.VPNManagement): log = Logger() + # HACK - reactor is expected to set this up when the process is spawned. + # should try to get it from within this class. + pid = None + # TODO do we really need the vpnconfig/providerconfig objects in here??? def __init__(self, vpnconfig, providerconfig, socket_host, socket_port, @@ -183,6 +187,9 @@ class _VPNProcess(protocol.ProcessProtocol, _management.VPNManagement): # launcher + def preUp(self): + pass + def getCommand(self): """ Gets the vpn command from the aproppriate launcher. @@ -252,11 +259,19 @@ elif IS_MAC: This is a workaround to allow the state machine to be notified when openvpn process is spawned by the privileged helper. """ + def setupHelper(self): + # TODO use get_vpn_launcher instead self.helper = darwin.HelperCommand() + def preUp(self): + self.setupHelper() + cmd = self.getVPNCommand() + self.helper.send('openvpn_start %s' % ' '.join(cmd)) + def connectionMade(self): - VPNProcess.connectionMade(self) + super(_VPNProcess, self).connectionMade() + self.setupHelper() reactor.callLater(2, self.registerPID) def registerPID(self): @@ -268,7 +283,8 @@ elif IS_MAC: self.helper.send(cmd) def getVPNCommand(self): - return VPNProcess.getCommand(self) + vpncmd = _VPNProcess.getCommand(self) + return vpncmd def getCommand(self): canary = '''import sys, signal, time -- cgit v1.2.3