From 1d4a3d68869dd9c416b104399097a6bb0c1bace3 Mon Sep 17 00:00:00 2001 From: "Kali Kaneko (leap communications)" Date: Wed, 1 Feb 2017 14:49:45 +0100 Subject: [feature] new commands: get_cert --- src/leap/bitmask/bonafide/_protocol.py | 9 ++-- src/leap/bitmask/bonafide/service.py | 16 +++++- src/leap/bitmask/cli/command.py | 6 ++- src/leap/bitmask/cli/eip.py | 14 ++++-- src/leap/bitmask/core/dispatcher.py | 21 ++++++-- src/leap/bitmask/core/service.py | 2 +- src/leap/bitmask/vpn/__init__.py | 10 ---- src/leap/bitmask/vpn/_checks.py | 27 ++++++++++ src/leap/bitmask/vpn/_control.py | 7 +-- src/leap/bitmask/vpn/_management.py | 14 ++---- src/leap/bitmask/vpn/eip.py | 2 +- src/leap/bitmask/vpn/errors.py | 2 +- src/leap/bitmask/vpn/manager.py | 13 +++-- src/leap/bitmask/vpn/process.py | 7 ++- src/leap/bitmask/vpn/service.py | 91 ++++++++++++++++++++++------------ 15 files changed, 163 insertions(+), 78 deletions(-) create mode 100644 src/leap/bitmask/vpn/_checks.py (limited to 'src') 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 -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 . """ -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" -- cgit v1.2.3