summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorhagbard <vyosdev@derith.de>2019-07-25 09:48:08 -0700
committerhagbard <vyosdev@derith.de>2019-07-25 09:48:08 -0700
commitab5ca2796c1aad0043cc0db80299e4e2d42c1b22 (patch)
treebd0bcd4232e3099c38a142c0953f3c5a89b20d4d /src
parent36f8a1e4e5966c43c5330ff223fa2ef07d346b6e (diff)
downloadvyos-1x-ab5ca2796c1aad0043cc0db80299e4e2d42c1b22.tar.gz
vyos-1x-ab5ca2796c1aad0043cc0db80299e4e2d42c1b22.zip
[accel-l2tp] - T834: l2tp implementation
- node.def deletion for show remote-access - IPSec interface checking for L2TP - IPSec x509 for l2tp - verification of outside-address to warning since it was optional in the previous config
Diffstat (limited to 'src')
-rwxr-xr-xsrc/conf_mode/accel_l2tp.py466
-rwxr-xr-xsrc/conf_mode/ipsec-settings.py197
-rwxr-xr-xsrc/op_mode/show_vpn_ra.py58
3 files changed, 719 insertions, 2 deletions
diff --git a/src/conf_mode/accel_l2tp.py b/src/conf_mode/accel_l2tp.py
new file mode 100755
index 000000000..39732b97d
--- /dev/null
+++ b/src/conf_mode/accel_l2tp.py
@@ -0,0 +1,466 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2019 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+import sys
+import os
+import re
+import subprocess
+import jinja2
+import socket
+import time
+import syslog as sl
+
+from vyos.config import Config
+from vyos import ConfigError
+
+pidfile = r'/var/run/accel_l2tp.pid'
+l2tp_cnf_dir = r'/etc/accel-ppp/l2tp'
+chap_secrets = l2tp_cnf_dir + '/chap-secrets'
+l2tp_conf = l2tp_cnf_dir + '/l2tp.config'
+# accel-pppd -d -c /etc/accel-ppp/l2tp/l2tp.config -p /var/run/accel_l2tp.pid
+
+### config path creation
+if not os.path.exists(l2tp_cnf_dir):
+ os.makedirs(l2tp_cnf_dir)
+ sl.syslog(sl.LOG_NOTICE, l2tp_cnf_dir + " created")
+
+l2tp_config = '''
+### generated by accel_l2tp.py ###
+[modules]
+log_syslog
+l2tp
+chap-secrets
+{% for proto in authentication['auth_proto']: %}
+{{proto}}
+{% endfor%}
+{% if authentication['mode'] == 'radius' %}
+radius
+{% endif -%}
+ippool
+shaper
+
+[core]
+thread-count={{thread_cnt}}
+
+[log]
+syslog=accel-l2tp,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 -%}
+
+[l2tp]
+verbose=1
+ppp-max-mtu={{mtu}}
+mppe={{authentication['mppe']}}
+{% if outside_addr %}
+bind={{outside_addr}}
+{% endif %}
+
+[client-ip-range]
+0.0.0.0/0
+
+{% if (client_ip_pool) or (client_ip_subnets) %}
+[ip-pool]
+{% if client_ip_pool %}
+{{client_ip_pool}}
+{% endif -%}
+{% if client_ip_subnets %}
+{% for sn in client_ip_subnets %}
+{{sn}}
+{% endfor -%}
+{% endif %}
+{% endif %}
+{% if outside_nexthop %}
+gw-ip-address={{outside_nexthop}}
+{% endif %}
+
+{% if authentication['mode'] == 'local' %}
+[chap-secrets]
+chap-secrets=/etc/accel-ppp/l2tp/chap-secrets
+{% endif %}
+
+[ppp]
+verbose=1
+check-ip=1
+single-session=replace
+{% if idle_timeout%}
+lcp-echo-timeout={{idle_timeout}}
+{% endif %}
+lcp-echo-interval=30
+
+{% 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 %}
+{% if authentication['radiusopt']['timeout'] %}
+timeout={{authentication['radiusopt']['timeout']}}
+{% endif %}
+{% if authentication['radiusopt']['acct-timeout'] %}
+acct-timeout={{authentication['radiusopt']['acct-timeout']}}
+{% endif %}
+{% if authentication['radiusopt']['max-try'] %}
+max-try={{authentication['radiusopt']['max-try']}}
+{% endif %}
+{% if authentication['radiusopt']['nas-id'] %}
+nas-identifier={{authentication['radiusopt']['nas-id']}}
+{% endif %}
+{% if authentication['radiusopt']['nas-ip'] %}
+nas-ip-address={{authentication['radiusopt']['nas-ip']}}
+{% endif -%}
+{% if authentication['radiusopt']['dae-srv'] %}
+dae-server={{authentication['radiusopt']['dae-srv']['ip-addr']}}:\
+{{authentication['radiusopt']['dae-srv']['port']}},\
+{{authentication['radiusopt']['dae-srv']['secret']}}
+{% endif -%}
+gw-ip-address={{outside_nexthop}}
+verbose=1
+{% endif -%}
+
+{% if authentication['radiusopt']['shaper'] %}
+[shaper]
+verbose=1
+attr={{authentication['radiusopt']['shaper']['attr']}}
+{% if authentication['radiusopt']['shaper']['vendor'] %}
+vendor={{authentication['radiusopt']['shaper']['vendor']}}
+{% endif -%}
+{% endif %}
+
+[cli]
+tcp=127.0.0.1:2004
+
+'''
+
+### l2tp chap secrets
+chap_secrets_conf = '''
+# 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']) %}
+{{user}}\t*\t{{authentication['local-users'][user]['passwd']}}\t{{authentication['local-users'][user]['ip']}}\t\
+{{authentication['local-users'][user]['download']}}/{{authentication['local-users'][user]['upload']}}
+{% else %}
+{{user}}\t*\t{{authentication['local-users'][user]['passwd']}}\t{{authentication['local-users'][user]['ip']}}
+{% endif %}
+{% endif %}
+{% endfor %}
+'''
+
+###
+# inline helper functions
+###
+# depending on hw and threads, daemon needs a little to start
+# if it takes longer than 100 * 0.5 secs, exception is being raised
+# not sure if that's the best way to check it, but it worked so far quite well
+###
+def chk_con():
+ cnt = 0
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ while True:
+ try:
+ s.connect(("127.0.0.1", 2004))
+ break
+ except ConnectionRefusedError:
+ time.sleep(0.5)
+ cnt +=1
+ if cnt == 100:
+ raise("failed to start l2tp server")
+ break
+
+### chap_secrets file if auth mode local
+def write_chap_secrets(c):
+ tmpl = jinja2.Template(chap_secrets_conf, trim_blocks=True)
+ chap_secrets_txt = tmpl.render(c)
+ old_umask = os.umask(0o077)
+ open(chap_secrets,'w').write(chap_secrets_txt)
+ os.umask(old_umask)
+ sl.syslog(sl.LOG_NOTICE, chap_secrets + ' written')
+
+def accel_cmd(cmd=''):
+ if not cmd:
+ return None
+ try:
+ ret = subprocess.check_output(['/usr/bin/accel-cmd','-p','2004',cmd]).decode().strip()
+ return ret
+ except:
+ return 1
+
+###
+# inline helper functions end
+###
+
+def get_config():
+ c = Config()
+ if not c.exists('vpn l2tp remote-access '):
+ return None
+
+ c.set_level('vpn l2tp remote-access')
+ config_data = {
+ 'authentication' : {
+ 'mode' : 'local',
+ 'local-users' : {
+ },
+ 'radiussrv' : {},
+ 'radiusopt' : {},
+ 'auth_proto' : [],
+ 'mppe' : 'prefer'
+ },
+ 'outside_addr' : '',
+ 'outside_nexthop' : '',
+ 'dns' : [],
+ 'wins' : [],
+ 'client_ip_pool' : None,
+ 'client_ip_subnets' : [],
+ 'mtu' : '1436',
+ }
+
+ ### general options ###
+
+ if c.exists('dns-servers server-1'):
+ config_data['dns'].append( c.return_value('dns-servers server-1'))
+ if c.exists('dns-servers server-2'):
+ config_data['dns'].append( c.return_value('dns-servers server-2'))
+ if c.exists('wins-servers server-1'):
+ config_data['wins'].append( c.return_value('wins-servers server-1'))
+ if c.exists('wins-servers server-2'):
+ config_data['wins'].append( c.return_value('wins-servers server-2'))
+ if c.exists('outside-address'):
+ config_data['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'):
+ config_data['authentication']['local-users'].update(
+ {
+ usr : {
+ 'passwd' : '',
+ 'state' : 'enabled',
+ 'ip' : '*',
+ 'upload' : None,
+ 'download' : None
+ }
+ }
+ )
+
+ if c.exists('authentication local-users username ' + usr + ' password'):
+ config_data['authentication']['local-users'][usr]['passwd'] = c.return_value('authentication local-users username ' + usr + ' password')
+ if c.exists('authentication local-users username ' + usr + ' disable'):
+ config_data['authentication']['local-users'][usr]['state'] = 'disable'
+ if c.exists('authentication local-users username ' + usr + ' static-ip'):
+ config_data['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'):
+ config_data['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'):
+ config_data['authentication']['local-users'][usr]['upload'] = c.return_value('authentication local-users username ' + usr + ' rate-limit upload')
+
+ ### authentication mode radius servers and settings
+
+ if c.exists('authentication mode radius'):
+ config_data['authentication']['mode'] = 'radius'
+ rsrvs = c.list_nodes('authentication radius server')
+ for rsrv in rsrvs:
+ if c.return_value('authentication radius server ' + rsrv + ' fail-time') == None:
+ ftime = '0'
+ else:
+ ftime = str(c.return_value('authentication radius server ' + rsrv + ' fail-time'))
+ if c.return_value('authentication radius-server ' + rsrv + ' req-limit') == None:
+ reql = '0'
+ else:
+ reql = str(c.return_value('authentication radius server ' + rsrv + ' req-limit'))
+
+ config_data['authentication']['radiussrv'].update(
+ {
+ rsrv : {
+ 'secret' : c.return_value('authentication radius server ' + rsrv + ' key'),
+ 'fail-time' : ftime,
+ 'req-limit' : reql
+ }
+ }
+ )
+
+ #### advanced radius-setting
+ if c.exists('authentication radius-settings'):
+ if c.exists('authentication radius-settings acct-timeout'):
+ config_data['authentication']['radiusopt']['acct-timeout'] = c.return_value('authentication radius-settings acct-timeout')
+ if c.exists('authentication radius-settings max-try'):
+ config_data['authentication']['radiusopt']['max-try'] = c.return_value('authentication radius-settings max-try')
+ if c.exists('authentication radius-settings timeout'):
+ config_data['authentication']['radiusopt']['timeout'] = c.return_value('authentication radius-settings timeout')
+ if c.exists('authentication radius-settings nas-identifier'):
+ config_data['authentication']['radiusopt']['nas-id'] = c.return_value('authentication radius-settings nas-identifier')
+ if c.exists('authentication radius-settings nas-ip-address'):
+ config_data['authentication']['radiusopt']['nas-ip'] = c.return_value('authentication radius-settings nas-ip-address')
+ if c.exists('authentication radius-settings dae-server'):
+ # Set default dae-server port if not defined
+ if c.exists('authentication radius-settings dae-server port'):
+ dae_server_port = c.return_value('authentication radius-settings dae-server port')
+ else:
+ dae_server_port = "3799"
+ config_data['authentication']['radiusopt'].update(
+ {
+ 'dae-srv' : {
+ 'ip-addr' : c.return_value('authentication radius-settings dae-server ip-address'),
+ 'port' : dae_server_port,
+ 'secret' : str(c.return_value('authentication radius-settings 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-settings rate-limit enable'):
+ if not c.exists('authentication radius-settings rate-limit attribute'):
+ config_data['authentication']['radiusopt']['shaper'] = {
+ 'attr' : 'Filter-Id'
+ }
+ else:
+ config_data['authentication']['radiusopt']['shaper'] = {
+ 'attr' : c.return_value('authentication radius-settings rate-limit attribute')
+ }
+ if c.exists('authentication radius-settings rate-limit vendor'):
+ config_data['authentication']['radiusopt']['shaper']['vendor'] = c.return_value('authentication radius-settings rate-limit vendor')
+
+ if c.exists('client-ip-pool'):
+ if c.exists('client-ip-pool start') and c.exists('client-ip-pool stop'):
+ config_data['client_ip_pool'] = c.return_value('client-ip-pool start') + '-' + re.search('[0-9]+$', c.return_value('client-ip-pool stop')).group(0)
+
+ if c.exists('client-ip-pool subnet'):
+ config_data['client_ip_subnets'] = c.return_values('client-ip-pool subnet')
+
+ if c.exists('mtu'):
+ config_data['mtu'] = c.return_value('mtu')
+
+ ### gateway address
+ if c.exists('outside-nexthop'):
+ config_data['outside_nexthop'] = c.return_value('outside-nexthop')
+
+ 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'):
+ config_data['authentication']['auth_proto'].append(auth_mods[proto])
+ else:
+ config_data['authentication']['auth_proto'] = ['auth_mschap_v2']
+
+ if c.exists('authentication mppe'):
+ config_data['authentication']['mppe'] = c.return_value('authentication mppe')
+
+ if c.exists('idle'):
+ config_data['idle_timeout'] = c.return_value('idle')
+
+ return config_data
+
+def verify(c):
+ if c == None:
+ return None
+
+ if c['authentication']['mode'] == 'local':
+ if not c['authentication']['local-users']:
+ raise ConfigError('l2tp-server authentication local-users required')
+ for usr in c['authentication']['local-users']:
+ if not c['authentication']['local-users'][usr]['passwd']:
+ raise ConfigError('user ' + usr + ' requires a password')
+
+ if c['authentication']['mode'] == 'radius':
+ if len(c['authentication']['radiussrv']) == 0:
+ raise ConfigError('radius server required')
+ for rsrv in c['authentication']['radiussrv']:
+ if c['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 c['client_ip_pool'] and not c['client_ip_subnets']:
+ raise ConfigError("set vpn l2tp remote-access client-ip-pool requires subnet or start/stop IP pool")
+
+ if not c['outside_nexthop']:
+ #raise ConfigError('set vpn l2tp remote-access outside-nexthop required')
+ print ("WARMING: set vpn l2tp remote-access outside-nexthop required")
+
+def generate(c):
+ if c == None:
+ return None
+
+ ### accel-cmd reload doesn't work so any change results in a restart of the daemon
+ try:
+ if os.cpu_count() == 1:
+ c['thread_cnt'] = 1
+ else:
+ c['thread_cnt'] = int(os.cpu_count()/2)
+ except KeyError:
+ if os.cpu_count() == 1:
+ c['thread_cnt'] = 1
+ else:
+ c['thread_cnt'] = int(os.cpu_count()/2)
+
+ tmpl = jinja2.Template(l2tp_config, trim_blocks=True)
+ config_text = tmpl.render(c)
+ open(l2tp_conf,'w').write(config_text)
+
+ if c['authentication']['local-users']:
+ write_chap_secrets(c)
+
+ return c
+
+def apply(c):
+ if c == None:
+ if os.path.exists(pidfile):
+ accel_cmd('shutdown hard')
+ if os.path.exists(pidfile):
+ os.remove(pidfile)
+ return None
+
+ if not os.path.exists(pidfile):
+ ret = subprocess.call(['/usr/sbin/accel-pppd','-c',l2tp_conf,'-p',pidfile,'-d'])
+ chk_con()
+ if ret !=0 and os.path.exists(pidfile):
+ os.remove(pidfile)
+ raise ConfigError('accel-pppd failed to start')
+ else:
+ ### if gw ip changes, only restart doesn't work
+ accel_cmd('restart')
+ sl.syslog(sl.LOG_NOTICE, "reloading config via daemon restart")
+
+if __name__ == '__main__':
+ try:
+ c = get_config()
+ verify(c)
+ generate(c)
+ apply(c)
+ except ConfigError as e:
+ print(e)
+ sys.exit(1)
diff --git a/src/conf_mode/ipsec-settings.py b/src/conf_mode/ipsec-settings.py
index 921f20491..8d25e7abd 100755
--- a/src/conf_mode/ipsec-settings.py
+++ b/src/conf_mode/ipsec-settings.py
@@ -16,16 +16,75 @@
#
#
+import sys
+import re
import os
import jinja2
+import syslog as sl
import vyos.config
import vyos.defaults
from vyos import ConfigError
+
+ra_conn_name = "remote-access"
charon_conf_file = "/etc/strongswan.d/charon.conf"
+ipsec_secrets_flie = "/etc/ipsec.secrets"
+ipsec_ra_conn_file = "/etc/ipsec.d/tunnels/"+ra_conn_name
+ipsec_conf_flie = "/etc/ipsec.conf"
+ca_cert_path = '/etc/ipsec.d/cacerts'
+server_cert_path = '/etc/ipsec.d/certs'
+server_key_path = '/etc/ipsec.d/private'
+delim_ipsec_l2tp_begin = "### VyOS L2TP VPN Begin ###"
+delim_ipsec_l2tp_end = "### VyOS L2TP VPN End ###"
+
+l2pt_ipsec_conf = '''
+{{delim_ipsec_l2tp_begin}}
+include {{ipsec_ra_conn_file}}
+{{delim_ipsec_l2tp_end}}
+'''
+
+l2pt_ipsec_secrets_conf = '''
+{{delim_ipsec_l2tp_begin}}
+{% if ipsec_l2tp_auth_mode == 'pre-shared-secret' %}
+{{outside_addr}} %any : PSK "{{ipsec_l2tp_secret}}"
+{% elif ipsec_l2tp_auth_mode == 'x509' %}
+: RSA {{server_key_file_copied}}
+{% endif%}
+{{delim_ipsec_l2tp_end}}
+'''
+l2tp_ipsec_ra_conn_conf = '''
+{{delim_ipsec_l2tp_begin}}
+conn {{ra_conn_name}}
+ type=transport
+ left={{outside_addr}}
+ leftsubnet=%dynamic[/1701]
+ rightsubnet=%dynamic
+ mark=%unique
+ auto=add
+ ike=aes256-sha1-modp1024,3des-sha1-modp1024,3des-sha1-modp1024!
+ dpddelay=15
+ dpdtimeout=45
+ dpdaction=clear
+ esp=aes256-sha1,3des-sha1!
+ rekey=no
+{% if ipsec_l2tp_auth_mode == 'pre-shared-secret' %}
+ authby=secret
+ leftauth=psk
+ rightauth=psk
+{% elif ipsec_l2tp_auth_mode == 'x509' %}
+ authby=rsasig
+ leftrsasigkey=%cert
+ rightrsasigkey=%cert
+ rightca=%same
+ leftcert={{server_cert_file_copied}}
+{% endif %}
+ ikelifetime={{ipsec_l2tp_ike_lifetime}}
+ keylife={{ipsec_l2tp_lifetime}}
+{{delim_ipsec_l2tp_end}}
+'''
def get_config():
config = vyos.config.Config()
@@ -34,10 +93,133 @@ def get_config():
if config.exists("vpn ipsec options disable-route-autoinstall"):
data["install_routes"] = "no"
+ if config.exists("vpn ipsec ipsec-interfaces interface"):
+ data["ipsec_interfaces"] = config.return_values("vpn ipsec ipsec-interfaces interface")
+
+ # Init config variables
+ data["delim_ipsec_l2tp_begin"] = delim_ipsec_l2tp_begin
+ data["delim_ipsec_l2tp_end"] = delim_ipsec_l2tp_end
+ data["ipsec_ra_conn_file"] = ipsec_ra_conn_file
+ data["ra_conn_name"] = ra_conn_name
+ # Get l2tp ipsec settings
+ data["ipsec_l2tp"] = False
+ conf_ipsec_command = "vpn l2tp remote-access ipsec-settings " #last space is useful
+ if config.exists(conf_ipsec_command):
+ data["ipsec_l2tp"] = True
+
+ # Authentication params
+ if config.exists(conf_ipsec_command + "authentication mode"):
+ data["ipsec_l2tp_auth_mode"] = config.return_value(conf_ipsec_command + "authentication mode")
+ if config.exists(conf_ipsec_command + "authentication pre-shared-secret"):
+ data["ipsec_l2tp_secret"] = config.return_value(conf_ipsec_command + "authentication pre-shared-secret")
+
+ # mode x509
+ if config.exists(conf_ipsec_command + "authentication x509 ca-cert-file"):
+ data["ipsec_l2tp_x509_ca_cert_file"] = config.return_value(conf_ipsec_command + "authentication x509 ca-cert-file")
+ if config.exists(conf_ipsec_command + "authentication x509 crl-file"):
+ data["ipsec_l2tp_x509_crl_file"] = config.return_value(conf_ipsec_command + "authentication x509 crl-file")
+ if config.exists(conf_ipsec_command + "authentication x509 server-cert-file"):
+ data["ipsec_l2tp_x509_server_cert_file"] = config.return_value(conf_ipsec_command + "authentication x509 server-cert-file")
+ data["server_cert_file_copied"] = server_cert_path+"/"+re.search('\w+(?:\.\w+)*$', config.return_value(conf_ipsec_command + "authentication x509 server-cert-file")).group(0)
+ if config.exists(conf_ipsec_command + "authentication x509 server-key-file"):
+ data["ipsec_l2tp_x509_server_key_file"] = config.return_value(conf_ipsec_command + "authentication x509 server-key-file")
+ data["server_key_file_copied"] = server_key_path+"/"+re.search('\w+(?:\.\w+)*$', config.return_value(conf_ipsec_command + "authentication x509 server-key-file")).group(0)
+ if config.exists(conf_ipsec_command + "authentication x509 server-key-password"):
+ data["ipsec_l2tp_x509_server_key_password"] = config.return_value(conf_ipsec_command + "authentication x509 server-key-password")
+
+ # Common l2tp ipsec params
+ if config.exists(conf_ipsec_command + "ike-lifetime"):
+ data["ipsec_l2tp_ike_lifetime"] = config.return_value(conf_ipsec_command + "ike-lifetime")
+ else:
+ data["ipsec_l2tp_ike_lifetime"] = "3600"
+
+ if config.exists(conf_ipsec_command + "lifetime"):
+ data["ipsec_l2tp_lifetime"] = config.return_value(conf_ipsec_command + "lifetime")
+ else:
+ data["ipsec_l2tp_lifetime"] = "3600"
+
+ if config.exists("vpn l2tp remote-access outside-address"):
+ data['outside_addr'] = config.return_value('vpn l2tp remote-access outside-address')
+
return data
+### ipsec secret l2tp
+def write_ipsec_secrets(c):
+ tmpl = jinja2.Template(l2pt_ipsec_secrets_conf, trim_blocks=True)
+ l2pt_ipsec_secrets_txt = tmpl.render(c)
+ old_umask = os.umask(0o077)
+ open(ipsec_secrets_flie,'w').write(l2pt_ipsec_secrets_txt)
+ os.umask(old_umask)
+ sl.syslog(sl.LOG_NOTICE, ipsec_secrets_flie + ' written')
+
+### ipsec remote access connection config
+def write_ipsec_ra_conn(c):
+ tmpl = jinja2.Template(l2tp_ipsec_ra_conn_conf, trim_blocks=True)
+ ipsec_ra_conn_txt = tmpl.render(c)
+ old_umask = os.umask(0o077)
+ open(ipsec_ra_conn_file,'w').write(ipsec_ra_conn_txt)
+ os.umask(old_umask)
+ sl.syslog(sl.LOG_NOTICE, ipsec_ra_conn_file + ' written')
+
+### Remove config from file by delimiter
+def remove_confs(delim_begin, delim_end, conf_file):
+ os.system("sed -i '/"+delim_begin+"/,/"+delim_end+"/d' "+conf_file)
+
+
+### Append "include /path/to/ra_conn" to ipsec conf file
+def append_ipsec_conf(c):
+ tmpl = jinja2.Template(l2pt_ipsec_conf, trim_blocks=True)
+ l2pt_ipsec_conf_txt = tmpl.render(c)
+ old_umask = os.umask(0o077)
+ open(ipsec_conf_flie,'a').write(l2pt_ipsec_conf_txt)
+ os.umask(old_umask)
+ sl.syslog(sl.LOG_NOTICE, ipsec_conf_flie + ' written')
+
+### Checking certificate storage and notice if certificate not in /config directory
+def check_cert_file_store(cert_name, file_path, dts_path):
+ if not re.search('^\/config\/.+', file_path):
+ print("Warning: \"" + file_path + "\" lies outside of /config/auth directory. It will not get preserved during image upgrade.")
+ #Checking file existence
+ if not os.path.isfile(file_path):
+ raise ConfigError("L2TP VPN configuration error: Invalid "+cert_name+" \""+file_path+"\"")
+ else:
+ ### Cpy file to /etc/ipsec.d/certs/ /etc/ipsec.d/cacerts/
+ # todo make check
+ ret = os.system('cp -f '+file_path+' '+dts_path)
+ if ret:
+ raise ConfigError("L2TP VPN configuration error: Cannot copy "+file_path)
+ else:
+ sl.syslog(sl.LOG_NOTICE, file_path + ' copied to '+dts_path)
+
def verify(data):
- pass
+ # l2tp ipsec check
+ if data["ipsec_l2tp"]:
+ # Checking dependecies for "authentication mode pre-shared-secret"
+ if data.get("ipsec_l2tp_auth_mode") == "pre-shared-secret":
+ if not data.get("ipsec_l2tp_secret"):
+ raise ConfigError("pre-shared-secret required")
+ if not data.get("outside_addr"):
+ raise ConfigError("outside-address not defined")
+
+ # Checking dependecies for "authentication mode x509"
+ if data.get("ipsec_l2tp_auth_mode") == "x509":
+ if not data.get("ipsec_l2tp_x509_server_key_file"):
+ raise ConfigError("L2TP VPN configuration error: \"server-key-file\" not defined.")
+ else:
+ check_cert_file_store("server-key-file", data['ipsec_l2tp_x509_server_key_file'], server_key_path)
+
+ if not data.get("ipsec_l2tp_x509_server_cert_file"):
+ raise ConfigError("L2TP VPN configuration error: \"server-cert-file\" not defined.")
+ else:
+ check_cert_file_store("server-cert-file", data['ipsec_l2tp_x509_server_cert_file'], server_cert_path)
+
+ if not data.get("ipsec_l2tp_x509_ca_cert_file"):
+ raise ConfigError("L2TP VPN configuration error: \"ca-cert-file\" must be defined for X.509")
+ else:
+ check_cert_file_store("ca-cert-file", data['ipsec_l2tp_x509_ca_cert_file'], ca_cert_path)
+
+ if not data.get('ipsec_interfaces'):
+ raise ConfigError("L2TP VPN configuration error: \"vpn ipsec ipsec-interfaces\" must be specified.")
def generate(data):
tmpl_path = os.path.join(vyos.defaults.directories["data"], "templates", "ipsec")
@@ -51,10 +233,21 @@ def generate(data):
with open(charon_conf_file, 'w') as f:
f.write(charon_conf)
+ if data["ipsec_l2tp"]:
+ remove_confs(delim_ipsec_l2tp_begin, delim_ipsec_l2tp_end, ipsec_conf_flie)
+ write_ipsec_secrets(data)
+ write_ipsec_ra_conn(data)
+ append_ipsec_conf(data)
+ else:
+ remove_confs(delim_ipsec_l2tp_begin, delim_ipsec_l2tp_end, ipsec_ra_conn_file)
+ remove_confs(delim_ipsec_l2tp_begin, delim_ipsec_l2tp_end, ipsec_secrets_flie)
+ remove_confs(delim_ipsec_l2tp_begin, delim_ipsec_l2tp_end, ipsec_conf_flie)
+
def apply(data):
# Do nothing
# StrongSWAN should only be restarted when actual tunnels are configured
- pass
+ # Restart ipsec for l2tp
+ os.system("ipsec restart >&/dev/null")
if __name__ == '__main__':
try:
diff --git a/src/op_mode/show_vpn_ra.py b/src/op_mode/show_vpn_ra.py
new file mode 100755
index 000000000..cf6119c2f
--- /dev/null
+++ b/src/op_mode/show_vpn_ra.py
@@ -0,0 +1,58 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2019 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import os
+import sys
+import re
+import subprocess
+# from subprocess import Popen, PIPE
+
+# chech connection to pptp and l2tp daemon
+def get_sessions():
+ absent_pptp = False
+ absent_l2tp = False
+ pptp_cmd = ["accel-cmd", "-p 2003", "show sessions"]
+ l2tp_cmd = ["accel-cmd", "-p 2004", "show sessions"]
+ err_pattern = "^Connection.+failed$"
+ # This value for chack only output header without sessions.
+ len_def_header = 170
+
+ # Check pptp
+ ret = subprocess.Popen(pptp_cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+ (output, err) = ret.communicate()
+ if not err and len(output.decode("utf-8")) > len_def_header and not re.search(err_pattern, output.decode("utf-8")):
+ print(output.decode("utf-8"))
+ else:
+ absent_pptp = True
+
+ # Check l2tp
+ ret = subprocess.Popen(l2tp_cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+ (output, err) = ret.communicate()
+ if not err and len(output.decode("utf-8")) > len_def_header and not re.search(err_pattern, output.decode("utf-8")):
+ print(output.decode("utf-8"))
+ else:
+ absent_l2tp = True
+
+ if absent_l2tp and absent_pptp:
+ print("No active remote access VPN sessions")
+
+
+def main():
+ get_sessions()
+
+
+if __name__ == '__main__':
+ main()