From 6e197c1353c788109df07ee6d1242a5c2327e8f9 Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 1 Aug 2012 09:58:08 +0900 Subject: fileutil.which implementation --- src/leap/eip/conductor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/conductor.py b/src/leap/eip/conductor.py index e3adadc4..3ce062aa 100644 --- a/src/leap/eip/conductor.py +++ b/src/leap/eip/conductor.py @@ -6,7 +6,7 @@ from __future__ import (division, unicode_literals, print_function) from functools import partial import logging -from leap.utils.coroutines import spawn_and_watch_process +from leap.util.coroutines import spawn_and_watch_process from leap.baseapp.config import get_config, get_vpn_stdout_mockup from leap.eip.vpnwatcher import EIPConnectionStatus, status_watcher from leap.eip.vpnmanager import OpenVPNManager, ConnectionRefusedError -- cgit v1.2.3 From 23502b72f8cd8a9ec2fd28387f7788aeef54c2d1 Mon Sep 17 00:00:00 2001 From: kali Date: Thu, 2 Aug 2012 02:21:45 +0900 Subject: create config file if not exist. also locate openvpn binary when building eip configparser defaults. implement half of feature #356 --- src/leap/eip/conductor.py | 14 +++++++-- src/leap/eip/config.py | 72 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 3 deletions(-) create mode 100644 src/leap/eip/config.py (limited to 'src/leap/eip') diff --git a/src/leap/eip/conductor.py b/src/leap/eip/conductor.py index 3ce062aa..1d5e4b59 100644 --- a/src/leap/eip/conductor.py +++ b/src/leap/eip/conductor.py @@ -7,7 +7,8 @@ from functools import partial import logging from leap.util.coroutines import spawn_and_watch_process -from leap.baseapp.config import get_config, get_vpn_stdout_mockup + +from leap.eip.config import get_config, get_vpn_stdout_mockup from leap.eip.vpnwatcher import EIPConnectionStatus, status_watcher from leap.eip.vpnmanager import OpenVPNManager, ConnectionRefusedError @@ -39,6 +40,10 @@ class UnrecoverableError(EIPClientError): """ we cannot do anything about it, sorry """ + # XXX we should catch this and raise + # to qtland, so we emit signal + # to translate whatever kind of error + # to user-friendly msg in dialog. pass @@ -78,7 +83,7 @@ to be triggered for each one of them. self.autostart = True - self._get_config() + self._get_or_create_config() def _set_command_mockup(self): """ @@ -88,16 +93,19 @@ to be triggered for each one of them. command, args = get_vpn_stdout_mockup() self.command, self.args = command, args - def _get_config(self): + def _get_or_create_config(self): """ retrieves the config options from defaults or home file, or config file passed in command line. """ config = get_config(config_file=self.config_file) self.config = config + import ipdb;ipdb.set_trace() if config.has_option('openvpn', 'command'): commandline = config.get('openvpn', 'command') + #XXX remove mockup from here. + #it was just for testing early. if commandline == "mockup": self._set_command_mockup() return diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py new file mode 100644 index 00000000..d8ffeb28 --- /dev/null +++ b/src/leap/eip/config.py @@ -0,0 +1,72 @@ +import ConfigParser +import os + +from leap.util.fileutil import which, mkdir_p + + +def get_sensible_defaults(): + """ + gathers a dict of sensible defaults, + platform sensitive, + to be used to initialize the config parser + """ + defaults = dict() + defaults['openvpn_binary'] = which('openvpn') + return defaults + + +def get_config(config_file=None): + """ + temporary method for getting configs, + mainly for early stage development process. + in the future we will get preferences + from the storage api + """ + # TODO + # - refactor out common things and get + # them to util/ or baseapp/ + + defaults = get_sensible_defaults() + config = ConfigParser.ConfigParser(defaults) + + if not config_file: + fpath = os.path.expanduser('~/.config/leap/eip.cfg') + if not os.path.isfile(fpath): + dpath, cfile = os.path.split(fpath) + if not os.path.isdir(dpath): + mkdir_p(dpath) + with open(fpath, 'wb') as configfile: + config.write(configfile) + config_file = open(fpath) + + #TODO + # - get a more sensible path for win/mac + # - convert config_file to list; + # look in places like /etc/leap/eip.cfg + # for global settings. + # - raise warnings/error if bad options. + + try: + config.readfp(config_file) + except: + # XXX no file exists? + raise + return config + + +# XXX wrapper around config? to get default values +def get_with_defaults(config, section, option): + # XXX REMOVE ME + if config.has_option(section, option): + return config.get(section, option) + else: + # XXX lookup in defaults dict??? + pass + + +def get_vpn_stdout_mockup(): + # XXX REMOVE ME + command = "python" + args = ["-u", "-c", "from eip_client import fakeclient;\ +fakeclient.write_output()"] + return command, args -- cgit v1.2.3 From 65db011c13aa6bf03867cc0e579f191cbf611ef6 Mon Sep 17 00:00:00 2001 From: kali Date: Fri, 3 Aug 2012 06:44:17 +0900 Subject: fix icon change on disconnect --- src/leap/eip/conductor.py | 34 ++++++++++++---------------------- 1 file changed, 12 insertions(+), 22 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/conductor.py b/src/leap/eip/conductor.py index 1d5e4b59..b1683e7d 100644 --- a/src/leap/eip/conductor.py +++ b/src/leap/eip/conductor.py @@ -98,32 +98,40 @@ to be triggered for each one of them. retrieves the config options from defaults or home file, or config file passed in command line. """ + #print('get or create config') config = get_config(config_file=self.config_file) self.config = config - import ipdb;ipdb.set_trace() if config.has_option('openvpn', 'command'): commandline = config.get('openvpn', 'command') + #XXX remove mockup from here. #it was just for testing early. if commandline == "mockup": self._set_command_mockup() return + command_split = commandline.split(' ') command = command_split[0] if len(command_split) > 1: args = command_split[1:] else: args = [] + + # XXX CALL BUILD COMMAND self.command = command - #print("debug: command = %s" % command) self.args = args else: self._set_command_mockup() if config.has_option('openvpn', 'autostart'): - autostart = config.get('openvpn', 'autostart') + autostart = config.getboolean('openvpn', 'autostart') + print('autostart = %s' % autostart) self.autostart = autostart + else: + if config.has_option('DEFAULT', 'autostart'): + autostart = config.getboolean('DEFAULT', 'autostart') + self.autostart = autostart def _launch_openvpn(self): """ @@ -194,7 +202,7 @@ class EIPConductor(OpenVPNConnection): """ self.manager.forget_errors() self._try_connection() - # XXX should capture errors? + # XXX should capture errors here? def disconnect(self): """ @@ -202,25 +210,7 @@ class EIPConductor(OpenVPNConnection): """ self._disconnect() self.status.change_to(self.status.DISCONNECTED) - pass - def shutdown(self): - """ - shutdown and quit - """ - self.desired_con_state = self.status.DISCONNECTED - - def connection_state(self): - """ - returns the current connection state - """ - return self.status.current - - def desired_connection_state(self): - """ - returns the desired_connection state - """ - return self.desired_con_state def poll_connection_state(self): """ -- cgit v1.2.3 From b9c9b5536f9d1648a196e741cdf4570f64c3fb11 Mon Sep 17 00:00:00 2001 From: kali Date: Fri, 3 Aug 2012 07:32:47 +0900 Subject: build default invocation command + options if not found in config file. fix #182 and #356 --- src/leap/eip/conductor.py | 26 +++------ src/leap/eip/config.py | 144 ++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 133 insertions(+), 37 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/conductor.py b/src/leap/eip/conductor.py index b1683e7d..bf7f0fb2 100644 --- a/src/leap/eip/conductor.py +++ b/src/leap/eip/conductor.py @@ -8,7 +8,7 @@ import logging from leap.util.coroutines import spawn_and_watch_process -from leap.eip.config import get_config, get_vpn_stdout_mockup +from leap.eip.config import get_config, build_ovpn_command from leap.eip.vpnwatcher import EIPConnectionStatus, status_watcher from leap.eip.vpnmanager import OpenVPNManager, ConnectionRefusedError @@ -82,21 +82,13 @@ to be triggered for each one of them. self.proto = None self.autostart = True - self._get_or_create_config() - def _set_command_mockup(self): - """ - sets command and args for a command mockup - that just mimics the output from the real thing - """ - command, args = get_vpn_stdout_mockup() - self.command, self.args = command, args - def _get_or_create_config(self): """ retrieves the config options from defaults or home file, or config file passed in command line. + populates command and args to be passed to subprocess. """ #print('get or create config') config = get_config(config_file=self.config_file) @@ -105,12 +97,6 @@ to be triggered for each one of them. if config.has_option('openvpn', 'command'): commandline = config.get('openvpn', 'command') - #XXX remove mockup from here. - #it was just for testing early. - if commandline == "mockup": - self._set_command_mockup() - return - command_split = commandline.split(' ') command = command_split[0] if len(command_split) > 1: @@ -122,11 +108,14 @@ to be triggered for each one of them. self.command = command self.args = args else: - self._set_command_mockup() + # no command in config, we build it up. + # XXX check also for command-line --command flag + command, args = build_ovpn_command(config) + self.command = command + self.args = args if config.has_option('openvpn', 'autostart'): autostart = config.getboolean('openvpn', 'autostart') - print('autostart = %s' % autostart) self.autostart = autostart else: if config.has_option('DEFAULT', 'autostart'): @@ -211,7 +200,6 @@ class EIPConductor(OpenVPNConnection): self._disconnect() self.status.change_to(self.status.DISCONNECTED) - def poll_connection_state(self): """ """ diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py index d8ffeb28..3fca329c 100644 --- a/src/leap/eip/config.py +++ b/src/leap/eip/config.py @@ -1,17 +1,132 @@ import ConfigParser +import grp import os +import platform from leap.util.fileutil import which, mkdir_p +def build_ovpn_options(): + """ + build a list of options + to be passed in the + openvpn invocation + @rtype: list + @rparam: options + """ + # XXX review which of the + # options we don't need. + + # TODO pass also the config file, + # since we will need to take some + # things from there if present. + + # get user/group name + # also from config. + user = os.getlogin() + gid = os.getgroups()[-1] + group = grp.getgrgid(gid).gr_name + + opts = [] + opts.append('--persist-tun') + + # set user and group + opts.append('--user') + opts.append('%s' % user) + opts.append('--group') + opts.append('%s' % group) + + opts.append('--management-client-user') + opts.append('%s' % user) + opts.append('--management-signal') + + # set default options for management + # interface. unix sockets or telnet interface for win. + # XXX take them from the config object. + + ourplatform = platform.system() + if ourplatform in ("Linux", "Mac"): + opts.append('--management') + opts.append('/tmp/.eip.sock') + opts.append('unix') + if ourplatform == "Windows": + opts.append('--management') + opts.append('localhost') + # XXX which is a good choice? + opts.append('7777') + + # remaining config options, in a file + # NOTE: we will build this file from + # the service definition file. + ovpncnf = os.path.expanduser( + '~/.config/leap/openvpn.conf') + opts.append('--config') + opts.append(ovpncnf) + + return opts + + +def build_ovpn_command(config): + """ + build a string with the + complete openvpn invocation + + @param config: config object + @type config: ConfigParser instance + + @rtype [string, [list of strings]] + @rparam: a list containing the command string + and a list of options. + """ + command = [] + use_pkexec = False + ovpn = None + + if config.has_option('openvpn', 'openvpn_binary'): + ovpn = config.get('openvpn', 'openvpn_binary') + if not ovpn and config.has_option('DEFAULT', 'openvpn_binary'): + ovpn = config.get('DEFAULT', 'openvpn_binary') + + if config.has_option('openvpn', 'use_pkexec'): + use_pkexec = config.get('openvpn', 'use_pkexec') + + if use_pkexec: + command.append('pkexec') + if ovpn: + command.append(ovpn) + + for opt in build_ovpn_options(): + command.append(opt) + + # XXX check len and raise proper error + + return [command[0], command[1:]] + + def get_sensible_defaults(): """ gathers a dict of sensible defaults, platform sensitive, to be used to initialize the config parser + @rtype: dict + @rparam: default options. """ + + # this way we're passing a simple dict + # that will initialize the configparser + # and will get written to "DEFAULTS" section, + # which is fine for now. + # if we want to write to a particular section + # we can better pass a tuple of triples + # (('section1', 'foo', '23'),) + # and config.set them + defaults = dict() defaults['openvpn_binary'] = which('openvpn') + defaults['autostart'] = 'true' + + # TODO + # - management. return defaults @@ -21,6 +136,9 @@ def get_config(config_file=None): mainly for early stage development process. in the future we will get preferences from the storage api + + @rtype: ConfigParser instance + @rparam: a config object """ # TODO # - refactor out common things and get @@ -30,7 +148,8 @@ def get_config(config_file=None): config = ConfigParser.ConfigParser(defaults) if not config_file: - fpath = os.path.expanduser('~/.config/leap/eip.cfg') + fpath = os.path.expanduser( + '~/.config/leap/eip.cfg') if not os.path.isfile(fpath): dpath, cfile = os.path.split(fpath) if not os.path.isdir(dpath): @@ -46,27 +165,16 @@ def get_config(config_file=None): # for global settings. # - raise warnings/error if bad options. - try: - config.readfp(config_file) - except: - # XXX no file exists? - raise - return config - + # at this point, the file should exist. + # errors would have been raised above. + config.readfp(config_file) -# XXX wrapper around config? to get default values -def get_with_defaults(config, section, option): - # XXX REMOVE ME - if config.has_option(section, option): - return config.get(section, option) - else: - # XXX lookup in defaults dict??? - pass + return config def get_vpn_stdout_mockup(): # XXX REMOVE ME command = "python" - args = ["-u", "-c", "from eip_client import fakeclient;\ -fakeclient.write_output()"] + args = ["-u", "-c", ("from eip_client import fakeclient;" + "fakeclient.write_output()")] return command, args -- cgit v1.2.3 From 81613b2ef70e5d73b7c34eb4b78ee63189b45ab6 Mon Sep 17 00:00:00 2001 From: kali Date: Fri, 3 Aug 2012 09:42:14 +0900 Subject: pkexec check --- src/leap/eip/conductor.py | 37 +++++++++++++++++++++++++++---------- src/leap/eip/config.py | 35 ++++++++++++++++++++++++++++------- 2 files changed, 55 insertions(+), 17 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/conductor.py b/src/leap/eip/conductor.py index bf7f0fb2..2d6ad764 100644 --- a/src/leap/eip/conductor.py +++ b/src/leap/eip/conductor.py @@ -8,7 +8,9 @@ import logging from leap.util.coroutines import spawn_and_watch_process -from leap.eip.config import get_config, build_ovpn_command + +from leap.eip.config import (get_config, build_ovpn_command, + EIPNoPkexecAvailable) from leap.eip.vpnwatcher import EIPConnectionStatus, status_watcher from leap.eip.vpnmanager import OpenVPNManager, ConnectionRefusedError @@ -17,6 +19,9 @@ logger = logging.getLogger(name=__name__) # TODO Move exceptions to their own module +class EIPNoCommandError(Exception): + pass + class ConnectionError(Exception): """ @@ -81,6 +86,10 @@ to be triggered for each one of them. self.port = None self.proto = None + self.missing_pkexec = False + self.command = None + self.args = None + self.autostart = True self._get_or_create_config() @@ -94,6 +103,14 @@ to be triggered for each one of them. config = get_config(config_file=self.config_file) self.config = config + if config.has_option('openvpn', 'autostart'): + autostart = config.getboolean('openvpn', 'autostart') + self.autostart = autostart + else: + if config.has_option('DEFAULT', 'autostart'): + autostart = config.getboolean('DEFAULT', 'autostart') + self.autostart = autostart + if config.has_option('openvpn', 'command'): commandline = config.get('openvpn', 'command') @@ -110,18 +127,16 @@ to be triggered for each one of them. else: # no command in config, we build it up. # XXX check also for command-line --command flag - command, args = build_ovpn_command(config) + try: + command, args = build_ovpn_command(config) + except EIPNoPkexecAvailable: + command = args = None + self.missing_pkexec = True + + # XXX if not command, signal error. self.command = command self.args = args - if config.has_option('openvpn', 'autostart'): - autostart = config.getboolean('openvpn', 'autostart') - self.autostart = autostart - else: - if config.has_option('DEFAULT', 'autostart'): - autostart = config.getboolean('DEFAULT', 'autostart') - self.autostart = autostart - def _launch_openvpn(self): """ invocation of openvpn binaries in a subprocess. @@ -152,6 +167,8 @@ to be triggered for each one of them. """ attempts to connect """ + if self.command is None: + raise EIPNoCommandError if self.subp is not None: print('cowardly refusing to launch subprocess again') return diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py index 3fca329c..c632ba40 100644 --- a/src/leap/eip/config.py +++ b/src/leap/eip/config.py @@ -4,6 +4,11 @@ import os import platform from leap.util.fileutil import which, mkdir_p +from leap.baseapp.permcheck import is_pkexec_in_system + + +class EIPNoPkexecAvailable(Exception): + pass def build_ovpn_options(): @@ -79,19 +84,35 @@ def build_ovpn_command(config): and a list of options. """ command = [] - use_pkexec = False + use_pkexec = True ovpn = None - if config.has_option('openvpn', 'openvpn_binary'): - ovpn = config.get('openvpn', 'openvpn_binary') - if not ovpn and config.has_option('DEFAULT', 'openvpn_binary'): - ovpn = config.get('DEFAULT', 'openvpn_binary') - if config.has_option('openvpn', 'use_pkexec'): use_pkexec = config.get('openvpn', 'use_pkexec') + if platform.system() == "Linux" and use_pkexec: + + # XXX check for both pkexec (done) + # AND a suitable authentication + # agent running. + + if not is_pkexec_in_system(): + raise EIPNoPkexecAvailable + + #TBD -- + #if not is_auth_agent_running() + # raise EIPNoPolkitAuthAgentAvailable - if use_pkexec: command.append('pkexec') + + if config.has_option('openvpn', + 'openvpn_binary'): + ovpn = config.get('openvpn', + 'openvpn_binary') + if not ovpn and config.has_option('DEFAULT', + 'openvpn_binary'): + ovpn = config.get('DEFAULT', + 'openvpn_binary') + if ovpn: command.append(ovpn) -- cgit v1.2.3 From 5c34052ef9261a47947e3e03616fe34b099b9fa4 Mon Sep 17 00:00:00 2001 From: kali Date: Fri, 3 Aug 2012 10:18:50 +0900 Subject: stub for daemon mode; disabled by now until #383 is fixed --- src/leap/eip/conductor.py | 9 ++++++--- src/leap/eip/config.py | 22 +++++++++++----------- 2 files changed, 17 insertions(+), 14 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/conductor.py b/src/leap/eip/conductor.py index 2d6ad764..eeb7f8f8 100644 --- a/src/leap/eip/conductor.py +++ b/src/leap/eip/conductor.py @@ -59,7 +59,8 @@ class OpenVPNConnection(object): """ # Connection Methods - def __init__(self, config_file=None, watcher_cb=None): + def __init__(self, config_file=None, + watcher_cb=None, debug=False): #XXX FIXME #change watcher_cb to line_observer """ @@ -74,6 +75,8 @@ to be triggered for each one of them. """ # XXX get host/port from config self.manager = OpenVPNManager() + self.debug = debug + print('conductor:%s' % debug) self.config_file = config_file self.watcher_cb = watcher_cb @@ -99,7 +102,6 @@ to be triggered for each one of them. home file, or config file passed in command line. populates command and args to be passed to subprocess. """ - #print('get or create config') config = get_config(config_file=self.config_file) self.config = config @@ -128,7 +130,8 @@ to be triggered for each one of them. # no command in config, we build it up. # XXX check also for command-line --command flag try: - command, args = build_ovpn_command(config) + command, args = build_ovpn_command(config, + debug=self.debug) except EIPNoPkexecAvailable: command = args = None self.missing_pkexec = True diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py index c632ba40..4577837a 100644 --- a/src/leap/eip/config.py +++ b/src/leap/eip/config.py @@ -11,7 +11,7 @@ class EIPNoPkexecAvailable(Exception): pass -def build_ovpn_options(): +def build_ovpn_options(daemon=False): """ build a list of options to be passed in the @@ -68,10 +68,16 @@ def build_ovpn_options(): opts.append('--config') opts.append(ovpncnf) + # we cannot run in daemon mode + # with the current subp setting. + # see: https://leap.se/code/issues/383 + #if daemon is True: + # opts.append('--daemon') + return opts -def build_ovpn_command(config): +def build_ovpn_command(config, debug=False): """ build a string with the complete openvpn invocation @@ -116,7 +122,9 @@ def build_ovpn_command(config): if ovpn: command.append(ovpn) - for opt in build_ovpn_options(): + daemon_mode = not debug + + for opt in build_ovpn_options(daemon=daemon_mode): command.append(opt) # XXX check len and raise proper error @@ -191,11 +199,3 @@ def get_config(config_file=None): config.readfp(config_file) return config - - -def get_vpn_stdout_mockup(): - # XXX REMOVE ME - command = "python" - args = ["-u", "-c", ("from eip_client import fakeclient;" - "fakeclient.write_output()")] - return command, args -- cgit v1.2.3 From 0bb8cca027ab32a54f6792ab1b1368e2f1845368 Mon Sep 17 00:00:00 2001 From: kali Date: Fri, 3 Aug 2012 10:46:22 +0900 Subject: check also for a suitable polkit-authentication-agent running fix #382. --- src/leap/eip/conductor.py | 6 +++++- src/leap/eip/config.py | 13 +++++++++---- 2 files changed, 14 insertions(+), 5 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/conductor.py b/src/leap/eip/conductor.py index eeb7f8f8..7b927143 100644 --- a/src/leap/eip/conductor.py +++ b/src/leap/eip/conductor.py @@ -10,7 +10,7 @@ from leap.util.coroutines import spawn_and_watch_process from leap.eip.config import (get_config, build_ovpn_command, - EIPNoPkexecAvailable) + EIPNoPkexecAvailable, EIPNoPolkitAuthAgentAvailable) from leap.eip.vpnwatcher import EIPConnectionStatus, status_watcher from leap.eip.vpnmanager import OpenVPNManager, ConnectionRefusedError @@ -90,6 +90,7 @@ to be triggered for each one of them. self.proto = None self.missing_pkexec = False + self.missing_auth_agent = False self.command = None self.args = None @@ -132,6 +133,9 @@ to be triggered for each one of them. try: command, args = build_ovpn_command(config, debug=self.debug) + except EIPNoPolkitAuthAgentAvailable: + command = args = None + self.missing_auth_agent = True except EIPNoPkexecAvailable: command = args = None self.missing_pkexec = True diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py index 4577837a..9583720e 100644 --- a/src/leap/eip/config.py +++ b/src/leap/eip/config.py @@ -4,13 +4,18 @@ import os import platform from leap.util.fileutil import which, mkdir_p -from leap.baseapp.permcheck import is_pkexec_in_system +from leap.baseapp.permcheck import (is_pkexec_in_system, + is_auth_agent_running) class EIPNoPkexecAvailable(Exception): pass +class EIPNoPolkitAuthAgentAvailable(Exception): + pass + + def build_ovpn_options(daemon=False): """ build a list of options @@ -34,6 +39,7 @@ def build_ovpn_options(daemon=False): opts = [] opts.append('--persist-tun') + opts.append('--persist-key') # set user and group opts.append('--user') @@ -104,9 +110,8 @@ def build_ovpn_command(config, debug=False): if not is_pkexec_in_system(): raise EIPNoPkexecAvailable - #TBD -- - #if not is_auth_agent_running() - # raise EIPNoPolkitAuthAgentAvailable + if not is_auth_agent_running(): + raise EIPNoPolkitAuthAgentAvailable command.append('pkexec') -- cgit v1.2.3 From a6416bd5e4dc57390ba0748878d229098aeca42e Mon Sep 17 00:00:00 2001 From: kali Date: Fri, 3 Aug 2012 11:17:04 +0900 Subject: added log info for polkit checks --- src/leap/eip/config.py | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'src/leap/eip') diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py index 9583720e..f0cf1d86 100644 --- a/src/leap/eip/config.py +++ b/src/leap/eip/config.py @@ -1,5 +1,6 @@ import ConfigParser import grp +import logging import os import platform @@ -7,6 +8,8 @@ from leap.util.fileutil import which, mkdir_p from leap.baseapp.permcheck import (is_pkexec_in_system, is_auth_agent_running) +logger = logging.getLogger(name=__name__) + class EIPNoPkexecAvailable(Exception): pass @@ -106,11 +109,18 @@ def build_ovpn_command(config, debug=False): # XXX check for both pkexec (done) # AND a suitable authentication # agent running. + logger.info('use_pkexec set to True') if not is_pkexec_in_system(): + logger.error('no pkexec in system') raise EIPNoPkexecAvailable if not is_auth_agent_running(): + logger.warning( + "no polkit auth agent found. " + "pkexec will use its own text " + "based authentication agent. " + "that's probably a bad idea") raise EIPNoPolkitAuthAgentAvailable command.append('pkexec') -- cgit v1.2.3 From 36b0dfacca794e9cb899b5dde2dae3b8bbc6cc43 Mon Sep 17 00:00:00 2001 From: kali Date: Tue, 7 Aug 2012 04:14:06 +0900 Subject: build default provider openvpn config. preparation for completion of #356, #355, #354, #182 if no default openvpn config is present, we build one with a preset template and the remote_ip of the eip service as the only input. right now we're taking it from the eip.cfg file. --- src/leap/eip/conductor.py | 58 +++++++++++++----- src/leap/eip/config.py | 149 ++++++++++++++++++++++++++++++++++++++++++--- src/leap/eip/vpnmanager.py | 3 +- 3 files changed, 184 insertions(+), 26 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/conductor.py b/src/leap/eip/conductor.py index 7b927143..243f1fde 100644 --- a/src/leap/eip/conductor.py +++ b/src/leap/eip/conductor.py @@ -5,12 +5,15 @@ from __future__ import (division, unicode_literals, print_function) #import threading from functools import partial import logging +import os from leap.util.coroutines import spawn_and_watch_process from leap.eip.config import (get_config, build_ovpn_command, - EIPNoPkexecAvailable, EIPNoPolkitAuthAgentAvailable) + check_or_create_default_vpnconf, + EIPNoPkexecAvailable, + EIPNoPolkitAuthAgentAvailable) from leap.eip.vpnwatcher import EIPConnectionStatus, status_watcher from leap.eip.vpnmanager import OpenVPNManager, ConnectionRefusedError @@ -51,6 +54,10 @@ class UnrecoverableError(EIPClientError): # to user-friendly msg in dialog. pass +# +# Openvpn related classes +# + class OpenVPNConnection(object): """ @@ -76,7 +83,7 @@ to be triggered for each one of them. # XXX get host/port from config self.manager = OpenVPNManager() self.debug = debug - print('conductor:%s' % debug) + #print('conductor:%s' % debug) self.config_file = config_file self.watcher_cb = watcher_cb @@ -97,23 +104,20 @@ to be triggered for each one of them. self.autostart = True self._get_or_create_config() - def _get_or_create_config(self): - """ - retrieves the config options from defaults or - home file, or config file passed in command line. - populates command and args to be passed to subprocess. - """ - config = get_config(config_file=self.config_file) - self.config = config - + def _set_autostart(self): + config = self.config if config.has_option('openvpn', 'autostart'): - autostart = config.getboolean('openvpn', 'autostart') + autostart = config.getboolean('openvpn', + 'autostart') self.autostart = autostart else: if config.has_option('DEFAULT', 'autostart'): - autostart = config.getboolean('DEFAULT', 'autostart') + autostart = config.getboolean('DEFAULT', + 'autostart') self.autostart = autostart + def _set_ovpn_command(self): + config = self.config if config.has_option('openvpn', 'command'): commandline = config.get('openvpn', 'command') @@ -124,7 +128,6 @@ to be triggered for each one of them. else: args = [] - # XXX CALL BUILD COMMAND self.command = command self.args = args else: @@ -144,6 +147,29 @@ to be triggered for each one of them. self.command = command self.args = args + def _check_ovpn_config(self): + """ + checks if there is a default openvpn config. + if not, it writes one with info from the provider + definition file + """ + # TODO + # - get --with-openvpn-config from opts + check_or_create_default_vpnconf(self.config) + + def _get_or_create_config(self): + """ + retrieves the config options from defaults or + home file, or config file passed in command line. + populates command and args to be passed to subprocess. + """ + config = get_config(config_file=self.config_file) + self.config = config + + self._set_autostart() + self._set_ovpn_command() + self._check_ovpn_config() + def _launch_openvpn(self): """ invocation of openvpn binaries in a subprocess. @@ -167,8 +193,8 @@ to be triggered for each one of them. self.subp = subp self.watcher = watcher - conn_result = self.status.CONNECTED - return conn_result + #conn_result = self.status.CONNECTED + #return conn_result def _try_connection(self): """ diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py index f0cf1d86..9af6f57a 100644 --- a/src/leap/eip/config.py +++ b/src/leap/eip/config.py @@ -9,6 +9,7 @@ from leap.baseapp.permcheck import (is_pkexec_in_system, is_auth_agent_running) logger = logging.getLogger(name=__name__) +logger.setLevel('DEBUG') class EIPNoPkexecAvailable(Exception): @@ -19,6 +20,129 @@ class EIPNoPolkitAuthAgentAvailable(Exception): pass +OPENVPN_CONFIG_TEMPLATE = """#Autogenerated by eip-client wizard +remote {VPN_REMOTE_HOST} {VPN_REMOTE_PORT} + +client +dev tun +persist-tun +persist-key +proto udp +tls-client +remote-cert-tls server + +cert {LEAP_EIP_KEYS} +key {LEAP_EIP_KEYS} +ca {LEAP_EIP_KEYS} +""" + + +def get_config_dir(): + """ + get the base dir for all leap config + @rparam: config path + @rtype: string + """ + # TODO + # check for $XDG_CONFIG_HOME var? + # get a more sensible path for win/mac + # kclair: opinion? ^^ + return os.path.expanduser( + os.path.join('~', + '.config', + 'leap')) + + +def get_config_file(filename, folder=None): + """ + concatenates the given filename + with leap config dir. + @param filename: name of the file + @type filename: string + @rparam: full path to config file + """ + path = [] + path.append(get_config_dir()) + if folder is not None: + path.append(folder) + path.append(filename) + return os.path.join(*path) + + +def get_default_provider_path(): + default_subpath = os.path.join("providers", + "default") + default_provider_path = get_config_file( + '', + folder=default_subpath) + return default_provider_path + + +def check_or_create_default_vpnconf(config): + """ + checks that a vpn config file + exists for a default provider, + or creates one if it does not. + ATM REQURES A [provider] section in + eip.cfg with _at least_ a remote_ip value + """ + default_provider_path = get_default_provider_path() + + if not os.path.isdir(default_provider_path): + mkdir_p(default_provider_path) + + conf_file = get_config_file( + 'openvpn.conf', + folder=default_provider_path) + + if os.path.isfile(conf_file): + return + else: + logger.debug( + 'missing default openvpn config\n' + 'creating one...') + + # We're getting provider from eip.cfg + # by now. Get it from a list of gateways + # instead. + + remote_ip = config.get('provider', + 'remote_ip') + + # XXX check that IT LOOKS LIKE AN IP!!! + if config.has_option('provider', 'remote_port'): + remote_port = config.get('provider', + 'remote_port') + else: + remote_port = 1194 + + default_subpath = os.path.join("providers", + "default") + default_provider_path = get_config_file( + '', + folder=default_subpath) + + if not os.path.isdir(default_provider_path): + mkdir_p(default_provider_path) + + conf_file = get_config_file( + 'openvpn.conf', + folder=default_provider_path) + + # XXX keys have to be manually placed by now + keys_file = get_config_file( + 'openvpn.keys', + folder=default_provider_path) + + ovpn_config = OPENVPN_CONFIG_TEMPLATE.format( + VPN_REMOTE_HOST=remote_ip, + VPN_REMOTE_PORT=remote_port, + LEAP_EIP_KEYS=keys_file) + + with open(conf_file, 'wb') as f: + f.write(ovpn_config) + + def build_ovpn_options(daemon=False): """ build a list of options @@ -41,8 +165,10 @@ def build_ovpn_options(daemon=False): group = grp.getgrgid(gid).gr_name opts = [] - opts.append('--persist-tun') - opts.append('--persist-key') + + #moved to config files + #opts.append('--persist-tun') + #opts.append('--persist-key') # set user and group opts.append('--user') @@ -69,19 +195,25 @@ def build_ovpn_options(daemon=False): # XXX which is a good choice? opts.append('7777') - # remaining config options, in a file + # remaining config options will go in a file + # NOTE: we will build this file from # the service definition file. - ovpncnf = os.path.expanduser( - '~/.config/leap/openvpn.conf') + # XXX override from --with-openvpn-config + opts.append('--config') + + default_provider_path = get_default_provider_path() + ovpncnf = get_config_file( + 'openvpn.conf', + folder=default_provider_path) opts.append(ovpncnf) # we cannot run in daemon mode # with the current subp setting. # see: https://leap.se/code/issues/383 #if daemon is True: - # opts.append('--daemon') + #opts.append('--daemon') return opts @@ -192,8 +324,7 @@ def get_config(config_file=None): config = ConfigParser.ConfigParser(defaults) if not config_file: - fpath = os.path.expanduser( - '~/.config/leap/eip.cfg') + fpath = get_config_file('eip.cfg') if not os.path.isfile(fpath): dpath, cfile = os.path.split(fpath) if not os.path.isdir(dpath): @@ -203,7 +334,6 @@ def get_config(config_file=None): config_file = open(fpath) #TODO - # - get a more sensible path for win/mac # - convert config_file to list; # look in places like /etc/leap/eip.cfg # for global settings. @@ -211,6 +341,7 @@ def get_config(config_file=None): # at this point, the file should exist. # errors would have been raised above. + config.readfp(config_file) return config diff --git a/src/leap/eip/vpnmanager.py b/src/leap/eip/vpnmanager.py index 78777cfb..caf7ab76 100644 --- a/src/leap/eip/vpnmanager.py +++ b/src/leap/eip/vpnmanager.py @@ -6,6 +6,7 @@ import telnetlib import time logger = logging.getLogger(name=__name__) +logger.setLevel('DEBUG') TELNET_PORT = 23 @@ -74,7 +75,7 @@ class OpenVPNManager(object): self.with_errors = False def forget_errors(self): - print('forgetting errors') + logger.debug('forgetting errors') self.with_errors = False def connect(self): -- cgit v1.2.3 From 530e10214a6f018909714b288d997df13ab4f9df Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 8 Aug 2012 06:53:10 +0900 Subject: check for bad permissions on vpn key files --- src/leap/eip/conductor.py | 21 +++++++++++++++--- src/leap/eip/config.py | 56 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 73 insertions(+), 4 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/conductor.py b/src/leap/eip/conductor.py index 243f1fde..3f40f068 100644 --- a/src/leap/eip/conductor.py +++ b/src/leap/eip/conductor.py @@ -5,15 +5,16 @@ from __future__ import (division, unicode_literals, print_function) #import threading from functools import partial import logging -import os from leap.util.coroutines import spawn_and_watch_process - +# XXX import eip.config as eipconfig from leap.eip.config import (get_config, build_ovpn_command, check_or_create_default_vpnconf, + check_vpn_keys, EIPNoPkexecAvailable, - EIPNoPolkitAuthAgentAvailable) + EIPNoPolkitAuthAgentAvailable, + EIPInitBadKeyFilePermError) from leap.eip.vpnwatcher import EIPConnectionStatus, status_watcher from leap.eip.vpnmanager import OpenVPNManager, ConnectionRefusedError @@ -21,6 +22,7 @@ logger = logging.getLogger(name=__name__) # TODO Move exceptions to their own module +# eip.exceptions class EIPNoCommandError(Exception): pass @@ -98,11 +100,14 @@ to be triggered for each one of them. self.missing_pkexec = False self.missing_auth_agent = False + self.bad_keyfile_perms = False + self.command = None self.args = None self.autostart = True self._get_or_create_config() + self._check_vpn_keys() def _set_autostart(self): config = self.config @@ -170,6 +175,16 @@ to be triggered for each one of them. self._set_ovpn_command() self._check_ovpn_config() + def _check_vpn_keys(self): + """ + checks for correct permissions on vpn keys + """ + try: + check_vpn_keys(self.config) + except EIPInitBadKeyFilePermError: + logger.error('error while checking vpn keys') + self.bad_keyfile_perms = True + def _launch_openvpn(self): """ invocation of openvpn binaries in a subprocess. diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py index 9af6f57a..91c3953b 100644 --- a/src/leap/eip/config.py +++ b/src/leap/eip/config.py @@ -4,13 +4,17 @@ import logging import os import platform -from leap.util.fileutil import which, mkdir_p +from leap.util.fileutil import (which, mkdir_p, + check_and_fix_urw_only) from leap.baseapp.permcheck import (is_pkexec_in_system, is_auth_agent_running) logger = logging.getLogger(name=__name__) logger.setLevel('DEBUG') +# XXX move exceptions to +# from leap.eip import exceptions as eip_exceptions + class EIPNoPkexecAvailable(Exception): pass @@ -20,6 +24,14 @@ class EIPNoPolkitAuthAgentAvailable(Exception): pass +class EIPInitNoKeyFileError(Exception): + pass + + +class EIPInitBadKeyFilePermError(Exception): + pass + + OPENVPN_CONFIG_TEMPLATE = """#Autogenerated by eip-client wizard remote {VPN_REMOTE_HOST} {VPN_REMOTE_PORT} @@ -345,3 +357,45 @@ def get_config(config_file=None): config.readfp(config_file) return config + + +def check_vpn_keys(config): + """ + performs an existance and permission check + over the openvpn keys file. + Currently we're expecting a single file + per provider, containing the CA cert, + the provider key, and our client certificate + """ + + keyopt = ('provider', 'keyfile') + + # XXX at some point, + # should separate between CA, provider cert + # and our certificate. + # make changes in the default provider template + # accordingly. + + # get vpn keys + if config.has_option(*keyopt): + keyfile = config.get(*keyopt) + else: + keyfile = get_config_file( + 'openvpn.keys', + folder=get_default_provider_path()) + logger.debug('keyfile = %s', keyfile) + + # if no keys, raise error. + # should be catched by the ui and signal user. + + if not os.path.isfile(keyfile): + logger.error('key file %s not found. aborting.', + keyfile) + raise EIPInitNoKeyFileError + + # check proper permission on keys + # bad perms? try to fix them + try: + check_and_fix_urw_only(keyfile) + except OSError: + raise EIPInitBadKeyFilePermError -- cgit v1.2.3 From e81ddf7648e1075a15d8add11cd975a73aa09926 Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 8 Aug 2012 07:01:27 +0900 Subject: catch missing keyfile error --- src/leap/eip/conductor.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/leap/eip') diff --git a/src/leap/eip/conductor.py b/src/leap/eip/conductor.py index 3f40f068..11b0358c 100644 --- a/src/leap/eip/conductor.py +++ b/src/leap/eip/conductor.py @@ -14,6 +14,7 @@ from leap.eip.config import (get_config, build_ovpn_command, check_vpn_keys, EIPNoPkexecAvailable, EIPNoPolkitAuthAgentAvailable, + EIPInitNoKeyFileError, EIPInitBadKeyFilePermError) from leap.eip.vpnwatcher import EIPConnectionStatus, status_watcher from leap.eip.vpnmanager import OpenVPNManager, ConnectionRefusedError @@ -101,6 +102,7 @@ to be triggered for each one of them. self.missing_pkexec = False self.missing_auth_agent = False self.bad_keyfile_perms = False + self.missing_vpn_keyfile = False self.command = None self.args = None @@ -181,6 +183,8 @@ to be triggered for each one of them. """ try: check_vpn_keys(self.config) + except EIPInitNoKeyFileError: + self.missing_vpn_keyfile = True except EIPInitBadKeyFilePermError: logger.error('error while checking vpn keys') self.bad_keyfile_perms = True -- cgit v1.2.3 From c217bd1f1456cf10ceabf698ea6f4dd8f636f454 Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 8 Aug 2012 07:22:36 +0900 Subject: check for validity of the remote_ip entry (is ip?) --- src/leap/eip/conductor.py | 17 +++++++++++++++-- src/leap/eip/config.py | 31 ++++++++++++++++++++++++++++--- 2 files changed, 43 insertions(+), 5 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/conductor.py b/src/leap/eip/conductor.py index 11b0358c..8f9d6051 100644 --- a/src/leap/eip/conductor.py +++ b/src/leap/eip/conductor.py @@ -8,12 +8,16 @@ import logging from leap.util.coroutines import spawn_and_watch_process -# XXX import eip.config as eipconfig +# XXX from leap.eip import config as eipconfig +# from leap.eip import exceptions as eip_exceptions + from leap.eip.config import (get_config, build_ovpn_command, check_or_create_default_vpnconf, check_vpn_keys, EIPNoPkexecAvailable, EIPNoPolkitAuthAgentAvailable, + EIPInitNoProviderError, + EIPInitBadProviderError, EIPInitNoKeyFileError, EIPInitBadKeyFilePermError) from leap.eip.vpnwatcher import EIPConnectionStatus, status_watcher @@ -103,6 +107,8 @@ to be triggered for each one of them. self.missing_auth_agent = False self.bad_keyfile_perms = False self.missing_vpn_keyfile = False + self.missing_provider = False + self.bad_provider = False self.command = None self.args = None @@ -162,7 +168,14 @@ to be triggered for each one of them. """ # TODO # - get --with-openvpn-config from opts - check_or_create_default_vpnconf(self.config) + try: + check_or_create_default_vpnconf(self.config) + except EIPInitNoProviderError: + logger.error('missing default provider definition') + self.missing_provider = True + except EIPInitBadProviderError: + logger.error('bad provider definition') + self.bad_provider = True def _get_or_create_config(self): """ diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py index 91c3953b..6118c9de 100644 --- a/src/leap/eip/config.py +++ b/src/leap/eip/config.py @@ -3,6 +3,7 @@ import grp import logging import os import platform +import socket from leap.util.fileutil import (which, mkdir_p, check_and_fix_urw_only) @@ -24,6 +25,14 @@ class EIPNoPolkitAuthAgentAvailable(Exception): pass +class EIPInitNoProviderError(Exception): + pass + + +class EIPInitBadProviderError(Exception): + pass + + class EIPInitNoKeyFileError(Exception): pass @@ -90,6 +99,14 @@ def get_default_provider_path(): return default_provider_path +def validate_ip(ip_str): + """ + raises exception if the ip_str is + not a valid representation of an ip + """ + socket.inet_aton(ip_str) + + def check_or_create_default_vpnconf(config): """ checks that a vpn config file @@ -118,10 +135,18 @@ def check_or_create_default_vpnconf(config): # by now. Get it from a list of gateways # instead. - remote_ip = config.get('provider', - 'remote_ip') + try: + remote_ip = config.get('provider', + 'remote_ip') + validate_ip(remote_ip) + + except ConfigParser.NoOptionError: + raise EIPInitNoProviderError + + except socket.error: + # this does not look like an ip, dave + raise EIPInitBadProviderError - # XXX check that IT LOOKS LIKE AN IP!!! if config.has_option('provider', 'remote_port'): remote_port = config.get('provider', 'remote_port') -- cgit v1.2.3 From 07ed489ed46140d6de814667ab3e64c6076f3776 Mon Sep 17 00:00:00 2001 From: antialias Date: Tue, 14 Aug 2012 16:10:11 -0700 Subject: Works and is now ready to write tests for. --- src/leap/eip/conductor.py | 1 - src/leap/eip/vpnmanager.py | 263 --------------------------------------------- 2 files changed, 264 deletions(-) delete mode 100644 src/leap/eip/vpnmanager.py (limited to 'src/leap/eip') diff --git a/src/leap/eip/conductor.py b/src/leap/eip/conductor.py index 8f9d6051..776a1092 100644 --- a/src/leap/eip/conductor.py +++ b/src/leap/eip/conductor.py @@ -337,4 +337,3 @@ class EIPConductor(OpenVPNConnection): self.error_queue.append(except_msg) logger.error("Failed Connection: %s" % unicode(except_msg)) - return conn_result diff --git a/src/leap/eip/vpnmanager.py b/src/leap/eip/vpnmanager.py deleted file mode 100644 index caf7ab76..00000000 --- a/src/leap/eip/vpnmanager.py +++ /dev/null @@ -1,263 +0,0 @@ -from __future__ import (print_function) -import logging -import os -import socket -import telnetlib -import time - -logger = logging.getLogger(name=__name__) -logger.setLevel('DEBUG') - -TELNET_PORT = 23 - - -class MissingSocketError(Exception): - pass - - -class ConnectionRefusedError(Exception): - pass - - -class UDSTelnet(telnetlib.Telnet): - - def open(self, host, port=0, timeout=socket._GLOBAL_DEFAULT_TIMEOUT): - """Connect to a host. If port is 'unix', it - will open a connection over unix docmain sockets. - - The optional second argument is the port number, which - defaults to the standard telnet port (23). - - Don't try to reopen an already connected instance. - """ - self.eof = 0 - if not port: - port = TELNET_PORT - self.host = host - self.port = port - self.timeout = timeout - - if self.port == "unix": - # unix sockets spoken - if not os.path.exists(self.host): - raise MissingSocketError - self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - try: - self.sock.connect(self.host) - except socket.error: - raise ConnectionRefusedError - else: - self.sock = socket.create_connection((host, port), timeout) - - -# this class based in code from cube-routed project - -class OpenVPNManager(object): - """ - Run commands over OpenVPN management interface - and parses the output. - """ - # XXX might need a lock to avoid - # race conditions here... - - def __init__(self, host="/tmp/.eip.sock", port="unix", password=None): - #XXX hardcoded host here. change. - self.host = host - if isinstance(port, str) and port.isdigit(): - port = int(port) - self.port = port - self.password = password - self.tn = None - - #XXX workaround for signaling - #the ui that we don't know how to - #manage a connection error - self.with_errors = False - - def forget_errors(self): - logger.debug('forgetting errors') - self.with_errors = False - - def connect(self): - """Connect to openvpn management interface""" - try: - self.close() - except: - #XXX don't like this general - #catch here. - pass - if self.connected(): - return True - self.tn = UDSTelnet(self.host, self.port) - - # XXX make password optional - # specially for win plat. we should generate - # the pass on the fly when invoking manager - # from conductor - - #self.tn.read_until('ENTER PASSWORD:', 2) - #self.tn.write(self.password + '\n') - #self.tn.read_until('SUCCESS:', 2) - - self._seek_to_eof() - self.forget_errors() - return True - - def _seek_to_eof(self): - """ - Read as much as available. Position seek pointer to end of stream - """ - b = self.tn.read_eager() - while b: - b = self.tn.read_eager() - - def connected(self): - """ - Returns True if connected - rtype: bool - """ - #return bool(getattr(self, 'tn', None)) - try: - assert self.tn - return True - except: - #XXX get rid of - #this pokemon exception!!! - return False - - def close(self, announce=True): - """ - Close connection to openvpn management interface - """ - if announce: - self.tn.write("quit\n") - self.tn.read_all() - self.tn.get_socket().close() - del self.tn - - def _send_command(self, cmd, tries=0): - """ - Send a command to openvpn and return response as list - """ - if tries > 3: - return [] - if not self.connected(): - try: - self.connect() - except MissingSocketError: - #XXX capture more helpful error - #messages - #pass - return self.make_error() - try: - self.tn.write(cmd + "\n") - except socket.error: - logger.error('socket error') - print('socket error!') - self.close(announce=False) - self._send_command(cmd, tries=tries + 1) - return [] - buf = self.tn.read_until(b"END", 2) - self._seek_to_eof() - blist = buf.split('\r\n') - if blist[-1].startswith('END'): - del blist[-1] - return blist - else: - return [] - - def _send_short_command(self, cmd): - """ - parse output from commands that are - delimited by "success" instead - """ - if not self.connected(): - self.connect() - self.tn.write(cmd + "\n") - # XXX not working? - buf = self.tn.read_until(b"SUCCESS", 2) - self._seek_to_eof() - blist = buf.split('\r\n') - return blist - - # - # useful vpn commands - # - - def pid(self): - #XXX broken - return self._send_short_command("pid") - - def make_error(self): - """ - capture error and wrap it in an - understandable format - """ - #XXX get helpful error codes - self.with_errors = True - now = int(time.time()) - return '%s,LAUNCHER ERROR,ERROR,-,-' % now - - def state(self): - """ - OpenVPN command: state - """ - state = self._send_command("state") - if not state: - return None - if isinstance(state, str): - return state - if isinstance(state, list): - if len(state) == 1: - return state[0] - else: - return state[-1] - - def status(self): - """ - OpenVPN command: status - """ - status = self._send_command("status") - return status - - def status2(self): - """ - OpenVPN command: last 2 statuses - """ - return self._send_command("status 2") - - # - # parse info - # - - def get_status_io(self): - status = self.status() - if isinstance(status, str): - lines = status.split('\n') - if isinstance(status, list): - lines = status - try: - (header, when, tun_read, tun_write, - tcp_read, tcp_write, auth_read) = tuple(lines) - except ValueError: - return None - - when_ts = time.strptime(when.split(',')[1], "%a %b %d %H:%M:%S %Y") - sep = ',' - # XXX cleanup! - tun_read = tun_read.split(sep)[1] - tun_write = tun_write.split(sep)[1] - tcp_read = tcp_read.split(sep)[1] - tcp_write = tcp_write.split(sep)[1] - auth_read = auth_read.split(sep)[1] - - # XXX this could be a named tuple. prettier. - return when_ts, (tun_read, tun_write, tcp_read, tcp_write, auth_read) - - def get_connection_state(self): - state = self.state() - if state is not None: - ts, status_step, ok, ip, remote = state.split(',') - ts = time.gmtime(float(ts)) - # XXX this could be a named tuple. prettier. - return ts, status_step, ok, ip, remote -- cgit v1.2.3 From 451189d369f6661a67a1692945e68b5128cb9a65 Mon Sep 17 00:00:00 2001 From: antialias Date: Thu, 16 Aug 2012 16:06:53 -0700 Subject: Cleaned up files and file names using the PEP 8 style guide. --- src/leap/eip/eipconnection.py | 270 +++++++++++++++++++++++++ src/leap/eip/openvpnconnection.py | 408 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 678 insertions(+) create mode 100644 src/leap/eip/eipconnection.py create mode 100644 src/leap/eip/openvpnconnection.py (limited to 'src/leap/eip') diff --git a/src/leap/eip/eipconnection.py b/src/leap/eip/eipconnection.py new file mode 100644 index 00000000..f16f01f5 --- /dev/null +++ b/src/leap/eip/eipconnection.py @@ -0,0 +1,270 @@ +""" +EIP Connection Class +""" + +from leap.OpenVPNConnection import OpenVPNConnection, MissingSocketError, ConnectionRefusedError +from leap.Connection import ConnectionError + +class EIPConnection(OpenVPNConnection): + """ + Manages the execution of the OpenVPN process, auto starts, monitors the + network connection, handles configuration, fixes leaky hosts, handles + errors, etc. + Preferences will be stored via the Storage API. (TBD) + Status updates (connected, bandwidth, etc) are signaled to the GUI. + """ + + def __init__(self, *args, **kwargs): + self.settingsfile = kwargs.get('settingsfile', None) + self.logfile = kwargs.get('logfile', None) + self.error_queue = [] + self.desired_con_state = None # ??? + + status_signals = kwargs.pop('status_signals', None) + self.status = EIPConnectionStatus(callbacks=status_signals) + + super(EIPConnection, self).__init__(*args, **kwargs) + + def connect(self): + """ + entry point for connection process + """ + self.forget_errors() + self._try_connection() + # XXX should capture errors? + + def disconnect(self): + """ + disconnects client + """ + self._disconnect() + self.status.change_to(self.status.DISCONNECTED) + pass + + def shutdown(self): + """ + shutdown and quit + """ + self.desired_con_state = self.status.DISCONNECTED + + def connection_state(self): + """ + returns the current connection state + """ + return self.status.current + + def desired_connection_state(self): + """ + returns the desired_connection state + """ + return self.desired_con_state + + def poll_connection_state(self): + """ + """ + try: + state = self.get_connection_state() + except ConnectionRefusedError: + # connection refused. might be not ready yet. + return + if not state: + return + (ts, status_step, + ok, ip, remote) = state + self.status.set_vpn_state(status_step) + status_step = self.status.get_readable_status() + return (ts, status_step, ok, ip, remote) + + def get_icon_name(self): + """ + get icon name from status object + """ + return self.status.get_state_icon() + + # + # private methods + # + + def _disconnect(self): + """ + private method for disconnecting + """ + if self.subp is not None: + self.subp.terminate() + self.subp = None + # XXX signal state changes! :) + + def _is_alive(self): + """ + don't know yet + """ + pass + + def _connect(self): + """ + entry point for connection cascade methods. + """ + #conn_result = ConState.DISCONNECTED + try: + conn_result = self._try_connection() + except UnrecoverableError as except_msg: + logger.error("FATAL: %s" % unicode(except_msg)) + conn_result = self.status.UNRECOVERABLE + except Exception as except_msg: + self.error_queue.append(except_msg) + logger.error("Failed Connection: %s" % + unicode(except_msg)) + return conn_result + +"""generic watcher object that keeps track of connection status""" +# This should be deprecated in favor of daemon mode + management +# interface. But we can leave it here for debug purposes. + + +class EIPConnectionStatus(object): + """ + Keep track of client (gui) and openvpn + states. + + These are the OpenVPN states: + CONNECTING -- OpenVPN's initial state. + WAIT -- (Client only) Waiting for initial response + from server. + AUTH -- (Client only) Authenticating with server. + GET_CONFIG -- (Client only) Downloading configuration options + from server. + ASSIGN_IP -- Assigning IP address to virtual network + interface. + ADD_ROUTES -- Adding routes to system. + CONNECTED -- Initialization Sequence Completed. + RECONNECTING -- A restart has occurred. + EXITING -- A graceful exit is in progress. + + We add some extra states: + + DISCONNECTED -- GUI initial state. + UNRECOVERABLE -- An unrecoverable error has been raised + while invoking openvpn service. + """ + CONNECTING = 1 + WAIT = 2 + AUTH = 3 + GET_CONFIG = 4 + ASSIGN_IP = 5 + ADD_ROUTES = 6 + CONNECTED = 7 + RECONNECTING = 8 + EXITING = 9 + + # gui specific states: + UNRECOVERABLE = 11 + DISCONNECTED = 0 + + def __init__(self, callbacks=None): + """ + EIPConnectionStatus is initialized with a tuple + of signals to be triggered. + :param callbacks: a tuple of (callable) observers + :type callbacks: tuple + """ + # (callbacks to connect to signals in Qt-land) + self.current = self.DISCONNECTED + self.previous = None + self.callbacks = callbacks + + def get_readable_status(self): + # XXX DRY status / labels a little bit. + # think we'll want to i18n this. + human_status = { + 0: 'disconnected', + 1: 'connecting', + 2: 'waiting', + 3: 'authenticating', + 4: 'getting config', + 5: 'assigning ip', + 6: 'adding routes', + 7: 'connected', + 8: 'reconnecting', + 9: 'exiting', + 11: 'unrecoverable error', + } + return human_status[self.current] + + def get_state_icon(self): + """ + returns the high level icon + for each fine-grain openvpn state + """ + connecting = (self.CONNECTING, + self.WAIT, + self.AUTH, + self.GET_CONFIG, + self.ASSIGN_IP, + self.ADD_ROUTES) + connected = (self.CONNECTED,) + disconnected = (self.DISCONNECTED, + self.UNRECOVERABLE) + + # this can be made smarter, + # but it's like it'll change, + # so +readability. + + if self.current in connecting: + return "connecting" + if self.current in connected: + return "connected" + if self.current in disconnected: + return "disconnected" + + def set_vpn_state(self, status): + """ + accepts a state string from the management + interface, and sets the internal state. + :param status: openvpn STATE (uppercase). + :type status: str + """ + if hasattr(self, status): + self.change_to(getattr(self, status)) + + def set_current(self, to): + """ + setter for the 'current' property + :param to: destination state + :type to: int + """ + self.current = to + + def change_to(self, to): + """ + :param to: destination state + :type to: int + """ + if to == self.current: + return + changed = False + from_ = self.current + self.current = to + + # We can add transition restrictions + # here to ensure no transitions are + # allowed outside the fsm. + + self.set_current(to) + changed = True + + #trigger signals (as callbacks) + #print('current state: %s' % self.current) + if changed: + self.previous = from_ + if self.callbacks: + for cb in self.callbacks: + if callable(cb): + cb(self) + + + +class EIPClientError(ConnectionError): + """ + base EIPClient Exception + """ + pass diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py new file mode 100644 index 00000000..a26059a7 --- /dev/null +++ b/src/leap/eip/openvpnconnection.py @@ -0,0 +1,408 @@ +""" +OpenVPN Connection +""" + +from __future__ import (print_function) +import logging +import os +import socket +import telnetlib +import time +from functools import partial + +logger = logging.getLogger(name=__name__) + +from leap.utils.coroutines import spawn_and_watch_process +from leap.baseapp.config import get_config +from leap.Connection import Connection + +class OpenVPNConnection(Connection): + """ + All related to invocation + of the openvpn binary + """ + # Connection Methods + + def __init__(self, config_file=None, watcher_cb=None,host="/tmp/.eip.sock", port="unix", password=None): + #XXX FIXME + #change watcher_cb to line_observer + """ + :param config_file: configuration file to read from + :param watcher_cb: callback to be \ +called for each line in watched stdout + :param signal_map: dictionary of signal names and callables \ +to be triggered for each one of them. + :type config_file: str + :type watcher_cb: function + :type signal_map: dict + """ + + self.config_file = config_file + self.watcher_cb = watcher_cb + #self.signal_maps = signal_maps + + self.subp = None + self.watcher = None + + self.server = None + self.port = None + self.proto = None + + self.autostart = True + + self._get_config() + + #Get this info from the Configuration Class + #XXX hardcoded host here. change. + self.host = host + if isinstance(port, str) and port.isdigit(): + port = int(port) + self.port = port + self.password = password + self.tn = None + + #XXX workaround for signaling + #the ui that we don't know how to + #manage a connection error + self.with_errors = False + + + def _set_command_mockup(self): + """ + sets command and args for a command mockup + that just mimics the output from the real thing + """ + command, args = get_vpn_stdout_mockup() + self.command, self.args = command, args + + def _get_config(self): + """ + retrieves the config options from defaults or + home file, or config file passed in command line. + """ + config = get_config(config_file=self.config_file) + self.config = config + + if config.has_option('openvpn', 'command'): + commandline = config.get('openvpn', 'command') + if commandline == "mockup": + self._set_command_mockup() + return + command_split = commandline.split(' ') + command = command_split[0] + if len(command_split) > 1: + args = command_split[1:] + else: + args = [] + self.command = command + #print("debug: command = %s" % command) + self.args = args + else: + self._set_command_mockup() + + if config.has_option('openvpn', 'autostart'): + autostart = config.get('openvpn', 'autostart') + self.autostart = autostart + + def _launch_openvpn(self): + """ + invocation of openvpn binaries in a subprocess. + """ + #XXX TODO: + #deprecate watcher_cb, + #use _only_ signal_maps instead + + if self.watcher_cb is not None: + linewrite_callback = self.watcher_cb + else: + #XXX get logger instead + linewrite_callback = lambda line: print('watcher: %s' % line) + + observers = (linewrite_callback, + partial(self.status_watcher, self.status)) + subp, watcher = spawn_and_watch_process( + self.command, + self.args, + observers=observers) + self.subp = subp + self.watcher = watcher + + conn_result = self.status.CONNECTED + return conn_result + + def _try_connection(self): + """ + attempts to connect + """ + if self.subp is not None: + print('cowardly refusing to launch subprocess again') + return + self._launch_openvpn() + + def cleanup(self): + """ + terminates child subprocess + """ + if self.subp: + self.subp.terminate() + + + #Here are the actual code to manage OpenVPN Connection + #TODO: Look into abstraction them and moving them up into base class + # this code based on code from cube-routed project + + """ + Run commands over OpenVPN management interface + and parses the output. + """ + # XXX might need a lock to avoid + # race conditions here... + + def forget_errors(self): + print('forgetting errors') + self.with_errors = False + + def connect(self): + """Connect to openvpn management interface""" + try: + self.close() + except: + #XXX don't like this general + #catch here. + pass + if self.connected(): + return True + self.tn = UDSTelnet(self.host, self.port) + + # XXX make password optional + # specially for win plat. we should generate + # the pass on the fly when invoking manager + # from conductor + + #self.tn.read_until('ENTER PASSWORD:', 2) + #self.tn.write(self.password + '\n') + #self.tn.read_until('SUCCESS:', 2) + + self._seek_to_eof() + self.forget_errors() + return True + + def _seek_to_eof(self): + """ + Read as much as available. Position seek pointer to end of stream + """ + b = self.tn.read_eager() + while b: + b = self.tn.read_eager() + + def connected(self): + """ + Returns True if connected + rtype: bool + """ + #return bool(getattr(self, 'tn', None)) + try: + assert self.tn + return True + except: + #XXX get rid of + #this pokemon exception!!! + return False + + def close(self, announce=True): + """ + Close connection to openvpn management interface + """ + if announce: + self.tn.write("quit\n") + self.tn.read_all() + self.tn.get_socket().close() + del self.tn + + def _send_command(self, cmd, tries=0): + """ + Send a command to openvpn and return response as list + """ + if tries > 3: + return [] + if not self.connected(): + try: + self.connect() + except MissingSocketError: + #XXX capture more helpful error + #messages + #pass + return self.make_error() + try: + self.tn.write(cmd + "\n") + except socket.error: + logger.error('socket error') + print('socket error!') + self.close(announce=False) + self._send_command(cmd, tries=tries + 1) + return [] + buf = self.tn.read_until(b"END", 2) + self._seek_to_eof() + blist = buf.split('\r\n') + if blist[-1].startswith('END'): + del blist[-1] + return blist + else: + return [] + + def _send_short_command(self, cmd): + """ + parse output from commands that are + delimited by "success" instead + """ + if not self.connected(): + self.connect() + self.tn.write(cmd + "\n") + # XXX not working? + buf = self.tn.read_until(b"SUCCESS", 2) + self._seek_to_eof() + blist = buf.split('\r\n') + return blist + + # + # useful vpn commands + # + + def pid(self): + #XXX broken + return self._send_short_command("pid") + + def make_error(self): + """ + capture error and wrap it in an + understandable format + """ + #XXX get helpful error codes + self.with_errors = True + now = int(time.time()) + return '%s,LAUNCHER ERROR,ERROR,-,-' % now + + def state(self): + """ + OpenVPN command: state + """ + state = self._send_command("state") + if not state: + return None + if isinstance(state, str): + return state + if isinstance(state, list): + if len(state) == 1: + return state[0] + else: + return state[-1] + + def status(self): + """ + OpenVPN command: status + """ + status = self._send_command("status") + return status + + def status2(self): + """ + OpenVPN command: last 2 statuses + """ + return self._send_command("status 2") + + # + # parse info + # + + def get_status_io(self): + status = self.status() + if isinstance(status, str): + lines = status.split('\n') + if isinstance(status, list): + lines = status + try: + (header, when, tun_read, tun_write, + tcp_read, tcp_write, auth_read) = tuple(lines) + except ValueError: + return None + + when_ts = time.strptime(when.split(',')[1], "%a %b %d %H:%M:%S %Y") + sep = ',' + # XXX cleanup! + tun_read = tun_read.split(sep)[1] + tun_write = tun_write.split(sep)[1] + tcp_read = tcp_read.split(sep)[1] + tcp_write = tcp_write.split(sep)[1] + auth_read = auth_read.split(sep)[1] + + # XXX this could be a named tuple. prettier. + return when_ts, (tun_read, tun_write, tcp_read, tcp_write, auth_read) + + def get_connection_state(self): + state = self.state() + if state is not None: + ts, status_step, ok, ip, remote = state.split(',') + ts = time.gmtime(float(ts)) + # XXX this could be a named tuple. prettier. + return ts, status_step, ok, ip, remote + + def status_watcher(self, cs, line): + """ + a wrapper that calls to ConnectionStatus object + :param cs: a EIPConnectionStatus instance + :type cs: EIPConnectionStatus object + :param line: a single line of the watched output + :type line: str + """ + #print('status watcher watching') + + # from the mullvad code, should watch for + # things like: + # "Initialization Sequence Completed" + # "With Errors" + # "Tap-Win32" + + if "Completed" in line: + cs.change_to(cs.CONNECTED) + return + + if "Initial packet from" in line: + cs.change_to(cs.CONNECTING) + return + + + +class MissingSocketError(Exception): + pass + + +class ConnectionRefusedError(Exception): + pass + +class UDSTelnet(telnetlib.Telnet): + + def open(self, host, port=23, timeout=socket._GLOBAL_DEFAULT_TIMEOUT): + """Connect to a host. If port is 'unix', it + will open a connection over unix docmain sockets. + + The optional second argument is the port number, which + defaults to the standard telnet port (23). + + Don't try to reopen an already connected instance. + """ + self.eof = 0 + self.host = host + self.port = port + self.timeout = timeout + + if self.port == "unix": + # unix sockets spoken + if not os.path.exists(self.host): + raise MissingSocketError + self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + try: + self.sock.connect(self.host) + except socket.error: + raise ConnectionRefusedError + else: + self.sock = socket.create_connection((host, port), timeout) -- cgit v1.2.3 From f5948577939dce4f85dd86f37c0823a0a852e074 Mon Sep 17 00:00:00 2001 From: kali Date: Tue, 21 Aug 2012 03:11:32 +0900 Subject: fix imports + style cleaning --- src/leap/eip/eipconnection.py | 13 +++++++++---- src/leap/eip/openvpnconnection.py | 35 +++++++++++++++++++++-------------- 2 files changed, 30 insertions(+), 18 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/eipconnection.py b/src/leap/eip/eipconnection.py index f16f01f5..a0fdd77d 100644 --- a/src/leap/eip/eipconnection.py +++ b/src/leap/eip/eipconnection.py @@ -1,9 +1,15 @@ """ EIP Connection Class """ +from __future__ import (absolute_import,) +import logging + +logger = logging.getLogger(name=__name__) + +from leap.eip.openvpnconnection import ( + OpenVPNConnection, ConnectionRefusedError) +from leap.base.connection import ConnectionError -from leap.OpenVPNConnection import OpenVPNConnection, MissingSocketError, ConnectionRefusedError -from leap.Connection import ConnectionError class EIPConnection(OpenVPNConnection): """ @@ -39,7 +45,6 @@ class EIPConnection(OpenVPNConnection): """ self._disconnect() self.status.change_to(self.status.DISCONNECTED) - pass def shutdown(self): """ @@ -262,7 +267,7 @@ class EIPConnectionStatus(object): cb(self) - +# XXX move to exceptions class EIPClientError(ConnectionError): """ base EIPClient Exception diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index a26059a7..d3ce3578 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -12,9 +12,10 @@ from functools import partial logger = logging.getLogger(name=__name__) -from leap.utils.coroutines import spawn_and_watch_process -from leap.baseapp.config import get_config -from leap.Connection import Connection +from leap.util.coroutines import spawn_and_watch_process +from leap.eip.config import get_config +from leap.base.connection import Connection + class OpenVPNConnection(Connection): """ @@ -23,7 +24,13 @@ class OpenVPNConnection(Connection): """ # Connection Methods - def __init__(self, config_file=None, watcher_cb=None,host="/tmp/.eip.sock", port="unix", password=None): + def __init__(self, config_file=None, + watcher_cb=None, + debug=False, + host="/tmp/.eip.sock", + port="unix", + password=None, + *args, **kwargs): #XXX FIXME #change watcher_cb to line_observer """ @@ -66,20 +73,20 @@ to be triggered for each one of them. #manage a connection error self.with_errors = False - - def _set_command_mockup(self): - """ - sets command and args for a command mockup - that just mimics the output from the real thing - """ - command, args = get_vpn_stdout_mockup() - self.command, self.args = command, args + #def _set_command_mockup(self): + #""" + #sets command and args for a command mockup + #that just mimics the output from the real thing + #""" + #command, args = get_vpn_stdout_mockup() + #self.command, self.args = command, args def _get_config(self): """ retrieves the config options from defaults or home file, or config file passed in command line. """ + #XXX merge! was changed in test-eip branch!!! config = get_config(config_file=self.config_file) self.config = config @@ -97,8 +104,8 @@ to be triggered for each one of them. self.command = command #print("debug: command = %s" % command) self.args = args - else: - self._set_command_mockup() + #else: + #self._set_command_mockup() if config.has_option('openvpn', 'autostart'): autostart = config.get('openvpn', 'autostart') -- cgit v1.2.3 From 1abd35337a186e7ab1bab414c0a3809b8583b5a3 Mon Sep 17 00:00:00 2001 From: kali Date: Tue, 21 Aug 2012 03:30:44 +0900 Subject: moved exceptions to its own file --- src/leap/eip/conductor.py | 36 +----------------------------------- src/leap/eip/eipconnection.py | 5 +++-- src/leap/eip/exceptions.py | 31 +++++++++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 37 deletions(-) create mode 100644 src/leap/eip/exceptions.py (limited to 'src/leap/eip') diff --git a/src/leap/eip/conductor.py b/src/leap/eip/conductor.py index 776a1092..f528d639 100644 --- a/src/leap/eip/conductor.py +++ b/src/leap/eip/conductor.py @@ -26,44 +26,10 @@ from leap.eip.vpnmanager import OpenVPNManager, ConnectionRefusedError logger = logging.getLogger(name=__name__) -# TODO Move exceptions to their own module -# eip.exceptions - -class EIPNoCommandError(Exception): - pass - - -class ConnectionError(Exception): - """ - generic connection error - """ - pass - - -class EIPClientError(Exception): - """ - base EIPClient exception - """ - def __str__(self): - if len(self.args) >= 1: - return repr(self.args[0]) - else: - return ConnectionError - - -class UnrecoverableError(EIPClientError): - """ - we cannot do anything about it, sorry - """ - # XXX we should catch this and raise - # to qtland, so we emit signal - # to translate whatever kind of error - # to user-friendly msg in dialog. - pass - # # Openvpn related classes # +# XXX deprecated! moved to eipconnection class OpenVPNConnection(object): diff --git a/src/leap/eip/eipconnection.py b/src/leap/eip/eipconnection.py index a0fdd77d..7e6c4038 100644 --- a/src/leap/eip/eipconnection.py +++ b/src/leap/eip/eipconnection.py @@ -6,9 +6,10 @@ import logging logger = logging.getLogger(name=__name__) +from leap.base.connection import ConnectionError +from leap.eip import exceptions as eip_exceptions from leap.eip.openvpnconnection import ( OpenVPNConnection, ConnectionRefusedError) -from leap.base.connection import ConnectionError class EIPConnection(OpenVPNConnection): @@ -112,7 +113,7 @@ class EIPConnection(OpenVPNConnection): #conn_result = ConState.DISCONNECTED try: conn_result = self._try_connection() - except UnrecoverableError as except_msg: + except eip_exceptions.UnrecoverableError as except_msg: logger.error("FATAL: %s" % unicode(except_msg)) conn_result = self.status.UNRECOVERABLE except Exception as except_msg: diff --git a/src/leap/eip/exceptions.py b/src/leap/eip/exceptions.py new file mode 100644 index 00000000..bd6489ce --- /dev/null +++ b/src/leap/eip/exceptions.py @@ -0,0 +1,31 @@ +class EIPNoCommandError(Exception): + pass + + +class ConnectionError(Exception): + """ + generic connection error + """ + pass + + +class EIPClientError(Exception): + """ + base EIPClient exception + """ + def __str__(self): + if len(self.args) >= 1: + return repr(self.args[0]) + else: + return ConnectionError + + +class UnrecoverableError(EIPClientError): + """ + we cannot do anything about it, sorry + """ + # XXX we should catch this and raise + # to qtland, so we emit signal + # to translate whatever kind of error + # to user-friendly msg in dialog. + pass -- cgit v1.2.3 From bb7c03a2d7244beff71ae610c012f525496daeb9 Mon Sep 17 00:00:00 2001 From: kali Date: Tue, 21 Aug 2012 04:29:03 +0900 Subject: udstelnet to its own file --- src/leap/eip/udstelnet.py | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 src/leap/eip/udstelnet.py (limited to 'src/leap/eip') diff --git a/src/leap/eip/udstelnet.py b/src/leap/eip/udstelnet.py new file mode 100644 index 00000000..18e927c2 --- /dev/null +++ b/src/leap/eip/udstelnet.py @@ -0,0 +1,38 @@ +import os +import socket +import telnetlib + +from leap.eip import exceptions as eip_exceptions + + +class UDSTelnet(telnetlib.Telnet): + """ + a telnet-alike class, that can listen + on unix domain sockets + """ + + def open(self, host, port=23, timeout=socket._GLOBAL_DEFAULT_TIMEOUT): + """Connect to a host. If port is 'unix', it + will open a connection over unix docmain sockets. + + The optional second argument is the port number, which + defaults to the standard telnet port (23). + + Don't try to reopen an already connected instance. + """ + self.eof = 0 + self.host = host + self.port = port + self.timeout = timeout + + if self.port == "unix": + # unix sockets spoken + if not os.path.exists(self.host): + raise eip_exceptions.MissingSocketError + self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + try: + self.sock.connect(self.host) + except socket.error: + raise eip_exceptions.ConnectionRefusedError + else: + self.sock = socket.create_connection((host, port), timeout) -- cgit v1.2.3 From 738b4bf8c6b75a1d73b7fa3e1a5edb69adf9d8a0 Mon Sep 17 00:00:00 2001 From: kali Date: Tue, 21 Aug 2012 04:58:05 +0900 Subject: fix out-of-sync refactor. manually merge changes from the develop branch that were lost due to having branched a previous state when refactored former "conductor" class. also, moved more exceptions to its own file. --- src/leap/eip/config.py | 38 ++----- src/leap/eip/eipconnection.py | 7 +- src/leap/eip/exceptions.py | 34 ++++++ src/leap/eip/openvpnconnection.py | 231 +++++++++++++++++++------------------- 4 files changed, 159 insertions(+), 151 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py index 6118c9de..8e55d789 100644 --- a/src/leap/eip/config.py +++ b/src/leap/eip/config.py @@ -9,37 +9,15 @@ from leap.util.fileutil import (which, mkdir_p, check_and_fix_urw_only) from leap.baseapp.permcheck import (is_pkexec_in_system, is_auth_agent_running) +from leap.eip import exceptions as eip_exceptions logger = logging.getLogger(name=__name__) logger.setLevel('DEBUG') -# XXX move exceptions to -# from leap.eip import exceptions as eip_exceptions - - -class EIPNoPkexecAvailable(Exception): - pass - - -class EIPNoPolkitAuthAgentAvailable(Exception): - pass - - -class EIPInitNoProviderError(Exception): - pass - - -class EIPInitBadProviderError(Exception): - pass - - -class EIPInitNoKeyFileError(Exception): - pass - - -class EIPInitBadKeyFilePermError(Exception): - pass - +# XXX this has to be REMOVED +# and all these options passed in the +# command line --> move to build_ovpn_command +# issue #447 OPENVPN_CONFIG_TEMPLATE = """#Autogenerated by eip-client wizard remote {VPN_REMOTE_HOST} {VPN_REMOTE_PORT} @@ -278,11 +256,12 @@ def build_ovpn_command(config, debug=False): # XXX check for both pkexec (done) # AND a suitable authentication # agent running. + # (until we implement setuid helper) logger.info('use_pkexec set to True') if not is_pkexec_in_system(): logger.error('no pkexec in system') - raise EIPNoPkexecAvailable + raise eip_exceptions.EIPNoPkexecAvailable if not is_auth_agent_running(): logger.warning( @@ -290,7 +269,7 @@ def build_ovpn_command(config, debug=False): "pkexec will use its own text " "based authentication agent. " "that's probably a bad idea") - raise EIPNoPolkitAuthAgentAvailable + raise eip_exceptions.EIPNoPolkitAuthAgentAvailable command.append('pkexec') @@ -312,7 +291,6 @@ def build_ovpn_command(config, debug=False): command.append(opt) # XXX check len and raise proper error - return [command[0], command[1:]] diff --git a/src/leap/eip/eipconnection.py b/src/leap/eip/eipconnection.py index 7e6c4038..139ee750 100644 --- a/src/leap/eip/eipconnection.py +++ b/src/leap/eip/eipconnection.py @@ -8,8 +8,7 @@ logger = logging.getLogger(name=__name__) from leap.base.connection import ConnectionError from leap.eip import exceptions as eip_exceptions -from leap.eip.openvpnconnection import ( - OpenVPNConnection, ConnectionRefusedError) +from leap.eip.openvpnconnection import OpenVPNConnection class EIPConnection(OpenVPNConnection): @@ -25,7 +24,7 @@ class EIPConnection(OpenVPNConnection): self.settingsfile = kwargs.get('settingsfile', None) self.logfile = kwargs.get('logfile', None) self.error_queue = [] - self.desired_con_state = None # ??? + #self.desired_con_state = None # not in use status_signals = kwargs.pop('status_signals', None) self.status = EIPConnectionStatus(callbacks=status_signals) @@ -70,7 +69,7 @@ class EIPConnection(OpenVPNConnection): """ try: state = self.get_connection_state() - except ConnectionRefusedError: + except eip_exceptions.ConnectionRefusedError: # connection refused. might be not ready yet. return if not state: diff --git a/src/leap/eip/exceptions.py b/src/leap/eip/exceptions.py index bd6489ce..ac61f42b 100644 --- a/src/leap/eip/exceptions.py +++ b/src/leap/eip/exceptions.py @@ -29,3 +29,37 @@ class UnrecoverableError(EIPClientError): # to translate whatever kind of error # to user-friendly msg in dialog. pass + + +class MissingSocketError(Exception): + pass + + +class ConnectionRefusedError(Exception): + pass + + +class EIPNoPkexecAvailable(Exception): + pass + + +class EIPNoPolkitAuthAgentAvailable(Exception): + pass + + +class EIPInitNoProviderError(Exception): + pass + + +class EIPInitBadProviderError(Exception): + pass + + +class EIPInitNoKeyFileError(Exception): + pass + + +class EIPInitBadKeyFilePermError(Exception): + pass + + diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index d3ce3578..601bb54a 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -1,20 +1,20 @@ """ OpenVPN Connection """ - from __future__ import (print_function) import logging -import os import socket -import telnetlib import time from functools import partial logger = logging.getLogger(name=__name__) -from leap.util.coroutines import spawn_and_watch_process -from leap.eip.config import get_config from leap.base.connection import Connection +from leap.util.coroutines import spawn_and_watch_process + +from leap.eip.udstelnet import UDSTelnet +from leap.eip import config as eip_config +from leap.eip import exceptions as eip_exceptions class OpenVPNConnection(Connection): @@ -43,6 +43,8 @@ to be triggered for each one of them. :type watcher_cb: function :type signal_map: dict """ + self.debug = debug + #print('conductor:%s' % debug) self.config_file = config_file self.watcher_cb = watcher_cb @@ -55,12 +57,29 @@ to be triggered for each one of them. self.port = None self.proto = None + self.missing_pkexec = False + self.missing_auth_agent = False + self.bad_keyfile_perms = False + self.missing_vpn_keyfile = False + self.missing_provider = False + self.bad_provider = False + + #XXX workaround for signaling + #the ui that we don't know how to + #manage a connection error + self.with_errors = False + + self.command = None + self.args = None + self.autostart = True + self._get_or_create_config() + self._check_vpn_keys() - self._get_config() + # + # management init methods + # - #Get this info from the Configuration Class - #XXX hardcoded host here. change. self.host = host if isinstance(port, str) and port.isdigit(): port = int(port) @@ -68,48 +87,92 @@ to be triggered for each one of them. self.password = password self.tn = None - #XXX workaround for signaling - #the ui that we don't know how to - #manage a connection error - self.with_errors = False - - #def _set_command_mockup(self): - #""" - #sets command and args for a command mockup - #that just mimics the output from the real thing - #""" - #command, args = get_vpn_stdout_mockup() - #self.command, self.args = command, args - - def _get_config(self): - """ - retrieves the config options from defaults or - home file, or config file passed in command line. - """ - #XXX merge! was changed in test-eip branch!!! - config = get_config(config_file=self.config_file) - self.config = config + def _set_autostart(self): + config = self.config + if config.has_option('openvpn', 'autostart'): + autostart = config.getboolean('openvpn', + 'autostart') + self.autostart = autostart + else: + if config.has_option('DEFAULT', 'autostart'): + autostart = config.getboolean('DEFAULT', + 'autostart') + self.autostart = autostart + def _set_ovpn_command(self): + config = self.config if config.has_option('openvpn', 'command'): commandline = config.get('openvpn', 'command') - if commandline == "mockup": - self._set_command_mockup() - return + command_split = commandline.split(' ') command = command_split[0] if len(command_split) > 1: args = command_split[1:] else: args = [] + + self.command = command + self.args = args + else: + # no command in config, we build it up. + # XXX check also for command-line --command flag + try: + command, args = eip_config.build_ovpn_command( + config, + debug=self.debug) + except eip_exceptions.EIPNoPolkitAuthAgentAvailable: + command = args = None + self.missing_auth_agent = True + except eip_exceptions.EIPNoPkexecAvailable: + command = args = None + self.missing_pkexec = True + + # XXX if not command, signal error. self.command = command - #print("debug: command = %s" % command) self.args = args - #else: - #self._set_command_mockup() - if config.has_option('openvpn', 'autostart'): - autostart = config.get('openvpn', 'autostart') - self.autostart = autostart + def _check_ovpn_config(self): + """ + checks if there is a default openvpn config. + if not, it writes one with info from the provider + definition file + """ + # TODO + # - get --with-openvpn-config from opts + try: + eip_config.check_or_create_default_vpnconf(self.config) + except eip_exceptions.EIPInitNoProviderError: + logger.error('missing default provider definition') + self.missing_provider = True + except eip_exceptions.EIPInitBadProviderError: + logger.error('bad provider definition') + self.bad_provider = True + + def _get_or_create_config(self): + """ + retrieves the config options from defaults or + home file, or config file passed in command line. + populates command and args to be passed to subprocess. + """ + config = eip_config.get_config( + config_file=self.config_file) + self.config = config + + self._set_autostart() + self._set_ovpn_command() + self._check_ovpn_config() + + def _check_vpn_keys(self): + """ + checks for correct permissions on vpn keys + """ + try: + eip_config.check_vpn_keys(self.config) + except eip_exceptions.EIPInitNoKeyFileError: + self.missing_vpn_keyfile = True + except eip_exceptions.EIPInitBadKeyFilePermError: + logger.error('error while checking vpn keys') + self.bad_keyfile_perms = True def _launch_openvpn(self): """ @@ -126,7 +189,7 @@ to be triggered for each one of them. linewrite_callback = lambda line: print('watcher: %s' % line) observers = (linewrite_callback, - partial(self.status_watcher, self.status)) + partial(lambda: None, self.status)) subp, watcher = spawn_and_watch_process( self.command, self.args, @@ -134,13 +197,12 @@ to be triggered for each one of them. self.subp = subp self.watcher = watcher - conn_result = self.status.CONNECTED - return conn_result - def _try_connection(self): """ attempts to connect """ + if self.command is None: + raise eip_exceptions.EIPNoCommandError if self.subp is not None: print('cowardly refusing to launch subprocess again') return @@ -153,17 +215,14 @@ to be triggered for each one of them. if self.subp: self.subp.terminate() - - #Here are the actual code to manage OpenVPN Connection - #TODO: Look into abstraction them and moving them up into base class - # this code based on code from cube-routed project - - """ - Run commands over OpenVPN management interface - and parses the output. - """ - # XXX might need a lock to avoid - # race conditions here... + # + # management methods + # + # XXX REVIEW-ME + # REFACTOR INFO: (former "manager". + # Can we move to another + # base class to test independently?) + # def forget_errors(self): print('forgetting errors') @@ -182,7 +241,7 @@ to be triggered for each one of them. self.tn = UDSTelnet(self.host, self.port) # XXX make password optional - # specially for win plat. we should generate + # specially for win. we should generate # the pass on the fly when invoking manager # from conductor @@ -207,7 +266,6 @@ to be triggered for each one of them. Returns True if connected rtype: bool """ - #return bool(getattr(self, 'tn', None)) try: assert self.tn return True @@ -235,7 +293,7 @@ to be triggered for each one of them. if not self.connected(): try: self.connect() - except MissingSocketError: + except eip_exceptions.MissingSocketError: #XXX capture more helpful error #messages #pass @@ -352,64 +410,3 @@ to be triggered for each one of them. ts = time.gmtime(float(ts)) # XXX this could be a named tuple. prettier. return ts, status_step, ok, ip, remote - - def status_watcher(self, cs, line): - """ - a wrapper that calls to ConnectionStatus object - :param cs: a EIPConnectionStatus instance - :type cs: EIPConnectionStatus object - :param line: a single line of the watched output - :type line: str - """ - #print('status watcher watching') - - # from the mullvad code, should watch for - # things like: - # "Initialization Sequence Completed" - # "With Errors" - # "Tap-Win32" - - if "Completed" in line: - cs.change_to(cs.CONNECTED) - return - - if "Initial packet from" in line: - cs.change_to(cs.CONNECTING) - return - - - -class MissingSocketError(Exception): - pass - - -class ConnectionRefusedError(Exception): - pass - -class UDSTelnet(telnetlib.Telnet): - - def open(self, host, port=23, timeout=socket._GLOBAL_DEFAULT_TIMEOUT): - """Connect to a host. If port is 'unix', it - will open a connection over unix docmain sockets. - - The optional second argument is the port number, which - defaults to the standard telnet port (23). - - Don't try to reopen an already connected instance. - """ - self.eof = 0 - self.host = host - self.port = port - self.timeout = timeout - - if self.port == "unix": - # unix sockets spoken - if not os.path.exists(self.host): - raise MissingSocketError - self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - try: - self.sock.connect(self.host) - except socket.error: - raise ConnectionRefusedError - else: - self.sock = socket.create_connection((host, port), timeout) -- cgit v1.2.3 From d908247dcc2cac66d31f081d892a04833206de3b Mon Sep 17 00:00:00 2001 From: kali Date: Tue, 21 Aug 2012 05:43:55 +0900 Subject: fix status_watcher callback stub --- src/leap/eip/openvpnconnection.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index 601bb54a..81e6b1ba 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -7,6 +7,7 @@ import socket import time from functools import partial +logging.basicConfig() logger = logging.getLogger(name=__name__) from leap.base.connection import Connection @@ -182,14 +183,20 @@ to be triggered for each one of them. #deprecate watcher_cb, #use _only_ signal_maps instead + logger.debug('_launch_openvpn called') if self.watcher_cb is not None: linewrite_callback = self.watcher_cb else: #XXX get logger instead linewrite_callback = lambda line: print('watcher: %s' % line) + # the partial is not + # being applied now because we're not observing the process + # stdout like we did in the early stages. but I leave it + # here since it will be handy for observing patterns in the + # thru-the-manager updates (with regex) observers = (linewrite_callback, - partial(lambda: None, self.status)) + partial(lambda con_status, line: None, self.status)) subp, watcher = spawn_and_watch_process( self.command, self.args, @@ -235,7 +242,7 @@ to be triggered for each one of them. except: #XXX don't like this general #catch here. - pass + raise if self.connected(): return True self.tn = UDSTelnet(self.host, self.port) @@ -250,7 +257,7 @@ to be triggered for each one of them. #self.tn.read_until('SUCCESS:', 2) self._seek_to_eof() - self.forget_errors() + #self.forget_errors() return True def _seek_to_eof(self): @@ -290,6 +297,8 @@ to be triggered for each one of them. """ if tries > 3: return [] + if self.tn is None: + return [] if not self.connected(): try: self.connect() -- cgit v1.2.3 From d5af0f112eca2c9af0344c589d731cd6b3051acf Mon Sep 17 00:00:00 2001 From: antialias Date: Mon, 20 Aug 2012 17:41:50 -0700 Subject: added json parsing from eip.json file and some basic tests. --- src/leap/eip/tests/tests_config.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 src/leap/eip/tests/tests_config.py (limited to 'src/leap/eip') diff --git a/src/leap/eip/tests/tests_config.py b/src/leap/eip/tests/tests_config.py new file mode 100644 index 00000000..6534723e --- /dev/null +++ b/src/leap/eip/tests/tests_config.py @@ -0,0 +1,18 @@ + +"""Test config helper functions""" + +import unittest + +from leap.eip import config + +class TestConfig(unittest.TestCase): + """ + Test configuration help functions. + """ + def test_get_config_json(self): + config_js = config.get_config_json() + self.assertTrue(isinstance(config_js, dict)) + self.assertTrue(config_js.has_key('transport')) + self.assertTrue(config_js.has_key('provider')) + self.assertEqual(config_js['provider'], "testprovider.org") + -- cgit v1.2.3 From 560232609ef229d46932f8ffcd66b8e114e8b3e6 Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 22 Aug 2012 01:30:37 +0900 Subject: yay! First WORKING GUI in refactor branch :) Obviously then, you should ignore the commit message in 489ed46140d6d. That commit WAS NOT working, believe me :) Fix an annoying bug by which we were overwriting the "connect" method that came from vpnmanager with basically an empty stub. --- src/leap/eip/eipconnection.py | 9 +++++++- src/leap/eip/openvpnconnection.py | 48 +++++++++++++++------------------------ 2 files changed, 26 insertions(+), 31 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/eipconnection.py b/src/leap/eip/eipconnection.py index 139ee750..2dfc1503 100644 --- a/src/leap/eip/eipconnection.py +++ b/src/leap/eip/eipconnection.py @@ -4,7 +4,9 @@ EIP Connection Class from __future__ import (absolute_import,) import logging +logging.basicConfig() logger = logging.getLogger(name=__name__) +logger.setLevel(logging.DEBUG) from leap.base.connection import ConnectionError from leap.eip import exceptions as eip_exceptions @@ -67,12 +69,17 @@ class EIPConnection(OpenVPNConnection): def poll_connection_state(self): """ """ + # XXX this separation does not + # make sense anymore after having + # merged Connection and Manager classes. try: state = self.get_connection_state() except eip_exceptions.ConnectionRefusedError: # connection refused. might be not ready yet. + logger.warning('connection refused') return if not state: + logger.debug('no state') return (ts, status_step, ok, ip, remote) = state @@ -172,9 +179,9 @@ class EIPConnectionStatus(object): :param callbacks: a tuple of (callable) observers :type callbacks: tuple """ - # (callbacks to connect to signals in Qt-land) self.current = self.DISCONNECTED self.previous = None + # (callbacks to connect to signals in Qt-land) self.callbacks = callbacks def get_readable_status(self): diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index 81e6b1ba..a230d229 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -9,6 +9,7 @@ from functools import partial logging.basicConfig() logger = logging.getLogger(name=__name__) +logger.setLevel(logging.DEBUG) from leap.base.connection import Connection from leap.util.coroutines import spawn_and_watch_process @@ -86,7 +87,7 @@ to be triggered for each one of them. port = int(port) self.port = port self.password = password - self.tn = None + #self.tn = None def _set_autostart(self): config = self.config @@ -235,16 +236,11 @@ to be triggered for each one of them. print('forgetting errors') self.with_errors = False - def connect(self): + def connect_to_management(self): """Connect to openvpn management interface""" - try: + #logger.debug('connecting socket') + if hasattr(self, 'tn'): self.close() - except: - #XXX don't like this general - #catch here. - raise - if self.connected(): - return True self.tn = UDSTelnet(self.host, self.port) # XXX make password optional @@ -273,47 +269,39 @@ to be triggered for each one of them. Returns True if connected rtype: bool """ - try: - assert self.tn - return True - except: - #XXX get rid of - #this pokemon exception!!! - return False + return hasattr(self, 'tn') def close(self, announce=True): """ Close connection to openvpn management interface """ + logger.debug('closing socket') if announce: self.tn.write("quit\n") self.tn.read_all() self.tn.get_socket().close() del self.tn - def _send_command(self, cmd, tries=0): + def _send_command(self, cmd): """ Send a command to openvpn and return response as list """ - if tries > 3: - return [] - if self.tn is None: - return [] + #logger.debug('connected? %s' % self.connected()) if not self.connected(): try: - self.connect() + #logger.debug('try to connect') + self.connect_to_management() except eip_exceptions.MissingSocketError: #XXX capture more helpful error - #messages - #pass return self.make_error() + except: + raise try: - self.tn.write(cmd + "\n") + if hasattr(self, 'tn'): + self.tn.write(cmd + "\n") except socket.error: logger.error('socket error') - print('socket error!') self.close(announce=False) - self._send_command(cmd, tries=tries + 1) return [] buf = self.tn.read_until(b"END", 2) self._seek_to_eof() @@ -371,14 +359,14 @@ to be triggered for each one of them. else: return state[-1] - def status(self): + def vpn_status(self): """ OpenVPN command: status """ status = self._send_command("status") return status - def status2(self): + def vpn_status2(self): """ OpenVPN command: last 2 statuses """ @@ -389,7 +377,7 @@ to be triggered for each one of them. # def get_status_io(self): - status = self.status() + status = self.vpn_status() if isinstance(status, str): lines = status.split('\n') if isinstance(status, list): -- cgit v1.2.3 From ac00ec313a142e910447857c0e46e6d36c7f2ab2 Mon Sep 17 00:00:00 2001 From: antialias Date: Tue, 21 Aug 2012 10:12:22 -0700 Subject: Error fixes and json commit. --- src/leap/eip/config.py | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py index 8e55d789..a219fedb 100644 --- a/src/leap/eip/config.py +++ b/src/leap/eip/config.py @@ -2,6 +2,7 @@ import ConfigParser import grp import logging import os +import json import platform import socket @@ -118,8 +119,8 @@ def check_or_create_default_vpnconf(config): 'remote_ip') validate_ip(remote_ip) - except ConfigParser.NoOptionError: - raise EIPInitNoProviderError + except ConfigParser.NoSectionError: + raise eip_exceptions.EIPInitNoProviderError except socket.error: # this does not look like an ip, dave @@ -394,7 +395,7 @@ def check_vpn_keys(config): if not os.path.isfile(keyfile): logger.error('key file %s not found. aborting.', keyfile) - raise EIPInitNoKeyFileError + raise eip_exceptions.EIPInitNoKeyFileError # check proper permission on keys # bad perms? try to fix them @@ -402,3 +403,27 @@ def check_vpn_keys(config): check_and_fix_urw_only(keyfile) except OSError: raise EIPInitBadKeyFilePermError + + +def get_config_json(config_file=None): + """ + will replace get_config function be developing them + in parralel for branch purposes. + @param: configuration file + @type: file + @rparam: configuration turples + @rtype: dictionary + """ + if not config_file: + fpath = get_config_file('eip.json') + if not os.path.isfile(fpath): + dpath, cfile = os.path.split(fpath) + if not os.path.isdir(dpath): + mkdir_p(dpath) + with open(fpath, 'wb') as configfile: + configfile.write() + config_file = open(fpath) + + config = json.load(config_file) + + return config -- cgit v1.2.3 From 78c83ab3bb7f95564bdc537d29f6d278b1710b17 Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 22 Aug 2012 03:31:07 +0900 Subject: pep8 --- src/leap/eip/exceptions.py | 2 -- 1 file changed, 2 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/exceptions.py b/src/leap/eip/exceptions.py index ac61f42b..3719c605 100644 --- a/src/leap/eip/exceptions.py +++ b/src/leap/eip/exceptions.py @@ -61,5 +61,3 @@ class EIPInitNoKeyFileError(Exception): class EIPInitBadKeyFilePermError(Exception): pass - - -- cgit v1.2.3 From 04cf64af3702ab85a670efe6850c60f20bbf7eb0 Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 22 Aug 2012 04:11:44 +0900 Subject: conductor tests --- src/leap/eip/test_conductor.py | 180 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 src/leap/eip/test_conductor.py (limited to 'src/leap/eip') diff --git a/src/leap/eip/test_conductor.py b/src/leap/eip/test_conductor.py new file mode 100644 index 00000000..51772b7c --- /dev/null +++ b/src/leap/eip/test_conductor.py @@ -0,0 +1,180 @@ +import ConfigParser +import logging +import platform + +logging.basicConfig() +logger = logging.getLogger(name=__name__) + +try: + import unittest2 as unittest +except ImportError: + import unittest + +from mock import Mock, patch # MagicMock + +from leap.eip.eipconnection import EIPConnection +from leap.eip.exceptions import ConnectionRefusedError + +_system = platform.system() + + +class NotImplementedError(Exception): + pass + + +@patch('OpenVPNConnection._get_or_create_config') +@patch('OpenVPNConnection._set_ovpn_command') +class MockedEIPConnection(EIPConnection): + def _get_or_create_config(self): + self.config = ConfigParser.ConfigParser() + self._set_ovpn_command() + + def _set_ovpn_command(self): + self.command = "mock_command" + self.args = [1, 2, 3] + + +class EIPConductorTest(unittest.TestCase): + + __name__ = "eip_conductor_tests" + + def setUp(self): + self.manager = Mock( + name="openvpnmanager_mock") + + self.con = MockedEIPConnection() + #manager=self.manager) + + def tearDown(self): + del self.con + + # + # helpers + # + + def _missing_test_for_plat(self, do_raise=False): + if do_raise: + raise NotImplementedError( + "This test is not implemented " + "for the running platform: %s" % + _system) + + # + # tests + # + + @unittest.skip + #ain't manager anymore! + def test_manager_was_initialized(self): + """ + manager init ok during conductor init? + """ + self.manager.assert_called_once_with() + + def test_vpnconnection_defaults(self): + """ + default attrs as expected + """ + con = self.con + self.assertEqual(con.autostart, True) + self.assertEqual(con.missing_pkexec, False) + self.assertEqual(con.missing_vpn_keyfile, False) + self.assertEqual(con.missing_provider, False) + self.assertEqual(con.bad_provider, False) + + def test_config_was_init(self): + """ + is there a config object? + """ + self.assertTrue(isinstance(self.con.config, + ConfigParser.ConfigParser)) + + def test_ovpn_command(self): + """ + set_ovpn_command called + """ + self.assertEqual(self.con.command, + "mock_command") + self.assertEqual(self.con.args, + [1, 2, 3]) + + # connect/disconnect calls + + def test_disconnect(self): + """ + disconnect method calls private and changes status + """ + self.con._disconnect = Mock( + name="_disconnect") + + # first we set status to connected + self.con.status.set_current(self.con.status.CONNECTED) + self.assertEqual(self.con.status.current, + self.con.status.CONNECTED) + + # disconnect + self.con.disconnect() + self.con._disconnect.assert_called_once_with() + + # new status should be disconnected + # XXX this should evolve and check no errors + # during disconnection + self.assertEqual(self.con.status.current, + self.con.status.DISCONNECTED) + + def test_connect(self): + """ + connect calls _launch_openvpn private + """ + self.con._launch_openvpn = Mock() + self.con.connect() + self.con._launch_openvpn.assert_called_once_with() + + # XXX tests breaking here ... + + def test_good_poll_connection_state(self): + """ + """ + #@patch -- + # self.manager.get_connection_state + + #XXX review this set of poll_state tests + #they SHOULD NOT NEED TO MOCK ANYTHING IN THE + #lower layers!! -- status, vpn_manager.. + #right now we're testing implementation, not + #behavior!!! + good_state = ["1345466946", "unknown_state", "ok", + "192.168.1.1", "192.168.1.100"] + self.con.get_connection_state = Mock(return_value=good_state) + self.con.status.set_vpn_state = Mock() + + state = self.con.poll_connection_state() + good_state[1] = "disconnected" + final_state = tuple(good_state) + self.con.status.set_vpn_state.assert_called_with("unknown_state") + self.assertEqual(state, final_state) + + # TODO between "good" and "bad" (exception raised) cases, + # we can still test for malformed states and see that only good + # states do have a change (and from only the expected transition + # states). + + def test_bad_poll_connection_state(self): + """ + get connection state raises ConnectionRefusedError + state is None + """ + self.con.get_connection_state = Mock( + side_effect=ConnectionRefusedError('foo!')) + state = self.con.poll_connection_state() + self.assertEqual(state, None) + + + # XXX more things to test: + # - called config routines during initz. + # - raising proper exceptions with no config + # - called proper checks on config / permissions + + +if __name__ == "__main__": + unittest.main() -- cgit v1.2.3 From a048ecc7d709f6378ccba6201131b8c03df94716 Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 22 Aug 2012 04:30:41 +0900 Subject: add conductor + manager tests (red) --- src/leap/eip/test_conductor.py | 180 --------------------------------- src/leap/eip/test_eipconnection.py | 180 +++++++++++++++++++++++++++++++++ src/leap/eip/test_openvpnconnection.py | 136 +++++++++++++++++++++++++ 3 files changed, 316 insertions(+), 180 deletions(-) delete mode 100644 src/leap/eip/test_conductor.py create mode 100644 src/leap/eip/test_eipconnection.py create mode 100644 src/leap/eip/test_openvpnconnection.py (limited to 'src/leap/eip') diff --git a/src/leap/eip/test_conductor.py b/src/leap/eip/test_conductor.py deleted file mode 100644 index 51772b7c..00000000 --- a/src/leap/eip/test_conductor.py +++ /dev/null @@ -1,180 +0,0 @@ -import ConfigParser -import logging -import platform - -logging.basicConfig() -logger = logging.getLogger(name=__name__) - -try: - import unittest2 as unittest -except ImportError: - import unittest - -from mock import Mock, patch # MagicMock - -from leap.eip.eipconnection import EIPConnection -from leap.eip.exceptions import ConnectionRefusedError - -_system = platform.system() - - -class NotImplementedError(Exception): - pass - - -@patch('OpenVPNConnection._get_or_create_config') -@patch('OpenVPNConnection._set_ovpn_command') -class MockedEIPConnection(EIPConnection): - def _get_or_create_config(self): - self.config = ConfigParser.ConfigParser() - self._set_ovpn_command() - - def _set_ovpn_command(self): - self.command = "mock_command" - self.args = [1, 2, 3] - - -class EIPConductorTest(unittest.TestCase): - - __name__ = "eip_conductor_tests" - - def setUp(self): - self.manager = Mock( - name="openvpnmanager_mock") - - self.con = MockedEIPConnection() - #manager=self.manager) - - def tearDown(self): - del self.con - - # - # helpers - # - - def _missing_test_for_plat(self, do_raise=False): - if do_raise: - raise NotImplementedError( - "This test is not implemented " - "for the running platform: %s" % - _system) - - # - # tests - # - - @unittest.skip - #ain't manager anymore! - def test_manager_was_initialized(self): - """ - manager init ok during conductor init? - """ - self.manager.assert_called_once_with() - - def test_vpnconnection_defaults(self): - """ - default attrs as expected - """ - con = self.con - self.assertEqual(con.autostart, True) - self.assertEqual(con.missing_pkexec, False) - self.assertEqual(con.missing_vpn_keyfile, False) - self.assertEqual(con.missing_provider, False) - self.assertEqual(con.bad_provider, False) - - def test_config_was_init(self): - """ - is there a config object? - """ - self.assertTrue(isinstance(self.con.config, - ConfigParser.ConfigParser)) - - def test_ovpn_command(self): - """ - set_ovpn_command called - """ - self.assertEqual(self.con.command, - "mock_command") - self.assertEqual(self.con.args, - [1, 2, 3]) - - # connect/disconnect calls - - def test_disconnect(self): - """ - disconnect method calls private and changes status - """ - self.con._disconnect = Mock( - name="_disconnect") - - # first we set status to connected - self.con.status.set_current(self.con.status.CONNECTED) - self.assertEqual(self.con.status.current, - self.con.status.CONNECTED) - - # disconnect - self.con.disconnect() - self.con._disconnect.assert_called_once_with() - - # new status should be disconnected - # XXX this should evolve and check no errors - # during disconnection - self.assertEqual(self.con.status.current, - self.con.status.DISCONNECTED) - - def test_connect(self): - """ - connect calls _launch_openvpn private - """ - self.con._launch_openvpn = Mock() - self.con.connect() - self.con._launch_openvpn.assert_called_once_with() - - # XXX tests breaking here ... - - def test_good_poll_connection_state(self): - """ - """ - #@patch -- - # self.manager.get_connection_state - - #XXX review this set of poll_state tests - #they SHOULD NOT NEED TO MOCK ANYTHING IN THE - #lower layers!! -- status, vpn_manager.. - #right now we're testing implementation, not - #behavior!!! - good_state = ["1345466946", "unknown_state", "ok", - "192.168.1.1", "192.168.1.100"] - self.con.get_connection_state = Mock(return_value=good_state) - self.con.status.set_vpn_state = Mock() - - state = self.con.poll_connection_state() - good_state[1] = "disconnected" - final_state = tuple(good_state) - self.con.status.set_vpn_state.assert_called_with("unknown_state") - self.assertEqual(state, final_state) - - # TODO between "good" and "bad" (exception raised) cases, - # we can still test for malformed states and see that only good - # states do have a change (and from only the expected transition - # states). - - def test_bad_poll_connection_state(self): - """ - get connection state raises ConnectionRefusedError - state is None - """ - self.con.get_connection_state = Mock( - side_effect=ConnectionRefusedError('foo!')) - state = self.con.poll_connection_state() - self.assertEqual(state, None) - - - # XXX more things to test: - # - called config routines during initz. - # - raising proper exceptions with no config - # - called proper checks on config / permissions - - -if __name__ == "__main__": - unittest.main() diff --git a/src/leap/eip/test_eipconnection.py b/src/leap/eip/test_eipconnection.py new file mode 100644 index 00000000..51772b7c --- /dev/null +++ b/src/leap/eip/test_eipconnection.py @@ -0,0 +1,180 @@ +import ConfigParser +import logging +import platform + +logging.basicConfig() +logger = logging.getLogger(name=__name__) + +try: + import unittest2 as unittest +except ImportError: + import unittest + +from mock import Mock, patch # MagicMock + +from leap.eip.eipconnection import EIPConnection +from leap.eip.exceptions import ConnectionRefusedError + +_system = platform.system() + + +class NotImplementedError(Exception): + pass + + +@patch('OpenVPNConnection._get_or_create_config') +@patch('OpenVPNConnection._set_ovpn_command') +class MockedEIPConnection(EIPConnection): + def _get_or_create_config(self): + self.config = ConfigParser.ConfigParser() + self._set_ovpn_command() + + def _set_ovpn_command(self): + self.command = "mock_command" + self.args = [1, 2, 3] + + +class EIPConductorTest(unittest.TestCase): + + __name__ = "eip_conductor_tests" + + def setUp(self): + self.manager = Mock( + name="openvpnmanager_mock") + + self.con = MockedEIPConnection() + #manager=self.manager) + + def tearDown(self): + del self.con + + # + # helpers + # + + def _missing_test_for_plat(self, do_raise=False): + if do_raise: + raise NotImplementedError( + "This test is not implemented " + "for the running platform: %s" % + _system) + + # + # tests + # + + @unittest.skip + #ain't manager anymore! + def test_manager_was_initialized(self): + """ + manager init ok during conductor init? + """ + self.manager.assert_called_once_with() + + def test_vpnconnection_defaults(self): + """ + default attrs as expected + """ + con = self.con + self.assertEqual(con.autostart, True) + self.assertEqual(con.missing_pkexec, False) + self.assertEqual(con.missing_vpn_keyfile, False) + self.assertEqual(con.missing_provider, False) + self.assertEqual(con.bad_provider, False) + + def test_config_was_init(self): + """ + is there a config object? + """ + self.assertTrue(isinstance(self.con.config, + ConfigParser.ConfigParser)) + + def test_ovpn_command(self): + """ + set_ovpn_command called + """ + self.assertEqual(self.con.command, + "mock_command") + self.assertEqual(self.con.args, + [1, 2, 3]) + + # connect/disconnect calls + + def test_disconnect(self): + """ + disconnect method calls private and changes status + """ + self.con._disconnect = Mock( + name="_disconnect") + + # first we set status to connected + self.con.status.set_current(self.con.status.CONNECTED) + self.assertEqual(self.con.status.current, + self.con.status.CONNECTED) + + # disconnect + self.con.disconnect() + self.con._disconnect.assert_called_once_with() + + # new status should be disconnected + # XXX this should evolve and check no errors + # during disconnection + self.assertEqual(self.con.status.current, + self.con.status.DISCONNECTED) + + def test_connect(self): + """ + connect calls _launch_openvpn private + """ + self.con._launch_openvpn = Mock() + self.con.connect() + self.con._launch_openvpn.assert_called_once_with() + + # XXX tests breaking here ... + + def test_good_poll_connection_state(self): + """ + """ + #@patch -- + # self.manager.get_connection_state + + #XXX review this set of poll_state tests + #they SHOULD NOT NEED TO MOCK ANYTHING IN THE + #lower layers!! -- status, vpn_manager.. + #right now we're testing implementation, not + #behavior!!! + good_state = ["1345466946", "unknown_state", "ok", + "192.168.1.1", "192.168.1.100"] + self.con.get_connection_state = Mock(return_value=good_state) + self.con.status.set_vpn_state = Mock() + + state = self.con.poll_connection_state() + good_state[1] = "disconnected" + final_state = tuple(good_state) + self.con.status.set_vpn_state.assert_called_with("unknown_state") + self.assertEqual(state, final_state) + + # TODO between "good" and "bad" (exception raised) cases, + # we can still test for malformed states and see that only good + # states do have a change (and from only the expected transition + # states). + + def test_bad_poll_connection_state(self): + """ + get connection state raises ConnectionRefusedError + state is None + """ + self.con.get_connection_state = Mock( + side_effect=ConnectionRefusedError('foo!')) + state = self.con.poll_connection_state() + self.assertEqual(state, None) + + + # XXX more things to test: + # - called config routines during initz. + # - raising proper exceptions with no config + # - called proper checks on config / permissions + + +if __name__ == "__main__": + unittest.main() diff --git a/src/leap/eip/test_openvpnconnection.py b/src/leap/eip/test_openvpnconnection.py new file mode 100644 index 00000000..821c1ed4 --- /dev/null +++ b/src/leap/eip/test_openvpnconnection.py @@ -0,0 +1,136 @@ +import logging +import platform +#import socket + +logging.basicConfig() +logger = logging.getLogger(name=__name__) + +try: + import unittest2 as unittest +except ImportError: + import unittest + +from mock import Mock, patch # MagicMock + +from leap.eip import openvpnconnection +from leap.eip import exceptions as eip_exceptions +from leap.eip.udstelnet import UDSTelnet + +_system = platform.system() + + +class NotImplementedError(Exception): + pass + + +mock_UDSTelnet = Mock(spec=UDSTelnet) +# XXX cautious!!! +# this might be fragile right now (counting a global +# reference of calls I think. +# investigate this other form instead: +# http://www.voidspace.org.uk/python/mock/patch.html#start-and-stop + +# XXX redo after merge-refactor + + +@patch('openvpnconnection.OpenVPNConnection.connect_to_management') +class MockedOpenVPNConnection(openvpnconnection.OpenVPNConnection): + def __init__(self, *args, **kwargs): + self.mock_UDSTelnet = Mock() + super(MockedOpenVPNConnection, self).__init__( + *args, **kwargs) + self.tn = self.mock_UDSTelnet(self.host, self.port) + + def connect_to_management(self): + #print 'patched connect' + self.tn = mock_UDSTelnet(self.host, port=self.port) + + +class OpenVPNConnectionTest(unittest.TestCase): + + __name__ = "vpnconnection_tests" + + def setUp(self): + self.manager = MockedOpenVPNConnection() + + def tearDown(self): + del self.manager + + # + # helpers + # + + # XXX hey, refactor this to basetestclass + + def _missing_test_for_plat(self, do_raise=False): + if do_raise: + raise NotImplementedError( + "This test is not implemented " + "for the running platform: %s" % + _system) + + # + # tests + # + + @unittest.skipIf(_system == "Windows", "lin/mac only") + def test_lin_mac_default_init(self): + """ + check default host for management iface + """ + self.assertEqual(self.manager.host, '/tmp/.eip.sock') + self.assertEqual(self.manager.port, 'unix') + + @unittest.skipUnless(_system == "Windows", "win only") + def test_win_default_init(self): + """ + check default host for management iface + """ + # XXX should we make the platform specific switch + # here or in the vpn command string building? + self.assertEqual(self.manager.host, 'localhost') + self.assertEqual(self.manager.port, 7777) + + def test_port_types_init(self): + self.manager = MockedOpenVPNConnection(port="42") + self.assertEqual(self.manager.port, 42) + self.manager = MockedOpenVPNConnection() + self.assertEqual(self.manager.port, "unix") + self.manager = MockedOpenVPNConnection(port="bad") + self.assertEqual(self.manager.port, None) + + def test_connect_raises_missing_socket(self): + self.manager = openvpnconnection.OpenVPNConnection() + with self.assertRaises(eip_exceptions.MissingSocketError): + self.manager.connect_to_management() + + def test_uds_telnet_called_on_connect(self): + self.manager.connect() + mock_UDSTelnet.assert_called_with( + self.manager.host, + port=self.manager.port) + + @unittest.skip + def test_connect(self): + raise NotImplementedError + # XXX calls close + # calls UDSTelnet mock. + + # XXX + # tests to write: + # UDSTelnetTest (for real?) + # HAVE A LOOK AT CORE TESTS FOR TELNETLIB. + # very illustrative instead... + + # - raise MissingSocket + # - raise ConnectionRefusedError + # - test send command + # - tries connect + # - ... tries? + # - ... calls _seek_to_eof + # - ... read_until --> return value + # - ... + + +if __name__ == "__main__": + unittest.main() -- cgit v1.2.3 From 5ab2163a89ad7bc303f436af738aa0e7e6bb24d4 Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 22 Aug 2012 04:37:41 +0900 Subject: fix for failing bad port init test --- src/leap/eip/openvpnconnection.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index a230d229..3972b617 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -85,9 +85,12 @@ to be triggered for each one of them. self.host = host if isinstance(port, str) and port.isdigit(): port = int(port) + elif port == "unix": + port = "unix" + else: + port = None self.port = port self.password = password - #self.tn = None def _set_autostart(self): config = self.config -- cgit v1.2.3 From 40e7706d7291605b7759561ae59550213af83a94 Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 22 Aug 2012 04:39:35 +0900 Subject: fix udstelnet called test (green) --- src/leap/eip/test_openvpnconnection.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/test_openvpnconnection.py b/src/leap/eip/test_openvpnconnection.py index 821c1ed4..dea75b55 100644 --- a/src/leap/eip/test_openvpnconnection.py +++ b/src/leap/eip/test_openvpnconnection.py @@ -105,7 +105,7 @@ class OpenVPNConnectionTest(unittest.TestCase): self.manager.connect_to_management() def test_uds_telnet_called_on_connect(self): - self.manager.connect() + self.manager.connect_to_management() mock_UDSTelnet.assert_called_with( self.manager.host, port=self.manager.port) -- cgit v1.2.3 From b9f9e2d5df2d9aa64377a02eba03fd877b134a8a Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 22 Aug 2012 04:41:59 +0900 Subject: moved tests to directory --- src/leap/eip/test_eipconnection.py | 180 --------------------------- src/leap/eip/test_openvpnconnection.py | 136 -------------------- src/leap/eip/tests/test_eipconnection.py | 180 +++++++++++++++++++++++++++ src/leap/eip/tests/test_openvpnconnection.py | 136 ++++++++++++++++++++ 4 files changed, 316 insertions(+), 316 deletions(-) delete mode 100644 src/leap/eip/test_eipconnection.py delete mode 100644 src/leap/eip/test_openvpnconnection.py create mode 100644 src/leap/eip/tests/test_eipconnection.py create mode 100644 src/leap/eip/tests/test_openvpnconnection.py (limited to 'src/leap/eip') diff --git a/src/leap/eip/test_eipconnection.py b/src/leap/eip/test_eipconnection.py deleted file mode 100644 index 51772b7c..00000000 --- a/src/leap/eip/test_eipconnection.py +++ /dev/null @@ -1,180 +0,0 @@ -import ConfigParser -import logging -import platform - -logging.basicConfig() -logger = logging.getLogger(name=__name__) - -try: - import unittest2 as unittest -except ImportError: - import unittest - -from mock import Mock, patch # MagicMock - -from leap.eip.eipconnection import EIPConnection -from leap.eip.exceptions import ConnectionRefusedError - -_system = platform.system() - - -class NotImplementedError(Exception): - pass - - -@patch('OpenVPNConnection._get_or_create_config') -@patch('OpenVPNConnection._set_ovpn_command') -class MockedEIPConnection(EIPConnection): - def _get_or_create_config(self): - self.config = ConfigParser.ConfigParser() - self._set_ovpn_command() - - def _set_ovpn_command(self): - self.command = "mock_command" - self.args = [1, 2, 3] - - -class EIPConductorTest(unittest.TestCase): - - __name__ = "eip_conductor_tests" - - def setUp(self): - self.manager = Mock( - name="openvpnmanager_mock") - - self.con = MockedEIPConnection() - #manager=self.manager) - - def tearDown(self): - del self.con - - # - # helpers - # - - def _missing_test_for_plat(self, do_raise=False): - if do_raise: - raise NotImplementedError( - "This test is not implemented " - "for the running platform: %s" % - _system) - - # - # tests - # - - @unittest.skip - #ain't manager anymore! - def test_manager_was_initialized(self): - """ - manager init ok during conductor init? - """ - self.manager.assert_called_once_with() - - def test_vpnconnection_defaults(self): - """ - default attrs as expected - """ - con = self.con - self.assertEqual(con.autostart, True) - self.assertEqual(con.missing_pkexec, False) - self.assertEqual(con.missing_vpn_keyfile, False) - self.assertEqual(con.missing_provider, False) - self.assertEqual(con.bad_provider, False) - - def test_config_was_init(self): - """ - is there a config object? - """ - self.assertTrue(isinstance(self.con.config, - ConfigParser.ConfigParser)) - - def test_ovpn_command(self): - """ - set_ovpn_command called - """ - self.assertEqual(self.con.command, - "mock_command") - self.assertEqual(self.con.args, - [1, 2, 3]) - - # connect/disconnect calls - - def test_disconnect(self): - """ - disconnect method calls private and changes status - """ - self.con._disconnect = Mock( - name="_disconnect") - - # first we set status to connected - self.con.status.set_current(self.con.status.CONNECTED) - self.assertEqual(self.con.status.current, - self.con.status.CONNECTED) - - # disconnect - self.con.disconnect() - self.con._disconnect.assert_called_once_with() - - # new status should be disconnected - # XXX this should evolve and check no errors - # during disconnection - self.assertEqual(self.con.status.current, - self.con.status.DISCONNECTED) - - def test_connect(self): - """ - connect calls _launch_openvpn private - """ - self.con._launch_openvpn = Mock() - self.con.connect() - self.con._launch_openvpn.assert_called_once_with() - - # XXX tests breaking here ... - - def test_good_poll_connection_state(self): - """ - """ - #@patch -- - # self.manager.get_connection_state - - #XXX review this set of poll_state tests - #they SHOULD NOT NEED TO MOCK ANYTHING IN THE - #lower layers!! -- status, vpn_manager.. - #right now we're testing implementation, not - #behavior!!! - good_state = ["1345466946", "unknown_state", "ok", - "192.168.1.1", "192.168.1.100"] - self.con.get_connection_state = Mock(return_value=good_state) - self.con.status.set_vpn_state = Mock() - - state = self.con.poll_connection_state() - good_state[1] = "disconnected" - final_state = tuple(good_state) - self.con.status.set_vpn_state.assert_called_with("unknown_state") - self.assertEqual(state, final_state) - - # TODO between "good" and "bad" (exception raised) cases, - # we can still test for malformed states and see that only good - # states do have a change (and from only the expected transition - # states). - - def test_bad_poll_connection_state(self): - """ - get connection state raises ConnectionRefusedError - state is None - """ - self.con.get_connection_state = Mock( - side_effect=ConnectionRefusedError('foo!')) - state = self.con.poll_connection_state() - self.assertEqual(state, None) - - - # XXX more things to test: - # - called config routines during initz. - # - raising proper exceptions with no config - # - called proper checks on config / permissions - - -if __name__ == "__main__": - unittest.main() diff --git a/src/leap/eip/test_openvpnconnection.py b/src/leap/eip/test_openvpnconnection.py deleted file mode 100644 index dea75b55..00000000 --- a/src/leap/eip/test_openvpnconnection.py +++ /dev/null @@ -1,136 +0,0 @@ -import logging -import platform -#import socket - -logging.basicConfig() -logger = logging.getLogger(name=__name__) - -try: - import unittest2 as unittest -except ImportError: - import unittest - -from mock import Mock, patch # MagicMock - -from leap.eip import openvpnconnection -from leap.eip import exceptions as eip_exceptions -from leap.eip.udstelnet import UDSTelnet - -_system = platform.system() - - -class NotImplementedError(Exception): - pass - - -mock_UDSTelnet = Mock(spec=UDSTelnet) -# XXX cautious!!! -# this might be fragile right now (counting a global -# reference of calls I think. -# investigate this other form instead: -# http://www.voidspace.org.uk/python/mock/patch.html#start-and-stop - -# XXX redo after merge-refactor - - -@patch('openvpnconnection.OpenVPNConnection.connect_to_management') -class MockedOpenVPNConnection(openvpnconnection.OpenVPNConnection): - def __init__(self, *args, **kwargs): - self.mock_UDSTelnet = Mock() - super(MockedOpenVPNConnection, self).__init__( - *args, **kwargs) - self.tn = self.mock_UDSTelnet(self.host, self.port) - - def connect_to_management(self): - #print 'patched connect' - self.tn = mock_UDSTelnet(self.host, port=self.port) - - -class OpenVPNConnectionTest(unittest.TestCase): - - __name__ = "vpnconnection_tests" - - def setUp(self): - self.manager = MockedOpenVPNConnection() - - def tearDown(self): - del self.manager - - # - # helpers - # - - # XXX hey, refactor this to basetestclass - - def _missing_test_for_plat(self, do_raise=False): - if do_raise: - raise NotImplementedError( - "This test is not implemented " - "for the running platform: %s" % - _system) - - # - # tests - # - - @unittest.skipIf(_system == "Windows", "lin/mac only") - def test_lin_mac_default_init(self): - """ - check default host for management iface - """ - self.assertEqual(self.manager.host, '/tmp/.eip.sock') - self.assertEqual(self.manager.port, 'unix') - - @unittest.skipUnless(_system == "Windows", "win only") - def test_win_default_init(self): - """ - check default host for management iface - """ - # XXX should we make the platform specific switch - # here or in the vpn command string building? - self.assertEqual(self.manager.host, 'localhost') - self.assertEqual(self.manager.port, 7777) - - def test_port_types_init(self): - self.manager = MockedOpenVPNConnection(port="42") - self.assertEqual(self.manager.port, 42) - self.manager = MockedOpenVPNConnection() - self.assertEqual(self.manager.port, "unix") - self.manager = MockedOpenVPNConnection(port="bad") - self.assertEqual(self.manager.port, None) - - def test_connect_raises_missing_socket(self): - self.manager = openvpnconnection.OpenVPNConnection() - with self.assertRaises(eip_exceptions.MissingSocketError): - self.manager.connect_to_management() - - def test_uds_telnet_called_on_connect(self): - self.manager.connect_to_management() - mock_UDSTelnet.assert_called_with( - self.manager.host, - port=self.manager.port) - - @unittest.skip - def test_connect(self): - raise NotImplementedError - # XXX calls close - # calls UDSTelnet mock. - - # XXX - # tests to write: - # UDSTelnetTest (for real?) - # HAVE A LOOK AT CORE TESTS FOR TELNETLIB. - # very illustrative instead... - - # - raise MissingSocket - # - raise ConnectionRefusedError - # - test send command - # - tries connect - # - ... tries? - # - ... calls _seek_to_eof - # - ... read_until --> return value - # - ... - - -if __name__ == "__main__": - unittest.main() diff --git a/src/leap/eip/tests/test_eipconnection.py b/src/leap/eip/tests/test_eipconnection.py new file mode 100644 index 00000000..51772b7c --- /dev/null +++ b/src/leap/eip/tests/test_eipconnection.py @@ -0,0 +1,180 @@ +import ConfigParser +import logging +import platform + +logging.basicConfig() +logger = logging.getLogger(name=__name__) + +try: + import unittest2 as unittest +except ImportError: + import unittest + +from mock import Mock, patch # MagicMock + +from leap.eip.eipconnection import EIPConnection +from leap.eip.exceptions import ConnectionRefusedError + +_system = platform.system() + + +class NotImplementedError(Exception): + pass + + +@patch('OpenVPNConnection._get_or_create_config') +@patch('OpenVPNConnection._set_ovpn_command') +class MockedEIPConnection(EIPConnection): + def _get_or_create_config(self): + self.config = ConfigParser.ConfigParser() + self._set_ovpn_command() + + def _set_ovpn_command(self): + self.command = "mock_command" + self.args = [1, 2, 3] + + +class EIPConductorTest(unittest.TestCase): + + __name__ = "eip_conductor_tests" + + def setUp(self): + self.manager = Mock( + name="openvpnmanager_mock") + + self.con = MockedEIPConnection() + #manager=self.manager) + + def tearDown(self): + del self.con + + # + # helpers + # + + def _missing_test_for_plat(self, do_raise=False): + if do_raise: + raise NotImplementedError( + "This test is not implemented " + "for the running platform: %s" % + _system) + + # + # tests + # + + @unittest.skip + #ain't manager anymore! + def test_manager_was_initialized(self): + """ + manager init ok during conductor init? + """ + self.manager.assert_called_once_with() + + def test_vpnconnection_defaults(self): + """ + default attrs as expected + """ + con = self.con + self.assertEqual(con.autostart, True) + self.assertEqual(con.missing_pkexec, False) + self.assertEqual(con.missing_vpn_keyfile, False) + self.assertEqual(con.missing_provider, False) + self.assertEqual(con.bad_provider, False) + + def test_config_was_init(self): + """ + is there a config object? + """ + self.assertTrue(isinstance(self.con.config, + ConfigParser.ConfigParser)) + + def test_ovpn_command(self): + """ + set_ovpn_command called + """ + self.assertEqual(self.con.command, + "mock_command") + self.assertEqual(self.con.args, + [1, 2, 3]) + + # connect/disconnect calls + + def test_disconnect(self): + """ + disconnect method calls private and changes status + """ + self.con._disconnect = Mock( + name="_disconnect") + + # first we set status to connected + self.con.status.set_current(self.con.status.CONNECTED) + self.assertEqual(self.con.status.current, + self.con.status.CONNECTED) + + # disconnect + self.con.disconnect() + self.con._disconnect.assert_called_once_with() + + # new status should be disconnected + # XXX this should evolve and check no errors + # during disconnection + self.assertEqual(self.con.status.current, + self.con.status.DISCONNECTED) + + def test_connect(self): + """ + connect calls _launch_openvpn private + """ + self.con._launch_openvpn = Mock() + self.con.connect() + self.con._launch_openvpn.assert_called_once_with() + + # XXX tests breaking here ... + + def test_good_poll_connection_state(self): + """ + """ + #@patch -- + # self.manager.get_connection_state + + #XXX review this set of poll_state tests + #they SHOULD NOT NEED TO MOCK ANYTHING IN THE + #lower layers!! -- status, vpn_manager.. + #right now we're testing implementation, not + #behavior!!! + good_state = ["1345466946", "unknown_state", "ok", + "192.168.1.1", "192.168.1.100"] + self.con.get_connection_state = Mock(return_value=good_state) + self.con.status.set_vpn_state = Mock() + + state = self.con.poll_connection_state() + good_state[1] = "disconnected" + final_state = tuple(good_state) + self.con.status.set_vpn_state.assert_called_with("unknown_state") + self.assertEqual(state, final_state) + + # TODO between "good" and "bad" (exception raised) cases, + # we can still test for malformed states and see that only good + # states do have a change (and from only the expected transition + # states). + + def test_bad_poll_connection_state(self): + """ + get connection state raises ConnectionRefusedError + state is None + """ + self.con.get_connection_state = Mock( + side_effect=ConnectionRefusedError('foo!')) + state = self.con.poll_connection_state() + self.assertEqual(state, None) + + + # XXX more things to test: + # - called config routines during initz. + # - raising proper exceptions with no config + # - called proper checks on config / permissions + + +if __name__ == "__main__": + unittest.main() diff --git a/src/leap/eip/tests/test_openvpnconnection.py b/src/leap/eip/tests/test_openvpnconnection.py new file mode 100644 index 00000000..dea75b55 --- /dev/null +++ b/src/leap/eip/tests/test_openvpnconnection.py @@ -0,0 +1,136 @@ +import logging +import platform +#import socket + +logging.basicConfig() +logger = logging.getLogger(name=__name__) + +try: + import unittest2 as unittest +except ImportError: + import unittest + +from mock import Mock, patch # MagicMock + +from leap.eip import openvpnconnection +from leap.eip import exceptions as eip_exceptions +from leap.eip.udstelnet import UDSTelnet + +_system = platform.system() + + +class NotImplementedError(Exception): + pass + + +mock_UDSTelnet = Mock(spec=UDSTelnet) +# XXX cautious!!! +# this might be fragile right now (counting a global +# reference of calls I think. +# investigate this other form instead: +# http://www.voidspace.org.uk/python/mock/patch.html#start-and-stop + +# XXX redo after merge-refactor + + +@patch('openvpnconnection.OpenVPNConnection.connect_to_management') +class MockedOpenVPNConnection(openvpnconnection.OpenVPNConnection): + def __init__(self, *args, **kwargs): + self.mock_UDSTelnet = Mock() + super(MockedOpenVPNConnection, self).__init__( + *args, **kwargs) + self.tn = self.mock_UDSTelnet(self.host, self.port) + + def connect_to_management(self): + #print 'patched connect' + self.tn = mock_UDSTelnet(self.host, port=self.port) + + +class OpenVPNConnectionTest(unittest.TestCase): + + __name__ = "vpnconnection_tests" + + def setUp(self): + self.manager = MockedOpenVPNConnection() + + def tearDown(self): + del self.manager + + # + # helpers + # + + # XXX hey, refactor this to basetestclass + + def _missing_test_for_plat(self, do_raise=False): + if do_raise: + raise NotImplementedError( + "This test is not implemented " + "for the running platform: %s" % + _system) + + # + # tests + # + + @unittest.skipIf(_system == "Windows", "lin/mac only") + def test_lin_mac_default_init(self): + """ + check default host for management iface + """ + self.assertEqual(self.manager.host, '/tmp/.eip.sock') + self.assertEqual(self.manager.port, 'unix') + + @unittest.skipUnless(_system == "Windows", "win only") + def test_win_default_init(self): + """ + check default host for management iface + """ + # XXX should we make the platform specific switch + # here or in the vpn command string building? + self.assertEqual(self.manager.host, 'localhost') + self.assertEqual(self.manager.port, 7777) + + def test_port_types_init(self): + self.manager = MockedOpenVPNConnection(port="42") + self.assertEqual(self.manager.port, 42) + self.manager = MockedOpenVPNConnection() + self.assertEqual(self.manager.port, "unix") + self.manager = MockedOpenVPNConnection(port="bad") + self.assertEqual(self.manager.port, None) + + def test_connect_raises_missing_socket(self): + self.manager = openvpnconnection.OpenVPNConnection() + with self.assertRaises(eip_exceptions.MissingSocketError): + self.manager.connect_to_management() + + def test_uds_telnet_called_on_connect(self): + self.manager.connect_to_management() + mock_UDSTelnet.assert_called_with( + self.manager.host, + port=self.manager.port) + + @unittest.skip + def test_connect(self): + raise NotImplementedError + # XXX calls close + # calls UDSTelnet mock. + + # XXX + # tests to write: + # UDSTelnetTest (for real?) + # HAVE A LOOK AT CORE TESTS FOR TELNETLIB. + # very illustrative instead... + + # - raise MissingSocket + # - raise ConnectionRefusedError + # - test send command + # - tries connect + # - ... tries? + # - ... calls _seek_to_eof + # - ... read_until --> return value + # - ... + + +if __name__ == "__main__": + unittest.main() -- cgit v1.2.3 From 5f6064b9dfa102b1115d5e3a6ecfb22cdcf82d14 Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 22 Aug 2012 04:47:14 +0900 Subject: config tests --- src/leap/eip/config.py | 72 +++++++++---- src/leap/eip/tests/test_config.py | 210 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 264 insertions(+), 18 deletions(-) create mode 100644 src/leap/eip/tests/test_config.py (limited to 'src/leap/eip') diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py index 8e55d789..8c67a258 100644 --- a/src/leap/eip/config.py +++ b/src/leap/eip/config.py @@ -9,15 +9,37 @@ from leap.util.fileutil import (which, mkdir_p, check_and_fix_urw_only) from leap.baseapp.permcheck import (is_pkexec_in_system, is_auth_agent_running) -from leap.eip import exceptions as eip_exceptions logger = logging.getLogger(name=__name__) logger.setLevel('DEBUG') -# XXX this has to be REMOVED -# and all these options passed in the -# command line --> move to build_ovpn_command -# issue #447 +# XXX move exceptions: +# from leap.eip import exceptions as eip_exceptions + + +class EIPNoPkexecAvailable(Exception): + pass + + +class EIPNoPolkitAuthAgentAvailable(Exception): + pass + + +class EIPInitNoProviderError(Exception): + pass + + +class EIPInitBadProviderError(Exception): + pass + + +class EIPInitNoKeyFileError(Exception): + pass + + +class EIPInitBadKeyFilePermError(Exception): + pass + OPENVPN_CONFIG_TEMPLATE = """#Autogenerated by eip-client wizard remote {VPN_REMOTE_HOST} {VPN_REMOTE_PORT} @@ -114,6 +136,10 @@ def check_or_create_default_vpnconf(config): # instead. try: + # XXX by now, we're expecting + # only IP format for remote. + # We should allow also domain names, + # and make a reverse resolv. remote_ip = config.get('provider', 'remote_ip') validate_ip(remote_ip) @@ -158,6 +184,15 @@ def check_or_create_default_vpnconf(config): f.write(ovpn_config) +def get_username(): + return os.getlogin() + + +def get_groupname(): + gid = os.getgroups()[-1] + return grp.getgrgid(gid).gr_name + + def build_ovpn_options(daemon=False): """ build a list of options @@ -175,16 +210,11 @@ def build_ovpn_options(daemon=False): # get user/group name # also from config. - user = os.getlogin() - gid = os.getgroups()[-1] - group = grp.getgrgid(gid).gr_name + user = get_username() + group = get_groupname() opts = [] - #moved to config files - #opts.append('--persist-tun') - #opts.append('--persist-key') - # set user and group opts.append('--user') opts.append('%s' % user) @@ -219,6 +249,8 @@ def build_ovpn_options(daemon=False): opts.append('--config') default_provider_path = get_default_provider_path() + + # XXX get rid of config_file at all ovpncnf = get_config_file( 'openvpn.conf', folder=default_provider_path) @@ -233,7 +265,7 @@ def build_ovpn_options(daemon=False): return opts -def build_ovpn_command(config, debug=False): +def build_ovpn_command(config, debug=False, do_pkexec_check=True): """ build a string with the complete openvpn invocation @@ -251,17 +283,16 @@ def build_ovpn_command(config, debug=False): if config.has_option('openvpn', 'use_pkexec'): use_pkexec = config.get('openvpn', 'use_pkexec') - if platform.system() == "Linux" and use_pkexec: + if platform.system() == "Linux" and use_pkexec and do_pkexec_check: # XXX check for both pkexec (done) # AND a suitable authentication # agent running. - # (until we implement setuid helper) logger.info('use_pkexec set to True') if not is_pkexec_in_system(): logger.error('no pkexec in system') - raise eip_exceptions.EIPNoPkexecAvailable + raise EIPNoPkexecAvailable if not is_auth_agent_running(): logger.warning( @@ -269,7 +300,7 @@ def build_ovpn_command(config, debug=False): "pkexec will use its own text " "based authentication agent. " "that's probably a bad idea") - raise eip_exceptions.EIPNoPolkitAuthAgentAvailable + raise EIPNoPolkitAuthAgentAvailable command.append('pkexec') @@ -283,7 +314,11 @@ def build_ovpn_command(config, debug=False): 'openvpn_binary') if ovpn: - command.append(ovpn) + vpn_command = ovpn + else: + vpn_command = "openvpn" + + command.append(vpn_command) daemon_mode = not debug @@ -291,6 +326,7 @@ def build_ovpn_command(config, debug=False): command.append(opt) # XXX check len and raise proper error + return [command[0], command[1:]] diff --git a/src/leap/eip/tests/test_config.py b/src/leap/eip/tests/test_config.py new file mode 100644 index 00000000..12679ec6 --- /dev/null +++ b/src/leap/eip/tests/test_config.py @@ -0,0 +1,210 @@ +import ConfigParser +import os +import platform +import shutil +import socket +import tempfile + +try: + import unittest2 as unittest +except ImportError: + import unittest + +from leap.eip import config + +_system = platform.system() + + +class NotImplementedError(Exception): + pass + +# XXX use mock_open here? + + +class EIPConfigTest(unittest.TestCase): + + __name__ = "eip_config_tests" + + def setUp(self): + self.old_path = os.environ['PATH'] + + self.tdir = tempfile.mkdtemp() + + bin_tdir = os.path.join( + self.tdir, + 'bin') + os.mkdir(bin_tdir) + os.environ['PATH'] = bin_tdir + + def tearDown(self): + os.environ['PATH'] = self.old_path + shutil.rmtree(self.tdir) + # + # helpers + # + + def get_username(self): + return config.get_username() + + def get_groupname(self): + return config.get_groupname() + + def _missing_test_for_plat(self, do_raise=False): + if do_raise: + raise NotImplementedError( + "This test is not implemented " + "for the running platform: %s" % + _system) + + def touch_exec(self): + tfile = os.path.join( + self.tdir, + 'bin', + 'openvpn') + open(tfile, 'bw').close() + + def get_empty_config(self): + _config = ConfigParser.ConfigParser() + return _config + + def get_minimal_config(self): + _config = ConfigParser.ConfigParser() + return _config + + def get_expected_openvpn_args(self): + args = [] + username = self.get_username() + groupname = self.get_groupname() + + args.append('--user') + args.append(username) + args.append('--group') + args.append(groupname) + args.append('--management-client-user') + args.append(username) + args.append('--management-signal') + args.append('--management') + + #XXX hey! + #get platform switches here! + args.append('/tmp/.eip.sock') + args.append('unix') + args.append('--config') + #XXX bad assumption. FIXME: expand $HOME + args.append('/home/%s/.config/leap/providers/default/openvpn.conf' % + username) + return args + + # + # tests + # + + # XXX fixme! /home/user should + # be replaced for proper home lookup. + + @unittest.skipUnless(_system == "Linux", "linux only") + def test_lin_get_config_file(self): + """ + config file path where expected? (linux) + """ + self.assertEqual( + config.get_config_file( + 'test', folder="foo/bar"), + '/home/%s/.config/leap/foo/bar/test' % + self.get_username()) + + @unittest.skipUnless(_system == "Darwin", "mac only") + def test_mac_get_config_file(self): + """ + config file path where expected? (mac) + """ + self._missing_test_for_plat(do_raise=True) + + @unittest.skipUnless(_system == "Windows", "win only") + def test_win_get_config_file(self): + """ + config file path where expected? + """ + self._missing_test_for_plat(do_raise=True) + + # + # XXX hey, I'm raising exceptions here + # on purpose. just wanted to make sure + # that the skip stuff is doing it right. + # If you're working on win/macos tests, + # feel free to remove tests that you see + # are too redundant. + + @unittest.skipUnless(_system == "Linux", "linux only") + def test_lin_get_config_dir(self): + """ + nice config dir? (linux) + """ + self.assertEqual( + config.get_config_dir(), + '/home/%s/.config/leap' % + self.get_username()) + + @unittest.skipUnless(_system == "Darwin", "mac only") + def test_mac_get_config_dir(self): + """ + nice config dir? (mac) + """ + self._missing_test_for_plat(do_raise=True) + + @unittest.skipUnless(_system == "Windows", "win only") + def test_win_get_config_dir(self): + """ + nice config dir? (win) + """ + self._missing_test_for_plat(do_raise=True) + + # provider paths + + @unittest.skipUnless(_system == "Linux", "linux only") + def test_get_default_provider_path(self): + """ + is default provider path ok? + """ + self.assertEqual( + config.get_default_provider_path(), + '/home/%s/.config/leap/providers/default/' % + self.get_username()) + + # validate ip + + def test_validate_ip(self): + """ + check our ip validation + """ + config.validate_ip('3.3.3.3') + with self.assertRaises(socket.error): + config.validate_ip('255.255.255.256') + with self.assertRaises(socket.error): + config.validate_ip('foobar') + + @unittest.skip + def test_validate_domain(self): + """ + code to be written yet + """ + pass + + # build command string + # these tests are going to have to check + # many combinations. we should inject some + # params in the function call, to disable + # some checks. + # XXX breaking! + + def test_build_ovpn_command_empty_config(self): + _config = self.get_empty_config() + command, args = config.build_ovpn_command( + _config, + do_pkexec_check=False) + self.assertEqual(command, 'openvpn') + self.assertEqual(args, self.get_expected_openvpn_args()) + + +if __name__ == "__main__": + unittest.main() -- cgit v1.2.3 From 6fcbd68152689f98d9c5b7526eee2e1e9b7dd0a2 Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 22 Aug 2012 04:52:31 +0900 Subject: minor tweaks to setup + env test --- src/leap/eip/tests/test_config.py | 1 - 1 file changed, 1 deletion(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/tests/test_config.py b/src/leap/eip/tests/test_config.py index 12679ec6..11433777 100644 --- a/src/leap/eip/tests/test_config.py +++ b/src/leap/eip/tests/test_config.py @@ -195,7 +195,6 @@ class EIPConfigTest(unittest.TestCase): # many combinations. we should inject some # params in the function call, to disable # some checks. - # XXX breaking! def test_build_ovpn_command_empty_config(self): _config = self.get_empty_config() -- cgit v1.2.3 From 6ce22c7ebd293550473bfa5453a2f720dffad3e8 Mon Sep 17 00:00:00 2001 From: antialias Date: Tue, 21 Aug 2012 13:46:01 -0700 Subject: minor pep8 clean up. --- src/leap/eip/config.py | 8 ++++---- src/leap/eip/exceptions.py | 2 -- src/leap/eip/tests/tests_config.py | 7 ++++--- 3 files changed, 8 insertions(+), 9 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py index a219fedb..e0151e87 100644 --- a/src/leap/eip/config.py +++ b/src/leap/eip/config.py @@ -403,14 +403,14 @@ def check_vpn_keys(config): check_and_fix_urw_only(keyfile) except OSError: raise EIPInitBadKeyFilePermError - - + + def get_config_json(config_file=None): """ will replace get_config function be developing them in parralel for branch purposes. @param: configuration file - @type: file + @type: file @rparam: configuration turples @rtype: dictionary """ @@ -421,7 +421,7 @@ def get_config_json(config_file=None): if not os.path.isdir(dpath): mkdir_p(dpath) with open(fpath, 'wb') as configfile: - configfile.write() + configfile.flush() config_file = open(fpath) config = json.load(config_file) diff --git a/src/leap/eip/exceptions.py b/src/leap/eip/exceptions.py index ac61f42b..3719c605 100644 --- a/src/leap/eip/exceptions.py +++ b/src/leap/eip/exceptions.py @@ -61,5 +61,3 @@ class EIPInitNoKeyFileError(Exception): class EIPInitBadKeyFilePermError(Exception): pass - - diff --git a/src/leap/eip/tests/tests_config.py b/src/leap/eip/tests/tests_config.py index 6534723e..5a0e2d94 100644 --- a/src/leap/eip/tests/tests_config.py +++ b/src/leap/eip/tests/tests_config.py @@ -5,14 +5,15 @@ import unittest from leap.eip import config + class TestConfig(unittest.TestCase): """ Test configuration help functions. """ + def test_get_config_json(self): config_js = config.get_config_json() self.assertTrue(isinstance(config_js, dict)) - self.assertTrue(config_js.has_key('transport')) - self.assertTrue(config_js.has_key('provider')) + self.assertTrue('transport' in config_js) + self.assertTrue('provider' in config_js) self.assertEqual(config_js['provider'], "testprovider.org") - -- cgit v1.2.3 From 3bd45c8e1e020bebf041bc266c5092a41f944130 Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 22 Aug 2012 07:05:39 +0900 Subject: removed dup exceptions --- src/leap/eip/config.py | 28 ---------------------------- 1 file changed, 28 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py index 8c67a258..a1dc2764 100644 --- a/src/leap/eip/config.py +++ b/src/leap/eip/config.py @@ -13,34 +13,6 @@ from leap.baseapp.permcheck import (is_pkexec_in_system, logger = logging.getLogger(name=__name__) logger.setLevel('DEBUG') -# XXX move exceptions: -# from leap.eip import exceptions as eip_exceptions - - -class EIPNoPkexecAvailable(Exception): - pass - - -class EIPNoPolkitAuthAgentAvailable(Exception): - pass - - -class EIPInitNoProviderError(Exception): - pass - - -class EIPInitBadProviderError(Exception): - pass - - -class EIPInitNoKeyFileError(Exception): - pass - - -class EIPInitBadKeyFilePermError(Exception): - pass - - OPENVPN_CONFIG_TEMPLATE = """#Autogenerated by eip-client wizard remote {VPN_REMOTE_HOST} {VPN_REMOTE_PORT} -- cgit v1.2.3 From 04676d5869c33a419d199b1be7dbb616c31434c2 Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 22 Aug 2012 07:12:03 +0900 Subject: moved json-config tests --- src/leap/eip/tests/test_config.py | 9 +++++++++ src/leap/eip/tests/tests_config.py | 19 ------------------- 2 files changed, 9 insertions(+), 19 deletions(-) delete mode 100644 src/leap/eip/tests/tests_config.py (limited to 'src/leap/eip') diff --git a/src/leap/eip/tests/test_config.py b/src/leap/eip/tests/test_config.py index 11433777..051faa29 100644 --- a/src/leap/eip/tests/test_config.py +++ b/src/leap/eip/tests/test_config.py @@ -204,6 +204,15 @@ class EIPConfigTest(unittest.TestCase): self.assertEqual(command, 'openvpn') self.assertEqual(args, self.get_expected_openvpn_args()) + # json config + + def test_get_config_json(self): + config_js = config.get_config_json() + self.assertTrue(isinstance(config_js, dict)) + self.assertTrue('transport' in config_js) + self.assertTrue('provider' in config_js) + self.assertEqual(config_js['provider'], "testprovider.org") + if __name__ == "__main__": unittest.main() diff --git a/src/leap/eip/tests/tests_config.py b/src/leap/eip/tests/tests_config.py deleted file mode 100644 index 5a0e2d94..00000000 --- a/src/leap/eip/tests/tests_config.py +++ /dev/null @@ -1,19 +0,0 @@ - -"""Test config helper functions""" - -import unittest - -from leap.eip import config - - -class TestConfig(unittest.TestCase): - """ - Test configuration help functions. - """ - - def test_get_config_json(self): - config_js = config.get_config_json() - self.assertTrue(isinstance(config_js, dict)) - self.assertTrue('transport' in config_js) - self.assertTrue('provider' in config_js) - self.assertEqual(config_js['provider'], "testprovider.org") -- cgit v1.2.3 From 83ac2efaa10de68f7fd35189f6cf272b03d60a30 Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 22 Aug 2012 07:46:51 +0900 Subject: fix exceptions location --- src/leap/eip/config.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py index c77bb142..f38268e2 100644 --- a/src/leap/eip/config.py +++ b/src/leap/eip/config.py @@ -10,6 +10,7 @@ from leap.util.fileutil import (which, mkdir_p, check_and_fix_urw_only) from leap.baseapp.permcheck import (is_pkexec_in_system, is_auth_agent_running) +from leap.eip import exceptions as eip_exceptions logger = logging.getLogger(name=__name__) logger.setLevel('DEBUG') @@ -122,7 +123,7 @@ def check_or_create_default_vpnconf(config): except socket.error: # this does not look like an ip, dave - raise EIPInitBadProviderError + raise eip_exceptions.EIPInitBadProviderError if config.has_option('provider', 'remote_port'): remote_port = config.get('provider', @@ -265,7 +266,7 @@ def build_ovpn_command(config, debug=False, do_pkexec_check=True): if not is_pkexec_in_system(): logger.error('no pkexec in system') - raise EIPNoPkexecAvailable + raise eip_exceptions.EIPNoPkexecAvailable if not is_auth_agent_running(): logger.warning( @@ -273,7 +274,7 @@ def build_ovpn_command(config, debug=False, do_pkexec_check=True): "pkexec will use its own text " "based authentication agent. " "that's probably a bad idea") - raise EIPNoPolkitAuthAgentAvailable + raise eip_exceptions.EIPNoPolkitAuthAgentAvailable command.append('pkexec') @@ -410,7 +411,7 @@ def check_vpn_keys(config): try: check_and_fix_urw_only(keyfile) except OSError: - raise EIPInitBadKeyFilePermError + raise eip_exceptions.EIPInitBadKeyFilePermError def get_config_json(config_file=None): -- cgit v1.2.3 From 24f288b5214b814e2e7daa6ef41b226a27d96b81 Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 22 Aug 2012 07:49:19 +0900 Subject: bye bye conductor, watcher (after refactor) --- src/leap/eip/conductor.py | 305 --------------------------------------------- src/leap/eip/vpnwatcher.py | 169 ------------------------- 2 files changed, 474 deletions(-) delete mode 100644 src/leap/eip/conductor.py delete mode 100644 src/leap/eip/vpnwatcher.py (limited to 'src/leap/eip') diff --git a/src/leap/eip/conductor.py b/src/leap/eip/conductor.py deleted file mode 100644 index f528d639..00000000 --- a/src/leap/eip/conductor.py +++ /dev/null @@ -1,305 +0,0 @@ -""" -stablishes a vpn connection and monitors its state -""" -from __future__ import (division, unicode_literals, print_function) -#import threading -from functools import partial -import logging - -from leap.util.coroutines import spawn_and_watch_process - -# XXX from leap.eip import config as eipconfig -# from leap.eip import exceptions as eip_exceptions - -from leap.eip.config import (get_config, build_ovpn_command, - check_or_create_default_vpnconf, - check_vpn_keys, - EIPNoPkexecAvailable, - EIPNoPolkitAuthAgentAvailable, - EIPInitNoProviderError, - EIPInitBadProviderError, - EIPInitNoKeyFileError, - EIPInitBadKeyFilePermError) -from leap.eip.vpnwatcher import EIPConnectionStatus, status_watcher -from leap.eip.vpnmanager import OpenVPNManager, ConnectionRefusedError - -logger = logging.getLogger(name=__name__) - - -# -# Openvpn related classes -# -# XXX deprecated! moved to eipconnection - - -class OpenVPNConnection(object): - """ - All related to invocation - of the openvpn binary - """ - # Connection Methods - - def __init__(self, config_file=None, - watcher_cb=None, debug=False): - #XXX FIXME - #change watcher_cb to line_observer - """ - :param config_file: configuration file to read from - :param watcher_cb: callback to be \ -called for each line in watched stdout - :param signal_map: dictionary of signal names and callables \ -to be triggered for each one of them. - :type config_file: str - :type watcher_cb: function - :type signal_map: dict - """ - # XXX get host/port from config - self.manager = OpenVPNManager() - self.debug = debug - #print('conductor:%s' % debug) - - self.config_file = config_file - self.watcher_cb = watcher_cb - #self.signal_maps = signal_maps - - self.subp = None - self.watcher = None - - self.server = None - self.port = None - self.proto = None - - self.missing_pkexec = False - self.missing_auth_agent = False - self.bad_keyfile_perms = False - self.missing_vpn_keyfile = False - self.missing_provider = False - self.bad_provider = False - - self.command = None - self.args = None - - self.autostart = True - self._get_or_create_config() - self._check_vpn_keys() - - def _set_autostart(self): - config = self.config - if config.has_option('openvpn', 'autostart'): - autostart = config.getboolean('openvpn', - 'autostart') - self.autostart = autostart - else: - if config.has_option('DEFAULT', 'autostart'): - autostart = config.getboolean('DEFAULT', - 'autostart') - self.autostart = autostart - - def _set_ovpn_command(self): - config = self.config - if config.has_option('openvpn', 'command'): - commandline = config.get('openvpn', 'command') - - command_split = commandline.split(' ') - command = command_split[0] - if len(command_split) > 1: - args = command_split[1:] - else: - args = [] - - self.command = command - self.args = args - else: - # no command in config, we build it up. - # XXX check also for command-line --command flag - try: - command, args = build_ovpn_command(config, - debug=self.debug) - except EIPNoPolkitAuthAgentAvailable: - command = args = None - self.missing_auth_agent = True - except EIPNoPkexecAvailable: - command = args = None - self.missing_pkexec = True - - # XXX if not command, signal error. - self.command = command - self.args = args - - def _check_ovpn_config(self): - """ - checks if there is a default openvpn config. - if not, it writes one with info from the provider - definition file - """ - # TODO - # - get --with-openvpn-config from opts - try: - check_or_create_default_vpnconf(self.config) - except EIPInitNoProviderError: - logger.error('missing default provider definition') - self.missing_provider = True - except EIPInitBadProviderError: - logger.error('bad provider definition') - self.bad_provider = True - - def _get_or_create_config(self): - """ - retrieves the config options from defaults or - home file, or config file passed in command line. - populates command and args to be passed to subprocess. - """ - config = get_config(config_file=self.config_file) - self.config = config - - self._set_autostart() - self._set_ovpn_command() - self._check_ovpn_config() - - def _check_vpn_keys(self): - """ - checks for correct permissions on vpn keys - """ - try: - check_vpn_keys(self.config) - except EIPInitNoKeyFileError: - self.missing_vpn_keyfile = True - except EIPInitBadKeyFilePermError: - logger.error('error while checking vpn keys') - self.bad_keyfile_perms = True - - def _launch_openvpn(self): - """ - invocation of openvpn binaries in a subprocess. - """ - #XXX TODO: - #deprecate watcher_cb, - #use _only_ signal_maps instead - - if self.watcher_cb is not None: - linewrite_callback = self.watcher_cb - else: - #XXX get logger instead - linewrite_callback = lambda line: print('watcher: %s' % line) - - observers = (linewrite_callback, - partial(status_watcher, self.status)) - subp, watcher = spawn_and_watch_process( - self.command, - self.args, - observers=observers) - self.subp = subp - self.watcher = watcher - - #conn_result = self.status.CONNECTED - #return conn_result - - def _try_connection(self): - """ - attempts to connect - """ - if self.command is None: - raise EIPNoCommandError - if self.subp is not None: - print('cowardly refusing to launch subprocess again') - return - self._launch_openvpn() - - def cleanup(self): - """ - terminates child subprocess - """ - if self.subp: - self.subp.terminate() - - -class EIPConductor(OpenVPNConnection): - """ - Manages the execution of the OpenVPN process, auto starts, monitors the - network connection, handles configuration, fixes leaky hosts, handles - errors, etc. - Preferences will be stored via the Storage API. (TBD) - Status updates (connected, bandwidth, etc) are signaled to the GUI. - """ - - def __init__(self, *args, **kwargs): - self.settingsfile = kwargs.get('settingsfile', None) - self.logfile = kwargs.get('logfile', None) - self.error_queue = [] - self.desired_con_state = None # ??? - - status_signals = kwargs.pop('status_signals', None) - self.status = EIPConnectionStatus(callbacks=status_signals) - - super(EIPConductor, self).__init__(*args, **kwargs) - - def connect(self): - """ - entry point for connection process - """ - self.manager.forget_errors() - self._try_connection() - # XXX should capture errors here? - - def disconnect(self): - """ - disconnects client - """ - self._disconnect() - self.status.change_to(self.status.DISCONNECTED) - - def poll_connection_state(self): - """ - """ - try: - state = self.manager.get_connection_state() - except ConnectionRefusedError: - # connection refused. might be not ready yet. - return - if not state: - return - (ts, status_step, - ok, ip, remote) = state - self.status.set_vpn_state(status_step) - status_step = self.status.get_readable_status() - return (ts, status_step, ok, ip, remote) - - def get_icon_name(self): - """ - get icon name from status object - """ - return self.status.get_state_icon() - - # - # private methods - # - - def _disconnect(self): - """ - private method for disconnecting - """ - if self.subp is not None: - self.subp.terminate() - self.subp = None - # XXX signal state changes! :) - - def _is_alive(self): - """ - don't know yet - """ - pass - - def _connect(self): - """ - entry point for connection cascade methods. - """ - #conn_result = ConState.DISCONNECTED - try: - conn_result = self._try_connection() - except UnrecoverableError as except_msg: - logger.error("FATAL: %s" % unicode(except_msg)) - conn_result = self.status.UNRECOVERABLE - except Exception as except_msg: - self.error_queue.append(except_msg) - logger.error("Failed Connection: %s" % - unicode(except_msg)) diff --git a/src/leap/eip/vpnwatcher.py b/src/leap/eip/vpnwatcher.py deleted file mode 100644 index 09bd5811..00000000 --- a/src/leap/eip/vpnwatcher.py +++ /dev/null @@ -1,169 +0,0 @@ -"""generic watcher object that keeps track of connection status""" -# This should be deprecated in favor of daemon mode + management -# interface. But we can leave it here for debug purposes. - - -class EIPConnectionStatus(object): - """ - Keep track of client (gui) and openvpn - states. - - These are the OpenVPN states: - CONNECTING -- OpenVPN's initial state. - WAIT -- (Client only) Waiting for initial response - from server. - AUTH -- (Client only) Authenticating with server. - GET_CONFIG -- (Client only) Downloading configuration options - from server. - ASSIGN_IP -- Assigning IP address to virtual network - interface. - ADD_ROUTES -- Adding routes to system. - CONNECTED -- Initialization Sequence Completed. - RECONNECTING -- A restart has occurred. - EXITING -- A graceful exit is in progress. - - We add some extra states: - - DISCONNECTED -- GUI initial state. - UNRECOVERABLE -- An unrecoverable error has been raised - while invoking openvpn service. - """ - CONNECTING = 1 - WAIT = 2 - AUTH = 3 - GET_CONFIG = 4 - ASSIGN_IP = 5 - ADD_ROUTES = 6 - CONNECTED = 7 - RECONNECTING = 8 - EXITING = 9 - - # gui specific states: - UNRECOVERABLE = 11 - DISCONNECTED = 0 - - def __init__(self, callbacks=None): - """ - EIPConnectionStatus is initialized with a tuple - of signals to be triggered. - :param callbacks: a tuple of (callable) observers - :type callbacks: tuple - """ - # (callbacks to connect to signals in Qt-land) - self.current = self.DISCONNECTED - self.previous = None - self.callbacks = callbacks - - def get_readable_status(self): - # XXX DRY status / labels a little bit. - # think we'll want to i18n this. - human_status = { - 0: 'disconnected', - 1: 'connecting', - 2: 'waiting', - 3: 'authenticating', - 4: 'getting config', - 5: 'assigning ip', - 6: 'adding routes', - 7: 'connected', - 8: 'reconnecting', - 9: 'exiting', - 11: 'unrecoverable error', - } - return human_status[self.current] - - def get_state_icon(self): - """ - returns the high level icon - for each fine-grain openvpn state - """ - connecting = (self.CONNECTING, - self.WAIT, - self.AUTH, - self.GET_CONFIG, - self.ASSIGN_IP, - self.ADD_ROUTES) - connected = (self.CONNECTED,) - disconnected = (self.DISCONNECTED, - self.UNRECOVERABLE) - - # this can be made smarter, - # but it's like it'll change, - # so +readability. - - if self.current in connecting: - return "connecting" - if self.current in connected: - return "connected" - if self.current in disconnected: - return "disconnected" - - def set_vpn_state(self, status): - """ - accepts a state string from the management - interface, and sets the internal state. - :param status: openvpn STATE (uppercase). - :type status: str - """ - if hasattr(self, status): - self.change_to(getattr(self, status)) - - def set_current(self, to): - """ - setter for the 'current' property - :param to: destination state - :type to: int - """ - self.current = to - - def change_to(self, to): - """ - :param to: destination state - :type to: int - """ - if to == self.current: - return - changed = False - from_ = self.current - self.current = to - - # We can add transition restrictions - # here to ensure no transitions are - # allowed outside the fsm. - - self.set_current(to) - changed = True - - #trigger signals (as callbacks) - #print('current state: %s' % self.current) - if changed: - self.previous = from_ - if self.callbacks: - for cb in self.callbacks: - if callable(cb): - cb(self) - - -def status_watcher(cs, line): - """ - a wrapper that calls to ConnectionStatus object - :param cs: a EIPConnectionStatus instance - :type cs: EIPConnectionStatus object - :param line: a single line of the watched output - :type line: str - """ - #print('status watcher watching') - - # from the mullvad code, should watch for - # things like: - # "Initialization Sequence Completed" - # "With Errors" - # "Tap-Win32" - - if "Completed" in line: - cs.change_to(cs.CONNECTED) - return - - if "Initial packet from" in line: - cs.change_to(cs.CONNECTING) - return -- cgit v1.2.3 From 5636f50cfa36bfa439651b4917b0beb3f9624ea6 Mon Sep 17 00:00:00 2001 From: kali Date: Thu, 23 Aug 2012 03:21:39 +0900 Subject: test_config uses the new leap base testcase --- src/leap/eip/tests/test_config.py | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/tests/test_config.py b/src/leap/eip/tests/test_config.py index 051faa29..b4ad66e5 100644 --- a/src/leap/eip/tests/test_config.py +++ b/src/leap/eip/tests/test_config.py @@ -10,35 +10,22 @@ try: except ImportError: import unittest +from leap.testing.basetest import BaseLeapTest from leap.eip import config _system = platform.system() -class NotImplementedError(Exception): - pass - -# XXX use mock_open here? - - -class EIPConfigTest(unittest.TestCase): +class EIPConfigTest(BaseLeapTest): __name__ = "eip_config_tests" def setUp(self): - self.old_path = os.environ['PATH'] - - self.tdir = tempfile.mkdtemp() - - bin_tdir = os.path.join( - self.tdir, - 'bin') - os.mkdir(bin_tdir) - os.environ['PATH'] = bin_tdir + pass def tearDown(self): - os.environ['PATH'] = self.old_path - shutil.rmtree(self.tdir) + pass + # # helpers # @@ -58,7 +45,7 @@ class EIPConfigTest(unittest.TestCase): def touch_exec(self): tfile = os.path.join( - self.tdir, + self.tempfile, 'bin', 'openvpn') open(tfile, 'bw').close() -- cgit v1.2.3 From 9891fc2f6869db7fc56503087ce124d74f5fc3b7 Mon Sep 17 00:00:00 2001 From: antialias Date: Wed, 22 Aug 2012 14:01:22 -0700 Subject: moved help functions from eip/config.py to base/configuration.py. --- src/leap/eip/config.py | 86 +++----------------------------------------------- 1 file changed, 4 insertions(+), 82 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py index f38268e2..b461422a 100644 --- a/src/leap/eip/config.py +++ b/src/leap/eip/config.py @@ -8,6 +8,10 @@ import socket from leap.util.fileutil import (which, mkdir_p, check_and_fix_urw_only) +from leap.base.configuration import (get_default_provider_path, + get_config_file, + get_username, + get_groupname) from leap.baseapp.permcheck import (is_pkexec_in_system, is_auth_agent_running) from leap.eip import exceptions as eip_exceptions @@ -32,55 +36,6 @@ ca {LEAP_EIP_KEYS} """ -def get_config_dir(): - """ - get the base dir for all leap config - @rparam: config path - @rtype: string - """ - # TODO - # check for $XDG_CONFIG_HOME var? - # get a more sensible path for win/mac - # kclair: opinion? ^^ - return os.path.expanduser( - os.path.join('~', - '.config', - 'leap')) - - -def get_config_file(filename, folder=None): - """ - concatenates the given filename - with leap config dir. - @param filename: name of the file - @type filename: string - @rparam: full path to config file - """ - path = [] - path.append(get_config_dir()) - if folder is not None: - path.append(folder) - path.append(filename) - return os.path.join(*path) - - -def get_default_provider_path(): - default_subpath = os.path.join("providers", - "default") - default_provider_path = get_config_file( - '', - folder=default_subpath) - return default_provider_path - - -def validate_ip(ip_str): - """ - raises exception if the ip_str is - not a valid representation of an ip - """ - socket.inet_aton(ip_str) - - def check_or_create_default_vpnconf(config): """ checks that a vpn config file @@ -158,15 +113,6 @@ def check_or_create_default_vpnconf(config): f.write(ovpn_config) -def get_username(): - return os.getlogin() - - -def get_groupname(): - gid = os.getgroups()[-1] - return grp.getgrgid(gid).gr_name - - def build_ovpn_options(daemon=False): """ build a list of options @@ -412,27 +358,3 @@ def check_vpn_keys(config): check_and_fix_urw_only(keyfile) except OSError: raise eip_exceptions.EIPInitBadKeyFilePermError - - -def get_config_json(config_file=None): - """ - will replace get_config function be developing them - in parralel for branch purposes. - @param: configuration file - @type: file - @rparam: configuration turples - @rtype: dictionary - """ - if not config_file: - fpath = get_config_file('eip.json') - if not os.path.isfile(fpath): - dpath, cfile = os.path.split(fpath) - if not os.path.isdir(dpath): - mkdir_p(dpath) - with open(fpath, 'wb') as configfile: - configfile.flush() - config_file = open(fpath) - - config = json.load(config_file) - - return config -- cgit v1.2.3 From dc10833bedcdecf081a7c79678614c5521445164 Mon Sep 17 00:00:00 2001 From: antialias Date: Wed, 22 Aug 2012 19:47:41 -0700 Subject: grabs a definition.json file if one isn't present. includes some basic error handling and tests. uses the requests library for network interactions and mocks for simulating network states. --- src/leap/eip/tests/test_config.py | 104 -------------------------------------- 1 file changed, 104 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/tests/test_config.py b/src/leap/eip/tests/test_config.py index b4ad66e5..0e1a3a01 100644 --- a/src/leap/eip/tests/test_config.py +++ b/src/leap/eip/tests/test_config.py @@ -82,101 +82,6 @@ class EIPConfigTest(BaseLeapTest): username) return args - # - # tests - # - - # XXX fixme! /home/user should - # be replaced for proper home lookup. - - @unittest.skipUnless(_system == "Linux", "linux only") - def test_lin_get_config_file(self): - """ - config file path where expected? (linux) - """ - self.assertEqual( - config.get_config_file( - 'test', folder="foo/bar"), - '/home/%s/.config/leap/foo/bar/test' % - self.get_username()) - - @unittest.skipUnless(_system == "Darwin", "mac only") - def test_mac_get_config_file(self): - """ - config file path where expected? (mac) - """ - self._missing_test_for_plat(do_raise=True) - - @unittest.skipUnless(_system == "Windows", "win only") - def test_win_get_config_file(self): - """ - config file path where expected? - """ - self._missing_test_for_plat(do_raise=True) - - # - # XXX hey, I'm raising exceptions here - # on purpose. just wanted to make sure - # that the skip stuff is doing it right. - # If you're working on win/macos tests, - # feel free to remove tests that you see - # are too redundant. - - @unittest.skipUnless(_system == "Linux", "linux only") - def test_lin_get_config_dir(self): - """ - nice config dir? (linux) - """ - self.assertEqual( - config.get_config_dir(), - '/home/%s/.config/leap' % - self.get_username()) - - @unittest.skipUnless(_system == "Darwin", "mac only") - def test_mac_get_config_dir(self): - """ - nice config dir? (mac) - """ - self._missing_test_for_plat(do_raise=True) - - @unittest.skipUnless(_system == "Windows", "win only") - def test_win_get_config_dir(self): - """ - nice config dir? (win) - """ - self._missing_test_for_plat(do_raise=True) - - # provider paths - - @unittest.skipUnless(_system == "Linux", "linux only") - def test_get_default_provider_path(self): - """ - is default provider path ok? - """ - self.assertEqual( - config.get_default_provider_path(), - '/home/%s/.config/leap/providers/default/' % - self.get_username()) - - # validate ip - - def test_validate_ip(self): - """ - check our ip validation - """ - config.validate_ip('3.3.3.3') - with self.assertRaises(socket.error): - config.validate_ip('255.255.255.256') - with self.assertRaises(socket.error): - config.validate_ip('foobar') - - @unittest.skip - def test_validate_domain(self): - """ - code to be written yet - """ - pass - # build command string # these tests are going to have to check # many combinations. we should inject some @@ -191,15 +96,6 @@ class EIPConfigTest(BaseLeapTest): self.assertEqual(command, 'openvpn') self.assertEqual(args, self.get_expected_openvpn_args()) - # json config - - def test_get_config_json(self): - config_js = config.get_config_json() - self.assertTrue(isinstance(config_js, dict)) - self.assertTrue('transport' in config_js) - self.assertTrue('provider' in config_js) - self.assertEqual(config_js['provider'], "testprovider.org") - if __name__ == "__main__": unittest.main() -- cgit v1.2.3 From 97ea8ee2fa43d345cf3f1013c87569155680625b Mon Sep 17 00:00:00 2001 From: antialias Date: Wed, 22 Aug 2012 14:01:22 -0700 Subject: moved help functions from eip/config.py to base/configuration.py. (cherry picked from get-definition.json branch) solve merge conflict since antialias was working in a version in which baseconfig was still at `configuration` file. Conflicts: src/leap/base/configuration.py --- src/leap/eip/config.py | 89 +++----------------------------------------------- 1 file changed, 5 insertions(+), 84 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py index f38268e2..8d5c19da 100644 --- a/src/leap/eip/config.py +++ b/src/leap/eip/config.py @@ -1,13 +1,16 @@ import ConfigParser -import grp import logging import os -import json import platform import socket from leap.util.fileutil import (which, mkdir_p, check_and_fix_urw_only) +from leap.base.config import (get_default_provider_path, + get_config_file, + get_username, + get_groupname, + validate_ip) from leap.baseapp.permcheck import (is_pkexec_in_system, is_auth_agent_running) from leap.eip import exceptions as eip_exceptions @@ -32,55 +35,6 @@ ca {LEAP_EIP_KEYS} """ -def get_config_dir(): - """ - get the base dir for all leap config - @rparam: config path - @rtype: string - """ - # TODO - # check for $XDG_CONFIG_HOME var? - # get a more sensible path for win/mac - # kclair: opinion? ^^ - return os.path.expanduser( - os.path.join('~', - '.config', - 'leap')) - - -def get_config_file(filename, folder=None): - """ - concatenates the given filename - with leap config dir. - @param filename: name of the file - @type filename: string - @rparam: full path to config file - """ - path = [] - path.append(get_config_dir()) - if folder is not None: - path.append(folder) - path.append(filename) - return os.path.join(*path) - - -def get_default_provider_path(): - default_subpath = os.path.join("providers", - "default") - default_provider_path = get_config_file( - '', - folder=default_subpath) - return default_provider_path - - -def validate_ip(ip_str): - """ - raises exception if the ip_str is - not a valid representation of an ip - """ - socket.inet_aton(ip_str) - - def check_or_create_default_vpnconf(config): """ checks that a vpn config file @@ -158,15 +112,6 @@ def check_or_create_default_vpnconf(config): f.write(ovpn_config) -def get_username(): - return os.getlogin() - - -def get_groupname(): - gid = os.getgroups()[-1] - return grp.getgrgid(gid).gr_name - - def build_ovpn_options(daemon=False): """ build a list of options @@ -412,27 +357,3 @@ def check_vpn_keys(config): check_and_fix_urw_only(keyfile) except OSError: raise eip_exceptions.EIPInitBadKeyFilePermError - - -def get_config_json(config_file=None): - """ - will replace get_config function be developing them - in parralel for branch purposes. - @param: configuration file - @type: file - @rparam: configuration turples - @rtype: dictionary - """ - if not config_file: - fpath = get_config_file('eip.json') - if not os.path.isfile(fpath): - dpath, cfile = os.path.split(fpath) - if not os.path.isdir(dpath): - mkdir_p(dpath) - with open(fpath, 'wb') as configfile: - configfile.flush() - config_file = open(fpath) - - config = json.load(config_file) - - return config -- cgit v1.2.3 From bd154da54eb022d12d225a84cea1053f868eab56 Mon Sep 17 00:00:00 2001 From: kali Date: Fri, 24 Aug 2012 00:09:57 +0900 Subject: fix config imports to make tests pass. we still have to move most of those tests to test_baseconfig --- src/leap/eip/tests/test_config.py | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/tests/test_config.py b/src/leap/eip/tests/test_config.py index b4ad66e5..2b949a19 100644 --- a/src/leap/eip/tests/test_config.py +++ b/src/leap/eip/tests/test_config.py @@ -1,9 +1,7 @@ import ConfigParser import os import platform -import shutil import socket -import tempfile try: import unittest2 as unittest @@ -11,10 +9,17 @@ except ImportError: import unittest from leap.testing.basetest import BaseLeapTest -from leap.eip import config +from leap.base import config as base_config +from leap.eip import config as eip_config _system = platform.system() +# +# XXX we moved a lot of stuff from eip_config +# to base_config. +# We should move most of these tests too. +# + class EIPConfigTest(BaseLeapTest): @@ -31,10 +36,10 @@ class EIPConfigTest(BaseLeapTest): # def get_username(self): - return config.get_username() + return base_config.get_username() def get_groupname(self): - return config.get_groupname() + return base_config.get_groupname() def _missing_test_for_plat(self, do_raise=False): if do_raise: @@ -95,7 +100,7 @@ class EIPConfigTest(BaseLeapTest): config file path where expected? (linux) """ self.assertEqual( - config.get_config_file( + base_config.get_config_file( 'test', folder="foo/bar"), '/home/%s/.config/leap/foo/bar/test' % self.get_username()) @@ -128,7 +133,7 @@ class EIPConfigTest(BaseLeapTest): nice config dir? (linux) """ self.assertEqual( - config.get_config_dir(), + base_config.get_config_dir(), '/home/%s/.config/leap' % self.get_username()) @@ -153,8 +158,9 @@ class EIPConfigTest(BaseLeapTest): """ is default provider path ok? """ + #XXX bad home assumption self.assertEqual( - config.get_default_provider_path(), + base_config.get_default_provider_path(), '/home/%s/.config/leap/providers/default/' % self.get_username()) @@ -164,11 +170,11 @@ class EIPConfigTest(BaseLeapTest): """ check our ip validation """ - config.validate_ip('3.3.3.3') + base_config.validate_ip('3.3.3.3') with self.assertRaises(socket.error): - config.validate_ip('255.255.255.256') + base_config.validate_ip('255.255.255.256') with self.assertRaises(socket.error): - config.validate_ip('foobar') + base_config.validate_ip('foobar') @unittest.skip def test_validate_domain(self): @@ -185,7 +191,7 @@ class EIPConfigTest(BaseLeapTest): def test_build_ovpn_command_empty_config(self): _config = self.get_empty_config() - command, args = config.build_ovpn_command( + command, args = eip_config.build_ovpn_command( _config, do_pkexec_check=False) self.assertEqual(command, 'openvpn') @@ -194,7 +200,7 @@ class EIPConfigTest(BaseLeapTest): # json config def test_get_config_json(self): - config_js = config.get_config_json() + config_js = base_config.get_config_json() self.assertTrue(isinstance(config_js, dict)) self.assertTrue('transport' in config_js) self.assertTrue('provider' in config_js) -- cgit v1.2.3 From 72c64c11e5b77901606a3f732aefcfa64f5d14d7 Mon Sep 17 00:00:00 2001 From: kali Date: Mon, 27 Aug 2012 05:20:44 +0900 Subject: fix expanduser for home in expected openvpn option --- src/leap/eip/tests/test_config.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/tests/test_config.py b/src/leap/eip/tests/test_config.py index 3c5a1cde..b6b06346 100644 --- a/src/leap/eip/tests/test_config.py +++ b/src/leap/eip/tests/test_config.py @@ -1,7 +1,6 @@ import ConfigParser import os import platform -import socket try: import unittest2 as unittest @@ -9,7 +8,6 @@ except ImportError: import unittest from leap.testing.basetest import BaseLeapTest -from leap.base import config as base_config from leap.eip import config as eip_config _system = platform.system() @@ -63,9 +61,8 @@ class EIPConfigTest(BaseLeapTest): args.append('/tmp/.eip.sock') args.append('unix') args.append('--config') - #XXX bad assumption. FIXME: expand $HOME - args.append('/home/%s/.config/leap/providers/default/openvpn.conf' % - username) + args.append(os.path.expanduser( + '~/.config/leap/providers/default/openvpn.conf')) return args # build command string -- cgit v1.2.3 From 2bbe0e0a2d852a1a7261b2fa927eab6e8f41c8c3 Mon Sep 17 00:00:00 2001 From: kali Date: Mon, 27 Aug 2012 05:45:58 +0900 Subject: change default_provider_path to base.constants fix tests by introducing a (dirtish) workaround for check for openvpn keys during vpn connection initialization. noted that eipconnection constructor should be better not having that class of side-effects. --- src/leap/eip/tests/test_config.py | 6 ++++-- src/leap/eip/tests/test_eipconnection.py | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/tests/test_config.py b/src/leap/eip/tests/test_config.py index b6b06346..ed9fe270 100644 --- a/src/leap/eip/tests/test_config.py +++ b/src/leap/eip/tests/test_config.py @@ -7,8 +7,9 @@ try: except ImportError: import unittest -from leap.testing.basetest import BaseLeapTest +from leap.base import constants from leap.eip import config as eip_config +from leap.testing.basetest import BaseLeapTest _system = platform.system() @@ -62,7 +63,8 @@ class EIPConfigTest(BaseLeapTest): args.append('unix') args.append('--config') args.append(os.path.expanduser( - '~/.config/leap/providers/default/openvpn.conf')) + '~/.config/leap/providers/%s/openvpn.conf' + % constants.DEFAULT_TEST_PROVIDER)) return args # build command string diff --git a/src/leap/eip/tests/test_eipconnection.py b/src/leap/eip/tests/test_eipconnection.py index 51772b7c..dee28935 100644 --- a/src/leap/eip/tests/test_eipconnection.py +++ b/src/leap/eip/tests/test_eipconnection.py @@ -1,6 +1,7 @@ import ConfigParser import logging import platform +import os logging.basicConfig() logger = logging.getLogger(name=__name__) @@ -12,6 +13,7 @@ except ImportError: from mock import Mock, patch # MagicMock +from leap.base import constants from leap.eip.eipconnection import EIPConnection from leap.eip.exceptions import ConnectionRefusedError @@ -59,6 +61,10 @@ class EIPConductorTest(unittest.TestCase): "for the running platform: %s" % _system) + def touch(self, filepath): + with open(filepath, 'w') as fp: + fp.write('') + # # tests # @@ -75,6 +81,19 @@ class EIPConductorTest(unittest.TestCase): """ default attrs as expected """ + # XXX there's a conceptual/design + # mistake here. + # If we're testing just attrs after init, + # init shold not be doing so much side effects. + + # for instance: + # We have to TOUCH a keys file because + # we're triggerig the key checks FROM + # the constructo. me not like that, + # key checker should better be called explicitelly. + self.touch(os.path.expanduser( + '~/.config/leap/providers/%s/openvpn.keys' + % constants.DEFAULT_TEST_PROVIDER)) con = self.con self.assertEqual(con.autostart, True) self.assertEqual(con.missing_pkexec, False) -- cgit v1.2.3 From 10292cac27bc2f10e2b5768c84091a73105bc495 Mon Sep 17 00:00:00 2001 From: kali Date: Mon, 27 Aug 2012 06:37:10 +0900 Subject: make eipconductor test use BaseLeapTest --- src/leap/eip/tests/test_eipconnection.py | 50 ++++++++++++++------------------ 1 file changed, 21 insertions(+), 29 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/tests/test_eipconnection.py b/src/leap/eip/tests/test_eipconnection.py index dee28935..7d8acad6 100644 --- a/src/leap/eip/tests/test_eipconnection.py +++ b/src/leap/eip/tests/test_eipconnection.py @@ -16,6 +16,7 @@ from mock import Mock, patch # MagicMock from leap.base import constants from leap.eip.eipconnection import EIPConnection from leap.eip.exceptions import ConnectionRefusedError +from leap.testing.basetest import BaseLeapTest _system = platform.system() @@ -36,11 +37,30 @@ class MockedEIPConnection(EIPConnection): self.args = [1, 2, 3] -class EIPConductorTest(unittest.TestCase): +class EIPConductorTest(BaseLeapTest): __name__ = "eip_conductor_tests" def setUp(self): + # XXX there's a conceptual/design + # mistake here. + # If we're testing just attrs after init, + # init shold not be doing so much side effects. + + # for instance: + # We have to TOUCH a keys file because + # we're triggerig the key checks FROM + # the constructo. me not like that, + # key checker should better be called explicitelly. + filepath = os.path.expanduser( + '~/.config/leap/providers/%s/openvpn.keys' + % constants.DEFAULT_TEST_PROVIDER) + self.touch(filepath) + self.chmod600(filepath) + + # we init the manager with only + # some methods mocked + self.manager = Mock( name="openvpnmanager_mock") @@ -50,21 +70,6 @@ class EIPConductorTest(unittest.TestCase): def tearDown(self): del self.con - # - # helpers - # - - def _missing_test_for_plat(self, do_raise=False): - if do_raise: - raise NotImplementedError( - "This test is not implemented " - "for the running platform: %s" % - _system) - - def touch(self, filepath): - with open(filepath, 'w') as fp: - fp.write('') - # # tests # @@ -81,19 +86,6 @@ class EIPConductorTest(unittest.TestCase): """ default attrs as expected """ - # XXX there's a conceptual/design - # mistake here. - # If we're testing just attrs after init, - # init shold not be doing so much side effects. - - # for instance: - # We have to TOUCH a keys file because - # we're triggerig the key checks FROM - # the constructo. me not like that, - # key checker should better be called explicitelly. - self.touch(os.path.expanduser( - '~/.config/leap/providers/%s/openvpn.keys' - % constants.DEFAULT_TEST_PROVIDER)) con = self.con self.assertEqual(con.autostart, True) self.assertEqual(con.missing_pkexec, False) -- cgit v1.2.3 From e896190d159342d9819f0ad6f11fe01deb8eb9e5 Mon Sep 17 00:00:00 2001 From: kali Date: Mon, 27 Aug 2012 07:35:00 +0900 Subject: add stubs for eip.checks will handle pre-init sanity checks for eip connection. some of this will actually end in more general leap-checks, but let's keep it alltogether by now. --- src/leap/eip/checks.py | 50 ++++++++++++++++++++++++++++++++++++ src/leap/eip/tests/test_checks.py | 54 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+) create mode 100644 src/leap/eip/checks.py create mode 100644 src/leap/eip/tests/test_checks.py (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py new file mode 100644 index 00000000..bb588cf7 --- /dev/null +++ b/src/leap/eip/checks.py @@ -0,0 +1,50 @@ +import logging +logger = logging.getLogger(name=__name__) + + +class EIPChecker(object): + """ + Executes all tests needed + to ensure a EIPConnection + can be sucessful + """ + def __init__(self): + pass + + def do_all_checks(self, checker=None): + """ + just runs all tests in a row. + will raise if some error encounter. + catching those exceptions is not + our responsibility at this moment + """ + if not checker: + checker = self + + # let's call all tests + # needed for a sane eip session. + + checker.dump_default_eipconfig() + checker.check_is_there_default_provider() + checker.fetch_definition() + checker.fetch_eip_config() + checker.check_complete_eip_config() + checker.ping_gateway() + + def dump_default_eipconfig(self): + raise NotImplementedError + + def check_is_there_default_provider(self): + raise NotImplementedError + + def fetch_definition(self): + raise NotImplementedError + + def fetch_eip_config(self): + raise NotImplementedError + + def check_complete_eip_config(self): + raise NotImplementedError + + def ping_gateway(self): + raise NotImplementedError diff --git a/src/leap/eip/tests/test_checks.py b/src/leap/eip/tests/test_checks.py new file mode 100644 index 00000000..53f8dc6c --- /dev/null +++ b/src/leap/eip/tests/test_checks.py @@ -0,0 +1,54 @@ +try: + import unittest2 as unittest +except ImportError: + import unittest + +from mock import Mock + +from leap.eip import checks as eip_checks +from leap.testing.basetest import BaseLeapTest + + +class EIPCheckTest(BaseLeapTest): + + __name__ = "eip_check_tests" + + def setUp(self): + pass + + def tearDown(self): + pass + + def test_checker_should_implement_check_methods(self): + checker = eip_checks.EIPChecker() + + self.assertTrue(hasattr(checker, "dump_default_eipconfig"), + "missing meth") + self.assertTrue(hasattr(checker, "check_is_there_default_provider"), + "missing meth") + self.assertTrue(hasattr(checker, "fetch_definition"), "missing meth") + self.assertTrue(hasattr(checker, "fetch_eip_config"), "missing meth") + self.assertTrue(hasattr(checker, "check_complete_eip_config"), + "missing meth") + self.assertTrue(hasattr(checker, "ping_gateway"), "missing meth") + + def test_checker_should_actually_call_all_tests(self): + checker = eip_checks.EIPChecker() + + mc = Mock() + checker.do_all_checks(checker=mc) + self.assertTrue(mc.dump_default_eipconfig.called, "not called") + self.assertTrue(mc.check_is_there_default_provider.called, + "not called") + self.assertTrue(mc.fetch_definition.called, + "not called") + self.assertTrue(mc.fetch_eip_config.called, + "not called") + self.assertTrue(mc.check_complete_eip_config.called, + "not called") + self.assertTrue(mc.ping_gateway.called, + "not called") + + +if __name__ == "__main__": + unittest.main() -- cgit v1.2.3 From 4a46723219e5284bec21b9dccd6589a670babc63 Mon Sep 17 00:00:00 2001 From: kali Date: Mon, 27 Aug 2012 23:21:43 +0900 Subject: add test_dump_default_eipconfig to eip.test_checks plus a little bit of cleaning around (created constants file). added some notes about inminent deprecation *work in progress* --- src/leap/eip/checks.py | 49 ++++++++++++++++++++++++++++++++------- src/leap/eip/config.py | 37 ++++++++++++++++------------- src/leap/eip/constants.py | 20 ++++++++++++++++ src/leap/eip/tests/test_checks.py | 35 +++++++++++++++++++++++----- src/leap/eip/tests/test_config.py | 4 ++++ 5 files changed, 114 insertions(+), 31 deletions(-) create mode 100644 src/leap/eip/constants.py (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index bb588cf7..1726e73a 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -1,20 +1,26 @@ import logging logger = logging.getLogger(name=__name__) +import os + +from leap.base import config as baseconfig +from leap.eip import config as eipconfig +from leap.eip import constants as eipconstants class EIPChecker(object): """ - Executes all tests needed + Several tests needed to ensure a EIPConnection can be sucessful """ - def __init__(self): - pass + #def __init__(self): + ## no init needed atm.. + #pass - def do_all_checks(self, checker=None): + def run_all(self, checker=None): """ - just runs all tests in a row. - will raise if some error encounter. + runs all checks in a row. + will raise if some error encountered. catching those exceptions is not our responsibility at this moment """ @@ -24,20 +30,32 @@ class EIPChecker(object): # let's call all tests # needed for a sane eip session. - checker.dump_default_eipconfig() + checker.check_default_eipconfig() checker.check_is_there_default_provider() checker.fetch_definition() checker.fetch_eip_config() checker.check_complete_eip_config() checker.ping_gateway() - def dump_default_eipconfig(self): - raise NotImplementedError + # public checks + + def check_default_eipconfig(self): + """ + checks if default eipconfig exists, + and dumps a default file if not + """ + # it *really* does not make sense to + # dump it right now, we can get an in-memory + # config object and dump it to disk in a + # later moment + if not self._is_there_default_eipconfig(): + self._dump_default_eipconfig() def check_is_there_default_provider(self): raise NotImplementedError def fetch_definition(self): + # check_and_get_definition_file raise NotImplementedError def fetch_eip_config(self): @@ -48,3 +66,16 @@ class EIPChecker(object): def ping_gateway(self): raise NotImplementedError + + # private helpers + + def _get_default_eipconfig_path(self): + return baseconfig.get_config_file(eipconstants.EIP_CONFIG) + + def _is_there_default_eipconfig(self): + return os.path.isfile( + self._get_default_eipconfig_path()) + + def _dump_default_eipconfig(self): + eipconfig.dump_default_eipconfig( + self._get_default_eipconfig_path()) diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py index 8d5c19da..2694ca61 100644 --- a/src/leap/eip/config.py +++ b/src/leap/eip/config.py @@ -1,4 +1,5 @@ -import ConfigParser +import ConfigParser # to be deprecated +import json import logging import os import platform @@ -6,6 +7,8 @@ import socket from leap.util.fileutil import (which, mkdir_p, check_and_fix_urw_only) + +# from leap.base import config as baseconfig from leap.base.config import (get_default_provider_path, get_config_file, get_username, @@ -14,6 +17,7 @@ from leap.base.config import (get_default_provider_path, from leap.baseapp.permcheck import (is_pkexec_in_system, is_auth_agent_running) from leap.eip import exceptions as eip_exceptions +from leap.eip import constants as eipconstants logger = logging.getLogger(name=__name__) logger.setLevel('DEBUG') @@ -276,6 +280,8 @@ def get_sensible_defaults(): return defaults +# XXX to be deprecated. see dump_default_eipconfig +# and the new JSONConfig classes. def get_config(config_file=None): """ temporary method for getting configs, @@ -286,10 +292,6 @@ def get_config(config_file=None): @rtype: ConfigParser instance @rparam: a config object """ - # TODO - # - refactor out common things and get - # them to util/ or baseapp/ - defaults = get_sensible_defaults() config = ConfigParser.ConfigParser(defaults) @@ -302,21 +304,24 @@ def get_config(config_file=None): with open(fpath, 'wb') as configfile: config.write(configfile) config_file = open(fpath) - - #TODO - # - convert config_file to list; - # look in places like /etc/leap/eip.cfg - # for global settings. - # - raise warnings/error if bad options. - - # at this point, the file should exist. - # errors would have been raised above. - config.readfp(config_file) - return config +def dump_default_eipconfig(filepath): + """ + writes a sample eip config + in the given location + """ + # XXX TODO: + # use EIPConfigSpec istead + folder, filename = os.path.split(filepath) + if not os.path.isdir(folder): + mkdir_p(folder) + with open(filepath, 'w') as fp: + json.dump(eipconstants.EIP_SAMPLE_JSON, fp) + + def check_vpn_keys(config): """ performs an existance and permission check diff --git a/src/leap/eip/constants.py b/src/leap/eip/constants.py new file mode 100644 index 00000000..7124ca57 --- /dev/null +++ b/src/leap/eip/constants.py @@ -0,0 +1,20 @@ +EIP_CONFIG = "eip.json" + +EIP_SAMPLE_JSON = { + "provider": "testprovider.example.org", + "transport": "openvpn", + "openvpn_protocol": "tcp", + "openvpn_port": "80", + "openvpn_ca_certificate": "~/.config/leap/providers/" + "testprovider.example.org/" + "keys/ca/testprovider-ca-cert-" + "2013-01-01.pem", + "openvpn_client_certificate": "~/.config/leap/providers/" + "testprovider.example.org/" + "keys/client/openvpn-2012-09-31.pem", + "connect_on_login": True, + "block_cleartext_traffic": True, + "primary_gateway": "usa_west", + "secondary_gateway": "france", + "management_password": "oph7Que1othahwiech6J" +} diff --git a/src/leap/eip/tests/test_checks.py b/src/leap/eip/tests/test_checks.py index 53f8dc6c..ea2b3d15 100644 --- a/src/leap/eip/tests/test_checks.py +++ b/src/leap/eip/tests/test_checks.py @@ -1,11 +1,15 @@ +import json try: import unittest2 as unittest except ImportError: import unittest +import os from mock import Mock -from leap.eip import checks as eip_checks +from leap.base import config as baseconfig +from leap.eip import checks as eipchecks +from leap.eip import constants as eipconstants from leap.testing.basetest import BaseLeapTest @@ -19,10 +23,12 @@ class EIPCheckTest(BaseLeapTest): def tearDown(self): pass + # test methods are there, and can be called from run_all + def test_checker_should_implement_check_methods(self): - checker = eip_checks.EIPChecker() + checker = eipchecks.EIPChecker() - self.assertTrue(hasattr(checker, "dump_default_eipconfig"), + self.assertTrue(hasattr(checker, "check_default_eipconfig"), "missing meth") self.assertTrue(hasattr(checker, "check_is_there_default_provider"), "missing meth") @@ -33,11 +39,11 @@ class EIPCheckTest(BaseLeapTest): self.assertTrue(hasattr(checker, "ping_gateway"), "missing meth") def test_checker_should_actually_call_all_tests(self): - checker = eip_checks.EIPChecker() + checker = eipchecks.EIPChecker() mc = Mock() - checker.do_all_checks(checker=mc) - self.assertTrue(mc.dump_default_eipconfig.called, "not called") + checker.run_all(checker=mc) + self.assertTrue(mc.check_default_eipconfig.called, "not called") self.assertTrue(mc.check_is_there_default_provider.called, "not called") self.assertTrue(mc.fetch_definition.called, @@ -49,6 +55,23 @@ class EIPCheckTest(BaseLeapTest): self.assertTrue(mc.ping_gateway.called, "not called") + # test individual check methods + + def test_dump_default_eipconfig(self): + checker = eipchecks.EIPChecker() + # no eip config (empty home) + eipconfig = baseconfig.get_config_file(eipconstants.EIP_CONFIG) + self.assertFalse(os.path.isfile(eipconfig)) + checker.check_default_eipconfig() + # we've written one, so it should be there. + self.assertTrue(os.path.isfile(eipconfig)) + with open(eipconfig, 'rb') as fp: + deserialized = json.load(fp) + self.assertEqual(deserialized, + eipconstants.EIP_SAMPLE_JSON) + # TODO: when new JSONConfig class is in place, we shold + # run validation methods. + if __name__ == "__main__": unittest.main() diff --git a/src/leap/eip/tests/test_config.py b/src/leap/eip/tests/test_config.py index ed9fe270..fac4729d 100644 --- a/src/leap/eip/tests/test_config.py +++ b/src/leap/eip/tests/test_config.py @@ -81,6 +81,10 @@ class EIPConfigTest(BaseLeapTest): self.assertEqual(command, 'openvpn') self.assertEqual(args, self.get_expected_openvpn_args()) + # XXX TODO: + # - should use touch_exec to plant an "executabe" in the path + # - should check that "which" for openvpn returns what's expected. + if __name__ == "__main__": unittest.main() -- cgit v1.2.3 From 568d52ccf33e6d7683f36f5fe2e3c32b47892216 Mon Sep 17 00:00:00 2001 From: kali Date: Tue, 28 Aug 2012 05:30:38 +0900 Subject: eipchecker.fetch definition and tests deprecated base:test_config.test_complete_file (dup functionality) --- src/leap/eip/checks.py | 93 +++++++++++++++++++++++++++++++++++---- src/leap/eip/exceptions.py | 4 ++ src/leap/eip/tests/test_checks.py | 43 +++++++++++++++++- 3 files changed, 129 insertions(+), 11 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index 1726e73a..dbb7d524 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -1,10 +1,17 @@ +import json import logging -logger = logging.getLogger(name=__name__) import os +logger = logging.getLogger(name=__name__) + +import requests + from leap.base import config as baseconfig +from leap.base import constants as baseconstants from leap.eip import config as eipconfig from leap.eip import constants as eipconstants +from leap.eip import exceptions as eipexceptions +from leap.util.fileutil import mkdir_p class EIPChecker(object): @@ -12,10 +19,17 @@ class EIPChecker(object): Several tests needed to ensure a EIPConnection can be sucessful + use run_all to run all checks. """ - #def __init__(self): - ## no init needed atm.. - #pass + + def __init__(self, fetcher=requests): + """ + we do not want to accept too many + argument on init. we want tests + to be explicitely run. + """ + self.config = None + self.fetcher = fetcher def run_all(self, checker=None): """ @@ -30,7 +44,11 @@ class EIPChecker(object): # let's call all tests # needed for a sane eip session. + # TODO: get rid of check_default. + # check_complete should + # be enough. checker.check_default_eipconfig() + checker.check_is_there_default_provider() checker.fetch_definition() checker.fetch_eip_config() @@ -44,6 +62,10 @@ class EIPChecker(object): checks if default eipconfig exists, and dumps a default file if not """ + # XXX ONLY a transient check + # because some old function still checks + # for eip config at the beginning. + # it *really* does not make sense to # dump it right now, we can get an in-memory # config object and dump it to disk in a @@ -51,12 +73,55 @@ class EIPChecker(object): if not self._is_there_default_eipconfig(): self._dump_default_eipconfig() - def check_is_there_default_provider(self): - raise NotImplementedError - - def fetch_definition(self): + def check_is_there_default_provider(self, config=None): + """ + raises EIPMissingDefaultProvider if no + default provider found on eip config. + This is catched by ui and runs FirstRunWizard (MVS+) + """ + # if config is not None: + # config = config + # else: self.get_eipconfig + # XXX parse EIPConfig. + # XXX get default_provider. + eipcfg = self._get_default_eipconfig_path() + with open(eipcfg, 'r') as fp: + config = json.load(fp) + provider = config.get('provider', None) + if provider is None: + raise eipexceptions.EIPMissingDefaultProvider + if config: + self.config = config + return True + + def fetch_definition(self, skip_download=False, + config=None, uri=None): # check_and_get_definition_file - raise NotImplementedError + if skip_download: + return True + if config is None: + config = self.config + if uri is None: + if config: + domain = config.get('provider', None) + else: + domain = None + uri = self._get_provider_definition_uri( + domain=domain) + + # XXX move to JSONConfig Fetcher + request = self.fetcher.get(uri) + request.raise_for_status() + + definition_file = os.path.join( + baseconfig.get_default_provider_path(), + baseconstants.DEFINITION_EXPECTED_PATH) + + folder, filename = os.path.split(definition_file) + if not os.path.isdir(folder): + mkdir_p(folder) + with open(definition_file, 'wb') as f: + f.write(json.dumps(request.json, indent=4)) def fetch_eip_config(self): raise NotImplementedError @@ -67,7 +132,9 @@ class EIPChecker(object): def ping_gateway(self): raise NotImplementedError + # # private helpers + # def _get_default_eipconfig_path(self): return baseconfig.get_config_file(eipconstants.EIP_CONFIG) @@ -79,3 +146,11 @@ class EIPChecker(object): def _dump_default_eipconfig(self): eipconfig.dump_default_eipconfig( self._get_default_eipconfig_path()) + + def _get_provider_definition_uri(self, domain=None, path=None): + if domain is None: + domain = baseconstants.DEFAULT_TEST_PROVIDER + if path is None: + path = baseconstants.DEFINITION_EXPECTED_PATH + return "https://%s/%s" % (domain, path) + diff --git a/src/leap/eip/exceptions.py b/src/leap/eip/exceptions.py index 3719c605..800c7f0e 100644 --- a/src/leap/eip/exceptions.py +++ b/src/leap/eip/exceptions.py @@ -61,3 +61,7 @@ class EIPInitNoKeyFileError(Exception): class EIPInitBadKeyFilePermError(Exception): pass + + +class EIPMissingDefaultProvider(Exception): + pass diff --git a/src/leap/eip/tests/test_checks.py b/src/leap/eip/tests/test_checks.py index ea2b3d15..8c022907 100644 --- a/src/leap/eip/tests/test_checks.py +++ b/src/leap/eip/tests/test_checks.py @@ -1,3 +1,4 @@ +import copy import json try: import unittest2 as unittest @@ -5,11 +6,15 @@ except ImportError: import unittest import os -from mock import Mock +from mock import patch, Mock + +import requests from leap.base import config as baseconfig +from leap.base.constants import DEFAULT_PROVIDER_DEFINITION from leap.eip import checks as eipchecks from leap.eip import constants as eipconstants +from leap.eip import exceptions as eipexceptions from leap.testing.basetest import BaseLeapTest @@ -57,7 +62,7 @@ class EIPCheckTest(BaseLeapTest): # test individual check methods - def test_dump_default_eipconfig(self): + def test_check_default_eipconfig(self): checker = eipchecks.EIPChecker() # no eip config (empty home) eipconfig = baseconfig.get_config_file(eipconstants.EIP_CONFIG) @@ -72,6 +77,40 @@ class EIPCheckTest(BaseLeapTest): # TODO: when new JSONConfig class is in place, we shold # run validation methods. + def test_check_is_there_default_provider(self): + checker = eipchecks.EIPChecker() + # we do dump a sample eip config, but lacking a + # default provider entry. + # This error will be possible catched in a different + # place, when JSONConfig does validation of required fields. + + sampleconfig = copy.copy(eipconstants.EIP_SAMPLE_JSON) + # blank out default_provider + sampleconfig['provider'] = None + eipcfg_path = checker._get_default_eipconfig_path() + with open(eipcfg_path, 'w') as fp: + json.dump(sampleconfig, fp) + with self.assertRaises(eipexceptions.EIPMissingDefaultProvider): + checker.check_is_there_default_provider() + + sampleconfig = eipconstants.EIP_SAMPLE_JSON + eipcfg_path = checker._get_default_eipconfig_path() + with open(eipcfg_path, 'w') as fp: + json.dump(sampleconfig, fp) + self.assertTrue(checker.check_is_there_default_provider()) + + def test_fetch_definition(self): + with patch.object(requests, "get") as mocked_get: + mocked_get.return_value.status_code = 200 + mocked_get.return_value.json = DEFAULT_PROVIDER_DEFINITION + checker = eipchecks.EIPChecker(fetcher=requests) + sampleconfig = eipconstants.EIP_SAMPLE_JSON + checker.fetch_definition(config=sampleconfig) + + # XXX TODO check for ConnectionError, HTTPError, InvalidUrl + # (and proper EIPExceptions are raised). + + # Look at base.test_config. if __name__ == "__main__": unittest.main() -- cgit v1.2.3 From c469e396bde67db15e486a320b254de0fa6f69df Mon Sep 17 00:00:00 2001 From: kali Date: Tue, 28 Aug 2012 06:08:58 +0900 Subject: checki complete eip_config tests. completed first version of EIPChecks --- src/leap/eip/checks.py | 59 +++++++++++++++++++++++++++++++++++---- src/leap/eip/constants.py | 28 +++++++++++++++++++ src/leap/eip/exceptions.py | 4 +++ src/leap/eip/tests/test_checks.py | 41 +++++++++++++++++++++++---- 4 files changed, 121 insertions(+), 11 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index dbb7d524..84a2ba6b 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -53,7 +53,7 @@ class EIPChecker(object): checker.fetch_definition() checker.fetch_eip_config() checker.check_complete_eip_config() - checker.ping_gateway() + #checker.ping_gateway() # public checks @@ -97,6 +97,13 @@ class EIPChecker(object): def fetch_definition(self, skip_download=False, config=None, uri=None): # check_and_get_definition_file + """ + fetches a definition file from server + """ + # TODO: + # - Implement diff + # - overwrite if different. + if skip_download: return True if config is None: @@ -123,11 +130,47 @@ class EIPChecker(object): with open(definition_file, 'wb') as f: f.write(json.dumps(request.json, indent=4)) - def fetch_eip_config(self): - raise NotImplementedError + def fetch_eip_config(self, skip_download=False, + config=None, uri=None): + if skip_download: + return True + if config is None: + config = self.config + if uri is None: + if config: + domain = config.get('provider', None) + else: + domain = None + uri = self._get_eip_service_uri( + domain=domain) - def check_complete_eip_config(self): - raise NotImplementedError + # XXX move to JSONConfig Fetcher + request = self.fetcher.get(uri) + request.raise_for_status() + + definition_file = os.path.join( + baseconfig.get_default_provider_path(), + eipconstants.EIP_SERVICE_EXPECTED_PATH) + + folder, filename = os.path.split(definition_file) + if not os.path.isdir(folder): + mkdir_p(folder) + with open(definition_file, 'wb') as f: + f.write(json.dumps(request.json, indent=4)) + + def check_complete_eip_config(self, config=None): + if config is None: + config = self.config + try: + 'trying assertions' + assert 'provider' in config + assert config['provider'] is not None + except AssertionError: + raise eipexceptions.EIPConfigurationError + + # XXX TODO: + # We should WRITE eip config if missing or + # incomplete at this point def ping_gateway(self): raise NotImplementedError @@ -154,3 +197,9 @@ class EIPChecker(object): path = baseconstants.DEFINITION_EXPECTED_PATH return "https://%s/%s" % (domain, path) + def _get_eip_service_uri(self, domain=None, path=None): + if domain is None: + domain = baseconstants.DEFAULT_TEST_PROVIDER + if path is None: + path = eipconstants.EIP_SERVICE_EXPECTED_PATH + return "https://%s/%s" % (domain, path) diff --git a/src/leap/eip/constants.py b/src/leap/eip/constants.py index 7124ca57..6161d744 100644 --- a/src/leap/eip/constants.py +++ b/src/leap/eip/constants.py @@ -18,3 +18,31 @@ EIP_SAMPLE_JSON = { "secondary_gateway": "france", "management_password": "oph7Que1othahwiech6J" } + +EIP_SERVICE_EXPECTED_PATH = "eip-service.json" + +EIP_SAMPLE_SERVICE = { + "serial": 1, + "version": "0.1.0", + "capabilities": { + "transport": ["openvpn"], + "ports": ["80", "53"], + "protocols": ["udp", "tcp"], + "static_ips": True, + "adblock": True + }, + "gateways": [ + {"country_code": "us", + "label": {"en":"west"}, + "capabilities": {}, + "hosts": ["1.2.3.4", "1.2.3.5"]}, + {"country_code": "us", + "label": {"en":"east"}, + "capabilities": {}, + "hosts": ["1.2.3.4", "1.2.3.5"]}, + {"country_code": "fr", + "label": {}, + "capabilities": {}, + "hosts": ["1.2.3.4", "1.2.3.5"]} + ] +} diff --git a/src/leap/eip/exceptions.py b/src/leap/eip/exceptions.py index 800c7f0e..19a0e707 100644 --- a/src/leap/eip/exceptions.py +++ b/src/leap/eip/exceptions.py @@ -65,3 +65,7 @@ class EIPInitBadKeyFilePermError(Exception): class EIPMissingDefaultProvider(Exception): pass + + +class EIPConfigurationError(Exception): + pass diff --git a/src/leap/eip/tests/test_checks.py b/src/leap/eip/tests/test_checks.py index 8c022907..83561833 100644 --- a/src/leap/eip/tests/test_checks.py +++ b/src/leap/eip/tests/test_checks.py @@ -11,7 +11,8 @@ from mock import patch, Mock import requests from leap.base import config as baseconfig -from leap.base.constants import DEFAULT_PROVIDER_DEFINITION +from leap.base.constants import (DEFAULT_PROVIDER_DEFINITION, + DEFINITION_EXPECTED_PATH) from leap.eip import checks as eipchecks from leap.eip import constants as eipconstants from leap.eip import exceptions as eipexceptions @@ -57,8 +58,8 @@ class EIPCheckTest(BaseLeapTest): "not called") self.assertTrue(mc.check_complete_eip_config.called, "not called") - self.assertTrue(mc.ping_gateway.called, - "not called") + #self.assertTrue(mc.ping_gateway.called, + #"not called") # test individual check methods @@ -107,10 +108,38 @@ class EIPCheckTest(BaseLeapTest): sampleconfig = eipconstants.EIP_SAMPLE_JSON checker.fetch_definition(config=sampleconfig) - # XXX TODO check for ConnectionError, HTTPError, InvalidUrl - # (and proper EIPExceptions are raised). + fn = os.path.join(baseconfig.get_default_provider_path(), + DEFINITION_EXPECTED_PATH) + with open(fn, 'r') as fp: + deserialized = json.load(fp) + self.assertEqual(DEFAULT_PROVIDER_DEFINITION, deserialized) + + # XXX TODO check for ConnectionError, HTTPError, InvalidUrl + # (and proper EIPExceptions are raised). + # Look at base.test_config. - # Look at base.test_config. + def test_fetch_eip_config(self): + with patch.object(requests, "get") as mocked_get: + mocked_get.return_value.status_code = 200 + mocked_get.return_value.json = eipconstants.EIP_SAMPLE_SERVICE + checker = eipchecks.EIPChecker(fetcher=requests) + sampleconfig = eipconstants.EIP_SAMPLE_JSON + checker.fetch_definition(config=sampleconfig) + + def test_check_complete_eip_config(self): + checker = eipchecks.EIPChecker() + with self.assertRaises(eipexceptions.EIPConfigurationError): + sampleconfig = copy.copy(eipconstants.EIP_SAMPLE_JSON) + sampleconfig['provider'] = None + checker.check_complete_eip_config(config=sampleconfig) + with self.assertRaises(eipexceptions.EIPConfigurationError): + sampleconfig = copy.copy(eipconstants.EIP_SAMPLE_JSON) + del sampleconfig['provider'] + checker.check_complete_eip_config(config=sampleconfig) + + # normal case + sampleconfig = copy.copy(eipconstants.EIP_SAMPLE_JSON) + checker.check_complete_eip_config(config=sampleconfig) if __name__ == "__main__": unittest.main() -- cgit v1.2.3 From 5ddb2ef5a803cc6c01a90b4c3b33c90a51d2666e Mon Sep 17 00:00:00 2001 From: kali Date: Tue, 28 Aug 2012 22:35:23 +0900 Subject: add todo --- src/leap/eip/checks.py | 1 + 1 file changed, 1 insertion(+) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index 84a2ba6b..7ef80cae 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -165,6 +165,7 @@ class EIPChecker(object): 'trying assertions' assert 'provider' in config assert config['provider'] is not None + # XXX assert there is gateway !! except AssertionError: raise eipexceptions.EIPConfigurationError -- cgit v1.2.3 From 06883461f2daa616b2e3c842f53d9422703cd9c7 Mon Sep 17 00:00:00 2001 From: kali Date: Tue, 28 Aug 2012 23:08:39 +0900 Subject: eip_checks called from main app. removed "configuration" object. checks are called from conductor. --- src/leap/eip/checks.py | 12 +++++++++--- src/leap/eip/eipconnection.py | 24 +++++++++--------------- src/leap/eip/openvpnconnection.py | 3 +++ 3 files changed, 21 insertions(+), 18 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index 7ef80cae..794e69e1 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -2,7 +2,9 @@ import json import logging import os +logging.basicConfig() logger = logging.getLogger(name=__name__) +logger.setLevel(logging.DEBUG) import requests @@ -31,7 +33,7 @@ class EIPChecker(object): self.config = None self.fetcher = fetcher - def run_all(self, checker=None): + def run_all(self, checker=None, skip_download=False): """ runs all checks in a row. will raise if some error encountered. @@ -50,8 +52,8 @@ class EIPChecker(object): checker.check_default_eipconfig() checker.check_is_there_default_provider() - checker.fetch_definition() - checker.fetch_eip_config() + checker.fetch_definition(skip_download=skip_download) + checker.fetch_eip_config(skip_download=skip_download) checker.check_complete_eip_config() #checker.ping_gateway() @@ -70,6 +72,7 @@ class EIPChecker(object): # dump it right now, we can get an in-memory # config object and dump it to disk in a # later moment + logger.debug('checking default eip config') if not self._is_there_default_eipconfig(): self._dump_default_eipconfig() @@ -84,6 +87,7 @@ class EIPChecker(object): # else: self.get_eipconfig # XXX parse EIPConfig. # XXX get default_provider. + logger.debug('checking default provider') eipcfg = self._get_default_eipconfig_path() with open(eipcfg, 'r') as fp: config = json.load(fp) @@ -103,8 +107,10 @@ class EIPChecker(object): # TODO: # - Implement diff # - overwrite if different. + logger.debug('fetching definition') if skip_download: + logger.debug('(fetching def skipped)') return True if config is None: config = self.config diff --git a/src/leap/eip/eipconnection.py b/src/leap/eip/eipconnection.py index 2dfc1503..aea560c9 100644 --- a/src/leap/eip/eipconnection.py +++ b/src/leap/eip/eipconnection.py @@ -8,7 +8,7 @@ logging.basicConfig() logger = logging.getLogger(name=__name__) logger.setLevel(logging.DEBUG) -from leap.base.connection import ConnectionError +from leap.eip.checks import EIPChecker from leap.eip import exceptions as eip_exceptions from leap.eip.openvpnconnection import OpenVPNConnection @@ -22,17 +22,23 @@ class EIPConnection(OpenVPNConnection): Status updates (connected, bandwidth, etc) are signaled to the GUI. """ - def __init__(self, *args, **kwargs): + def __init__(self, checker=EIPChecker, *args, **kwargs): self.settingsfile = kwargs.get('settingsfile', None) self.logfile = kwargs.get('logfile', None) self.error_queue = [] - #self.desired_con_state = None # not in use status_signals = kwargs.pop('status_signals', None) self.status = EIPConnectionStatus(callbacks=status_signals) + self.checker = checker() super(EIPConnection, self).__init__(*args, **kwargs) + def run_checks(self, skip_download=False): + """ + run all eip checks previous to attempting a connection + """ + self.checker.run_all(skip_download=skip_download) + def connect(self): """ entry point for connection process @@ -128,10 +134,6 @@ class EIPConnection(OpenVPNConnection): unicode(except_msg)) return conn_result -"""generic watcher object that keeps track of connection status""" -# This should be deprecated in favor of daemon mode + management -# interface. But we can leave it here for debug purposes. - class EIPConnectionStatus(object): """ @@ -272,11 +274,3 @@ class EIPConnectionStatus(object): for cb in self.callbacks: if callable(cb): cb(self) - - -# XXX move to exceptions -class EIPClientError(ConnectionError): - """ - base EIPClient Exception - """ - pass diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index 3972b617..5f67d27a 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -59,11 +59,14 @@ to be triggered for each one of them. self.port = None self.proto = None + # XXX move all error messages + # into a more encapsulated object. self.missing_pkexec = False self.missing_auth_agent = False self.bad_keyfile_perms = False self.missing_vpn_keyfile = False self.missing_provider = False + self.missing_definition = False self.bad_provider = False #XXX workaround for signaling -- cgit v1.2.3 From 7a8f4db1a4743582c34a52ab448eece0e7689bc8 Mon Sep 17 00:00:00 2001 From: kali Date: Tue, 28 Aug 2012 23:36:39 +0900 Subject: test for eip_config_checker called from eip_connection run_checks method also: - changed name EIPChecker -> EipConfigChecker - Added class documentation --- src/leap/eip/checks.py | 34 ++++++++++++++++++++++++-------- src/leap/eip/eipconnection.py | 11 ++++++----- src/leap/eip/tests/test_checks.py | 14 ++++++------- src/leap/eip/tests/test_eipconnection.py | 30 +++++++++++++++------------- 4 files changed, 55 insertions(+), 34 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index 794e69e1..27320b1f 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -15,21 +15,40 @@ from leap.eip import constants as eipconstants from leap.eip import exceptions as eipexceptions from leap.util.fileutil import mkdir_p +""" +EIPConfigChecker +---------- +this is the first of 3 consecutive checks that we're implementing. -class EIPChecker(object): +It is used from the eip conductor (a instance of EIPConnection that is +managed from the QtApp), running run_all method before trying to call +`connect` or any other of the state switching methods. + +It checks that the needed files are provided or can be discovered over the +net. Much of these tests are not specific to EIP module, and can be splitted +into base.tests to be invoked by the base leap init routines. +However, I'm testing them alltogether for the sake of having the whole unit +reachable and testable as a whole. + +Other related checkers - not implemented yet -: +* LeapNetworkChecker +* ProviderCertChecker +""" + + +class EIPConfigChecker(object): """ Several tests needed to ensure a EIPConnection - can be sucessful + can be sucessfully established. use run_all to run all checks. """ def __init__(self, fetcher=requests): - """ - we do not want to accept too many - argument on init. we want tests - to be explicitely run. - """ + # we do not want to accept too many + # argument on init. + # we want tests + # to be explicitely run. self.config = None self.fetcher = fetcher @@ -100,7 +119,6 @@ class EIPChecker(object): def fetch_definition(self, skip_download=False, config=None, uri=None): - # check_and_get_definition_file """ fetches a definition file from server """ diff --git a/src/leap/eip/eipconnection.py b/src/leap/eip/eipconnection.py index aea560c9..386b71be 100644 --- a/src/leap/eip/eipconnection.py +++ b/src/leap/eip/eipconnection.py @@ -8,7 +8,7 @@ logging.basicConfig() logger = logging.getLogger(name=__name__) logger.setLevel(logging.DEBUG) -from leap.eip.checks import EIPChecker +from leap.eip.checks import EIPConfigChecker from leap.eip import exceptions as eip_exceptions from leap.eip.openvpnconnection import OpenVPNConnection @@ -18,18 +18,19 @@ class EIPConnection(OpenVPNConnection): Manages the execution of the OpenVPN process, auto starts, monitors the network connection, handles configuration, fixes leaky hosts, handles errors, etc. - Preferences will be stored via the Storage API. (TBD) Status updates (connected, bandwidth, etc) are signaled to the GUI. """ - def __init__(self, checker=EIPChecker, *args, **kwargs): + def __init__(self, config_checker=EIPConfigChecker, *args, **kwargs): self.settingsfile = kwargs.get('settingsfile', None) self.logfile = kwargs.get('logfile', None) + + # not used atm. but should. self.error_queue = [] status_signals = kwargs.pop('status_signals', None) self.status = EIPConnectionStatus(callbacks=status_signals) - self.checker = checker() + self.config_checker = config_checker() super(EIPConnection, self).__init__(*args, **kwargs) @@ -37,7 +38,7 @@ class EIPConnection(OpenVPNConnection): """ run all eip checks previous to attempting a connection """ - self.checker.run_all(skip_download=skip_download) + self.config_checker.run_all(skip_download=skip_download) def connect(self): """ diff --git a/src/leap/eip/tests/test_checks.py b/src/leap/eip/tests/test_checks.py index 83561833..1c79ce0c 100644 --- a/src/leap/eip/tests/test_checks.py +++ b/src/leap/eip/tests/test_checks.py @@ -32,7 +32,7 @@ class EIPCheckTest(BaseLeapTest): # test methods are there, and can be called from run_all def test_checker_should_implement_check_methods(self): - checker = eipchecks.EIPChecker() + checker = eipchecks.EIPConfigChecker() self.assertTrue(hasattr(checker, "check_default_eipconfig"), "missing meth") @@ -45,7 +45,7 @@ class EIPCheckTest(BaseLeapTest): self.assertTrue(hasattr(checker, "ping_gateway"), "missing meth") def test_checker_should_actually_call_all_tests(self): - checker = eipchecks.EIPChecker() + checker = eipchecks.EIPConfigChecker() mc = Mock() checker.run_all(checker=mc) @@ -64,7 +64,7 @@ class EIPCheckTest(BaseLeapTest): # test individual check methods def test_check_default_eipconfig(self): - checker = eipchecks.EIPChecker() + checker = eipchecks.EIPConfigChecker() # no eip config (empty home) eipconfig = baseconfig.get_config_file(eipconstants.EIP_CONFIG) self.assertFalse(os.path.isfile(eipconfig)) @@ -79,7 +79,7 @@ class EIPCheckTest(BaseLeapTest): # run validation methods. def test_check_is_there_default_provider(self): - checker = eipchecks.EIPChecker() + checker = eipchecks.EIPConfigChecker() # we do dump a sample eip config, but lacking a # default provider entry. # This error will be possible catched in a different @@ -104,7 +104,7 @@ class EIPCheckTest(BaseLeapTest): with patch.object(requests, "get") as mocked_get: mocked_get.return_value.status_code = 200 mocked_get.return_value.json = DEFAULT_PROVIDER_DEFINITION - checker = eipchecks.EIPChecker(fetcher=requests) + checker = eipchecks.EIPConfigChecker(fetcher=requests) sampleconfig = eipconstants.EIP_SAMPLE_JSON checker.fetch_definition(config=sampleconfig) @@ -122,12 +122,12 @@ class EIPCheckTest(BaseLeapTest): with patch.object(requests, "get") as mocked_get: mocked_get.return_value.status_code = 200 mocked_get.return_value.json = eipconstants.EIP_SAMPLE_SERVICE - checker = eipchecks.EIPChecker(fetcher=requests) + checker = eipchecks.EIPConfigChecker(fetcher=requests) sampleconfig = eipconstants.EIP_SAMPLE_JSON checker.fetch_definition(config=sampleconfig) def test_check_complete_eip_config(self): - checker = eipchecks.EIPChecker() + checker = eipchecks.EIPConfigChecker() with self.assertRaises(eipexceptions.EIPConfigurationError): sampleconfig = copy.copy(eipconstants.EIP_SAMPLE_JSON) sampleconfig['provider'] = None diff --git a/src/leap/eip/tests/test_eipconnection.py b/src/leap/eip/tests/test_eipconnection.py index 7d8acad6..26f6529e 100644 --- a/src/leap/eip/tests/test_eipconnection.py +++ b/src/leap/eip/tests/test_eipconnection.py @@ -50,8 +50,12 @@ class EIPConductorTest(BaseLeapTest): # for instance: # We have to TOUCH a keys file because # we're triggerig the key checks FROM - # the constructo. me not like that, + # the constructor. me not like that, # key checker should better be called explicitelly. + + # XXX change to keys_checker invocation + # (see config_checker) + filepath = os.path.expanduser( '~/.config/leap/providers/%s/openvpn.keys' % constants.DEFAULT_TEST_PROVIDER) @@ -60,12 +64,8 @@ class EIPConductorTest(BaseLeapTest): # we init the manager with only # some methods mocked - - self.manager = Mock( - name="openvpnmanager_mock") - + self.manager = Mock(name="openvpnmanager_mock") self.con = MockedEIPConnection() - #manager=self.manager) def tearDown(self): del self.con @@ -74,14 +74,6 @@ class EIPConductorTest(BaseLeapTest): # tests # - @unittest.skip - #ain't manager anymore! - def test_manager_was_initialized(self): - """ - manager init ok during conductor init? - """ - self.manager.assert_called_once_with() - def test_vpnconnection_defaults(self): """ default attrs as expected @@ -109,6 +101,16 @@ class EIPConductorTest(BaseLeapTest): self.assertEqual(self.con.args, [1, 2, 3]) + # config checks + + def test_config_checked_called(self): + del(self.con) + config_checker = Mock() + self.con = MockedEIPConnection(config_checker=config_checker) + self.assertTrue(config_checker.called) + self.con.run_checks() + self.con.config_checker.run_all.assert_called_with(skip_download=False) + # connect/disconnect calls def test_disconnect(self): -- cgit v1.2.3 From ed4ad3a392caf0211e51a48d2d7b6c5a2f7bb17a Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 29 Aug 2012 23:05:38 +0900 Subject: add eipconfig spec and config object --- src/leap/eip/checks.py | 5 +++ src/leap/eip/config.py | 47 ++++++++++++++++------------ src/leap/eip/constants.py | 3 ++ src/leap/eip/specs.py | 64 +++++++++++++++++++++++++++++++++++++++ src/leap/eip/tests/test_config.py | 2 +- 5 files changed, 101 insertions(+), 20 deletions(-) create mode 100644 src/leap/eip/specs.py (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index 27320b1f..e5b8e971 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -52,6 +52,8 @@ class EIPConfigChecker(object): self.config = None self.fetcher = fetcher + #self.eipconfig = eipconfig.EIPConfig() + def run_all(self, checker=None, skip_download=False): """ runs all checks in a row. @@ -208,10 +210,13 @@ class EIPConfigChecker(object): return baseconfig.get_config_file(eipconstants.EIP_CONFIG) def _is_there_default_eipconfig(self): + #XXX + #self.eipconfig.exists() return os.path.isfile( self._get_default_eipconfig_path()) def _dump_default_eipconfig(self): + #XXX self.eipconfig.save() eipconfig.dump_default_eipconfig( self._get_default_eipconfig_path()) diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py index 2694ca61..34f05070 100644 --- a/src/leap/eip/config.py +++ b/src/leap/eip/config.py @@ -8,20 +8,17 @@ import socket from leap.util.fileutil import (which, mkdir_p, check_and_fix_urw_only) -# from leap.base import config as baseconfig -from leap.base.config import (get_default_provider_path, - get_config_file, - get_username, - get_groupname, - validate_ip) +from leap.base import config as baseconfig from leap.baseapp.permcheck import (is_pkexec_in_system, is_auth_agent_running) from leap.eip import exceptions as eip_exceptions from leap.eip import constants as eipconstants +from leap.eip import specs as eipspecs logger = logging.getLogger(name=__name__) logger.setLevel('DEBUG') +# XXX deprecate per #447 OPENVPN_CONFIG_TEMPLATE = """#Autogenerated by eip-client wizard remote {VPN_REMOTE_HOST} {VPN_REMOTE_PORT} @@ -39,6 +36,18 @@ ca {LEAP_EIP_KEYS} """ +class EIPConfig(baseconfig.JSONLeapConfig): + spec = eipspecs.eipconfig_spec + + def _get_slug(self): + return baseconfig.get_config_file('eip.json') + + def _set_slug(self, *args, **kwargs): + raise AttributeError("you cannot set slug") + + slug = property(_get_slug, _set_slug) + + def check_or_create_default_vpnconf(config): """ checks that a vpn config file @@ -47,12 +56,12 @@ def check_or_create_default_vpnconf(config): ATM REQURES A [provider] section in eip.cfg with _at least_ a remote_ip value """ - default_provider_path = get_default_provider_path() + default_provider_path = baseconfig.get_default_provider_path() if not os.path.isdir(default_provider_path): mkdir_p(default_provider_path) - conf_file = get_config_file( + conf_file = baseconfig.get_config_file( 'openvpn.conf', folder=default_provider_path) @@ -74,7 +83,7 @@ def check_or_create_default_vpnconf(config): # and make a reverse resolv. remote_ip = config.get('provider', 'remote_ip') - validate_ip(remote_ip) + baseconfig.validate_ip(remote_ip) except ConfigParser.NoSectionError: raise eip_exceptions.EIPInitNoProviderError @@ -91,19 +100,19 @@ def check_or_create_default_vpnconf(config): default_subpath = os.path.join("providers", "default") - default_provider_path = get_config_file( + default_provider_path = baseconfig.get_config_file( '', folder=default_subpath) if not os.path.isdir(default_provider_path): mkdir_p(default_provider_path) - conf_file = get_config_file( + conf_file = baseconfig.get_config_file( 'openvpn.conf', folder=default_provider_path) # XXX keys have to be manually placed by now - keys_file = get_config_file( + keys_file = baseconfig.get_config_file( 'openvpn.keys', folder=default_provider_path) @@ -133,8 +142,8 @@ def build_ovpn_options(daemon=False): # get user/group name # also from config. - user = get_username() - group = get_groupname() + user = baseconfig.get_username() + group = baseconfig.get_groupname() opts = [] @@ -171,10 +180,10 @@ def build_ovpn_options(daemon=False): opts.append('--config') - default_provider_path = get_default_provider_path() + default_provider_path = baseconfig.get_default_provider_path() # XXX get rid of config_file at all - ovpncnf = get_config_file( + ovpncnf = baseconfig.get_config_file( 'openvpn.conf', folder=default_provider_path) opts.append(ovpncnf) @@ -296,7 +305,7 @@ def get_config(config_file=None): config = ConfigParser.ConfigParser(defaults) if not config_file: - fpath = get_config_file('eip.cfg') + fpath = baseconfig.get_config_file('eip.cfg') if not os.path.isfile(fpath): dpath, cfile = os.path.split(fpath) if not os.path.isdir(dpath): @@ -343,9 +352,9 @@ def check_vpn_keys(config): if config.has_option(*keyopt): keyfile = config.get(*keyopt) else: - keyfile = get_config_file( + keyfile = baseconfig.get_config_file( 'openvpn.keys', - folder=get_default_provider_path()) + folder=baseconfig.get_default_provider_path()) logger.debug('keyfile = %s', keyfile) # if no keys, raise error. diff --git a/src/leap/eip/constants.py b/src/leap/eip/constants.py index 6161d744..31974926 100644 --- a/src/leap/eip/constants.py +++ b/src/leap/eip/constants.py @@ -1,5 +1,8 @@ EIP_CONFIG = "eip.json" +# XXX deprecate. EIPConfig used instead +# can move for testing purposes. + EIP_SAMPLE_JSON = { "provider": "testprovider.example.org", "transport": "openvpn", diff --git a/src/leap/eip/specs.py b/src/leap/eip/specs.py new file mode 100644 index 00000000..572177dd --- /dev/null +++ b/src/leap/eip/specs.py @@ -0,0 +1,64 @@ +import os + +from leap.base import config as baseconfig + + +provider_ca_path = os.path.join( + baseconfig.get_default_provider_path(), + 'keys', 'ca', + 'testprovider-ca-cert.pem' +) + +client_cert_path = os.path.join( + baseconfig.get_default_provider_path(), + 'keys', 'client', + 'openvpn.pem' +) + +eipconfig_spec = { + 'provider': { + 'type': unicode, + 'default': u"testprovider.example.org", + 'required': True, + }, + 'transport': { + 'type': unicode, + 'default': u"openvpn", + }, + 'openvpn_protocol': { + 'type': unicode, + 'default': u"tcp" + }, + 'openvpn_port': { + 'type': int, + 'default': 80 + }, + 'oepnvpn_ca_certificate': { + 'type': unicode, # path + 'default': provider_ca_path + }, + 'openvpn_client_certificate': { + 'type': unicode, # path + 'default': client_cert_path + }, + 'connect_on_login': { + 'type': bool, + 'default': True + }, + 'block_cleartext_tr affic': { + 'type': bool, + 'default': True + }, + 'primary_gateway': { + 'type': unicode, + 'default': u"usa_west", + 'required': True + }, + 'secondary_gateway': { + 'type': unicode, + 'default': u"france" + }, + 'management_password': { + 'type': unicode + } +} diff --git a/src/leap/eip/tests/test_config.py b/src/leap/eip/tests/test_config.py index fac4729d..16219648 100644 --- a/src/leap/eip/tests/test_config.py +++ b/src/leap/eip/tests/test_config.py @@ -82,7 +82,7 @@ class EIPConfigTest(BaseLeapTest): self.assertEqual(args, self.get_expected_openvpn_args()) # XXX TODO: - # - should use touch_exec to plant an "executabe" in the path + # - should use touch_exec to plant an "executable" in the path # - should check that "which" for openvpn returns what's expected. -- cgit v1.2.3 From 1263cd7a3cfca81ae3e6976a489e2d3d4013d64b Mon Sep 17 00:00:00 2001 From: kali Date: Thu, 30 Aug 2012 00:36:50 +0900 Subject: add lazy evaluation to config specs now callables are allowed in specs *only at one level depth* to allow for last-minute evaluation on context-sensitive data, like paths affected by os.environ also some minor modifications to make check tests pass after putting the new jsonconfig-based eipconfig in place. aaaaaall green again :) --- src/leap/eip/checks.py | 15 +++++++----- src/leap/eip/config.py | 2 ++ src/leap/eip/constants.py | 50 +-------------------------------------- src/leap/eip/specs.py | 14 ++++++----- src/leap/eip/tests/__init__.py | 0 src/leap/eip/tests/data.py | 50 +++++++++++++++++++++++++++++++++++++++ src/leap/eip/tests/test_checks.py | 42 +++++++++++++++++++------------- 7 files changed, 95 insertions(+), 78 deletions(-) create mode 100644 src/leap/eip/tests/__init__.py create mode 100644 src/leap/eip/tests/data.py (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index e5b8e971..b92ea706 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -52,7 +52,7 @@ class EIPConfigChecker(object): self.config = None self.fetcher = fetcher - #self.eipconfig = eipconfig.EIPConfig() + self.eipconfig = eipconfig.EIPConfig() def run_all(self, checker=None, skip_download=False): """ @@ -211,14 +211,17 @@ class EIPConfigChecker(object): def _is_there_default_eipconfig(self): #XXX - #self.eipconfig.exists() - return os.path.isfile( - self._get_default_eipconfig_path()) + return self.eipconfig.exists() + #return os.path.isfile( + #self._get_default_eipconfig_path()) def _dump_default_eipconfig(self): #XXX self.eipconfig.save() - eipconfig.dump_default_eipconfig( - self._get_default_eipconfig_path()) + logger.debug('saving eipconfig') + #import ipdb;ipdb.set_trace() + self.eipconfig.save() + #eipconfig.dump_default_eipconfig( + #self._get_default_eipconfig_path()) def _get_provider_definition_uri(self, domain=None, path=None): if domain is None: diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py index 34f05070..a7b24f9b 100644 --- a/src/leap/eip/config.py +++ b/src/leap/eip/config.py @@ -15,6 +15,7 @@ from leap.eip import exceptions as eip_exceptions from leap.eip import constants as eipconstants from leap.eip import specs as eipspecs +logging.basicConfig() logger = logging.getLogger(name=__name__) logger.setLevel('DEBUG') @@ -262,6 +263,7 @@ def build_ovpn_command(config, debug=False, do_pkexec_check=True): return [command[0], command[1:]] +# XXX deprecate def get_sensible_defaults(): """ gathers a dict of sensible defaults, diff --git a/src/leap/eip/constants.py b/src/leap/eip/constants.py index 31974926..ce50f5e0 100644 --- a/src/leap/eip/constants.py +++ b/src/leap/eip/constants.py @@ -1,51 +1,3 @@ +# not used anymore with the new JSONConfig.slug EIP_CONFIG = "eip.json" - -# XXX deprecate. EIPConfig used instead -# can move for testing purposes. - -EIP_SAMPLE_JSON = { - "provider": "testprovider.example.org", - "transport": "openvpn", - "openvpn_protocol": "tcp", - "openvpn_port": "80", - "openvpn_ca_certificate": "~/.config/leap/providers/" - "testprovider.example.org/" - "keys/ca/testprovider-ca-cert-" - "2013-01-01.pem", - "openvpn_client_certificate": "~/.config/leap/providers/" - "testprovider.example.org/" - "keys/client/openvpn-2012-09-31.pem", - "connect_on_login": True, - "block_cleartext_traffic": True, - "primary_gateway": "usa_west", - "secondary_gateway": "france", - "management_password": "oph7Que1othahwiech6J" -} - EIP_SERVICE_EXPECTED_PATH = "eip-service.json" - -EIP_SAMPLE_SERVICE = { - "serial": 1, - "version": "0.1.0", - "capabilities": { - "transport": ["openvpn"], - "ports": ["80", "53"], - "protocols": ["udp", "tcp"], - "static_ips": True, - "adblock": True - }, - "gateways": [ - {"country_code": "us", - "label": {"en":"west"}, - "capabilities": {}, - "hosts": ["1.2.3.4", "1.2.3.5"]}, - {"country_code": "us", - "label": {"en":"east"}, - "capabilities": {}, - "hosts": ["1.2.3.4", "1.2.3.5"]}, - {"country_code": "fr", - "label": {}, - "capabilities": {}, - "hosts": ["1.2.3.4", "1.2.3.5"]} - ] -} diff --git a/src/leap/eip/specs.py b/src/leap/eip/specs.py index 572177dd..a39e5979 100644 --- a/src/leap/eip/specs.py +++ b/src/leap/eip/specs.py @@ -1,19 +1,21 @@ +from __future__ import (unicode_literals) import os from leap.base import config as baseconfig -provider_ca_path = os.path.join( +provider_ca_path = lambda: unicode(os.path.join( baseconfig.get_default_provider_path(), 'keys', 'ca', 'testprovider-ca-cert.pem' -) +)) -client_cert_path = os.path.join( + +client_cert_path = lambda: unicode(os.path.join( baseconfig.get_default_provider_path(), 'keys', 'client', 'openvpn.pem' -) +)) eipconfig_spec = { 'provider': { @@ -33,7 +35,7 @@ eipconfig_spec = { 'type': int, 'default': 80 }, - 'oepnvpn_ca_certificate': { + 'openvpn_ca_certificate': { 'type': unicode, # path 'default': provider_ca_path }, @@ -45,7 +47,7 @@ eipconfig_spec = { 'type': bool, 'default': True }, - 'block_cleartext_tr affic': { + 'block_cleartext_traffic': { 'type': bool, 'default': True }, diff --git a/src/leap/eip/tests/__init__.py b/src/leap/eip/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/leap/eip/tests/data.py b/src/leap/eip/tests/data.py new file mode 100644 index 00000000..9067c270 --- /dev/null +++ b/src/leap/eip/tests/data.py @@ -0,0 +1,50 @@ +from __future__ import unicode_literals +import os + +# sample data used in tests + +EIP_SAMPLE_JSON = { + "provider": "testprovider.example.org", + "transport": "openvpn", + "openvpn_protocol": "tcp", + "openvpn_port": 80, + "openvpn_ca_certificate": os.path.expanduser( + "~/.config/leap/providers/" + "testprovider.example.org/" + "keys/ca/testprovider-ca-cert.pem"), + "openvpn_client_certificate": os.path.expanduser( + "~/.config/leap/providers/" + "testprovider.example.org/" + "keys/client/openvpn.pem"), + "connect_on_login": True, + "block_cleartext_traffic": True, + "primary_gateway": "usa_west", + "secondary_gateway": "france", + #"management_password": "oph7Que1othahwiech6J" +} + +EIP_SAMPLE_SERVICE = { + "serial": 1, + "version": "0.1.0", + "capabilities": { + "transport": ["openvpn"], + "ports": ["80", "53"], + "protocols": ["udp", "tcp"], + "static_ips": True, + "adblock": True + }, + "gateways": [ + {"country_code": "us", + "label": {"en":"west"}, + "capabilities": {}, + "hosts": ["1.2.3.4", "1.2.3.5"]}, + {"country_code": "us", + "label": {"en":"east"}, + "capabilities": {}, + "hosts": ["1.2.3.4", "1.2.3.5"]}, + {"country_code": "fr", + "label": {}, + "capabilities": {}, + "hosts": ["1.2.3.4", "1.2.3.5"]} + ] +} diff --git a/src/leap/eip/tests/test_checks.py b/src/leap/eip/tests/test_checks.py index 1c79ce0c..e53a2a1d 100644 --- a/src/leap/eip/tests/test_checks.py +++ b/src/leap/eip/tests/test_checks.py @@ -14,8 +14,9 @@ from leap.base import config as baseconfig from leap.base.constants import (DEFAULT_PROVIDER_DEFINITION, DEFINITION_EXPECTED_PATH) from leap.eip import checks as eipchecks -from leap.eip import constants as eipconstants +from leap.eip import specs as eipspecs from leap.eip import exceptions as eipexceptions +from leap.eip.tests import data as testdata from leap.testing.basetest import BaseLeapTest @@ -66,17 +67,24 @@ class EIPCheckTest(BaseLeapTest): def test_check_default_eipconfig(self): checker = eipchecks.EIPConfigChecker() # no eip config (empty home) - eipconfig = baseconfig.get_config_file(eipconstants.EIP_CONFIG) - self.assertFalse(os.path.isfile(eipconfig)) + eipconfig_path = checker.eipconfig.filename + self.assertFalse(os.path.isfile(eipconfig_path)) checker.check_default_eipconfig() # we've written one, so it should be there. - self.assertTrue(os.path.isfile(eipconfig)) - with open(eipconfig, 'rb') as fp: + self.assertTrue(os.path.isfile(eipconfig_path)) + with open(eipconfig_path, 'rb') as fp: deserialized = json.load(fp) - self.assertEqual(deserialized, - eipconstants.EIP_SAMPLE_JSON) - # TODO: when new JSONConfig class is in place, we shold - # run validation methods. + + # force re-evaluation of the paths + # small workaround for evaluating home dirs correctly + EIP_SAMPLE_JSON = copy.copy(testdata.EIP_SAMPLE_JSON) + EIP_SAMPLE_JSON['openvpn_client_certificate'] = \ + eipspecs.client_cert_path() + EIP_SAMPLE_JSON['openvpn_ca_certificate'] = \ + eipspecs.provider_ca_path() + self.assertEqual(deserialized, EIP_SAMPLE_JSON) + + # TODO: shold ALSO run validation methods. def test_check_is_there_default_provider(self): checker = eipchecks.EIPConfigChecker() @@ -85,7 +93,7 @@ class EIPCheckTest(BaseLeapTest): # This error will be possible catched in a different # place, when JSONConfig does validation of required fields. - sampleconfig = copy.copy(eipconstants.EIP_SAMPLE_JSON) + sampleconfig = copy.copy(testdata.EIP_SAMPLE_JSON) # blank out default_provider sampleconfig['provider'] = None eipcfg_path = checker._get_default_eipconfig_path() @@ -94,7 +102,7 @@ class EIPCheckTest(BaseLeapTest): with self.assertRaises(eipexceptions.EIPMissingDefaultProvider): checker.check_is_there_default_provider() - sampleconfig = eipconstants.EIP_SAMPLE_JSON + sampleconfig = testdata.EIP_SAMPLE_JSON eipcfg_path = checker._get_default_eipconfig_path() with open(eipcfg_path, 'w') as fp: json.dump(sampleconfig, fp) @@ -105,7 +113,7 @@ class EIPCheckTest(BaseLeapTest): mocked_get.return_value.status_code = 200 mocked_get.return_value.json = DEFAULT_PROVIDER_DEFINITION checker = eipchecks.EIPConfigChecker(fetcher=requests) - sampleconfig = eipconstants.EIP_SAMPLE_JSON + sampleconfig = testdata.EIP_SAMPLE_JSON checker.fetch_definition(config=sampleconfig) fn = os.path.join(baseconfig.get_default_provider_path(), @@ -121,24 +129,24 @@ class EIPCheckTest(BaseLeapTest): def test_fetch_eip_config(self): with patch.object(requests, "get") as mocked_get: mocked_get.return_value.status_code = 200 - mocked_get.return_value.json = eipconstants.EIP_SAMPLE_SERVICE + mocked_get.return_value.json = testdata.EIP_SAMPLE_SERVICE checker = eipchecks.EIPConfigChecker(fetcher=requests) - sampleconfig = eipconstants.EIP_SAMPLE_JSON + sampleconfig = testdata.EIP_SAMPLE_JSON checker.fetch_definition(config=sampleconfig) def test_check_complete_eip_config(self): checker = eipchecks.EIPConfigChecker() with self.assertRaises(eipexceptions.EIPConfigurationError): - sampleconfig = copy.copy(eipconstants.EIP_SAMPLE_JSON) + sampleconfig = copy.copy(testdata.EIP_SAMPLE_JSON) sampleconfig['provider'] = None checker.check_complete_eip_config(config=sampleconfig) with self.assertRaises(eipexceptions.EIPConfigurationError): - sampleconfig = copy.copy(eipconstants.EIP_SAMPLE_JSON) + sampleconfig = copy.copy(testdata.EIP_SAMPLE_JSON) del sampleconfig['provider'] checker.check_complete_eip_config(config=sampleconfig) # normal case - sampleconfig = copy.copy(eipconstants.EIP_SAMPLE_JSON) + sampleconfig = copy.copy(testdata.EIP_SAMPLE_JSON) checker.check_complete_eip_config(config=sampleconfig) if __name__ == "__main__": -- cgit v1.2.3 From e6483d20a5500e86b5fa4e7da63f911641b7e9dd Mon Sep 17 00:00:00 2001 From: kali Date: Thu, 30 Aug 2012 01:11:42 +0900 Subject: fix config load method it was not updating config dict --- src/leap/eip/checks.py | 27 +++++---------------------- src/leap/eip/tests/test_checks.py | 13 ++++++++++--- 2 files changed, 15 insertions(+), 25 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index b92ea706..4b2326a5 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -21,8 +21,8 @@ EIPConfigChecker this is the first of 3 consecutive checks that we're implementing. It is used from the eip conductor (a instance of EIPConnection that is -managed from the QtApp), running run_all method before trying to call -`connect` or any other of the state switching methods. +managed from the QtApp), running `run_all` method before trying to call +`connect` or any other of the state-changing methods. It checks that the needed files are provided or can be discovered over the net. Much of these tests are not specific to EIP module, and can be splitted @@ -69,7 +69,7 @@ class EIPConfigChecker(object): # TODO: get rid of check_default. # check_complete should - # be enough. + # be enough. but here to make early tests easier. checker.check_default_eipconfig() checker.check_is_there_default_provider() @@ -103,15 +103,9 @@ class EIPConfigChecker(object): default provider found on eip config. This is catched by ui and runs FirstRunWizard (MVS+) """ - # if config is not None: - # config = config - # else: self.get_eipconfig - # XXX parse EIPConfig. - # XXX get default_provider. + if config is None: + config = self.eipconfig.get_config() logger.debug('checking default provider') - eipcfg = self._get_default_eipconfig_path() - with open(eipcfg, 'r') as fp: - config = json.load(fp) provider = config.get('provider', None) if provider is None: raise eipexceptions.EIPMissingDefaultProvider @@ -206,22 +200,11 @@ class EIPConfigChecker(object): # private helpers # - def _get_default_eipconfig_path(self): - return baseconfig.get_config_file(eipconstants.EIP_CONFIG) - def _is_there_default_eipconfig(self): - #XXX return self.eipconfig.exists() - #return os.path.isfile( - #self._get_default_eipconfig_path()) def _dump_default_eipconfig(self): - #XXX self.eipconfig.save() - logger.debug('saving eipconfig') - #import ipdb;ipdb.set_trace() self.eipconfig.save() - #eipconfig.dump_default_eipconfig( - #self._get_default_eipconfig_path()) def _get_provider_definition_uri(self, domain=None, path=None): if domain is None: diff --git a/src/leap/eip/tests/test_checks.py b/src/leap/eip/tests/test_checks.py index e53a2a1d..5697ad10 100644 --- a/src/leap/eip/tests/test_checks.py +++ b/src/leap/eip/tests/test_checks.py @@ -93,19 +93,26 @@ class EIPCheckTest(BaseLeapTest): # This error will be possible catched in a different # place, when JSONConfig does validation of required fields. - sampleconfig = copy.copy(testdata.EIP_SAMPLE_JSON) + # passing direct config + with self.assertRaises(eipexceptions.EIPMissingDefaultProvider): + checker.check_is_there_default_provider(config={}) + + # ok. now, messing with real files... # blank out default_provider + sampleconfig = copy.copy(testdata.EIP_SAMPLE_JSON) sampleconfig['provider'] = None - eipcfg_path = checker._get_default_eipconfig_path() + eipcfg_path = checker.eipconfig.filename with open(eipcfg_path, 'w') as fp: json.dump(sampleconfig, fp) with self.assertRaises(eipexceptions.EIPMissingDefaultProvider): + checker.eipconfig.load(fromfile=eipcfg_path) checker.check_is_there_default_provider() sampleconfig = testdata.EIP_SAMPLE_JSON - eipcfg_path = checker._get_default_eipconfig_path() + #eipcfg_path = checker._get_default_eipconfig_path() with open(eipcfg_path, 'w') as fp: json.dump(sampleconfig, fp) + checker.eipconfig.load() self.assertTrue(checker.check_is_there_default_provider()) def test_fetch_definition(self): -- cgit v1.2.3 From d69976caa5070403f81799c79be974241cff7f70 Mon Sep 17 00:00:00 2001 From: kali Date: Thu, 30 Aug 2012 03:43:05 +0900 Subject: fetcher moved to baseconfig + eipchecker using eipservice config. --- src/leap/eip/checks.py | 76 +++++++++++++-------------------------- src/leap/eip/config.py | 14 ++++++++ src/leap/eip/specs.py | 29 +++++++++++++++ src/leap/eip/tests/data.py | 8 ----- src/leap/eip/tests/test_checks.py | 9 ++--- 5 files changed, 72 insertions(+), 64 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index 4b2326a5..b57977f0 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -8,12 +8,11 @@ logger.setLevel(logging.DEBUG) import requests -from leap.base import config as baseconfig from leap.base import constants as baseconstants +from leap.base import providers from leap.eip import config as eipconfig from leap.eip import constants as eipconstants from leap.eip import exceptions as eipexceptions -from leap.util.fileutil import mkdir_p """ EIPConfigChecker @@ -49,10 +48,11 @@ class EIPConfigChecker(object): # argument on init. # we want tests # to be explicitely run. - self.config = None self.fetcher = fetcher self.eipconfig = eipconfig.EIPConfig() + self.defaultprovider = providers.LeapProviderDefinition() + self.eipserviceconfig = eipconfig.EIPServiceConfig() def run_all(self, checker=None, skip_download=False): """ @@ -74,7 +74,7 @@ class EIPConfigChecker(object): checker.check_is_there_default_provider() checker.fetch_definition(skip_download=skip_download) - checker.fetch_eip_config(skip_download=skip_download) + checker.fetch_eip_service_config(skip_download=skip_download) checker.check_complete_eip_config() #checker.ping_gateway() @@ -109,8 +109,7 @@ class EIPConfigChecker(object): provider = config.get('provider', None) if provider is None: raise eipexceptions.EIPMissingDefaultProvider - if config: - self.config = config + # XXX raise also if malformed ProviderDefinition? return True def fetch_definition(self, skip_download=False, @@ -120,65 +119,38 @@ class EIPConfigChecker(object): """ # TODO: # - Implement diff - # - overwrite if different. + # - overwrite only if different. + # (attend to serial field different, for instance) + logger.debug('fetching definition') if skip_download: logger.debug('(fetching def skipped)') return True if config is None: - config = self.config + config = self.defaultprovider.get_config() if uri is None: - if config: - domain = config.get('provider', None) - else: - domain = None - uri = self._get_provider_definition_uri( - domain=domain) - - # XXX move to JSONConfig Fetcher - request = self.fetcher.get(uri) - request.raise_for_status() - - definition_file = os.path.join( - baseconfig.get_default_provider_path(), - baseconstants.DEFINITION_EXPECTED_PATH) - - folder, filename = os.path.split(definition_file) - if not os.path.isdir(folder): - mkdir_p(folder) - with open(definition_file, 'wb') as f: - f.write(json.dumps(request.json, indent=4)) - - def fetch_eip_config(self, skip_download=False, - config=None, uri=None): + domain = config.get('provider', None) + uri = self._get_provider_definition_uri(domain=domain) + + self.defaultprovider.load(from_uri=uri, fetcher=self.fetcher) + self.defaultprovider.save() + + def fetch_eip_service_config(self, skip_download=False, + config=None, uri=None): if skip_download: return True if config is None: - config = self.config + config = self.eipserviceconfig.get_config() if uri is None: - if config: - domain = config.get('provider', None) - else: - domain = None - uri = self._get_eip_service_uri( - domain=domain) - - # XXX move to JSONConfig Fetcher - request = self.fetcher.get(uri) - request.raise_for_status() - - definition_file = os.path.join( - baseconfig.get_default_provider_path(), - eipconstants.EIP_SERVICE_EXPECTED_PATH) - - folder, filename = os.path.split(definition_file) - if not os.path.isdir(folder): - mkdir_p(folder) - with open(definition_file, 'wb') as f: - f.write(json.dumps(request.json, indent=4)) + domain = config.get('provider', None) + uri = self._get_eip_service_uri(domain=domain) + + self.eipserviceconfig.load(from_uri=uri, fetcher=self.fetcher) + self.eipserviceconfig.save() def check_complete_eip_config(self, config=None): + # TODO check for gateway if config is None: config = self.config try: diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py index a7b24f9b..b6c38a77 100644 --- a/src/leap/eip/config.py +++ b/src/leap/eip/config.py @@ -49,6 +49,20 @@ class EIPConfig(baseconfig.JSONLeapConfig): slug = property(_get_slug, _set_slug) +class EIPServiceConfig(baseconfig.JSONLeapConfig): + spec = eipspecs.eipservice_config_spec + + def _get_slug(self): + return baseconfig.get_config_file( + 'eip-service.json', + folder=baseconfig.get_default_provider_path()) + + def _set_slug(self): + raise AttributeError("you cannot set slug") + + slug = property(_get_slug, _set_slug) + + def check_or_create_default_vpnconf(config): """ checks that a vpn config file diff --git a/src/leap/eip/specs.py b/src/leap/eip/specs.py index a39e5979..e617574c 100644 --- a/src/leap/eip/specs.py +++ b/src/leap/eip/specs.py @@ -64,3 +64,32 @@ eipconfig_spec = { 'type': unicode } } + +eipservice_config_spec = { + 'serial': { + 'type': int, + 'required': True, + 'default': 1 + }, + 'version': { + 'type': unicode, + 'required': True, + 'default': "0.1.0" + }, + 'capabilities': { + 'type': dict, + 'default': { + "transport": ["openvpn"], + "ports": ["80", "53"], + "protocols": ["udp", "tcp"], + "static_ips": True, + "adblock": True} + }, + 'gateways': { + 'type': list, + 'default': [{"country_code": "us", + "label": {"en":"west"}, + "capabilities": {}, + "hosts": ["1.2.3.4", "1.2.3.5"]}] + } +} diff --git a/src/leap/eip/tests/data.py b/src/leap/eip/tests/data.py index 9067c270..284b398f 100644 --- a/src/leap/eip/tests/data.py +++ b/src/leap/eip/tests/data.py @@ -38,13 +38,5 @@ EIP_SAMPLE_SERVICE = { "label": {"en":"west"}, "capabilities": {}, "hosts": ["1.2.3.4", "1.2.3.5"]}, - {"country_code": "us", - "label": {"en":"east"}, - "capabilities": {}, - "hosts": ["1.2.3.4", "1.2.3.5"]}, - {"country_code": "fr", - "label": {}, - "capabilities": {}, - "hosts": ["1.2.3.4", "1.2.3.5"]} ] } diff --git a/src/leap/eip/tests/test_checks.py b/src/leap/eip/tests/test_checks.py index 5697ad10..1e629203 100644 --- a/src/leap/eip/tests/test_checks.py +++ b/src/leap/eip/tests/test_checks.py @@ -40,7 +40,8 @@ class EIPCheckTest(BaseLeapTest): self.assertTrue(hasattr(checker, "check_is_there_default_provider"), "missing meth") self.assertTrue(hasattr(checker, "fetch_definition"), "missing meth") - self.assertTrue(hasattr(checker, "fetch_eip_config"), "missing meth") + self.assertTrue(hasattr(checker, "fetch_eip_service_config"), + "missing meth") self.assertTrue(hasattr(checker, "check_complete_eip_config"), "missing meth") self.assertTrue(hasattr(checker, "ping_gateway"), "missing meth") @@ -55,7 +56,7 @@ class EIPCheckTest(BaseLeapTest): "not called") self.assertTrue(mc.fetch_definition.called, "not called") - self.assertTrue(mc.fetch_eip_config.called, + self.assertTrue(mc.fetch_eip_service_config.called, "not called") self.assertTrue(mc.check_complete_eip_config.called, "not called") @@ -133,13 +134,13 @@ class EIPCheckTest(BaseLeapTest): # (and proper EIPExceptions are raised). # Look at base.test_config. - def test_fetch_eip_config(self): + def test_fetch_eip_service_config(self): with patch.object(requests, "get") as mocked_get: mocked_get.return_value.status_code = 200 mocked_get.return_value.json = testdata.EIP_SAMPLE_SERVICE checker = eipchecks.EIPConfigChecker(fetcher=requests) sampleconfig = testdata.EIP_SAMPLE_JSON - checker.fetch_definition(config=sampleconfig) + checker.fetch_eip_service_config(config=sampleconfig) def test_check_complete_eip_config(self): checker = eipchecks.EIPConfigChecker() -- cgit v1.2.3 From 5e77b77765154850fb708e6ea188fcf7ba99fdce Mon Sep 17 00:00:00 2001 From: kali Date: Thu, 30 Aug 2012 04:37:30 +0900 Subject: add test for JSONLeapConfig metaclass --- src/leap/eip/checks.py | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index b57977f0..1db7158f 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -35,6 +35,14 @@ Other related checkers - not implemented yet -: """ +class LeapNetworkChecker(object): + pass + + +class ProviderCertChecker(object): + pass + + class EIPConfigChecker(object): """ Several tests needed -- cgit v1.2.3 From b79a08b84e52871b1e1254f65ff774a6f0857608 Mon Sep 17 00:00:00 2001 From: kali Date: Thu, 30 Aug 2012 05:37:44 +0900 Subject: move extra options from config template to cl opts --- src/leap/eip/config.py | 44 +++++++++++++++++++++------------ src/leap/eip/tests/test_config.py | 51 +++++++++++++++++++++++++++++++++++---- 2 files changed, 75 insertions(+), 20 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py index b6c38a77..a9de60b2 100644 --- a/src/leap/eip/config.py +++ b/src/leap/eip/config.py @@ -63,6 +63,7 @@ class EIPServiceConfig(baseconfig.JSONLeapConfig): slug = property(_get_slug, _set_slug) +# XXX deprecate by #447 def check_or_create_default_vpnconf(config): """ checks that a vpn config file @@ -162,6 +163,26 @@ def build_ovpn_options(daemon=False): opts = [] + opts.append('--mode') + opts.append('client') + + opts.append('--dev') + # XXX same in win? + opts.append('tun') + opts.append('--persist-tun') + opts.append('--persist-key') + + # remote + # XXX get remote from eip.json + opts.append('--remote') + opts.append('testprovider.example.org') + opts.append('1194') + opts.append('udp') + + opts.append('--tls-client') + opts.append('--remote-cert-tls') + opts.append('server') + # set user and group opts.append('--user') opts.append('%s' % user) @@ -179,6 +200,7 @@ def build_ovpn_options(daemon=False): ourplatform = platform.system() if ourplatform in ("Linux", "Mac"): opts.append('--management') + # XXX get a different sock each time ... opts.append('/tmp/.eip.sock') opts.append('unix') if ourplatform == "Windows": @@ -187,21 +209,13 @@ def build_ovpn_options(daemon=False): # XXX which is a good choice? opts.append('7777') - # remaining config options will go in a file - - # NOTE: we will build this file from - # the service definition file. - # XXX override from --with-openvpn-config - - opts.append('--config') - - default_provider_path = baseconfig.get_default_provider_path() - - # XXX get rid of config_file at all - ovpncnf = baseconfig.get_config_file( - 'openvpn.conf', - folder=default_provider_path) - opts.append(ovpncnf) + # certs + opts.append('--cert') + opts.append(eipspecs.client_cert_path()) + opts.append('--key') + opts.append(eipspecs.client_cert_path()) + opts.append('--ca') + opts.append(eipspecs.provider_ca_path()) # we cannot run in daemon mode # with the current subp setting. diff --git a/src/leap/eip/tests/test_config.py b/src/leap/eip/tests/test_config.py index 16219648..c3a8075e 100644 --- a/src/leap/eip/tests/test_config.py +++ b/src/leap/eip/tests/test_config.py @@ -48,6 +48,23 @@ class EIPConfigTest(BaseLeapTest): username = self.get_username() groupname = self.get_groupname() + args.append('--mode') + args.append('client') + args.append('--dev') + #does this have to be tap for win?? + args.append('tun') + args.append('--persist-tun') + args.append('--persist-key') + args.append('--remote') + args.append('testprovider.example.org') + # XXX get port!? + args.append('1194') + # XXX get proto + args.append('udp') + args.append('--tls-client') + args.append('--remote-cert-tls') + args.append('server') + args.append('--user') args.append(username) args.append('--group') @@ -55,16 +72,40 @@ class EIPConfigTest(BaseLeapTest): args.append('--management-client-user') args.append(username) args.append('--management-signal') - args.append('--management') + args.append('--management') #XXX hey! #get platform switches here! args.append('/tmp/.eip.sock') args.append('unix') - args.append('--config') - args.append(os.path.expanduser( - '~/.config/leap/providers/%s/openvpn.conf' - % constants.DEFAULT_TEST_PROVIDER)) + + # certs + # XXX get values from specs? + args.append('--cert') + args.append(os.path.join( + self.home, + '.config', 'leap', 'providers', + 'testprovider.example.org', + 'keys', 'client', + 'openvpn.pem')) + args.append('--key') + args.append(os.path.join( + self.home, + '.config', 'leap', 'providers', + 'testprovider.example.org', + 'keys', 'client', + 'openvpn.pem')) + args.append('--ca') + args.append(os.path.join( + self.home, + '.config', 'leap', 'providers', + 'testprovider.example.org', + 'keys', 'ca', + 'testprovider-ca-cert.pem')) + #args.append('--config') + #args.append(os.path.expanduser( + #'~/.config/leap/providers/%s/openvpn.conf' + #% constants.DEFAULT_TEST_PROVIDER)) return args # build command string -- cgit v1.2.3 From 396d815e318d03df4e21269aa8c3e6c0e6f7fad0 Mon Sep 17 00:00:00 2001 From: kali Date: Thu, 30 Aug 2012 06:00:03 +0900 Subject: working with options only from cli --- src/leap/eip/checks.py | 2 +- src/leap/eip/config.py | 3 +-- src/leap/eip/tests/test_config.py | 7 +------ 3 files changed, 3 insertions(+), 9 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index 1db7158f..c6a7ca72 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -160,7 +160,7 @@ class EIPConfigChecker(object): def check_complete_eip_config(self, config=None): # TODO check for gateway if config is None: - config = self.config + config = self.eipconfig.get_config() try: 'trying assertions' assert 'provider' in config diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py index a9de60b2..70108a1d 100644 --- a/src/leap/eip/config.py +++ b/src/leap/eip/config.py @@ -163,8 +163,7 @@ def build_ovpn_options(daemon=False): opts = [] - opts.append('--mode') - opts.append('client') + opts.append('--client') opts.append('--dev') # XXX same in win? diff --git a/src/leap/eip/tests/test_config.py b/src/leap/eip/tests/test_config.py index c3a8075e..87ef33ef 100644 --- a/src/leap/eip/tests/test_config.py +++ b/src/leap/eip/tests/test_config.py @@ -48,8 +48,7 @@ class EIPConfigTest(BaseLeapTest): username = self.get_username() groupname = self.get_groupname() - args.append('--mode') - args.append('client') + args.append('--client') args.append('--dev') #does this have to be tap for win?? args.append('tun') @@ -102,10 +101,6 @@ class EIPConfigTest(BaseLeapTest): 'testprovider.example.org', 'keys', 'ca', 'testprovider-ca-cert.pem')) - #args.append('--config') - #args.append(os.path.expanduser( - #'~/.config/leap/providers/%s/openvpn.conf' - #% constants.DEFAULT_TEST_PROVIDER)) return args # build command string -- cgit v1.2.3 From d4de193b52881590c07468bdfece5f82fa48840d Mon Sep 17 00:00:00 2001 From: kali Date: Thu, 30 Aug 2012 06:05:49 +0900 Subject: remove unused function --- src/leap/eip/config.py | 95 --------------------------------------- src/leap/eip/openvpnconnection.py | 18 -------- 2 files changed, 113 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py index 70108a1d..c0819628 100644 --- a/src/leap/eip/config.py +++ b/src/leap/eip/config.py @@ -19,23 +19,6 @@ logging.basicConfig() logger = logging.getLogger(name=__name__) logger.setLevel('DEBUG') -# XXX deprecate per #447 -OPENVPN_CONFIG_TEMPLATE = """#Autogenerated by eip-client wizard -remote {VPN_REMOTE_HOST} {VPN_REMOTE_PORT} - -client -dev tun -persist-tun -persist-key -proto udp -tls-client -remote-cert-tls server - -cert {LEAP_EIP_KEYS} -key {LEAP_EIP_KEYS} -ca {LEAP_EIP_KEYS} -""" - class EIPConfig(baseconfig.JSONLeapConfig): spec = eipspecs.eipconfig_spec @@ -63,84 +46,6 @@ class EIPServiceConfig(baseconfig.JSONLeapConfig): slug = property(_get_slug, _set_slug) -# XXX deprecate by #447 -def check_or_create_default_vpnconf(config): - """ - checks that a vpn config file - exists for a default provider, - or creates one if it does not. - ATM REQURES A [provider] section in - eip.cfg with _at least_ a remote_ip value - """ - default_provider_path = baseconfig.get_default_provider_path() - - if not os.path.isdir(default_provider_path): - mkdir_p(default_provider_path) - - conf_file = baseconfig.get_config_file( - 'openvpn.conf', - folder=default_provider_path) - - if os.path.isfile(conf_file): - return - else: - logger.debug( - 'missing default openvpn config\n' - 'creating one...') - - # We're getting provider from eip.cfg - # by now. Get it from a list of gateways - # instead. - - try: - # XXX by now, we're expecting - # only IP format for remote. - # We should allow also domain names, - # and make a reverse resolv. - remote_ip = config.get('provider', - 'remote_ip') - baseconfig.validate_ip(remote_ip) - - except ConfigParser.NoSectionError: - raise eip_exceptions.EIPInitNoProviderError - - except socket.error: - # this does not look like an ip, dave - raise eip_exceptions.EIPInitBadProviderError - - if config.has_option('provider', 'remote_port'): - remote_port = config.get('provider', - 'remote_port') - else: - remote_port = 1194 - - default_subpath = os.path.join("providers", - "default") - default_provider_path = baseconfig.get_config_file( - '', - folder=default_subpath) - - if not os.path.isdir(default_provider_path): - mkdir_p(default_provider_path) - - conf_file = baseconfig.get_config_file( - 'openvpn.conf', - folder=default_provider_path) - - # XXX keys have to be manually placed by now - keys_file = baseconfig.get_config_file( - 'openvpn.keys', - folder=default_provider_path) - - ovpn_config = OPENVPN_CONFIG_TEMPLATE.format( - VPN_REMOTE_HOST=remote_ip, - VPN_REMOTE_PORT=remote_port, - LEAP_EIP_KEYS=keys_file) - - with open(conf_file, 'wb') as f: - f.write(ovpn_config) - - def build_ovpn_options(daemon=False): """ build a list of options diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index 5f67d27a..1f2f6d8c 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -139,23 +139,6 @@ to be triggered for each one of them. self.command = command self.args = args - def _check_ovpn_config(self): - """ - checks if there is a default openvpn config. - if not, it writes one with info from the provider - definition file - """ - # TODO - # - get --with-openvpn-config from opts - try: - eip_config.check_or_create_default_vpnconf(self.config) - except eip_exceptions.EIPInitNoProviderError: - logger.error('missing default provider definition') - self.missing_provider = True - except eip_exceptions.EIPInitBadProviderError: - logger.error('bad provider definition') - self.bad_provider = True - def _get_or_create_config(self): """ retrieves the config options from defaults or @@ -168,7 +151,6 @@ to be triggered for each one of them. self._set_autostart() self._set_ovpn_command() - self._check_ovpn_config() def _check_vpn_keys(self): """ -- cgit v1.2.3 From 6c4012fc128c5af1b75cf33eef00590cf0e82438 Mon Sep 17 00:00:00 2001 From: kali Date: Fri, 31 Aug 2012 04:39:13 +0900 Subject: deprecated configparser. closes #500 --- src/leap/eip/config.py | 151 ++++++------------------------- src/leap/eip/openvpnconnection.py | 69 ++++---------- src/leap/eip/tests/test_config.py | 43 ++++----- src/leap/eip/tests/test_eipconnection.py | 21 ++--- 4 files changed, 74 insertions(+), 210 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py index c0819628..810a5a8d 100644 --- a/src/leap/eip/config.py +++ b/src/leap/eip/config.py @@ -1,18 +1,13 @@ -import ConfigParser # to be deprecated -import json import logging import os import platform -import socket -from leap.util.fileutil import (which, mkdir_p, - check_and_fix_urw_only) +from leap.util.fileutil import (which, check_and_fix_urw_only) from leap.base import config as baseconfig from leap.baseapp.permcheck import (is_pkexec_in_system, is_auth_agent_running) from leap.eip import exceptions as eip_exceptions -from leap.eip import constants as eipconstants from leap.eip import specs as eipspecs logging.basicConfig() @@ -104,7 +99,9 @@ def build_ovpn_options(daemon=False): ourplatform = platform.system() if ourplatform in ("Linux", "Mac"): opts.append('--management') + # XXX get a different sock each time ... + # XXX #505 opts.append('/tmp/.eip.sock') opts.append('unix') if ourplatform == "Windows": @@ -130,14 +127,11 @@ def build_ovpn_options(daemon=False): return opts -def build_ovpn_command(config, debug=False, do_pkexec_check=True): +def build_ovpn_command(debug=False, do_pkexec_check=True, vpnbin=None): """ build a string with the complete openvpn invocation - @param config: config object - @type config: ConfigParser instance - @rtype [string, [list of strings]] @rparam: a list containing the command string and a list of options. @@ -146,11 +140,11 @@ def build_ovpn_command(config, debug=False, do_pkexec_check=True): use_pkexec = True ovpn = None - if config.has_option('openvpn', 'use_pkexec'): - use_pkexec = config.get('openvpn', 'use_pkexec') + # XXX get use_pkexec from config instead. + if platform.system() == "Linux" and use_pkexec and do_pkexec_check: - # XXX check for both pkexec (done) + # check for both pkexec # AND a suitable authentication # agent running. logger.info('use_pkexec set to True') @@ -168,23 +162,15 @@ def build_ovpn_command(config, debug=False, do_pkexec_check=True): raise eip_exceptions.EIPNoPolkitAuthAgentAvailable command.append('pkexec') - - if config.has_option('openvpn', - 'openvpn_binary'): - ovpn = config.get('openvpn', - 'openvpn_binary') - if not ovpn and config.has_option('DEFAULT', - 'openvpn_binary'): - ovpn = config.get('DEFAULT', - 'openvpn_binary') - + if vpnbin is None: + ovpn = which('openvpn') + else: + ovpn = vpnbin if ovpn: vpn_command = ovpn else: vpn_command = "openvpn" - command.append(vpn_command) - daemon_mode = not debug for opt in build_ovpn_options(daemon=daemon_mode): @@ -195,77 +181,7 @@ def build_ovpn_command(config, debug=False, do_pkexec_check=True): return [command[0], command[1:]] -# XXX deprecate -def get_sensible_defaults(): - """ - gathers a dict of sensible defaults, - platform sensitive, - to be used to initialize the config parser - @rtype: dict - @rparam: default options. - """ - - # this way we're passing a simple dict - # that will initialize the configparser - # and will get written to "DEFAULTS" section, - # which is fine for now. - # if we want to write to a particular section - # we can better pass a tuple of triples - # (('section1', 'foo', '23'),) - # and config.set them - - defaults = dict() - defaults['openvpn_binary'] = which('openvpn') - defaults['autostart'] = 'true' - - # TODO - # - management. - return defaults - - -# XXX to be deprecated. see dump_default_eipconfig -# and the new JSONConfig classes. -def get_config(config_file=None): - """ - temporary method for getting configs, - mainly for early stage development process. - in the future we will get preferences - from the storage api - - @rtype: ConfigParser instance - @rparam: a config object - """ - defaults = get_sensible_defaults() - config = ConfigParser.ConfigParser(defaults) - - if not config_file: - fpath = baseconfig.get_config_file('eip.cfg') - if not os.path.isfile(fpath): - dpath, cfile = os.path.split(fpath) - if not os.path.isdir(dpath): - mkdir_p(dpath) - with open(fpath, 'wb') as configfile: - config.write(configfile) - config_file = open(fpath) - config.readfp(config_file) - return config - - -def dump_default_eipconfig(filepath): - """ - writes a sample eip config - in the given location - """ - # XXX TODO: - # use EIPConfigSpec istead - folder, filename = os.path.split(filepath) - if not os.path.isdir(folder): - mkdir_p(folder) - with open(filepath, 'w') as fp: - json.dump(eipconstants.EIP_SAMPLE_JSON, fp) - - -def check_vpn_keys(config): +def check_vpn_keys(): """ performs an existance and permission check over the openvpn keys file. @@ -273,35 +189,24 @@ def check_vpn_keys(config): per provider, containing the CA cert, the provider key, and our client certificate """ + provider_ca = eipspecs.provider_ca_path() + client_cert = eipspecs.client_cert_path() - keyopt = ('provider', 'keyfile') - - # XXX at some point, - # should separate between CA, provider cert - # and our certificate. - # make changes in the default provider template - # accordingly. - - # get vpn keys - if config.has_option(*keyopt): - keyfile = config.get(*keyopt) - else: - keyfile = baseconfig.get_config_file( - 'openvpn.keys', - folder=baseconfig.get_default_provider_path()) - logger.debug('keyfile = %s', keyfile) + logger.debug('provider ca = %s', provider_ca) + logger.debug('client cert = %s', client_cert) # if no keys, raise error. # should be catched by the ui and signal user. - if not os.path.isfile(keyfile): - logger.error('key file %s not found. aborting.', - keyfile) - raise eip_exceptions.EIPInitNoKeyFileError - - # check proper permission on keys - # bad perms? try to fix them - try: - check_and_fix_urw_only(keyfile) - except OSError: - raise eip_exceptions.EIPInitBadKeyFilePermError + for keyfile in (provider_ca, client_cert): + if not os.path.isfile(keyfile): + logger.error('key file %s not found. aborting.', + keyfile) + raise eip_exceptions.EIPInitNoKeyFileError + + # check proper permission on keys + # bad perms? try to fix them + try: + check_and_fix_urw_only(keyfile) + except OSError: + raise eip_exceptions.EIPInitBadKeyFilePermError diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index 1f2f6d8c..32fa55b1 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -77,8 +77,10 @@ to be triggered for each one of them. self.command = None self.args = None + # XXX get autostart from config self.autostart = True - self._get_or_create_config() + #self._get_or_create_config() + self._set_ovpn_command() self._check_vpn_keys() # @@ -95,49 +97,21 @@ to be triggered for each one of them. self.port = port self.password = password - def _set_autostart(self): - config = self.config - if config.has_option('openvpn', 'autostart'): - autostart = config.getboolean('openvpn', - 'autostart') - self.autostart = autostart - else: - if config.has_option('DEFAULT', 'autostart'): - autostart = config.getboolean('DEFAULT', - 'autostart') - self.autostart = autostart - def _set_ovpn_command(self): - config = self.config - if config.has_option('openvpn', 'command'): - commandline = config.get('openvpn', 'command') - - command_split = commandline.split(' ') - command = command_split[0] - if len(command_split) > 1: - args = command_split[1:] - else: - args = [] - - self.command = command - self.args = args - else: - # no command in config, we build it up. # XXX check also for command-line --command flag - try: - command, args = eip_config.build_ovpn_command( - config, - debug=self.debug) - except eip_exceptions.EIPNoPolkitAuthAgentAvailable: - command = args = None - self.missing_auth_agent = True - except eip_exceptions.EIPNoPkexecAvailable: - command = args = None - self.missing_pkexec = True - - # XXX if not command, signal error. - self.command = command - self.args = args + try: + command, args = eip_config.build_ovpn_command( + debug=self.debug) + except eip_exceptions.EIPNoPolkitAuthAgentAvailable: + command = args = None + self.missing_auth_agent = True + except eip_exceptions.EIPNoPkexecAvailable: + command = args = None + self.missing_pkexec = True + + # XXX if not command, signal error. + self.command = command + self.args = args def _get_or_create_config(self): """ @@ -145,19 +119,16 @@ to be triggered for each one of them. home file, or config file passed in command line. populates command and args to be passed to subprocess. """ - config = eip_config.get_config( - config_file=self.config_file) - self.config = config - - self._set_autostart() - self._set_ovpn_command() + # XXX does nothing. + # XXX should get config? or get from checker? + pass def _check_vpn_keys(self): """ checks for correct permissions on vpn keys """ try: - eip_config.check_vpn_keys(self.config) + eip_config.check_vpn_keys() except eip_exceptions.EIPInitNoKeyFileError: self.missing_vpn_keyfile = True except eip_exceptions.EIPInitBadKeyFilePermError: diff --git a/src/leap/eip/tests/test_config.py b/src/leap/eip/tests/test_config.py index 87ef33ef..c73281cc 100644 --- a/src/leap/eip/tests/test_config.py +++ b/src/leap/eip/tests/test_config.py @@ -1,15 +1,16 @@ -import ConfigParser import os import platform +import stat try: import unittest2 as unittest except ImportError: import unittest -from leap.base import constants -from leap.eip import config as eip_config +#from leap.base import constants +#from leap.eip import config as eip_config from leap.testing.basetest import BaseLeapTest +from leap.util.fileutil import mkdir_p _system = platform.system() @@ -29,19 +30,14 @@ class EIPConfigTest(BaseLeapTest): # def touch_exec(self): + path = os.path.join( + self.tempdir, 'bin') + mkdir_p(path) tfile = os.path.join( - self.tempfile, - 'bin', + path, 'openvpn') - open(tfile, 'bw').close() - - def get_empty_config(self): - _config = ConfigParser.ConfigParser() - return _config - - def get_minimal_config(self): - _config = ConfigParser.ConfigParser() - return _config + open(tfile, 'wb').close() + os.chmod(tfile, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR) def get_expected_openvpn_args(self): args = [] @@ -110,17 +106,18 @@ class EIPConfigTest(BaseLeapTest): # some checks. def test_build_ovpn_command_empty_config(self): - _config = self.get_empty_config() - command, args = eip_config.build_ovpn_command( - _config, - do_pkexec_check=False) - self.assertEqual(command, 'openvpn') + self.touch_exec() + from leap.eip import config as eipconfig + from leap.util.fileutil import which + path = os.environ['PATH'] + vpnbin = which('openvpn', path=path) + print 'path =', path + print 'vpnbin = ', vpnbin + command, args = eipconfig.build_ovpn_command( + do_pkexec_check=False, vpnbin=vpnbin) + self.assertEqual(command, self.home + '/bin/openvpn') self.assertEqual(args, self.get_expected_openvpn_args()) - # XXX TODO: - # - should use touch_exec to plant an "executable" in the path - # - should check that "which" for openvpn returns what's expected. - if __name__ == "__main__": unittest.main() diff --git a/src/leap/eip/tests/test_eipconnection.py b/src/leap/eip/tests/test_eipconnection.py index 26f6529e..23f645c3 100644 --- a/src/leap/eip/tests/test_eipconnection.py +++ b/src/leap/eip/tests/test_eipconnection.py @@ -1,4 +1,3 @@ -import ConfigParser import logging import platform import os @@ -13,9 +12,9 @@ except ImportError: from mock import Mock, patch # MagicMock -from leap.base import constants from leap.eip.eipconnection import EIPConnection from leap.eip.exceptions import ConnectionRefusedError +from leap.eip import specs as eipspecs from leap.testing.basetest import BaseLeapTest _system = platform.system() @@ -29,7 +28,6 @@ class NotImplementedError(Exception): @patch('OpenVPNConnection._set_ovpn_command') class MockedEIPConnection(EIPConnection): def _get_or_create_config(self): - self.config = ConfigParser.ConfigParser() self._set_ovpn_command() def _set_ovpn_command(self): @@ -56,11 +54,11 @@ class EIPConductorTest(BaseLeapTest): # XXX change to keys_checker invocation # (see config_checker) - filepath = os.path.expanduser( - '~/.config/leap/providers/%s/openvpn.keys' - % constants.DEFAULT_TEST_PROVIDER) - self.touch(filepath) - self.chmod600(filepath) + keyfiles = (eipspecs.provider_ca_path(), + eipspecs.client_cert_path()) + for filepath in keyfiles: + self.touch(filepath) + self.chmod600(filepath) # we init the manager with only # some methods mocked @@ -85,13 +83,6 @@ class EIPConductorTest(BaseLeapTest): self.assertEqual(con.missing_provider, False) self.assertEqual(con.bad_provider, False) - def test_config_was_init(self): - """ - is there a config object? - """ - self.assertTrue(isinstance(self.con.config, - ConfigParser.ConfigParser)) - def test_ovpn_command(self): """ set_ovpn_command called -- cgit v1.2.3 From b612f422bf156a3b3927038472ad885b1afa556e Mon Sep 17 00:00:00 2001 From: kali Date: Mon, 3 Sep 2012 02:51:41 +0900 Subject: providercertchecks:check_https_is_working implementing a https server with its own base testcase for convenience. https is delicate, and I think it's better checking against a real implementation than mocking everything here. --- src/leap/eip/checks.py | 62 ++++++++++++++++++- src/leap/eip/tests/test_checks.py | 124 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 184 insertions(+), 2 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index c6a7ca72..4112ef57 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -40,12 +40,70 @@ class LeapNetworkChecker(object): class ProviderCertChecker(object): - pass + """ + Several checks needed for getting + client certs and checking tls connection + with provider. + """ + def __init__(self, fetcher=requests): + self.fetcher = fetcher + + def run_all(self, checker=None, skip_download=False): + if not checker: + checker = self + + # For MVS+ + # checker.download_ca_cert() + # checker.download_ca_signature() + # checker.get_ca_signatures() + # checker.is_there_trust_path() + + # For MVS + checker.is_there_provider_ca() + checker.is_https_working() + checker.download_new_client_cert() + + def download_ca_cert(self): + # MVS+ + raise NotImplementedError + + def download_ca_signature(self): + # MVS+ + raise NotImplementedError + + def get_ca_signatures(self): + # MVS+ + raise NotImplementedError + + def is_there_trust_path(self): + # MVS+ + raise NotImplementedError + + def is_there_provider_ca(self): + # XXX fake it till you make it! :P + return True + + # enable this when we have + # a custom "branded" bundle + # certs package. + try: + from leap.custom import certs + certs.ca.pemfile + except ImportError: + raise + + def is_https_working(self, uri=None, cacert=None, verify=True): + assert uri.startswith('https') + self.fetcher.get(uri, verify=verify) + return True + + def download_new_client_cert(self): + return True class EIPConfigChecker(object): """ - Several tests needed + Several checks needed to ensure a EIPConnection can be sucessfully established. use run_all to run all checks. diff --git a/src/leap/eip/tests/test_checks.py b/src/leap/eip/tests/test_checks.py index 1e629203..781fdad5 100644 --- a/src/leap/eip/tests/test_checks.py +++ b/src/leap/eip/tests/test_checks.py @@ -1,3 +1,4 @@ +from BaseHTTPServer import BaseHTTPRequestHandler import copy import json try: @@ -18,6 +19,16 @@ from leap.eip import specs as eipspecs from leap.eip import exceptions as eipexceptions from leap.eip.tests import data as testdata from leap.testing.basetest import BaseLeapTest +from leap.testing.https_server import BaseHTTPSServerTestCase + + +class NoLogRequestHandler: + def log_message(self, *args): + # don't write log msg to stderr + pass + + def read(self, n=None): + return '' class EIPCheckTest(BaseLeapTest): @@ -157,5 +168,118 @@ class EIPCheckTest(BaseLeapTest): sampleconfig = copy.copy(testdata.EIP_SAMPLE_JSON) checker.check_complete_eip_config(config=sampleconfig) + +class ProviderCertCheckerTest(BaseLeapTest): + + __name__ = "provider_cert_checker_tests" + + def setUp(self): + pass + + def tearDown(self): + pass + + # test methods are there, and can be called from run_all + + def test_checker_should_implement_check_methods(self): + checker = eipchecks.ProviderCertChecker() + + # For MVS+ + self.assertTrue(hasattr(checker, "download_ca_cert"), + "missing meth") + self.assertTrue(hasattr(checker, "download_ca_signature"), + "missing meth") + self.assertTrue(hasattr(checker, "get_ca_signatures"), "missing meth") + self.assertTrue(hasattr(checker, "is_there_trust_path"), + "missing meth") + + # For MVS + self.assertTrue(hasattr(checker, "is_there_provider_ca"), + "missing meth") + self.assertTrue(hasattr(checker, "is_https_working"), "missing meth") + self.assertTrue(hasattr(checker, "download_new_client_cert"), + "missing meth") + + def test_checker_should_actually_call_all_tests(self): + checker = eipchecks.ProviderCertChecker() + + mc = Mock() + checker.run_all(checker=mc) + # XXX MVS+ + #self.assertTrue(mc.download_ca_cert.called, "not called") + #self.assertTrue(mc.download_ca_signature.called, "not called") + #self.assertTrue(mc.get_ca_signatures.called, "not called") + #self.assertTrue(mc.is_there_trust_path.called, "not called") + + # For MVS + self.assertTrue(mc.is_there_provider_ca.called, "not called") + self.assertTrue(mc.is_https_working.called, + "not called") + self.assertTrue(mc.download_new_client_cert.called, + "not called") + + # test individual check methods + + def test_is_there_provider_ca(self): + checker = eipchecks.ProviderCertChecker() + self.assertTrue( + checker.is_there_provider_ca()) + + +class ProviderCertCheckerHTTPSTests(BaseHTTPSServerTestCase): + class request_handler(NoLogRequestHandler, BaseHTTPRequestHandler): + def do_GET(self): + #XXX use path to deliver foo stuff + #path = urlparse.urlparse(self.path) + #print path + message = '\n'.join([ + 'OK', + '']) + self.send_response(200) + self.end_headers() + self.wfile.write(message) + + def test_is_https_working(self): + fetcher = requests + uri = "https://%s/" % (self.get_server()) + # bare requests call. this should just pass (if there is + # an https service there). + fetcher.get(uri, verify=False) + checker = eipchecks.ProviderCertChecker(fetcher=fetcher) + self.assertTrue(checker.is_https_working(uri=uri, verify=False)) + + # for local debugs, when in doubt + #self.assertTrue(checker.is_https_working(uri="https://github.com", + #verify=True)) + + # for the two checks below, I know they fail because no ca + # cert is passed to them, and I know that's the error that + # requests return with our implementation. However, I believe + # the right error should be SSL23_READ_BYTES: alert bad certificate + # or something similar. I guess we're receiving this because our + # server is dying prematurely when the handshake is interrupted on the + # client side. In any case I think that requests could handle + # this error more consistently and return a ConnectionError on a + # higher level. + with self.assertRaises(requests.exceptions.SSLError) as exc: + fetcher.get(uri, verify=True) + self.assertTrue( + "SSL23_GET_SERVER_HELLO:unknown protocol" in exc.message) + with self.assertRaises(requests.exceptions.SSLError) as exc: + checker.is_https_working(uri=uri, verify=True) + self.assertTrue( + "SSL23_GET_SERVER_HELLO:unknown protocol" in exc.message) + + # XXX get cacert from testing.https_server + + def test_download_new_client_cert(self): + checker = eipchecks.ProviderCertChecker() + self.assertTrue(checker.download_new_client_cert()) + + #def test_download_bad_client_cert(self): + #checker = eipchecks.ProviderCertChecker() + #self.assertTrue(checker.download_new_client_cert()) + + if __name__ == "__main__": unittest.main() -- cgit v1.2.3 From 090aed5e7c569e07b14d74ca71068a277cc39152 Mon Sep 17 00:00:00 2001 From: kali Date: Mon, 3 Sep 2012 04:18:15 +0900 Subject: basic download cert functionality --- src/leap/eip/checks.py | 58 +++++++++++++++++++++++++++++++---- src/leap/eip/tests/test_checks.py | 63 ++++++++++++++++++++++++++++----------- 2 files changed, 99 insertions(+), 22 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index 4112ef57..b0fd6323 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -1,6 +1,7 @@ -import json +#import json import logging -import os +import ssl +#import os logging.basicConfig() logger = logging.getLogger(name=__name__) @@ -13,6 +14,7 @@ from leap.base import providers from leap.eip import config as eipconfig from leap.eip import constants as eipconstants from leap.eip import exceptions as eipexceptions +from leap.eip import specs as eipspecs """ EIPConfigChecker @@ -47,6 +49,7 @@ class ProviderCertChecker(object): """ def __init__(self, fetcher=requests): self.fetcher = fetcher + self.cacert = None def run_all(self, checker=None, skip_download=False): if not checker: @@ -88,18 +91,63 @@ class ProviderCertChecker(object): # certs package. try: from leap.custom import certs - certs.ca.pemfile except ImportError: raise + self.cacert = certs.where('cacert.pem') - def is_https_working(self, uri=None, cacert=None, verify=True): + def is_https_working(self, uri=None, verify=True): + # XXX raise InsecureURI or something better assert uri.startswith('https') + if verify is True and self.cacert is not None: + verify = self.cacert self.fetcher.get(uri, verify=verify) return True - def download_new_client_cert(self): + def download_new_client_cert(self, uri=None, verify=True): + if uri is None: + uri = self._get_client_cert_uri() + # XXX raise InsecureURI or something better + assert uri.startswith('https') + if verify is True and self.cacert is not None: + verify = self.cacert + req = self.fetcher.get(uri, verify=verify) + pemfile_content = req.content + self.validate_pemfile(pemfile_content) + cert_path = self._get_client_cert_path() + self.write_cert(pemfile_content, to=cert_path) return True + def validate_pemfile(self, cert_s): + """ + checks that the passed string + is a valid pem certificate + @param cert_s: string containing pem content + @type cert_s: string + @rtype: bool + """ + try: + # XXX get a real cert validation + # so far this is only checking begin/end + # delimiters :) + ssl.PEM_cert_to_DER_cert(cert_s) + except: + # XXX raise proper exception + raise + return True + + def _get_client_cert_uri(self): + # XXX TODO + # get from provider definition? + pass + + def _get_client_cert_path(self): + # MVS+ : get provider path + return eipspecs.client_cert_path() + + def write_cert(self, pemfile_content, to=None): + with open(to, 'w') as cert_f: + cert_f.write(pemfile_content) + class EIPConfigChecker(object): """ diff --git a/src/leap/eip/tests/test_checks.py b/src/leap/eip/tests/test_checks.py index 781fdad5..541b884b 100644 --- a/src/leap/eip/tests/test_checks.py +++ b/src/leap/eip/tests/test_checks.py @@ -6,6 +6,7 @@ try: except ImportError: import unittest import os +import urlparse from mock import patch, Mock @@ -20,6 +21,7 @@ from leap.eip import exceptions as eipexceptions from leap.eip.tests import data as testdata from leap.testing.basetest import BaseLeapTest from leap.testing.https_server import BaseHTTPSServerTestCase +from leap.testing.https_server import where as where_cert class NoLogRequestHandler: @@ -228,13 +230,18 @@ class ProviderCertCheckerTest(BaseLeapTest): class ProviderCertCheckerHTTPSTests(BaseHTTPSServerTestCase): class request_handler(NoLogRequestHandler, BaseHTTPRequestHandler): + responses = { + '/': ['OK', ''], + '/client.cert': [ + '-----BEGIN CERTIFICATE-----', + '-----END CERTIFICATE-----'], + '/badclient.cert': [ + 'BADCERT']} + def do_GET(self): - #XXX use path to deliver foo stuff - #path = urlparse.urlparse(self.path) - #print path - message = '\n'.join([ - 'OK', - '']) + path = urlparse.urlparse(self.path) + message = '\n'.join(self.responses.get( + path.path, None)) self.send_response(200) self.end_headers() self.wfile.write(message) @@ -254,13 +261,13 @@ class ProviderCertCheckerHTTPSTests(BaseHTTPSServerTestCase): # for the two checks below, I know they fail because no ca # cert is passed to them, and I know that's the error that - # requests return with our implementation. However, I believe - # the right error should be SSL23_READ_BYTES: alert bad certificate - # or something similar. I guess we're receiving this because our + # requests return with our implementation. + # We're receiving this because our # server is dying prematurely when the handshake is interrupted on the - # client side. In any case I think that requests could handle - # this error more consistently and return a ConnectionError on a - # higher level. + # client side. + # Since we have access to the server, we could check that + # the error raised has been: + # SSL23_READ_BYTES: alert bad certificate with self.assertRaises(requests.exceptions.SSLError) as exc: fetcher.get(uri, verify=True) self.assertTrue( @@ -270,15 +277,37 @@ class ProviderCertCheckerHTTPSTests(BaseHTTPSServerTestCase): self.assertTrue( "SSL23_GET_SERVER_HELLO:unknown protocol" in exc.message) - # XXX get cacert from testing.https_server + # get cacert from testing.https_server + cacert = where_cert('cacert.pem') + fetcher.get(uri, verify=cacert) + self.assertTrue(checker.is_https_working(uri=uri, verify=cacert)) + + # same, but get cacert from leap.custom + # XXX TODO! def test_download_new_client_cert(self): + uri = "https://%s/client.cert" % (self.get_server()) + cacert = where_cert('cacert.pem') checker = eipchecks.ProviderCertChecker() - self.assertTrue(checker.download_new_client_cert()) + self.assertTrue(checker.download_new_client_cert( + uri=uri, verify=cacert)) - #def test_download_bad_client_cert(self): - #checker = eipchecks.ProviderCertChecker() - #self.assertTrue(checker.download_new_client_cert()) + # now download a malformed cert + uri = "https://%s/badclient.cert" % (self.get_server()) + cacert = where_cert('cacert.pem') + checker = eipchecks.ProviderCertChecker() + with self.assertRaises(ValueError): + self.assertTrue(checker.download_new_client_cert( + uri=uri, verify=cacert)) + + # did we write cert to its path? + self.assertTrue(os.path.isfile(eipspecs.client_cert_path())) + certfile = eipspecs.client_cert_path() + with open(certfile, 'r') as cf: + certcontent = cf.read() + self.assertEqual(certcontent, + '\n'.join( + self.request_handler.responses['/client.cert'])) if __name__ == "__main__": -- cgit v1.2.3 From 37d7e272b7f8a649034a0cf60f6c4a1424bf767a Mon Sep 17 00:00:00 2001 From: kali Date: Tue, 4 Sep 2012 01:08:05 +0900 Subject: better separate cert validation/download logic stubbing out the timestamp validity check (waiting for #507) also some more deep tests are missing, wrote todo in tests. --- src/leap/eip/checks.py | 58 +++++++++++++++++++++++++++++++++------ src/leap/eip/tests/test_checks.py | 24 ++++++++++++++-- 2 files changed, 71 insertions(+), 11 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index b0fd6323..51a7e219 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -1,7 +1,7 @@ #import json import logging import ssl -#import os +import os logging.basicConfig() logger = logging.getLogger(name=__name__) @@ -64,7 +64,8 @@ class ProviderCertChecker(object): # For MVS checker.is_there_provider_ca() checker.is_https_working() - checker.download_new_client_cert() + checker.check_new_cert_needed() + #checker.download_new_client_cert() def download_ca_cert(self): # MVS+ @@ -103,7 +104,16 @@ class ProviderCertChecker(object): self.fetcher.get(uri, verify=verify) return True - def download_new_client_cert(self, uri=None, verify=True): + def check_new_cert_needed(self, skip_download=False): + if not self.is_cert_valid(do_raise=False): + self.download_new_client_cert(skip_download=skip_download) + return True + return False + + def download_new_client_cert(self, uri=None, verify=True, + skip_download=False): + if skip_download: + return True if uri is None: uri = self._get_client_cert_uri() # XXX raise InsecureURI or something better @@ -112,12 +122,39 @@ class ProviderCertChecker(object): verify = self.cacert req = self.fetcher.get(uri, verify=verify) pemfile_content = req.content - self.validate_pemfile(pemfile_content) + self.is_valid_pemfile(pemfile_content) cert_path = self._get_client_cert_path() self.write_cert(pemfile_content, to=cert_path) return True - def validate_pemfile(self, cert_s): + def is_cert_valid(self, cert_path=None, do_raise=True): + exists = lambda: self.is_certificate_exists() + valid_pemfile = lambda: self.is_valid_pemfile() + not_expired = lambda: self.is_cert_not_expired() + print 'exists?', exists + print 'valid', valid_pemfile + print 'not expired', not_expired + + valid = exists() and valid_pemfile() and not_expired() + if not valid: + if do_raise: + raise Exception('missing cert') + else: + return False + return True + + def is_certificate_exists(self, certfile=None): + if certfile is None: + certfile = self._get_client_cert_path() + return os.path.isfile(certfile) + + def is_cert_not_expired(self): + return True + # XXX TODO + # waiting on #507. If we're not using PyOpenSSL or anything alike + # we will have to roll our own x509 parsing to extract time info. + + def is_valid_pemfile(self, cert_s=None): """ checks that the passed string is a valid pem certificate @@ -125,6 +162,10 @@ class ProviderCertChecker(object): @type cert_s: string @rtype: bool """ + if cert_s is None: + certfile = self._get_client_cert_path() + with open(certfile) as cf: + cert_s = cf.read() try: # XXX get a real cert validation # so far this is only checking begin/end @@ -136,14 +177,15 @@ class ProviderCertChecker(object): return True def _get_client_cert_uri(self): - # XXX TODO - # get from provider definition? - pass + return "https://%s/cert/get" % (baseconstants.DEFAULT_TEST_PROVIDER) def _get_client_cert_path(self): # MVS+ : get provider path return eipspecs.client_cert_path() + def is_cert_still_valid(self): + raise NotImplementedError + def write_cert(self, pemfile_content, to=None): with open(to, 'w') as cert_f: cert_f.write(pemfile_content) diff --git a/src/leap/eip/tests/test_checks.py b/src/leap/eip/tests/test_checks.py index 541b884b..09fdaabf 100644 --- a/src/leap/eip/tests/test_checks.py +++ b/src/leap/eip/tests/test_checks.py @@ -199,7 +199,7 @@ class ProviderCertCheckerTest(BaseLeapTest): self.assertTrue(hasattr(checker, "is_there_provider_ca"), "missing meth") self.assertTrue(hasattr(checker, "is_https_working"), "missing meth") - self.assertTrue(hasattr(checker, "download_new_client_cert"), + self.assertTrue(hasattr(checker, "check_new_cert_needed"), "missing meth") def test_checker_should_actually_call_all_tests(self): @@ -217,7 +217,7 @@ class ProviderCertCheckerTest(BaseLeapTest): self.assertTrue(mc.is_there_provider_ca.called, "not called") self.assertTrue(mc.is_https_working.called, "not called") - self.assertTrue(mc.download_new_client_cert.called, + self.assertTrue(mc.check_new_cert_needed.called, "not called") # test individual check methods @@ -233,6 +233,7 @@ class ProviderCertCheckerHTTPSTests(BaseHTTPSServerTestCase): responses = { '/': ['OK', ''], '/client.cert': [ + # XXX get sample cert '-----BEGIN CERTIFICATE-----', '-----END CERTIFICATE-----'], '/badclient.cert': [ @@ -301,13 +302,30 @@ class ProviderCertCheckerHTTPSTests(BaseHTTPSServerTestCase): uri=uri, verify=cacert)) # did we write cert to its path? - self.assertTrue(os.path.isfile(eipspecs.client_cert_path())) + clientcertfile = eipspecs.client_cert_path() + self.assertTrue(os.path.isfile(clientcertfile)) certfile = eipspecs.client_cert_path() with open(certfile, 'r') as cf: certcontent = cf.read() self.assertEqual(certcontent, '\n'.join( self.request_handler.responses['/client.cert'])) + os.remove(clientcertfile) + + def test_is_cert_valid(self): + checker = eipchecks.ProviderCertChecker() + # TODO: better exception catching + with self.assertRaises(Exception) as exc: + self.assertFalse(checker.is_cert_valid()) + exc.message = "missing cert" + + def test_check_new_cert_needed(self): + # check: missing cert + checker = eipchecks.ProviderCertChecker() + self.assertTrue(checker.check_new_cert_needed(skip_download=True)) + # TODO check: malformed cert + # TODO check: expired cert + # TODO check: pass test server uri instead of skip if __name__ == "__main__": -- cgit v1.2.3 From 83a3fed0d38e44e64cec027f9fd2fcd5a894f96a Mon Sep 17 00:00:00 2001 From: kali Date: Tue, 4 Sep 2012 03:18:13 +0900 Subject: fix test_checks: do not mess with real home path! It is really dangerous to mess with expanduser paths in tests without deriving testcases from LeapTestCase. It'd be good to devise a way of checking for that :( --- src/leap/eip/checks.py | 15 ++++++++------- src/leap/eip/tests/test_checks.py | 2 +- 2 files changed, 9 insertions(+), 8 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index 51a7e219..1b7c2e1b 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -15,6 +15,7 @@ from leap.eip import config as eipconfig from leap.eip import constants as eipconstants from leap.eip import exceptions as eipexceptions from leap.eip import specs as eipspecs +from leap.util.fileutil import mkdir_p """ EIPConfigChecker @@ -33,7 +34,6 @@ reachable and testable as a whole. Other related checkers - not implemented yet -: * LeapNetworkChecker -* ProviderCertChecker """ @@ -131,9 +131,9 @@ class ProviderCertChecker(object): exists = lambda: self.is_certificate_exists() valid_pemfile = lambda: self.is_valid_pemfile() not_expired = lambda: self.is_cert_not_expired() - print 'exists?', exists - print 'valid', valid_pemfile - print 'not expired', not_expired + #print 'exists?', exists + #print 'valid', valid_pemfile + #print 'not expired', not_expired valid = exists() and valid_pemfile() and not_expired() if not valid: @@ -181,12 +181,13 @@ class ProviderCertChecker(object): def _get_client_cert_path(self): # MVS+ : get provider path + #import ipdb;ipdb.set_trace() return eipspecs.client_cert_path() - def is_cert_still_valid(self): - raise NotImplementedError - def write_cert(self, pemfile_content, to=None): + folder, filename = os.path.split(to) + if not os.path.isdir(folder): + mkdir_p(folder) with open(to, 'w') as cert_f: cert_f.write(pemfile_content) diff --git a/src/leap/eip/tests/test_checks.py b/src/leap/eip/tests/test_checks.py index 09fdaabf..0a87f573 100644 --- a/src/leap/eip/tests/test_checks.py +++ b/src/leap/eip/tests/test_checks.py @@ -228,7 +228,7 @@ class ProviderCertCheckerTest(BaseLeapTest): checker.is_there_provider_ca()) -class ProviderCertCheckerHTTPSTests(BaseHTTPSServerTestCase): +class ProviderCertCheckerHTTPSTests(BaseHTTPSServerTestCase, BaseLeapTest): class request_handler(NoLogRequestHandler, BaseHTTPRequestHandler): responses = { '/': ['OK', ''], -- cgit v1.2.3 From a2804c3de1470db98d8c6aa8a01e2de1aa1718a1 Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 5 Sep 2012 07:42:10 +0900 Subject: app wide logging handler --- src/leap/eip/checks.py | 7 ++----- src/leap/eip/config.py | 2 -- src/leap/eip/eipconnection.py | 6 ++---- src/leap/eip/openvpnconnection.py | 6 +++--- 4 files changed, 7 insertions(+), 14 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index 1b7c2e1b..4a2a9599 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -1,12 +1,7 @@ -#import json import logging import ssl import os -logging.basicConfig() -logger = logging.getLogger(name=__name__) -logger.setLevel(logging.DEBUG) - import requests from leap.base import constants as baseconstants @@ -17,6 +12,8 @@ from leap.eip import exceptions as eipexceptions from leap.eip import specs as eipspecs from leap.util.fileutil import mkdir_p +logger = logging.getLogger(name=__name__) + """ EIPConfigChecker ---------- diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py index 810a5a8d..f4b979ce 100644 --- a/src/leap/eip/config.py +++ b/src/leap/eip/config.py @@ -10,9 +10,7 @@ from leap.baseapp.permcheck import (is_pkexec_in_system, from leap.eip import exceptions as eip_exceptions from leap.eip import specs as eipspecs -logging.basicConfig() logger = logging.getLogger(name=__name__) -logger.setLevel('DEBUG') class EIPConfig(baseconfig.JSONLeapConfig): diff --git a/src/leap/eip/eipconnection.py b/src/leap/eip/eipconnection.py index 386b71be..3a6f4d49 100644 --- a/src/leap/eip/eipconnection.py +++ b/src/leap/eip/eipconnection.py @@ -4,14 +4,12 @@ EIP Connection Class from __future__ import (absolute_import,) import logging -logging.basicConfig() -logger = logging.getLogger(name=__name__) -logger.setLevel(logging.DEBUG) - from leap.eip.checks import EIPConfigChecker from leap.eip import exceptions as eip_exceptions from leap.eip.openvpnconnection import OpenVPNConnection +logger = logging.getLogger(name=__name__) + class EIPConnection(OpenVPNConnection): """ diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index 32fa55b1..48252e10 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -7,9 +7,7 @@ import socket import time from functools import partial -logging.basicConfig() logger = logging.getLogger(name=__name__) -logger.setLevel(logging.DEBUG) from leap.base.connection import Connection from leap.util.coroutines import spawn_and_watch_process @@ -45,6 +43,7 @@ to be triggered for each one of them. :type watcher_cb: function :type signal_map: dict """ + logger.debug('init openvpn connection') self.debug = debug #print('conductor:%s' % debug) @@ -192,7 +191,8 @@ to be triggered for each one of them. # def forget_errors(self): - print('forgetting errors') + #print('forgetting errors') + logger.debug('forgetting errors') self.with_errors = False def connect_to_management(self): -- cgit v1.2.3 From c190b7f66cc1977d0e058bfa2d8fc1a850326320 Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 5 Sep 2012 10:23:24 +0900 Subject: missing_pkexec error converted to "auto" error. idea is that we define user messages in the exceptions, and queue them during (conductor) checks. user facing dialogs get constucted from exception attrs. if critical, log as such and exit. --- src/leap/eip/eipconnection.py | 38 +++++++++++++---------- src/leap/eip/exceptions.py | 64 +++++++++++++++++++++------------------ src/leap/eip/openvpnconnection.py | 16 +++++++--- 3 files changed, 67 insertions(+), 51 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/eipconnection.py b/src/leap/eip/eipconnection.py index 3a6f4d49..e090f9a7 100644 --- a/src/leap/eip/eipconnection.py +++ b/src/leap/eip/eipconnection.py @@ -3,6 +3,7 @@ EIP Connection Class """ from __future__ import (absolute_import,) import logging +import Queue from leap.eip.checks import EIPConfigChecker from leap.eip import exceptions as eip_exceptions @@ -23,8 +24,8 @@ class EIPConnection(OpenVPNConnection): self.settingsfile = kwargs.get('settingsfile', None) self.logfile = kwargs.get('logfile', None) - # not used atm. but should. - self.error_queue = [] + # XXX USE THIS + self.error_queue = Queue.Queue() status_signals = kwargs.pop('status_signals', None) self.status = EIPConnectionStatus(callbacks=status_signals) @@ -36,7 +37,12 @@ class EIPConnection(OpenVPNConnection): """ run all eip checks previous to attempting a connection """ - self.config_checker.run_all(skip_download=skip_download) + logger.debug('running conductor checks') + try: + self.config_checker.run_all(skip_download=skip_download) + self.run_openvpn_checks() + except Exception as exc: + self.error_queue.put(exc) def connect(self): """ @@ -44,7 +50,6 @@ class EIPConnection(OpenVPNConnection): """ self.forget_errors() self._try_connection() - # XXX should capture errors? def disconnect(self): """ @@ -65,11 +70,11 @@ class EIPConnection(OpenVPNConnection): """ return self.status.current - def desired_connection_state(self): - """ - returns the desired_connection state - """ - return self.desired_con_state + #def desired_connection_state(self): + #""" + #returns the desired_connection state + #""" + #return self.desired_con_state def poll_connection_state(self): """ @@ -107,26 +112,27 @@ class EIPConnection(OpenVPNConnection): private method for disconnecting """ if self.subp is not None: + logger.debug('disconnecting...') self.subp.terminate() self.subp = None - # XXX signal state changes! :) - def _is_alive(self): - """ - don't know yet - """ - pass + #def _is_alive(self): + #""" + #don't know yet + #""" + #pass def _connect(self): """ entry point for connection cascade methods. """ - #conn_result = ConState.DISCONNECTED try: conn_result = self._try_connection() except eip_exceptions.UnrecoverableError as except_msg: logger.error("FATAL: %s" % unicode(except_msg)) conn_result = self.status.UNRECOVERABLE + + # XXX enqueue exceptions themselves instead? except Exception as except_msg: self.error_queue.append(except_msg) logger.error("Failed Connection: %s" % diff --git a/src/leap/eip/exceptions.py b/src/leap/eip/exceptions.py index 19a0e707..a30cd2a6 100644 --- a/src/leap/eip/exceptions.py +++ b/src/leap/eip/exceptions.py @@ -1,68 +1,72 @@ -class EIPNoCommandError(Exception): - pass - - -class ConnectionError(Exception): - """ - generic connection error - """ - pass - - class EIPClientError(Exception): """ base EIPClient exception """ - def __str__(self): - if len(self.args) >= 1: - return repr(self.args[0]) - else: - return ConnectionError + # Should inherit from LeapException + # and move basic attrs there + critical = False + + #def __str__(self): + #if len(self.args) >= 1: + #return repr(self.args[0]) + #else: + #return ConnectionError -class UnrecoverableError(EIPClientError): +class CriticalError(EIPClientError): """ we cannot do anything about it, sorry """ - # XXX we should catch this and raise - # to qtland, so we emit signal - # to translate whatever kind of error - # to user-friendly msg in dialog. - pass + critical = True -class MissingSocketError(Exception): +class EIPNoPolkitAuthAgentAvailable(CriticalError): + message = "No polkit authentication agent could be found" + usermessage = ("We could not find any authentication " + "agent in your system.
" + "Make sure you have " + "polkit-gnome-authentication-agent-1 " + "running and try again.") + +# Errors needing some work + + +class EIPNoPkexecAvailable(Exception): pass -class ConnectionRefusedError(Exception): +class EIPInitNoProviderError(Exception): pass -class EIPNoPkexecAvailable(Exception): +class EIPInitBadProviderError(Exception): pass -class EIPNoPolkitAuthAgentAvailable(Exception): +class EIPInitNoKeyFileError(Exception): pass -class EIPInitNoProviderError(Exception): +class EIPInitBadKeyFilePermError(Exception): pass -class EIPInitBadProviderError(Exception): +class EIPNoCommandError(Exception): pass +# Errors that probably we don't need anymore -class EIPInitNoKeyFileError(Exception): + +class MissingSocketError(Exception): pass -class EIPInitBadKeyFilePermError(Exception): +class ConnectionRefusedError(Exception): pass + + class EIPMissingDefaultProvider(Exception): pass diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index 48252e10..4a6a495a 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -22,7 +22,6 @@ class OpenVPNConnection(Connection): All related to invocation of the openvpn binary """ - # Connection Methods def __init__(self, config_file=None, watcher_cb=None, @@ -45,7 +44,6 @@ to be triggered for each one of them. """ logger.debug('init openvpn connection') self.debug = debug - #print('conductor:%s' % debug) self.config_file = config_file self.watcher_cb = watcher_cb @@ -58,15 +56,18 @@ to be triggered for each one of them. self.port = None self.proto = None + ################################## # XXX move all error messages # into a more encapsulated object. self.missing_pkexec = False self.missing_auth_agent = False + self.bad_keyfile_perms = False self.missing_vpn_keyfile = False self.missing_provider = False self.missing_definition = False self.bad_provider = False + ################################# #XXX workaround for signaling #the ui that we don't know how to @@ -78,9 +79,6 @@ to be triggered for each one of them. # XXX get autostart from config self.autostart = True - #self._get_or_create_config() - self._set_ovpn_command() - self._check_vpn_keys() # # management init methods @@ -96,6 +94,11 @@ to be triggered for each one of them. self.port = port self.password = password + def run_openvpn_checks(self): + logger.debug('running openvpn checks') + self._set_ovpn_command() + self._check_vpn_keys() + def _set_ovpn_command(self): # XXX check also for command-line --command flag try: @@ -103,10 +106,13 @@ to be triggered for each one of them. debug=self.debug) except eip_exceptions.EIPNoPolkitAuthAgentAvailable: command = args = None + # XXX deprecate self.missing_auth_agent = True + raise except eip_exceptions.EIPNoPkexecAvailable: command = args = None self.missing_pkexec = True + raise # XXX if not command, signal error. self.command = command -- cgit v1.2.3 From 8148bc9c8c113c41fcb18b397669b1f13447c653 Mon Sep 17 00:00:00 2001 From: kali Date: Thu, 6 Sep 2012 02:27:04 +0900 Subject: more generic error handler in EipConductorAppMixin documentation of the Exception Hierarchy and attributes. also a bit of general cleanup around error handling in conductor. Hopefully to be polished an abstracted to leap.base with time. not all errors are converted (and the old with_errors/ignoring errors) are still there, but we should be using this style of handlers from now on. wrapping up with this pseudo-feature for now. as we work on individual features we can mimick the exceptions that are working. --- src/leap/eip/eipconnection.py | 4 +- src/leap/eip/exceptions.py | 97 +++++++++++++++++++++++++++++++++---------- 2 files changed, 77 insertions(+), 24 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/eipconnection.py b/src/leap/eip/eipconnection.py index e090f9a7..5c54a986 100644 --- a/src/leap/eip/eipconnection.py +++ b/src/leap/eip/eipconnection.py @@ -24,7 +24,6 @@ class EIPConnection(OpenVPNConnection): self.settingsfile = kwargs.get('settingsfile', None) self.logfile = kwargs.get('logfile', None) - # XXX USE THIS self.error_queue = Queue.Queue() status_signals = kwargs.pop('status_signals', None) @@ -33,6 +32,9 @@ class EIPConnection(OpenVPNConnection): super(EIPConnection, self).__init__(*args, **kwargs) + def has_errors(self): + return True if self.error_queue.qsize != 0 else True + def run_checks(self, skip_download=False): """ run all eip checks previous to attempting a connection diff --git a/src/leap/eip/exceptions.py b/src/leap/eip/exceptions.py index a30cd2a6..3c8f6afb 100644 --- a/src/leap/eip/exceptions.py +++ b/src/leap/eip/exceptions.py @@ -1,23 +1,60 @@ +""" +Generic error hierarchy +Leap/EIP exceptions used for exception handling, +logging, and notifying user of errors +during leap operation. + +Exception hierarchy +------------------- +All EIP Errors must inherit from EIPClientError (note: move that to +a more generic LEAPClientBaseError). + +Exception attributes and their meaning/uses +------------------------------------------- + +* critical: if True, will abort execution prematurely, + after attempting any cleaning + action. + +* failfirst: breaks any error_check loop that is examining + the error queue. + +* message: the message that will be used in the __repr__ of the exception. + +* usermessage: the message that will be passed to user in ErrorDialogs + in Qt-land. + +TODO: + +* EIPClientError: + Should inherit from LeapException + and move basic attrs there + +* gettext / i18n for user messages. + +""" + + class EIPClientError(Exception): """ base EIPClient exception """ - # Should inherit from LeapException - # and move basic attrs there critical = False - #def __str__(self): - #if len(self.args) >= 1: - #return repr(self.args[0]) - #else: - #return ConnectionError - class CriticalError(EIPClientError): """ we cannot do anything about it, sorry """ critical = True + failfirst = True + + +class Warning(EIPClientError): + """ + just that, warnings + """ + pass class EIPNoPolkitAuthAgentAvailable(CriticalError): @@ -28,33 +65,53 @@ class EIPNoPolkitAuthAgentAvailable(CriticalError): "polkit-gnome-authentication-agent-1 " "running and try again.") -# Errors needing some work +class EIPNoPkexecAvailable(Warning): + message = "No pkexec binary found" + usermessage = ("We could not find pkexec in your " + "system.
Do you want to try " + "setuid workaround? " + "(DOES NOTHING YET)") + failfirst = True -class EIPNoPkexecAvailable(Exception): - pass +class EIPNoCommandError(EIPClientError): + message = "no suitable openvpn command found" + usermessage = ("No suitable openvpn command found. " + "
(Might be a permissions problem)") -class EIPInitNoProviderError(Exception): - pass +# +# errors still needing some love +# + +class EIPInitNoKeyFileError(CriticalError): + message = "No vpn keys found in the expected path" + usermessage = "We could not find your eip certs in the expected path" -class EIPInitBadProviderError(Exception): + +class EIPInitBadKeyFilePermError(Warning): + # I don't know if we should be telling user or not, + # we try to fix permissions and should only re-raise + # if permission check failed. pass -class EIPInitNoKeyFileError(Exception): +class EIPInitNoProviderError(EIPClientError): pass -class EIPInitBadKeyFilePermError(Exception): +class EIPInitBadProviderError(EIPClientError): pass -class EIPNoCommandError(Exception): +class EIPConfigurationError(EIPClientError): pass +# # Errors that probably we don't need anymore +# chase down for them and check. +# class MissingSocketError(Exception): @@ -65,11 +122,5 @@ class ConnectionRefusedError(Exception): pass - - class EIPMissingDefaultProvider(Exception): pass - - -class EIPConfigurationError(Exception): - pass -- cgit v1.2.3 From 5756a05eb9b66e46df55544d224e2dce7c312452 Mon Sep 17 00:00:00 2001 From: kali Date: Thu, 6 Sep 2012 03:14:36 +0900 Subject: fix silly return mistake on has_errors method --- src/leap/eip/eipconnection.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/eipconnection.py b/src/leap/eip/eipconnection.py index 5c54a986..ff71dc76 100644 --- a/src/leap/eip/eipconnection.py +++ b/src/leap/eip/eipconnection.py @@ -33,7 +33,7 @@ class EIPConnection(OpenVPNConnection): super(EIPConnection, self).__init__(*args, **kwargs) def has_errors(self): - return True if self.error_queue.qsize != 0 else True + return True if self.error_queue.qsize() != 0 else False def run_checks(self, skip_download=False): """ -- cgit v1.2.3 From 75f4128f5ed515c4df57275bf1479ccdf741c83f Mon Sep 17 00:00:00 2001 From: kali Date: Thu, 6 Sep 2012 03:15:53 +0900 Subject: make tests pass. forgot to update eipconnection tests after #504 changes :( --- src/leap/eip/openvpnconnection.py | 34 +++++++++++--------------------- src/leap/eip/tests/test_eipconnection.py | 8 +------- 2 files changed, 13 insertions(+), 29 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index 4a6a495a..ec8b48bf 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -57,16 +57,16 @@ to be triggered for each one of them. self.proto = None ################################## - # XXX move all error messages - # into a more encapsulated object. - self.missing_pkexec = False - self.missing_auth_agent = False - - self.bad_keyfile_perms = False - self.missing_vpn_keyfile = False - self.missing_provider = False - self.missing_definition = False - self.bad_provider = False + # This is handled by Exception attrs + # now (see #504) + #self.missing_pkexec = False + #self.missing_auth_agent = False + + #self.bad_keyfile_perms = False + #self.missing_vpn_keyfile = False + #self.missing_provider = False + #self.missing_definition = False + #self.bad_provider = False ################################# #XXX workaround for signaling @@ -107,27 +107,17 @@ to be triggered for each one of them. except eip_exceptions.EIPNoPolkitAuthAgentAvailable: command = args = None # XXX deprecate - self.missing_auth_agent = True + #self.missing_auth_agent = True raise except eip_exceptions.EIPNoPkexecAvailable: command = args = None - self.missing_pkexec = True + #self.missing_pkexec = True raise # XXX if not command, signal error. self.command = command self.args = args - def _get_or_create_config(self): - """ - retrieves the config options from defaults or - home file, or config file passed in command line. - populates command and args to be passed to subprocess. - """ - # XXX does nothing. - # XXX should get config? or get from checker? - pass - def _check_vpn_keys(self): """ checks for correct permissions on vpn keys diff --git a/src/leap/eip/tests/test_eipconnection.py b/src/leap/eip/tests/test_eipconnection.py index 23f645c3..ce9d39e2 100644 --- a/src/leap/eip/tests/test_eipconnection.py +++ b/src/leap/eip/tests/test_eipconnection.py @@ -27,9 +27,6 @@ class NotImplementedError(Exception): @patch('OpenVPNConnection._get_or_create_config') @patch('OpenVPNConnection._set_ovpn_command') class MockedEIPConnection(EIPConnection): - def _get_or_create_config(self): - self._set_ovpn_command() - def _set_ovpn_command(self): self.command = "mock_command" self.args = [1, 2, 3] @@ -64,6 +61,7 @@ class EIPConductorTest(BaseLeapTest): # some methods mocked self.manager = Mock(name="openvpnmanager_mock") self.con = MockedEIPConnection() + self.con.run_openvpn_checks() def tearDown(self): del self.con @@ -78,10 +76,6 @@ class EIPConductorTest(BaseLeapTest): """ con = self.con self.assertEqual(con.autostart, True) - self.assertEqual(con.missing_pkexec, False) - self.assertEqual(con.missing_vpn_keyfile, False) - self.assertEqual(con.missing_provider, False) - self.assertEqual(con.bad_provider, False) def test_ovpn_command(self): """ -- cgit v1.2.3 From fc8a54a40645412e9c738723e54159bfda40cfde Mon Sep 17 00:00:00 2001 From: kali Date: Thu, 6 Sep 2012 04:18:27 +0900 Subject: openvpn management socket is a temp path on each run --- src/leap/eip/config.py | 23 +++++++++++----- src/leap/eip/eipconnection.py | 10 +++---- src/leap/eip/openvpnconnection.py | 11 +++++--- src/leap/eip/tests/test_config.py | 5 ++-- src/leap/eip/tests/test_openvpnconnection.py | 39 ++++++++++++---------------- 5 files changed, 48 insertions(+), 40 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py index f4b979ce..833519ee 100644 --- a/src/leap/eip/config.py +++ b/src/leap/eip/config.py @@ -1,6 +1,7 @@ import logging import os import platform +import tempfile from leap.util.fileutil import (which, check_and_fix_urw_only) @@ -39,7 +40,15 @@ class EIPServiceConfig(baseconfig.JSONLeapConfig): slug = property(_get_slug, _set_slug) -def build_ovpn_options(daemon=False): +def get_socket_path(): + socket_path = os.path.join( + tempfile.mkdtemp(prefix="leap-tmp"), + 'openvpn.socket') + logger.debug('socket path: %s', socket_path) + return socket_path + + +def build_ovpn_options(daemon=False, socket_path=None): """ build a list of options to be passed in the @@ -98,10 +107,11 @@ def build_ovpn_options(daemon=False): if ourplatform in ("Linux", "Mac"): opts.append('--management') - # XXX get a different sock each time ... - # XXX #505 - opts.append('/tmp/.eip.sock') + if socket_path is None: + socket_path = get_socket_path() + opts.append(socket_path) opts.append('unix') + if ourplatform == "Windows": opts.append('--management') opts.append('localhost') @@ -125,7 +135,8 @@ def build_ovpn_options(daemon=False): return opts -def build_ovpn_command(debug=False, do_pkexec_check=True, vpnbin=None): +def build_ovpn_command(debug=False, do_pkexec_check=True, vpnbin=None, + socket_path=None): """ build a string with the complete openvpn invocation @@ -171,7 +182,7 @@ def build_ovpn_command(debug=False, do_pkexec_check=True, vpnbin=None): command.append(vpn_command) daemon_mode = not debug - for opt in build_ovpn_options(daemon=daemon_mode): + for opt in build_ovpn_options(daemon=daemon_mode, socket_path=socket_path): command.append(opt) # XXX check len and raise proper error diff --git a/src/leap/eip/eipconnection.py b/src/leap/eip/eipconnection.py index ff71dc76..3a879f01 100644 --- a/src/leap/eip/eipconnection.py +++ b/src/leap/eip/eipconnection.py @@ -6,6 +6,7 @@ import logging import Queue from leap.eip.checks import EIPConfigChecker +from leap.eip import config as eipconfig from leap.eip import exceptions as eip_exceptions from leap.eip.openvpnconnection import OpenVPNConnection @@ -30,6 +31,9 @@ class EIPConnection(OpenVPNConnection): self.status = EIPConnectionStatus(callbacks=status_signals) self.config_checker = config_checker() + host = eipconfig.get_socket_path() + kwargs['host'] = host + super(EIPConnection, self).__init__(*args, **kwargs) def has_errors(self): @@ -72,12 +76,6 @@ class EIPConnection(OpenVPNConnection): """ return self.status.current - #def desired_connection_state(self): - #""" - #returns the desired_connection state - #""" - #return self.desired_con_state - def poll_connection_state(self): """ """ diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index ec8b48bf..2ab0622e 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -26,12 +26,10 @@ class OpenVPNConnection(Connection): def __init__(self, config_file=None, watcher_cb=None, debug=False, - host="/tmp/.eip.sock", + host=None, port="unix", password=None, *args, **kwargs): - #XXX FIXME - #change watcher_cb to line_observer """ :param config_file: configuration file to read from :param watcher_cb: callback to be \ @@ -42,8 +40,12 @@ to be triggered for each one of them. :type watcher_cb: function :type signal_map: dict """ + #XXX FIXME + #change watcher_cb to line_observer + logger.debug('init openvpn connection') self.debug = debug + # XXX if not host: raise ImproperlyConfigured self.config_file = config_file self.watcher_cb = watcher_cb @@ -103,7 +105,8 @@ to be triggered for each one of them. # XXX check also for command-line --command flag try: command, args = eip_config.build_ovpn_command( - debug=self.debug) + debug=self.debug, + socket_path=self.host) except eip_exceptions.EIPNoPolkitAuthAgentAvailable: command = args = None # XXX deprecate diff --git a/src/leap/eip/tests/test_config.py b/src/leap/eip/tests/test_config.py index c73281cc..60300770 100644 --- a/src/leap/eip/tests/test_config.py +++ b/src/leap/eip/tests/test_config.py @@ -71,7 +71,7 @@ class EIPConfigTest(BaseLeapTest): args.append('--management') #XXX hey! #get platform switches here! - args.append('/tmp/.eip.sock') + args.append('/tmp/test.socket') args.append('unix') # certs @@ -114,7 +114,8 @@ class EIPConfigTest(BaseLeapTest): print 'path =', path print 'vpnbin = ', vpnbin command, args = eipconfig.build_ovpn_command( - do_pkexec_check=False, vpnbin=vpnbin) + do_pkexec_check=False, vpnbin=vpnbin, + socket_path="/tmp/test.socket") self.assertEqual(command, self.home + '/bin/openvpn') self.assertEqual(args, self.get_expected_openvpn_args()) diff --git a/src/leap/eip/tests/test_openvpnconnection.py b/src/leap/eip/tests/test_openvpnconnection.py index dea75b55..885c80b3 100644 --- a/src/leap/eip/tests/test_openvpnconnection.py +++ b/src/leap/eip/tests/test_openvpnconnection.py @@ -1,5 +1,7 @@ import logging +import os import platform +import shutil #import socket logging.basicConfig() @@ -12,9 +14,10 @@ except ImportError: from mock import Mock, patch # MagicMock +from leap.eip import config as eipconfig from leap.eip import openvpnconnection -from leap.eip import exceptions as eip_exceptions from leap.eip.udstelnet import UDSTelnet +from leap.testing.basetest import BaseLeapTest _system = platform.system() @@ -46,28 +49,25 @@ class MockedOpenVPNConnection(openvpnconnection.OpenVPNConnection): self.tn = mock_UDSTelnet(self.host, port=self.port) -class OpenVPNConnectionTest(unittest.TestCase): +class OpenVPNConnectionTest(BaseLeapTest): __name__ = "vpnconnection_tests" def setUp(self): - self.manager = MockedOpenVPNConnection() + # XXX this will have to change for win, host=localhost + host = eipconfig.get_socket_path() + self.manager = MockedOpenVPNConnection(host=host) def tearDown(self): - del self.manager - - # - # helpers - # - - # XXX hey, refactor this to basetestclass + # remove the socket folder. + # XXX only if posix. in win, host is localhost, so nothing + # has to be done. + if self.manager.host: + folder, fpath = os.path.split(self.manager.host) + assert folder.startswith('/tmp/leap-tmp') # safety check + shutil.rmtree(folder) - def _missing_test_for_plat(self, do_raise=False): - if do_raise: - raise NotImplementedError( - "This test is not implemented " - "for the running platform: %s" % - _system) + del self.manager # # tests @@ -78,7 +78,7 @@ class OpenVPNConnectionTest(unittest.TestCase): """ check default host for management iface """ - self.assertEqual(self.manager.host, '/tmp/.eip.sock') + self.assertTrue(self.manager.host.startswith('/tmp/leap-tmp')) self.assertEqual(self.manager.port, 'unix') @unittest.skipUnless(_system == "Windows", "win only") @@ -99,11 +99,6 @@ class OpenVPNConnectionTest(unittest.TestCase): self.manager = MockedOpenVPNConnection(port="bad") self.assertEqual(self.manager.port, None) - def test_connect_raises_missing_socket(self): - self.manager = openvpnconnection.OpenVPNConnection() - with self.assertRaises(eip_exceptions.MissingSocketError): - self.manager.connect_to_management() - def test_uds_telnet_called_on_connect(self): self.manager.connect_to_management() mock_UDSTelnet.assert_called_with( -- cgit v1.2.3 From ffe551fdbbade14e1a8de84ac48064aa7b45e2c1 Mon Sep 17 00:00:00 2001 From: antialias Date: Mon, 10 Sep 2012 19:59:30 -0400 Subject: Implemented basic networks checks: valid interface, default route, and can ping the listed gateway. --- src/leap/eip/checks.py | 53 +++++++++++++++++++++++++++++++++++++-- src/leap/eip/exceptions.py | 13 ++++++++++ src/leap/eip/tests/test_checks.py | 26 ++++++++++++++++++- 3 files changed, 89 insertions(+), 3 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index 4a2a9599..412be27b 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -1,7 +1,10 @@ import logging import ssl +import platform import os +import netifaces +import ping import requests from leap.base import constants as baseconstants @@ -319,8 +322,54 @@ class EIPConfigChecker(object): # We should WRITE eip config if missing or # incomplete at this point - def ping_gateway(self): - raise NotImplementedError + def test_internet_connection(self): + try: + requests.get('http://216.172.161.165') + except (requests.HTTPError, requests.RequestException) as e: + self.error = e.message + except requests.ConenctionError as e: + if e.message == "[Errno 113] No route to host": + if not self.is_internet_up(): + self.error = "No valid internet connection found." + else: + self.error = "Provider server appears to be down." + + def is_internet_up(self): + iface, gateway = self.get_default_interface_gateway() + self.ping_gateway(self) + + def get_default_interface_gateway(self): + """only impletemented for linux so far.""" + if not platform.system() == "Linux": + raise NotImplementedError + + f = open("/proc/net/route") + route_table = f.readlines() + #toss out header + route_table.pop(0) + + default_iface = None + gateway = None + while route_table: + line = route_table.pop(0) + iface, destination, gateway = line.split('\t')[0:3] + if destination == '00000000': + default_iface = iface + break + + if not default_iface: + raise eipexceptions.NoDefaultInterfaceFoundError + + if default_iface not in netifaces.interfaces(): + raise eipexceptions.InterfaceNotFoundError + + return default_iface, gateway + + def ping_gateway(self, gateway): + #TODO: Discuss how much packet loss (%) is acceptable. + packet_loss = ping.quiet_ping(gateway)[0] + if packet_loss > 10: + raise eipexceptions.NoConnectionToGateway # # private helpers diff --git a/src/leap/eip/exceptions.py b/src/leap/eip/exceptions.py index 3c8f6afb..4d0d70e2 100644 --- a/src/leap/eip/exceptions.py +++ b/src/leap/eip/exceptions.py @@ -108,6 +108,19 @@ class EIPInitBadProviderError(EIPClientError): class EIPConfigurationError(EIPClientError): pass + +class NoDefaultInterfaceFoundError(EIPClientError): + pass + + +class InterfaceNotFoundError(EIPClientError): + pass + + +class NoConnectionToGateway(EIPClientError): + pass + + # # Errors that probably we don't need anymore # chase down for them and check. diff --git a/src/leap/eip/tests/test_checks.py b/src/leap/eip/tests/test_checks.py index 0a87f573..1edcdfb2 100644 --- a/src/leap/eip/tests/test_checks.py +++ b/src/leap/eip/tests/test_checks.py @@ -8,8 +8,10 @@ except ImportError: import os import urlparse -from mock import patch, Mock +from StringIO import StringIO +from mock import patch, Mock, MagicMock +import ping import requests from leap.base import config as baseconfig @@ -23,6 +25,8 @@ from leap.testing.basetest import BaseLeapTest from leap.testing.https_server import BaseHTTPSServerTestCase from leap.testing.https_server import where as where_cert +_uid = os.getuid() + class NoLogRequestHandler: def log_message(self, *args): @@ -170,6 +174,26 @@ class EIPCheckTest(BaseLeapTest): sampleconfig = copy.copy(testdata.EIP_SAMPLE_JSON) checker.check_complete_eip_config(config=sampleconfig) + def test_get_default_interface_no_interface(self): + checker = eipchecks.EIPConfigChecker() + with patch('leap.eip.checks.open', create=True) as mock_open: + with self.assertRaises(eipexceptions.NoDefaultInterfaceFoundError): + mock_open.return_value = \ + StringIO("Iface\tDestination Gateway\tFlags\tRefCntd\tUse\tMetric\tMask\tMTU\tWindow\tIRTT") + checker.get_default_interface_gateway() + + def test_ping_gateway_fail(self): + checker = eipchecks.EIPConfigChecker() + with patch.object(ping, "quiet_ping") as mocked_ping: + with self.assertRaises(eipexceptions.NoConnectionToGateway): + mocked_ping.return_value = [11, "", ""] + checker.ping_gateway("4.2.2.2") + + @unittest.skipUnless(_uid == 0, "root only") + def test_ping_gateway(self): + checker = eipchecks.EIPConfigChecker() + checker.ping_gateway("4.2.2.2") + class ProviderCertCheckerTest(BaseLeapTest): -- cgit v1.2.3 From 4304ef6107a97d9d03cb626d4a7fcbd5afc1a2c9 Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 12 Sep 2012 08:01:26 +0900 Subject: pep8 --- src/leap/eip/tests/test_checks.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/tests/test_checks.py b/src/leap/eip/tests/test_checks.py index 1edcdfb2..caaa371f 100644 --- a/src/leap/eip/tests/test_checks.py +++ b/src/leap/eip/tests/test_checks.py @@ -178,8 +178,10 @@ class EIPCheckTest(BaseLeapTest): checker = eipchecks.EIPConfigChecker() with patch('leap.eip.checks.open', create=True) as mock_open: with self.assertRaises(eipexceptions.NoDefaultInterfaceFoundError): - mock_open.return_value = \ - StringIO("Iface\tDestination Gateway\tFlags\tRefCntd\tUse\tMetric\tMask\tMTU\tWindow\tIRTT") + mock_open.return_value = StringIO( + "Iface\tDestination Gateway\t" + "Flags\tRefCntd\tUse\tMetric\t" + "Mask\tMTU\tWindow\tIRTT") checker.get_default_interface_gateway() def test_ping_gateway_fail(self): -- cgit v1.2.3 From 37e9c942fa8b51436a332d6b49c3f8f411f57dc3 Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 12 Sep 2012 08:34:10 +0900 Subject: some human friendly stubs for raising these exceptions --- src/leap/eip/exceptions.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/exceptions.py b/src/leap/eip/exceptions.py index 4d0d70e2..467be7fe 100644 --- a/src/leap/eip/exceptions.py +++ b/src/leap/eip/exceptions.py @@ -110,15 +110,18 @@ class EIPConfigurationError(EIPClientError): class NoDefaultInterfaceFoundError(EIPClientError): - pass + message = "no default interface found" + usermessage = "Looks like your computer is not connected to the internet" class InterfaceNotFoundError(EIPClientError): - pass + # XXX should take iface arg on init maybe? + message = "interface not found" class NoConnectionToGateway(EIPClientError): - pass + message = "no connection to gateway" + usermessage = "Looks like there are problems with your internet connection" # -- cgit v1.2.3 From f3b601cb525b2884e7a48c7bfc41b4aef915adf7 Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 12 Sep 2012 08:38:36 +0900 Subject: moved network checks to its own class so it can be more easily moved to base.checks and reused when eip is a module. --- src/leap/eip/checks.py | 131 +++++++++++++++++++++++--------------- src/leap/eip/tests/test_checks.py | 79 ++++++++++++++++------- 2 files changed, 133 insertions(+), 77 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index 412be27b..24e97335 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -20,8 +20,6 @@ logger = logging.getLogger(name=__name__) """ EIPConfigChecker ---------- -this is the first of 3 consecutive checks that we're implementing. - It is used from the eip conductor (a instance of EIPConnection that is managed from the QtApp), running `run_all` method before trying to call `connect` or any other of the state-changing methods. @@ -32,13 +30,87 @@ into base.tests to be invoked by the base leap init routines. However, I'm testing them alltogether for the sake of having the whole unit reachable and testable as a whole. -Other related checkers - not implemented yet -: -* LeapNetworkChecker +LeapNetworkChecker +------------------ +Network checks. To be moved to base. +docs TBD + +ProviderCertChecker +------------------- +Checks on certificates. +docs TBD """ class LeapNetworkChecker(object): - pass + """ + all network related checks + """ + # XXX to be moved to leap.base.checks + # TODO eventually, use a more portable solution + # like psutil + + def run_all(self, checker=None): + if not checker: + checker = self + self.error = None # ? + + # for MVS + checker.test_internet_connection() + checker.is_internet_up() + checker.ping_gateway() + + def test_internet_connection(self): + # XXX we're not passing the error anywhere. + # XXX we probably should raise an exception here? + # unless we use this as smoke test + try: + requests.get('http://216.172.161.165') + except (requests.HTTPError, requests.RequestException) as e: + self.error = e.message + except requests.ConenctionError as e: + if e.message == "[Errno 113] No route to host": + if not self.is_internet_up(): + self.error = "No valid internet connection found." + else: + self.error = "Provider server appears to be down." + + def is_internet_up(self): + iface, gateway = self.get_default_interface_gateway() + self.ping_gateway(self) + + def get_default_interface_gateway(self): + """only impletemented for linux so far.""" + if not platform.system() == "Linux": + raise NotImplementedError + + with open("/proc/net/route") as f: + route_table = f.readlines() + #toss out header + route_table.pop(0) + + default_iface = None + gateway = None + while route_table: + line = route_table.pop(0) + iface, destination, gateway = line.split('\t')[0:3] + if destination == '00000000': + default_iface = iface + break + + if not default_iface: + raise eipexceptions.NoDefaultInterfaceFoundError + + if default_iface not in netifaces.interfaces(): + raise eipexceptions.InterfaceNotFoundError + + return default_iface, gateway + + def ping_gateway(self, gateway): + #TODO: Discuss how much packet loss (%) is acceptable. + packet_loss = ping.quiet_ping(gateway)[0] + if packet_loss > baseconstants.MAX_ICMP_PACKET_LOSS: + raise eipexceptions.NoConnectionToGateway class ProviderCertChecker(object): @@ -153,6 +225,7 @@ class ProviderCertChecker(object): # XXX TODO # waiting on #507. If we're not using PyOpenSSL or anything alike # we will have to roll our own x509 parsing to extract time info. + # XXX use gnutls def is_valid_pemfile(self, cert_s=None): """ @@ -322,54 +395,6 @@ class EIPConfigChecker(object): # We should WRITE eip config if missing or # incomplete at this point - def test_internet_connection(self): - try: - requests.get('http://216.172.161.165') - except (requests.HTTPError, requests.RequestException) as e: - self.error = e.message - except requests.ConenctionError as e: - if e.message == "[Errno 113] No route to host": - if not self.is_internet_up(): - self.error = "No valid internet connection found." - else: - self.error = "Provider server appears to be down." - - def is_internet_up(self): - iface, gateway = self.get_default_interface_gateway() - self.ping_gateway(self) - - def get_default_interface_gateway(self): - """only impletemented for linux so far.""" - if not platform.system() == "Linux": - raise NotImplementedError - - f = open("/proc/net/route") - route_table = f.readlines() - #toss out header - route_table.pop(0) - - default_iface = None - gateway = None - while route_table: - line = route_table.pop(0) - iface, destination, gateway = line.split('\t')[0:3] - if destination == '00000000': - default_iface = iface - break - - if not default_iface: - raise eipexceptions.NoDefaultInterfaceFoundError - - if default_iface not in netifaces.interfaces(): - raise eipexceptions.InterfaceNotFoundError - - return default_iface, gateway - - def ping_gateway(self, gateway): - #TODO: Discuss how much packet loss (%) is acceptable. - packet_loss = ping.quiet_ping(gateway)[0] - if packet_loss > 10: - raise eipexceptions.NoConnectionToGateway # # private helpers diff --git a/src/leap/eip/tests/test_checks.py b/src/leap/eip/tests/test_checks.py index caaa371f..bc7db79c 100644 --- a/src/leap/eip/tests/test_checks.py +++ b/src/leap/eip/tests/test_checks.py @@ -9,7 +9,7 @@ import os import urlparse from StringIO import StringIO -from mock import patch, Mock, MagicMock +from mock import (patch, Mock) import ping import requests @@ -37,6 +37,60 @@ class NoLogRequestHandler: return '' +class LeapNetworkCheckTest(BaseLeapTest): + # XXX to be moved to base.checks + + __name__ = "leap_network_check_tests" + + def setUp(self): + pass + + def tearDown(self): + pass + + def test_checker_should_implement_check_methods(self): + checker = eipchecks.LeapNetworkChecker() + + self.assertTrue(hasattr(checker, "test_internet_connection"), + "missing meth") + self.assertTrue(hasattr(checker, "is_internet_up"), + "missing meth") + self.assertTrue(hasattr(checker, "ping_gateway"), + "missing meth") + + def test_checker_should_actually_call_all_tests(self): + checker = eipchecks.LeapNetworkChecker() + + mc = Mock() + checker.run_all(checker=mc) + self.assertTrue(mc.test_internet_connection.called, "not called") + self.assertTrue(mc.ping_gateway.called, "not called") + self.assertTrue(mc.is_internet_up.called, + "not called") + + def test_get_default_interface_no_interface(self): + checker = eipchecks.LeapNetworkChecker() + with patch('leap.eip.checks.open', create=True) as mock_open: + with self.assertRaises(eipexceptions.NoDefaultInterfaceFoundError): + mock_open.return_value = StringIO( + "Iface\tDestination Gateway\t" + "Flags\tRefCntd\tUse\tMetric\t" + "Mask\tMTU\tWindow\tIRTT") + checker.get_default_interface_gateway() + + def test_ping_gateway_fail(self): + checker = eipchecks.LeapNetworkChecker() + with patch.object(ping, "quiet_ping") as mocked_ping: + with self.assertRaises(eipexceptions.NoConnectionToGateway): + mocked_ping.return_value = [11, "", ""] + checker.ping_gateway("4.2.2.2") + + @unittest.skipUnless(_uid == 0, "root only") + def test_ping_gateway(self): + checker = eipchecks.LeapNetworkChecker() + checker.ping_gateway("4.2.2.2") + + class EIPCheckTest(BaseLeapTest): __name__ = "eip_check_tests" @@ -61,7 +115,6 @@ class EIPCheckTest(BaseLeapTest): "missing meth") self.assertTrue(hasattr(checker, "check_complete_eip_config"), "missing meth") - self.assertTrue(hasattr(checker, "ping_gateway"), "missing meth") def test_checker_should_actually_call_all_tests(self): checker = eipchecks.EIPConfigChecker() @@ -174,28 +227,6 @@ class EIPCheckTest(BaseLeapTest): sampleconfig = copy.copy(testdata.EIP_SAMPLE_JSON) checker.check_complete_eip_config(config=sampleconfig) - def test_get_default_interface_no_interface(self): - checker = eipchecks.EIPConfigChecker() - with patch('leap.eip.checks.open', create=True) as mock_open: - with self.assertRaises(eipexceptions.NoDefaultInterfaceFoundError): - mock_open.return_value = StringIO( - "Iface\tDestination Gateway\t" - "Flags\tRefCntd\tUse\tMetric\t" - "Mask\tMTU\tWindow\tIRTT") - checker.get_default_interface_gateway() - - def test_ping_gateway_fail(self): - checker = eipchecks.EIPConfigChecker() - with patch.object(ping, "quiet_ping") as mocked_ping: - with self.assertRaises(eipexceptions.NoConnectionToGateway): - mocked_ping.return_value = [11, "", ""] - checker.ping_gateway("4.2.2.2") - - @unittest.skipUnless(_uid == 0, "root only") - def test_ping_gateway(self): - checker = eipchecks.EIPConfigChecker() - checker.ping_gateway("4.2.2.2") - class ProviderCertCheckerTest(BaseLeapTest): -- cgit v1.2.3 From 8b353443d6e4523d9d95cba190daaec564838e72 Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 12 Sep 2012 08:58:20 +0900 Subject: close fd. with breaks with StringIO passed in tests. --- src/leap/eip/checks.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index 24e97335..4dd4a95c 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -84,8 +84,9 @@ class LeapNetworkChecker(object): if not platform.system() == "Linux": raise NotImplementedError - with open("/proc/net/route") as f: - route_table = f.readlines() + f = open("/proc/net/route") + route_table = f.readlines() + f.close() #toss out header route_table.pop(0) @@ -395,7 +396,6 @@ class EIPConfigChecker(object): # We should WRITE eip config if missing or # incomplete at this point - # # private helpers # -- cgit v1.2.3 From 79764a5624acee85bcd03cd315c3d834a9a25a02 Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 12 Sep 2012 10:00:29 +0900 Subject: time boundary check of certificate using gnutls --- src/leap/eip/checks.py | 18 ++++++++++++------ src/leap/eip/tests/test_checks.py | 13 +++++++++++++ 2 files changed, 25 insertions(+), 6 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index 4dd4a95c..f368c551 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -1,8 +1,10 @@ import logging import ssl import platform +import time import os +from gnutls import crypto import netifaces import ping import requests @@ -221,12 +223,13 @@ class ProviderCertChecker(object): certfile = self._get_client_cert_path() return os.path.isfile(certfile) - def is_cert_not_expired(self): - return True - # XXX TODO - # waiting on #507. If we're not using PyOpenSSL or anything alike - # we will have to roll our own x509 parsing to extract time info. - # XXX use gnutls + def is_cert_not_expired(self, certfile=None, now=time.gmtime): + if certfile is None: + certfile = self._get_client_cert_path() + with open(certfile) as cf: + cert_s = cf.read() + cert = crypto.X509Certificate(cert_s) + return cert.activation_time < now() < cert.expiration_time def is_valid_pemfile(self, cert_s=None): """ @@ -244,6 +247,9 @@ class ProviderCertChecker(object): # XXX get a real cert validation # so far this is only checking begin/end # delimiters :) + # XXX use gnutls for get proper + # validation. + # crypto.X509Certificate(cert_s) ssl.PEM_cert_to_DER_cert(cert_s) except: # XXX raise proper exception diff --git a/src/leap/eip/tests/test_checks.py b/src/leap/eip/tests/test_checks.py index bc7db79c..952b10d2 100644 --- a/src/leap/eip/tests/test_checks.py +++ b/src/leap/eip/tests/test_checks.py @@ -6,6 +6,7 @@ try: except ImportError: import unittest import os +import time import urlparse from StringIO import StringIO @@ -372,10 +373,22 @@ class ProviderCertCheckerHTTPSTests(BaseHTTPSServerTestCase, BaseLeapTest): def test_is_cert_valid(self): checker = eipchecks.ProviderCertChecker() # TODO: better exception catching + # should raise eipexceptions.BadClientCertificate, and give reasons + # on msg. with self.assertRaises(Exception) as exc: self.assertFalse(checker.is_cert_valid()) exc.message = "missing cert" + def test_bad_validity_certs(self): + checker = eipchecks.ProviderCertChecker() + certfile = where_cert('leaptestscert.pem') + self.assertFalse(checker.is_cert_not_expired( + certfile=certfile, + now=lambda: time.mktime((2038, 1, 1, 1, 1, 1, 1, 1, 1)))) + self.assertFalse(checker.is_cert_not_expired( + certfile=certfile, + now=lambda: time.mktime((1970, 1, 1, 1, 1, 1, 1, 1, 1)))) + def test_check_new_cert_needed(self): # check: missing cert checker = eipchecks.ProviderCertChecker() -- cgit v1.2.3 From 99058b9f6536a3717ab82a9d77b09d5489334eb5 Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 12 Sep 2012 10:32:22 +0900 Subject: add openvpn-verb option to cli. Closes #534. accepts int [1-6] that get passed to openvpn invocation. We should filter out the polling "state"/"status" commands from the log if we want it to be real useful. --- src/leap/eip/config.py | 12 +++++++++--- src/leap/eip/openvpnconnection.py | 22 ++++++---------------- 2 files changed, 15 insertions(+), 19 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py index 833519ee..c0e17a19 100644 --- a/src/leap/eip/config.py +++ b/src/leap/eip/config.py @@ -48,7 +48,7 @@ def get_socket_path(): return socket_path -def build_ovpn_options(daemon=False, socket_path=None): +def build_ovpn_options(daemon=False, socket_path=None, **kwargs): """ build a list of options to be passed in the @@ -78,6 +78,11 @@ def build_ovpn_options(daemon=False, socket_path=None): opts.append('--persist-tun') opts.append('--persist-key') + verbosity = kwargs.get('ovpn_verbosity', None) + if verbosity and 1 <= verbosity <= 6: + opts.append('--verb') + opts.append("%s" % verbosity) + # remote # XXX get remote from eip.json opts.append('--remote') @@ -136,7 +141,7 @@ def build_ovpn_options(daemon=False, socket_path=None): def build_ovpn_command(debug=False, do_pkexec_check=True, vpnbin=None, - socket_path=None): + socket_path=None, **kwargs): """ build a string with the complete openvpn invocation @@ -182,7 +187,8 @@ def build_ovpn_command(debug=False, do_pkexec_check=True, vpnbin=None, command.append(vpn_command) daemon_mode = not debug - for opt in build_ovpn_options(daemon=daemon_mode, socket_path=socket_path): + for opt in build_ovpn_options(daemon=daemon_mode, socket_path=socket_path, + **kwargs): command.append(opt) # XXX check len and raise proper error diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index 2ab0622e..c280f70d 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -23,7 +23,8 @@ class OpenVPNConnection(Connection): of the openvpn binary """ - def __init__(self, config_file=None, + def __init__(self, + #config_file=None, watcher_cb=None, debug=False, host=None, @@ -46,8 +47,9 @@ to be triggered for each one of them. logger.debug('init openvpn connection') self.debug = debug # XXX if not host: raise ImproperlyConfigured + self.ovpn_verbosity = kwargs.get('ovpn_verbosity', None) - self.config_file = config_file + #self.config_file = config_file self.watcher_cb = watcher_cb #self.signal_maps = signal_maps @@ -58,19 +60,6 @@ to be triggered for each one of them. self.port = None self.proto = None - ################################## - # This is handled by Exception attrs - # now (see #504) - #self.missing_pkexec = False - #self.missing_auth_agent = False - - #self.bad_keyfile_perms = False - #self.missing_vpn_keyfile = False - #self.missing_provider = False - #self.missing_definition = False - #self.bad_provider = False - ################################# - #XXX workaround for signaling #the ui that we don't know how to #manage a connection error @@ -106,7 +95,8 @@ to be triggered for each one of them. try: command, args = eip_config.build_ovpn_command( debug=self.debug, - socket_path=self.host) + socket_path=self.host, + ovpn_verbosity=self.ovpn_verbosity) except eip_exceptions.EIPNoPolkitAuthAgentAvailable: command = args = None # XXX deprecate -- cgit v1.2.3 From 71cedd4e4a882765862496d77c7f04173ab4712a Mon Sep 17 00:00:00 2001 From: kali Date: Thu, 13 Sep 2012 16:38:22 +0900 Subject: fix race condition on app init still fragile; sometimes the qt app inits faster and make the send command miss the not yet created managemente socket. --- src/leap/eip/openvpnconnection.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index c280f70d..b679a544 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -99,12 +99,9 @@ to be triggered for each one of them. ovpn_verbosity=self.ovpn_verbosity) except eip_exceptions.EIPNoPolkitAuthAgentAvailable: command = args = None - # XXX deprecate - #self.missing_auth_agent = True raise except eip_exceptions.EIPNoPkexecAvailable: command = args = None - #self.missing_pkexec = True raise # XXX if not command, signal error. @@ -159,7 +156,7 @@ to be triggered for each one of them. if self.command is None: raise eip_exceptions.EIPNoCommandError if self.subp is not None: - print('cowardly refusing to launch subprocess again') + logger.debug('cowardly refusing to launch subprocess again') return self._launch_openvpn() @@ -234,16 +231,17 @@ to be triggered for each one of them. """ Send a command to openvpn and return response as list """ - #logger.debug('connected? %s' % self.connected()) if not self.connected(): try: - #logger.debug('try to connect') self.connect_to_management() except eip_exceptions.MissingSocketError: - #XXX capture more helpful error - return self.make_error() - except: - raise + logger.warning('missing management socket') + # This should only happen briefly during + # the first invocation. Race condition make + # the polling begin before management socket + # is ready + return [] + #return self.make_error() try: if hasattr(self, 'tn'): self.tn.write(cmd + "\n") @@ -311,6 +309,7 @@ to be triggered for each one of them. """ OpenVPN command: status """ + #logger.debug('status called') status = self._send_command("status") return status -- cgit v1.2.3 From 0d35f2a82bf15504ace2135af3e0c66ae1c16874 Mon Sep 17 00:00:00 2001 From: kali Date: Tue, 18 Sep 2012 11:11:43 +0900 Subject: do_branding command added to setup --- src/leap/eip/checks.py | 14 +++++++++----- src/leap/eip/eipconnection.py | 4 +++- src/leap/eip/openvpnconnection.py | 7 +++---- src/leap/eip/specs.py | 10 ++++++++-- 4 files changed, 23 insertions(+), 12 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index f368c551..aea5a5d7 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -257,7 +257,7 @@ class ProviderCertChecker(object): return True def _get_client_cert_uri(self): - return "https://%s/cert/get" % (baseconstants.DEFAULT_TEST_PROVIDER) + return "https://%s/cert/get" % (baseconstants.DEFAULT_PROVIDER) def _get_client_cert_path(self): # MVS+ : get provider path @@ -414,14 +414,18 @@ class EIPConfigChecker(object): def _get_provider_definition_uri(self, domain=None, path=None): if domain is None: - domain = baseconstants.DEFAULT_TEST_PROVIDER + domain = baseconstants.DEFAULT_PROVIDER if path is None: path = baseconstants.DEFINITION_EXPECTED_PATH - return "https://%s/%s" % (domain, path) + uri = u"https://%s/%s" % (domain, path) + logger.debug('getting provider definition from %s' % uri) + return uri def _get_eip_service_uri(self, domain=None, path=None): if domain is None: - domain = baseconstants.DEFAULT_TEST_PROVIDER + domain = baseconstants.DEFAULT_PROVIDER if path is None: path = eipconstants.EIP_SERVICE_EXPECTED_PATH - return "https://%s/%s" % (domain, path) + uri = "https://%s/%s" % (domain, path) + logger.debug('getting eip service file from %s', uri) + return uri diff --git a/src/leap/eip/eipconnection.py b/src/leap/eip/eipconnection.py index 3a879f01..d1c84b2a 100644 --- a/src/leap/eip/eipconnection.py +++ b/src/leap/eip/eipconnection.py @@ -4,6 +4,7 @@ EIP Connection Class from __future__ import (absolute_import,) import logging import Queue +import sys from leap.eip.checks import EIPConfigChecker from leap.eip import config as eipconfig @@ -48,7 +49,8 @@ class EIPConnection(OpenVPNConnection): self.config_checker.run_all(skip_download=skip_download) self.run_openvpn_checks() except Exception as exc: - self.error_queue.put(exc) + exc_traceback = sys.exc_info()[2] + self.error_queue.put((exc, exc_traceback)) def connect(self): """ diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index c280f70d..65683485 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -117,11 +117,10 @@ to be triggered for each one of them. """ try: eip_config.check_vpn_keys() - except eip_exceptions.EIPInitNoKeyFileError: - self.missing_vpn_keyfile = True except eip_exceptions.EIPInitBadKeyFilePermError: - logger.error('error while checking vpn keys') - self.bad_keyfile_perms = True + logger.error('Bad VPN Keys permission!') + # do nothing now + # and raise the rest ... def _launch_openvpn(self): """ diff --git a/src/leap/eip/specs.py b/src/leap/eip/specs.py index e617574c..05aef590 100644 --- a/src/leap/eip/specs.py +++ b/src/leap/eip/specs.py @@ -1,15 +1,21 @@ from __future__ import (unicode_literals) import os +from leap import __branding from leap.base import config as baseconfig +PROVIDER_CA_CERT = __branding.get( + 'provider_ca_file', + 'testprovider-ca-cert.pem') provider_ca_path = lambda: unicode(os.path.join( baseconfig.get_default_provider_path(), 'keys', 'ca', - 'testprovider-ca-cert.pem' + PROVIDER_CA_CERT )) +PROVIDER_DOMAIN = __branding.get('provider_domain', 'testprovider.example.org') + client_cert_path = lambda: unicode(os.path.join( baseconfig.get_default_provider_path(), @@ -20,7 +26,7 @@ client_cert_path = lambda: unicode(os.path.join( eipconfig_spec = { 'provider': { 'type': unicode, - 'default': u"testprovider.example.org", + 'default': u"%s" % PROVIDER_DOMAIN, 'required': True, }, 'transport': { -- cgit v1.2.3 From 89735a5fd3c81e8aba3cb7b1d4836c1bf1e8c098 Mon Sep 17 00:00:00 2001 From: kali Date: Tue, 18 Sep 2012 22:55:45 +0900 Subject: cert verification and malformed json checks --- src/leap/eip/checks.py | 65 ++++++++++++++++++++++++++++++------------- src/leap/eip/eipconnection.py | 26 +++++++++++++++-- src/leap/eip/exceptions.py | 15 +++++++++- 3 files changed, 83 insertions(+), 23 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index aea5a5d7..b55f5827 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -9,6 +9,8 @@ import netifaces import ping import requests +from leap import __branding as BRANDING +from leap import certs from leap.base import constants as baseconstants from leap.base import providers from leap.eip import config as eipconfig @@ -20,6 +22,11 @@ from leap.util.fileutil import mkdir_p logger = logging.getLogger(name=__name__) """ +ProviderCertChecker +------------------- +Checks on certificates. To be moved to base. +docs TBD + EIPConfigChecker ---------- It is used from the eip conductor (a instance of EIPConnection that is @@ -36,14 +43,15 @@ LeapNetworkChecker ------------------ Network checks. To be moved to base. docs TBD - -ProviderCertChecker -------------------- -Checks on certificates. -docs TBD """ +def get_ca_cert(): + ca_file = BRANDING.get('provider_ca_file') + if ca_file: + return certs.where(ca_file) + + class LeapNetworkChecker(object): """ all network related checks @@ -67,6 +75,7 @@ class LeapNetworkChecker(object): # XXX we probably should raise an exception here? # unless we use this as smoke test try: + # XXX remove this hardcoded random ip requests.get('http://216.172.161.165') except (requests.HTTPError, requests.RequestException) as e: self.error = e.message @@ -124,7 +133,7 @@ class ProviderCertChecker(object): """ def __init__(self, fetcher=requests): self.fetcher = fetcher - self.cacert = None + self.cacert = get_ca_cert() def run_all(self, checker=None, skip_download=False): if not checker: @@ -159,25 +168,34 @@ class ProviderCertChecker(object): raise NotImplementedError def is_there_provider_ca(self): - # XXX fake it till you make it! :P + from leap import certs + logger.debug('do we have provider_ca?') + cacert_path = BRANDING.get('provider_ca_file', None) + if not cacert_path: + logger.debug('False') + return False + self.cacert = certs.where(cacert_path) + logger.debug('True') return True - # enable this when we have - # a custom "branded" bundle - # certs package. - try: - from leap.custom import certs - except ImportError: - raise - self.cacert = certs.where('cacert.pem') - def is_https_working(self, uri=None, verify=True): + if uri is None: + uri = self._get_root_uri() # XXX raise InsecureURI or something better + logger.debug('is https working?') + logger.debug('uri: %s', uri) + #import ipdb;ipdb.set_trace() assert uri.startswith('https') if verify is True and self.cacert is not None: + logger.debug('verify cert: %s', self.cacert) verify = self.cacert - self.fetcher.get(uri, verify=verify) - return True + try: + self.fetcher.get(uri, verify=verify) + except requests.exceptions.SSLError: + raise eipexceptions.EIPBadCertError + else: + logger.debug('True') + return True def check_new_cert_needed(self, skip_download=False): if not self.is_cert_valid(do_raise=False): @@ -256,7 +274,11 @@ class ProviderCertChecker(object): raise return True + def _get_root_uri(self): + return u"https://%s/" % baseconstants.DEFAULT_PROVIDER + def _get_client_cert_uri(self): + # XXX get the whole thing from constants return "https://%s/cert/get" % (baseconstants.DEFAULT_PROVIDER) def _get_client_cert_path(self): @@ -370,7 +392,12 @@ class EIPConfigChecker(object): domain = config.get('provider', None) uri = self._get_provider_definition_uri(domain=domain) - self.defaultprovider.load(from_uri=uri, fetcher=self.fetcher) + # FIXME! Pass ca path verify!!! + self.defaultprovider.load( + from_uri=uri, + fetcher=self.fetcher, + verify=False) + #import ipdb;ipdb.set_trace() self.defaultprovider.save() def fetch_eip_service_config(self, skip_download=False, diff --git a/src/leap/eip/eipconnection.py b/src/leap/eip/eipconnection.py index d1c84b2a..4e240f16 100644 --- a/src/leap/eip/eipconnection.py +++ b/src/leap/eip/eipconnection.py @@ -6,6 +6,7 @@ import logging import Queue import sys +from leap.eip.checks import ProviderCertChecker from leap.eip.checks import EIPConfigChecker from leap.eip import config as eipconfig from leap.eip import exceptions as eip_exceptions @@ -22,7 +23,10 @@ class EIPConnection(OpenVPNConnection): Status updates (connected, bandwidth, etc) are signaled to the GUI. """ - def __init__(self, config_checker=EIPConfigChecker, *args, **kwargs): + def __init__(self, + provider_cert_checker=ProviderCertChecker, + config_checker=EIPConfigChecker, + *args, **kwargs): self.settingsfile = kwargs.get('settingsfile', None) self.logfile = kwargs.get('logfile', None) @@ -30,6 +34,8 @@ class EIPConnection(OpenVPNConnection): status_signals = kwargs.pop('status_signals', None) self.status = EIPConnectionStatus(callbacks=status_signals) + + self.provider_cert_checker = provider_cert_checker() self.config_checker = config_checker() host = eipconfig.get_socket_path() @@ -45,12 +51,25 @@ class EIPConnection(OpenVPNConnection): run all eip checks previous to attempting a connection """ logger.debug('running conductor checks') + + def push_err(exc): + # keep the original traceback! + exc_traceback = sys.exc_info()[2] + self.error_queue.put((exc, exc_traceback)) + + try: + # network (1) + self.provider_cert_checker.run_all() + except Exception as exc: + push_err(exc) try: self.config_checker.run_all(skip_download=skip_download) + except Exception as exc: + push_err(exc) + try: self.run_openvpn_checks() except Exception as exc: - exc_traceback = sys.exc_info()[2] - self.error_queue.put((exc, exc_traceback)) + push_err(exc) def connect(self): """ @@ -84,6 +103,7 @@ class EIPConnection(OpenVPNConnection): # XXX this separation does not # make sense anymore after having # merged Connection and Manager classes. + # XXX GET RID OF THIS FUNCTION HERE! try: state = self.get_connection_state() except eip_exceptions.ConnectionRefusedError: diff --git a/src/leap/eip/exceptions.py b/src/leap/eip/exceptions.py index 467be7fe..f048621f 100644 --- a/src/leap/eip/exceptions.py +++ b/src/leap/eip/exceptions.py @@ -40,6 +40,8 @@ class EIPClientError(Exception): base EIPClient exception """ critical = False + failfirst = False + warning = False class CriticalError(EIPClientError): @@ -54,7 +56,7 @@ class Warning(EIPClientError): """ just that, warnings """ - pass + warning = True class EIPNoPolkitAuthAgentAvailable(CriticalError): @@ -81,10 +83,21 @@ class EIPNoCommandError(EIPClientError): "
(Might be a permissions problem)") +class EIPBadCertError(Warning): + # XXX this should be critical and fail close + message = "cert verification failed" + usermessage = "there is a problem with provider certificate" + + +class LeapBadConfigFetchedError(Warning): + message = "provider sent a malformed json file" + usermessage = "an error occurred during configuratio of leap services" + # # errors still needing some love # + class EIPInitNoKeyFileError(CriticalError): message = "No vpn keys found in the expected path" usermessage = "We could not find your eip certs in the expected path" -- cgit v1.2.3 From 79db1d90f617d90cda61a790c92a54afcf30ff16 Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 19 Sep 2012 05:02:30 +0900 Subject: checks for certificate --- src/leap/eip/checks.py | 52 +++++++++++++++++++++++++++++++---------------- src/leap/eip/constants.py | 2 +- 2 files changed, 36 insertions(+), 18 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index b55f5827..cf758314 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -147,9 +147,10 @@ class ProviderCertChecker(object): # For MVS checker.is_there_provider_ca() - checker.is_https_working() - checker.check_new_cert_needed() - #checker.download_new_client_cert() + + # XXX FAKE IT!!! + checker.is_https_working(verify=False) + checker.check_new_cert_needed(verify=False) def download_ca_cert(self): # MVS+ @@ -184,7 +185,6 @@ class ProviderCertChecker(object): # XXX raise InsecureURI or something better logger.debug('is https working?') logger.debug('uri: %s', uri) - #import ipdb;ipdb.set_trace() assert uri.startswith('https') if verify is True and self.cacert is not None: logger.debug('verify cert: %s', self.cacert) @@ -192,19 +192,26 @@ class ProviderCertChecker(object): try: self.fetcher.get(uri, verify=verify) except requests.exceptions.SSLError: + logger.debug('False!') raise eipexceptions.EIPBadCertError else: logger.debug('True') return True - def check_new_cert_needed(self, skip_download=False): + def check_new_cert_needed(self, skip_download=False, verify=True): + logger.debug('is new cert needed?') if not self.is_cert_valid(do_raise=False): - self.download_new_client_cert(skip_download=skip_download) + logger.debug('True') + self.download_new_client_cert( + skip_download=skip_download, + verify=verify) return True + logger.debug('False') return False def download_new_client_cert(self, uri=None, verify=True, skip_download=False): + logger.debug('download new client cert') if skip_download: return True if uri is None: @@ -213,20 +220,28 @@ class ProviderCertChecker(object): assert uri.startswith('https') if verify is True and self.cacert is not None: verify = self.cacert - req = self.fetcher.get(uri, verify=verify) - pemfile_content = req.content - self.is_valid_pemfile(pemfile_content) - cert_path = self._get_client_cert_path() - self.write_cert(pemfile_content, to=cert_path) + try: + req = self.fetcher.get(uri, verify=verify) + req.raise_for_status() + except requests.exceptions.SSLError: + logger.warning('SSLError while fetching cert. ' + 'Look below for stack trace.') + # XXX raise better exception + raise + try: + pemfile_content = req.content + self.is_valid_pemfile(pemfile_content) + cert_path = self._get_client_cert_path() + self.write_cert(pemfile_content, to=cert_path) + except: + logger.warning('Error while validating cert') + raise return True def is_cert_valid(self, cert_path=None, do_raise=True): exists = lambda: self.is_certificate_exists() valid_pemfile = lambda: self.is_valid_pemfile() not_expired = lambda: self.is_cert_not_expired() - #print 'exists?', exists - #print 'valid', valid_pemfile - #print 'not expired', not_expired valid = exists() and valid_pemfile() and not_expired() if not valid: @@ -268,6 +283,11 @@ class ProviderCertChecker(object): # XXX use gnutls for get proper # validation. # crypto.X509Certificate(cert_s) + sep = "-" * 5 + "BEGIN CERTIFICATE" + "-" * 5 + # we might have private key and cert in the same file + certparts = cert_s.split(sep) + if len(certparts) > 1: + cert_s = sep + certparts[1] ssl.PEM_cert_to_DER_cert(cert_s) except: # XXX raise proper exception @@ -279,11 +299,10 @@ class ProviderCertChecker(object): def _get_client_cert_uri(self): # XXX get the whole thing from constants - return "https://%s/cert/get" % (baseconstants.DEFAULT_PROVIDER) + return "https://%s/1/cert" % (baseconstants.DEFAULT_PROVIDER) def _get_client_cert_path(self): # MVS+ : get provider path - #import ipdb;ipdb.set_trace() return eipspecs.client_cert_path() def write_cert(self, pemfile_content, to=None): @@ -397,7 +416,6 @@ class EIPConfigChecker(object): from_uri=uri, fetcher=self.fetcher, verify=False) - #import ipdb;ipdb.set_trace() self.defaultprovider.save() def fetch_eip_service_config(self, skip_download=False, diff --git a/src/leap/eip/constants.py b/src/leap/eip/constants.py index ce50f5e0..9af5a947 100644 --- a/src/leap/eip/constants.py +++ b/src/leap/eip/constants.py @@ -1,3 +1,3 @@ # not used anymore with the new JSONConfig.slug EIP_CONFIG = "eip.json" -EIP_SERVICE_EXPECTED_PATH = "eip-service.json" +EIP_SERVICE_EXPECTED_PATH = "1/config/eip-service.json" -- cgit v1.2.3 From 68b1a4a987b85540d2f13cfc800cbdf5efc27805 Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 19 Sep 2012 05:16:57 +0900 Subject: copy cacert to local config dir --- src/leap/eip/config.py | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py index c0e17a19..c3e830dd 100644 --- a/src/leap/eip/config.py +++ b/src/leap/eip/config.py @@ -3,7 +3,9 @@ import os import platform import tempfile -from leap.util.fileutil import (which, check_and_fix_urw_only) +from leap import __branding as BRANDING +from leap import certs +from leap.util.fileutil import (which, mkdir_p, check_and_fix_urw_only) from leap.base import config as baseconfig from leap.baseapp.permcheck import (is_pkexec_in_system, @@ -12,6 +14,7 @@ from leap.eip import exceptions as eip_exceptions from leap.eip import specs as eipspecs logger = logging.getLogger(name=__name__) +provider_ca_file = BRANDING.get('provider_ca_file', None) class EIPConfig(baseconfig.JSONLeapConfig): @@ -211,15 +214,30 @@ def check_vpn_keys(): logger.debug('client cert = %s', client_cert) # if no keys, raise error. - # should be catched by the ui and signal user. + # it's catched by the ui and signal user. + + if not os.path.isfile(provider_ca): + # not there. let's try to copy. + folder, filename = os.path.split(provider_ca) + if not os.path.isdir(folder): + mkdir_p(folder) + if provider_ca_file: + cacert = certs.where(provider_ca_file) + with open(provider_ca, 'w') as pca: + with open(cacert, 'r') as cac: + pca.write(cac.read()) + + if not os.path.isfile(provider_ca): + logger.error('key file %s not found. aborting.', + provider_ca) + raise eip_exceptions.EIPInitNoKeyFileError + + if not os.path.isfile(client_cert): + logger.error('key file %s not found. aborting.', + client_cert) + raise eip_exceptions.EIPInitNoKeyFileError for keyfile in (provider_ca, client_cert): - if not os.path.isfile(keyfile): - logger.error('key file %s not found. aborting.', - keyfile) - raise eip_exceptions.EIPInitNoKeyFileError - - # check proper permission on keys # bad perms? try to fix them try: check_and_fix_urw_only(keyfile) -- cgit v1.2.3 From 6a9523b0e83aca75bbfde5a8939ee612c5a78f9a Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 19 Sep 2012 05:52:16 +0900 Subject: openvpn options come from eip.json --- src/leap/eip/config.py | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py index c3e830dd..e5fcd164 100644 --- a/src/leap/eip/config.py +++ b/src/leap/eip/config.py @@ -21,7 +21,11 @@ class EIPConfig(baseconfig.JSONLeapConfig): spec = eipspecs.eipconfig_spec def _get_slug(self): - return baseconfig.get_config_file('eip.json') + dppath = baseconfig.get_default_provider_path() + eipjsonpath = baseconfig.get_config_file( + 'eip-service.json', + folder=dppath) + return eipjsonpath def _set_slug(self, *args, **kwargs): raise AttributeError("you cannot set slug") @@ -51,6 +55,25 @@ def get_socket_path(): return socket_path +def get_eip_gateway(): + """ + return the first host in the list of hosts + under gateways list + """ + eipconfig = EIPConfig() + eipconfig.load() + conf = eipconfig.get_config() + gateways = conf.get('gateways', None) + if len(gateways) > 0: + # we just pick first + gw = gateways[0] + hosts = gw['hosts'] + if len(hosts) > 0: + return hosts[0] + else: + return "testprovider.example.org" + + def build_ovpn_options(daemon=False, socket_path=None, **kwargs): """ build a list of options @@ -87,9 +110,10 @@ def build_ovpn_options(daemon=False, socket_path=None, **kwargs): opts.append("%s" % verbosity) # remote - # XXX get remote from eip.json opts.append('--remote') - opts.append('testprovider.example.org') + gw = get_eip_gateway() + logger.debug('setting eip gateway to %s', gw) + opts.append(str(gw)) opts.append('1194') opts.append('udp') @@ -140,6 +164,7 @@ def build_ovpn_options(daemon=False, socket_path=None, **kwargs): #if daemon is True: #opts.append('--daemon') + logger.debug('vpn options: %s', opts) return opts -- cgit v1.2.3 From ecd8696e6e009826523b62a508cdf2202eaa2411 Mon Sep 17 00:00:00 2001 From: kali Date: Thu, 20 Sep 2012 02:29:19 +0900 Subject: tests pass after branding changes --- src/leap/eip/config.py | 1 + src/leap/eip/tests/data.py | 14 +++++++++----- src/leap/eip/tests/test_checks.py | 4 ++-- src/leap/eip/tests/test_config.py | 26 +++++++++++++++++++++----- 4 files changed, 33 insertions(+), 12 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py index e5fcd164..44922310 100644 --- a/src/leap/eip/config.py +++ b/src/leap/eip/config.py @@ -112,6 +112,7 @@ def build_ovpn_options(daemon=False, socket_path=None, **kwargs): # remote opts.append('--remote') gw = get_eip_gateway() + #gw = "springbokvpn.org" logger.debug('setting eip gateway to %s', gw) opts.append(str(gw)) opts.append('1194') diff --git a/src/leap/eip/tests/data.py b/src/leap/eip/tests/data.py index 284b398f..4da0e18f 100644 --- a/src/leap/eip/tests/data.py +++ b/src/leap/eip/tests/data.py @@ -1,21 +1,25 @@ from __future__ import unicode_literals import os +from leap import __branding + # sample data used in tests +PROVIDER = __branding.get('provider_domain') + EIP_SAMPLE_JSON = { - "provider": "testprovider.example.org", + "provider": "%s" % PROVIDER, "transport": "openvpn", "openvpn_protocol": "tcp", "openvpn_port": 80, "openvpn_ca_certificate": os.path.expanduser( "~/.config/leap/providers/" - "testprovider.example.org/" - "keys/ca/testprovider-ca-cert.pem"), + "%s/" + "keys/ca/testprovider-ca-cert.pem" % PROVIDER), "openvpn_client_certificate": os.path.expanduser( "~/.config/leap/providers/" - "testprovider.example.org/" - "keys/client/openvpn.pem"), + "%s/" + "keys/client/openvpn.pem" % PROVIDER), "connect_on_login": True, "block_cleartext_traffic": True, "primary_gateway": "usa_west", diff --git a/src/leap/eip/tests/test_checks.py b/src/leap/eip/tests/test_checks.py index 952b10d2..42aa9cce 100644 --- a/src/leap/eip/tests/test_checks.py +++ b/src/leap/eip/tests/test_checks.py @@ -331,10 +331,10 @@ class ProviderCertCheckerHTTPSTests(BaseHTTPSServerTestCase, BaseLeapTest): fetcher.get(uri, verify=True) self.assertTrue( "SSL23_GET_SERVER_HELLO:unknown protocol" in exc.message) - with self.assertRaises(requests.exceptions.SSLError) as exc: + with self.assertRaises(eipexceptions.EIPBadCertError) as exc: checker.is_https_working(uri=uri, verify=True) self.assertTrue( - "SSL23_GET_SERVER_HELLO:unknown protocol" in exc.message) + "cert verification failed" in exc.message) # get cacert from testing.https_server cacert = where_cert('cacert.pem') diff --git a/src/leap/eip/tests/test_config.py b/src/leap/eip/tests/test_config.py index 60300770..f9f963dc 100644 --- a/src/leap/eip/tests/test_config.py +++ b/src/leap/eip/tests/test_config.py @@ -1,3 +1,4 @@ +import json import os import platform import stat @@ -9,11 +10,17 @@ except ImportError: #from leap.base import constants #from leap.eip import config as eip_config +from leap import __branding as BRANDING +from leap.eip import config as eipconfig +from leap.eip.tests.data import EIP_SAMPLE_SERVICE from leap.testing.basetest import BaseLeapTest from leap.util.fileutil import mkdir_p _system = platform.system() +PROVIDER = BRANDING.get('provider_domain') +PROVIDER_SHORTNAME = BRANDING.get('short_name') + class EIPConfigTest(BaseLeapTest): @@ -39,6 +46,14 @@ class EIPConfigTest(BaseLeapTest): open(tfile, 'wb').close() os.chmod(tfile, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR) + def write_sample_eipservice(self): + conf = eipconfig.EIPConfig() + folder, f = os.path.split(conf.filename) + if not os.path.isdir(folder): + mkdir_p(folder) + with open(conf.filename, 'w') as fd: + fd.write(json.dumps(EIP_SAMPLE_SERVICE)) + def get_expected_openvpn_args(self): args = [] username = self.get_username() @@ -51,7 +66,7 @@ class EIPConfigTest(BaseLeapTest): args.append('--persist-tun') args.append('--persist-key') args.append('--remote') - args.append('testprovider.example.org') + args.append('%s' % eipconfig.get_eip_gateway()) # XXX get port!? args.append('1194') # XXX get proto @@ -80,23 +95,23 @@ class EIPConfigTest(BaseLeapTest): args.append(os.path.join( self.home, '.config', 'leap', 'providers', - 'testprovider.example.org', + '%s' % PROVIDER, 'keys', 'client', 'openvpn.pem')) args.append('--key') args.append(os.path.join( self.home, '.config', 'leap', 'providers', - 'testprovider.example.org', + '%s' % PROVIDER, 'keys', 'client', 'openvpn.pem')) args.append('--ca') args.append(os.path.join( self.home, '.config', 'leap', 'providers', - 'testprovider.example.org', + '%s' % PROVIDER, 'keys', 'ca', - 'testprovider-ca-cert.pem')) + '%s-cacert.pem' % PROVIDER_SHORTNAME)) return args # build command string @@ -107,6 +122,7 @@ class EIPConfigTest(BaseLeapTest): def test_build_ovpn_command_empty_config(self): self.touch_exec() + self.write_sample_eipservice() from leap.eip import config as eipconfig from leap.util.fileutil import which path = os.environ['PATH'] -- cgit v1.2.3 From f2749fa3ff1df5875d3bc0b932a408031fee9874 Mon Sep 17 00:00:00 2001 From: kali Date: Thu, 20 Sep 2012 04:39:50 +0900 Subject: toggle connection on/off --- src/leap/eip/config.py | 1 - src/leap/eip/openvpnconnection.py | 7 ++++++- 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py index 44922310..e5fcd164 100644 --- a/src/leap/eip/config.py +++ b/src/leap/eip/config.py @@ -112,7 +112,6 @@ def build_ovpn_options(daemon=False, socket_path=None, **kwargs): # remote opts.append('--remote') gw = get_eip_gateway() - #gw = "springbokvpn.org" logger.debug('setting eip gateway to %s', gw) opts.append(str(gw)) opts.append('1194') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index 92ae9de9..e32d584c 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -163,8 +163,13 @@ to be triggered for each one of them. """ terminates child subprocess """ + # XXX we should send a quit process using management + # interface. if self.subp: - self.subp.terminate() + try: + self.subp.terminate() + except OSError: + logger.error('cannot terminate subprocess!') # # management methods -- cgit v1.2.3 From 4389eed796afb58e530ac2c0d3fa0df2c5cad97f Mon Sep 17 00:00:00 2001 From: kali Date: Thu, 20 Sep 2012 04:53:03 +0900 Subject: add logging --- src/leap/eip/openvpnconnection.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index e32d584c..f4d1c449 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -169,7 +169,8 @@ to be triggered for each one of them. try: self.subp.terminate() except OSError: - logger.error('cannot terminate subprocess!') + logger.error('cannot terminate subprocess!' + '(maybe openvpn still running?)') # # management methods -- cgit v1.2.3 From d1ebe98239fbc2baffa345558d396fa539e79202 Mon Sep 17 00:00:00 2001 From: kali Date: Fri, 21 Sep 2012 06:32:40 +0900 Subject: added --no-provider-checks and --no-ca-verify for ease of debugging Close #604 --- src/leap/eip/checks.py | 20 +++++++++++++------- src/leap/eip/eipconnection.py | 4 ++-- 2 files changed, 15 insertions(+), 9 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index cf758314..ef09a582 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -135,10 +135,12 @@ class ProviderCertChecker(object): self.fetcher = fetcher self.cacert = get_ca_cert() - def run_all(self, checker=None, skip_download=False): + def run_all(self, checker=None, skip_download=False, skip_verify=False): if not checker: checker = self + do_verify = not skip_verify + logger.debug('do_verify: %s', do_verify) # For MVS+ # checker.download_ca_cert() # checker.download_ca_signature() @@ -149,8 +151,8 @@ class ProviderCertChecker(object): checker.is_there_provider_ca() # XXX FAKE IT!!! - checker.is_https_working(verify=False) - checker.check_new_cert_needed(verify=False) + checker.is_https_working(verify=do_verify) + checker.check_new_cert_needed(verify=do_verify) def download_ca_cert(self): # MVS+ @@ -183,17 +185,21 @@ class ProviderCertChecker(object): if uri is None: uri = self._get_root_uri() # XXX raise InsecureURI or something better - logger.debug('is https working?') - logger.debug('uri: %s', uri) assert uri.startswith('https') if verify is True and self.cacert is not None: logger.debug('verify cert: %s', self.cacert) verify = self.cacert + logger.debug('is https working?') + logger.debug('uri: %s (verify:%s)', uri, verify) try: self.fetcher.get(uri, verify=verify) - except requests.exceptions.SSLError: - logger.debug('False!') + except requests.exceptions.SSLError as exc: + logger.warning('False! CERT VERIFICATION FAILED! ' + '(this should be CRITICAL)') + logger.warning('SSLError: %s', exc.message) raise eipexceptions.EIPBadCertError + # XXX get requests.exceptions.ConnectionError Errno 110 + # Connection timed out, and raise ours. else: logger.debug('True') return True diff --git a/src/leap/eip/eipconnection.py b/src/leap/eip/eipconnection.py index 4e240f16..f0a98d8c 100644 --- a/src/leap/eip/eipconnection.py +++ b/src/leap/eip/eipconnection.py @@ -46,7 +46,7 @@ class EIPConnection(OpenVPNConnection): def has_errors(self): return True if self.error_queue.qsize() != 0 else False - def run_checks(self, skip_download=False): + def run_checks(self, skip_download=False, skip_verify=False): """ run all eip checks previous to attempting a connection """ @@ -59,7 +59,7 @@ class EIPConnection(OpenVPNConnection): try: # network (1) - self.provider_cert_checker.run_all() + self.provider_cert_checker.run_all(skip_verify=skip_verify) except Exception as exc: push_err(exc) try: -- cgit v1.2.3 From a38e61691a79b20199cdedf23f60a5760bba7a06 Mon Sep 17 00:00:00 2001 From: kali Date: Fri, 21 Sep 2012 06:44:19 +0900 Subject: add property to baseconfig config instead of get_config() --- src/leap/eip/checks.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index ef09a582..5ace1479 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -388,7 +388,7 @@ class EIPConfigChecker(object): This is catched by ui and runs FirstRunWizard (MVS+) """ if config is None: - config = self.eipconfig.get_config() + config = self.eipconfig.config logger.debug('checking default provider') provider = config.get('provider', None) if provider is None: @@ -412,7 +412,7 @@ class EIPConfigChecker(object): logger.debug('(fetching def skipped)') return True if config is None: - config = self.defaultprovider.get_config() + config = self.defaultprovider.config if uri is None: domain = config.get('provider', None) uri = self._get_provider_definition_uri(domain=domain) @@ -429,7 +429,7 @@ class EIPConfigChecker(object): if skip_download: return True if config is None: - config = self.eipserviceconfig.get_config() + config = self.eipserviceconfig.config if uri is None: domain = config.get('provider', None) uri = self._get_eip_service_uri(domain=domain) @@ -440,7 +440,7 @@ class EIPConfigChecker(object): def check_complete_eip_config(self, config=None): # TODO check for gateway if config is None: - config = self.eipconfig.get_config() + config = self.eipconfig.config try: 'trying assertions' assert 'provider' in config -- cgit v1.2.3 From 5c32cc7b5e00853b3cc28b5003b92ab009418dff Mon Sep 17 00:00:00 2001 From: kali Date: Mon, 24 Sep 2012 22:01:53 +0900 Subject: fix slug for eip config (was taking the one for eip-service) also correct the path (should be in root leap config folder). --- src/leap/eip/config.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py index e5fcd164..24e837d0 100644 --- a/src/leap/eip/config.py +++ b/src/leap/eip/config.py @@ -21,10 +21,8 @@ class EIPConfig(baseconfig.JSONLeapConfig): spec = eipspecs.eipconfig_spec def _get_slug(self): - dppath = baseconfig.get_default_provider_path() eipjsonpath = baseconfig.get_config_file( - 'eip-service.json', - folder=dppath) + 'eip.json') return eipjsonpath def _set_slug(self, *args, **kwargs): -- cgit v1.2.3 From 30570bd89c04a56b35b91a0bc1d5fc00bb6ad266 Mon Sep 17 00:00:00 2001 From: kali Date: Mon, 24 Sep 2012 22:21:50 +0900 Subject: add schema to JSONLeapConfig classes and a jsonvalidate function too, that calls to jsonchemea.validate(self, data) with self.schema We're using the specs to both purposes now: * providing a type casting system for our config options (work in progress for the type casting) * json schema validation --- src/leap/eip/specs.py | 148 ++++++++++++++++++++++++++------------------------ 1 file changed, 78 insertions(+), 70 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/specs.py b/src/leap/eip/specs.py index 05aef590..a10a9623 100644 --- a/src/leap/eip/specs.py +++ b/src/leap/eip/specs.py @@ -8,7 +8,7 @@ PROVIDER_CA_CERT = __branding.get( 'provider_ca_file', 'testprovider-ca-cert.pem') -provider_ca_path = lambda: unicode(os.path.join( +provider_ca_path = lambda: str(os.path.join( baseconfig.get_default_provider_path(), 'keys', 'ca', PROVIDER_CA_CERT @@ -24,78 +24,86 @@ client_cert_path = lambda: unicode(os.path.join( )) eipconfig_spec = { - 'provider': { - 'type': unicode, - 'default': u"%s" % PROVIDER_DOMAIN, - 'required': True, - }, - 'transport': { - 'type': unicode, - 'default': u"openvpn", - }, - 'openvpn_protocol': { - 'type': unicode, - 'default': u"tcp" - }, - 'openvpn_port': { - 'type': int, - 'default': 80 - }, - 'openvpn_ca_certificate': { - 'type': unicode, # path - 'default': provider_ca_path - }, - 'openvpn_client_certificate': { - 'type': unicode, # path - 'default': client_cert_path - }, - 'connect_on_login': { - 'type': bool, - 'default': True - }, - 'block_cleartext_traffic': { - 'type': bool, - 'default': True - }, - 'primary_gateway': { - 'type': unicode, - 'default': u"usa_west", - 'required': True - }, - 'secondary_gateway': { - 'type': unicode, - 'default': u"france" - }, - 'management_password': { - 'type': unicode + 'description': 'sample eipconfig', + 'type': 'object', + 'properties': { + 'provider': { + 'type': unicode, + 'default': u"%s" % PROVIDER_DOMAIN, + 'required': True, + }, + 'transport': { + 'type': unicode, + 'default': u"openvpn", + }, + 'openvpn_protocol': { + 'type': unicode, + 'default': u"tcp" + }, + 'openvpn_port': { + 'type': int, + 'default': 80 + }, + 'openvpn_ca_certificate': { + 'type': unicode, # path + 'default': provider_ca_path + }, + 'openvpn_client_certificate': { + 'type': unicode, # path + 'default': client_cert_path + }, + 'connect_on_login': { + 'type': bool, + 'default': True + }, + 'block_cleartext_traffic': { + 'type': bool, + 'default': True + }, + 'primary_gateway': { + 'type': unicode, + 'default': u"usa_west", + #'required': True + }, + 'secondary_gateway': { + 'type': unicode, + 'default': u"france" + }, + 'management_password': { + 'type': unicode + } } } eipservice_config_spec = { - 'serial': { - 'type': int, - 'required': True, - 'default': 1 - }, - 'version': { - 'type': unicode, - 'required': True, - 'default': "0.1.0" - }, - 'capabilities': { - 'type': dict, - 'default': { - "transport": ["openvpn"], - "ports": ["80", "53"], - "protocols": ["udp", "tcp"], - "static_ips": True, - "adblock": True} - }, - 'gateways': { - 'type': list, - 'default': [{"country_code": "us", - "label": {"en":"west"}, - "capabilities": {}, - "hosts": ["1.2.3.4", "1.2.3.5"]}] + 'description': 'sample eip service config', + 'type': 'object', + 'properties': { + 'serial': { + 'type': int, + 'required': True, + 'default': 1 + }, + 'version': { + 'type': unicode, + 'required': True, + 'default': "0.1.0" + }, + 'capabilities': { + 'type': dict, + 'default': { + "transport": ["openvpn"], + "ports": ["80", "53"], + "protocols": ["udp", "tcp"], + "static_ips": True, + "adblock": True} + }, + 'gateways': { + 'type': list, + 'default': [{"country_code": "us", + "label": {"en":"west"}, + "capabilities": {}, + "hosts": ["1.2.3.4", "1.2.3.5"]}] + } } } -- cgit v1.2.3 From f4f5fc21e186bcd94d39f78333f758ed906f5b98 Mon Sep 17 00:00:00 2001 From: kali Date: Mon, 24 Sep 2012 22:01:53 +0900 Subject: fix slug for eip config (was taking the one for eip-service) also correct the path (should be in root leap config folder). --- src/leap/eip/config.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py index e5fcd164..24e837d0 100644 --- a/src/leap/eip/config.py +++ b/src/leap/eip/config.py @@ -21,10 +21,8 @@ class EIPConfig(baseconfig.JSONLeapConfig): spec = eipspecs.eipconfig_spec def _get_slug(self): - dppath = baseconfig.get_default_provider_path() eipjsonpath = baseconfig.get_config_file( - 'eip-service.json', - folder=dppath) + 'eip.json') return eipjsonpath def _set_slug(self, *args, **kwargs): -- cgit v1.2.3 From 5173c0ee937696782a2f62078a860246ec388c39 Mon Sep 17 00:00:00 2001 From: kali Date: Tue, 25 Sep 2012 05:48:06 +0900 Subject: workaround for #638 and fix for eip config check for gateways (we were picking gateway in a wrong way) Closes #610. --- src/leap/eip/checks.py | 10 ++++++++-- src/leap/eip/config.py | 34 ++++++++++++++++++++++++---------- src/leap/eip/specs.py | 2 +- src/leap/eip/tests/data.py | 2 +- src/leap/eip/tests/test_checks.py | 10 ++++++---- 5 files changed, 40 insertions(+), 18 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index ef09a582..9b7b1cee 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -197,7 +197,8 @@ class ProviderCertChecker(object): logger.warning('False! CERT VERIFICATION FAILED! ' '(this should be CRITICAL)') logger.warning('SSLError: %s', exc.message) - raise eipexceptions.EIPBadCertError + # XXX RAISE! See #638 + #raise eipexceptions.EIPBadCertError # XXX get requests.exceptions.ConnectionError Errno 110 # Connection timed out, and raise ours. else: @@ -227,7 +228,11 @@ class ProviderCertChecker(object): if verify is True and self.cacert is not None: verify = self.cacert try: - req = self.fetcher.get(uri, verify=verify) + # XXX FIXME!!!! + # verify=verify + # Workaround for #638. return to verification + # when That's done!!! + req = self.fetcher.get(uri, verify=False) req.raise_for_status() except requests.exceptions.SSLError: logger.warning('SSLError while fetching cert. ' @@ -452,6 +457,7 @@ class EIPConfigChecker(object): # XXX TODO: # We should WRITE eip config if missing or # incomplete at this point + #self.eipconfig.save() # # private helpers diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py index 24e837d0..082cc24d 100644 --- a/src/leap/eip/config.py +++ b/src/leap/eip/config.py @@ -55,21 +55,35 @@ def get_socket_path(): def get_eip_gateway(): """ - return the first host in the list of hosts - under gateways list + return the first host in eip service config + that matches the name defined in the eip.json config + file. """ + placeholder = "testprovider.example.org" eipconfig = EIPConfig() eipconfig.load() conf = eipconfig.get_config() - gateways = conf.get('gateways', None) + primary_gateway = conf.get('primary_gateway', None) + if not primary_gateway: + return placeholder + + eipserviceconfig = EIPServiceConfig() + eipserviceconfig.load() + eipsconf = eipserviceconfig.get_config() + gateways = eipsconf.get('gateways', None) + if not gateways: + logger.error('missing gateways in eip service config') + return placeholder if len(gateways) > 0: - # we just pick first - gw = gateways[0] - hosts = gw['hosts'] - if len(hosts) > 0: - return hosts[0] - else: - return "testprovider.example.org" + for gw in gateways: + if gw['name'] == primary_gateway: + hosts = gw['hosts'] + if len(hosts) > 0: + return hosts[0] + else: + logger.error('no hosts') + logger.error('could not find primary gateway in provider' + 'gateway list') def build_ovpn_options(daemon=False, socket_path=None, **kwargs): diff --git a/src/leap/eip/specs.py b/src/leap/eip/specs.py index 05aef590..2391e919 100644 --- a/src/leap/eip/specs.py +++ b/src/leap/eip/specs.py @@ -59,7 +59,7 @@ eipconfig_spec = { }, 'primary_gateway': { 'type': unicode, - 'default': u"usa_west", + 'default': u"turkey", 'required': True }, 'secondary_gateway': { diff --git a/src/leap/eip/tests/data.py b/src/leap/eip/tests/data.py index 4da0e18f..9bf86540 100644 --- a/src/leap/eip/tests/data.py +++ b/src/leap/eip/tests/data.py @@ -22,7 +22,7 @@ EIP_SAMPLE_JSON = { "keys/client/openvpn.pem" % PROVIDER), "connect_on_login": True, "block_cleartext_traffic": True, - "primary_gateway": "usa_west", + "primary_gateway": "turkey", "secondary_gateway": "france", #"management_password": "oph7Que1othahwiech6J" } diff --git a/src/leap/eip/tests/test_checks.py b/src/leap/eip/tests/test_checks.py index 42aa9cce..19b54c04 100644 --- a/src/leap/eip/tests/test_checks.py +++ b/src/leap/eip/tests/test_checks.py @@ -331,10 +331,12 @@ class ProviderCertCheckerHTTPSTests(BaseHTTPSServerTestCase, BaseLeapTest): fetcher.get(uri, verify=True) self.assertTrue( "SSL23_GET_SERVER_HELLO:unknown protocol" in exc.message) - with self.assertRaises(eipexceptions.EIPBadCertError) as exc: - checker.is_https_working(uri=uri, verify=True) - self.assertTrue( - "cert verification failed" in exc.message) + + # XXX FIXME! Uncomment after #638 is done + #with self.assertRaises(eipexceptions.EIPBadCertError) as exc: + #checker.is_https_working(uri=uri, verify=True) + #self.assertTrue( + #"cert verification failed" in exc.message) # get cacert from testing.https_server cacert = where_cert('cacert.pem') -- cgit v1.2.3 From ddf5e546916ad94c62b1e42b6f03831f906b2f29 Mon Sep 17 00:00:00 2001 From: antialias Date: Mon, 24 Sep 2012 17:34:25 -0400 Subject: improved network checks on the way to a network checker. --- src/leap/eip/checks.py | 15 +++++++-------- src/leap/eip/exceptions.py | 2 ++ src/leap/eip/tests/test_checks.py | 22 ++++++++++++++++++++-- 3 files changed, 29 insertions(+), 10 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index 9b7b1cee..82940fd3 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -70,21 +70,20 @@ class LeapNetworkChecker(object): checker.is_internet_up() checker.ping_gateway() - def test_internet_connection(self): - # XXX we're not passing the error anywhere. - # XXX we probably should raise an exception here? - # unless we use this as smoke test + def check_internet_connection(self): try: # XXX remove this hardcoded random ip requests.get('http://216.172.161.165') except (requests.HTTPError, requests.RequestException) as e: - self.error = e.message - except requests.ConenctionError as e: + raise eipexceptions.NoInternetConnection(e.message) + except requests.ConnectionError as e: + error = "Unidentified Connection Error" if e.message == "[Errno 113] No route to host": if not self.is_internet_up(): - self.error = "No valid internet connection found." + error = "No valid internet connection found." else: - self.error = "Provider server appears to be down." + error = "Provider server appears to be down." + raise eipexceptions.NoInternetConnection(error) def is_internet_up(self): iface, gateway = self.get_default_interface_gateway() diff --git a/src/leap/eip/exceptions.py b/src/leap/eip/exceptions.py index f048621f..f883a173 100644 --- a/src/leap/eip/exceptions.py +++ b/src/leap/eip/exceptions.py @@ -136,6 +136,8 @@ class NoConnectionToGateway(EIPClientError): message = "no connection to gateway" usermessage = "Looks like there are problems with your internet connection" +class NoInternetConnection(EIPClientError): + message = "No Internet connection found" # # Errors that probably we don't need anymore diff --git a/src/leap/eip/tests/test_checks.py b/src/leap/eip/tests/test_checks.py index 19b54c04..f412dbec 100644 --- a/src/leap/eip/tests/test_checks.py +++ b/src/leap/eip/tests/test_checks.py @@ -52,7 +52,7 @@ class LeapNetworkCheckTest(BaseLeapTest): def test_checker_should_implement_check_methods(self): checker = eipchecks.LeapNetworkChecker() - self.assertTrue(hasattr(checker, "test_internet_connection"), + self.assertTrue(hasattr(checker, "check_internet_connection"), "missing meth") self.assertTrue(hasattr(checker, "is_internet_up"), "missing meth") @@ -64,7 +64,7 @@ class LeapNetworkCheckTest(BaseLeapTest): mc = Mock() checker.run_all(checker=mc) - self.assertTrue(mc.test_internet_connection.called, "not called") + self.assertTrue(mc.check_internet_connection.called, "not called") self.assertTrue(mc.ping_gateway.called, "not called") self.assertTrue(mc.is_internet_up.called, "not called") @@ -86,6 +86,24 @@ class LeapNetworkCheckTest(BaseLeapTest): mocked_ping.return_value = [11, "", ""] checker.ping_gateway("4.2.2.2") + def test_check_internet_connection_failures(self): + checker = eipchecks.LeapNetworkChecker() + with patch.object(requests, "get") as mocked_get: + mocked_get.side_effect = requests.HTTPError + with self.assertRaises(eipexceptions.NoInternetConnection): + checker.check_internet_connection() + + with patch.object(requests, "get") as mocked_get: + mocked_get.side_effect = requests.RequestException + with self.assertRaises(eipexceptions.NoInternetConnection): + checker.check_internet_connection() + + #TODO: Mock possible errors that can be raised by is_internet_up + with patch.object(requests, "get") as mocked_get: + mocked_get.side_effect = requests.ConnectionError + with self.assertRaises(eipexceptions.NoInternetConnection): + checker.check_internet_connection() + @unittest.skipUnless(_uid == 0, "root only") def test_ping_gateway(self): checker = eipchecks.LeapNetworkChecker() -- cgit v1.2.3 From 4c183e81074066eb2b064896fbb741e99c50286d Mon Sep 17 00:00:00 2001 From: antialias Date: Mon, 24 Sep 2012 17:44:21 -0400 Subject: Missed a renaming of test_internet_connection to check_internet_connection. --- src/leap/eip/checks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index 82940fd3..20d1296d 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -66,7 +66,7 @@ class LeapNetworkChecker(object): self.error = None # ? # for MVS - checker.test_internet_connection() + checker.check_internet_connection() checker.is_internet_up() checker.ping_gateway() -- cgit v1.2.3 From 15b017656e6865b7b85ae389ab3b462c562a1e42 Mon Sep 17 00:00:00 2001 From: antialias Date: Tue, 25 Sep 2012 16:05:02 -0400 Subject: moved LeapNetworkChecker and test in base. --- src/leap/eip/checks.py | 76 -------------------------------------- src/leap/eip/exceptions.py | 18 --------- src/leap/eip/tests/test_checks.py | 78 --------------------------------------- 3 files changed, 172 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index 20d1296d..9872f8d8 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -39,10 +39,6 @@ into base.tests to be invoked by the base leap init routines. However, I'm testing them alltogether for the sake of having the whole unit reachable and testable as a whole. -LeapNetworkChecker ------------------- -Network checks. To be moved to base. -docs TBD """ @@ -52,78 +48,6 @@ def get_ca_cert(): return certs.where(ca_file) -class LeapNetworkChecker(object): - """ - all network related checks - """ - # XXX to be moved to leap.base.checks - # TODO eventually, use a more portable solution - # like psutil - - def run_all(self, checker=None): - if not checker: - checker = self - self.error = None # ? - - # for MVS - checker.check_internet_connection() - checker.is_internet_up() - checker.ping_gateway() - - def check_internet_connection(self): - try: - # XXX remove this hardcoded random ip - requests.get('http://216.172.161.165') - except (requests.HTTPError, requests.RequestException) as e: - raise eipexceptions.NoInternetConnection(e.message) - except requests.ConnectionError as e: - error = "Unidentified Connection Error" - if e.message == "[Errno 113] No route to host": - if not self.is_internet_up(): - error = "No valid internet connection found." - else: - error = "Provider server appears to be down." - raise eipexceptions.NoInternetConnection(error) - - def is_internet_up(self): - iface, gateway = self.get_default_interface_gateway() - self.ping_gateway(self) - - def get_default_interface_gateway(self): - """only impletemented for linux so far.""" - if not platform.system() == "Linux": - raise NotImplementedError - - f = open("/proc/net/route") - route_table = f.readlines() - f.close() - #toss out header - route_table.pop(0) - - default_iface = None - gateway = None - while route_table: - line = route_table.pop(0) - iface, destination, gateway = line.split('\t')[0:3] - if destination == '00000000': - default_iface = iface - break - - if not default_iface: - raise eipexceptions.NoDefaultInterfaceFoundError - - if default_iface not in netifaces.interfaces(): - raise eipexceptions.InterfaceNotFoundError - - return default_iface, gateway - - def ping_gateway(self, gateway): - #TODO: Discuss how much packet loss (%) is acceptable. - packet_loss = ping.quiet_ping(gateway)[0] - if packet_loss > baseconstants.MAX_ICMP_PACKET_LOSS: - raise eipexceptions.NoConnectionToGateway - - class ProviderCertChecker(object): """ Several checks needed for getting diff --git a/src/leap/eip/exceptions.py b/src/leap/eip/exceptions.py index f883a173..6b4ee6aa 100644 --- a/src/leap/eip/exceptions.py +++ b/src/leap/eip/exceptions.py @@ -121,24 +121,6 @@ class EIPInitBadProviderError(EIPClientError): class EIPConfigurationError(EIPClientError): pass - -class NoDefaultInterfaceFoundError(EIPClientError): - message = "no default interface found" - usermessage = "Looks like your computer is not connected to the internet" - - -class InterfaceNotFoundError(EIPClientError): - # XXX should take iface arg on init maybe? - message = "interface not found" - - -class NoConnectionToGateway(EIPClientError): - message = "no connection to gateway" - usermessage = "Looks like there are problems with your internet connection" - -class NoInternetConnection(EIPClientError): - message = "No Internet connection found" - # # Errors that probably we don't need anymore # chase down for them and check. diff --git a/src/leap/eip/tests/test_checks.py b/src/leap/eip/tests/test_checks.py index f412dbec..06133825 100644 --- a/src/leap/eip/tests/test_checks.py +++ b/src/leap/eip/tests/test_checks.py @@ -9,10 +9,8 @@ import os import time import urlparse -from StringIO import StringIO from mock import (patch, Mock) -import ping import requests from leap.base import config as baseconfig @@ -26,8 +24,6 @@ from leap.testing.basetest import BaseLeapTest from leap.testing.https_server import BaseHTTPSServerTestCase from leap.testing.https_server import where as where_cert -_uid = os.getuid() - class NoLogRequestHandler: def log_message(self, *args): @@ -38,78 +34,6 @@ class NoLogRequestHandler: return '' -class LeapNetworkCheckTest(BaseLeapTest): - # XXX to be moved to base.checks - - __name__ = "leap_network_check_tests" - - def setUp(self): - pass - - def tearDown(self): - pass - - def test_checker_should_implement_check_methods(self): - checker = eipchecks.LeapNetworkChecker() - - self.assertTrue(hasattr(checker, "check_internet_connection"), - "missing meth") - self.assertTrue(hasattr(checker, "is_internet_up"), - "missing meth") - self.assertTrue(hasattr(checker, "ping_gateway"), - "missing meth") - - def test_checker_should_actually_call_all_tests(self): - checker = eipchecks.LeapNetworkChecker() - - mc = Mock() - checker.run_all(checker=mc) - self.assertTrue(mc.check_internet_connection.called, "not called") - self.assertTrue(mc.ping_gateway.called, "not called") - self.assertTrue(mc.is_internet_up.called, - "not called") - - def test_get_default_interface_no_interface(self): - checker = eipchecks.LeapNetworkChecker() - with patch('leap.eip.checks.open', create=True) as mock_open: - with self.assertRaises(eipexceptions.NoDefaultInterfaceFoundError): - mock_open.return_value = StringIO( - "Iface\tDestination Gateway\t" - "Flags\tRefCntd\tUse\tMetric\t" - "Mask\tMTU\tWindow\tIRTT") - checker.get_default_interface_gateway() - - def test_ping_gateway_fail(self): - checker = eipchecks.LeapNetworkChecker() - with patch.object(ping, "quiet_ping") as mocked_ping: - with self.assertRaises(eipexceptions.NoConnectionToGateway): - mocked_ping.return_value = [11, "", ""] - checker.ping_gateway("4.2.2.2") - - def test_check_internet_connection_failures(self): - checker = eipchecks.LeapNetworkChecker() - with patch.object(requests, "get") as mocked_get: - mocked_get.side_effect = requests.HTTPError - with self.assertRaises(eipexceptions.NoInternetConnection): - checker.check_internet_connection() - - with patch.object(requests, "get") as mocked_get: - mocked_get.side_effect = requests.RequestException - with self.assertRaises(eipexceptions.NoInternetConnection): - checker.check_internet_connection() - - #TODO: Mock possible errors that can be raised by is_internet_up - with patch.object(requests, "get") as mocked_get: - mocked_get.side_effect = requests.ConnectionError - with self.assertRaises(eipexceptions.NoInternetConnection): - checker.check_internet_connection() - - @unittest.skipUnless(_uid == 0, "root only") - def test_ping_gateway(self): - checker = eipchecks.LeapNetworkChecker() - checker.ping_gateway("4.2.2.2") - - class EIPCheckTest(BaseLeapTest): __name__ = "eip_check_tests" @@ -149,8 +73,6 @@ class EIPCheckTest(BaseLeapTest): "not called") self.assertTrue(mc.check_complete_eip_config.called, "not called") - #self.assertTrue(mc.ping_gateway.called, - #"not called") # test individual check methods -- cgit v1.2.3 From abf481cab381a86d8a9c5607a131b56636081382 Mon Sep 17 00:00:00 2001 From: kali Date: Tue, 25 Sep 2012 05:48:06 +0900 Subject: refactored jsonconfig, included jsonschema validation and type casting. --- src/leap/eip/checks.py | 10 ++++++++-- src/leap/eip/config.py | 39 ++++++++++++++++++++++++++++----------- src/leap/eip/specs.py | 2 +- src/leap/eip/tests/data.py | 11 ++++++----- src/leap/eip/tests/test_checks.py | 39 +++++++++++++++++++++++---------------- src/leap/eip/tests/test_config.py | 14 ++++++++++++-- 6 files changed, 78 insertions(+), 37 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index 5ace1479..898af2fe 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -197,7 +197,8 @@ class ProviderCertChecker(object): logger.warning('False! CERT VERIFICATION FAILED! ' '(this should be CRITICAL)') logger.warning('SSLError: %s', exc.message) - raise eipexceptions.EIPBadCertError + # XXX RAISE! See #638 + #raise eipexceptions.EIPBadCertError # XXX get requests.exceptions.ConnectionError Errno 110 # Connection timed out, and raise ours. else: @@ -227,7 +228,11 @@ class ProviderCertChecker(object): if verify is True and self.cacert is not None: verify = self.cacert try: - req = self.fetcher.get(uri, verify=verify) + # XXX FIXME!!!! + # verify=verify + # Workaround for #638. return to verification + # when That's done!!! + req = self.fetcher.get(uri, verify=False) req.raise_for_status() except requests.exceptions.SSLError: logger.warning('SSLError while fetching cert. ' @@ -452,6 +457,7 @@ class EIPConfigChecker(object): # XXX TODO: # We should WRITE eip config if missing or # incomplete at this point + #self.eipconfig.save() # # private helpers diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py index 24e837d0..7c9bf335 100644 --- a/src/leap/eip/config.py +++ b/src/leap/eip/config.py @@ -55,21 +55,38 @@ def get_socket_path(): def get_eip_gateway(): """ - return the first host in the list of hosts - under gateways list + return the first host in eip service config + that matches the name defined in the eip.json config + file. """ + placeholder = "testprovider.example.org" + eipconfig = EIPConfig() + #import ipdb;ipdb.set_trace() eipconfig.load() - conf = eipconfig.get_config() - gateways = conf.get('gateways', None) + conf = eipconfig.config + + primary_gateway = conf.get('primary_gateway', None) + if not primary_gateway: + return placeholder + + eipserviceconfig = EIPServiceConfig() + eipserviceconfig.load() + eipsconf = eipserviceconfig.get_config() + gateways = eipsconf.get('gateways', None) + if not gateways: + logger.error('missing gateways in eip service config') + return placeholder if len(gateways) > 0: - # we just pick first - gw = gateways[0] - hosts = gw['hosts'] - if len(hosts) > 0: - return hosts[0] - else: - return "testprovider.example.org" + for gw in gateways: + if gw['name'] == primary_gateway: + hosts = gw['hosts'] + if len(hosts) > 0: + return hosts[0] + else: + logger.error('no hosts') + logger.error('could not find primary gateway in provider' + 'gateway list') def build_ovpn_options(daemon=False, socket_path=None, **kwargs): diff --git a/src/leap/eip/specs.py b/src/leap/eip/specs.py index a10a9623..1a670b0e 100644 --- a/src/leap/eip/specs.py +++ b/src/leap/eip/specs.py @@ -62,7 +62,7 @@ eipconfig_spec = { }, 'primary_gateway': { 'type': unicode, - 'default': u"usa_west", + 'default': u"turkey", #'required': True }, 'secondary_gateway': { diff --git a/src/leap/eip/tests/data.py b/src/leap/eip/tests/data.py index 4da0e18f..43df2013 100644 --- a/src/leap/eip/tests/data.py +++ b/src/leap/eip/tests/data.py @@ -7,7 +7,7 @@ from leap import __branding PROVIDER = __branding.get('provider_domain') -EIP_SAMPLE_JSON = { +EIP_SAMPLE_CONFIG = { "provider": "%s" % PROVIDER, "transport": "openvpn", "openvpn_protocol": "tcp", @@ -22,7 +22,7 @@ EIP_SAMPLE_JSON = { "keys/client/openvpn.pem" % PROVIDER), "connect_on_login": True, "block_cleartext_traffic": True, - "primary_gateway": "usa_west", + "primary_gateway": "turkey", "secondary_gateway": "france", #"management_password": "oph7Que1othahwiech6J" } @@ -38,9 +38,10 @@ EIP_SAMPLE_SERVICE = { "adblock": True }, "gateways": [ - {"country_code": "us", - "label": {"en":"west"}, + {"country_code": "tr", + "name": "turkey", + "label": {"en":"Ankara, Turkey"}, "capabilities": {}, - "hosts": ["1.2.3.4", "1.2.3.5"]}, + "hosts": ["94.103.43.4"]} ] } diff --git a/src/leap/eip/tests/test_checks.py b/src/leap/eip/tests/test_checks.py index 42aa9cce..582dcb84 100644 --- a/src/leap/eip/tests/test_checks.py +++ b/src/leap/eip/tests/test_checks.py @@ -12,6 +12,7 @@ import urlparse from StringIO import StringIO from mock import (patch, Mock) +import jsonschema import ping import requests @@ -149,12 +150,12 @@ class EIPCheckTest(BaseLeapTest): # force re-evaluation of the paths # small workaround for evaluating home dirs correctly - EIP_SAMPLE_JSON = copy.copy(testdata.EIP_SAMPLE_JSON) - EIP_SAMPLE_JSON['openvpn_client_certificate'] = \ + EIP_SAMPLE_CONFIG = copy.copy(testdata.EIP_SAMPLE_CONFIG) + EIP_SAMPLE_CONFIG['openvpn_client_certificate'] = \ eipspecs.client_cert_path() - EIP_SAMPLE_JSON['openvpn_ca_certificate'] = \ + EIP_SAMPLE_CONFIG['openvpn_ca_certificate'] = \ eipspecs.provider_ca_path() - self.assertEqual(deserialized, EIP_SAMPLE_JSON) + self.assertEqual(deserialized, EIP_SAMPLE_CONFIG) # TODO: shold ALSO run validation methods. @@ -171,16 +172,20 @@ class EIPCheckTest(BaseLeapTest): # ok. now, messing with real files... # blank out default_provider - sampleconfig = copy.copy(testdata.EIP_SAMPLE_JSON) + sampleconfig = copy.copy(testdata.EIP_SAMPLE_CONFIG) sampleconfig['provider'] = None eipcfg_path = checker.eipconfig.filename with open(eipcfg_path, 'w') as fp: json.dump(sampleconfig, fp) - with self.assertRaises(eipexceptions.EIPMissingDefaultProvider): + #with self.assertRaises(eipexceptions.EIPMissingDefaultProvider): + # XXX we should catch this as one of our errors, but do not + # see how to do it quickly. + with self.assertRaises(jsonschema.ValidationError): + #import ipdb;ipdb.set_trace() checker.eipconfig.load(fromfile=eipcfg_path) checker.check_is_there_default_provider() - sampleconfig = testdata.EIP_SAMPLE_JSON + sampleconfig = testdata.EIP_SAMPLE_CONFIG #eipcfg_path = checker._get_default_eipconfig_path() with open(eipcfg_path, 'w') as fp: json.dump(sampleconfig, fp) @@ -192,7 +197,7 @@ class EIPCheckTest(BaseLeapTest): mocked_get.return_value.status_code = 200 mocked_get.return_value.json = DEFAULT_PROVIDER_DEFINITION checker = eipchecks.EIPConfigChecker(fetcher=requests) - sampleconfig = testdata.EIP_SAMPLE_JSON + sampleconfig = testdata.EIP_SAMPLE_CONFIG checker.fetch_definition(config=sampleconfig) fn = os.path.join(baseconfig.get_default_provider_path(), @@ -210,22 +215,22 @@ class EIPCheckTest(BaseLeapTest): mocked_get.return_value.status_code = 200 mocked_get.return_value.json = testdata.EIP_SAMPLE_SERVICE checker = eipchecks.EIPConfigChecker(fetcher=requests) - sampleconfig = testdata.EIP_SAMPLE_JSON + sampleconfig = testdata.EIP_SAMPLE_CONFIG checker.fetch_eip_service_config(config=sampleconfig) def test_check_complete_eip_config(self): checker = eipchecks.EIPConfigChecker() with self.assertRaises(eipexceptions.EIPConfigurationError): - sampleconfig = copy.copy(testdata.EIP_SAMPLE_JSON) + sampleconfig = copy.copy(testdata.EIP_SAMPLE_CONFIG) sampleconfig['provider'] = None checker.check_complete_eip_config(config=sampleconfig) with self.assertRaises(eipexceptions.EIPConfigurationError): - sampleconfig = copy.copy(testdata.EIP_SAMPLE_JSON) + sampleconfig = copy.copy(testdata.EIP_SAMPLE_CONFIG) del sampleconfig['provider'] checker.check_complete_eip_config(config=sampleconfig) # normal case - sampleconfig = copy.copy(testdata.EIP_SAMPLE_JSON) + sampleconfig = copy.copy(testdata.EIP_SAMPLE_CONFIG) checker.check_complete_eip_config(config=sampleconfig) @@ -331,10 +336,12 @@ class ProviderCertCheckerHTTPSTests(BaseHTTPSServerTestCase, BaseLeapTest): fetcher.get(uri, verify=True) self.assertTrue( "SSL23_GET_SERVER_HELLO:unknown protocol" in exc.message) - with self.assertRaises(eipexceptions.EIPBadCertError) as exc: - checker.is_https_working(uri=uri, verify=True) - self.assertTrue( - "cert verification failed" in exc.message) + + # XXX FIXME! Uncomment after #638 is done + #with self.assertRaises(eipexceptions.EIPBadCertError) as exc: + #checker.is_https_working(uri=uri, verify=True) + #self.assertTrue( + #"cert verification failed" in exc.message) # get cacert from testing.https_server cacert = where_cert('cacert.pem') diff --git a/src/leap/eip/tests/test_config.py b/src/leap/eip/tests/test_config.py index f9f963dc..6759b522 100644 --- a/src/leap/eip/tests/test_config.py +++ b/src/leap/eip/tests/test_config.py @@ -12,7 +12,7 @@ except ImportError: #from leap.eip import config as eip_config from leap import __branding as BRANDING from leap.eip import config as eipconfig -from leap.eip.tests.data import EIP_SAMPLE_SERVICE +from leap.eip.tests.data import EIP_SAMPLE_CONFIG, EIP_SAMPLE_SERVICE from leap.testing.basetest import BaseLeapTest from leap.util.fileutil import mkdir_p @@ -47,13 +47,21 @@ class EIPConfigTest(BaseLeapTest): os.chmod(tfile, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR) def write_sample_eipservice(self): - conf = eipconfig.EIPConfig() + conf = eipconfig.EIPServiceConfig() folder, f = os.path.split(conf.filename) if not os.path.isdir(folder): mkdir_p(folder) with open(conf.filename, 'w') as fd: fd.write(json.dumps(EIP_SAMPLE_SERVICE)) + def write_sample_eipconfig(self): + conf = eipconfig.EIPConfig() + folder, f = os.path.split(conf.filename) + if not os.path.isdir(folder): + mkdir_p(folder) + with open(conf.filename, 'w') as fd: + fd.write(json.dumps(EIP_SAMPLE_CONFIG)) + def get_expected_openvpn_args(self): args = [] username = self.get_username() @@ -123,6 +131,8 @@ class EIPConfigTest(BaseLeapTest): def test_build_ovpn_command_empty_config(self): self.touch_exec() self.write_sample_eipservice() + self.write_sample_eipconfig() + from leap.eip import config as eipconfig from leap.util.fileutil import which path = os.environ['PATH'] -- cgit v1.2.3 From d0540e808749ff9f9e90ec5e055168f5f408e51b Mon Sep 17 00:00:00 2001 From: antialias Date: Mon, 1 Oct 2012 16:58:39 -0400 Subject: Now throws a CriticalError when an pre-exisiting openvpn istance is found. --- src/leap/eip/exceptions.py | 7 +++++++ src/leap/eip/openvpnconnection.py | 15 ++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/exceptions.py b/src/leap/eip/exceptions.py index f048621f..bb375cf0 100644 --- a/src/leap/eip/exceptions.py +++ b/src/leap/eip/exceptions.py @@ -93,6 +93,13 @@ class LeapBadConfigFetchedError(Warning): message = "provider sent a malformed json file" usermessage = "an error occurred during configuratio of leap services" + +class OpenVPNAlreadyRunning(EIPClientError): + message = "Another OpenVPN Process is already running." + usermessage = ("Another OpenVPN Process has been detect it." + "Please close it before starting LEAP") + + # # errors still needing some love # diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index f4d1c449..a835ead9 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -3,6 +3,7 @@ OpenVPN Connection """ from __future__ import (print_function) import logging +import psutil import socket import time from functools import partial @@ -87,6 +88,7 @@ to be triggered for each one of them. def run_openvpn_checks(self): logger.debug('running openvpn checks') + self._check_if_running_instance() self._set_ovpn_command() self._check_vpn_keys() @@ -156,9 +158,20 @@ to be triggered for each one of them. raise eip_exceptions.EIPNoCommandError if self.subp is not None: logger.debug('cowardly refusing to launch subprocess again') - return + self._launch_openvpn() + def _check_if_running_instance(self): + """ + check if openvpn is already running + """ + for process in psutil.get_process_list(): + if process.name == "openvpn": + logger.debug('an openvpn instance is already running.') + raise eip_exceptions.OpenVPNAlreadyRunning + + logger.debug('no openvpn instance found.') + def cleanup(self): """ terminates child subprocess -- cgit v1.2.3 From 3ad57cfe2851038a6e7231a428f70ea8985f7b1e Mon Sep 17 00:00:00 2001 From: kali Date: Fri, 5 Oct 2012 11:01:55 +0900 Subject: fix cert needed evaluation --- src/leap/eip/checks.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index 898af2fe..f79d47f5 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -257,7 +257,7 @@ class ProviderCertChecker(object): valid = exists() and valid_pemfile() and not_expired() if not valid: if do_raise: - raise Exception('missing cert') + raise Exception('missing valid cert') else: return False return True @@ -273,7 +273,9 @@ class ProviderCertChecker(object): with open(certfile) as cf: cert_s = cf.read() cert = crypto.X509Certificate(cert_s) - return cert.activation_time < now() < cert.expiration_time + from_ = time.gmtime(cert.activation_time) + to_ = time.gmtime(cert.expiration_time) + return from_ < now() < to_ def is_valid_pemfile(self, cert_s=None): """ -- cgit v1.2.3 From aee621fbe90016f368c74978d47b15eeb656a853 Mon Sep 17 00:00:00 2001 From: kali Date: Fri, 5 Oct 2012 19:52:14 +0900 Subject: todo comments --- src/leap/eip/checks.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index f79d47f5..413a3467 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -232,6 +232,9 @@ class ProviderCertChecker(object): # verify=verify # Workaround for #638. return to verification # when That's done!!! + + # XXX HOOK SRP here... + # will have to be more generic in the future. req = self.fetcher.get(uri, verify=False) req.raise_for_status() except requests.exceptions.SSLError: -- cgit v1.2.3 From cdc80a4b84bc68dd179376e8c4cbd7db478ffd32 Mon Sep 17 00:00:00 2001 From: kali Date: Fri, 5 Oct 2012 19:57:42 +0900 Subject: typo --- src/leap/eip/exceptions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/exceptions.py b/src/leap/eip/exceptions.py index bb375cf0..a6216caa 100644 --- a/src/leap/eip/exceptions.py +++ b/src/leap/eip/exceptions.py @@ -96,8 +96,8 @@ class LeapBadConfigFetchedError(Warning): class OpenVPNAlreadyRunning(EIPClientError): message = "Another OpenVPN Process is already running." - usermessage = ("Another OpenVPN Process has been detect it." - "Please close it before starting LEAP") + usermessage = ("Another OpenVPN Process has been detected." + "Please close it before starting leap-client") # -- cgit v1.2.3 From 1c77b95d8f0a69af582d6cddfea2e378ee2da80f Mon Sep 17 00:00:00 2001 From: antialias Date: Fri, 5 Oct 2012 11:52:30 -0400 Subject: added tests. --- src/leap/eip/tests/test_openvpnconnection.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'src/leap/eip') diff --git a/src/leap/eip/tests/test_openvpnconnection.py b/src/leap/eip/tests/test_openvpnconnection.py index 885c80b3..61769f04 100644 --- a/src/leap/eip/tests/test_openvpnconnection.py +++ b/src/leap/eip/tests/test_openvpnconnection.py @@ -1,6 +1,7 @@ import logging import os import platform +import psutil import shutil #import socket @@ -16,6 +17,7 @@ from mock import Mock, patch # MagicMock from leap.eip import config as eipconfig from leap.eip import openvpnconnection +from leap.eip import exceptions as eipexceptions from leap.eip.udstelnet import UDSTelnet from leap.testing.basetest import BaseLeapTest @@ -73,6 +75,16 @@ class OpenVPNConnectionTest(BaseLeapTest): # tests # + def test_detect_vpn(self): + openvpn_connection = openvpnconnection.OpenVPNConnection() + with patch.object(psutil, "get_process_list") as mocked_psutil: + with self.assertRaises(eipexceptions.OpenVPNAlreadyRunning): + mocked_process = Mock() + mocked_process.name = "openvpn" + mocked_psutil.return_value = [mocked_process] + openvpn_connection._check_if_running_instance() + openvpn_connection._check_if_running_instance() + @unittest.skipIf(_system == "Windows", "lin/mac only") def test_lin_mac_default_init(self): """ -- cgit v1.2.3 From 75b5abe95137c676b4390c9f43c3d50192c2392e Mon Sep 17 00:00:00 2001 From: kali Date: Fri, 5 Oct 2012 11:01:55 +0900 Subject: fix cert needed evaluation --- src/leap/eip/checks.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index 898af2fe..f79d47f5 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -257,7 +257,7 @@ class ProviderCertChecker(object): valid = exists() and valid_pemfile() and not_expired() if not valid: if do_raise: - raise Exception('missing cert') + raise Exception('missing valid cert') else: return False return True @@ -273,7 +273,9 @@ class ProviderCertChecker(object): with open(certfile) as cf: cert_s = cf.read() cert = crypto.X509Certificate(cert_s) - return cert.activation_time < now() < cert.expiration_time + from_ = time.gmtime(cert.activation_time) + to_ = time.gmtime(cert.expiration_time) + return from_ < now() < to_ def is_valid_pemfile(self, cert_s=None): """ -- cgit v1.2.3 From 6728eb9afb21bad867e4052a6190a9bdb34c928a Mon Sep 17 00:00:00 2001 From: kali Date: Mon, 8 Oct 2012 07:50:24 +0900 Subject: popup dialog error when network error happens we are shutting down for now. we should be acting upon failures in the near future. lowered the recurrent checks interval to 10 seconds. --- src/leap/eip/checks.py | 6 +++--- src/leap/eip/exceptions.py | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index 9872f8d8..b68ee23a 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -1,12 +1,12 @@ import logging import ssl -import platform +#import platform import time import os from gnutls import crypto -import netifaces -import ping +#import netifaces +#import ping import requests from leap import __branding as BRANDING diff --git a/src/leap/eip/exceptions.py b/src/leap/eip/exceptions.py index 6b4ee6aa..24c9bfe8 100644 --- a/src/leap/eip/exceptions.py +++ b/src/leap/eip/exceptions.py @@ -28,7 +28,6 @@ TODO: * EIPClientError: Should inherit from LeapException - and move basic attrs there * gettext / i18n for user messages. -- cgit v1.2.3 From 479710e977327774b9ba9e1839f75b4a38b51e5f Mon Sep 17 00:00:00 2001 From: kali Date: Mon, 8 Oct 2012 09:32:34 +0900 Subject: add leap-status to main window in non-debug mode not very DRY but just to have it ready for rc cut. --- src/leap/eip/eipconnection.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'src/leap/eip') diff --git a/src/leap/eip/eipconnection.py b/src/leap/eip/eipconnection.py index f0a98d8c..a5b59892 100644 --- a/src/leap/eip/eipconnection.py +++ b/src/leap/eip/eipconnection.py @@ -35,6 +35,9 @@ class EIPConnection(OpenVPNConnection): status_signals = kwargs.pop('status_signals', None) self.status = EIPConnectionStatus(callbacks=status_signals) + checker_signals = kwargs.pop('checker_signals', None) + self.checker_signals = checker_signals + self.provider_cert_checker = provider_cert_checker() self.config_checker = config_checker() @@ -59,10 +62,14 @@ class EIPConnection(OpenVPNConnection): try: # network (1) + for signal in self.checker_signals: + signal('checking encryption keys') self.provider_cert_checker.run_all(skip_verify=skip_verify) except Exception as exc: push_err(exc) try: + for signal in self.checker_signals: + signal('checking provider config') self.config_checker.run_all(skip_download=skip_download) except Exception as exc: push_err(exc) @@ -125,6 +132,9 @@ class EIPConnection(OpenVPNConnection): """ return self.status.get_state_icon() + def get_leap_status(self): + return self.status.get_leap_status() + # # private methods # @@ -231,6 +241,22 @@ class EIPConnectionStatus(object): } return human_status[self.current] + def get_leap_status(self): + # XXX improve nomenclature + leap_status = { + 1: 'connecting to gateway', + 2: 'connecting to gateway', + 3: 'authenticating', + 4: 'establishing network encryption', + 5: 'establishing network encryption', + 6: 'establishing network encryption', + 7: 'connected', + 8: 'reconnecting', + 9: 'exiting', + 11: 'unrecoverable error', + } + return leap_status[self.current] + def get_state_icon(self): """ returns the high level icon -- cgit v1.2.3 From 83bff4cad1e4345eb534cef28ea464b0b5a5e2fd Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 10 Oct 2012 04:23:34 +0900 Subject: fix failing test on test_eipconnection Closes #738 --- src/leap/eip/eipconnection.py | 11 +++++++---- src/leap/eip/tests/test_eipconnection.py | 9 ++++++++- 2 files changed, 15 insertions(+), 5 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/eipconnection.py b/src/leap/eip/eipconnection.py index a5b59892..2750d641 100644 --- a/src/leap/eip/eipconnection.py +++ b/src/leap/eip/eipconnection.py @@ -54,6 +54,7 @@ class EIPConnection(OpenVPNConnection): run all eip checks previous to attempting a connection """ logger.debug('running conductor checks') + print 'conductor checks!' def push_err(exc): # keep the original traceback! @@ -62,14 +63,16 @@ class EIPConnection(OpenVPNConnection): try: # network (1) - for signal in self.checker_signals: - signal('checking encryption keys') + if self.checker_signals: + for signal in self.checker_signals: + signal('checking encryption keys') self.provider_cert_checker.run_all(skip_verify=skip_verify) except Exception as exc: push_err(exc) try: - for signal in self.checker_signals: - signal('checking provider config') + if self.checker_signals: + for signal in self.checker_signals: + signal('checking provider config') self.config_checker.run_all(skip_download=skip_download) except Exception as exc: push_err(exc) diff --git a/src/leap/eip/tests/test_eipconnection.py b/src/leap/eip/tests/test_eipconnection.py index ce9d39e2..bb643ae0 100644 --- a/src/leap/eip/tests/test_eipconnection.py +++ b/src/leap/eip/tests/test_eipconnection.py @@ -89,12 +89,19 @@ class EIPConductorTest(BaseLeapTest): # config checks def test_config_checked_called(self): + # XXX this single test is taking half of the time + # needed to run tests. (roughly 3 secs for this only) + # We should modularize and inject Mocks on more places. + del(self.con) config_checker = Mock() self.con = MockedEIPConnection(config_checker=config_checker) self.assertTrue(config_checker.called) self.con.run_checks() - self.con.config_checker.run_all.assert_called_with(skip_download=False) + self.con.config_checker.run_all.assert_called_with( + skip_download=False) + + # XXX test for cert_checker also # connect/disconnect calls -- cgit v1.2.3 From cf7ddd017f20ca4a3020628999562e9b3b82bd0b Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 10 Oct 2012 05:13:29 +0900 Subject: fix geometry saving for debug/regular mode. Closes #732 --- src/leap/eip/eipconnection.py | 1 - 1 file changed, 1 deletion(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/eipconnection.py b/src/leap/eip/eipconnection.py index 2750d641..bdf70f9c 100644 --- a/src/leap/eip/eipconnection.py +++ b/src/leap/eip/eipconnection.py @@ -54,7 +54,6 @@ class EIPConnection(OpenVPNConnection): run all eip checks previous to attempting a connection """ logger.debug('running conductor checks') - print 'conductor checks!' def push_err(exc): # keep the original traceback! -- cgit v1.2.3 From b70a6664f0603297bf8b20809b5a64677900b405 Mon Sep 17 00:00:00 2001 From: kali Date: Thu, 11 Oct 2012 08:23:22 +0900 Subject: add signal to end of eip checks this fixes random error on leap initialization --- src/leap/eip/eipconnection.py | 4 ++-- src/leap/eip/openvpnconnection.py | 8 +++----- 2 files changed, 5 insertions(+), 7 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/eipconnection.py b/src/leap/eip/eipconnection.py index bdf70f9c..fea830f3 100644 --- a/src/leap/eip/eipconnection.py +++ b/src/leap/eip/eipconnection.py @@ -84,7 +84,7 @@ class EIPConnection(OpenVPNConnection): """ entry point for connection process """ - self.forget_errors() + #self.forget_errors() self._try_connection() def disconnect(self): @@ -120,7 +120,7 @@ class EIPConnection(OpenVPNConnection): logger.warning('connection refused') return if not state: - logger.debug('no state') + #logger.debug('no state') return (ts, status_step, ok, ip, remote) = state diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index a835ead9..14839f6b 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -194,10 +194,9 @@ to be triggered for each one of them. # base class to test independently?) # - def forget_errors(self): - #print('forgetting errors') - logger.debug('forgetting errors') - self.with_errors = False + #def forget_errors(self): + #logger.debug('forgetting errors') + #self.with_errors = False def connect_to_management(self): """Connect to openvpn management interface""" @@ -216,7 +215,6 @@ to be triggered for each one of them. #self.tn.read_until('SUCCESS:', 2) self._seek_to_eof() - #self.forget_errors() return True def _seek_to_eof(self): -- cgit v1.2.3 From 0875a3d498c30187a40a788d3bd1eefa9c5924e2 Mon Sep 17 00:00:00 2001 From: antialias Date: Fri, 12 Oct 2012 15:49:28 -0400 Subject: stopping openvpn via management interface. --- src/leap/eip/eipconnection.py | 19 ++++++++++--------- src/leap/eip/openvpnconnection.py | 30 +++++++++++++++++++++--------- 2 files changed, 31 insertions(+), 18 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/eipconnection.py b/src/leap/eip/eipconnection.py index fea830f3..f0e7861e 100644 --- a/src/leap/eip/eipconnection.py +++ b/src/leap/eip/eipconnection.py @@ -91,7 +91,8 @@ class EIPConnection(OpenVPNConnection): """ disconnects client """ - self._disconnect() + self.cleanup() + logger.debug("disconnect: clicked.") self.status.change_to(self.status.DISCONNECTED) def shutdown(self): @@ -141,14 +142,14 @@ class EIPConnection(OpenVPNConnection): # private methods # - def _disconnect(self): - """ - private method for disconnecting - """ - if self.subp is not None: - logger.debug('disconnecting...') - self.subp.terminate() - self.subp = None + #def _disconnect(self): + # """ + # private method for disconnecting + # """ + # if self.subp is not None: + # logger.debug('disconnecting...') + # self.subp.terminate() + # self.subp = None #def _is_alive(self): #""" diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index 14839f6b..96df4f1d 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -174,17 +174,22 @@ to be triggered for each one of them. def cleanup(self): """ - terminates child subprocess + terminates openvpn child subprocess """ - # XXX we should send a quit process using management - # interface. if self.subp: - try: - self.subp.terminate() - except OSError: - logger.error('cannot terminate subprocess!' + self._stop() + RETCODE = self.subp.wait() + if RETCODE: + logger.error('cannot terminate subprocess! ' '(maybe openvpn still running?)') + def _stop(self): + """ + stop openvpn process + """ + logger.debug("disconnecting...") + self._send_command("signal SIGTERM\n") + # # management methods # @@ -221,9 +226,16 @@ to be triggered for each one of them. """ Read as much as available. Position seek pointer to end of stream """ - b = self.tn.read_eager() - while b: + try: b = self.tn.read_eager() + except EOFError: + logger.debug("Could not read from socket. Assuming it died.") + return + while b: + try: + b = self.tn.read_eager() + except EOFError: + logger.debug("Could not read from socket. Assuming it died.") def connected(self): """ -- cgit v1.2.3 From a6c587edad293996e4015876d7e59432d6a4e8ea Mon Sep 17 00:00:00 2001 From: antialias Date: Mon, 15 Oct 2012 12:28:54 -0400 Subject: attempts to stop exisiting instances of openvpn when discovered at start up. --- src/leap/eip/openvpnconnection.py | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index 96df4f1d..d93bc40f 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -168,7 +168,9 @@ to be triggered for each one of them. for process in psutil.get_process_list(): if process.name == "openvpn": logger.debug('an openvpn instance is already running.') - raise eip_exceptions.OpenVPNAlreadyRunning + logger.debug('attempting to stop openvpn instance.') + if not self._stop(): + raise eip_exceptions.OpenVPNAlreadyRunning logger.debug('no openvpn instance found.') @@ -190,7 +192,34 @@ to be triggered for each one of them. logger.debug("disconnecting...") self._send_command("signal SIGTERM\n") - # + if self.subp: + return True + + #shutting openvpn failured + #try patching in old openvpn host and trying again + process = self._get_openvpn_process() + if process: + self.host = \ + process.cmdline[process.cmdline.index("--management") + 1] + self._send_command("signal SIGTERM\n") + + #make sure the process was terminated + process = self._get_openvpn_process() + if not process: + logger.debug("Exisiting OpenVPN Process Terminated") + return True + else: + logger.error("Unable to terminate exisiting OpenVPN Process.") + return False + + return True + + def _get_openvpn_process(self): + for process in psutil.get_process_list(): + if process.name == "openvpn": + return process + return None + # management methods # # XXX REVIEW-ME -- cgit v1.2.3 From e1dbfc454180a77ebb38ecae6244ac4abe6d0ac5 Mon Sep 17 00:00:00 2001 From: kali Date: Thu, 18 Oct 2012 09:30:53 +0900 Subject: catch cert verification errors and ask user for trust with a little helper function using gnutls --- src/leap/eip/checks.py | 27 ++++++++++++++++++++++----- src/leap/eip/exceptions.py | 11 +++++++++++ 2 files changed, 33 insertions(+), 5 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index f739c3e8..c704aef3 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -94,6 +94,7 @@ class ProviderCertChecker(object): raise NotImplementedError def is_there_provider_ca(self): + # XXX remove for generic build from leap import certs logger.debug('do we have provider_ca?') cacert_path = BRANDING.get('provider_ca_file', None) @@ -104,30 +105,46 @@ class ProviderCertChecker(object): logger.debug('True') return True - def is_https_working(self, uri=None, verify=True): + def is_https_working( + self, uri=None, verify=True, + autocacert=False): if uri is None: uri = self._get_root_uri() # XXX raise InsecureURI or something better - assert uri.startswith('https') - if verify is True and self.cacert is not None: + try: + assert uri.startswith('https') + except AssertionError: + raise AssertionError( + "uri passed should start with https") + if autocacert and verify is True and self.cacert is not None: logger.debug('verify cert: %s', self.cacert) verify = self.cacert logger.debug('is https working?') logger.debug('uri: %s (verify:%s)', uri, verify) try: self.fetcher.get(uri, verify=verify) + + except requests.exceptions.SSLError as exc: + logger.error("SSLError") + raise eipexceptions.HttpsBadCertError + + except requests.exceptions.ConnectionError: + logger.error('ConnectionError') + raise eipexceptions.HttpsNotSupported + except requests.exceptions.SSLError as exc: logger.warning('False! CERT VERIFICATION FAILED! ' '(this should be CRITICAL)') logger.warning('SSLError: %s', exc.message) # XXX RAISE! See #638 #raise eipexceptions.EIPBadCertError - # XXX get requests.exceptions.ConnectionError Errno 110 - # Connection timed out, and raise ours. else: logger.debug('True') return True + def get_certificate_fingerprint(self, domain): + pass + def check_new_cert_needed(self, skip_download=False, verify=True): logger.debug('is new cert needed?') if not self.is_cert_valid(do_raise=False): diff --git a/src/leap/eip/exceptions.py b/src/leap/eip/exceptions.py index 11bfd620..41eed77a 100644 --- a/src/leap/eip/exceptions.py +++ b/src/leap/eip/exceptions.py @@ -32,8 +32,10 @@ TODO: * gettext / i18n for user messages. """ +from leap.base.exceptions import LeapException +# This should inherit from LeapException class EIPClientError(Exception): """ base EIPClient exception @@ -99,6 +101,15 @@ class OpenVPNAlreadyRunning(EIPClientError): "Please close it before starting leap-client") +class HttpsNotSupported(LeapException): + message = "connection refused while accessing via https" + usermessage = "Server does not allow secure connections." + + +class HttpsBadCertError(LeapException): + message = "verification error on cert" + usermessage = "Server certificate could not be verified." + # # errors still needing some love # -- cgit v1.2.3 From 36957e9c926f4cc56cab383d99a8f82afc4b0302 Mon Sep 17 00:00:00 2001 From: kali Date: Fri, 19 Oct 2012 01:26:41 +0900 Subject: openvpn commands cleanup --- src/leap/eip/openvpnconnection.py | 81 +++++++++++++++++++++------------------ 1 file changed, 44 insertions(+), 37 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index d93bc40f..2ec7d08c 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -64,7 +64,7 @@ to be triggered for each one of them. #XXX workaround for signaling #the ui that we don't know how to #manage a connection error - self.with_errors = False + #self.with_errors = False self.command = None self.args = None @@ -180,41 +180,22 @@ to be triggered for each one of them. """ if self.subp: self._stop() - RETCODE = self.subp.wait() - if RETCODE: - logger.error('cannot terminate subprocess! ' - '(maybe openvpn still running?)') - def _stop(self): - """ - stop openvpn process - """ - logger.debug("disconnecting...") - self._send_command("signal SIGTERM\n") - - if self.subp: - return True + # XXX kali -- + # I think this will block if child process + # does not return. + # Maybe we can .poll() for a given + # interval and exit in any case. - #shutting openvpn failured - #try patching in old openvpn host and trying again - process = self._get_openvpn_process() - if process: - self.host = \ - process.cmdline[process.cmdline.index("--management") + 1] - self._send_command("signal SIGTERM\n") - - #make sure the process was terminated - process = self._get_openvpn_process() - if not process: - logger.debug("Exisiting OpenVPN Process Terminated") - return True - else: - logger.error("Unable to terminate exisiting OpenVPN Process.") - return False - - return True + RETCODE = self.subp.wait() + if RETCODE: + logger.error( + 'cannot terminate subprocess! ' + '(We might have left openvpn running)') def _get_openvpn_process(self): + # plist = [p for p in psutil.get_process_list() if p.name == "openvpn"] + # return plist[0] if plist else None for process in psutil.get_process_list(): if process.name == "openvpn": return process @@ -293,12 +274,7 @@ to be triggered for each one of them. self.connect_to_management() except eip_exceptions.MissingSocketError: logger.warning('missing management socket') - # This should only happen briefly during - # the first invocation. Race condition make - # the polling begin before management socket - # is ready return [] - #return self.make_error() try: if hasattr(self, 'tn'): self.tn.write(cmd + "\n") @@ -376,6 +352,37 @@ to be triggered for each one of them. """ return self._send_command("status 2") + def _stop(self): + """ + stop openvpn process + by sending SIGTERM to the management + interface + """ + logger.debug("disconnecting...") + self._send_command("signal SIGTERM\n") + + if self.subp: + return True + + #shutting openvpn failured + #try patching in old openvpn host and trying again + process = self._get_openvpn_process() + if process: + self.host = \ + process.cmdline[process.cmdline.index("--management") + 1] + self._send_command("signal SIGTERM\n") + + #make sure the process was terminated + process = self._get_openvpn_process() + if not process: + logger.debug("Existing OpenVPN Process Terminated") + return True + else: + logger.error("Unable to terminate existing OpenVPN Process.") + return False + + return True + # # parse info # -- cgit v1.2.3 From bc775969e2db31b892526b65a5037470a86b3882 Mon Sep 17 00:00:00 2001 From: kali Date: Fri, 19 Oct 2012 06:12:14 +0900 Subject: logic for cert validation widgets in wizard --- src/leap/eip/checks.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index c704aef3..560f7f53 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -142,9 +142,6 @@ class ProviderCertChecker(object): logger.debug('True') return True - def get_certificate_fingerprint(self, domain): - pass - def check_new_cert_needed(self, skip_download=False, verify=True): logger.debug('is new cert needed?') if not self.is_cert_valid(do_raise=False): @@ -347,7 +344,8 @@ class EIPConfigChecker(object): return True def fetch_definition(self, skip_download=False, - config=None, uri=None): + config=None, uri=None, + domain=None): """ fetches a definition file from server """ @@ -364,7 +362,8 @@ class EIPConfigChecker(object): if config is None: config = self.defaultprovider.config if uri is None: - domain = config.get('provider', None) + if not domain: + domain = config.get('provider', None) uri = self._get_provider_definition_uri(domain=domain) # FIXME! Pass ca path verify!!! -- cgit v1.2.3 From 2a01c969e0f8dff575007043996c3b0489e20e75 Mon Sep 17 00:00:00 2001 From: kali Date: Fri, 19 Oct 2012 08:18:34 +0900 Subject: download ca cert from provider --- src/leap/eip/checks.py | 53 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 44 insertions(+), 9 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index 560f7f53..e925e11c 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -4,13 +4,14 @@ import ssl import time import os -from gnutls import crypto +import gnutls.crypto #import netifaces #import ping import requests from leap import __branding as BRANDING from leap import certs +from leap.base import config as baseconfig from leap.base import constants as baseconstants from leap.base import providers from leap.eip import config as eipconfig @@ -54,18 +55,25 @@ class ProviderCertChecker(object): client certs and checking tls connection with provider. """ - def __init__(self, fetcher=requests): + def __init__(self, fetcher=requests, + domain=None): + self.fetcher = fetcher + self.domain = domain self.cacert = get_ca_cert() - def run_all(self, checker=None, skip_download=False, skip_verify=False): + def run_all( + self, checker=None, + skip_download=False, skip_verify=False): + if not checker: checker = self do_verify = not skip_verify logger.debug('do_verify: %s', do_verify) - # For MVS+ # checker.download_ca_cert() + + # For MVS+ # checker.download_ca_signature() # checker.get_ca_signatures() # checker.is_there_trust_path() @@ -77,9 +85,19 @@ class ProviderCertChecker(object): checker.is_https_working(verify=do_verify) checker.check_new_cert_needed(verify=do_verify) - def download_ca_cert(self): - # MVS+ - raise NotImplementedError + def download_ca_cert(self, uri=None, verify=True): + req = self.fetcher.get(uri, verify=verify) + req.raise_for_status() + + # should check domain exists + capath = self._get_ca_cert_path(self.domain) + with open(capath, 'w') as f: + f.write(req.content) + + def check_ca_cert_fingerprint( + self, hash_type="SHA256", + fingerprint=None): + pass def download_ca_signature(self): # MVS+ @@ -94,11 +112,12 @@ class ProviderCertChecker(object): raise NotImplementedError def is_there_provider_ca(self): - # XXX remove for generic build + # XXX modify for generic build from leap import certs logger.debug('do we have provider_ca?') cacert_path = BRANDING.get('provider_ca_file', None) if not cacert_path: + # XXX look from the domain logger.debug('False') return False self.cacert = certs.where(cacert_path) @@ -212,7 +231,7 @@ class ProviderCertChecker(object): certfile = self._get_client_cert_path() with open(certfile) as cf: cert_s = cf.read() - cert = crypto.X509Certificate(cert_s) + cert = gnutls.crypto.X509Certificate(cert_s) from_ = time.gmtime(cert.activation_time) to_ = time.gmtime(cert.expiration_time) return from_ < now() < to_ @@ -247,6 +266,10 @@ class ProviderCertChecker(object): raise return True + @property + def ca_cert_path(self): + return self._get_ca_cert_path() + def _get_root_uri(self): return u"https://%s/" % baseconstants.DEFAULT_PROVIDER @@ -258,6 +281,18 @@ class ProviderCertChecker(object): # MVS+ : get provider path return eipspecs.client_cert_path() + def _get_ca_cert_path(self, domain): + # XXX this folder path will be broken for win + # and this should be moved to eipspecs.ca_path + + capath = baseconfig.get_config_file( + 'cacert.pem', + folder='providers/%s/certs/ca' % domain) + folder, fname = os.path.split(capath) + if not os.path.isdir(folder): + mkdir_p(folder) + return capath + def write_cert(self, pemfile_content, to=None): folder, filename = os.path.split(to) if not os.path.isdir(folder): -- cgit v1.2.3 From 634030e5bba3fe7c2ea3632fff252a60b471487a Mon Sep 17 00:00:00 2001 From: kali Date: Fri, 19 Oct 2012 09:05:14 +0900 Subject: ca cert fingerprint check + api cert verification --- src/leap/eip/checks.py | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index e925e11c..1c29dab1 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -10,10 +10,11 @@ import gnutls.crypto import requests from leap import __branding as BRANDING -from leap import certs +from leap import certs as leapcerts from leap.base import config as baseconfig from leap.base import constants as baseconstants from leap.base import providers +from leap.crypto import certs from leap.eip import config as eipconfig from leap.eip import constants as eipconstants from leap.eip import exceptions as eipexceptions @@ -46,7 +47,7 @@ reachable and testable as a whole. def get_ca_cert(): ca_file = BRANDING.get('provider_ca_file') if ca_file: - return certs.where(ca_file) + return leapcerts.where(ca_file) class ProviderCertChecker(object): @@ -97,7 +98,18 @@ class ProviderCertChecker(object): def check_ca_cert_fingerprint( self, hash_type="SHA256", fingerprint=None): - pass + ca_cert_path = self.ca_cert_path + ca_cert_fpr = certs.get_cert_fingerprint( + filepath=ca_cert_path) + return ca_cert_fpr == fingerprint + + def verify_api_https(self, uri): + assert uri.startswith('https://') + cacert = self.ca_cert_path + verify = cacert and cacert or True + req = self.fetcher.get(uri, verify=verify) + req.raise_for_status() + return True def download_ca_signature(self): # MVS+ @@ -268,7 +280,7 @@ class ProviderCertChecker(object): @property def ca_cert_path(self): - return self._get_ca_cert_path() + return self._get_ca_cert_path(self.domain) def _get_root_uri(self): return u"https://%s/" % baseconstants.DEFAULT_PROVIDER -- cgit v1.2.3 From b0be517ed8b2fb9dd0a38dad5b5c06741b6b9b09 Mon Sep 17 00:00:00 2001 From: kali Date: Fri, 19 Oct 2012 11:02:44 +0900 Subject: add bug number in log for #638 cases (domain name mismatch) --- src/leap/eip/checks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index 1c29dab1..74afd677 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -164,7 +164,7 @@ class ProviderCertChecker(object): raise eipexceptions.HttpsNotSupported except requests.exceptions.SSLError as exc: - logger.warning('False! CERT VERIFICATION FAILED! ' + logger.warning('BUG #638 CERT VERIFICATION FAILED! ' '(this should be CRITICAL)') logger.warning('SSLError: %s', exc.message) # XXX RAISE! See #638 -- cgit v1.2.3 From f791a83ce57cef7010da819d61e7f5132fa4611e Mon Sep 17 00:00:00 2001 From: kali Date: Sat, 20 Oct 2012 06:30:16 +0900 Subject: connecting page and changes to functions having to do with the default path to certs. --- src/leap/eip/checks.py | 67 +++++++++++++++++++++++++++++++++++++++----------- src/leap/eip/specs.py | 21 +++++++++++++--- 2 files changed, 69 insertions(+), 19 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index 74afd677..635308bb 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -11,6 +11,7 @@ import requests from leap import __branding as BRANDING from leap import certs as leapcerts +from leap.base.auth import srpauth_protected from leap.base import config as baseconfig from leap.base import constants as baseconstants from leap.base import providers @@ -98,6 +99,17 @@ class ProviderCertChecker(object): def check_ca_cert_fingerprint( self, hash_type="SHA256", fingerprint=None): + """ + compares the fingerprint in + the ca cert with a string + we are passed + returns True if they are equal, False if not. + @param hash_type: digest function + @type hash_type: str + @param fingerprint: the fingerprint to compare with. + @type fingerprint: str (with : separator) + @rtype bool + """ ca_cert_path = self.ca_cert_path ca_cert_fpr = certs.get_cert_fingerprint( filepath=ca_cert_path) @@ -185,7 +197,8 @@ class ProviderCertChecker(object): return False def download_new_client_cert(self, uri=None, verify=True, - skip_download=False): + skip_download=False, + credentials=None): logger.debug('download new client cert') if skip_download: return True @@ -193,18 +206,34 @@ class ProviderCertChecker(object): uri = self._get_client_cert_uri() # XXX raise InsecureURI or something better assert uri.startswith('https') + if verify is True and self.cacert is not None: verify = self.cacert + + fgetfn = self.fetcher.get + + if credentials: + user, passwd = credentials + + @srpauth_protected(user, passwd) + def getfn(*args, **kwargs): + return fgetfn(*args, **kwargs) + + else: + # XXX use magic_srpauth decorator instead, + # merge with the branch above + def getfn(*args, **kwargs): + return fgetfn(*args, **kwargs) try: + # XXX FIXME!!!! # verify=verify # Workaround for #638. return to verification # when That's done!!! - - # XXX HOOK SRP here... - # will have to be more generic in the future. - req = self.fetcher.get(uri, verify=False) + #req = self.fetcher.get(uri, verify=False) + req = getfn(uri, verify=False) req.raise_for_status() + except requests.exceptions.SSLError: logger.warning('SSLError while fetching cert. ' 'Look below for stack trace.') @@ -283,23 +312,26 @@ class ProviderCertChecker(object): return self._get_ca_cert_path(self.domain) def _get_root_uri(self): - return u"https://%s/" % baseconstants.DEFAULT_PROVIDER + return u"https://%s/" % self.domain def _get_client_cert_uri(self): # XXX get the whole thing from constants - return "https://%s/1/cert" % (baseconstants.DEFAULT_PROVIDER) + return "https://%s/1/cert" % self.domain def _get_client_cert_path(self): # MVS+ : get provider path - return eipspecs.client_cert_path() + return eipspecs.client_cert_path(domain=self.domain) def _get_ca_cert_path(self, domain): # XXX this folder path will be broken for win # and this should be moved to eipspecs.ca_path + # XXX use baseconfig.get_provider_path(folder=Foo) + # !!! + capath = baseconfig.get_config_file( 'cacert.pem', - folder='providers/%s/certs/ca' % domain) + folder='providers/%s/keys/ca' % domain) folder, fname = os.path.split(capath) if not os.path.isdir(folder): mkdir_p(folder) @@ -321,16 +353,20 @@ class EIPConfigChecker(object): use run_all to run all checks. """ - def __init__(self, fetcher=requests): + def __init__(self, fetcher=requests, domain=None): # we do not want to accept too many # argument on init. # we want tests # to be explicitely run. + self.fetcher = fetcher - self.eipconfig = eipconfig.EIPConfig() - self.defaultprovider = providers.LeapProviderDefinition() - self.eipserviceconfig = eipconfig.EIPServiceConfig() + # if not domain, get from config + self.domain = domain + + self.eipconfig = eipconfig.EIPConfig(domain=domain) + self.defaultprovider = providers.LeapProviderDefinition(domain=domain) + self.eipserviceconfig = eipconfig.EIPServiceConfig(domain=domain) def run_all(self, checker=None, skip_download=False): """ @@ -421,13 +457,14 @@ class EIPConfigChecker(object): self.defaultprovider.save() def fetch_eip_service_config(self, skip_download=False, - config=None, uri=None): + config=None, uri=None, domain=None): if skip_download: return True if config is None: config = self.eipserviceconfig.config if uri is None: - domain = config.get('provider', None) + if not domain: + domain = config.get('provider', None) uri = self._get_eip_service_uri(domain=domain) self.eipserviceconfig.load(from_uri=uri, fetcher=self.fetcher) diff --git a/src/leap/eip/specs.py b/src/leap/eip/specs.py index 1a670b0e..4014b7c9 100644 --- a/src/leap/eip/specs.py +++ b/src/leap/eip/specs.py @@ -8,7 +8,14 @@ PROVIDER_CA_CERT = __branding.get( 'provider_ca_file', 'testprovider-ca-cert.pem') -provider_ca_path = lambda: str(os.path.join( +provider_ca_path = lambda domain: str(os.path.join( + #baseconfig.get_default_provider_path(), + baseconfig.get_provider_path(domain), + 'keys', 'ca', + 'cacert.pem' +)) + +default_provider_ca_path = lambda: str(os.path.join( baseconfig.get_default_provider_path(), 'keys', 'ca', PROVIDER_CA_CERT @@ -17,7 +24,13 @@ provider_ca_path = lambda: str(os.path.join( PROVIDER_DOMAIN = __branding.get('provider_domain', 'testprovider.example.org') -client_cert_path = lambda: unicode(os.path.join( +client_cert_path = lambda domain: unicode(os.path.join( + baseconfig.get_provider_path(domain), + 'keys', 'client', + 'openvpn.pem' +)) + +default_client_cert_path = lambda: unicode(os.path.join( baseconfig.get_default_provider_path(), 'keys', 'client', 'openvpn.pem' @@ -46,11 +59,11 @@ eipconfig_spec = { }, 'openvpn_ca_certificate': { 'type': unicode, # path - 'default': provider_ca_path + 'default': default_provider_ca_path }, 'openvpn_client_certificate': { 'type': unicode, # path - 'default': client_cert_path + 'default': default_client_cert_path }, 'connect_on_login': { 'type': bool, -- cgit v1.2.3 From a85e488ed323ba35b9d12c5cc344bf06337a9a00 Mon Sep 17 00:00:00 2001 From: kali Date: Sat, 20 Oct 2012 07:13:22 +0900 Subject: add bypass for already trusted fingerprints --- src/leap/eip/checks.py | 1 - src/leap/eip/config.py | 11 +++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index 635308bb..b335b857 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -319,7 +319,6 @@ class ProviderCertChecker(object): return "https://%s/1/cert" % self.domain def _get_client_cert_path(self): - # MVS+ : get provider path return eipspecs.client_cert_path(domain=self.domain) def _get_ca_cert_path(self, domain): diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py index ef0f52b4..1ce4a54e 100644 --- a/src/leap/eip/config.py +++ b/src/leap/eip/config.py @@ -78,8 +78,15 @@ def get_eip_gateway(): return placeholder if len(gateways) > 0: for gw in gateways: - if gw['name'] == primary_gateway: - hosts = gw['hosts'] + name = gw.get('name', None) + if not name: + return + + if name == primary_gateway: + hosts = gw.get('hosts', None) + if not hosts: + logger.error('no hosts') + return if len(hosts) > 0: return hosts[0] else: -- cgit v1.2.3 From 0060d3c74adce19fab7215b3788c5197cc05a9ae Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 24 Oct 2012 04:05:19 +0900 Subject: sign up branch ends by triggering eip connection still need to bind signals properly, and block on the validation process until we receive the "connected" signal. but the basic flow is working again, i.e, user should be able to remove the .config/leap folder and get all the needed info from the provider. --- src/leap/eip/checks.py | 37 +++++++++++++++++-------------------- src/leap/eip/config.py | 19 +++++++++++++------ src/leap/eip/eipconnection.py | 15 +++++++++++++-- src/leap/eip/openvpnconnection.py | 4 ++-- src/leap/eip/specs.py | 6 ++++-- 5 files changed, 49 insertions(+), 32 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index b335b857..44c8f234 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -45,7 +45,8 @@ reachable and testable as a whole. """ -def get_ca_cert(): +def get_branding_ca_cert(domain): + # XXX deprecated ca_file = BRANDING.get('provider_ca_file') if ca_file: return leapcerts.where(ca_file) @@ -62,7 +63,7 @@ class ProviderCertChecker(object): self.fetcher = fetcher self.domain = domain - self.cacert = get_ca_cert() + self.cacert = eipspecs.provider_ca_path(domain) def run_all( self, checker=None, @@ -84,7 +85,7 @@ class ProviderCertChecker(object): checker.is_there_provider_ca() # XXX FAKE IT!!! - checker.is_https_working(verify=do_verify) + checker.is_https_working(verify=do_verify, autocacert=True) checker.check_new_cert_needed(verify=do_verify) def download_ca_cert(self, uri=None, verify=True): @@ -136,17 +137,14 @@ class ProviderCertChecker(object): raise NotImplementedError def is_there_provider_ca(self): - # XXX modify for generic build - from leap import certs - logger.debug('do we have provider_ca?') - cacert_path = BRANDING.get('provider_ca_file', None) - if not cacert_path: - # XXX look from the domain - logger.debug('False') + if not self.cacert: return False - self.cacert = certs.where(cacert_path) - logger.debug('True') - return True + cacert_exists = os.path.isfile(self.cacert) + if cacert_exists: + logger.debug('True') + return True + logger.debug('False!') + return False def is_https_working( self, uri=None, verify=True, @@ -162,6 +160,7 @@ class ProviderCertChecker(object): if autocacert and verify is True and self.cacert is not None: logger.debug('verify cert: %s', self.cacert) verify = self.cacert + #import pdb4qt; pdb4qt.set_trace() logger.debug('is https working?') logger.debug('uri: %s (verify:%s)', uri, verify) try: @@ -169,18 +168,16 @@ class ProviderCertChecker(object): except requests.exceptions.SSLError as exc: logger.error("SSLError") - raise eipexceptions.HttpsBadCertError + # XXX RAISE! See #638 + #raise eipexceptions.HttpsBadCertError + logger.warning('BUG #638 CERT VERIFICATION FAILED! ' + '(this should be CRITICAL)') + logger.warning('SSLError: %s', exc.message) except requests.exceptions.ConnectionError: logger.error('ConnectionError') raise eipexceptions.HttpsNotSupported - except requests.exceptions.SSLError as exc: - logger.warning('BUG #638 CERT VERIFICATION FAILED! ' - '(this should be CRITICAL)') - logger.warning('SSLError: %s', exc.message) - # XXX RAISE! See #638 - #raise eipexceptions.EIPBadCertError else: logger.debug('True') return True diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py index 1ce4a54e..57e15c9e 100644 --- a/src/leap/eip/config.py +++ b/src/leap/eip/config.py @@ -110,6 +110,8 @@ def build_ovpn_options(daemon=False, socket_path=None, **kwargs): # since we will need to take some # things from there if present. + provider = kwargs.pop('provider', None) + # get user/group name # also from config. user = baseconfig.get_username() @@ -136,6 +138,7 @@ def build_ovpn_options(daemon=False, socket_path=None, **kwargs): logger.debug('setting eip gateway to %s', gw) opts.append(str(gw)) opts.append('1194') + #opts.append('80') opts.append('udp') opts.append('--tls-client') @@ -172,12 +175,15 @@ def build_ovpn_options(daemon=False, socket_path=None, **kwargs): opts.append('7777') # certs + client_cert_path = eipspecs.client_cert_path(provider) + ca_cert_path = eipspecs.provider_ca_path(provider) + opts.append('--cert') - opts.append(eipspecs.client_cert_path()) + opts.append(client_cert_path) opts.append('--key') - opts.append(eipspecs.client_cert_path()) + opts.append(client_cert_path) opts.append('--ca') - opts.append(eipspecs.provider_ca_path()) + opts.append(ca_cert_path) # we cannot run in daemon mode # with the current subp setting. @@ -245,7 +251,7 @@ def build_ovpn_command(debug=False, do_pkexec_check=True, vpnbin=None, return [command[0], command[1:]] -def check_vpn_keys(): +def check_vpn_keys(provider=None): """ performs an existance and permission check over the openvpn keys file. @@ -253,8 +259,9 @@ def check_vpn_keys(): per provider, containing the CA cert, the provider key, and our client certificate """ - provider_ca = eipspecs.provider_ca_path() - client_cert = eipspecs.client_cert_path() + assert provider is not None + provider_ca = eipspecs.provider_ca_path(provider) + client_cert = eipspecs.client_cert_path(provider) logger.debug('provider ca = %s', provider_ca) logger.debug('client cert = %s', client_cert) diff --git a/src/leap/eip/eipconnection.py b/src/leap/eip/eipconnection.py index f0e7861e..d4aeddf6 100644 --- a/src/leap/eip/eipconnection.py +++ b/src/leap/eip/eipconnection.py @@ -29,6 +29,7 @@ class EIPConnection(OpenVPNConnection): *args, **kwargs): self.settingsfile = kwargs.get('settingsfile', None) self.logfile = kwargs.get('logfile', None) + self.provider = kwargs.pop('provider', None) self.error_queue = Queue.Queue() @@ -38,8 +39,10 @@ class EIPConnection(OpenVPNConnection): checker_signals = kwargs.pop('checker_signals', None) self.checker_signals = checker_signals - self.provider_cert_checker = provider_cert_checker() - self.config_checker = config_checker() + # initialize checkers + self.provider_cert_checker = provider_cert_checker( + domain=self.provider) + self.config_checker = config_checker(domain=self.provider) host = eipconfig.get_socket_path() kwargs['host'] = host @@ -49,6 +52,14 @@ class EIPConnection(OpenVPNConnection): def has_errors(self): return True if self.error_queue.qsize() != 0 else False + def set_provider_domain(self, domain): + """ + sets the provider domain. + used from the first run wizard when we launch the run_checks + and connect process after having initialized the conductor. + """ + self.provider = domain + def run_checks(self, skip_download=False, skip_verify=False): """ run all eip checks previous to attempting a connection diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index 2ec7d08c..d7c571bc 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -25,7 +25,6 @@ class OpenVPNConnection(Connection): """ def __init__(self, - #config_file=None, watcher_cb=None, debug=False, host=None, @@ -96,6 +95,7 @@ to be triggered for each one of them. # XXX check also for command-line --command flag try: command, args = eip_config.build_ovpn_command( + provider=self.provider, debug=self.debug, socket_path=self.host, ovpn_verbosity=self.ovpn_verbosity) @@ -115,7 +115,7 @@ to be triggered for each one of them. checks for correct permissions on vpn keys """ try: - eip_config.check_vpn_keys() + eip_config.check_vpn_keys(provider=self.provider) except eip_exceptions.EIPInitBadKeyFilePermError: logger.error('Bad VPN Keys permission!') # do nothing now diff --git a/src/leap/eip/specs.py b/src/leap/eip/specs.py index 4014b7c9..84b2597d 100644 --- a/src/leap/eip/specs.py +++ b/src/leap/eip/specs.py @@ -4,6 +4,8 @@ import os from leap import __branding from leap.base import config as baseconfig +# XXX move provider stuff to base config + PROVIDER_CA_CERT = __branding.get( 'provider_ca_file', 'testprovider-ca-cert.pem') @@ -13,7 +15,7 @@ provider_ca_path = lambda domain: str(os.path.join( baseconfig.get_provider_path(domain), 'keys', 'ca', 'cacert.pem' -)) +)) if domain else None default_provider_ca_path = lambda: str(os.path.join( baseconfig.get_default_provider_path(), @@ -28,7 +30,7 @@ client_cert_path = lambda domain: unicode(os.path.join( baseconfig.get_provider_path(domain), 'keys', 'client', 'openvpn.pem' -)) +)) if domain else None default_client_cert_path = lambda: unicode(os.path.join( baseconfig.get_default_provider_path(), -- cgit v1.2.3 From ec888610b0a76cf5d0659e51f36265a4de42b8d7 Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 24 Oct 2012 07:41:21 +0900 Subject: fix checks that were getting default provider domain var still --- src/leap/eip/checks.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index 44c8f234..cd9d9972 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -495,7 +495,7 @@ class EIPConfigChecker(object): def _get_provider_definition_uri(self, domain=None, path=None): if domain is None: - domain = baseconstants.DEFAULT_PROVIDER + domain = self.domain or baseconstants.DEFAULT_PROVIDER if path is None: path = baseconstants.DEFINITION_EXPECTED_PATH uri = u"https://%s/%s" % (domain, path) @@ -504,7 +504,7 @@ class EIPConfigChecker(object): def _get_eip_service_uri(self, domain=None, path=None): if domain is None: - domain = baseconstants.DEFAULT_PROVIDER + domain = self.domain or baseconstants.DEFAULT_PROVIDER if path is None: path = eipconstants.EIP_SERVICE_EXPECTED_PATH uri = "https://%s/%s" % (domain, path) -- cgit v1.2.3 From 0590991d7777de473a7df21ed32e1fa7caa9cf4b Mon Sep 17 00:00:00 2001 From: kali Date: Fri, 26 Oct 2012 00:12:08 +0900 Subject: user credentials saved on login/signup branches. cert request is using magick decorator that retrieves the certificates using srp. --- src/leap/eip/checks.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index cd9d9972..ae3634bc 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -11,7 +11,7 @@ import requests from leap import __branding as BRANDING from leap import certs as leapcerts -from leap.base.auth import srpauth_protected +from leap.base.auth import srpauth_protected, magick_srpauth from leap.base import config as baseconfig from leap.base import constants as baseconstants from leap.base import providers @@ -217,8 +217,7 @@ class ProviderCertChecker(object): return fgetfn(*args, **kwargs) else: - # XXX use magic_srpauth decorator instead, - # merge with the branch above + @magick_srpauth def getfn(*args, **kwargs): return fgetfn(*args, **kwargs) try: -- cgit v1.2.3 From 593e4ba1ddf185d14f27c96ffb970fde7a3271fa Mon Sep 17 00:00:00 2001 From: kali Date: Fri, 26 Oct 2012 02:04:34 +0900 Subject: fix systray context menu. Closes #761 --- src/leap/eip/eipconnection.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/eipconnection.py b/src/leap/eip/eipconnection.py index d4aeddf6..acd40beb 100644 --- a/src/leap/eip/eipconnection.py +++ b/src/leap/eip/eipconnection.py @@ -106,11 +106,11 @@ class EIPConnection(OpenVPNConnection): logger.debug("disconnect: clicked.") self.status.change_to(self.status.DISCONNECTED) - def shutdown(self): - """ - shutdown and quit - """ - self.desired_con_state = self.status.DISCONNECTED + #def shutdown(self): + #""" + #shutdown and quit + #""" + #self.desired_con_state = self.status.DISCONNECTED def connection_state(self): """ @@ -121,10 +121,6 @@ class EIPConnection(OpenVPNConnection): def poll_connection_state(self): """ """ - # XXX this separation does not - # make sense anymore after having - # merged Connection and Manager classes. - # XXX GET RID OF THIS FUNCTION HERE! try: state = self.get_connection_state() except eip_exceptions.ConnectionRefusedError: @@ -132,7 +128,7 @@ class EIPConnection(OpenVPNConnection): logger.warning('connection refused') return if not state: - #logger.debug('no state') + logger.debug('no state') return (ts, status_step, ok, ip, remote) = state @@ -258,6 +254,7 @@ class EIPConnectionStatus(object): def get_leap_status(self): # XXX improve nomenclature leap_status = { + 0: 'disconnected', 1: 'connecting to gateway', 2: 'connecting to gateway', 3: 'authenticating', -- cgit v1.2.3 From b84007d8fec8c949ba4ac1d26695c710a210d797 Mon Sep 17 00:00:00 2001 From: kali Date: Thu, 8 Nov 2012 08:37:24 +0900 Subject: more careful error catching during registration. added a twisted server that fakes some of the provider interaction. --- src/leap/eip/checks.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index ae3634bc..9bd96a1c 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -212,12 +212,12 @@ class ProviderCertChecker(object): if credentials: user, passwd = credentials - @srpauth_protected(user, passwd) + @srpauth_protected(user, passwd, verify) def getfn(*args, **kwargs): return fgetfn(*args, **kwargs) else: - @magick_srpauth + @magick_srpauth(verify) def getfn(*args, **kwargs): return fgetfn(*args, **kwargs) try: -- cgit v1.2.3 From fc857d25025ea07d46a8c8f5ffd4fb2902a76c13 Mon Sep 17 00:00:00 2001 From: kali Date: Mon, 12 Nov 2012 10:04:09 +0900 Subject: fix cert fetching over https --- src/leap/eip/checks.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index 9bd96a1c..caaef2ea 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -212,11 +212,16 @@ class ProviderCertChecker(object): if credentials: user, passwd = credentials - @srpauth_protected(user, passwd, verify) + logger.debug('domain = %s', self.domain) + + @srpauth_protected(user, passwd, + server="https://%s" % self.domain, + verify=verify) def getfn(*args, **kwargs): return fgetfn(*args, **kwargs) else: + # XXX FIXME fix decorated args @magick_srpauth(verify) def getfn(*args, **kwargs): return fgetfn(*args, **kwargs) -- cgit v1.2.3 From 50ae1a415698af8aaa2fbed186a9f05037a9bfd9 Mon Sep 17 00:00:00 2001 From: kali Date: Mon, 12 Nov 2012 22:00:58 +0900 Subject: catch error when management interface is missing during shutdown --- src/leap/eip/openvpnconnection.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index d7c571bc..34f1e18b 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -179,7 +179,12 @@ to be triggered for each one of them. terminates openvpn child subprocess """ if self.subp: - self._stop() + try: + self._stop() + except eip_exceptions.ConnectionRefusedError: + logger.warning( + 'unable to send sigterm signal to openvpn: ' + 'connection refused.') # XXX kali -- # I think this will block if child process @@ -190,8 +195,8 @@ to be triggered for each one of them. RETCODE = self.subp.wait() if RETCODE: logger.error( - 'cannot terminate subprocess! ' - '(We might have left openvpn running)') + 'cannot terminate subprocess! Retcode %s' + '(We might have left openvpn running)' % RETCODE) def _get_openvpn_process(self): # plist = [p for p in psutil.get_process_list() if p.name == "openvpn"] -- cgit v1.2.3 From d2d2bbd96a44c347c248a7abb2ee72d7d728d79f Mon Sep 17 00:00:00 2001 From: kali Date: Tue, 13 Nov 2012 20:51:22 +0900 Subject: remove sample service Ip for example.org --- src/leap/eip/tests/data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/tests/data.py b/src/leap/eip/tests/data.py index 43df2013..f1d3b0bc 100644 --- a/src/leap/eip/tests/data.py +++ b/src/leap/eip/tests/data.py @@ -42,6 +42,6 @@ EIP_SAMPLE_SERVICE = { "name": "turkey", "label": {"en":"Ankara, Turkey"}, "capabilities": {}, - "hosts": ["94.103.43.4"]} + "hosts": ["192.0.43.10"]} ] } -- cgit v1.2.3 From d2dcf5a1060d60c451570349a6a06ad102d6924c Mon Sep 17 00:00:00 2001 From: kali Date: Tue, 13 Nov 2012 21:54:04 +0900 Subject: fix missing provider parameter in leapconfig objects chain --- src/leap/eip/checks.py | 4 +++- src/leap/eip/config.py | 19 ++++++++++++------- src/leap/eip/eipconnection.py | 18 ++++++++++++++---- 3 files changed, 29 insertions(+), 12 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index caaef2ea..116c535e 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -450,6 +450,8 @@ class EIPConfigChecker(object): uri = self._get_provider_definition_uri(domain=domain) # FIXME! Pass ca path verify!!! + # BUG #638 + # FIXME FIXME FIXME self.defaultprovider.load( from_uri=uri, fetcher=self.fetcher, @@ -464,7 +466,7 @@ class EIPConfigChecker(object): config = self.eipserviceconfig.config if uri is None: if not domain: - domain = config.get('provider', None) + domain = self.domain or config.get('provider', None) uri = self._get_eip_service_uri(domain=domain) self.eipserviceconfig.load(from_uri=uri, fetcher=self.fetcher) diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py index 57e15c9e..42c00380 100644 --- a/src/leap/eip/config.py +++ b/src/leap/eip/config.py @@ -35,9 +35,13 @@ class EIPServiceConfig(baseconfig.JSONLeapConfig): spec = eipspecs.eipservice_config_spec def _get_slug(self): + domain = getattr(self, 'domain', None) + if domain: + path = baseconfig.get_provider_path(domain) + else: + path = baseconfig.get_default_provider_path() return baseconfig.get_config_file( - 'eip-service.json', - folder=baseconfig.get_default_provider_path()) + 'eip-service.json', folder=path) def _set_slug(self): raise AttributeError("you cannot set slug") @@ -53,15 +57,16 @@ def get_socket_path(): return socket_path -def get_eip_gateway(): +def get_eip_gateway(provider=None): """ return the first host in eip service config that matches the name defined in the eip.json config file. """ placeholder = "testprovider.example.org" - eipconfig = EIPConfig() - #import ipdb;ipdb.set_trace() + # XXX check for null on provider?? + + eipconfig = EIPConfig(domain=provider) eipconfig.load() conf = eipconfig.config @@ -69,7 +74,7 @@ def get_eip_gateway(): if not primary_gateway: return placeholder - eipserviceconfig = EIPServiceConfig() + eipserviceconfig = EIPServiceConfig(domain=provider) eipserviceconfig.load() eipsconf = eipserviceconfig.get_config() gateways = eipsconf.get('gateways', None) @@ -134,7 +139,7 @@ def build_ovpn_options(daemon=False, socket_path=None, **kwargs): # remote opts.append('--remote') - gw = get_eip_gateway() + gw = get_eip_gateway(provider=provider) logger.debug('setting eip gateway to %s', gw) opts.append(str(gw)) opts.append('1194') diff --git a/src/leap/eip/eipconnection.py b/src/leap/eip/eipconnection.py index acd40beb..7828c864 100644 --- a/src/leap/eip/eipconnection.py +++ b/src/leap/eip/eipconnection.py @@ -30,6 +30,8 @@ class EIPConnection(OpenVPNConnection): self.settingsfile = kwargs.get('settingsfile', None) self.logfile = kwargs.get('logfile', None) self.provider = kwargs.pop('provider', None) + self._providercertchecker = provider_cert_checker + self._configchecker = config_checker self.error_queue = Queue.Queue() @@ -39,10 +41,7 @@ class EIPConnection(OpenVPNConnection): checker_signals = kwargs.pop('checker_signals', None) self.checker_signals = checker_signals - # initialize checkers - self.provider_cert_checker = provider_cert_checker( - domain=self.provider) - self.config_checker = config_checker(domain=self.provider) + self.init_checkers() host = eipconfig.get_socket_path() kwargs['host'] = host @@ -52,13 +51,24 @@ class EIPConnection(OpenVPNConnection): def has_errors(self): return True if self.error_queue.qsize() != 0 else False + def init_checkers(self): + # initialize checkers + self.provider_cert_checker = self._providercertchecker( + domain=self.provider) + self.config_checker = self._configchecker(domain=self.provider) + def set_provider_domain(self, domain): """ sets the provider domain. used from the first run wizard when we launch the run_checks and connect process after having initialized the conductor. """ + # This looks convoluted, right. + # We have to reinstantiate checkers cause we're passing + # the domain param that we did not know at the beginning + # (only for the firstrunwizard case) self.provider = domain + self.init_checkers() def run_checks(self, skip_download=False, skip_verify=False): """ -- cgit v1.2.3 From d24c7328fa845737dbb83d512e4b3f287634c4cc Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 14 Nov 2012 00:33:05 +0900 Subject: make tests pass + pep8 They were breaking mainly because I did not bother to have a pass over them to change the PROVIDER settings from the branding case. All good now, although much testing is yet needed and some refactor could be used. long live green tests! --- src/leap/eip/openvpnconnection.py | 17 ++++++++----- src/leap/eip/specs.py | 2 +- src/leap/eip/tests/data.py | 7 +++--- src/leap/eip/tests/test_checks.py | 37 ++++++++++++++++++++-------- src/leap/eip/tests/test_config.py | 19 ++++++++------ src/leap/eip/tests/test_eipconnection.py | 12 ++++++--- src/leap/eip/tests/test_openvpnconnection.py | 10 +++++--- 7 files changed, 70 insertions(+), 34 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index 34f1e18b..4104bd0e 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -233,8 +233,8 @@ to be triggered for each one of them. #self.tn.read_until('ENTER PASSWORD:', 2) #self.tn.write(self.password + '\n') #self.tn.read_until('SUCCESS:', 2) - - self._seek_to_eof() + if self.tn: + self._seek_to_eof() return True def _seek_to_eof(self): @@ -364,7 +364,8 @@ to be triggered for each one of them. interface """ logger.debug("disconnecting...") - self._send_command("signal SIGTERM\n") + if self.connected(): + self._send_command("signal SIGTERM\n") if self.subp: return True @@ -373,9 +374,13 @@ to be triggered for each one of them. #try patching in old openvpn host and trying again process = self._get_openvpn_process() if process: - self.host = \ - process.cmdline[process.cmdline.index("--management") + 1] - self._send_command("signal SIGTERM\n") + logger.debug('process :%s' % process) + cmdline = process.cmdline + + if isinstance(cmdline, list): + _index = cmdline.index("--management") + self.host = cmdline[_index + 1] + self._send_command("signal SIGTERM\n") #make sure the process was terminated process = self._get_openvpn_process() diff --git a/src/leap/eip/specs.py b/src/leap/eip/specs.py index 84b2597d..57e7537b 100644 --- a/src/leap/eip/specs.py +++ b/src/leap/eip/specs.py @@ -8,7 +8,7 @@ from leap.base import config as baseconfig PROVIDER_CA_CERT = __branding.get( 'provider_ca_file', - 'testprovider-ca-cert.pem') + 'cacert.pem') provider_ca_path = lambda domain: str(os.path.join( #baseconfig.get_default_provider_path(), diff --git a/src/leap/eip/tests/data.py b/src/leap/eip/tests/data.py index f1d3b0bc..cadf720e 100644 --- a/src/leap/eip/tests/data.py +++ b/src/leap/eip/tests/data.py @@ -1,11 +1,12 @@ from __future__ import unicode_literals import os -from leap import __branding +#from leap import __branding # sample data used in tests -PROVIDER = __branding.get('provider_domain') +#PROVIDER = __branding.get('provider_domain') +PROVIDER = "testprovider.example.org" EIP_SAMPLE_CONFIG = { "provider": "%s" % PROVIDER, @@ -15,7 +16,7 @@ EIP_SAMPLE_CONFIG = { "openvpn_ca_certificate": os.path.expanduser( "~/.config/leap/providers/" "%s/" - "keys/ca/testprovider-ca-cert.pem" % PROVIDER), + "keys/ca/cacert.pem" % PROVIDER), "openvpn_client_certificate": os.path.expanduser( "~/.config/leap/providers/" "%s/" diff --git a/src/leap/eip/tests/test_checks.py b/src/leap/eip/tests/test_checks.py index 58ce473f..1d7bfc17 100644 --- a/src/leap/eip/tests/test_checks.py +++ b/src/leap/eip/tests/test_checks.py @@ -39,6 +39,8 @@ class NoLogRequestHandler: class EIPCheckTest(BaseLeapTest): __name__ = "eip_check_tests" + provider = "testprovider.example.org" + maxDiff = None def setUp(self): pass @@ -49,7 +51,7 @@ class EIPCheckTest(BaseLeapTest): # test methods are there, and can be called from run_all def test_checker_should_implement_check_methods(self): - checker = eipchecks.EIPConfigChecker() + checker = eipchecks.EIPConfigChecker(domain=self.provider) self.assertTrue(hasattr(checker, "check_default_eipconfig"), "missing meth") @@ -62,7 +64,7 @@ class EIPCheckTest(BaseLeapTest): "missing meth") def test_checker_should_actually_call_all_tests(self): - checker = eipchecks.EIPConfigChecker() + checker = eipchecks.EIPConfigChecker(domain=self.provider) mc = Mock() checker.run_all(checker=mc) @@ -79,7 +81,7 @@ class EIPCheckTest(BaseLeapTest): # test individual check methods def test_check_default_eipconfig(self): - checker = eipchecks.EIPConfigChecker() + checker = eipchecks.EIPConfigChecker(domain=self.provider) # no eip config (empty home) eipconfig_path = checker.eipconfig.filename self.assertFalse(os.path.isfile(eipconfig_path)) @@ -93,15 +95,15 @@ class EIPCheckTest(BaseLeapTest): # small workaround for evaluating home dirs correctly EIP_SAMPLE_CONFIG = copy.copy(testdata.EIP_SAMPLE_CONFIG) EIP_SAMPLE_CONFIG['openvpn_client_certificate'] = \ - eipspecs.client_cert_path() + eipspecs.client_cert_path(self.provider) EIP_SAMPLE_CONFIG['openvpn_ca_certificate'] = \ - eipspecs.provider_ca_path() + eipspecs.provider_ca_path(self.provider) self.assertEqual(deserialized, EIP_SAMPLE_CONFIG) # TODO: shold ALSO run validation methods. def test_check_is_there_default_provider(self): - checker = eipchecks.EIPConfigChecker() + checker = eipchecks.EIPConfigChecker(domain=self.provider) # we do dump a sample eip config, but lacking a # default provider entry. # This error will be possible catched in a different @@ -178,6 +180,7 @@ class EIPCheckTest(BaseLeapTest): class ProviderCertCheckerTest(BaseLeapTest): __name__ = "provider_cert_checker_tests" + provider = "testprovider.example.org" def setUp(self): pass @@ -226,13 +229,20 @@ class ProviderCertCheckerTest(BaseLeapTest): # test individual check methods + @unittest.skip def test_is_there_provider_ca(self): + # XXX commenting out this test. + # With the generic client this does not make sense, + # we should dump one there. + # or test conductor logic. checker = eipchecks.ProviderCertChecker() self.assertTrue( checker.is_there_provider_ca()) class ProviderCertCheckerHTTPSTests(BaseHTTPSServerTestCase, BaseLeapTest): + provider = "testprovider.example.org" + class request_handler(NoLogRequestHandler, BaseHTTPRequestHandler): responses = { '/': ['OK', ''], @@ -292,12 +302,19 @@ class ProviderCertCheckerHTTPSTests(BaseHTTPSServerTestCase, BaseLeapTest): # same, but get cacert from leap.custom # XXX TODO! + @unittest.skip def test_download_new_client_cert(self): + # FIXME + # Magick srp decorator broken right now... + # Have to mock the decorator and inject something that + # can bypass the authentication + uri = "https://%s/client.cert" % (self.get_server()) cacert = where_cert('cacert.pem') - checker = eipchecks.ProviderCertChecker() + checker = eipchecks.ProviderCertChecker(domain=self.provider) + credentials = "testuser", "testpassword" self.assertTrue(checker.download_new_client_cert( - uri=uri, verify=cacert)) + credentials=credentials, uri=uri, verify=cacert)) # now download a malformed cert uri = "https://%s/badclient.cert" % (self.get_server()) @@ -305,7 +322,7 @@ class ProviderCertCheckerHTTPSTests(BaseHTTPSServerTestCase, BaseLeapTest): checker = eipchecks.ProviderCertChecker() with self.assertRaises(ValueError): self.assertTrue(checker.download_new_client_cert( - uri=uri, verify=cacert)) + credentials=credentials, uri=uri, verify=cacert)) # did we write cert to its path? clientcertfile = eipspecs.client_cert_path() @@ -339,7 +356,7 @@ class ProviderCertCheckerHTTPSTests(BaseHTTPSServerTestCase, BaseLeapTest): def test_check_new_cert_needed(self): # check: missing cert - checker = eipchecks.ProviderCertChecker() + checker = eipchecks.ProviderCertChecker(domain=self.provider) self.assertTrue(checker.check_new_cert_needed(skip_download=True)) # TODO check: malformed cert # TODO check: expired cert diff --git a/src/leap/eip/tests/test_config.py b/src/leap/eip/tests/test_config.py index 6759b522..50538240 100644 --- a/src/leap/eip/tests/test_config.py +++ b/src/leap/eip/tests/test_config.py @@ -18,13 +18,14 @@ from leap.util.fileutil import mkdir_p _system = platform.system() -PROVIDER = BRANDING.get('provider_domain') -PROVIDER_SHORTNAME = BRANDING.get('short_name') +#PROVIDER = BRANDING.get('provider_domain') +#PROVIDER_SHORTNAME = BRANDING.get('short_name') class EIPConfigTest(BaseLeapTest): __name__ = "eip_config_tests" + provider = "testprovider.example.org" def setUp(self): pass @@ -74,7 +75,8 @@ class EIPConfigTest(BaseLeapTest): args.append('--persist-tun') args.append('--persist-key') args.append('--remote') - args.append('%s' % eipconfig.get_eip_gateway()) + args.append('%s' % eipconfig.get_eip_gateway( + provider=self.provider)) # XXX get port!? args.append('1194') # XXX get proto @@ -103,23 +105,23 @@ class EIPConfigTest(BaseLeapTest): args.append(os.path.join( self.home, '.config', 'leap', 'providers', - '%s' % PROVIDER, + '%s' % self.provider, 'keys', 'client', 'openvpn.pem')) args.append('--key') args.append(os.path.join( self.home, '.config', 'leap', 'providers', - '%s' % PROVIDER, + '%s' % self.provider, 'keys', 'client', 'openvpn.pem')) args.append('--ca') args.append(os.path.join( self.home, '.config', 'leap', 'providers', - '%s' % PROVIDER, + '%s' % self.provider, 'keys', 'ca', - '%s-cacert.pem' % PROVIDER_SHORTNAME)) + 'cacert.pem')) return args # build command string @@ -141,7 +143,8 @@ class EIPConfigTest(BaseLeapTest): print 'vpnbin = ', vpnbin command, args = eipconfig.build_ovpn_command( do_pkexec_check=False, vpnbin=vpnbin, - socket_path="/tmp/test.socket") + socket_path="/tmp/test.socket", + provider=self.provider) self.assertEqual(command, self.home + '/bin/openvpn') self.assertEqual(args, self.get_expected_openvpn_args()) diff --git a/src/leap/eip/tests/test_eipconnection.py b/src/leap/eip/tests/test_eipconnection.py index bb643ae0..aefca36f 100644 --- a/src/leap/eip/tests/test_eipconnection.py +++ b/src/leap/eip/tests/test_eipconnection.py @@ -19,6 +19,8 @@ from leap.testing.basetest import BaseLeapTest _system = platform.system() +PROVIDER = "testprovider.example.org" + class NotImplementedError(Exception): pass @@ -27,6 +29,7 @@ class NotImplementedError(Exception): @patch('OpenVPNConnection._get_or_create_config') @patch('OpenVPNConnection._set_ovpn_command') class MockedEIPConnection(EIPConnection): + def _set_ovpn_command(self): self.command = "mock_command" self.args = [1, 2, 3] @@ -35,6 +38,7 @@ class MockedEIPConnection(EIPConnection): class EIPConductorTest(BaseLeapTest): __name__ = "eip_conductor_tests" + provider = PROVIDER def setUp(self): # XXX there's a conceptual/design @@ -51,8 +55,8 @@ class EIPConductorTest(BaseLeapTest): # XXX change to keys_checker invocation # (see config_checker) - keyfiles = (eipspecs.provider_ca_path(), - eipspecs.client_cert_path()) + keyfiles = (eipspecs.provider_ca_path(domain=self.provider), + eipspecs.client_cert_path(domain=self.provider)) for filepath in keyfiles: self.touch(filepath) self.chmod600(filepath) @@ -61,6 +65,7 @@ class EIPConductorTest(BaseLeapTest): # some methods mocked self.manager = Mock(name="openvpnmanager_mock") self.con = MockedEIPConnection() + self.con.provider = self.provider self.con.run_openvpn_checks() def tearDown(self): @@ -118,8 +123,9 @@ class EIPConductorTest(BaseLeapTest): self.con.status.CONNECTED) # disconnect + self.con.cleanup = Mock() self.con.disconnect() - self.con._disconnect.assert_called_once_with() + self.con.cleanup.assert_called_once_with() # new status should be disconnected # XXX this should evolve and check no errors diff --git a/src/leap/eip/tests/test_openvpnconnection.py b/src/leap/eip/tests/test_openvpnconnection.py index 61769f04..0f27facf 100644 --- a/src/leap/eip/tests/test_openvpnconnection.py +++ b/src/leap/eip/tests/test_openvpnconnection.py @@ -76,13 +76,17 @@ class OpenVPNConnectionTest(BaseLeapTest): # def test_detect_vpn(self): + # XXX review, not sure if captured all the logic + # while fixing. kali. openvpn_connection = openvpnconnection.OpenVPNConnection() + with patch.object(psutil, "get_process_list") as mocked_psutil: + mocked_process = Mock() + mocked_process.name = "openvpn" + mocked_psutil.return_value = [mocked_process] with self.assertRaises(eipexceptions.OpenVPNAlreadyRunning): - mocked_process = Mock() - mocked_process.name = "openvpn" - mocked_psutil.return_value = [mocked_process] openvpn_connection._check_if_running_instance() + openvpn_connection._check_if_running_instance() @unittest.skipIf(_system == "Windows", "lin/mac only") -- cgit v1.2.3 From 834b04317535cb6d18c02e3aa4d37b09fcf99868 Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 14 Nov 2012 03:39:14 +0900 Subject: delete temp folder for management socket after client shutdown Close #742 --- src/leap/eip/openvpnconnection.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'src/leap/eip') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index 4104bd0e..45a1847c 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -3,7 +3,9 @@ OpenVPN Connection """ from __future__ import (print_function) import logging +import os import psutil +import shutil import socket import time from functools import partial @@ -187,6 +189,7 @@ to be triggered for each one of them. 'connection refused.') # XXX kali -- + # XXX review-me # I think this will block if child process # does not return. # Maybe we can .poll() for a given @@ -198,6 +201,26 @@ to be triggered for each one of them. 'cannot terminate subprocess! Retcode %s' '(We might have left openvpn running)' % RETCODE) + self.cleanup_tempfiles() + + def cleanup_tempfiles(self): + """ + remove all temporal files + we might have left behind + """ + # if self.port is 'unix', we have + # created a temporal socket path that, under + # normal circumstances, we should be able to + # delete + + if self.port == "unix": + # I'm tempted to catch a generic exception here, + # but I prefer to let it crash so we can catch + # specific errors that right now I'm not able + # to think of. + logger.debug('cleaning socket file temp folder') + shutil.rmtree(os.path.split(self.host)[0]) + def _get_openvpn_process(self): # plist = [p for p in psutil.get_process_list() if p.name == "openvpn"] # return plist[0] if plist else None -- cgit v1.2.3 From 862014f68fce37318f77309a8f8f9782dabc60d2 Mon Sep 17 00:00:00 2001 From: kali Date: Mon, 19 Nov 2012 14:09:24 +0900 Subject: fix delete temporal files --- src/leap/eip/openvpnconnection.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index 45a1847c..85874cfd 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -214,12 +214,14 @@ to be triggered for each one of them. # delete if self.port == "unix": - # I'm tempted to catch a generic exception here, - # but I prefer to let it crash so we can catch - # specific errors that right now I'm not able - # to think of. logger.debug('cleaning socket file temp folder') - shutil.rmtree(os.path.split(self.host)[0]) + + tempfolder = os.path.split(self.host)[0] + if os.path.isdir(tempfolder): + try: + shutil.rmtree(tempfolder) + except OSError: + logger.error('could not delete tmpfolder %s' % tempfolder) def _get_openvpn_process(self): # plist = [p for p in psutil.get_process_list() if p.name == "openvpn"] -- cgit v1.2.3 From a3ce61ea54b0b0f5c1ecd5904379e27cfec885b5 Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 28 Nov 2012 02:43:25 +0900 Subject: call shutdown signal from sigint_handler --- src/leap/eip/openvpnconnection.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index 85874cfd..859378c0 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -390,9 +390,14 @@ to be triggered for each one of them. """ logger.debug("disconnecting...") if self.connected(): - self._send_command("signal SIGTERM\n") + try: + self._send_command("signal SIGTERM\n") + except socket.error: + logger.warning('management socket died') + return if self.subp: + # ??? return True #shutting openvpn failured -- cgit v1.2.3 From cd78d9d552977e8f8fb12b6a2ff56fda9c37bf35 Mon Sep 17 00:00:00 2001 From: kali Date: Fri, 7 Dec 2012 05:32:50 +0900 Subject: only remove management socket when shutting down Closes #1090 --- src/leap/eip/openvpnconnection.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index 859378c0..07bc628a 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -176,7 +176,7 @@ to be triggered for each one of them. logger.debug('no openvpn instance found.') - def cleanup(self): + def cleanup(self, shutdown=False): """ terminates openvpn child subprocess """ @@ -201,7 +201,8 @@ to be triggered for each one of them. 'cannot terminate subprocess! Retcode %s' '(We might have left openvpn running)' % RETCODE) - self.cleanup_tempfiles() + if shutdown: + self.cleanup_tempfiles() def cleanup_tempfiles(self): """ -- cgit v1.2.3 From 38cc1758240a3c64db387b0437dcf1517b52da15 Mon Sep 17 00:00:00 2001 From: kali Date: Mon, 10 Dec 2012 19:51:53 +0900 Subject: cleanup and rewrite eipconnection/openvpnconnection classes --- src/leap/eip/config.py | 2 +- src/leap/eip/eipconnection.py | 238 ++++++++++------ src/leap/eip/openvpnconnection.py | 472 +++++++++++++------------------ src/leap/eip/tests/test_eipconnection.py | 9 +- 4 files changed, 355 insertions(+), 366 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py index 42c00380..8e687bda 100644 --- a/src/leap/eip/config.py +++ b/src/leap/eip/config.py @@ -53,7 +53,7 @@ def get_socket_path(): socket_path = os.path.join( tempfile.mkdtemp(prefix="leap-tmp"), 'openvpn.socket') - logger.debug('socket path: %s', socket_path) + #logger.debug('socket path: %s', socket_path) return socket_path diff --git a/src/leap/eip/eipconnection.py b/src/leap/eip/eipconnection.py index 7828c864..8751f643 100644 --- a/src/leap/eip/eipconnection.py +++ b/src/leap/eip/eipconnection.py @@ -5,6 +5,7 @@ from __future__ import (absolute_import,) import logging import Queue import sys +import time from leap.eip.checks import ProviderCertChecker from leap.eip.checks import EIPConfigChecker @@ -15,20 +16,143 @@ from leap.eip.openvpnconnection import OpenVPNConnection logger = logging.getLogger(name=__name__) -class EIPConnection(OpenVPNConnection): +class StatusMixIn(object): + + # a bunch of methods related with querying the connection + # state/status and displaying useful info. + # Needs to get clear on what is what, and + # separate functions. + # Should separate EIPConnectionStatus (self.status) + # from the OpenVPN state/status command and parsing. + + def connection_state(self): + """ + returns the current connection state + """ + return self.status.current + + def get_icon_name(self): + """ + get icon name from status object + """ + return self.status.get_state_icon() + + def get_leap_status(self): + return self.status.get_leap_status() + + def poll_connection_state(self): + """ + """ + try: + state = self.get_connection_state() + except eip_exceptions.ConnectionRefusedError: + # connection refused. might be not ready yet. + logger.warning('connection refused') + return + if not state: + logger.debug('no state') + return + (ts, status_step, + ok, ip, remote) = state + self.status.set_vpn_state(status_step) + status_step = self.status.get_readable_status() + return (ts, status_step, ok, ip, remote) + + def make_error(self): + """ + capture error and wrap it in an + understandable format + """ + # mostly a hack to display errors in the debug UI + # w/o breaking the polling. + #XXX get helpful error codes + self.with_errors = True + now = int(time.time()) + return '%s,LAUNCHER ERROR,ERROR,-,-' % now + + def state(self): + """ + Sends OpenVPN command: state + """ + state = self._send_command("state") + if not state: + return None + if isinstance(state, str): + return state + if isinstance(state, list): + if len(state) == 1: + return state[0] + else: + return state[-1] + + def vpn_status(self): + """ + OpenVPN command: status + """ + status = self._send_command("status") + return status + + def vpn_status2(self): + """ + OpenVPN command: last 2 statuses + """ + return self._send_command("status 2") + + # + # parse info as the UI expects + # + + def get_status_io(self): + status = self.vpn_status() + if isinstance(status, str): + lines = status.split('\n') + if isinstance(status, list): + lines = status + try: + (header, when, tun_read, tun_write, + tcp_read, tcp_write, auth_read) = tuple(lines) + except ValueError: + return None + + # XXX this will break with different locales I assume... + when_ts = time.strptime(when.split(',')[1], "%a %b %d %H:%M:%S %Y") + sep = ',' + # XXX clean up this! + tun_read = tun_read.split(sep)[1] + tun_write = tun_write.split(sep)[1] + tcp_read = tcp_read.split(sep)[1] + tcp_write = tcp_write.split(sep)[1] + auth_read = auth_read.split(sep)[1] + + # XXX this could be a named tuple. prettier. + return when_ts, (tun_read, tun_write, tcp_read, tcp_write, auth_read) + + def get_connection_state(self): + state = self.state() + if state is not None: + ts, status_step, ok, ip, remote = state.split(',') + ts = time.gmtime(float(ts)) + # XXX this could be a named tuple. prettier. + return ts, status_step, ok, ip, remote + + +class EIPConnection(OpenVPNConnection, StatusMixIn): """ + Aka conductor. Manages the execution of the OpenVPN process, auto starts, monitors the network connection, handles configuration, fixes leaky hosts, handles errors, etc. Status updates (connected, bandwidth, etc) are signaled to the GUI. """ + # XXX change name to EIPConductor ?? + def __init__(self, provider_cert_checker=ProviderCertChecker, config_checker=EIPConfigChecker, *args, **kwargs): - self.settingsfile = kwargs.get('settingsfile', None) - self.logfile = kwargs.get('logfile', None) + #self.settingsfile = kwargs.get('settingsfile', None) + #self.logfile = kwargs.get('logfile', None) self.provider = kwargs.pop('provider', None) self._providercertchecker = provider_cert_checker self._configchecker = config_checker @@ -48,11 +172,27 @@ class EIPConnection(OpenVPNConnection): super(EIPConnection, self).__init__(*args, **kwargs) + def connect(self): + """ + entry point for connection process + """ + # in OpenVPNConnection + self.try_openvpn_connection() + + def disconnect(self, shutdown=False): + """ + disconnects client + """ + self.terminate_openvpn_connection(shutdown=shutdown) + self.status.change_to(self.status.DISCONNECTED) + def has_errors(self): return True if self.error_queue.qsize() != 0 else False def init_checkers(self): - # initialize checkers + """ + initialize checkers + """ self.provider_cert_checker = self._providercertchecker( domain=self.provider) self.config_checker = self._configchecker(domain=self.provider) @@ -101,96 +241,6 @@ class EIPConnection(OpenVPNConnection): except Exception as exc: push_err(exc) - def connect(self): - """ - entry point for connection process - """ - #self.forget_errors() - self._try_connection() - - def disconnect(self): - """ - disconnects client - """ - self.cleanup() - logger.debug("disconnect: clicked.") - self.status.change_to(self.status.DISCONNECTED) - - #def shutdown(self): - #""" - #shutdown and quit - #""" - #self.desired_con_state = self.status.DISCONNECTED - - def connection_state(self): - """ - returns the current connection state - """ - return self.status.current - - def poll_connection_state(self): - """ - """ - try: - state = self.get_connection_state() - except eip_exceptions.ConnectionRefusedError: - # connection refused. might be not ready yet. - logger.warning('connection refused') - return - if not state: - logger.debug('no state') - return - (ts, status_step, - ok, ip, remote) = state - self.status.set_vpn_state(status_step) - status_step = self.status.get_readable_status() - return (ts, status_step, ok, ip, remote) - - def get_icon_name(self): - """ - get icon name from status object - """ - return self.status.get_state_icon() - - def get_leap_status(self): - return self.status.get_leap_status() - - # - # private methods - # - - #def _disconnect(self): - # """ - # private method for disconnecting - # """ - # if self.subp is not None: - # logger.debug('disconnecting...') - # self.subp.terminate() - # self.subp = None - - #def _is_alive(self): - #""" - #don't know yet - #""" - #pass - - def _connect(self): - """ - entry point for connection cascade methods. - """ - try: - conn_result = self._try_connection() - except eip_exceptions.UnrecoverableError as except_msg: - logger.error("FATAL: %s" % unicode(except_msg)) - conn_result = self.status.UNRECOVERABLE - - # XXX enqueue exceptions themselves instead? - except Exception as except_msg: - self.error_queue.append(except_msg) - logger.error("Failed Connection: %s" % - unicode(except_msg)) - return conn_result - class EIPConnectionStatus(object): """ diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index 07bc628a..253f5056 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -7,7 +7,6 @@ import os import psutil import shutil import socket -import time from functools import partial logger = logging.getLogger(name=__name__) @@ -20,12 +19,123 @@ from leap.eip import config as eip_config from leap.eip import exceptions as eip_exceptions -class OpenVPNConnection(Connection): +class OpenVPNManagement(object): + + # TODO explain a little bit how management interface works + # and our telnet interface with support for unix sockets. + + """ + for more information, read openvpn management notes. + zcat `dpkg -L openvpn | grep management` + """ + + def _connect_to_management(self): + """ + Connect to openvpn management interface + """ + if hasattr(self, 'tn'): + self._close_management_socket() + self.tn = UDSTelnet(self.host, self.port) + + # XXX make password optional + # specially for win. we should generate + # the pass on the fly when invoking manager + # from conductor + + #self.tn.read_until('ENTER PASSWORD:', 2) + #self.tn.write(self.password + '\n') + #self.tn.read_until('SUCCESS:', 2) + if self.tn: + self._seek_to_eof() + return True + + def _close_management_socket(self, announce=True): + """ + Close connection to openvpn management interface + """ + logger.debug('closing socket') + if announce: + self.tn.write("quit\n") + self.tn.read_all() + self.tn.get_socket().close() + del self.tn + + def _seek_to_eof(self): + """ + Read as much as available. Position seek pointer to end of stream + """ + try: + b = self.tn.read_eager() + except EOFError: + logger.debug("Could not read from socket. Assuming it died.") + return + while b: + try: + b = self.tn.read_eager() + except EOFError: + logger.debug("Could not read from socket. Assuming it died.") + + def _send_command(self, cmd): + """ + Send a command to openvpn and return response as list + """ + if not self.connected(): + try: + self._connect_to_management() + except eip_exceptions.MissingSocketError: + logger.warning('missing management socket') + return [] + try: + if hasattr(self, 'tn'): + self.tn.write(cmd + "\n") + except socket.error: + logger.error('socket error') + self._close_management_socket(announce=False) + return [] + buf = self.tn.read_until(b"END", 2) + self._seek_to_eof() + blist = buf.split('\r\n') + if blist[-1].startswith('END'): + del blist[-1] + return blist + else: + return [] + + def _send_short_command(self, cmd): + """ + parse output from commands that are + delimited by "success" instead + """ + if not self.connected(): + self.connect() + self.tn.write(cmd + "\n") + # XXX not working? + buf = self.tn.read_until(b"SUCCESS", 2) + self._seek_to_eof() + blist = buf.split('\r\n') + return blist + + # + # random maybe useful vpn commands + # + + def pid(self): + #XXX broken + return self._send_short_command("pid") + + +class OpenVPNConnection(Connection, OpenVPNManagement): """ All related to invocation - of the openvpn binary + of the openvpn binary. + It's extended by EIPConnection. """ + # XXX Inheriting from Connection was an early design idea + # but currently that's an empty class. + # We can get rid of that if we don't use it for sharing + # state with other leap modules. + def __init__(self, watcher_cb=None, debug=False, @@ -34,24 +144,21 @@ class OpenVPNConnection(Connection): password=None, *args, **kwargs): """ - :param config_file: configuration file to read from :param watcher_cb: callback to be \ called for each line in watched stdout :param signal_map: dictionary of signal names and callables \ to be triggered for each one of them. - :type config_file: str :type watcher_cb: function :type signal_map: dict """ #XXX FIXME #change watcher_cb to line_observer + # XXX if not host: raise ImproperlyConfigured logger.debug('init openvpn connection') self.debug = debug - # XXX if not host: raise ImproperlyConfigured self.ovpn_verbosity = kwargs.get('ovpn_verbosity', None) - #self.config_file = config_file self.watcher_cb = watcher_cb #self.signal_maps = signal_maps @@ -62,21 +169,13 @@ to be triggered for each one of them. self.port = None self.proto = None - #XXX workaround for signaling - #the ui that we don't know how to - #manage a connection error - #self.with_errors = False - self.command = None self.args = None # XXX get autostart from config self.autostart = True - # - # management init methods - # - + # management interface init self.host = host if isinstance(port, str) and port.isdigit(): port = int(port) @@ -88,101 +187,47 @@ to be triggered for each one of them. self.password = password def run_openvpn_checks(self): + """ + runs check needed before launching + openvpn subprocess. will raise if errors found. + """ logger.debug('running openvpn checks') + # XXX I think that "check_if_running" should be called + # from try openvpn connection instead. -- kali. + # let's prepare tests for that before changing it... self._check_if_running_instance() self._set_ovpn_command() self._check_vpn_keys() - def _set_ovpn_command(self): - # XXX check also for command-line --command flag - try: - command, args = eip_config.build_ovpn_command( - provider=self.provider, - debug=self.debug, - socket_path=self.host, - ovpn_verbosity=self.ovpn_verbosity) - except eip_exceptions.EIPNoPolkitAuthAgentAvailable: - command = args = None - raise - except eip_exceptions.EIPNoPkexecAvailable: - command = args = None - raise - - # XXX if not command, signal error. - self.command = command - self.args = args - - def _check_vpn_keys(self): - """ - checks for correct permissions on vpn keys - """ - try: - eip_config.check_vpn_keys(provider=self.provider) - except eip_exceptions.EIPInitBadKeyFilePermError: - logger.error('Bad VPN Keys permission!') - # do nothing now - # and raise the rest ... - - def _launch_openvpn(self): - """ - invocation of openvpn binaries in a subprocess. - """ - #XXX TODO: - #deprecate watcher_cb, - #use _only_ signal_maps instead - - logger.debug('_launch_openvpn called') - if self.watcher_cb is not None: - linewrite_callback = self.watcher_cb - else: - #XXX get logger instead - linewrite_callback = lambda line: print('watcher: %s' % line) - - # the partial is not - # being applied now because we're not observing the process - # stdout like we did in the early stages. but I leave it - # here since it will be handy for observing patterns in the - # thru-the-manager updates (with regex) - observers = (linewrite_callback, - partial(lambda con_status, line: None, self.status)) - subp, watcher = spawn_and_watch_process( - self.command, - self.args, - observers=observers) - self.subp = subp - self.watcher = watcher - - def _try_connection(self): + def try_openvpn_connection(self): """ attempts to connect """ + # XXX should make public method if self.command is None: raise eip_exceptions.EIPNoCommandError if self.subp is not None: logger.debug('cowardly refusing to launch subprocess again') + # XXX this is not returning ???!! + # FIXME -- so it's calling it all the same!! self._launch_openvpn() - def _check_if_running_instance(self): + def connected(self): """ - check if openvpn is already running + Returns True if connected + rtype: bool """ - for process in psutil.get_process_list(): - if process.name == "openvpn": - logger.debug('an openvpn instance is already running.') - logger.debug('attempting to stop openvpn instance.') - if not self._stop(): - raise eip_exceptions.OpenVPNAlreadyRunning - - logger.debug('no openvpn instance found.') + # XXX make a property + return hasattr(self, 'tn') - def cleanup(self, shutdown=False): + def terminate_openvpn_connection(self, shutdown=False): """ terminates openvpn child subprocess """ if self.subp: try: - self._stop() + self._stop_openvpn() except eip_exceptions.ConnectionRefusedError: logger.warning( 'unable to send sigterm signal to openvpn: ' @@ -202,9 +247,9 @@ to be triggered for each one of them. '(We might have left openvpn running)' % RETCODE) if shutdown: - self.cleanup_tempfiles() + self._cleanup_tempfiles() - def cleanup_tempfiles(self): + def _cleanup_tempfiles(self): """ remove all temporal files we might have left behind @@ -224,172 +269,89 @@ to be triggered for each one of them. except OSError: logger.error('could not delete tmpfolder %s' % tempfolder) - def _get_openvpn_process(self): - # plist = [p for p in psutil.get_process_list() if p.name == "openvpn"] - # return plist[0] if plist else None + # checks + + def _check_if_running_instance(self): + """ + check if openvpn is already running + """ for process in psutil.get_process_list(): if process.name == "openvpn": - return process - return None - - # management methods - # - # XXX REVIEW-ME - # REFACTOR INFO: (former "manager". - # Can we move to another - # base class to test independently?) - # - - #def forget_errors(self): - #logger.debug('forgetting errors') - #self.with_errors = False - - def connect_to_management(self): - """Connect to openvpn management interface""" - #logger.debug('connecting socket') - if hasattr(self, 'tn'): - self.close() - self.tn = UDSTelnet(self.host, self.port) - - # XXX make password optional - # specially for win. we should generate - # the pass on the fly when invoking manager - # from conductor + logger.debug('an openvpn instance is already running.') + logger.debug('attempting to stop openvpn instance.') + if not self._stop_openvpn(): + raise eip_exceptions.OpenVPNAlreadyRunning - #self.tn.read_until('ENTER PASSWORD:', 2) - #self.tn.write(self.password + '\n') - #self.tn.read_until('SUCCESS:', 2) - if self.tn: - self._seek_to_eof() - return True + logger.debug('no openvpn instance found.') - def _seek_to_eof(self): - """ - Read as much as available. Position seek pointer to end of stream - """ + def _set_ovpn_command(self): try: - b = self.tn.read_eager() - except EOFError: - logger.debug("Could not read from socket. Assuming it died.") - return - while b: - try: - b = self.tn.read_eager() - except EOFError: - logger.debug("Could not read from socket. Assuming it died.") - - def connected(self): - """ - Returns True if connected - rtype: bool - """ - return hasattr(self, 'tn') + command, args = eip_config.build_ovpn_command( + provider=self.provider, + debug=self.debug, + socket_path=self.host, + ovpn_verbosity=self.ovpn_verbosity) + except eip_exceptions.EIPNoPolkitAuthAgentAvailable: + command = args = None + raise + except eip_exceptions.EIPNoPkexecAvailable: + command = args = None + raise - def close(self, announce=True): - """ - Close connection to openvpn management interface - """ - logger.debug('closing socket') - if announce: - self.tn.write("quit\n") - self.tn.read_all() - self.tn.get_socket().close() - del self.tn + # XXX if not command, signal error. + self.command = command + self.args = args - def _send_command(self, cmd): + def _check_vpn_keys(self): """ - Send a command to openvpn and return response as list + checks for correct permissions on vpn keys """ - if not self.connected(): - try: - self.connect_to_management() - except eip_exceptions.MissingSocketError: - logger.warning('missing management socket') - return [] try: - if hasattr(self, 'tn'): - self.tn.write(cmd + "\n") - except socket.error: - logger.error('socket error') - self.close(announce=False) - return [] - buf = self.tn.read_until(b"END", 2) - self._seek_to_eof() - blist = buf.split('\r\n') - if blist[-1].startswith('END'): - del blist[-1] - return blist - else: - return [] - - def _send_short_command(self, cmd): - """ - parse output from commands that are - delimited by "success" instead - """ - if not self.connected(): - self.connect() - self.tn.write(cmd + "\n") - # XXX not working? - buf = self.tn.read_until(b"SUCCESS", 2) - self._seek_to_eof() - blist = buf.split('\r\n') - return blist - - # - # useful vpn commands - # - - def pid(self): - #XXX broken - return self._send_short_command("pid") + eip_config.check_vpn_keys(provider=self.provider) + except eip_exceptions.EIPInitBadKeyFilePermError: + logger.error('Bad VPN Keys permission!') + # do nothing now + # and raise the rest ... - def make_error(self): - """ - capture error and wrap it in an - understandable format - """ - #XXX get helpful error codes - self.with_errors = True - now = int(time.time()) - return '%s,LAUNCHER ERROR,ERROR,-,-' % now + # starting and stopping openvpn subprocess - def state(self): + def _launch_openvpn(self): """ - OpenVPN command: state + invocation of openvpn binaries in a subprocess. """ - state = self._send_command("state") - if not state: - return None - if isinstance(state, str): - return state - if isinstance(state, list): - if len(state) == 1: - return state[0] - else: - return state[-1] + #XXX TODO: + #deprecate watcher_cb, + #use _only_ signal_maps instead - def vpn_status(self): - """ - OpenVPN command: status - """ - #logger.debug('status called') - status = self._send_command("status") - return status + logger.debug('_launch_openvpn called') + if self.watcher_cb is not None: + linewrite_callback = self.watcher_cb + else: + #XXX get logger instead + linewrite_callback = lambda line: print('watcher: %s' % line) - def vpn_status2(self): - """ - OpenVPN command: last 2 statuses - """ - return self._send_command("status 2") + # the partial is not + # being applied now because we're not observing the process + # stdout like we did in the early stages. but I leave it + # here since it will be handy for observing patterns in the + # thru-the-manager updates (with regex) + observers = (linewrite_callback, + partial(lambda con_status, line: None, self.status)) + subp, watcher = spawn_and_watch_process( + self.command, + self.args, + observers=observers) + self.subp = subp + self.watcher = watcher - def _stop(self): + def _stop_openvpn(self): """ stop openvpn process by sending SIGTERM to the management interface """ - logger.debug("disconnecting...") + # XXX method a bit too long, split + logger.debug("terminating openvpn process...") if self.connected(): try: self._send_command("signal SIGTERM\n") @@ -424,38 +386,10 @@ to be triggered for each one of them. return True - # - # parse info - # - - def get_status_io(self): - status = self.vpn_status() - if isinstance(status, str): - lines = status.split('\n') - if isinstance(status, list): - lines = status - try: - (header, when, tun_read, tun_write, - tcp_read, tcp_write, auth_read) = tuple(lines) - except ValueError: - return None - - when_ts = time.strptime(when.split(',')[1], "%a %b %d %H:%M:%S %Y") - sep = ',' - # XXX cleanup! - tun_read = tun_read.split(sep)[1] - tun_write = tun_write.split(sep)[1] - tcp_read = tcp_read.split(sep)[1] - tcp_write = tcp_write.split(sep)[1] - auth_read = auth_read.split(sep)[1] - - # XXX this could be a named tuple. prettier. - return when_ts, (tun_read, tun_write, tcp_read, tcp_write, auth_read) - - def get_connection_state(self): - state = self.state() - if state is not None: - ts, status_step, ok, ip, remote = state.split(',') - ts = time.gmtime(float(ts)) - # XXX this could be a named tuple. prettier. - return ts, status_step, ok, ip, remote + def _get_openvpn_process(self): + # plist = [p for p in psutil.get_process_list() if p.name == "openvpn"] + # return plist[0] if plist else None + for process in psutil.get_process_list(): + if process.name == "openvpn": + return process + return None diff --git a/src/leap/eip/tests/test_eipconnection.py b/src/leap/eip/tests/test_eipconnection.py index aefca36f..4ee5ae30 100644 --- a/src/leap/eip/tests/test_eipconnection.py +++ b/src/leap/eip/tests/test_eipconnection.py @@ -123,9 +123,14 @@ class EIPConductorTest(BaseLeapTest): self.con.status.CONNECTED) # disconnect - self.con.cleanup = Mock() + self.con.terminate_openvpn_connection = Mock() self.con.disconnect() - self.con.cleanup.assert_called_once_with() + self.con.terminate_openvpn_connection.assert_called_once_with( + shutdown=False) + self.con.terminate_openvpn_connection = Mock() + self.con.disconnect(shutdown=True) + self.con.terminate_openvpn_connection.assert_called_once_with( + shutdown=True) # new status should be disconnected # XXX this should evolve and check no errors -- cgit v1.2.3 From 53fa2c134ab2c96376276aa1c0ed74db0aaba218 Mon Sep 17 00:00:00 2001 From: kali Date: Mon, 10 Dec 2012 23:20:09 +0900 Subject: get cipher config from eip-service --- src/leap/eip/checks.py | 7 ++++++- src/leap/eip/config.py | 57 ++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 52 insertions(+), 12 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index 116c535e..a876eea1 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -427,6 +427,7 @@ class EIPConfigChecker(object): return True def fetch_definition(self, skip_download=False, + force_download=False, config=None, uri=None, domain=None): """ @@ -459,6 +460,7 @@ class EIPConfigChecker(object): self.defaultprovider.save() def fetch_eip_service_config(self, skip_download=False, + force_download=False, config=None, uri=None, domain=None): if skip_download: return True @@ -469,7 +471,10 @@ class EIPConfigChecker(object): domain = self.domain or config.get('provider', None) uri = self._get_eip_service_uri(domain=domain) - self.eipserviceconfig.load(from_uri=uri, fetcher=self.fetcher) + self.eipserviceconfig.load( + from_uri=uri, + fetcher=self.fetcher, + force_download=force_download) self.eipserviceconfig.save() def check_complete_eip_config(self, config=None): diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py index 8e687bda..1fe0530a 100644 --- a/src/leap/eip/config.py +++ b/src/leap/eip/config.py @@ -5,6 +5,7 @@ import tempfile from leap import __branding as BRANDING from leap import certs +from leap.util.misc import null_check from leap.util.fileutil import (which, mkdir_p, check_and_fix_urw_only) from leap.base import config as baseconfig @@ -57,30 +58,30 @@ def get_socket_path(): return socket_path -def get_eip_gateway(provider=None): +def get_eip_gateway(eipconfig=None, eipserviceconfig=None): """ return the first host in eip service config that matches the name defined in the eip.json config file. """ - placeholder = "testprovider.example.org" - # XXX check for null on provider?? + null_check(eipconfig, "eipconfig") + null_check(eipserviceconfig, "eipserviceconfig") + + PLACEHOLDER = "testprovider.example.org" - eipconfig = EIPConfig(domain=provider) - eipconfig.load() conf = eipconfig.config + eipsconf = eipserviceconfig.config primary_gateway = conf.get('primary_gateway', None) if not primary_gateway: - return placeholder + return PLACEHOLDER - eipserviceconfig = EIPServiceConfig(domain=provider) - eipserviceconfig.load() - eipsconf = eipserviceconfig.get_config() gateways = eipsconf.get('gateways', None) + if not gateways: logger.error('missing gateways in eip service config') - return placeholder + return PLACEHOLDER + if len(gateways) > 0: for gw in gateways: name = gw.get('name', None) @@ -100,6 +101,26 @@ def get_eip_gateway(provider=None): 'gateway list') +def get_cipher_options(eipserviceconfig=None): + """ + gathers optional cipher options from eip-service config. + :param eipserviceconfig: EIPServiceConfig instance + """ + null_check(eipserviceconfig, 'eipserviceconfig') + eipsconf = eipserviceconfig.get_config() + + ALLOWED_KEYS = ("auth", "cipher", "tls-cipher") + opts = [] + if 'openvpn_configuration' in eipsconf: + config = eipserviceconfig.openvpn_configuration + for key, value in config.items(): + if key in ALLOWED_KEYS and value is not None: + # I humbly think we should sanitize this + # input against `valid` openvpn settings. -- kali. + opts.append(['--%s' % key, value]) + return opts + + def build_ovpn_options(daemon=False, socket_path=None, **kwargs): """ build a list of options @@ -116,6 +137,10 @@ def build_ovpn_options(daemon=False, socket_path=None, **kwargs): # things from there if present. provider = kwargs.pop('provider', None) + eipconfig = EIPConfig(domain=provider) + eipconfig.load() + eipserviceconfig = EIPServiceConfig(domain=provider) + eipserviceconfig.load() # get user/group name # also from config. @@ -139,9 +164,19 @@ def build_ovpn_options(daemon=False, socket_path=None, **kwargs): # remote opts.append('--remote') - gw = get_eip_gateway(provider=provider) + + gw = get_eip_gateway(eipconfig=eipconfig, + eipserviceconfig=eipserviceconfig) logger.debug('setting eip gateway to %s', gw) opts.append(str(gw)) + + # get ciphers + ciphers = get_cipher_options( + eipserviceconfig=eipserviceconfig) + for cipheropt in ciphers: + opts.append(str(cipheropt)) + + # get port/protocol from eipservice too opts.append('1194') #opts.append('80') opts.append('udp') -- cgit v1.2.3 From 04d423e2a89034dfb86fe305108162fd2a696079 Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 12 Dec 2012 03:29:31 +0900 Subject: tests for openvpn options and make the rest of tests pass after some changes in this branch (dirtyness in config files) --- src/leap/eip/checks.py | 2 +- src/leap/eip/config.py | 30 ++++++++----- src/leap/eip/specs.py | 7 +++ src/leap/eip/tests/test_checks.py | 6 +++ src/leap/eip/tests/test_config.py | 93 +++++++++++++++++++++++++++++++++++---- 5 files changed, 117 insertions(+), 21 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index a876eea1..8d615b94 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -502,7 +502,7 @@ class EIPConfigChecker(object): return self.eipconfig.exists() def _dump_default_eipconfig(self): - self.eipconfig.save() + self.eipconfig.save(force=True) def _get_provider_definition_uri(self, domain=None, path=None): if domain is None: diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py index 1fe0530a..e40d2785 100644 --- a/src/leap/eip/config.py +++ b/src/leap/eip/config.py @@ -1,6 +1,7 @@ import logging import os import platform +import re import tempfile from leap import __branding as BRANDING @@ -110,14 +111,18 @@ def get_cipher_options(eipserviceconfig=None): eipsconf = eipserviceconfig.get_config() ALLOWED_KEYS = ("auth", "cipher", "tls-cipher") + CIPHERS_REGEX = re.compile("[A-Z0-9\-]+") opts = [] if 'openvpn_configuration' in eipsconf: - config = eipserviceconfig.openvpn_configuration + config = eipserviceconfig.config.get( + "openvpn_configuration", {}) for key, value in config.items(): if key in ALLOWED_KEYS and value is not None: - # I humbly think we should sanitize this - # input against `valid` openvpn settings. -- kali. - opts.append(['--%s' % key, value]) + sanitized_val = CIPHERS_REGEX.findall(value) + if len(sanitized_val) != 0: + _val = sanitized_val[0] + opts.append('--%s' % key) + opts.append('%s' % _val) return opts @@ -162,7 +167,9 @@ def build_ovpn_options(daemon=False, socket_path=None, **kwargs): opts.append('--verb') opts.append("%s" % verbosity) - # remote + # remote ############################## + # (server, port, protocol) + opts.append('--remote') gw = get_eip_gateway(eipconfig=eipconfig, @@ -170,12 +177,6 @@ def build_ovpn_options(daemon=False, socket_path=None, **kwargs): logger.debug('setting eip gateway to %s', gw) opts.append(str(gw)) - # get ciphers - ciphers = get_cipher_options( - eipserviceconfig=eipserviceconfig) - for cipheropt in ciphers: - opts.append(str(cipheropt)) - # get port/protocol from eipservice too opts.append('1194') #opts.append('80') @@ -185,6 +186,13 @@ def build_ovpn_options(daemon=False, socket_path=None, **kwargs): opts.append('--remote-cert-tls') opts.append('server') + # get ciphers ####################### + + ciphers = get_cipher_options( + eipserviceconfig=eipserviceconfig) + for cipheropt in ciphers: + opts.append(str(cipheropt)) + # set user and group opts.append('--user') opts.append('%s' % user) diff --git a/src/leap/eip/specs.py b/src/leap/eip/specs.py index 57e7537b..cf5d5359 100644 --- a/src/leap/eip/specs.py +++ b/src/leap/eip/specs.py @@ -119,6 +119,13 @@ eipservice_config_spec = { "label": {"en":"west"}, "capabilities": {}, "hosts": ["1.2.3.4", "1.2.3.5"]}] + }, + 'openvpn_configuration': { + 'type': dict, + 'default': { + "auth": None, + "cipher": None, + "tls-cipher": None} } } } diff --git a/src/leap/eip/tests/test_checks.py b/src/leap/eip/tests/test_checks.py index 1d7bfc17..ab11037a 100644 --- a/src/leap/eip/tests/test_checks.py +++ b/src/leap/eip/tests/test_checks.py @@ -25,6 +25,7 @@ from leap.eip.tests import data as testdata from leap.testing.basetest import BaseLeapTest from leap.testing.https_server import BaseHTTPSServerTestCase from leap.testing.https_server import where as where_cert +from leap.util.fileutil import mkdir_f class NoLogRequestHandler: @@ -118,6 +119,7 @@ class EIPCheckTest(BaseLeapTest): sampleconfig = copy.copy(testdata.EIP_SAMPLE_CONFIG) sampleconfig['provider'] = None eipcfg_path = checker.eipconfig.filename + mkdir_f(eipcfg_path) with open(eipcfg_path, 'w') as fp: json.dump(sampleconfig, fp) #with self.assertRaises(eipexceptions.EIPMissingDefaultProvider): @@ -138,6 +140,8 @@ class EIPCheckTest(BaseLeapTest): def test_fetch_definition(self): with patch.object(requests, "get") as mocked_get: mocked_get.return_value.status_code = 200 + mocked_get.return_value.headers = { + 'last-modified': "Wed Dec 12 12:12:12 GMT 2012"} mocked_get.return_value.json = DEFAULT_PROVIDER_DEFINITION checker = eipchecks.EIPConfigChecker(fetcher=requests) sampleconfig = testdata.EIP_SAMPLE_CONFIG @@ -156,6 +160,8 @@ class EIPCheckTest(BaseLeapTest): def test_fetch_eip_service_config(self): with patch.object(requests, "get") as mocked_get: mocked_get.return_value.status_code = 200 + mocked_get.return_value.headers = { + 'last-modified': "Wed Dec 12 12:12:12 GMT 2012"} mocked_get.return_value.json = testdata.EIP_SAMPLE_SERVICE checker = eipchecks.EIPConfigChecker(fetcher=requests) sampleconfig = testdata.EIP_SAMPLE_CONFIG diff --git a/src/leap/eip/tests/test_config.py b/src/leap/eip/tests/test_config.py index 50538240..404d543f 100644 --- a/src/leap/eip/tests/test_config.py +++ b/src/leap/eip/tests/test_config.py @@ -1,3 +1,4 @@ +from collections import OrderedDict import json import os import platform @@ -10,7 +11,7 @@ except ImportError: #from leap.base import constants #from leap.eip import config as eip_config -from leap import __branding as BRANDING +#from leap import __branding as BRANDING from leap.eip import config as eipconfig from leap.eip.tests.data import EIP_SAMPLE_CONFIG, EIP_SAMPLE_SERVICE from leap.testing.basetest import BaseLeapTest @@ -47,11 +48,21 @@ class EIPConfigTest(BaseLeapTest): open(tfile, 'wb').close() os.chmod(tfile, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR) - def write_sample_eipservice(self): + def write_sample_eipservice(self, vpnciphers=False, extra_vpnopts=None): conf = eipconfig.EIPServiceConfig() folder, f = os.path.split(conf.filename) if not os.path.isdir(folder): mkdir_p(folder) + if vpnciphers: + openvpnconfig = OrderedDict({ + "auth": "SHA1", + "cipher": "AES-128-CBC", + "tls-cipher": "DHE-RSA-AES128-SHA"}) + if extra_vpnopts: + for k, v in extra_vpnopts.items(): + openvpnconfig[k] = v + EIP_SAMPLE_SERVICE['openvpn_configuration'] = openvpnconfig + with open(conf.filename, 'w') as fd: fd.write(json.dumps(EIP_SAMPLE_SERVICE)) @@ -63,8 +74,13 @@ class EIPConfigTest(BaseLeapTest): with open(conf.filename, 'w') as fd: fd.write(json.dumps(EIP_SAMPLE_CONFIG)) - def get_expected_openvpn_args(self): + def get_expected_openvpn_args(self, with_openvpn_ciphers=False): args = [] + eipconf = eipconfig.EIPConfig(domain=self.provider) + eipconf.load() + eipsconf = eipconfig.EIPServiceConfig(domain=self.provider) + eipsconf.load() + username = self.get_username() groupname = self.get_groupname() @@ -75,8 +91,10 @@ class EIPConfigTest(BaseLeapTest): args.append('--persist-tun') args.append('--persist-key') args.append('--remote') + args.append('%s' % eipconfig.get_eip_gateway( - provider=self.provider)) + eipconfig=eipconf, + eipserviceconfig=eipsconf)) # XXX get port!? args.append('1194') # XXX get proto @@ -85,6 +103,14 @@ class EIPConfigTest(BaseLeapTest): args.append('--remote-cert-tls') args.append('server') + if with_openvpn_ciphers: + CIPHERS = [ + "--tls-cipher", "DHE-RSA-AES128-SHA", + "--cipher", "AES-128-CBC", + "--auth", "SHA1"] + for opt in CIPHERS: + args.append(opt) + args.append('--user') args.append(username) args.append('--group') @@ -139,14 +165,63 @@ class EIPConfigTest(BaseLeapTest): from leap.util.fileutil import which path = os.environ['PATH'] vpnbin = which('openvpn', path=path) - print 'path =', path - print 'vpnbin = ', vpnbin - command, args = eipconfig.build_ovpn_command( + #print 'path =', path + #print 'vpnbin = ', vpnbin + vpncommand, vpnargs = eipconfig.build_ovpn_command( + do_pkexec_check=False, vpnbin=vpnbin, + socket_path="/tmp/test.socket", + provider=self.provider) + self.assertEqual(vpncommand, self.home + '/bin/openvpn') + self.assertEqual(vpnargs, self.get_expected_openvpn_args()) + + def test_build_ovpn_command_openvpnoptions(self): + self.touch_exec() + + from leap.eip import config as eipconfig + from leap.util.fileutil import which + path = os.environ['PATH'] + vpnbin = which('openvpn', path=path) + + self.write_sample_eipconfig() + + # regular run, everything normal + self.write_sample_eipservice(vpnciphers=True) + vpncommand, vpnargs = eipconfig.build_ovpn_command( + do_pkexec_check=False, vpnbin=vpnbin, + socket_path="/tmp/test.socket", + provider=self.provider) + self.assertEqual(vpncommand, self.home + '/bin/openvpn') + expected = self.get_expected_openvpn_args( + with_openvpn_ciphers=True) + self.assertEqual(vpnargs, expected) + + # bad options -- illegal options + self.write_sample_eipservice( + vpnciphers=True, + # WE ONLY ALLOW vpn options in auth, cipher, tls-cipher + extra_vpnopts={"notallowedconfig": "badvalue"}) + vpncommand, vpnargs = eipconfig.build_ovpn_command( + do_pkexec_check=False, vpnbin=vpnbin, + socket_path="/tmp/test.socket", + provider=self.provider) + self.assertEqual(vpncommand, self.home + '/bin/openvpn') + expected = self.get_expected_openvpn_args( + with_openvpn_ciphers=True) + self.assertEqual(vpnargs, expected) + + # bad options -- illegal chars + self.write_sample_eipservice( + vpnciphers=True, + # WE ONLY ALLOW A-Z09\- + extra_vpnopts={"cipher": "AES-128-CBC;FOOTHING"}) + vpncommand, vpnargs = eipconfig.build_ovpn_command( do_pkexec_check=False, vpnbin=vpnbin, socket_path="/tmp/test.socket", provider=self.provider) - self.assertEqual(command, self.home + '/bin/openvpn') - self.assertEqual(args, self.get_expected_openvpn_args()) + self.assertEqual(vpncommand, self.home + '/bin/openvpn') + expected = self.get_expected_openvpn_args( + with_openvpn_ciphers=True) + self.assertEqual(vpnargs, expected) if __name__ == "__main__": -- cgit v1.2.3 From 490cde9c33039c2c5b16d929d6f8bb8e8f06f430 Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 5 Dec 2012 23:50:08 +0900 Subject: tests for firstrun/wizard --- src/leap/eip/tests/test_eipconnection.py | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src/leap/eip') diff --git a/src/leap/eip/tests/test_eipconnection.py b/src/leap/eip/tests/test_eipconnection.py index 4ee5ae30..1f1605ed 100644 --- a/src/leap/eip/tests/test_eipconnection.py +++ b/src/leap/eip/tests/test_eipconnection.py @@ -66,6 +66,11 @@ class EIPConductorTest(BaseLeapTest): self.manager = Mock(name="openvpnmanager_mock") self.con = MockedEIPConnection() self.con.provider = self.provider + + # XXX watch out. This sometimes is throwing the following error: + # NoSuchProcess: process no longer exists (pid=6571) + # because of a bad implementation of _check_if_running_instance + self.con.run_openvpn_checks() def tearDown(self): -- cgit v1.2.3 From 52aa909c23bff688e2a164dca546e4a493e72fe4 Mon Sep 17 00:00:00 2001 From: kali Date: Thu, 6 Dec 2012 00:55:07 +0900 Subject: cleanup lingering temporal files --- src/leap/eip/tests/test_eipconnection.py | 17 ++++++++++++++++- src/leap/eip/tests/test_openvpnconnection.py | 21 +++++++++++++++++---- 2 files changed, 33 insertions(+), 5 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/tests/test_eipconnection.py b/src/leap/eip/tests/test_eipconnection.py index 1f1605ed..163f8d45 100644 --- a/src/leap/eip/tests/test_eipconnection.py +++ b/src/leap/eip/tests/test_eipconnection.py @@ -1,6 +1,8 @@ +import glob import logging import platform -import os +#import os +import shutil logging.basicConfig() logger = logging.getLogger(name=__name__) @@ -74,8 +76,18 @@ class EIPConductorTest(BaseLeapTest): self.con.run_openvpn_checks() def tearDown(self): + pass + + def doCleanups(self): + super(BaseLeapTest, self).doCleanups() + self.cleanupSocketDir() del self.con + def cleanupSocketDir(self): + ptt = ('/tmp/leap-tmp*') + for tmpdir in glob.glob(ptt): + shutil.rmtree(tmpdir) + # # tests # @@ -86,6 +98,7 @@ class EIPConductorTest(BaseLeapTest): """ con = self.con self.assertEqual(con.autostart, True) + # XXX moar! def test_ovpn_command(self): """ @@ -103,6 +116,7 @@ class EIPConductorTest(BaseLeapTest): # needed to run tests. (roughly 3 secs for this only) # We should modularize and inject Mocks on more places. + oldcon = self.con del(self.con) config_checker = Mock() self.con = MockedEIPConnection(config_checker=config_checker) @@ -112,6 +126,7 @@ class EIPConductorTest(BaseLeapTest): skip_download=False) # XXX test for cert_checker also + self.con = oldcon # connect/disconnect calls diff --git a/src/leap/eip/tests/test_openvpnconnection.py b/src/leap/eip/tests/test_openvpnconnection.py index 0f27facf..f7493567 100644 --- a/src/leap/eip/tests/test_openvpnconnection.py +++ b/src/leap/eip/tests/test_openvpnconnection.py @@ -58,16 +58,27 @@ class OpenVPNConnectionTest(BaseLeapTest): def setUp(self): # XXX this will have to change for win, host=localhost host = eipconfig.get_socket_path() + self.host = host self.manager = MockedOpenVPNConnection(host=host) def tearDown(self): + pass + + def doCleanups(self): + super(BaseLeapTest, self).doCleanups() + self.cleanupSocketDir() + + def cleanupSocketDir(self): # remove the socket folder. # XXX only if posix. in win, host is localhost, so nothing # has to be done. - if self.manager.host: - folder, fpath = os.path.split(self.manager.host) - assert folder.startswith('/tmp/leap-tmp') # safety check - shutil.rmtree(folder) + if self.host: + folder, fpath = os.path.split(self.host) + try: + assert folder.startswith('/tmp/leap-tmp') # safety check + shutil.rmtree(folder) + except: + self.fail("could not remove temp file") del self.manager @@ -108,12 +119,14 @@ class OpenVPNConnectionTest(BaseLeapTest): self.assertEqual(self.manager.port, 7777) def test_port_types_init(self): + oldmanager = self.manager self.manager = MockedOpenVPNConnection(port="42") self.assertEqual(self.manager.port, 42) self.manager = MockedOpenVPNConnection() self.assertEqual(self.manager.port, "unix") self.manager = MockedOpenVPNConnection(port="bad") self.assertEqual(self.manager.port, None) + self.manager = oldmanager def test_uds_telnet_called_on_connect(self): self.manager.connect_to_management() -- cgit v1.2.3 From f671412ebd4f2ce0dd9948cb8821f1d6d8ac7d9b Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 12 Dec 2012 07:21:51 +0900 Subject: parse new service format --- src/leap/eip/config.py | 27 +++++++++-------- src/leap/eip/specs.py | 37 ++++++++++++---------- src/leap/eip/tests/data.py | 33 +++++++++++--------- src/leap/eip/tests/test_config.py | 64 ++++++++++++++++++++++++++++++++++++--- 4 files changed, 113 insertions(+), 48 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py index e40d2785..48e6e9a7 100644 --- a/src/leap/eip/config.py +++ b/src/leap/eip/config.py @@ -65,9 +65,12 @@ def get_eip_gateway(eipconfig=None, eipserviceconfig=None): that matches the name defined in the eip.json config file. """ + # XXX eventually we should move to a more clever + # gateway selection. maybe we could return + # all gateways that match our cluster. + null_check(eipconfig, "eipconfig") null_check(eipserviceconfig, "eipserviceconfig") - PLACEHOLDER = "testprovider.example.org" conf = eipconfig.config @@ -78,26 +81,26 @@ def get_eip_gateway(eipconfig=None, eipserviceconfig=None): return PLACEHOLDER gateways = eipsconf.get('gateways', None) - if not gateways: logger.error('missing gateways in eip service config') return PLACEHOLDER if len(gateways) > 0: for gw in gateways: - name = gw.get('name', None) - if not name: + clustername = gw.get('cluster', None) + if not clustername: + logger.error('no cluster name') return - if name == primary_gateway: - hosts = gw.get('hosts', None) - if not hosts: - logger.error('no hosts') + if clustername == primary_gateway: + # XXX at some moment, we must + # make this a more generic function, + # and return ports, protocols... + ipaddress = gw.get('ip_address', None) + if not ipaddress: + logger.error('no ip_address') return - if len(hosts) > 0: - return hosts[0] - else: - logger.error('no hosts') + return ipaddress logger.error('could not find primary gateway in provider' 'gateway list') diff --git a/src/leap/eip/specs.py b/src/leap/eip/specs.py index cf5d5359..c41fd29b 100644 --- a/src/leap/eip/specs.py +++ b/src/leap/eip/specs.py @@ -77,12 +77,12 @@ eipconfig_spec = { }, 'primary_gateway': { 'type': unicode, - 'default': u"turkey", + 'default': u"location_unknown", #'required': True }, 'secondary_gateway': { 'type': unicode, - 'default': u"france" + 'default': u"location_unknown2" }, 'management_password': { 'type': unicode @@ -100,25 +100,30 @@ eipservice_config_spec = { 'default': 1 }, 'version': { - 'type': unicode, + 'type': int, 'required': True, - 'default': "0.1.0" + 'default': 1 }, - 'capabilities': { - 'type': dict, - 'default': { - "transport": ["openvpn"], - "ports": ["80", "53"], - "protocols": ["udp", "tcp"], - "static_ips": True, - "adblock": True} + 'clusters': { + 'type': list, + 'default': [ + {"label": { + "en": "Location Unknown"}, + "name": "location_unknown"}] }, 'gateways': { 'type': list, - 'default': [{"country_code": "us", - "label": {"en":"west"}, - "capabilities": {}, - "hosts": ["1.2.3.4", "1.2.3.5"]}] + 'default': [ + {"capabilities": { + "adblock": True, + "filter_dns": True, + "ports": ["80", "53", "443", "1194"], + "protocols": ["udp", "tcp"], + "transport": ["openvpn"], + "user_ips": False}, + "cluster": "location_unknown", + "host": "location.example.org", + "ip_address": "127.0.0.1"}] }, 'openvpn_configuration': { 'type': dict, diff --git a/src/leap/eip/tests/data.py b/src/leap/eip/tests/data.py index cadf720e..a7fe1853 100644 --- a/src/leap/eip/tests/data.py +++ b/src/leap/eip/tests/data.py @@ -23,26 +23,29 @@ EIP_SAMPLE_CONFIG = { "keys/client/openvpn.pem" % PROVIDER), "connect_on_login": True, "block_cleartext_traffic": True, - "primary_gateway": "turkey", - "secondary_gateway": "france", + "primary_gateway": "location_unknown", + "secondary_gateway": "location_unknown2", #"management_password": "oph7Que1othahwiech6J" } EIP_SAMPLE_SERVICE = { "serial": 1, - "version": "0.1.0", - "capabilities": { - "transport": ["openvpn"], - "ports": ["80", "53"], - "protocols": ["udp", "tcp"], - "static_ips": True, - "adblock": True - }, + "version": 1, + "clusters": [ + {"label": { + "en": "Location Unknown"}, + "name": "location_unknown"} + ], "gateways": [ - {"country_code": "tr", - "name": "turkey", - "label": {"en":"Ankara, Turkey"}, - "capabilities": {}, - "hosts": ["192.0.43.10"]} + {"capabilities": { + "adblock": True, + "filter_dns": True, + "ports": ["80", "53", "443", "1194"], + "protocols": ["udp", "tcp"], + "transport": ["openvpn"], + "user_ips": False}, + "cluster": "location_unknown", + "host": "location.example.org", + "ip_address": "192.0.43.10"} ] } diff --git a/src/leap/eip/tests/test_config.py b/src/leap/eip/tests/test_config.py index 404d543f..5977ef3c 100644 --- a/src/leap/eip/tests/test_config.py +++ b/src/leap/eip/tests/test_config.py @@ -15,7 +15,7 @@ except ImportError: from leap.eip import config as eipconfig from leap.eip.tests.data import EIP_SAMPLE_CONFIG, EIP_SAMPLE_SERVICE from leap.testing.basetest import BaseLeapTest -from leap.util.fileutil import mkdir_p +from leap.util.fileutil import mkdir_p, mkdir_f _system = platform.system() @@ -48,11 +48,12 @@ class EIPConfigTest(BaseLeapTest): open(tfile, 'wb').close() os.chmod(tfile, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR) - def write_sample_eipservice(self, vpnciphers=False, extra_vpnopts=None): + def write_sample_eipservice(self, vpnciphers=False, extra_vpnopts=None, + gateways=None): conf = eipconfig.EIPServiceConfig() - folder, f = os.path.split(conf.filename) - if not os.path.isdir(folder): - mkdir_p(folder) + mkdir_f(conf.filename) + if gateways: + EIP_SAMPLE_SERVICE['gateways'] = gateways if vpnciphers: openvpnconfig = OrderedDict({ "auth": "SHA1", @@ -75,6 +76,10 @@ class EIPConfigTest(BaseLeapTest): fd.write(json.dumps(EIP_SAMPLE_CONFIG)) def get_expected_openvpn_args(self, with_openvpn_ciphers=False): + """ + yeah, this is almost as duplicating the + code for building the command + """ args = [] eipconf = eipconfig.EIPConfig(domain=self.provider) eipconf.load() @@ -156,6 +161,55 @@ class EIPConfigTest(BaseLeapTest): # params in the function call, to disable # some checks. + def test_get_eip_gateway(self): + self.write_sample_eipconfig() + eipconf = eipconfig.EIPConfig(domain=self.provider) + + # default eipservice + self.write_sample_eipservice() + eipsconf = eipconfig.EIPServiceConfig(domain=self.provider) + + gateway = eipconfig.get_eip_gateway( + eipconfig=eipconf, + eipserviceconfig=eipsconf) + + # in spec is local gateway by default + self.assertEqual(gateway, '127.0.0.1') + + # change eipservice + # right now we only check that cluster == selected primary gw in + # eip.json, and pick first matching ip + eipconf._config.config['primary_gateway'] = "foo_provider" + newgateways = [{"cluster": "foo_provider", + "ip_address": "127.0.0.99"}] + self.write_sample_eipservice(gateways=newgateways) + eipsconf = eipconfig.EIPServiceConfig(domain=self.provider) + # load from disk file + eipsconf.load() + + gateway = eipconfig.get_eip_gateway( + eipconfig=eipconf, + eipserviceconfig=eipsconf) + self.assertEqual(gateway, '127.0.0.99') + + # change eipservice, several gateways + # right now we only check that cluster == selected primary gw in + # eip.json, and pick first matching ip + eipconf._config.config['primary_gateway'] = "bar_provider" + newgateways = [{"cluster": "foo_provider", + "ip_address": "127.0.0.99"}, + {'cluster': "bar_provider", + "ip_address": "127.0.0.88"}] + self.write_sample_eipservice(gateways=newgateways) + eipsconf = eipconfig.EIPServiceConfig(domain=self.provider) + # load from disk file + eipsconf.load() + + gateway = eipconfig.get_eip_gateway( + eipconfig=eipconf, + eipserviceconfig=eipsconf) + self.assertEqual(gateway, '127.0.0.88') + def test_build_ovpn_command_empty_config(self): self.touch_exec() self.write_sample_eipservice() -- cgit v1.2.3 From 4984f2c966d11f529a2a8b722814b748b6a524d2 Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 12 Dec 2012 09:16:53 +0900 Subject: changed some values in new style eipconfig --- src/leap/eip/checks.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index 8d615b94..92964a9d 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -242,7 +242,9 @@ class ProviderCertChecker(object): raise try: pemfile_content = req.content - self.is_valid_pemfile(pemfile_content) + valid = self.is_valid_pemfile(pemfile_content) + if not valid: + return False cert_path = self._get_client_cert_path() self.write_cert(pemfile_content, to=cert_path) except: @@ -303,6 +305,10 @@ class ProviderCertChecker(object): if len(certparts) > 1: cert_s = sep + certparts[1] ssl.PEM_cert_to_DER_cert(cert_s) + except ValueError: + # valid_pemfile raises a value error if not BEGIN_CERTIFICATE in + # there... + return False except: # XXX raise proper exception raise -- cgit v1.2.3 From d71e05fdefa7cb9699804bc93adba97921ca923f Mon Sep 17 00:00:00 2001 From: kali Date: Sat, 15 Dec 2012 02:23:36 +0900 Subject: workaround for not-yet-valid certs skipping valid_from ts on cert --- src/leap/eip/checks.py | 29 ++++++----------------------- 1 file changed, 6 insertions(+), 23 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index 92964a9d..d7f4402b 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -160,7 +160,6 @@ class ProviderCertChecker(object): if autocacert and verify is True and self.cacert is not None: logger.debug('verify cert: %s', self.cacert) verify = self.cacert - #import pdb4qt; pdb4qt.set_trace() logger.debug('is https working?') logger.debug('uri: %s (verify:%s)', uri, verify) try: @@ -278,7 +277,10 @@ class ProviderCertChecker(object): cert = gnutls.crypto.X509Certificate(cert_s) from_ = time.gmtime(cert.activation_time) to_ = time.gmtime(cert.expiration_time) - return from_ < now() < to_ + # FIXME BUG ON LEAP_CLI, certs are not valid on gmtime + # See #1153 + #return from_ < now() < to_ + return now() < to_ def is_valid_pemfile(self, cert_s=None): """ @@ -292,27 +294,8 @@ class ProviderCertChecker(object): certfile = self._get_client_cert_path() with open(certfile) as cf: cert_s = cf.read() - try: - # XXX get a real cert validation - # so far this is only checking begin/end - # delimiters :) - # XXX use gnutls for get proper - # validation. - # crypto.X509Certificate(cert_s) - sep = "-" * 5 + "BEGIN CERTIFICATE" + "-" * 5 - # we might have private key and cert in the same file - certparts = cert_s.split(sep) - if len(certparts) > 1: - cert_s = sep + certparts[1] - ssl.PEM_cert_to_DER_cert(cert_s) - except ValueError: - # valid_pemfile raises a value error if not BEGIN_CERTIFICATE in - # there... - return False - except: - # XXX raise proper exception - raise - return True + valid = certs.can_load_cert_and_pkey(cert_s) + return valid @property def ca_cert_path(self): -- cgit v1.2.3 From f104e834c96c9ec10a465bda46ef05e87ea32516 Mon Sep 17 00:00:00 2001 From: kali Date: Tue, 18 Dec 2012 03:45:23 +0900 Subject: Fix parsing of timestamps in a locate independent way Close #772 --- src/leap/eip/eipconnection.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/eipconnection.py b/src/leap/eip/eipconnection.py index 8751f643..27734f80 100644 --- a/src/leap/eip/eipconnection.py +++ b/src/leap/eip/eipconnection.py @@ -7,6 +7,8 @@ import Queue import sys import time +from dateutil.parser import parse as dateparse + from leap.eip.checks import ProviderCertChecker from leap.eip.checks import EIPConfigChecker from leap.eip import config as eipconfig @@ -114,8 +116,7 @@ class StatusMixIn(object): except ValueError: return None - # XXX this will break with different locales I assume... - when_ts = time.strptime(when.split(',')[1], "%a %b %d %H:%M:%S %Y") + when_ts = dateparse(when.split(',')[1]).timetuple() sep = ',' # XXX clean up this! tun_read = tun_read.split(sep)[1] -- cgit v1.2.3 From 75057e55828accb62beef1f73364edd82a75ed30 Mon Sep 17 00:00:00 2001 From: kali Date: Tue, 18 Dec 2012 05:54:50 +0900 Subject: do not pass CA.crt to checks for https also skip temporary errors when previous openvpn process vanishes. --- src/leap/eip/checks.py | 3 +-- src/leap/eip/openvpnconnection.py | 16 ++++++++++------ 2 files changed, 11 insertions(+), 8 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index d7f4402b..4afba8b6 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -84,8 +84,7 @@ class ProviderCertChecker(object): # For MVS checker.is_there_provider_ca() - # XXX FAKE IT!!! - checker.is_https_working(verify=do_verify, autocacert=True) + checker.is_https_working(verify=do_verify, autocacert=False) checker.check_new_cert_needed(verify=do_verify) def download_ca_cert(self, uri=None, verify=True): diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index 253f5056..59ba44f0 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -275,12 +275,16 @@ to be triggered for each one of them. """ check if openvpn is already running """ - for process in psutil.get_process_list(): - if process.name == "openvpn": - logger.debug('an openvpn instance is already running.') - logger.debug('attempting to stop openvpn instance.') - if not self._stop_openvpn(): - raise eip_exceptions.OpenVPNAlreadyRunning + try: + for process in psutil.get_process_list(): + if process.name == "openvpn": + logger.debug('an openvpn instance is already running.') + logger.debug('attempting to stop openvpn instance.') + if not self._stop_openvpn(): + raise eip_exceptions.OpenVPNAlreadyRunning + + except psutil.error.NoSuchProcess: + logger.debug('detected a process which died. passing.') logger.debug('no openvpn instance found.') -- cgit v1.2.3 From 8808c0e2cba1660515fab4a2931221c3bda7e093 Mon Sep 17 00:00:00 2001 From: kali Date: Tue, 18 Dec 2012 20:59:51 +0900 Subject: do not assume that we were the only ones launching openvpn --- src/leap/eip/openvpnconnection.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index 59ba44f0..c2dc71a6 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -374,8 +374,9 @@ to be triggered for each one of them. logger.debug('process :%s' % process) cmdline = process.cmdline - if isinstance(cmdline, list): - _index = cmdline.index("--management") + manag_flag = "--management" + if isinstance(cmdline, list) and manag_flag in cmdline: + _index = cmdline.index(manag_flag) self.host = cmdline[_index + 1] self._send_command("signal SIGTERM\n") -- cgit v1.2.3 From 20f779b644a551bf56cb735868c55cd50d7c3610 Mon Sep 17 00:00:00 2001 From: kali Date: Tue, 18 Dec 2012 21:07:06 +0900 Subject: catch gnutls error while validating pemfile --- src/leap/eip/checks.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index 4afba8b6..65596d1c 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -293,7 +293,11 @@ class ProviderCertChecker(object): certfile = self._get_client_cert_path() with open(certfile) as cf: cert_s = cf.read() - valid = certs.can_load_cert_and_pkey(cert_s) + try: + valid = certs.can_load_cert_and_pkey(cert_s) + except certs.BadCertError: + logger.warning("Not valid pemfile") + valid = False return valid @property -- cgit v1.2.3 From e98c3cc5fad75bea038dc67238e5ce85d701b1e1 Mon Sep 17 00:00:00 2001 From: kali Date: Thu, 20 Dec 2012 02:50:52 +0900 Subject: fix broken tests --- src/leap/eip/checks.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index 65596d1c..9ae6e5f5 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -278,8 +278,8 @@ class ProviderCertChecker(object): to_ = time.gmtime(cert.expiration_time) # FIXME BUG ON LEAP_CLI, certs are not valid on gmtime # See #1153 - #return from_ < now() < to_ - return now() < to_ + return from_ < now() < to_ + #return now() < to_ def is_valid_pemfile(self, cert_s=None): """ -- cgit v1.2.3 From 656419216f15bfb1859ba850d2d9c9d143034e23 Mon Sep 17 00:00:00 2001 From: kali Date: Tue, 8 Jan 2013 00:32:21 +0900 Subject: doc polishing --- src/leap/eip/openvpnconnection.py | 1 + 1 file changed, 1 insertion(+) (limited to 'src/leap/eip') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index c2dc71a6..eb3b5ec0 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -276,6 +276,7 @@ to be triggered for each one of them. check if openvpn is already running """ try: + #FIXME this gives DeprecationWarning for process in psutil.get_process_list(): if process.name == "openvpn": logger.debug('an openvpn instance is already running.') -- cgit v1.2.3 From e35eb606faef1ccd06201a0b38a462375426cedd Mon Sep 17 00:00:00 2001 From: kali Date: Mon, 7 Jan 2013 21:10:41 +0900 Subject: Working OSX installer workflow. Using platypus for installer. Working installer at 17.6MB compressed. --- src/leap/eip/config.py | 31 +++++++++++++++++++++++++++---- src/leap/eip/eipconnection.py | 2 +- src/leap/eip/openvpnconnection.py | 7 ++++--- 3 files changed, 32 insertions(+), 8 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py index 48e6e9a7..f82049d3 100644 --- a/src/leap/eip/config.py +++ b/src/leap/eip/config.py @@ -211,7 +211,7 @@ def build_ovpn_options(daemon=False, socket_path=None, **kwargs): # XXX take them from the config object. ourplatform = platform.system() - if ourplatform in ("Linux", "Mac"): + if ourplatform in ("Linux", "Darwin"): opts.append('--management') if socket_path is None: @@ -229,6 +229,7 @@ def build_ovpn_options(daemon=False, socket_path=None, **kwargs): client_cert_path = eipspecs.client_cert_path(provider) ca_cert_path = eipspecs.provider_ca_path(provider) + # XXX FIX paths for MAC opts.append('--cert') opts.append(client_cert_path) opts.append('--key') @@ -260,9 +261,11 @@ def build_ovpn_command(debug=False, do_pkexec_check=True, vpnbin=None, use_pkexec = True ovpn = None + _plat = platform.system() + # XXX get use_pkexec from config instead. - if platform.system() == "Linux" and use_pkexec and do_pkexec_check: + if _plat == "Linux" and use_pkexec and do_pkexec_check: # check for both pkexec # AND a suitable authentication @@ -282,8 +285,17 @@ def build_ovpn_command(debug=False, do_pkexec_check=True, vpnbin=None, raise eip_exceptions.EIPNoPolkitAuthAgentAvailable command.append('pkexec') + + if vpnbin is None: - ovpn = which('openvpn') + if _plat == "Darwin": + # XXX Should hardcode our installed path + # /Applications/LEAPClient.app/Contents/Resources/openvpn.leap + openvpn_bin = "openvpn.leap" + else: + openvpn_bin = "openvpn" + #XXX hardcode for darwin + ovpn = which(openvpn_bin) else: ovpn = vpnbin if ovpn: @@ -299,7 +311,18 @@ def build_ovpn_command(debug=False, do_pkexec_check=True, vpnbin=None, # XXX check len and raise proper error - return [command[0], command[1:]] + if _plat == "Darwin": + OSX_ASADMIN = 'do shell script "%s" with administrator privileges' + # XXX fix workaround for Nones + _command = [x if x else " " for x in command] + # XXX debugging! + #import ipdb;ipdb.set_trace() + #XXX get openvpn log path from debug flags + _command.append('--log') + _command.append('/tmp/leap_openvpn.log') + return ["osascript", ["-e", OSX_ASADMIN % ' '.join(_command)]] + else: + return [command[0], command[1:]] def check_vpn_keys(provider=None): diff --git a/src/leap/eip/eipconnection.py b/src/leap/eip/eipconnection.py index 27734f80..540e7558 100644 --- a/src/leap/eip/eipconnection.py +++ b/src/leap/eip/eipconnection.py @@ -52,7 +52,7 @@ class StatusMixIn(object): logger.warning('connection refused') return if not state: - logger.debug('no state') + #logger.debug('no state') return (ts, status_step, ok, ip, remote) = state diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index eb3b5ec0..b36b0b16 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -83,7 +83,7 @@ class OpenVPNManagement(object): try: self._connect_to_management() except eip_exceptions.MissingSocketError: - logger.warning('missing management socket') + #logger.warning('missing management socket') return [] try: if hasattr(self, 'tn'): @@ -329,11 +329,12 @@ to be triggered for each one of them. #use _only_ signal_maps instead logger.debug('_launch_openvpn called') + logger.debug('watcher_cb: %s' % self.watcher_cb) if self.watcher_cb is not None: linewrite_callback = self.watcher_cb else: #XXX get logger instead - linewrite_callback = lambda line: print('watcher: %s' % line) + linewrite_callback = lambda line: logger.debug('watcher: %s' % line) # the partial is not # being applied now because we're not observing the process @@ -341,7 +342,7 @@ to be triggered for each one of them. # here since it will be handy for observing patterns in the # thru-the-manager updates (with regex) observers = (linewrite_callback, - partial(lambda con_status, line: None, self.status)) + partial(lambda con_status, line: linewrite_callback, self.status)) subp, watcher = spawn_and_watch_process( self.command, self.args, -- cgit v1.2.3 From 460dd7c20408958dda1ca8e77050e9af334b558f Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 9 Jan 2013 05:02:21 +0900 Subject: fix from_login --- src/leap/eip/checks.py | 3 --- 1 file changed, 3 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index 9ae6e5f5..a002e2d9 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -276,10 +276,7 @@ class ProviderCertChecker(object): cert = gnutls.crypto.X509Certificate(cert_s) from_ = time.gmtime(cert.activation_time) to_ = time.gmtime(cert.expiration_time) - # FIXME BUG ON LEAP_CLI, certs are not valid on gmtime - # See #1153 return from_ < now() < to_ - #return now() < to_ def is_valid_pemfile(self, cert_s=None): """ -- cgit v1.2.3 From 289722fe0eda46c8f5fbbecb84c8a0fbbe36a15f Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 9 Jan 2013 05:41:12 +0900 Subject: add resolvconf option --- src/leap/eip/config.py | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py index f82049d3..6a19633d 100644 --- a/src/leap/eip/config.py +++ b/src/leap/eip/config.py @@ -18,6 +18,8 @@ from leap.eip import specs as eipspecs logger = logging.getLogger(name=__name__) provider_ca_file = BRANDING.get('provider_ca_file', None) +_platform = platform.system() + class EIPConfig(baseconfig.JSONLeapConfig): spec = eipspecs.eipconfig_spec @@ -210,8 +212,13 @@ def build_ovpn_options(daemon=False, socket_path=None, **kwargs): # interface. unix sockets or telnet interface for win. # XXX take them from the config object. - ourplatform = platform.system() - if ourplatform in ("Linux", "Darwin"): + if _platform == "Windows": + opts.append('--management') + opts.append('localhost') + # XXX which is a good choice? + opts.append('7777') + + if _platform in ("Linux", "Darwin"): opts.append('--management') if socket_path is None: @@ -219,11 +226,14 @@ def build_ovpn_options(daemon=False, socket_path=None, **kwargs): opts.append(socket_path) opts.append('unix') - if ourplatform == "Windows": - opts.append('--management') - opts.append('localhost') - # XXX which is a good choice? - opts.append('7777') + opts.append('--script-security') + opts.append('2') + + if _platform == "Linux": + opts.append("--up") + opts.append("/etc/openvpn/update-resolv-conf") + opts.append("--down") + opts.append("/etc/openvpn/update-resolv-conf") # certs client_cert_path = eipspecs.client_cert_path(provider) @@ -261,11 +271,9 @@ def build_ovpn_command(debug=False, do_pkexec_check=True, vpnbin=None, use_pkexec = True ovpn = None - _plat = platform.system() - # XXX get use_pkexec from config instead. - if _plat == "Linux" and use_pkexec and do_pkexec_check: + if _platform == "Linux" and use_pkexec and do_pkexec_check: # check for both pkexec # AND a suitable authentication @@ -286,9 +294,8 @@ def build_ovpn_command(debug=False, do_pkexec_check=True, vpnbin=None, command.append('pkexec') - if vpnbin is None: - if _plat == "Darwin": + if _platform == "Darwin": # XXX Should hardcode our installed path # /Applications/LEAPClient.app/Contents/Resources/openvpn.leap openvpn_bin = "openvpn.leap" @@ -311,13 +318,12 @@ def build_ovpn_command(debug=False, do_pkexec_check=True, vpnbin=None, # XXX check len and raise proper error - if _plat == "Darwin": + if _platform == "Darwin": OSX_ASADMIN = 'do shell script "%s" with administrator privileges' # XXX fix workaround for Nones _command = [x if x else " " for x in command] # XXX debugging! - #import ipdb;ipdb.set_trace() - #XXX get openvpn log path from debug flags + # XXX get openvpn log path from debug flags _command.append('--log') _command.append('/tmp/leap_openvpn.log') return ["osascript", ["-e", OSX_ASADMIN % ' '.join(_command)]] -- cgit v1.2.3 From a5b4b7020daebbcb25c016cf1821818b71a2e457 Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 9 Jan 2013 06:23:45 +0900 Subject: more missed strings to be translated plus initial translation. --- src/leap/eip/exceptions.py | 55 +++++++++++++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 18 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/exceptions.py b/src/leap/eip/exceptions.py index 41eed77a..c127a58f 100644 --- a/src/leap/eip/exceptions.py +++ b/src/leap/eip/exceptions.py @@ -33,6 +33,7 @@ TODO: """ from leap.base.exceptions import LeapException +from leap.util.translations import translate # This should inherit from LeapException @@ -62,53 +63,69 @@ class Warning(EIPClientError): class EIPNoPolkitAuthAgentAvailable(CriticalError): message = "No polkit authentication agent could be found" - usermessage = ("We could not find any authentication " - "agent in your system.
" - "Make sure you have " - "polkit-gnome-authentication-agent-1 " - "running and try again.") + usermessage = translate( + "EIPErrors", + "We could not find any authentication " + "agent in your system.
" + "Make sure you have " + "polkit-gnome-authentication-agent-1 " + "running and try again.") class EIPNoPkexecAvailable(Warning): message = "No pkexec binary found" - usermessage = ("We could not find pkexec in your " - "system.
Do you want to try " - "setuid workaround? " - "(DOES NOTHING YET)") + usermessage = translate( + "EIPErrors", + "We could not find pkexec in your " + "system.
Do you want to try " + "setuid workaround? " + "(DOES NOTHING YET)") failfirst = True class EIPNoCommandError(EIPClientError): message = "no suitable openvpn command found" - usermessage = ("No suitable openvpn command found. " - "
(Might be a permissions problem)") + usermessage = translate( + "EIPErrors", + "No suitable openvpn command found. " + "
(Might be a permissions problem)") class EIPBadCertError(Warning): # XXX this should be critical and fail close message = "cert verification failed" - usermessage = "there is a problem with provider certificate" + usermessage = translate( + "EIPErrors", + "there is a problem with provider certificate") class LeapBadConfigFetchedError(Warning): message = "provider sent a malformed json file" - usermessage = "an error occurred during configuratio of leap services" + usermessage = translate( + "EIPErrors", + "an error occurred during configuratio of leap services") class OpenVPNAlreadyRunning(EIPClientError): message = "Another OpenVPN Process is already running." - usermessage = ("Another OpenVPN Process has been detected." - "Please close it before starting leap-client") + usermessage = translate( + "EIPErrors", + "Another OpenVPN Process has been detected." + "Please close it before starting leap-client") class HttpsNotSupported(LeapException): message = "connection refused while accessing via https" - usermessage = "Server does not allow secure connections." + usermessage = translate( + "EIPErrors", + "Server does not allow secure connections") class HttpsBadCertError(LeapException): message = "verification error on cert" - usermessage = "Server certificate could not be verified." + usermessage = translate( + "EIPErrors", + "Server certificate could not be verified") # # errors still needing some love @@ -117,7 +134,9 @@ class HttpsBadCertError(LeapException): class EIPInitNoKeyFileError(CriticalError): message = "No vpn keys found in the expected path" - usermessage = "We could not find your eip certs in the expected path" + usermessage = translate( + "EIPErrors", + "We could not find your eip certs in the expected path") class EIPInitBadKeyFilePermError(Warning): -- cgit v1.2.3 From f55dcd717a946651492142ed198853b1c667254b Mon Sep 17 00:00:00 2001 From: kali Date: Thu, 10 Jan 2013 02:00:21 +0900 Subject: renamed connection page --- src/leap/eip/checks.py | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index a002e2d9..b14e5dd3 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -164,13 +164,12 @@ class ProviderCertChecker(object): try: self.fetcher.get(uri, verify=verify) - except requests.exceptions.SSLError as exc: + except requests.exceptions.SSLError: # as exc: logger.error("SSLError") - # XXX RAISE! See #638 - #raise eipexceptions.HttpsBadCertError - logger.warning('BUG #638 CERT VERIFICATION FAILED! ' - '(this should be CRITICAL)') - logger.warning('SSLError: %s', exc.message) + raise eipexceptions.HttpsBadCertError + #logger.warning('BUG #638 CERT VERIFICATION FAILED! ' + #'(this should be CRITICAL)') + #logger.warning('SSLError: %s', exc.message) except requests.exceptions.ConnectionError: logger.error('ConnectionError') @@ -225,12 +224,7 @@ class ProviderCertChecker(object): return fgetfn(*args, **kwargs) try: - # XXX FIXME!!!! - # verify=verify - # Workaround for #638. return to verification - # when That's done!!! - #req = self.fetcher.get(uri, verify=False) - req = getfn(uri, verify=False) + req = getfn(uri, verify=verify) req.raise_for_status() except requests.exceptions.SSLError: @@ -444,8 +438,8 @@ class EIPConfigChecker(object): # FIXME FIXME FIXME self.defaultprovider.load( from_uri=uri, - fetcher=self.fetcher, - verify=False) + fetcher=self.fetcher) + #verify=False) self.defaultprovider.save() def fetch_eip_service_config(self, skip_download=False, -- cgit v1.2.3 From 6d85c97ddcc8a151b157919e9a7322fba151a551 Mon Sep 17 00:00:00 2001 From: kali Date: Fri, 11 Jan 2013 03:00:41 +0900 Subject: all calls except the first one are made to api uri we also parse the port number --- src/leap/eip/checks.py | 85 ++++++++++++++++++++++++++++++++++---------------- 1 file changed, 58 insertions(+), 27 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index b14e5dd3..bd158e1e 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -1,5 +1,5 @@ import logging -import ssl +#import ssl #import platform import time import os @@ -21,6 +21,8 @@ from leap.eip import constants as eipconstants from leap.eip import exceptions as eipexceptions from leap.eip import specs as eipspecs from leap.util.fileutil import mkdir_p +from leap.util.web import get_https_domain_and_port +from leap.util.misc import null_check logger = logging.getLogger(name=__name__) @@ -46,7 +48,7 @@ reachable and testable as a whole. def get_branding_ca_cert(domain): - # XXX deprecated + # deprecated ca_file = BRANDING.get('provider_ca_file') if ca_file: return leapcerts.where(ca_file) @@ -63,6 +65,10 @@ class ProviderCertChecker(object): self.fetcher = fetcher self.domain = domain + #XXX needs some kind of autoinit + #right now we set by hand + #by loading and reading provider config + self.apidomain = None self.cacert = eipspecs.provider_ca_path(domain) def run_all( @@ -159,7 +165,7 @@ class ProviderCertChecker(object): if autocacert and verify is True and self.cacert is not None: logger.debug('verify cert: %s', self.cacert) verify = self.cacert - logger.debug('is https working?') + logger.debug('checking https connection') logger.debug('uri: %s (verify:%s)', uri, verify) try: self.fetcher.get(uri, verify=verify) @@ -167,27 +173,24 @@ class ProviderCertChecker(object): except requests.exceptions.SSLError: # as exc: logger.error("SSLError") raise eipexceptions.HttpsBadCertError - #logger.warning('BUG #638 CERT VERIFICATION FAILED! ' - #'(this should be CRITICAL)') - #logger.warning('SSLError: %s', exc.message) except requests.exceptions.ConnectionError: logger.error('ConnectionError') raise eipexceptions.HttpsNotSupported else: - logger.debug('True') return True def check_new_cert_needed(self, skip_download=False, verify=True): + # XXX add autocacert logger.debug('is new cert needed?') if not self.is_cert_valid(do_raise=False): - logger.debug('True') + logger.debug('cert needed: true') self.download_new_client_cert( skip_download=skip_download, verify=verify) return True - logger.debug('False') + logger.debug('cert needed: false') return False def download_new_client_cert(self, uri=None, verify=True, @@ -199,20 +202,20 @@ class ProviderCertChecker(object): if uri is None: uri = self._get_client_cert_uri() # XXX raise InsecureURI or something better - assert uri.startswith('https') + #assert uri.startswith('https') if verify is True and self.cacert is not None: verify = self.cacert + logger.debug('verify = %s', verify) fgetfn = self.fetcher.get if credentials: user, passwd = credentials - - logger.debug('domain = %s', self.domain) + logger.debug('apidomain = %s', self.apidomain) @srpauth_protected(user, passwd, - server="https://%s" % self.domain, + server="https://%s" % self.apidomain, verify=verify) def getfn(*args, **kwargs): return fgetfn(*args, **kwargs) @@ -231,11 +234,16 @@ class ProviderCertChecker(object): logger.warning('SSLError while fetching cert. ' 'Look below for stack trace.') # XXX raise better exception - raise + return self.fail("SSLError") + except Exception as exc: + return self.fail(exc.message) + try: + logger.debug('validating cert...') pemfile_content = req.content valid = self.is_valid_pemfile(pemfile_content) if not valid: + logger.warning('invalid cert') return False cert_path = self._get_client_cert_path() self.write_cert(pemfile_content, to=cert_path) @@ -299,8 +307,7 @@ class ProviderCertChecker(object): return u"https://%s/" % self.domain def _get_client_cert_uri(self): - # XXX get the whole thing from constants - return "https://%s/1/cert" % self.domain + return "https://%s/1/cert" % self.apidomain def _get_client_cert_path(self): return eipspecs.client_cert_path(domain=self.domain) @@ -327,6 +334,9 @@ class ProviderCertChecker(object): with open(to, 'w') as cert_f: cert_f.write(pemfile_content) + def set_api_domain(self, domain): + self.apidomain = domain + class EIPConfigChecker(object): """ @@ -346,10 +356,15 @@ class EIPConfigChecker(object): # if not domain, get from config self.domain = domain + self.apidomain = None + self.cacert = eipspecs.provider_ca_path(domain) - self.eipconfig = eipconfig.EIPConfig(domain=domain) self.defaultprovider = providers.LeapProviderDefinition(domain=domain) + self.defaultprovider.load() + self.eipconfig = eipconfig.EIPConfig(domain=domain) + self.set_api_domain() self.eipserviceconfig = eipconfig.EIPServiceConfig(domain=domain) + self.eipserviceconfig.load() def run_all(self, checker=None, skip_download=False): """ @@ -433,31 +448,35 @@ class EIPConfigChecker(object): domain = config.get('provider', None) uri = self._get_provider_definition_uri(domain=domain) - # FIXME! Pass ca path verify!!! - # BUG #638 - # FIXME FIXME FIXME self.defaultprovider.load( from_uri=uri, fetcher=self.fetcher) - #verify=False) self.defaultprovider.save() def fetch_eip_service_config(self, skip_download=False, force_download=False, - config=None, uri=None, domain=None): + config=None, uri=None, # domain=None, + autocacert=True): if skip_download: return True if config is None: + self.eipserviceconfig.load() config = self.eipserviceconfig.config if uri is None: - if not domain: - domain = self.domain or config.get('provider', None) - uri = self._get_eip_service_uri(domain=domain) + #XXX + #if not domain: + #domain = self.domain or config.get('provider', None) + uri = self._get_eip_service_uri( + domain=self.apidomain) + + if autocacert and self.cacert is not None: + verify = self.cacert self.eipserviceconfig.load( from_uri=uri, fetcher=self.fetcher, - force_download=force_download) + force_download=force_download, + verify=verify) self.eipserviceconfig.save() def check_complete_eip_config(self, config=None): @@ -465,7 +484,6 @@ class EIPConfigChecker(object): if config is None: config = self.eipconfig.config try: - 'trying assertions' assert 'provider' in config assert config['provider'] is not None # XXX assert there is gateway !! @@ -504,3 +522,16 @@ class EIPConfigChecker(object): uri = "https://%s/%s" % (domain, path) logger.debug('getting eip service file from %s', uri) return uri + + def set_api_domain(self): + """sets api domain from defaultprovider config object""" + api = self.defaultprovider.config.get('api_uri', None) + # the caller is responsible for having loaded the config + # object at this point + if api: + api_dom = get_https_domain_and_port(api) + self.apidomain = "%s:%s" % api_dom + + def get_api_domain(self): + """gets api domain""" + return self.apidomain -- cgit v1.2.3 From ade0eded09176fd687d1ee30724468c048d15065 Mon Sep 17 00:00:00 2001 From: kali Date: Fri, 11 Jan 2013 09:16:49 +0900 Subject: fix for missing cacert bundle frozen app cannot find requests cacert bundle. added to Resources to get us going. --- src/leap/eip/checks.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index bd158e1e..cc395bcb 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -3,6 +3,7 @@ import logging #import platform import time import os +import sys import gnutls.crypto #import netifaces @@ -20,6 +21,7 @@ from leap.eip import config as eipconfig from leap.eip import constants as eipconstants from leap.eip import exceptions as eipexceptions from leap.eip import specs as eipspecs +from leap.util.certs import get_mac_cabundle from leap.util.fileutil import mkdir_p from leap.util.web import get_https_domain_and_port from leap.util.misc import null_check @@ -165,13 +167,15 @@ class ProviderCertChecker(object): if autocacert and verify is True and self.cacert is not None: logger.debug('verify cert: %s', self.cacert) verify = self.cacert + if sys.platform == "darwin": + verify = get_mac_cabundle() logger.debug('checking https connection') logger.debug('uri: %s (verify:%s)', uri, verify) + try: self.fetcher.get(uri, verify=verify) - except requests.exceptions.SSLError: # as exc: - logger.error("SSLError") + except requests.exceptions.SSLError as exc: raise eipexceptions.HttpsBadCertError except requests.exceptions.ConnectionError: @@ -448,9 +452,15 @@ class EIPConfigChecker(object): domain = config.get('provider', None) uri = self._get_provider_definition_uri(domain=domain) + if sys.platform == "darwin": + verify = get_mac_cabundle() + else: + verify = True + self.defaultprovider.load( from_uri=uri, - fetcher=self.fetcher) + fetcher=self.fetcher, + verify=verify) self.defaultprovider.save() def fetch_eip_service_config(self, skip_download=False, -- cgit v1.2.3 From 348eb0852d6f1b8b2b72baba8a236bc30a6f2a4e Mon Sep 17 00:00:00 2001 From: antialias Date: Fri, 16 Nov 2012 17:38:46 -0800 Subject: reads and searches for strings from openvpn logs via the management interface. --- src/leap/eip/openvpnconnection.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/leap/eip') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index b36b0b16..233b9da3 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -400,3 +400,7 @@ to be triggered for each one of them. if process.name == "openvpn": return process return None + + def get_log(self, lines=1): + log = self._send_command("log %s" % lines) + return log -- cgit v1.2.3 From bf39c45eddc62733fdb72b4f46cdb81ec649cb30 Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 16 Jan 2013 00:58:22 +0900 Subject: handle loss of tun iface trigger only one dialog and disconnect. additional cleanup of log handling. --- src/leap/eip/eipconnection.py | 6 +++++- src/leap/eip/openvpnconnection.py | 26 +++++++++++++++----------- 2 files changed, 20 insertions(+), 12 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/eipconnection.py b/src/leap/eip/eipconnection.py index 540e7558..20b45e36 100644 --- a/src/leap/eip/eipconnection.py +++ b/src/leap/eip/eipconnection.py @@ -27,6 +27,8 @@ class StatusMixIn(object): # Should separate EIPConnectionStatus (self.status) # from the OpenVPN state/status command and parsing. + ERR_CONNREFUSED = False + def connection_state(self): """ returns the current connection state @@ -49,7 +51,9 @@ class StatusMixIn(object): state = self.get_connection_state() except eip_exceptions.ConnectionRefusedError: # connection refused. might be not ready yet. - logger.warning('connection refused') + if not self.ERR_CONNREFUSED: + logger.warning('connection refused') + self.ERR_CONNREFUSED = True return if not state: #logger.debug('no state') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index 233b9da3..a36d99de 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -92,14 +92,17 @@ class OpenVPNManagement(object): logger.error('socket error') self._close_management_socket(announce=False) return [] - buf = self.tn.read_until(b"END", 2) - self._seek_to_eof() - blist = buf.split('\r\n') - if blist[-1].startswith('END'): - del blist[-1] - return blist - else: - return [] + try: + buf = self.tn.read_until(b"END", 2) + self._seek_to_eof() + blist = buf.split('\r\n') + if blist[-1].startswith('END'): + del blist[-1] + return blist + else: + return [] + except socket.error as exc: + logger.debug('socket error: %s' % exc.message) def _send_short_command(self, cmd): """ @@ -329,12 +332,12 @@ to be triggered for each one of them. #use _only_ signal_maps instead logger.debug('_launch_openvpn called') - logger.debug('watcher_cb: %s' % self.watcher_cb) if self.watcher_cb is not None: linewrite_callback = self.watcher_cb else: #XXX get logger instead - linewrite_callback = lambda line: logger.debug('watcher: %s' % line) + linewrite_callback = lambda line: logger.debug( + 'watcher: %s' % line) # the partial is not # being applied now because we're not observing the process @@ -342,7 +345,8 @@ to be triggered for each one of them. # here since it will be handy for observing patterns in the # thru-the-manager updates (with regex) observers = (linewrite_callback, - partial(lambda con_status, line: linewrite_callback, self.status)) + partial(lambda con_status, + line: linewrite_callback, self.status)) subp, watcher = spawn_and_watch_process( self.command, self.args, -- cgit v1.2.3 From d72b5d9057bcea884c2e828f5e3045920d4c2205 Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 16 Jan 2013 23:31:49 +0900 Subject: pass cacert bundle only in frozen apps --- src/leap/eip/checks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index cc395bcb..9fb13c74 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -167,7 +167,7 @@ class ProviderCertChecker(object): if autocacert and verify is True and self.cacert is not None: logger.debug('verify cert: %s', self.cacert) verify = self.cacert - if sys.platform == "darwin": + if sys.platform == "darwin": verify = get_mac_cabundle() logger.debug('checking https connection') logger.debug('uri: %s (verify:%s)', uri, verify) -- cgit v1.2.3 From d6c8cb0f12e8924820c296a8114a7899f61e5180 Mon Sep 17 00:00:00 2001 From: kali Date: Thu, 17 Jan 2013 05:54:16 +0900 Subject: (osx) detect which interface is traffic going thru --- src/leap/eip/checks.py | 1 - src/leap/eip/config.py | 2 +- src/leap/eip/openvpnconnection.py | 3 +++ 3 files changed, 4 insertions(+), 2 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index 9fb13c74..0d07ef08 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -187,7 +187,6 @@ class ProviderCertChecker(object): def check_new_cert_needed(self, skip_download=False, verify=True): # XXX add autocacert - logger.debug('is new cert needed?') if not self.is_cert_valid(do_raise=False): logger.debug('cert needed: true') self.download_new_client_cert( diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py index 6a19633d..a60d7ed5 100644 --- a/src/leap/eip/config.py +++ b/src/leap/eip/config.py @@ -253,7 +253,7 @@ def build_ovpn_options(daemon=False, socket_path=None, **kwargs): #if daemon is True: #opts.append('--daemon') - logger.debug('vpn options: %s', opts) + logger.debug('vpn options: %s', ' '.join(opts)) return opts diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index a36d99de..e5169465 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -6,6 +6,7 @@ import logging import os import psutil import shutil +import select import socket from functools import partial @@ -103,6 +104,8 @@ class OpenVPNManagement(object): return [] except socket.error as exc: logger.debug('socket error: %s' % exc.message) + except select.error as exc: + logger.debug('select error: %s' % exc.message) def _send_short_command(self, cmd): """ -- cgit v1.2.3 From 6e9c63f47b98fbfcd3a5104fbfa5cc9d9ffe5143 Mon Sep 17 00:00:00 2001 From: kali Date: Thu, 17 Jan 2013 07:31:59 +0900 Subject: osx fixed already running instance check --- src/leap/eip/exceptions.py | 4 ++-- src/leap/eip/openvpnconnection.py | 47 +++++++++++++++++++-------------------- 2 files changed, 25 insertions(+), 26 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/exceptions.py b/src/leap/eip/exceptions.py index c127a58f..b7d398c3 100644 --- a/src/leap/eip/exceptions.py +++ b/src/leap/eip/exceptions.py @@ -106,11 +106,11 @@ class LeapBadConfigFetchedError(Warning): "an error occurred during configuratio of leap services") -class OpenVPNAlreadyRunning(EIPClientError): +class OpenVPNAlreadyRunning(CriticalError): message = "Another OpenVPN Process is already running." usermessage = translate( "EIPErrors", - "Another OpenVPN Process has been detected." + "Another OpenVPN Process has been detected. " "Please close it before starting leap-client") diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index e5169465..05979ff7 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -2,18 +2,21 @@ OpenVPN Connection """ from __future__ import (print_function) +from functools import partial import logging import os import psutil import shutil import select import socket -from functools import partial +from time import sleep logger = logging.getLogger(name=__name__) from leap.base.connection import Connection +from leap.base.constants import OPENVPN_BIN from leap.util.coroutines import spawn_and_watch_process +from leap.util.misc import get_openvpn_pids from leap.eip.udstelnet import UDSTelnet from leap.eip import config as eip_config @@ -277,23 +280,20 @@ to be triggered for each one of them. # checks + def _check_if_running_instance(self): """ check if openvpn is already running """ - try: - #FIXME this gives DeprecationWarning - for process in psutil.get_process_list(): - if process.name == "openvpn": - logger.debug('an openvpn instance is already running.') - logger.debug('attempting to stop openvpn instance.') - if not self._stop_openvpn(): - raise eip_exceptions.OpenVPNAlreadyRunning - - except psutil.error.NoSuchProcess: - logger.debug('detected a process which died. passing.') - - logger.debug('no openvpn instance found.') + openvpn_pids = get_openvpn_pids() + if openvpn_pids: + logger.debug('an openvpn instance is already running.') + logger.debug('attempting to stop openvpn instance.') + if not self._stop_openvpn(): + raise eip_exceptions.OpenVPNAlreadyRunning + return + else: + logger.debug('no openvpn instance found.') def _set_ovpn_command(self): try: @@ -334,7 +334,7 @@ to be triggered for each one of them. #deprecate watcher_cb, #use _only_ signal_maps instead - logger.debug('_launch_openvpn called') + #logger.debug('_launch_openvpn called') if self.watcher_cb is not None: linewrite_callback = self.watcher_cb else: @@ -364,23 +364,24 @@ to be triggered for each one of them. interface """ # XXX method a bit too long, split - logger.debug("terminating openvpn process...") + logger.debug("atempting to terminate openvpn process...") if self.connected(): try: self._send_command("signal SIGTERM\n") + sleep(1) + if not self.subp: # XXX ??? + return True except socket.error: logger.warning('management socket died') return - if self.subp: - # ??? - return True #shutting openvpn failured #try patching in old openvpn host and trying again + # XXX could be more than one! process = self._get_openvpn_process() if process: - logger.debug('process :%s' % process) + logger.debug('process: %s' % process.name) cmdline = process.cmdline manag_flag = "--management" @@ -401,10 +402,8 @@ to be triggered for each one of them. return True def _get_openvpn_process(self): - # plist = [p for p in psutil.get_process_list() if p.name == "openvpn"] - # return plist[0] if plist else None - for process in psutil.get_process_list(): - if process.name == "openvpn": + for process in psutil.process_iter(): + if OPENVPN_BIN in process.name: return process return None -- cgit v1.2.3 From 8226d6032b6db0c15ff70e377f87f4acfdd21787 Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 23 Jan 2013 07:02:58 +0900 Subject: working up/down resolv-conf script --- src/leap/eip/config.py | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py index a60d7ed5..917871da 100644 --- a/src/leap/eip/config.py +++ b/src/leap/eip/config.py @@ -130,6 +130,22 @@ def get_cipher_options(eipserviceconfig=None): opts.append('%s' % _val) return opts +LINUX_UP_DOWN_SCRIPT = "/etc/leap/resolv-update" +OPENVPN_DOWN_ROOT = "/usr/lib/openvpn/openvpn-down-root.so" + + +def has_updown_scripts(): + """ + checks the existence of the up/down scripts + """ + # XXX should check permissions too + is_file = os.path.isfile(LINUX_UP_DOWN_SCRIPT) + if not is_file: + logger.warning( + "Could not find up/down scripts at %s! " + "Risk of DNS Leaks!!!") + return is_file + def build_ovpn_options(daemon=False, socket_path=None, **kwargs): """ @@ -230,10 +246,14 @@ def build_ovpn_options(daemon=False, socket_path=None, **kwargs): opts.append('2') if _platform == "Linux": - opts.append("--up") - opts.append("/etc/openvpn/update-resolv-conf") - opts.append("--down") - opts.append("/etc/openvpn/update-resolv-conf") + if has_updown_scripts(): + opts.append("--up") + opts.append(LINUX_UP_DOWN_SCRIPT) + opts.append("--down") + opts.append(LINUX_UP_DOWN_SCRIPT) + opts.append("--plugin") + opts.append(OPENVPN_DOWN_ROOT) + opts.append("'script_type=down %s'" % LINUX_UP_DOWN_SCRIPT) # certs client_cert_path = eipspecs.client_cert_path(provider) -- cgit v1.2.3 From 407b030bb7d27b797fb27254710a358c9c69f8be Mon Sep 17 00:00:00 2001 From: kali Date: Thu, 24 Jan 2013 01:57:28 +0900 Subject: catch missing messages on last page of wizard --- src/leap/eip/eipconnection.py | 2 +- src/leap/eip/openvpnconnection.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/eipconnection.py b/src/leap/eip/eipconnection.py index 20b45e36..d012c567 100644 --- a/src/leap/eip/eipconnection.py +++ b/src/leap/eip/eipconnection.py @@ -177,7 +177,7 @@ class EIPConnection(OpenVPNConnection, StatusMixIn): super(EIPConnection, self).__init__(*args, **kwargs) - def connect(self): + def connect(self, **kwargs): """ entry point for connection process """ diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index 05979ff7..4953db11 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -280,7 +280,6 @@ to be triggered for each one of them. # checks - def _check_if_running_instance(self): """ check if openvpn is already running -- cgit v1.2.3 From 9cdc193c587631986e579c1ba37a8b982be01238 Mon Sep 17 00:00:00 2001 From: kali Date: Thu, 24 Jan 2013 18:47:41 +0900 Subject: all tests green again plus: * added soledad test requirements * removed soledad from run_tests run (+1K tests failing) * added option to run All tests to run_tests script * pep8 cleanup --- src/leap/eip/checks.py | 4 ++-- src/leap/eip/openvpnconnection.py | 3 +-- src/leap/eip/tests/test_config.py | 14 ++++++++++++++ src/leap/eip/tests/test_openvpnconnection.py | 3 ++- 4 files changed, 19 insertions(+), 5 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index 0d07ef08..de738de6 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -451,7 +451,7 @@ class EIPConfigChecker(object): domain = config.get('provider', None) uri = self._get_provider_definition_uri(domain=domain) - if sys.platform == "darwin": + if sys.platform == "darwin": verify = get_mac_cabundle() else: verify = True @@ -465,7 +465,7 @@ class EIPConfigChecker(object): def fetch_eip_service_config(self, skip_download=False, force_download=False, config=None, uri=None, # domain=None, - autocacert=True): + autocacert=True, verify=True): if skip_download: return True if config is None: diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index 4953db11..455735c8 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -107,7 +107,7 @@ class OpenVPNManagement(object): return [] except socket.error as exc: logger.debug('socket error: %s' % exc.message) - except select.error as exc: + except select.error as exc: logger.debug('select error: %s' % exc.message) def _send_short_command(self, cmd): @@ -374,7 +374,6 @@ to be triggered for each one of them. logger.warning('management socket died') return - #shutting openvpn failured #try patching in old openvpn host and trying again # XXX could be more than one! diff --git a/src/leap/eip/tests/test_config.py b/src/leap/eip/tests/test_config.py index 5977ef3c..05e78de4 100644 --- a/src/leap/eip/tests/test_config.py +++ b/src/leap/eip/tests/test_config.py @@ -28,6 +28,8 @@ class EIPConfigTest(BaseLeapTest): __name__ = "eip_config_tests" provider = "testprovider.example.org" + maxDiff = None + def setUp(self): pass @@ -130,6 +132,18 @@ class EIPConfigTest(BaseLeapTest): args.append('/tmp/test.socket') args.append('unix') + args.append('--script-security') + args.append('2') + + if _system == "Linux": + args.append('--up') + args.append('/etc/leap/resolv-update') + args.append('--down') + args.append('/etc/leap/resolv-update') + args.append('--plugin') + args.append('/usr/lib/openvpn/openvpn-down-root.so') + args.append("'script_type=down /etc/leap/resolv-update'") + # certs # XXX get values from specs? args.append('--cert') diff --git a/src/leap/eip/tests/test_openvpnconnection.py b/src/leap/eip/tests/test_openvpnconnection.py index f7493567..95bfb2f0 100644 --- a/src/leap/eip/tests/test_openvpnconnection.py +++ b/src/leap/eip/tests/test_openvpnconnection.py @@ -91,9 +91,10 @@ class OpenVPNConnectionTest(BaseLeapTest): # while fixing. kali. openvpn_connection = openvpnconnection.OpenVPNConnection() - with patch.object(psutil, "get_process_list") as mocked_psutil: + with patch.object(psutil, "process_iter") as mocked_psutil: mocked_process = Mock() mocked_process.name = "openvpn" + mocked_process.cmdline = ["openvpn", "-foo", "-bar", "-gaaz"] mocked_psutil.return_value = [mocked_process] with self.assertRaises(eipexceptions.OpenVPNAlreadyRunning): openvpn_connection._check_if_running_instance() -- cgit v1.2.3 From 19da34c598ce6db172c1e1a8978bf031fc6db89b Mon Sep 17 00:00:00 2001 From: kali Date: Thu, 24 Jan 2013 20:07:06 +0900 Subject: check cert time_boundaries uses pyOpenSSL I had missed this one while deprecating gnutls --- src/leap/eip/checks.py | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index de738de6..9a34a428 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -1,13 +1,8 @@ import logging -#import ssl -#import platform import time import os import sys -import gnutls.crypto -#import netifaces -#import ping import requests from leap import __branding as BRANDING @@ -24,7 +19,6 @@ from leap.eip import specs as eipspecs from leap.util.certs import get_mac_cabundle from leap.util.fileutil import mkdir_p from leap.util.web import get_https_domain_and_port -from leap.util.misc import null_check logger = logging.getLogger(name=__name__) @@ -276,11 +270,8 @@ class ProviderCertChecker(object): def is_cert_not_expired(self, certfile=None, now=time.gmtime): if certfile is None: certfile = self._get_client_cert_path() - with open(certfile) as cf: - cert_s = cf.read() - cert = gnutls.crypto.X509Certificate(cert_s) - from_ = time.gmtime(cert.activation_time) - to_ = time.gmtime(cert.expiration_time) + from_, to_ = certs.get_time_boundaries(certfile) + return from_ < now() < to_ def is_valid_pemfile(self, cert_s=None): -- cgit v1.2.3 From 05b407b6c74b939a02c3d97ffe4a92faf0325284 Mon Sep 17 00:00:00 2001 From: kali Date: Fri, 25 Jan 2013 02:36:08 +0900 Subject: fix test when missing system updown script --- src/leap/eip/tests/test_config.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/tests/test_config.py b/src/leap/eip/tests/test_config.py index 05e78de4..72ab3c8e 100644 --- a/src/leap/eip/tests/test_config.py +++ b/src/leap/eip/tests/test_config.py @@ -136,13 +136,15 @@ class EIPConfigTest(BaseLeapTest): args.append('2') if _system == "Linux": - args.append('--up') - args.append('/etc/leap/resolv-update') - args.append('--down') - args.append('/etc/leap/resolv-update') - args.append('--plugin') - args.append('/usr/lib/openvpn/openvpn-down-root.so') - args.append("'script_type=down /etc/leap/resolv-update'") + UPDOWN_SCRIPT = "/etc/leap/resolv-update" + if os.path.isfile(UPDOWN_SCRIPT): + args.append('--up') + args.append('/etc/leap/resolv-update') + args.append('--down') + args.append('/etc/leap/resolv-update') + args.append('--plugin') + args.append('/usr/lib/openvpn/openvpn-down-root.so') + args.append("'script_type=down /etc/leap/resolv-update'") # certs # XXX get values from specs? -- cgit v1.2.3 From 1e9ba29d0f1e12b95536099e29396a1f35908381 Mon Sep 17 00:00:00 2001 From: kali Date: Thu, 31 Jan 2013 05:47:45 +0900 Subject: pep8 --- src/leap/eip/openvpnconnection.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index 455735c8..bee8c010 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -339,7 +339,7 @@ to be triggered for each one of them. else: #XXX get logger instead linewrite_callback = lambda line: logger.debug( - 'watcher: %s' % line) + 'watcher: %s' % line) # the partial is not # being applied now because we're not observing the process -- cgit v1.2.3 From da8a8ac4ebc62f7549d2927c41472561541abfa2 Mon Sep 17 00:00:00 2001 From: kali Date: Thu, 31 Jan 2013 09:09:54 +0900 Subject: hide jsonschema exception in tests --- src/leap/eip/tests/test_checks.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/tests/test_checks.py b/src/leap/eip/tests/test_checks.py index ab11037a..f42a0eeb 100644 --- a/src/leap/eip/tests/test_checks.py +++ b/src/leap/eip/tests/test_checks.py @@ -11,11 +11,10 @@ import urlparse from mock import (patch, Mock) -import jsonschema -#import ping import requests from leap.base import config as baseconfig +from leap.base import pluggableconfig from leap.base.constants import (DEFAULT_PROVIDER_DEFINITION, DEFINITION_EXPECTED_PATH) from leap.eip import checks as eipchecks @@ -125,7 +124,7 @@ class EIPCheckTest(BaseLeapTest): #with self.assertRaises(eipexceptions.EIPMissingDefaultProvider): # XXX we should catch this as one of our errors, but do not # see how to do it quickly. - with self.assertRaises(jsonschema.ValidationError): + with self.assertRaises(pluggableconfig.ValidationError): #import ipdb;ipdb.set_trace() checker.eipconfig.load(fromfile=eipcfg_path) checker.check_is_there_default_provider() -- cgit v1.2.3 From b1f4715334a1b6559ce35dbfe40cd1e1ad91cf7f Mon Sep 17 00:00:00 2001 From: kali Date: Tue, 12 Feb 2013 08:40:40 +0900 Subject: add debug info --- src/leap/eip/checks.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index 9a34a428..af824c57 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -120,7 +120,12 @@ class ProviderCertChecker(object): def verify_api_https(self, uri): assert uri.startswith('https://') cacert = self.ca_cert_path - verify = cacert and cacert or True + verify = cacert or True + + # DEBUG + logger.debug('uri -> %s' % uri) + logger.debug('cacertpath -> %s' % cacert) + req = self.fetcher.get(uri, verify=verify) req.raise_for_status() return True -- cgit v1.2.3