From 762d36d5b71d600e5f286a4f06c806a2e016ae7a Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Fri, 10 Apr 2020 23:22:17 +0200 Subject: vpn: l2tp: T2264: migrate to new dictionary keys for local auth --- data/templates/l2tp/chap-secrets.tmpl | 12 +- data/templates/l2tp/l2tp.config.tmpl | 10 +- src/conf_mode/vpn_l2tp.py | 271 ++++++++++++++++++---------------- 3 files changed, 156 insertions(+), 137 deletions(-) diff --git a/data/templates/l2tp/chap-secrets.tmpl b/data/templates/l2tp/chap-secrets.tmpl index 0db295fdc..dd00d7bd0 100644 --- a/data/templates/l2tp/chap-secrets.tmpl +++ b/data/templates/l2tp/chap-secrets.tmpl @@ -1,10 +1,10 @@ -# username server password acceptable local IP addresses shaper -{% for user in authentication['local-users'] %} -{% if authentication['local-users'][user]['state'] == 'enabled' %} -{% if authentication['local-users'][user]['upload'] and authentication['local-users'][user]['download'] %} -{{ "%-12s" | format(user) }} * {{ "%-16s" | format(authentication['local-users'][user]['passwd']) }} {{ "%-16s" | format(authentication['local-users'][user]['ip']) }} {{ authentication['local-users'][user]['download'] }} / {{ authentication['local-users'][user]['upload'] }} +# username server password acceptable local IP addresses shaper +{% for user in local_users %} +{% if user.state == 'enabled' %} +{% if user.upload and user.download %} +{{ "%-12s" | format(user.name) }} * {{ "%-16s" | format(user.password) }} {{ "%-16s" | format(user.ip) }} {{ user.download }} / {{ user.upload }} {% else %} -{{ "%-12s" | format(user) }} * {{ "%-16s" | format(authentication['local-users'][user]['passwd']) }} {{ "%-16s" | format(authentication['local-users'][user]['ip']) }} +{{ "%-12s" | format(user.name) }} * {{ "%-16s" | format(user.password) }} {{ "%-16s" | format(user.ip) }} {% endif %} {% endif %} {% endfor %} diff --git a/data/templates/l2tp/l2tp.config.tmpl b/data/templates/l2tp/l2tp.config.tmpl index 7e15233bb..cce526dd8 100644 --- a/data/templates/l2tp/l2tp.config.tmpl +++ b/data/templates/l2tp/l2tp.config.tmpl @@ -3,12 +3,14 @@ log_syslog l2tp chap-secrets -{% for proto in authentication['auth_proto']: %} +{% for proto in auth_proto: %} {{proto}} {% endfor%} -{% if authentication['mode'] == 'radius' %} + +{% if auth_mode == 'radius' %} radius {% endif -%} + ippool shaper ipv6pool @@ -74,7 +76,7 @@ secret={{lns_shared_secret}} gw-ip-address={{gateway_address}} {% endif %} -{% if authentication['mode'] == 'local' %} +{% if auth_mode == 'local' %} [chap-secrets] chap-secrets=/etc/accel-ppp/l2tp/chap-secrets {% if gateway_address %} @@ -106,7 +108,7 @@ ccp=0 ipv6=allow {% endif %} -{% if authentication['mode'] == 'radius' %} +{% if auth_mode == 'radius' %} [radius] {% for rsrv in authentication['radiussrv']: %} server={{rsrv}},{{authentication['radiussrv'][rsrv]['secret']}},\ diff --git a/src/conf_mode/vpn_l2tp.py b/src/conf_mode/vpn_l2tp.py index 8f493ddaf..4cd28e0fe 100755 --- a/src/conf_mode/vpn_l2tp.py +++ b/src/conf_mode/vpn_l2tp.py @@ -43,14 +43,14 @@ if not os.path.exists(l2tp_cnf_dir): default_config_data = { 'authentication': { - 'mode': 'local', - 'local-users': { - }, 'radiussrv': {}, 'radiusopt': {}, - 'auth_proto': [], 'mppe': 'prefer' }, + 'auth_proto' : [], + 'local_users' : [], + 'auth_mode' : 'local', + 'radius_server' : [], 'outside_addr': '', 'gateway_address': '10.255.255.0', 'dnsv4': [], @@ -91,12 +91,12 @@ def _accel_cmd(command): def get_config(): - c = Config() - base = ['vpn', 'l2tp', 'remote-access'] - if not c.exists(base): + conf = Config() + base_path = ['vpn', 'l2tp', 'remote-access'] + if not conf.exists(base_path): return None - c.set_level(base) + conf.set_level(base_path) l2tp = deepcopy(default_config_data) cpu = os.cpu_count() @@ -104,197 +104,210 @@ def get_config(): l2tp['thread_cnt'] = int(cpu/2) ### general options ### - if c.exists(['name-server']): - for name_server in c.return_values(['name-server']): + if conf.exists(['name-server']): + for name_server in conf.return_values(['name-server']): if is_ipv4(name_server): l2tp['dnsv4'].append(name_server) else: l2tp['dnsv6'].append(name_server) - if c.exists(['wins-server']): - l2tp['wins'] = c.return_values(['wins-server']) - - if c.exists('outside-address'): - l2tp['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'): - l2tp['authentication']['local-users'].update( - { - usr: { - 'passwd': '', - 'state': 'enabled', - 'ip': '*', - 'upload': None, - 'download': None - } - } - ) - - if c.exists('authentication local-users username ' + usr + ' password'): - l2tp['authentication']['local-users'][usr]['passwd'] = c.return_value( - 'authentication local-users username ' + usr + ' password') - if c.exists('authentication local-users username ' + usr + ' disable'): - l2tp['authentication']['local-users'][usr]['state'] = 'disable' - if c.exists('authentication local-users username ' + usr + ' static-ip'): - l2tp['authentication']['local-users'][usr]['ip'] = c.return_value( - 'authentication local-users username ' + usr + ' static-ip') - if c.exists('authentication local-users username ' + usr + ' rate-limit download'): - l2tp['authentication']['local-users'][usr]['download'] = c.return_value( - 'authentication local-users username ' + usr + ' rate-limit download') - if c.exists('authentication local-users username ' + usr + ' rate-limit upload'): - l2tp['authentication']['local-users'][usr]['upload'] = c.return_value( - 'authentication local-users username ' + usr + ' rate-limit upload') + if conf.exists(['wins-server']): + l2tp['wins'] = conf.return_values(['wins-server']) - # authentication mode radius servers and settings + if conf.exists('outside-address'): + l2tp['outside_addr'] = conf.return_value('outside-address') + + if conf.exists(['authentication', 'mode']): + l2tp['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 c.exists('authentication mode radius'): - l2tp['authentication']['mode'] = 'radius' - rsrvs = c.list_nodes('authentication radius server') + if conf.exists(['rate-limit', 'upload']): + user['upload'] = conf.return_value(['rate-limit', 'upload']) + + l2tp['local_users'].append(user) + + conf.set_level(base_path) + # authentication mode radius servers and settings + if conf.exists('authentication mode radius'): + rsrvs = conf.list_nodes('authentication radius server') for rsrv in rsrvs: - if c.return_value('authentication radius server ' + rsrv + ' fail-time') == None: + if conf.return_value('authentication radius server ' + rsrv + ' fail-time') == None: ftime = '0' else: - ftime = str(c.return_value( + ftime = str(conf.return_value( 'authentication radius server ' + rsrv + ' fail-time')) - if c.return_value('authentication radius-server ' + rsrv + ' req-limit') == None: + if conf.return_value('authentication radius-server ' + rsrv + ' req-limit') == None: reql = '0' else: - reql = str(c.return_value( + reql = str(conf.return_value( 'authentication radius server ' + rsrv + ' req-limit')) l2tp['authentication']['radiussrv'].update( { rsrv: { - 'secret': c.return_value('authentication radius server ' + rsrv + ' key'), + 'secret': conf.return_value('authentication radius server ' + rsrv + ' key'), 'fail-time': ftime, 'req-limit': reql } } ) # Source ip address feature - if c.exists('authentication radius source-address'): - l2tp['authentication']['radius_source_address'] = c.return_value( + if conf.exists('authentication radius source-address'): + l2tp['authentication']['radius_source_address'] = conf.return_value( 'authentication radius source-address') # advanced radius-setting - if c.exists('authentication radius acct-timeout'): - l2tp['authentication']['radiusopt']['acct-timeout'] = c.return_value( + if conf.exists('authentication radius acct-timeout'): + l2tp['authentication']['radiusopt']['acct-timeout'] = conf.return_value( 'authentication radius acct-timeout') - if c.exists('authentication radius max-try'): - l2tp['authentication']['radiusopt']['max-try'] = c.return_value( + if conf.exists('authentication radius max-try'): + l2tp['authentication']['radiusopt']['max-try'] = conf.return_value( 'authentication radius max-try') - if c.exists('authentication radius timeout'): - l2tp['authentication']['radiusopt']['timeout'] = c.return_value( + if conf.exists('authentication radius timeout'): + l2tp['authentication']['radiusopt']['timeout'] = conf.return_value( 'authentication radius timeout') - if c.exists('authentication radius nas-identifier'): - l2tp['authentication']['radiusopt']['nas-id'] = c.return_value( + if conf.exists('authentication radius nas-identifier'): + l2tp['authentication']['radiusopt']['nas-id'] = conf.return_value( 'authentication radius nas-identifier') - if c.exists('authentication radius dae-server'): + if conf.exists('authentication radius dae-server'): # Set default dae-server port if not defined - if c.exists('authentication radius dae-server port'): - dae_server_port = c.return_value( + if conf.exists('authentication radius dae-server port'): + dae_server_port = conf.return_value( 'authentication radius dae-server port') else: dae_server_port = "3799" l2tp['authentication']['radiusopt'].update( { 'dae-srv': { - 'ip-addr': c.return_value('authentication radius dae-server ip-address'), + 'ip-addr': conf.return_value('authentication radius dae-server ip-address'), 'port': dae_server_port, - 'secret': str(c.return_value('authentication radius dae-server secret')) + 'secret': str(conf.return_value('authentication radius dae-server secret')) } } ) # filter-id is the internal accel default if attribute is empty # set here as default for visibility which may change in the future - if c.exists('authentication radius rate-limit enable'): - if not c.exists('authentication radius rate-limit attribute'): + if conf.exists('authentication radius rate-limit enable'): + if not conf.exists('authentication radius rate-limit attribute'): l2tp['authentication']['radiusopt']['shaper'] = { 'attr': 'Filter-Id' } else: l2tp['authentication']['radiusopt']['shaper'] = { - 'attr': c.return_value('authentication radius rate-limit attribute') + 'attr': conf.return_value('authentication radius rate-limit attribute') } - if c.exists('authentication radius rate-limit vendor'): - l2tp['authentication']['radiusopt']['shaper']['vendor'] = c.return_value( + if conf.exists('authentication radius rate-limit vendor'): + l2tp['authentication']['radiusopt']['shaper']['vendor'] = conf.return_value( 'authentication radius rate-limit vendor') - if c.exists('client-ip-pool'): - if c.exists('client-ip-pool start') and c.exists('client-ip-pool stop'): - l2tp['client_ip_pool'] = c.return_value( - 'client-ip-pool start') + '-' + re.search('[0-9]+$', c.return_value('client-ip-pool stop')).group(0) + if conf.exists('client-ip-pool'): + if conf.exists('client-ip-pool start') and conf.exists('client-ip-pool stop'): + l2tp['client_ip_pool'] = conf.return_value( + 'client-ip-pool start') + '-' + re.search('[0-9]+$', conf.return_value('client-ip-pool stop')).group(0) - if c.exists('client-ip-pool subnet'): - l2tp['client_ip_subnets'] = c.return_values( + if conf.exists('client-ip-pool subnet'): + l2tp['client_ip_subnets'] = conf.return_values( 'client-ip-pool subnet') - if c.exists('client-ipv6-pool prefix'): - l2tp['client_ipv6_pool']['prefix'] = c.return_values( + if conf.exists('client-ipv6-pool prefix'): + l2tp['client_ipv6_pool']['prefix'] = conf.return_values( 'client-ipv6-pool prefix') l2tp['ip6_column'] = 'ip6,' - if c.exists('client-ipv6-pool delegate-prefix'): - l2tp['client_ipv6_pool']['delegate_prefix'] = c.return_values( + if conf.exists('client-ipv6-pool delegate-prefix'): + l2tp['client_ipv6_pool']['delegate_prefix'] = conf.return_values( 'client-ipv6-pool delegate-prefix') l2tp['ip6_dp_column'] = 'ip6-dp,' - if c.exists('mtu'): - l2tp['mtu'] = c.return_value('mtu') + if conf.exists('mtu'): + l2tp['mtu'] = conf.return_value('mtu') # gateway address - if c.exists('gateway-address'): - l2tp['gateway_address'] = c.return_value('gateway-address') + if conf.exists('gateway-address'): + l2tp['gateway_address'] = conf.return_value('gateway-address') else: # calculate gw-ip-address - if c.exists('client-ip-pool start'): + if conf.exists('client-ip-pool start'): # use start ip as gw-ip-address - l2tp['gateway_address'] = c.return_value( + l2tp['gateway_address'] = conf.return_value( 'client-ip-pool start') - elif c.exists('client-ip-pool subnet'): + elif conf.exists('client-ip-pool subnet'): # use first ip address from first defined pool - lst_ip = re.findall("\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}", c.return_values( + lst_ip = re.findall("\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}", conf.return_values( 'client-ip-pool subnet')[0]) l2tp['gateway_address'] = lst_ip[0] - if c.exists('authentication require'): - auth_mods = {'pap': 'pap', 'chap': 'auth_chap_md5', - 'mschap': 'auth_mschap_v1', 'mschap-v2': 'auth_mschap_v2'} - for proto in c.return_values('authentication require'): - l2tp['authentication']['auth_proto'].append( - auth_mods[proto]) + # + # authentication protocols + conf.set_level(base_path + ['authentication']) + if conf.exists(['protocols']): + 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']): + l2tp['auth_proto'].append(auth_mods[proto]) + else: - l2tp['authentication']['auth_proto'] = ['auth_mschap_v2'] + l2tp['auth_proto'] = ['auth_mschap_v2'] - if c.exists('authentication mppe'): - l2tp['authentication']['mppe'] = c.return_value( + if conf.exists('authentication mppe'): + l2tp['authentication']['mppe'] = conf.return_value( 'authentication mppe') - if c.exists('idle'): - l2tp['idle_timeout'] = c.return_value('idle') + if conf.exists('idle'): + l2tp['idle_timeout'] = conf.return_value('idle') # LNS secret - if c.exists('lns shared-secret'): - l2tp['lns_shared_secret'] = c.return_value('lns shared-secret') + if conf.exists('lns shared-secret'): + l2tp['lns_shared_secret'] = conf.return_value('lns shared-secret') - if c.exists('ccp-disable'): + if conf.exists('ccp-disable'): l2tp['ccp_disable'] = True # ppp_options ppp_options = {} - if c.exists('ppp-options'): - if c.exists('ppp-options lcp-echo-failure'): - ppp_options['lcp-echo-failure'] = c.return_value( + if conf.exists('ppp-options'): + if conf.exists('ppp-options lcp-echo-failure'): + ppp_options['lcp-echo-failure'] = conf.return_value( 'ppp-options lcp-echo-failure') - if c.exists('ppp-options lcp-echo-interval'): - ppp_options['lcp-echo-interval'] = c.return_value( + if conf.exists('ppp-options lcp-echo-interval'): + ppp_options['lcp-echo-interval'] = conf.return_value( 'ppp-options lcp-echo-interval') if len(ppp_options) != 0: l2tp['ppp_options'] = ppp_options + import pprint + pprint.pprint(l2tp) return l2tp @@ -302,24 +315,25 @@ def verify(l2tp): if l2tp == None: return None - if l2tp['authentication']['mode'] == 'local': - if not l2tp['authentication']['local-users']: - raise ConfigError( - 'l2tp-server authentication local-users required') - for usr in l2tp['authentication']['local-users']: - if not l2tp['authentication']['local-users'][usr]['passwd']: - raise ConfigError('user ' + usr + ' requires a password') + if l2tp['auth_mode'] == 'local': + if not l2tp['local_users']: + raise ConfigError('L2TP local auth mode requires local users to be configured!') - if l2tp['authentication']['mode'] == 'radius': + for user in l2tp['local_users']: + if not user['password']: + raise ConfigError(f"Password required for user {user['name']}") + + elif l2tp['auth_mode'] == 'radius': if len(l2tp['authentication']['radiussrv']) == 0: raise ConfigError('radius server required') + for rsrv in l2tp['authentication']['radiussrv']: if l2tp['authentication']['radiussrv'][rsrv]['secret'] == None: raise ConfigError('radius server ' + rsrv + ' needs a secret configured') # check for the existence of a client ip pool - if not l2tp['client_ip_pool'] and not l2tp['client_ip_subnets']: + if not (l2tp['client_ip_pool'] or l2tp['client_ip_subnets']): raise ConfigError( "set vpn l2tp remote-access client-ip-pool requires subnet or start/stop IP pool") @@ -337,6 +351,8 @@ def verify(l2tp): if len(l2tp['dnsv6']) > 3: raise ConfigError('Not more then three IPv6 DNS name-servers can be configured') + return None + def generate(l2tp): if l2tp == None: @@ -351,18 +367,19 @@ def generate(l2tp): config_text = tmpl.render(c) open(l2tp_conf, 'w').write(config_text) - if l2tp['authentication']['local-users']: + if l2tp['auth_mode'] == 'local': tmpl = env.get_template('chap-secrets.tmpl') - chap_secrets_txt = tmpl.render(c) + chap_secrets_txt = tmpl.render(l2tp) old_umask = os.umask(0o077) - open(chap_secrets, 'w').write(chap_secrets_txt) + with open(chap_secrets, 'w') as f: + f.write(chap_secrets_txt) os.umask(old_umask) - return c + return None -def apply(c): - if c == None: +def apply(l2tp): + if l2tp == None: if os.path.exists(pidfile): _accel_cmd('shutdown hard') if os.path.exists(pidfile): -- cgit v1.2.3