diff options
| author | Kali Kaneko (leap communications) <kali@leap.se> | 2017-02-01 14:49:45 +0100 | 
|---|---|---|
| committer | Kali Kaneko (leap communications) <kali@leap.se> | 2017-02-23 00:40:31 +0100 | 
| commit | 1d4a3d68869dd9c416b104399097a6bb0c1bace3 (patch) | |
| tree | 9afc38280e17a84e55184f064cf34f1a490ac539 | |
| parent | 6d1d18faec5caa60c26b8245f0ab17c63d0b80d8 (diff) | |
[feature] new commands: get_cert
| -rw-r--r-- | src/leap/bitmask/bonafide/_protocol.py | 9 | ||||
| -rw-r--r-- | src/leap/bitmask/bonafide/service.py | 16 | ||||
| -rw-r--r-- | src/leap/bitmask/cli/command.py | 6 | ||||
| -rw-r--r-- | src/leap/bitmask/cli/eip.py | 14 | ||||
| -rw-r--r-- | src/leap/bitmask/core/dispatcher.py | 21 | ||||
| -rw-r--r-- | src/leap/bitmask/core/service.py | 2 | ||||
| -rw-r--r-- | src/leap/bitmask/vpn/__init__.py | 10 | ||||
| -rw-r--r-- | src/leap/bitmask/vpn/_checks.py | 27 | ||||
| -rw-r--r-- | src/leap/bitmask/vpn/_control.py | 7 | ||||
| -rw-r--r-- | src/leap/bitmask/vpn/_management.py | 14 | ||||
| -rw-r--r-- | src/leap/bitmask/vpn/eip.py | 2 | ||||
| -rw-r--r-- | src/leap/bitmask/vpn/errors.py | 2 | ||||
| -rw-r--r-- | src/leap/bitmask/vpn/manager.py | 13 | ||||
| -rw-r--r-- | src/leap/bitmask/vpn/process.py | 7 | ||||
| -rw-r--r-- | src/leap/bitmask/vpn/service.py | 91 | 
15 files changed, 163 insertions, 78 deletions
| diff --git a/src/leap/bitmask/bonafide/_protocol.py b/src/leap/bitmask/bonafide/_protocol.py index ac631d8c..91b8b242 100644 --- a/src/leap/bitmask/bonafide/_protocol.py +++ b/src/leap/bitmask/bonafide/_protocol.py @@ -192,9 +192,12 @@ class BonafideProtocol(object):          d = self._sessions[full_id].get_smtp_cert()          return d -    def do_get_vpn_cert(self): -        # FIXME to be implemented -        pass +    def do_get_vpn_cert(self, full_id): +        if (full_id not in self._sessions or +                not self._sessions[full_id].is_authenticated): +            return fail(RuntimeError("There is no session for such user")) +        d = self._sessions[full_id].get_vpn_cert() +        return d      def do_update_user(self):          # FIXME to be implemented diff --git a/src/leap/bitmask/bonafide/service.py b/src/leap/bitmask/bonafide/service.py index 37d1e214..69aac2df 100644 --- a/src/leap/bitmask/bonafide/service.py +++ b/src/leap/bitmask/bonafide/service.py @@ -120,7 +120,21 @@ class BonafideService(HookableService):      def do_provider_list(self, seeded=False):          return self._bonafide.do_provider_list(seeded) -    def do_get_smtp_cert(self, username): +    # TODO make username mandatory +    # and move active_user to the cli machinery +    def do_get_vpn_cert(self, username=None): +        if not username: +            username = self._active_user +        if not username: +            return defer.fail( +                RuntimeError('No active user, cannot get VPN cert.')) +        d = self._bonafide.do_get_vpn_cert(username) +        d.addCallback(lambda response: (username, response)) +        return d + +    def do_get_smtp_cert(self, username=None): +        if not username: +            username = self._active_user          if not username:              return defer.fail(                  RuntimeError('No username, cannot get SMTP cert.')) diff --git a/src/leap/bitmask/cli/command.py b/src/leap/bitmask/cli/command.py index 0e70d2e7..ff213a35 100644 --- a/src/leap/bitmask/cli/command.py +++ b/src/leap/bitmask/cli/command.py @@ -116,7 +116,11 @@ class Command(object):          if self.print_json:              print(json.dumps(obj, indent=2))          elif not obj['error']: -            return printer(obj['result']) +            if not obj['result']: +                print (Fore.RED + 'ERROR: malformed response, expected' +                       ' obj["result"]' + Fore.RESET) +            else: +                return printer(obj['result'])          else:              print Fore.RED + 'ERROR:' + '%s' % obj['error'] + Fore.RESET diff --git a/src/leap/bitmask/cli/eip.py b/src/leap/bitmask/cli/eip.py index eac8682a..5fb240cd 100644 --- a/src/leap/bitmask/cli/eip.py +++ b/src/leap/bitmask/cli/eip.py @@ -1,6 +1,6 @@  # -*- coding: utf-8 -*-  # eip -# Copyright (C) 2016 LEAP +# Copyright (C) 2016-2017 LEAP  #  # 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 @@ -24,14 +24,18 @@ class Eip(command.Command):      service = 'eip'      usage = '''{name} eip <subcommand> -Bitmask Encrypted Internet Service +Bitmask Encrypted Internet Proxy Service  SUBCOMMANDS: -   start      Start service -   stop       Stop service +   enable     Enable EIP Service +   disable    Disable EIP Service +   check      Check whether EIP service is properly configured +   get_cert   Get EIP Certificate from provider +   start      Start EIP +   stop       Stop EIP     status     Display status about service  '''.format(name=command.appname) -    commands = ['start', 'stop', 'status'] +    commands = ['start', 'stop', 'status', 'check', 'get_cert'] diff --git a/src/leap/bitmask/core/dispatcher.py b/src/leap/bitmask/core/dispatcher.py index 833741dd..a93c3ec5 100644 --- a/src/leap/bitmask/core/dispatcher.py +++ b/src/leap/bitmask/core/dispatcher.py @@ -201,12 +201,27 @@ class EIPCmd(SubCommand):          except IndexError:              raise DispatchError(                  'wrong number of arguments: expected 1, got none') -        d = eip.do_start(provider) +        # TODO --- attempt to get active provider +        # TODO or catch the exception and send error +        provider = parts[2] +        d = eip.start_vpn(provider)          return d      @register_method('dict')      def do_STOP(self, eip, *parts): -        d = eip.do_stop() +        d = eip.stop_vpn() +        return d + +    @register_method('check') +    def do_CHECK(self, eip, *parts): +        d = eip.do_check() +        return d + +    @register_method('get_cert') +    def do_GET_CERT(self, eip, *parts): +        # TODO -- attempt to get active provider +        provider = parts[2] +        d = eip.do_get_cert(provider)          return d @@ -513,7 +528,7 @@ def _format_error(failure):              logger.failure('[DISPATCHER] Unexpected error:')          except:              logger.debug('[DISPATCHER] Unexpected error: %r' % failure.value) -            logger.debug('%r' % failure) +            print failure.getTraceback()      # if needed, we could add here the exception type as an extra field      return json.dumps({'error': failure.value.message, 'result': None}) diff --git a/src/leap/bitmask/core/service.py b/src/leap/bitmask/core/service.py index 3a3b1f69..65b5ddcc 100644 --- a/src/leap/bitmask/core/service.py +++ b/src/leap/bitmask/core/service.py @@ -32,7 +32,7 @@ from leap.bitmask.core import _zmq  from leap.bitmask.core import flags  from leap.bitmask.core import _session  from leap.bitmask.core.web.service import HTTPDispatcherService -from leap.bitmask.vpn import EIPService +from leap.bitmask.vpn.service import EIPService  from leap.common.events import server as event_server  logger = Logger() diff --git a/src/leap/bitmask/vpn/__init__.py b/src/leap/bitmask/vpn/__init__.py index 6c3cf064..e69de29b 100644 --- a/src/leap/bitmask/vpn/__init__.py +++ b/src/leap/bitmask/vpn/__init__.py @@ -1,10 +0,0 @@ -# -*- coding: utf-8 -*- -from .manager import VPNManager -from .eip import EIPManager -from .service import EIPService -from .fw.firewall import FirewallManager - -import errors - -__all__ = ['VPNManager', 'FirewallManager', 'EIPManager', 'EIPService', -           'errors'] diff --git a/src/leap/bitmask/vpn/_checks.py b/src/leap/bitmask/vpn/_checks.py new file mode 100644 index 00000000..3a1914f1 --- /dev/null +++ b/src/leap/bitmask/vpn/_checks.py @@ -0,0 +1,27 @@ +import os + +from leap.common.config import get_path_prefix + + +class ImproperlyConfigured(Exception): +    pass + + +def is_service_ready(provider): +    valid_cert = _has_valid_cert(provider) +    return True + + +def get_eip_cert_path(provider): +    return os.path.join(get_path_prefix(), +                        'leap', 'providers', provider,  +                        'keys', 'client', 'openvpn.pem') + + +def _has_valid_cert(provider): +    cert_path = get_eip_cert_path(provider) +    has_file = os.path.isfile(cert_path) +    if not has_file: +        raise ImproperlyConfigured('Missing EIP certificate') + +     diff --git a/src/leap/bitmask/vpn/_control.py b/src/leap/bitmask/vpn/_control.py index 991dc0ff..82dd90bc 100644 --- a/src/leap/bitmask/vpn/_control.py +++ b/src/leap/bitmask/vpn/_control.py @@ -14,11 +14,8 @@ class VPNControl(object):      OPENVPN_VERB = "openvpn_verb"      def __init__(self, **kwargs): -        """ -        Instantiate empty attributes and get a copy -        of a QObject containing the QSignals that we will pass along -        to the VPNManager. -        """ +        # TODO what the fuck this is doing that is different from +        # the manager?          self._vpnproc = None          self._pollers = [] diff --git a/src/leap/bitmask/vpn/_management.py b/src/leap/bitmask/vpn/_management.py index 0eb37b7b..51120a34 100644 --- a/src/leap/bitmask/vpn/_management.py +++ b/src/leap/bitmask/vpn/_management.py @@ -1,3 +1,6 @@ +from leap.bitmask.vpn.constants import IS_MAC + +  class OpenVPNAlreadyRunning(Exception):      message = ("Another openvpn instance is already running, and could "                 "not be stopped.") @@ -36,15 +39,7 @@ class VPNManagement(object):          """          self._tn = None          self._signaler = signaler -        self._aborted = False - -    @property -    def aborted(self): -        return self._aborted - -    @aborted.setter -    def aborted(self, value): -        self._aborted = value +        self.aborted = False      def _seek_to_eof(self):          """ @@ -419,4 +414,3 @@ class VPNManagement(object):          else:              logger.warning("Unable to terminate OpenVPN")              raise OpenVPNAlreadyRunning - diff --git a/src/leap/bitmask/vpn/eip.py b/src/leap/bitmask/vpn/eip.py index 853fabda..d6736629 100644 --- a/src/leap/bitmask/vpn/eip.py +++ b/src/leap/bitmask/vpn/eip.py @@ -18,7 +18,7 @@  from colorama import Fore -from leap.bitmask.vpn import VPNManager +from leap.bitmask.vpn.manager import VPNManager  from leap.bitmask.vpn.fw.firewall import FirewallManager  from leap.bitmask.vpn.status import StatusQueue  from leap.bitmask.vpn.zmq_pub import ZMQPublisher diff --git a/src/leap/bitmask/vpn/errors.py b/src/leap/bitmask/vpn/errors.py index 77cf1dcb..239ea352 100644 --- a/src/leap/bitmask/vpn/errors.py +++ b/src/leap/bitmask/vpn/errors.py @@ -1,7 +1,7 @@  #!/usr/bin/env python  # -*- coding: utf-8 -*- -from .process import OpenVPNAlreadyRunning, AlienOpenVPNAlreadyRunning +from ._management import OpenVPNAlreadyRunning, AlienOpenVPNAlreadyRunning  from .launcher import OpenVPNNotFoundException, VPNLauncherException  from leap.bitmask.vpn.launchers.linux import (      EIPNoPolkitAuthAgentAvailable, EIPNoPkexecAvailable) diff --git a/src/leap/bitmask/vpn/manager.py b/src/leap/bitmask/vpn/manager.py index 541da9e0..68025852 100644 --- a/src/leap/bitmask/vpn/manager.py +++ b/src/leap/bitmask/vpn/manager.py @@ -22,11 +22,15 @@ VPN Manager  import os  import tempfile -from leap.bitmask.vpn import process, _config -from leap.bitmask.vpn.constants import IS_WIN +from .process import VPNProcess +from ._config import _TempEIPConfig +from .constants import IS_WIN +# TODO this is very badly named. There is another class that is called +# manager!!! +  class VPNManager(object):      def __init__(self, remotes, cert_path, key_path, ca_path, extra_flags, @@ -45,11 +49,12 @@ class VPNManager(object):          domain = "demo.bitmask.net"          self._remotes = remotes -        self._eipconfig = _config._TempEIPConfig(extra_flags, cert_path, ports) +        self._eipconfig = _TempEIPConfig(extra_flags, cert_path, ports)          self._providerconfig = _config._TempProviderConfig(domain, ca_path)          # signaler = None  # XXX handle signaling somehow...          signaler = mock_signaler -        self._vpn = process.VPN(remotes=remotes, signaler=signaler) +        self._vpn = VPNProcess(remotes=remotes, signaler=signaler) +      def start(self):          """ diff --git a/src/leap/bitmask/vpn/process.py b/src/leap/bitmask/vpn/process.py index 4aae26b5..47c29423 100644 --- a/src/leap/bitmask/vpn/process.py +++ b/src/leap/bitmask/vpn/process.py @@ -16,7 +16,9 @@  # along with this program.  If not, see <http://www.gnu.org/licenses/>.  """ -VPN Manager, spawned in a custom processProtocol. +VPN Process management. +A custom processProtocol launches the VPNProcess and connects to its management +interface.  """  import os @@ -48,6 +50,7 @@ from leap.bitmask.vpn.utils import get_vpn_launcher  from leap.bitmask.vpn.launchers import linux  from leap.bitmask.vpn.udstelnet import UDSTelnet  from leap.bitmask.vpn import _observer +from leap.bitmask.vpn import _management  logger = Logger() @@ -56,7 +59,7 @@ logger = Logger()  OPENVPN_VERBOSITY = 1 -class VPNProcess(protocol.ProcessProtocol, VPNManager): +class VPNProcess(protocol.ProcessProtocol, _management.VPNManagement):      """      A ProcessProtocol class that can be used to spawn a process that will      launch openvpn and connect to its management interface to control it diff --git a/src/leap/bitmask/vpn/service.py b/src/leap/bitmask/vpn/service.py index d83a9ef8..59e251da 100644 --- a/src/leap/bitmask/vpn/service.py +++ b/src/leap/bitmask/vpn/service.py @@ -22,16 +22,19 @@ EIP service declaration.  import os -from twisted.application import service -from twisted.python import log +from twisted.internet import defer  from leap.bitmask.hooks import HookableService -from leap.bitmask.vpn import EIPManager +from leap.bitmask.vpn.eip import EIPManager +from leap.bitmask.vpn._checks import is_service_ready, get_eip_cert_path  from leap.common.config import get_path_prefix +from leap.common.files import check_and_fix_urw_only  class EIPService(HookableService): +    name = 'eip' +      def __init__(self, basepath=None):          """          Initialize EIP service @@ -45,18 +48,66 @@ class EIPService(HookableService):          else:              self._basepath = basepath +    def startService(self): +        print "Starting EIP Service..." +        # TODO this could trigger a check for validity of the certificates, +        # etc. +        super(EIPService, self).startService() + +    def stopService(self): +        print "Stopping EIP Service..." +        super(EIPService, self).stopService() + +    def start_vpn(self, domain): +        self._setup(domain) +        self._eip.start() +        self._started = True +        return "Starting" + +    def stop_vpn(self): +        if self._started: +            self._eip.stop() +            self._started = False +            return "Stopping" +        else: +            return "Not started" + +    def do_status(self): +        # TODO -- get status from a dedicated STATUS CLASS +        return {'result': 'running'} + +    def do_check(self): +        """Check whether the EIP Service is properly configured, +        and can be started""" +        # TODO either pass a provider, or set a given provider +        _ready = is_service_ready('demo.bitmask.net') +        return {'eip_ready': 'ok'} + +    @defer.inlineCallbacks +    def do_get_cert(self, provider): +        # fetch vpn cert and store +        bonafide = self.parent.getServiceNamed("bonafide") +        _, cert_str = yield bonafide.do_get_vpn_cert() + +        cert_path = get_eip_cert_path(provider) +        cert_dir = os.path.dirname(cert_path) +        if not os.path.exists(cert_dir): +            os.makedirs(cert_dir, mode=0700) +        with open(cert_path, 'w') as outf: +            outf.write(cert_str) +        check_and_fix_urw_only(cert_path) +        defer.returnValue({'get_cert': 'ok'}) +      def _setup(self, provider): -        """ -        Set up EIPManager for a specified provider. +        """Set up EIPManager for a specified provider.          :param provider: the provider to use, e.g. 'demo.bitmask.net' -        :type provider: str -        """ +        :type provider: str"""          # FIXME          # XXX picked manually from eip-service.json          remotes = ( -            ("198.252.153.84", "1194"), -            ("46.165.242.169", "1194"), +            ("198.252.153.84", "1194 "), +            ("46.165.242.169", "1194 "),          )          prefix = os.path.join(self._basepath, @@ -76,25 +127,3 @@ class EIPService(HookableService):          self._eip = EIPManager(remotes, cert_path, key_path, ca_path,                                 extra_flags) - -    def startService(self): -        print "Starting EIP Service..." -        super(EIPService, self).startService() - -    def stopService(self): -        print "Stopping EIP Service..." -        super(EIPService, self).stopService() - -    def do_start(self, domain): -        self._setup(domain) -        self._eip.start() -        self._started = True -        return "Starting" - -    def do_stop(self): -        if self._started: -            self._eip.stop() -            self._started = False -            return "Stopping" -        else: -            return "Not started" | 
