summaryrefslogtreecommitdiff
path: root/src/leap/bitmask/vpn
diff options
context:
space:
mode:
authorKali Kaneko <kali@leap.se>2018-01-10 12:31:17 +0100
committerKali Kaneko <kali@leap.se>2018-01-25 01:19:10 +0100
commit5ffa0c1710ce4038b94a026a58daf8f12aef2ec4 (patch)
tree61723ea8c2385b2930ed82f401f52d6239ecc221 /src/leap/bitmask/vpn
parent5510c24e61046269e5b29df7d7ffb67a42bdc763 (diff)
[feat] support anonymous vpn
honor the anonymous certificate for the providers that offer it. this still needs a change in bonafide, in which if provider supports anonymous access we still have to download eip-service.json for testing, I assume this has been already manually downloaded.
Diffstat (limited to 'src/leap/bitmask/vpn')
-rw-r--r--src/leap/bitmask/vpn/_checks.py11
-rw-r--r--src/leap/bitmask/vpn/service.py97
2 files changed, 80 insertions, 28 deletions
diff --git a/src/leap/bitmask/vpn/_checks.py b/src/leap/bitmask/vpn/_checks.py
index c8f7dd36..9586d096 100644
--- a/src/leap/bitmask/vpn/_checks.py
+++ b/src/leap/bitmask/vpn/_checks.py
@@ -17,14 +17,19 @@ class ImproperlyConfigured(Exception):
pass
-def is_service_ready(provider):
+def get_failure_for(provider):
if not _has_valid_cert(provider):
raise ImproperlyConfigured('Missing VPN certificate')
-
if IS_LINUX and not is_pkexec_in_system():
- log.warn('System has no pkexec')
raise NoPkexecAvailable()
+
+def is_service_ready(provider):
+ if not _has_valid_cert(provider):
+ return False
+ if IS_LINUX and not is_pkexec_in_system():
+ log.warn('System has no pkexec')
+ return False
return True
diff --git a/src/leap/bitmask/vpn/service.py b/src/leap/bitmask/vpn/service.py
index 922cfaea..609d70ee 100644
--- a/src/leap/bitmask/vpn/service.py
+++ b/src/leap/bitmask/vpn/service.py
@@ -1,7 +1,7 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# service.py
-# Copyright (C) 2015-2017 LEAP
+# Copyright (C) 2015-2018 LEAP
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -35,6 +35,7 @@ from leap.bitmask.vpn._checks import (
get_vpn_cert_path,
cert_expires
)
+
from leap.bitmask.vpn import privilege, helpers
from leap.common.config import get_path_prefix
from leap.common.files import check_and_fix_urw_only
@@ -79,13 +80,22 @@ class VPNService(HookableService):
except ValueError:
self._loc = []
+ _autostart = self._cfg.get('autostart', False)
+ self._autostart = _autostart
+
+ _anonymous = self._cfg.get('anonymous', True)
+ self._anonymous_enabled = _anonymous
+
if helpers.check() and self._firewall.is_up():
self._firewall.stop()
def startService(self):
- # TODO this could trigger a check for validity of the certificates,
- # etc.
+ # TODO trigger a check for validity of the certificates,
+ # and schedule a re-download if needed.
+ # TODO start a watchDog service (to push status events)
super(VPNService, self).startService()
+ if self._autostart:
+ self.start_vpn()
def stopService(self):
try:
@@ -100,18 +110,21 @@ class VPNService(HookableService):
exc = Exception('VPN already started')
exc.expected = True
raise exc
- if not domain:
+ if domain is None:
domain = self._read_last()
- if not domain:
+ if domain is None:
exc = Exception("VPN can't start, a provider is needed")
exc.expected = True
raise exc
+
+ yield self._setup(domain)
if not is_service_ready(domain):
- exc = Exception("VPN is not ready")
+ exc = Exception('VPN is not ready')
exc.expected = True
raise exc
- yield self._setup(domain)
+ # XXX we can signal status to frontend, use
+ # get_failure_for(provider) -- no polkit, etc.
fw_ok = self._firewall.start()
if not fw_ok:
@@ -177,7 +190,7 @@ class VPNService(HookableService):
return ret
@defer.inlineCallbacks
- def do_get_cert(self, username):
+ def do_get_cert(self, username, anonymous=False):
try:
_, provider = username.split('@')
except ValueError:
@@ -188,7 +201,8 @@ class VPNService(HookableService):
# fetch vpn cert and store
bonafide = self.parent.getServiceNamed("bonafide")
- _, cert_str = yield bonafide.do_get_vpn_cert(username)
+ _, cert_str = yield bonafide.do_get_vpn_cert(
+ username, anonymous=anonymous)
cert_path = get_vpn_cert_path(provider)
cert_dir = os.path.dirname(cert_path)
@@ -242,32 +256,29 @@ class VPNService(HookableService):
return self._cco
@defer.inlineCallbacks
- def _setup(self, provider):
+ def _setup(self, provider_id):
"""Set up ConfiguredTunnel for a specified provider.
:param provider: the provider to use, e.g. 'demo.bitmask.net'
:type provider: str"""
bonafide = self.parent.getServiceNamed('bonafide')
- config = yield bonafide.do_provider_read(provider, 'eip')
- sorted_gateways = GatewaySelector(
- config.gateways, config.locations,
- preferred={'cc': self._cco, 'loc': self._loc}
- ).select_gateways()
+ # bootstrap if not yet done
+ if provider_id not in bonafide.do_provider_list(seeded=False):
+ yield bonafide.do_provider_create(provider_id)
- extra_flags = config.openvpn_configuration
+ provider = yield bonafide.do_provider_read(provider_id)
+ config = yield bonafide.do_provider_read(provider_id, 'eip')
- prefix = os.path.join(
- self._basepath, "leap", "providers", provider, "keys")
- cert_path = key_path = os.path.join(prefix, "client", "openvpn.pem")
- ca_path = os.path.join(prefix, "ca", "cacert.pem")
+ sorted_gateways = self._get_gateways(config)
+ extra_flags = config.openvpn_configuration
+ cert_path, ca_path = self._get_cert_paths(provider_id)
+ key_path = cert_path
+ anonvpn = self._has_anonvpn(provider)
if not os.path.isfile(cert_path):
- gotcert = yield self.do_get_cert('ignored@%s' % provider)
- if gotcert['get_cert'] != 'ok':
- raise ImproperlyConfigured(
- 'Cannot find client certificate. Please get one')
+ yield self._maybe_get_anon_cert(anonvpn, provider_id)
if not os.path.isfile(ca_path):
raise ImproperlyConfigured(
@@ -277,9 +288,45 @@ class VPNService(HookableService):
# TODO add remote ports, according to preferred sequence
remotes = tuple([(ip, '443') for ip in sorted_gateways])
self._tunnel = ConfiguredTunnel(
- provider, remotes, cert_path, key_path, ca_path, extra_flags)
+ provider_id, remotes, cert_path, key_path, ca_path, extra_flags)
self._firewall = FirewallManager(remotes)
+ def _get_gateways(self, config):
+ return GatewaySelector(
+ config.gateways, config.locations,
+ preferred={'cc': self._cco, 'loc': self._loc}
+ ).select_gateways()
+
+ def _get_cert_paths(self, provider_id):
+ prefix = os.path.join(
+ self._basepath, "leap", "providers", provider_id, "keys")
+ cert_path = os.path.join(prefix, "client", "openvpn.pem")
+ ca_path = os.path.join(prefix, "ca", "cacert.pem")
+ return cert_path, ca_path
+
+ def _has_anonvpn(self, provider):
+ try:
+ allows_anonymous = provider.get('service').get('allow_anonymous')
+ except (ValueError,):
+ allows_anonymous = False
+ return self._anonymous_enabled and allows_anonymous
+
+ @defer.inlineCallbacks
+ def _maybe_get_anon_cert(self, anonvpn, provider_id):
+ if anonvpn:
+ self.log.debug('Getting anon VPN cert')
+ gotcert = yield self.do_get_cert(
+ 'anonymous@%s' % provider_id, anonymous=True)
+ if gotcert['get_cert'] != 'ok':
+ raise ImproperlyConfigured(
+ '(anon) Could not get client certificate. '
+ 'Please get one')
+ else:
+ # this should instruct get_cert to get a session from bonafide
+ # if we are authenticated.
+ raise ImproperlyConfigured(
+ 'Cannot find client certificate. Please get one')
+
def _write_last(self, domain):
path = os.path.join(self._basepath, self._last_vpn_path)
with open(path, 'w') as f: