Esempio n. 1
0
    def read_config(self, *args, **kwargs):
        try:
            serialized = self.core.config_manager.get(self.MASTER_KEY)
        except (ConfigError, KeyError):
            self.warning('Openvpn not configured, default values loaded.')
            self.openvpn_cfg = OpenVpnConf()
            return

        try:
            self.openvpn_cfg = OpenVpnConf.deserialize(serialized)
        except DatastructureIncompatible:
            self.openvpn_cfg = OpenVpnConf.deserialize(serialized)
Esempio n. 2
0
 def service_setOpenVpnConfig(self, context, serialized, message):
     if not _acceptclient(context):
         raise OpenVpnError(OPENVPN_CLIENT_TOO_OLD, tr('Impossible to '
             'configure openvpn with this frontend version; '
             'please upgrade'))
     openvpn_cfg = OpenVpnConf.deserialize(serialized)
     is_valid, msg = openvpn_cfg.isValidWithMsg()
     if is_valid:
         self.openvpn_cfg = openvpn_cfg
         self.save_config(message, context=context, action=CONFIG_MODIFICATION)
     else:
         raise OpenVpnError(OPENVPN_INVALID_CONFIGURATION, msg)
Esempio n. 3
0
class OpenvpnComponent(ConfigServiceComponent, UseCertificateComponent):
    """
    Manage a Openvpn server
    """
    NAME = "openvpn"
    MASTER_KEY = NAME
    VERSION = "1.0"

    ACLS = {
        'resolv': set(('getResolvConfig',)),
        'network': set(('getNetconfig',)),
        'nupki': set(('copyPKI', 'copyCRL', )),
        'config': set(('apply', 'reset', )),
    }

    REQUIRES = ('config', 'network')
    CONFIG_DEPENDS = frozenset(('network', 'resolv'))

    ROLES = {
        'conf_read': set(('getClientConfig', 'getOpenVpnConfig',
                            'runtimeFiles', 'getCertificatesInfo')),
        'conf_write': set(('setOpenVpnConfig',)),
        'dpi_read': set(('getOpenVpnConfig',)),
    }

    PIDFILE = "/var/run/openvpn.server.pid"
    #<feth>I'd rather not use the exe name because it is not specific of a client or a particular server.
    #(many openvpn instances can coexist)
    #EXE_NAME="openvpn"

    INIT_SCRIPT = "openvpn"
    INITRANK_S = 16
    INITRANK_K = 80

    check_vpn_port = ConfigServiceComponent.check_port
    check_vpn_address = ConfigServiceComponent.check_ip
    # TODO check the vpn_netmask

    OPENVPN_BASE = '/etc/openvpn'
    CERT_PATH = join(OPENVPN_BASE, 'server.crt')
    KEY_PATH = join(OPENVPN_BASE, 'server.key')
    CRL_PATH = join(OPENVPN_BASE, 'server.crl')
    CA_PATH = join(OPENVPN_BASE, 'ca.crt')

    CLIENT_CONF = join(OPENVPN_BASE, 'client.ovpn')
    SERVER_CONF = join(OPENVPN_BASE, 'server.conf')
    CONF_FILES = CLIENT_CONF, SERVER_CONF

    #override UseCertificateComponent value
    CERT_OWNER_AND_GROUP = "openvpn", "openvpn"

    #apply_config is inherited

    def __init__(self):
        ConfigServiceComponent.__init__(self)
        UseCertificateComponent.__init__(self)
        self.openvpn_cfg = self.context = self.core = None

    def init(self, core):
        UseCertificateComponent.init(self, core)
        self.context = Context.fromComponent(self)
        for filename in self.CONF_FILES:
            self.addConfFile(filename, 'root:root', '0644')
        ConfigServiceComponent.init(self, core)

    @inlineCallbacks
    def init_done(self):
        config_version = self.openvpn_cfg.getReceivedSerialVersion()
        if config_version < 4:
            if not self.openvpn_cfg.manual_pushed_routes and self.should_run(None):
                self.critical(
                    "A configuration with version %d was read, and no pushed "
                    "routes were defined in it. Adding all routes. and saving" %
                    config_version
                    )
                if self._append_all_routes():
                    yield self.core.callService(self.context, 'config', 'reset')
                    self.save_config(
                        "openvpn : Adding pushed routes",
                        context=self.context,
                        action=CONFIG_AUTOCONFIGURATION
                        )
                    yield self.core.callService(self.context, 'config', 'apply')
                else:
                    self.critical("Could'nt find a route to add")
        else:
            self.debug("Not need to add any routes.")

    def read_config(self, *args, **kwargs):
        try:
            serialized = self.core.config_manager.get(self.MASTER_KEY)
        except (ConfigError, KeyError):
            self.warning('Openvpn not configured, default values loaded.')
            self.openvpn_cfg = OpenVpnConf()
            return

        try:
            self.openvpn_cfg = OpenVpnConf.deserialize(serialized)
        except DatastructureIncompatible:
            self.openvpn_cfg = OpenVpnConf.deserialize(serialized)

    def should_run(self, responsible):
        if not self.openvpn_cfg.enabled:
            if responsible:
                responsible.feedback(tr("Explicitely disabled."))
            return False
        if not self.openvpn_cfg.client_network:
            if responsible:
                responsible.feedback(
                    tr("No client network was defined, disabling server.")
                    )
            return False
        return True

    def _template_variables(self, responsible, resolvcfg):
        # The server will listen on 0.0.0.0.
        net, mask = split_net(
            self.openvpn_cfg.client_network
        )
        yield 'client_network', net
        yield 'netmask_long_format', mask

        #For client.ovpn.
        yield 'server_address', self.openvpn_cfg.server

        yield 'port', self.openvpn_cfg.port
        yield 'protocol', self.openvpn_cfg.protocol
        yield 'redirect', self.openvpn_cfg.redirect
        yield 'disable_crl', self.openvpn_cfg.disable_crl

        yield 'domain', resolvcfg.domain
        yield 'nameserver1', resolvcfg.nameserver1
        yield 'nameserver2', resolvcfg.nameserver2

        routes = []

        for network in self.openvpn_cfg.manual_pushed_routes:
            routes.append(_pushRoute(network))
        yield 'routes', routes

    @inlineCallbacks
    def _fetchResolv(self):
        context = Context.fromComponent(self)

        serialized_resolvcfg = yield self.core.callService(
            context, 'resolv', 'getResolvConfig'
        )

        resolvcfg = deserializeResolv(serialized_resolvcfg)
        returnValue(resolvcfg)


    @inlineCallbacks
    def genConfigFiles(self, responsible):
        #FIXME: id 'read_config' call really useful?
        self.read_config()

        if not self.should_run(responsible):
            for filename in self.CONF_FILES:
                if exists(filename):
                    unlink(filename)
            return

        resolvcfg = yield self._fetchResolv()

        template_variables = dict(
            self._template_variables(responsible, resolvcfg)
            )

        self.generate_configfile(template_variables)

        #svn commit r19729:
        #auth_cert, openvpn: don't set SSL in apply_config() in a restoration
        #To avoid nupki.copyPKI() error if the PKI doesn't exist anymore.
        if not responsible.isRestoring():
            ssl_conf = self.openvpn_cfg.getSSLDict()
            yield self._setSSLConfig(ssl_conf)

        self.setCertsOwnership()

    def save_config(self, message, context=None, action=None):
        with self.core.config_manager.begin(self, context, action=action) as cm:
            try:
                cm.delete(self.MASTER_KEY)
            except ConfigError:
                pass
            cm.set(self.MASTER_KEY, self.openvpn_cfg.serialize())
            cm.commit(message)

    def get_ports(self):
        return [{'proto': self.openvpn_cfg.protocol,
                 'port': self.openvpn_cfg.port}]

    def check_vpn_proto(self, value):
        return value in ('tcp', 'udp')

    # Services:
    def service_getOpenVpnConfig(self, context):
        serialized = self.openvpn_cfg.serialize()

        if _acceptclient(context):
            return serialized

        #sending for v3
        self.debug("Downgrading openvpn conf - will be read only for that client")
        return self.openvpn_cfg.downgradeFields(serialized, 2)

    def service_setOpenVpnConfig(self, context, serialized, message):
        if not _acceptclient(context):
            raise OpenVpnError(OPENVPN_CLIENT_TOO_OLD, tr('Impossible to '
                'configure openvpn with this frontend version; '
                'please upgrade'))
        openvpn_cfg = OpenVpnConf.deserialize(serialized)
        is_valid, msg = openvpn_cfg.isValidWithMsg()
        if is_valid:
            self.openvpn_cfg = openvpn_cfg
            self.save_config(message, context=context, action=CONFIG_MODIFICATION)
        else:
            raise OpenVpnError(OPENVPN_INVALID_CONFIGURATION, msg)

    def service_sendFile(self, context, type_, encoded_bin):
        # deprecated
        raise RpcdError(tr('Your EAS is too old to change the certificate configuration, please upgrade it.'))

    def service_copyPKI(self, context, pkiname, cname):
        # deprecated
        raise RpcdError(tr('Your EAS is too old to change the certificate configuration, please upgrade it.'))

    def service_getClientConfig(self, context):
        """
        Return a string containing a configuration for a client.
        """
        try:
            with open(self.CLIENT_CONF) as fd:
                return encodeFileContent(fd.read())
        except IOError:
            raise OpenVpnError(OPENVPN_CLIENT_CONF_UNAVAILABLE,
                tr('The client configuration for VPN client is not available. '
                    'Have you configured the VPN client service, '
                    'then saved and applied the configuration?'))

    # TODO : factorize with auth_cert, aka authentication, aka nuauth
    def service_runtimeFiles(self, context):
        cert_files = (self.CERT_PATH, self.KEY_PATH, self.CRL_PATH, self.CA_PATH)
        cert_tuples = [(cert, 'bin') for cert in cert_files]
        return {'added': cert_tuples}

    def service_runtimeFilesModified(self, context):
        #Nothing to do because the config module will reload us
        pass

    # TODO : factorize with auth_cert, aka authentication, aka nuauth
    def _getSSLConfig(self):
        return self.openvpn_cfg.getSSLDict()

    def _append_all_routes(self):
        """
        When importing an old config without any manual pushed routes
        """
        changed = False

        #fetch net config
        netconfig = self.core.config_manager.get('network')
        netcfg = deserializeNetCfg(netconfig)

        #fetch default routes and gateways
        default_routes = tuple(netcfg.iterRoutes(default_only=True))
        #default_gateways: IPy.IP
        default_gateways = tuple(route.router for route in default_routes)

        #add everything into config
        for network in netcfg.iterNetworks(include_ha=False):
            if _includeNet(network, default_gateways):
                self.openvpn_cfg.manual_pushed_routes += (network.net, )
                changed = True

        return changed