From e8a3153f09de71e5671bd4d662a51a2c39136be3 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 4 Oct 2020 19:34:16 +0200 Subject: sstp: T2960: migrate to get_config_dict() and reusable templates --- src/conf_mode/service_pppoe-server.py | 98 +--------- src/conf_mode/vpn_sstp.py | 346 ++++------------------------------ 2 files changed, 41 insertions(+), 403 deletions(-) (limited to 'src/conf_mode') diff --git a/src/conf_mode/service_pppoe-server.py b/src/conf_mode/service_pppoe-server.py index 30b382c69..a520120f8 100755 --- a/src/conf_mode/service_pppoe-server.py +++ b/src/conf_mode/service_pppoe-server.py @@ -19,13 +19,11 @@ import os from sys import exit from vyos.config import Config -from vyos.configdict import dict_merge -from vyos.validate import is_ipv4 +from vyos.configdict import get_accel_dict +from vyos.configverify import verify_accel_ppp_base_service from vyos.template import render from vyos.util import call -from vyos.util import get_half_cpus from vyos.util import vyos_dict_search -from vyos.xml import defaults from vyos import ConfigError from vyos import airbag airbag.enable() @@ -42,108 +40,22 @@ def get_config(config=None): if not conf.exists(base): return None - pppoe = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True) - # We have gathered the dict representation of the CLI, but there are default - # options which we need to update into the dictionary retrived. - default_values = defaults(base) - - # defaults include RADIUS server specifics per TAG node which need to be - # added to individual RADIUS servers instead - so we can simply delete them - if vyos_dict_search('authentication.radius.server', default_values): - del default_values['authentication']['radius']['server'] - # defaults include static-ip address per TAG node which need to be added to - # individual local users instead - so we can simply delete them - if vyos_dict_search('authentication.local_users.username', default_values): - del default_values['authentication']['local_users']['username'] - - pppoe = dict_merge(default_values, pppoe) - - # set CPUs cores to process requests - pppoe.update({'thread_count' : get_half_cpus()}) - # we need to store the path to the secrets file - pppoe.update({'chap_secrets_file' : pppoe_chap_secrets}) - - # We can only have two IPv4 and three IPv6 nameservers - also they are - # configured in a different way in the configuration, this is why we split - # the configuration - if 'name_server' in pppoe: - ns_v4 = [] - ns_v6 = [] - for ns in pppoe['name_server']: - if is_ipv4(ns): ns_v4.append(ns) - else: ns_v6.append(ns) - - pppoe.update({'name_server_ipv4' : ns_v4, 'name_server_ipv6' : ns_v6}) - del pppoe['name_server'] - - # Add individual RADIUS server default values - if vyos_dict_search('authentication.radius.server', pppoe): - default_values = defaults(base + ['authentication', 'radius', 'server']) - - for server in vyos_dict_search('authentication.radius.server', pppoe): - pppoe['authentication']['radius']['server'][server] = dict_merge( - default_values, pppoe['authentication']['radius']['server'][server]) - - # Add individual local-user default values - if vyos_dict_search('authentication.local_users.username', pppoe): - default_values = defaults(base + ['authentication', 'local_users', 'username']) - - for username in vyos_dict_search('authentication.local_users.username', pppoe): - pppoe['authentication']['local_users']['username'][username] = dict_merge( - default_values, pppoe['authentication']['local_users']['username'][username]) - + # retrieve common dictionary keys + pppoe = get_accel_dict(conf, base, pppoe_chap_secrets) return pppoe - def verify(pppoe): if not pppoe: return None - # vertify auth settings - if vyos_dict_search('authentication.mode', pppoe) == 'local': - if not vyos_dict_search('authentication.local_users', pppoe): - raise ConfigError('PPPoE local auth mode requires local users to be configured!') - - for user in vyos_dict_search('authentication.local_users.username', pppoe): - user_config = pppoe['authentication']['local_users']['username'][user] - - if 'password' not in user_config: - raise ConfigError(f'Password required for local user "{user}"') - - if 'rate_limit' in user_config: - # if up/download is set, check that both have a value - if not {'upload', 'download'} <= set(user_config['rate_limit']): - raise ConfigError(f'User "{user}" has rate-limit configured for only one ' \ - 'direction but both upload and download must be given!') - - elif vyos_dict_search('authentication.mode', pppoe) == 'radius': - if not vyos_dict_search('authentication.radius.server', pppoe): - raise ConfigError('RADIUS authentication requires at least one server') - - for server in vyos_dict_search('authentication.radius.server', pppoe): - radius_config = pppoe['authentication']['radius']['server'][server] - if 'key' not in radius_config: - raise ConfigError(f'Missing RADIUS secret key for server "{server}"') + verify_accel_ppp_base_service(pppoe) if 'wins_server' in pppoe and len(pppoe['wins_server']) > 2: raise ConfigError('Not more then two IPv4 WINS name-servers can be configured') - if 'name_server_ipv4' in pppoe: - if len(pppoe['name_server_ipv4']) > 2: - raise ConfigError('Not more then two IPv4 DNS name-servers ' \ - 'can be configured') - - if 'name_server_ipv6' in pppoe: - if len(pppoe['name_server_ipv6']) > 3: - raise ConfigError('Not more then three IPv6 DNS name-servers ' \ - 'can be configured') - if 'interface' not in pppoe: raise ConfigError('At least one listen interface must be defined!') - if 'gateway_address' not in pppoe: - raise ConfigError('PPPoE server requires gateway-address to be configured!') - # local ippool and gateway settings config checks if not (vyos_dict_search('client_ip_pool.subnet', pppoe) or (vyos_dict_search('client_ip_pool.start', pppoe) and diff --git a/src/conf_mode/vpn_sstp.py b/src/conf_mode/vpn_sstp.py index 3eece1922..2597ba42f 100755 --- a/src/conf_mode/vpn_sstp.py +++ b/src/conf_mode/vpn_sstp.py @@ -16,340 +16,66 @@ import os -from time import sleep from sys import exit -from copy import deepcopy -from stat import S_IRUSR, S_IWUSR, S_IRGRP from vyos.config import Config +from vyos.configdict import get_accel_dict +from vyos.configverify import verify_accel_ppp_base_service from vyos.template import render -from vyos.util import call, run, get_half_cpus -from vyos.validate import is_ipv4 +from vyos.util import call +from vyos.util import vyos_dict_search from vyos import ConfigError - from vyos import airbag airbag.enable() sstp_conf = '/run/accel-pppd/sstp.conf' sstp_chap_secrets = '/run/accel-pppd/sstp.chap-secrets' -default_config_data = { - 'local_users' : [], - 'auth_mode' : 'local', - 'auth_proto' : ['auth_mschap_v2'], - 'chap_secrets_file': sstp_chap_secrets, # used in Jinja2 template - 'client_ip_pool' : [], - 'client_ipv6_pool': [], - 'client_ipv6_delegate_prefix': [], - 'client_gateway': '', - 'dnsv4' : [], - 'dnsv6' : [], - 'radius_server' : [], - 'radius_acct_tmo' : '3', - 'radius_max_try' : '3', - 'radius_timeout' : '3', - 'radius_nas_id' : '', - 'radius_nas_ip' : '', - 'radius_source_address' : '', - 'radius_shaper_attr' : '', - 'radius_shaper_vendor': '', - 'radius_dynamic_author' : '', - 'ssl_ca' : '', - 'ssl_cert' : '', - 'ssl_key' : '', - 'mtu' : '', - 'ppp_mppe' : 'prefer', - 'ppp_echo_failure' : '', - 'ppp_echo_interval' : '', - 'ppp_echo_timeout' : '', - 'thread_cnt' : get_half_cpus() -} - def get_config(config=None): - sstp = deepcopy(default_config_data) - base_path = ['vpn', 'sstp'] if config: conf = config else: conf = Config() - if not conf.exists(base_path): + base = ['vpn', 'sstp'] + if not conf.exists(base): return None - conf.set_level(base_path) - - if conf.exists(['authentication', 'mode']): - sstp['auth_mode'] = conf.return_value(['authentication', 'mode']) - - # - # local auth - if conf.exists(['authentication', 'local-users']): - for username in conf.list_nodes(['authentication', 'local-users', 'username']): - user = { - 'name' : username, - 'password' : '', - 'state' : 'enabled', - 'ip' : '*', - 'upload' : None, - 'download' : None - } - - conf.set_level(base_path + ['authentication', 'local-users', 'username', username]) - - if conf.exists(['password']): - user['password'] = conf.return_value(['password']) - - if conf.exists(['disable']): - user['state'] = 'disable' - - if conf.exists(['static-ip']): - user['ip'] = conf.return_value(['static-ip']) - - if conf.exists(['rate-limit', 'download']): - user['download'] = conf.return_value(['rate-limit', 'download']) - - if conf.exists(['rate-limit', 'upload']): - user['upload'] = conf.return_value(['rate-limit', 'upload']) - - sstp['local_users'].append(user) - - # - # RADIUS auth and settings - conf.set_level(base_path + ['authentication', 'radius']) - if conf.exists(['server']): - for server in conf.list_nodes(['server']): - radius = { - 'server' : server, - 'key' : '', - 'fail_time' : 0, - 'port' : '1812', - 'acct_port' : '1813' - } - - conf.set_level(base_path + ['authentication', 'radius', 'server', server]) - - if conf.exists(['fail-time']): - radius['fail_time'] = conf.return_value(['fail-time']) - - if conf.exists(['port']): - radius['port'] = conf.return_value(['port']) - - if conf.exists(['acct-port']): - radius['acct_port'] = conf.return_value(['acct-port']) - - if conf.exists(['key']): - radius['key'] = conf.return_value(['key']) - - if not conf.exists(['disable']): - sstp['radius_server'].append(radius) - - # - # advanced radius-setting - conf.set_level(base_path + ['authentication', 'radius']) - - if conf.exists(['acct-timeout']): - sstp['radius_acct_tmo'] = conf.return_value(['acct-timeout']) - - if conf.exists(['max-try']): - sstp['radius_max_try'] = conf.return_value(['max-try']) - - if conf.exists(['timeout']): - sstp['radius_timeout'] = conf.return_value(['timeout']) - - if conf.exists(['nas-identifier']): - sstp['radius_nas_id'] = conf.return_value(['nas-identifier']) - - if conf.exists(['nas-ip-address']): - sstp['radius_nas_ip'] = conf.return_value(['nas-ip-address']) - - if conf.exists(['source-address']): - sstp['radius_source_address'] = conf.return_value(['source-address']) - - # Dynamic Authorization Extensions (DOA)/Change Of Authentication (COA) - if conf.exists(['dynamic-author']): - dae = { - 'port' : '1700', - 'server' : '', - 'key' : '' - } - - if conf.exists(['dynamic-author', 'server']): - dae['server'] = conf.return_value(['dynamic-author', 'server']) - - if conf.exists(['dynamic-author', 'port']): - dae['port'] = conf.return_value(['dynamic-author', 'port']) - - if conf.exists(['dynamic-author', 'key']): - dae['key'] = conf.return_value(['dynamic-author', 'key']) - - sstp['radius_dynamic_author'] = dae - - if conf.exists(['rate-limit', 'enable']): - sstp['radius_shaper_attr'] = 'Filter-Id' - c_attr = ['rate-limit', 'enable', 'attribute'] - if conf.exists(c_attr): - sstp['radius_shaper_attr'] = conf.return_value(c_attr) - - c_vendor = ['rate-limit', 'enable', 'vendor'] - if conf.exists(c_vendor): - sstp['radius_shaper_vendor'] = conf.return_value(c_vendor) - - # - # authentication protocols - conf.set_level(base_path + ['authentication']) - if conf.exists(['protocols']): - # clear default list content, now populate with actual CLI values - sstp['auth_proto'] = [] - auth_mods = { - 'pap': 'auth_pap', - 'chap': 'auth_chap_md5', - 'mschap': 'auth_mschap_v1', - 'mschap-v2': 'auth_mschap_v2' - } - - for proto in conf.return_values(['protocols']): - sstp['auth_proto'].append(auth_mods[proto]) - - # - # read in SSL certs - conf.set_level(base_path + ['ssl']) - if conf.exists(['ca-cert-file']): - sstp['ssl_ca'] = conf.return_value(['ca-cert-file']) - - if conf.exists(['cert-file']): - sstp['ssl_cert'] = conf.return_value(['cert-file']) - - if conf.exists(['key-file']): - sstp['ssl_key'] = conf.return_value(['key-file']) - - - # - # read in client IPv4 pool - conf.set_level(base_path + ['client-ip-pool']) - if conf.exists(['subnet']): - sstp['client_ip_pool'] = conf.return_values(['subnet']) - - # - # read in client IPv6 pool - conf.set_level(base_path + ['client-ipv6-pool']) - if conf.exists(['prefix']): - for prefix in conf.list_nodes(['prefix']): - tmp = { - 'prefix': prefix, - 'mask': '64' - } - - if conf.exists(['prefix', prefix, 'mask']): - tmp['mask'] = conf.return_value(['prefix', prefix, 'mask']) - - sstp['client_ipv6_pool'].append(tmp) - - if conf.exists(['delegate']): - for prefix in conf.list_nodes(['delegate']): - tmp = { - 'prefix': prefix, - 'mask': '' - } - - if conf.exists(['delegate', prefix, 'delegation-prefix']): - tmp['mask'] = conf.return_value(['delegate', prefix, 'delegation-prefix']) - - sstp['client_ipv6_delegate_prefix'].append(tmp) - - # - # read in network settings - conf.set_level(base_path) - if conf.exists(['gateway-address']): - sstp['client_gateway'] = conf.return_value(['gateway-address']) - - if conf.exists(['name-server']): - for name_server in conf.return_values(['name-server']): - if is_ipv4(name_server): - sstp['dnsv4'].append(name_server) - else: - sstp['dnsv6'].append(name_server) - - if conf.exists(['mtu']): - sstp['mtu'] = conf.return_value(['mtu']) - - # - # read in PPP stuff - conf.set_level(base_path + ['ppp-options']) - if conf.exists('mppe'): - sstp['ppp_mppe'] = conf.return_value(['mppe']) - - if conf.exists(['lcp-echo-failure']): - sstp['ppp_echo_failure'] = conf.return_value(['lcp-echo-failure']) - - if conf.exists(['lcp-echo-interval']): - sstp['ppp_echo_interval'] = conf.return_value(['lcp-echo-interval']) - - if conf.exists(['lcp-echo-timeout']): - sstp['ppp_echo_timeout'] = conf.return_value(['lcp-echo-timeout']) - + # retrieve common dictionary keys + sstp = get_accel_dict(conf, base, sstp_chap_secrets) return sstp - def verify(sstp): - if sstp is None: + if not sstp: return None - # vertify auth settings - if sstp['auth_mode'] == 'local': - if not sstp['local_users']: - raise ConfigError('SSTP local auth mode requires local users to be configured!') - - for user in sstp['local_users']: - username = user['name'] - if not user['password']: - raise ConfigError(f'Password required for local user "{username}"') - - # if up/download is set, check that both have a value - if user['upload'] and not user['download']: - raise ConfigError(f'Download speed value required for local user "{username}"') - - if user['download'] and not user['upload']: - raise ConfigError(f'Upload speed value required for local user "{username}"') - - if not sstp['client_ip_pool']: - raise ConfigError('Client IP subnet required') - - if not sstp['client_gateway']: - raise ConfigError('Client gateway IP address required') - - if len(sstp['dnsv4']) > 2: - raise ConfigError('Not more then two IPv4 DNS name-servers can be configured') + verify_accel_ppp_base_service(sstp) - # check ipv6 - if sstp['client_ipv6_delegate_prefix'] and not sstp['client_ipv6_pool']: - raise ConfigError('IPv6 prefix delegation requires client-ipv6-pool prefix') + if not sstp['client_ip_pool']: + raise ConfigError('Client IP subnet required') - for prefix in sstp['client_ipv6_delegate_prefix']: - if not prefix['mask']: - raise ConfigError('Delegation-prefix required for individual delegated networks') - - if not sstp['ssl_ca'] or not sstp['ssl_cert'] or not sstp['ssl_key']: - raise ConfigError('One or more SSL certificates missing') - - if not os.path.exists(sstp['ssl_ca']): - file = sstp['ssl_ca'] - raise ConfigError(f'SSL CA certificate file "{file}" does not exist') - - if not os.path.exists(sstp['ssl_cert']): - file = sstp['ssl_cert'] - raise ConfigError(f'SSL public key file "{file}" does not exist') - - if not os.path.exists(sstp['ssl_key']): - file = sstp['ssl_key'] - raise ConfigError(f'SSL private key file "{file}" does not exist') + # + # SSL certificate checks + # + tmp = vyos_dict_search('ssl.ca_cert_file', sstp) + if not tmp: + raise ConfigError(f'SSL CA certificate file required!') + else: + if not os.path.isfile(tmp): + raise ConfigError(f'SSL CA certificate "{tmp}" does not exist!') - if sstp['auth_mode'] == 'radius': - if len(sstp['radius_server']) == 0: - raise ConfigError('RADIUS authentication requires at least one server') + tmp = vyos_dict_search('ssl.cert_file', sstp) + if not tmp: + raise ConfigError(f'SSL public key file required!') + else: + if not os.path.isfile(tmp): + raise ConfigError(f'SSL public key "{tmp}" does not exist!') - for radius in sstp['radius_server']: - if not radius['key']: - server = radius['server'] - raise ConfigError(f'Missing RADIUS secret key for server "{ server }"') + tmp = vyos_dict_search('ssl.key_file', sstp) + if not tmp: + raise ConfigError(f'SSL private key file required!') + else: + if not os.path.isfile(tmp): + raise ConfigError(f'SSL private key "{tmp}" does not exist!') def generate(sstp): if not sstp: @@ -358,9 +84,9 @@ def generate(sstp): # accel-cmd reload doesn't work so any change results in a restart of the daemon render(sstp_conf, 'accel-ppp/sstp.config.tmpl', sstp, trim_blocks=True) - if sstp['local_users']: - render(sstp_chap_secrets, 'accel-ppp/chap-secrets.tmpl', sstp, trim_blocks=True) - os.chmod(sstp_chap_secrets, S_IRUSR | S_IWUSR | S_IRGRP) + if vyos_dict_search('authentication.mode', sstp) == 'local': + render(sstp_chap_secrets, 'accel-ppp/chap-secrets.config_dict.tmpl', + sstp, trim_blocks=True, permission=0o640) else: if os.path.exists(sstp_chap_secrets): os.unlink(sstp_chap_secrets) -- cgit v1.2.3