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 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)
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