From 05f839574b101bdea4d91740834479811a271e11 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Wed, 22 Apr 2020 08:14:10 +0200 Subject: vpn: pptp: T2351: align configuration to other accel implementations --- data/templates/accel-ppp/pptp.config.tmpl | 89 +++++++++ data/templates/pptp/chap-secrets.tmpl | 6 - data/templates/pptp/pptp.config.tmpl | 86 -------- src/conf_mode/vpn_pptp.py | 318 ++++++++++++++++++------------ 4 files changed, 283 insertions(+), 216 deletions(-) create mode 100644 data/templates/accel-ppp/pptp.config.tmpl delete mode 100644 data/templates/pptp/chap-secrets.tmpl delete mode 100644 data/templates/pptp/pptp.config.tmpl diff --git a/data/templates/accel-ppp/pptp.config.tmpl b/data/templates/accel-ppp/pptp.config.tmpl new file mode 100644 index 000000000..0bbfc13c5 --- /dev/null +++ b/data/templates/accel-ppp/pptp.config.tmpl @@ -0,0 +1,89 @@ +### generated by accel_pptp.py ### +[modules] +log_syslog +pptp +ippool +{% if auth_mode == 'local' %} +chap-secrets +{% elif auth_mode == 'radius' %} +radius +{% endif -%} +{% for proto in auth_proto %} +{{proto}} +{% endfor %} + +[core] +thread-count={{ thread_cnt }} + +[log] +syslog=accel-pptp,daemon +copy=1 +level=5 + +{% if dnsv4 %} +[dns] +{% for dns in dnsv4 -%} +dns{{ loop.index }}={{ dns }} +{% endfor -%} +{% endif %} + +{% if wins %} +[wins] +{% for server in wins -%} +wins{{ loop.index }}={{ server }} +{% endfor -%} +{% endif %} + + +[pptp] +ifname=pptp%d +{% if outside_addr %} +bind={{ outside_addr }} +{% endif %} +verbose=1 +ppp-max-mtu={{mtu}} +mppe={{ ppp_mppe }} +echo-interval=10 +echo-failure=3 + + +[client-ip-range] +0.0.0.0/0 + +[ip-pool] +tunnel={{ client_ip_pool }} +gw-ip-address={{ gw_ip }} + +[ppp] +verbose=5 +check-ip=1 +single-session=replace + +{% if auth_mode == 'local' %} +[chap-secrets] +chap-secrets={{ chap_secrets_file }} +{% elif auth_mode == 'radius' %} +[radius] +verbose=1 +{% for r in radius_server %} +server={{ r.server }},{{ r.key }},auth-port={{ r.port }},req-limit=0,fail-time={{ r.fail_time }} +{% endfor -%} + +acct-timeout={{ radius_acct_tmo }} +timeout={{ radius_timeout }} +max-try={{ radius_max_try }} + +{% if radius_nas_id %} +nas-identifier={{ radius_nas_id }} +{% endif -%} +{% if radius_nas_ip %} +nas-ip-address={{ radius_nas_ip }} +{% endif -%} +{% if radius_source_address %} +bind={{ radius_source_address }} +{% endif -%} +{% endif %} + +[cli] +tcp=127.0.0.1:2003 + diff --git a/data/templates/pptp/chap-secrets.tmpl b/data/templates/pptp/chap-secrets.tmpl deleted file mode 100644 index f93f4607b..000000000 --- a/data/templates/pptp/chap-secrets.tmpl +++ /dev/null @@ -1,6 +0,0 @@ -# username server password acceptable local IP addresses -{% for user in authentication['local-users'] %} -{% if authentication['local-users'][user]['state'] == 'enabled' %} -{{ "%-12s" | format(user) }} * {{ "%-16s" | format(authentication['local-users'][user]['passwd']) }} {{ "%-16s" | format(authentication['local-users'][user]['ip']) }} -{% endif %} -{% endfor %} diff --git a/data/templates/pptp/pptp.config.tmpl b/data/templates/pptp/pptp.config.tmpl deleted file mode 100644 index 5f8395f2d..000000000 --- a/data/templates/pptp/pptp.config.tmpl +++ /dev/null @@ -1,86 +0,0 @@ -### generated by accel_pptp.py ### -[modules] -log_syslog -pptp -ippool -chap-secrets -{% if authentication['auth_proto'] %} -{{ authentication['auth_proto'] }} -{% else %} -auth_mschap_v2 -{% endif %} -{% if authentication['mode'] == 'radius' %} -radius -{% endif -%} - -[core] -thread-count={{thread_cnt}} - -[log] -syslog=accel-pptp,daemon -copy=1 -level=5 - -{% if dns %} -[dns] -{% if dns[0] %} -dns1={{dns[0]}} -{% endif %} -{% if dns[1] %} -dns2={{dns[1]}} -{% endif %} -{% endif %} - -{% if wins %} -[wins] -{% if wins[0] %} -wins1={{wins[0]}} -{% endif %} -{% if wins[1] %} -wins2={{wins[1]}} -{% endif %} -{% endif %} - -[pptp] -ifname=pptp%d -{% if outside_addr %} -bind={{outside_addr}} -{% endif %} -verbose=1 -ppp-max-mtu={{mtu}} -mppe={{authentication['mppe']}} -echo-interval=10 -echo-failure=3 - - -[client-ip-range] -0.0.0.0/0 - -[ip-pool] -tunnel={{client_ip_pool}} -gw-ip-address={{gw_ip}} - -{% if authentication['mode'] == 'local' %} -[chap-secrets] -chap-secrets={{ chap_secrets_file }} -{% endif %} - -[ppp] -verbose=5 -check-ip=1 -single-session=replace - -{% if authentication['mode'] == 'radius' %} -[radius] -{% for rsrv in authentication['radiussrv']: %} -server={{rsrv}},{{authentication['radiussrv'][rsrv]['secret']}},\ -req-limit={{authentication['radiussrv'][rsrv]['req-limit']}},\ -fail-time={{authentication['radiussrv'][rsrv]['fail-time']}} -{% endfor %} -timeout=30 -acct-timeout=30 -max-try=3 -{%endif %} - -[cli] -tcp=127.0.0.1:2003 diff --git a/src/conf_mode/vpn_pptp.py b/src/conf_mode/vpn_pptp.py index 9e8d1c08b..d76cd4147 100755 --- a/src/conf_mode/vpn_pptp.py +++ b/src/conf_mode/vpn_pptp.py @@ -18,140 +18,200 @@ import os import re from copy import deepcopy -from socket import socket, AF_INET, SOCK_STREAM +from stat import S_IRUSR, S_IWUSR, S_IRGRP from sys import exit -from time import sleep from vyos.config import Config -from vyos import ConfigError -from vyos.util import call, get_half_cpus from vyos.template import render +from vyos.util import call, get_half_cpus +from vyos import ConfigError pptp_conf = '/run/accel-pppd/pptp.conf' pptp_chap_secrets = '/run/accel-pppd/pptp.chap-secrets' default_pptp = { - 'authentication': { - 'mode': 'local', - 'local-users': { - }, - 'radiussrv': {}, - 'auth_proto': 'auth_mschap_v2', - 'mppe': 'require' - }, + 'auth_mode' : 'local', + 'local_users' : [], + 'radius_server' : [], + 'radius_acct_tmo' : '30', + 'radius_max_try' : '3', + 'radius_timeout' : '30', + 'radius_nas_id' : '', + 'radius_nas_ip' : '', + 'radius_source_address' : '', + 'radius_shaper_attr' : '', + 'radius_shaper_vendor': '', + 'radius_dynamic_author' : '', 'chap_secrets_file': pptp_chap_secrets, # used in Jinja2 template 'outside_addr': '', - 'dns': [], + 'dnsv4': [], 'wins': [], 'client_ip_pool': '', 'mtu': '1436', + 'auth_proto' : ['auth_mschap_v2'], + 'ppp_mppe' : 'prefer', 'thread_cnt': get_half_cpus() } def get_config(): - c = Config() - if not c.exists(['vpn', 'pptp', 'remote-access']): + conf = Config() + base_path = ['vpn', 'pptp', 'remote-access'] + if not conf.exists(base_path): return None - c.set_level(['vpn', 'pptp', 'remote-access']) pptp = deepcopy(default_pptp) + conf.set_level(base_path) + + for server in ['server-1', 'server-2']: + if conf.exists(['dns-servers', server]): + tmp = conf.return_value(['dns-servers', server]) + pptp['dnsv4'].append(tmp) + + for server in ['server-1', 'server-2']: + if conf.exists(['wins-servers', server]): + tmp = conf.return_value(['wins-servers', server]) + pptp['wins'].append(tmp) + + if conf.exists(['outside-address']): + pptp['outside_addr'] = conf.return_value(['outside-address']) + + if conf.exists(['authentication', 'mode']): + pptp['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' : '*', + } + + 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 not conf.exists(['disable']): + pptp['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' + } + + 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(['secret']): + radius['key'] = conf.return_value(['secret']) + + if not conf.exists(['disable']): + pptp['radius_server'].append(radius) + + # + # advanced radius-setting + conf.set_level(base_path + ['authentication', 'radius']) - ### general options ### - - if c.exists(['dns-servers', 'server-1']): - pptp['dns'].append(c.return_value(['dns-servers', 'server-1'])) - if c.exists(['dns-servers', 'server-2']): - pptp['dns'].append(c.return_value(['dns-servers', 'server-2'])) - if c.exists(['wins-servers', 'server-1']): - pptp['wins'].append( - c.return_value(['wins-servers', 'server-1'])) - if c.exists(['wins-servers', 'server-2']): - pptp['wins'].append( - c.return_value(['wins-servers', 'server-2'])) - if c.exists(['outside-address']): - pptp['outside_addr'] = c.return_value(['outside-address']) - - # auth local - if c.exists(['authentication', 'mode', 'local']): - if c.exists(['authentication', 'local-users', 'username']): - for usr in c.list_nodes(['authentication', 'local-users', 'username']): - pptp['authentication']['local-users'].update( - { - usr: { - 'passwd': '', - 'state': 'enabled', - 'ip': '' - } - } - ) - - if c.exists(['authentication', 'local-users', 'username', usr, 'password']): - pptp['authentication']['local-users'][usr]['passwd'] = c.return_value( - ['authentication', 'local-users', 'username', usr, 'password']) - if c.exists(['authentication', 'local-users', 'username', usr, 'disable']): - pptp['authentication']['local-users'][usr]['state'] = 'disable' - if c.exists(['authentication', 'local-users', 'username', usr, 'static-ip']): - pptp['authentication']['local-users'][usr]['ip'] = c.return_value( - ['authentication', 'local-users', 'username', usr, 'static-ip']) - - # authentication mode radius servers and settings - - if c.exists(['authentication', 'mode', 'radius']): - pptp['authentication']['mode'] = 'radius' - rsrvs = c.list_nodes(['authentication', 'radius', 'server']) - for rsrv in rsrvs: - if not c.return_value(['authentication', 'radius', 'server', rsrv, 'fail-time']): - ftime = '0' - else: - ftime = c.return_value( - ['authentication', 'radius', 'server', rsrv, 'fail-time']) - if not c.return_value(['authentication', 'radius-server', rsrv, 'req-limit']): - reql = '0' - else: - reql = c.return_value( - ['authentication', 'radius', 'server', rsrv, 'req-limit']) - - pptp['authentication']['radiussrv'].update( - { - rsrv: { - 'secret': c.return_value(['authentication', 'radius', 'server', rsrv, 'key']), - 'fail-time': ftime, - 'req-limit': reql - } - } - ) - - if c.exists(['client-ip-pool']): - if c.exists(['client-ip-pool', 'start']): - pptp['client_ip_pool'] = c.return_value( - ['client-ip-pool', 'start']) - if c.exists(['client-ip-pool', 'stop']): - pptp['client_ip_pool'] += '-' + \ - re.search( - '[0-9]+$', c.return_value(['client-ip-pool', 'stop'])).group(0) - if c.exists(['mtu']): - pptp['mtu'] = c.return_value(['mtu']) + if conf.exists(['acct-timeout']): + pptp['radius_acct_tmo'] = conf.return_value(['acct-timeout']) + + if conf.exists(['max-try']): + pptp['radius_max_try'] = conf.return_value(['max-try']) + + if conf.exists(['timeout']): + pptp['radius_timeout'] = conf.return_value(['timeout']) + + if conf.exists(['nas-identifier']): + pptp['radius_nas_id'] = conf.return_value(['nas-identifier']) + + if conf.exists(['nas-ip-address']): + pptp['radius_nas_ip'] = conf.return_value(['nas-ip-address']) + + if conf.exists(['source-address']): + pptp['radius_source_address'] = conf.return_value(['source-address']) + + # Dynamic Authorization Extensions (DOA)/Change Of Authentication (COA) + if conf.exists(['dae-server']): + dae = { + 'port' : '', + 'server' : '', + 'key' : '' + } + + if conf.exists(['dynamic-author', 'ip-address']): + dae['server'] = conf.return_value(['dynamic-author', 'ip-address']) + + if conf.exists(['dynamic-author', 'port']): + dae['port'] = conf.return_value(['dynamic-author', 'port']) + + if conf.exists(['dynamic-author', 'secret']): + dae['key'] = conf.return_value(['dynamic-author', 'secret']) + + pptp['radius_dynamic_author'] = dae + + if conf.exists(['rate-limit', 'enable']): + pptp['radius_shaper_attr'] = 'Filter-Id' + c_attr = ['rate-limit', 'enable', 'attribute'] + if conf.exists(c_attr): + pptp['radius_shaper_attr'] = conf.return_value(c_attr) + + c_vendor = ['rate-limit', 'enable', 'vendor'] + if conf.exists(c_vendor): + pptp['radius_shaper_vendor'] = conf.return_value(c_vendor) + + conf.set_level(base_path) + if conf.exists(['client-ip-pool']): + if conf.exists(['client-ip-pool', 'start']) and conf.exists(['client-ip-pool', 'stop']): + start = conf.return_value(['client-ip-pool', 'start']) + stop = conf.return_value(['client-ip-pool', 'stop']) + pptp['client_ip_pool'] = start + '-' + re.search('[0-9]+$', stop).group(0) + + if conf.exists(['mtu']): + pptp['mtu'] = conf.return_value(['mtu']) # gateway address - if c.exists(['gateway-address']): - pptp['gw_ip'] = c.return_value(['gateway-address']) + if conf.exists(['gateway-address']): + pptp['gw_ip'] = conf.return_value(['gateway-address']) else: pptp['gw_ip'] = re.sub( '[0-9]+$', '1', pptp['client_ip_pool']) - if c.exists(['authentication', 'require']): - if c.return_value(['authentication', 'require']) == 'pap': - pptp['authentication']['auth_proto'] = 'auth_pap' - if c.return_value(['authentication', 'require']) == 'chap': - pptp['authentication']['auth_proto'] = 'auth_chap_md5' - if c.return_value(['authentication', 'require']) == 'mschap': - pptp['authentication']['auth_proto'] = 'auth_mschap_v1' - if c.return_value(['authentication', 'require']) == 'mschap-v2': - pptp['authentication']['auth_proto'] = 'auth_mschap_v2' + if conf.exists(['authentication', 'require']): + # clear default list content, now populate with actual CLI values + pptp['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(['authentication', 'require']): + pptp['auth_proto'].append(auth_mods[proto]) - if c.exists(['authentication', 'mppe']): - pptp['authentication']['mppe'] = c.return_value( - ['authentication', 'mppe']) + if conf.exists(['authentication', 'mppe']): + pptp['ppp_mppe'] = conf.return_value(['authentication', 'mppe']) return pptp @@ -160,37 +220,47 @@ def verify(pptp): if not pptp: return None - if pptp['authentication']['mode'] == 'local': - if not pptp['authentication']['local-users']: - raise ConfigError( - 'pptp-server authentication local-users required') - for usr in pptp['authentication']['local-users']: - if not pptp['authentication']['local-users'][usr]['passwd']: - raise ConfigError('user ' + usr + ' requires a password') + if pptp['auth_mode'] == 'local': + if not pptp['local_users']: + raise ConfigError('PPTP local auth mode requires local users to be configured!') - if pptp['authentication']['mode'] == 'radius': - if len(pptp['authentication']['radiussrv']) == 0: - raise ConfigError('radius server required') - for rsrv in pptp['authentication']['radiussrv']: - if pptp['authentication']['radiussrv'][rsrv]['secret'] == None: - raise ConfigError('radius server ' + rsrv + - ' needs a secret configured') + for user in pptp['local_users']: + username = user['name'] + if not user['password']: + raise ConfigError(f'Password required for local user "{username}"') + + elif pptp['auth_mode'] == 'radius': + if len(pptp['radius_server']) == 0: + raise ConfigError('RADIUS authentication requires at least one server') + + for radius in pptp['radius_server']: + if not radius['key']: + server = radius['server'] + raise ConfigError(f'Missing RADIUS secret key for server "{{ server }}"') + + if len(pptp['dnsv4']) > 2: + raise ConfigError('Not more then two IPv4 DNS name-servers can be configured') def generate(pptp): if not pptp: return None + import pprint + pprint.pprint(pptp) + dirname = os.path.dirname(pptp_conf) if not os.path.exists(dirname): os.mkdir(dirname) - render(pptp_conf, 'pptp/pptp.config.tmpl', pptp, trim_blocks=True) + render(pptp_conf, 'accel-ppp/pptp.config.tmpl', pptp, trim_blocks=True) - if pptp['authentication']['local-users']: - old_umask = os.umask(0o077) - render(pptp_chap_secrets, 'pptp/chap-secrets.tmpl', pptp, trim_blocks=True) - os.umask(old_umask) + if pptp['local_users']: + render(pptp_chap_secrets, 'accel-ppp/chap-secrets.tmpl', pptp, trim_blocks=True) + os.chmod(pptp_chap_secrets, S_IRUSR | S_IWUSR | S_IRGRP) + else: + if os.path.exists(pptp_chap_secrets): + os.unlink(pptp_chap_secrets) def apply(pptp): -- cgit v1.2.3