summaryrefslogtreecommitdiff
path: root/src/conf_mode
diff options
context:
space:
mode:
Diffstat (limited to 'src/conf_mode')
-rwxr-xr-xsrc/conf_mode/interfaces-vxlan.py21
-rwxr-xr-xsrc/conf_mode/ipsec-settings.py224
-rw-r--r--[-rwxr-xr-x]src/conf_mode/vpn_ipsec.py70
-rwxr-xr-xsrc/conf_mode/vpn_l2tp.py1
4 files changed, 66 insertions, 250 deletions
diff --git a/src/conf_mode/interfaces-vxlan.py b/src/conf_mode/interfaces-vxlan.py
index 8e6247a30..804f2d14f 100755
--- a/src/conf_mode/interfaces-vxlan.py
+++ b/src/conf_mode/interfaces-vxlan.py
@@ -25,7 +25,9 @@ from vyos.configverify import verify_address
from vyos.configverify import verify_bridge_delete
from vyos.configverify import verify_mtu_ipv6
from vyos.configverify import verify_source_interface
-from vyos.ifconfig import VXLANIf, Interface
+from vyos.ifconfig import Interface
+from vyos.ifconfig import VXLANIf
+from vyos.template import is_ipv6
from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -65,12 +67,19 @@ def verify(vxlan):
raise ConfigError('Must configure VNI for VXLAN')
if 'source_interface' in vxlan:
- # VXLAN adds a 50 byte overhead - we need to check the underlaying MTU
- # if our configured MTU is at least 50 bytes less
+ # VXLAN adds at least an overhead of 50 byte - we need to check the
+ # underlaying device if our VXLAN package is not going to be fragmented!
+ vxlan_overhead = 50
+ if 'source_address' in vxlan and is_ipv6(vxlan['source_address']):
+ # IPv6 adds an extra 20 bytes overhead because the IPv6 header is 20
+ # bytes larger than the IPv4 header - assuming no extra options are
+ # in use.
+ vxlan_overhead += 20
+
lower_mtu = Interface(vxlan['source_interface']).get_mtu()
- if lower_mtu < (int(vxlan['mtu']) + 50):
- raise ConfigError('VXLAN has a 50 byte overhead, underlaying device ' \
- f'MTU is to small ({lower_mtu} bytes)')
+ if lower_mtu < (int(vxlan['mtu']) + vxlan_overhead):
+ raise ConfigError(f'Underlaying device MTU is to small ({lower_mtu} '\
+ f'bytes) for VXLAN overhead ({vxlan_overhead} bytes!)')
verify_mtu_ipv6(vxlan)
verify_address(vxlan)
diff --git a/src/conf_mode/ipsec-settings.py b/src/conf_mode/ipsec-settings.py
deleted file mode 100755
index 0599bf101..000000000
--- a/src/conf_mode/ipsec-settings.py
+++ /dev/null
@@ -1,224 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2018-2020 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 re
-import os
-
-from time import sleep
-from sys import exit
-
-from vyos.config import Config
-from vyos import ConfigError
-from vyos.util import call
-from vyos.template import render
-
-from vyos import airbag
-airbag.enable()
-
-ra_conn_name = "remote-access"
-ipsec_secrets_file = "/etc/ipsec.secrets"
-ipsec_ra_conn_dir = "/etc/ipsec.d/tunnels/"
-ipsec_ra_conn_file = ipsec_ra_conn_dir + ra_conn_name
-ipsec_conf_file = "/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 ###"
-charon_pidfile = "/var/run/charon.pid"
-
-def get_config(config=None):
- if config:
- config = config
- else:
- config = Config()
-
- data = {}
- 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
-
-def write_ipsec_secrets(c):
- if c.get("ipsec_l2tp_auth_mode") == "pre-shared-secret":
- secret_txt = "{0}\n{1} %any : PSK \"{2}\"\n{3}\n".format(delim_ipsec_l2tp_begin, c['outside_addr'], c['ipsec_l2tp_secret'], delim_ipsec_l2tp_end)
- elif c.get("ipsec_l2tp_auth_mode") == "x509":
- secret_txt = "{0}\n: RSA {1}\n{2}\n".format(delim_ipsec_l2tp_begin, c['server_key_file_copied'], delim_ipsec_l2tp_end)
-
- old_umask = os.umask(0o077)
- with open(ipsec_secrets_file, 'a+') as f:
- f.write(secret_txt)
- os.umask(old_umask)
-
-def write_ipsec_conf(c):
- ipsec_confg_txt = "{0}\ninclude {1}\n{2}\n".format(delim_ipsec_l2tp_begin, ipsec_ra_conn_file, delim_ipsec_l2tp_end)
-
- old_umask = os.umask(0o077)
- with open(ipsec_conf_file, 'a+') as f:
- f.write(ipsec_confg_txt)
- os.umask(old_umask)
-
-### Remove config from file by delimiter
-def remove_confs(delim_begin, delim_end, conf_file):
- call("sed -i '/"+delim_begin+"/,/"+delim_end+"/d' "+conf_file)
-
-
-### 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 = call('cp -f '+file_path+' '+dts_path)
- if ret:
- raise ConfigError("L2TP VPN configuration error: Cannot copy "+file_path)
-
-def verify(data):
- # l2tp ipsec check
- if 'ipsec_l2tp' in data:
- # 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):
- if 'ipsec_l2tp' in data:
- remove_confs(delim_ipsec_l2tp_begin, delim_ipsec_l2tp_end, ipsec_secrets_file)
- # old_umask = os.umask(0o077)
- # render(ipsec_secrets_file, 'ipsec/ipsec.secrets.tmpl', data)
- # os.umask(old_umask)
- ## Use this method while IPSec CLI handler won't be overwritten to python
- write_ipsec_secrets(data)
-
- old_umask = os.umask(0o077)
-
- # Create tunnels directory if does not exist
- if not os.path.exists(ipsec_ra_conn_dir):
- os.makedirs(ipsec_ra_conn_dir)
-
- render(ipsec_ra_conn_file, 'ipsec/remote-access.tmpl', data)
- os.umask(old_umask)
-
- remove_confs(delim_ipsec_l2tp_begin, delim_ipsec_l2tp_end, ipsec_conf_file)
- # old_umask = os.umask(0o077)
- # render(ipsec_conf_file, 'ipsec/ipsec.conf.tmpl', data)
- # os.umask(old_umask)
- ## Use this method while IPSec CLI handler won't be overwritten to python
- write_ipsec_conf(data)
-
- else:
- if os.path.exists(ipsec_ra_conn_file):
- 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_file)
- remove_confs(delim_ipsec_l2tp_begin, delim_ipsec_l2tp_end, ipsec_conf_file)
-
-def restart_ipsec():
- call('ipsec restart >&/dev/null')
- # counter for apply swanctl config
- counter = 10
- while counter <= 10:
- if os.path.exists(charon_pidfile):
- call('swanctl -q >&/dev/null')
- break
- counter -=1
- sleep(1)
- if counter == 0:
- raise ConfigError('VPN configuration error: IPSec is not running.')
-
-def apply(data):
- # Restart IPSec daemon
- restart_ipsec()
-
-if __name__ == '__main__':
- try:
- c = get_config()
- verify(c)
- generate(c)
- apply(c)
- except ConfigError as e:
- print(e)
- exit(1)
diff --git a/src/conf_mode/vpn_ipsec.py b/src/conf_mode/vpn_ipsec.py
index 3fab8e868..a359361f3 100755..100644
--- a/src/conf_mode/vpn_ipsec.py
+++ b/src/conf_mode/vpn_ipsec.py
@@ -73,6 +73,7 @@ def get_config(config=None):
else:
conf = Config()
base = ['vpn', 'ipsec']
+ l2tp_base = ['vpn', 'l2tp', 'remote-access', 'ipsec-settings']
if not conf.exists(base):
return None
@@ -108,15 +109,22 @@ def get_config(config=None):
ipsec['dhcp_no_address'] = {}
ipsec['install_routes'] = 'no' if conf.exists(base + ["options", "disable-route-autoinstall"]) else default_install_routes
- ipsec['interface_change'] = leaf_node_changed(conf, base + ['ipsec-interfaces',
- 'interface'])
- ipsec['l2tp_exists'] = conf.exists(['vpn', 'l2tp', 'remote-access',
- 'ipsec-settings'])
+ ipsec['interface_change'] = leaf_node_changed(conf, base + ['interface'])
ipsec['nhrp_exists'] = conf.exists(['protocols', 'nhrp', 'tunnel'])
ipsec['pki'] = conf.get_config_dict(['pki'], key_mangling=('-', '_'),
get_first_key=True,
no_tag_node_value_mangle=True)
+ ipsec['l2tp'] = conf.get_config_dict(l2tp_base, key_mangling=('-', '_'),
+ get_first_key=True,
+ no_tag_node_value_mangle=True)
+ if ipsec['l2tp']:
+ l2tp_defaults = defaults(l2tp_base)
+ ipsec['l2tp'] = dict_merge(l2tp_defaults, ipsec['l2tp'])
+ ipsec['l2tp_outside_address'] = conf.return_value(['vpn', 'l2tp', 'remote-access', 'outside-address'])
+ ipsec['l2tp_ike_default'] = 'aes256-sha1-modp1024,3des-sha1-modp1024,3des-sha1-modp1024'
+ ipsec['l2tp_esp_default'] = 'aes256-sha1,3des-sha1'
+
return ipsec
def get_dhcp_address(iface):
@@ -165,14 +173,43 @@ def verify(ipsec):
if not ipsec:
return None
- if 'ipsec_interfaces' in ipsec and 'interface' in ipsec['ipsec_interfaces']:
- interfaces = ipsec['ipsec_interfaces']['interface']
- if isinstance(interfaces, str):
- interfaces = [interfaces]
-
- for ifname in interfaces:
+ if 'interfaces' in ipsec :
+ for ifname in ipsec['interface']:
verify_interface_exists(ifname)
+ if ipsec['l2tp']:
+ if 'esp_group' in ipsec['l2tp']:
+ if 'esp_group' not in ipsec or ipsec['l2tp']['esp_group'] not in ipsec['esp_group']:
+ raise ConfigError(f"Invalid esp-group on L2TP remote-access config")
+
+ if 'ike_group' in ipsec['l2tp']:
+ if 'ike_group' not in ipsec or ipsec['l2tp']['ike_group'] not in ipsec['ike_group']:
+ raise ConfigError(f"Invalid ike-group on L2TP remote-access config")
+
+ if 'authentication' not in ipsec['l2tp']:
+ raise ConfigError(f'Missing authentication settings on L2TP remote-access config')
+
+ if 'mode' not in ipsec['l2tp']['authentication']:
+ raise ConfigError(f'Missing authentication mode on L2TP remote-access config')
+
+ if not ipsec['l2tp_outside_address']:
+ raise ConfigError(f'Missing outside-address on L2TP remote-access config')
+
+ if ipsec['l2tp']['authentication']['mode'] == 'pre-shared-secret':
+ if 'pre_shared_secret' not in ipsec['l2tp']['authentication']:
+ raise ConfigError(f'Missing pre shared secret on L2TP remote-access config')
+
+ if ipsec['l2tp']['authentication']['mode'] == 'x509':
+ if 'x509' not in ipsec['l2tp']['authentication']:
+ raise ConfigError(f'Missing x509 settings on L2TP remote-access config')
+
+ x509 = ipsec['l2tp']['authentication']['x509']
+
+ if 'ca_certificate' not in x509 or 'certificate' not in x509:
+ raise ConfigError(f'Missing x509 certificates on L2TP remote-access config')
+
+ verify_pki_x509(ipsec['pki'], x509)
+
if 'profile' in ipsec:
for profile, profile_conf in ipsec['profile'].items():
if 'esp_group' in profile_conf:
@@ -389,6 +426,10 @@ def generate(ipsec):
if not os.path.exists(KEY_PATH):
os.mkdir(KEY_PATH, mode=0o700)
+ if ipsec['l2tp']:
+ if 'authentication' in ipsec['l2tp'] and 'x509' in ipsec['l2tp']['authentication']:
+ generate_pki_files_x509(ipsec['pki'], ipsec['l2tp']['authentication']['x509'])
+
if 'remote_access' in ipsec:
for rw, rw_conf in ipsec['remote_access'].items():
if 'authentication' in rw_conf and 'x509' in rw_conf['authentication']:
@@ -439,14 +480,6 @@ def generate(ipsec):
render(interface_conf, 'ipsec/interfaces_use.conf.tmpl', ipsec)
render(swanctl_conf, 'ipsec/swanctl.conf.tmpl', ipsec)
-def resync_l2tp(ipsec):
- if ipsec and not ipsec['l2tp_exists']:
- return
-
- tmp = run('/usr/libexec/vyos/conf_mode/ipsec-settings.py')
- if tmp > 0:
- print('ERROR: failed to reapply L2TP IPSec settings!')
-
def resync_nhrp(ipsec):
if ipsec and not ipsec['nhrp_exists']:
return
@@ -480,7 +513,6 @@ def apply(ipsec):
if wait_for_vici_socket():
call('sudo swanctl -q')
- resync_l2tp(ipsec)
resync_nhrp(ipsec)
if __name__ == '__main__':
diff --git a/src/conf_mode/vpn_l2tp.py b/src/conf_mode/vpn_l2tp.py
index e970d2ef5..9c52f77ca 100755
--- a/src/conf_mode/vpn_l2tp.py
+++ b/src/conf_mode/vpn_l2tp.py
@@ -20,7 +20,6 @@ import re
from copy import deepcopy
from stat import S_IRUSR, S_IWUSR, S_IRGRP
from sys import exit
-from time import sleep
from ipaddress import ip_network