diff options
Diffstat (limited to 'src/conf_mode')
-rwxr-xr-x | src/conf_mode/ipsec-settings.py | 222 | ||||
-rwxr-xr-x | src/conf_mode/vpn_ipsec.py | 59 |
2 files changed, 48 insertions, 233 deletions
diff --git a/src/conf_mode/ipsec-settings.py b/src/conf_mode/ipsec-settings.py deleted file mode 100755 index a9885f3d3..000000000 --- a/src/conf_mode/ipsec-settings.py +++ /dev/null @@ -1,222 +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 sys import exit - -from vyos.config import Config -from vyos import ConfigError -from vyos.util import call, wait_for_file_write_complete -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(): - try: - wait_for_file_write_complete(charon_pidfile, - pre_hook=(lambda: call('ipsec restart >&/dev/null')), - timeout=10) - - # Force configuration load - call('swanctl -q >&/dev/null') - - except OSError: - raise ConfigError('VPN configuration error: IPSec process did not start.') - -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..645108a8f 100755 --- 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 @@ -110,13 +111,21 @@ def get_config(config=None): 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['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): @@ -173,6 +182,39 @@ def verify(ipsec): for ifname in interfaces: 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 +431,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 +485,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 +518,6 @@ def apply(ipsec): if wait_for_vici_socket(): call('sudo swanctl -q') - resync_l2tp(ipsec) resync_nhrp(ipsec) if __name__ == '__main__': |