diff options
Diffstat (limited to 'src/migration-scripts')
-rwxr-xr-x | src/migration-scripts/https/2-to-3 | 86 | ||||
-rwxr-xr-x | src/migration-scripts/interfaces/19-to-20 | 130 | ||||
-rwxr-xr-x | src/migration-scripts/interfaces/20-to-21 | 130 | ||||
-rwxr-xr-x | src/migration-scripts/interfaces/22-to-23 | 369 | ||||
-rwxr-xr-x | src/migration-scripts/ipsec/5-to-6 | 11 | ||||
-rwxr-xr-x | src/migration-scripts/l2tp/3-to-4 | 169 | ||||
-rwxr-xr-x | src/migration-scripts/openconnect/0-to-1 | 136 | ||||
-rwxr-xr-x | src/migration-scripts/quagga/7-to-8 | 99 | ||||
-rwxr-xr-x | src/migration-scripts/quagga/8-to-9 | 114 | ||||
-rwxr-xr-x | src/migration-scripts/sstp/3-to-4 | 136 | ||||
-rwxr-xr-x | src/migration-scripts/vrf/0-to-1 | 10 | ||||
-rwxr-xr-x | src/migration-scripts/vrf/1-to-2 | 1 | ||||
-rwxr-xr-x | src/migration-scripts/vrf/2-to-3 | 144 |
13 files changed, 1306 insertions, 229 deletions
diff --git a/src/migration-scripts/https/2-to-3 b/src/migration-scripts/https/2-to-3 new file mode 100755 index 000000000..fa29fdd18 --- /dev/null +++ b/src/migration-scripts/https/2-to-3 @@ -0,0 +1,86 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 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/>. + +# * Migrate system signed certificate to use PKI + +import sys + +from vyos.configtree import ConfigTree +from vyos.pki import create_certificate +from vyos.pki import create_certificate_request +from vyos.pki import create_private_key +from vyos.pki import encode_certificate +from vyos.pki import encode_private_key + +if (len(sys.argv) < 2): + print("Must specify file name!") + sys.exit(1) + +file_name = sys.argv[1] + +with open(file_name, 'r') as f: + config_file = f.read() + +config = ConfigTree(config_file) + +base = ['service', 'https', 'certificates'] +pki_base = ['pki'] + +if not config.exists(base + ['system-generated-certificate']): + sys.exit(0) + +def wrapped_pem_to_config_value(pem): + out = [] + for line in pem.strip().split("\n"): + if not line or line.startswith("-----") or line[0] == '#': + continue + out.append(line) + return "".join(out) + +if not config.exists(pki_base + ['certificate']): + config.set(pki_base + ['certificate']) + config.set_tag(pki_base + ['certificate']) + +valid_days = 365 +if config.exists(base + ['system-generated-certificate', 'lifetime']): + valid_days = int(config.return_value(base + ['system-generated-certificate', 'lifetime'])) + +key = create_private_key('rsa', 2048) +subject = {'country': 'GB', 'state': 'N/A', 'locality': 'N/A', 'organization': 'VyOS', 'common_name': 'vyos'} +cert_req = create_certificate_request(subject, key, ['vyos']) +cert = create_certificate(cert_req, cert_req, key, valid_days) + +if cert: + cert_pem = encode_certificate(cert) + config.set(pki_base + ['certificate', 'generated_https', 'certificate'], value=wrapped_pem_to_config_value(cert_pem)) + +if key: + key_pem = encode_private_key(key) + config.set(pki_base + ['certificate', 'generated_https', 'private', 'key'], value=wrapped_pem_to_config_value(key_pem)) + +if cert and key: + config.set(base + ['certificate'], value='generated_https') +else: + print('Failed to migrate system-generated-certificate from https service') + +config.delete(base + ['system-generated-certificate']) + +try: + with open(file_name, 'w') as f: + f.write(config.to_string()) +except OSError as e: + print("Failed to save the modified config: {}".format(e)) + sys.exit(1) diff --git a/src/migration-scripts/interfaces/19-to-20 b/src/migration-scripts/interfaces/19-to-20 index 06e07572f..e96663e54 100755 --- a/src/migration-scripts/interfaces/19-to-20 +++ b/src/migration-scripts/interfaces/19-to-20 @@ -18,62 +18,6 @@ from sys import argv from sys import exit from vyos.configtree import ConfigTree -def migrate_ospf(config, path, interface): - path = path + ['ospf'] - if config.exists(path): - new_base = ['protocols', 'ospf', 'interface'] - config.set(new_base) - config.set_tag(new_base) - config.copy(path, new_base + [interface]) - config.delete(path) - - # if "ip ospf" was the only setting, we can clean out the empty - # ip node afterwards - if len(config.list_nodes(path[:-1])) == 0: - config.delete(path[:-1]) - -def migrate_ospfv3(config, path, interface): - path = path + ['ospfv3'] - if config.exists(path): - new_base = ['protocols', 'ospfv3', 'interface'] - config.set(new_base) - config.set_tag(new_base) - config.copy(path, new_base + [interface]) - config.delete(path) - - # if "ipv6 ospfv3" was the only setting, we can clean out the empty - # ip node afterwards - if len(config.list_nodes(path[:-1])) == 0: - config.delete(path[:-1]) - -def migrate_rip(config, path, interface): - path = path + ['rip'] - if config.exists(path): - new_base = ['protocols', 'rip', 'interface'] - config.set(new_base) - config.set_tag(new_base) - config.copy(path, new_base + [interface]) - config.delete(path) - - # if "ip rip" was the only setting, we can clean out the empty - # ip node afterwards - if len(config.list_nodes(path[:-1])) == 0: - config.delete(path[:-1]) - -def migrate_ripng(config, path, interface): - path = path + ['ripng'] - if config.exists(path): - new_base = ['protocols', 'ripng', 'interface'] - config.set(new_base) - config.set_tag(new_base) - config.copy(path, new_base + [interface]) - config.delete(path) - - # if "ipv6 ripng" was the only setting, we can clean out the empty - # ip node afterwards - if len(config.list_nodes(path[:-1])) == 0: - config.delete(path[:-1]) - if __name__ == '__main__': if (len(argv) < 1): print("Must specify file name!") @@ -85,57 +29,29 @@ if __name__ == '__main__': config = ConfigTree(config_file) - # - # Migrate "interface ethernet eth0 ip ospf" to "protocols ospf interface eth0" - # - for type in config.list_nodes(['interfaces']): - for interface in config.list_nodes(['interfaces', type]): - ip_base = ['interfaces', type, interface, 'ip'] - ipv6_base = ['interfaces', type, interface, 'ipv6'] - migrate_rip(config, ip_base, interface) - migrate_ripng(config, ipv6_base, interface) - migrate_ospf(config, ip_base, interface) - migrate_ospfv3(config, ipv6_base, interface) - - vif_path = ['interfaces', type, interface, 'vif'] - if config.exists(vif_path): - for vif in config.list_nodes(vif_path): - vif_ip_base = vif_path + [vif, 'ip'] - vif_ipv6_base = vif_path + [vif, 'ipv6'] - ifname = f'{interface}.{vif}' - - migrate_rip(config, vif_ip_base, ifname) - migrate_ripng(config, vif_ipv6_base, ifname) - migrate_ospf(config, vif_ip_base, ifname) - migrate_ospfv3(config, vif_ipv6_base, ifname) - - - vif_s_path = ['interfaces', type, interface, 'vif-s'] - if config.exists(vif_s_path): - for vif_s in config.list_nodes(vif_s_path): - vif_s_ip_base = vif_s_path + [vif_s, 'ip'] - vif_s_ipv6_base = vif_s_path + [vif_s, 'ipv6'] - - # vif-c interfaces MUST be migrated before their parent vif-s - # interface as the migrate_*() functions delete the path! - vif_c_path = ['interfaces', type, interface, 'vif-s', vif_s, 'vif-c'] - if config.exists(vif_c_path): - for vif_c in config.list_nodes(vif_c_path): - vif_c_ip_base = vif_c_path + [vif_c, 'ip'] - vif_c_ipv6_base = vif_c_path + [vif_c, 'ipv6'] - ifname = f'{interface}.{vif_s}.{vif_c}' - - migrate_rip(config, vif_c_ip_base, ifname) - migrate_ripng(config, vif_c_ipv6_base, ifname) - migrate_ospf(config, vif_c_ip_base, ifname) - migrate_ospfv3(config, vif_c_ipv6_base, ifname) - - - ifname = f'{interface}.{vif_s}' - migrate_rip(config, vif_s_ip_base, ifname) - migrate_ripng(config, vif_s_ipv6_base, ifname) - migrate_ospf(config, vif_s_ip_base, ifname) - migrate_ospfv3(config, vif_s_ipv6_base, ifname) + for type in ['tunnel', 'l2tpv3']: + base = ['interfaces', type] + if not config.exists(base): + # Nothing to do + continue + + for interface in config.list_nodes(base): + # Migrate "interface tunnel <tunX> encapsulation gre-bridge" to gretap + encap_path = base + [interface, 'encapsulation'] + if type == 'tunnel' and config.exists(encap_path): + tmp = config.return_value(encap_path) + if tmp == 'gre-bridge': + config.set(encap_path, value='gretap') + + # Migrate "interface tunnel|l2tpv3 <interface> local-ip" to source-address + # Migrate "interface tunnel|l2tpv3 <interface> remote-ip" to remote + local_ip_path = base + [interface, 'local-ip'] + if config.exists(local_ip_path): + config.rename(local_ip_path, 'source-address') + + remote_ip_path = base + [interface, 'remote-ip'] + if config.exists(remote_ip_path): + config.rename(remote_ip_path, 'remote') try: with open(file_name, 'w') as f: diff --git a/src/migration-scripts/interfaces/20-to-21 b/src/migration-scripts/interfaces/20-to-21 index e96663e54..06e07572f 100755 --- a/src/migration-scripts/interfaces/20-to-21 +++ b/src/migration-scripts/interfaces/20-to-21 @@ -18,6 +18,62 @@ from sys import argv from sys import exit from vyos.configtree import ConfigTree +def migrate_ospf(config, path, interface): + path = path + ['ospf'] + if config.exists(path): + new_base = ['protocols', 'ospf', 'interface'] + config.set(new_base) + config.set_tag(new_base) + config.copy(path, new_base + [interface]) + config.delete(path) + + # if "ip ospf" was the only setting, we can clean out the empty + # ip node afterwards + if len(config.list_nodes(path[:-1])) == 0: + config.delete(path[:-1]) + +def migrate_ospfv3(config, path, interface): + path = path + ['ospfv3'] + if config.exists(path): + new_base = ['protocols', 'ospfv3', 'interface'] + config.set(new_base) + config.set_tag(new_base) + config.copy(path, new_base + [interface]) + config.delete(path) + + # if "ipv6 ospfv3" was the only setting, we can clean out the empty + # ip node afterwards + if len(config.list_nodes(path[:-1])) == 0: + config.delete(path[:-1]) + +def migrate_rip(config, path, interface): + path = path + ['rip'] + if config.exists(path): + new_base = ['protocols', 'rip', 'interface'] + config.set(new_base) + config.set_tag(new_base) + config.copy(path, new_base + [interface]) + config.delete(path) + + # if "ip rip" was the only setting, we can clean out the empty + # ip node afterwards + if len(config.list_nodes(path[:-1])) == 0: + config.delete(path[:-1]) + +def migrate_ripng(config, path, interface): + path = path + ['ripng'] + if config.exists(path): + new_base = ['protocols', 'ripng', 'interface'] + config.set(new_base) + config.set_tag(new_base) + config.copy(path, new_base + [interface]) + config.delete(path) + + # if "ipv6 ripng" was the only setting, we can clean out the empty + # ip node afterwards + if len(config.list_nodes(path[:-1])) == 0: + config.delete(path[:-1]) + if __name__ == '__main__': if (len(argv) < 1): print("Must specify file name!") @@ -29,29 +85,57 @@ if __name__ == '__main__': config = ConfigTree(config_file) - for type in ['tunnel', 'l2tpv3']: - base = ['interfaces', type] - if not config.exists(base): - # Nothing to do - continue - - for interface in config.list_nodes(base): - # Migrate "interface tunnel <tunX> encapsulation gre-bridge" to gretap - encap_path = base + [interface, 'encapsulation'] - if type == 'tunnel' and config.exists(encap_path): - tmp = config.return_value(encap_path) - if tmp == 'gre-bridge': - config.set(encap_path, value='gretap') - - # Migrate "interface tunnel|l2tpv3 <interface> local-ip" to source-address - # Migrate "interface tunnel|l2tpv3 <interface> remote-ip" to remote - local_ip_path = base + [interface, 'local-ip'] - if config.exists(local_ip_path): - config.rename(local_ip_path, 'source-address') - - remote_ip_path = base + [interface, 'remote-ip'] - if config.exists(remote_ip_path): - config.rename(remote_ip_path, 'remote') + # + # Migrate "interface ethernet eth0 ip ospf" to "protocols ospf interface eth0" + # + for type in config.list_nodes(['interfaces']): + for interface in config.list_nodes(['interfaces', type]): + ip_base = ['interfaces', type, interface, 'ip'] + ipv6_base = ['interfaces', type, interface, 'ipv6'] + migrate_rip(config, ip_base, interface) + migrate_ripng(config, ipv6_base, interface) + migrate_ospf(config, ip_base, interface) + migrate_ospfv3(config, ipv6_base, interface) + + vif_path = ['interfaces', type, interface, 'vif'] + if config.exists(vif_path): + for vif in config.list_nodes(vif_path): + vif_ip_base = vif_path + [vif, 'ip'] + vif_ipv6_base = vif_path + [vif, 'ipv6'] + ifname = f'{interface}.{vif}' + + migrate_rip(config, vif_ip_base, ifname) + migrate_ripng(config, vif_ipv6_base, ifname) + migrate_ospf(config, vif_ip_base, ifname) + migrate_ospfv3(config, vif_ipv6_base, ifname) + + + vif_s_path = ['interfaces', type, interface, 'vif-s'] + if config.exists(vif_s_path): + for vif_s in config.list_nodes(vif_s_path): + vif_s_ip_base = vif_s_path + [vif_s, 'ip'] + vif_s_ipv6_base = vif_s_path + [vif_s, 'ipv6'] + + # vif-c interfaces MUST be migrated before their parent vif-s + # interface as the migrate_*() functions delete the path! + vif_c_path = ['interfaces', type, interface, 'vif-s', vif_s, 'vif-c'] + if config.exists(vif_c_path): + for vif_c in config.list_nodes(vif_c_path): + vif_c_ip_base = vif_c_path + [vif_c, 'ip'] + vif_c_ipv6_base = vif_c_path + [vif_c, 'ipv6'] + ifname = f'{interface}.{vif_s}.{vif_c}' + + migrate_rip(config, vif_c_ip_base, ifname) + migrate_ripng(config, vif_c_ipv6_base, ifname) + migrate_ospf(config, vif_c_ip_base, ifname) + migrate_ospfv3(config, vif_c_ipv6_base, ifname) + + + ifname = f'{interface}.{vif_s}' + migrate_rip(config, vif_s_ip_base, ifname) + migrate_ripng(config, vif_s_ipv6_base, ifname) + migrate_ospf(config, vif_s_ip_base, ifname) + migrate_ospfv3(config, vif_s_ipv6_base, ifname) try: with open(file_name, 'w') as f: diff --git a/src/migration-scripts/interfaces/22-to-23 b/src/migration-scripts/interfaces/22-to-23 new file mode 100755 index 000000000..93ce9215f --- /dev/null +++ b/src/migration-scripts/interfaces/22-to-23 @@ -0,0 +1,369 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 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/>. + +# Migrate Wireguard to store keys in CLI +# Migrate EAPoL to PKI configuration + +import os +import sys +from vyos.configtree import ConfigTree +from vyos.pki import load_certificate +from vyos.pki import load_crl +from vyos.pki import load_dh_parameters +from vyos.pki import load_private_key +from vyos.pki import encode_certificate +from vyos.pki import encode_dh_parameters +from vyos.pki import encode_private_key +from vyos.util import run + +def wrapped_pem_to_config_value(pem): + out = [] + for line in pem.strip().split("\n"): + if not line or line.startswith("-----") or line[0] == '#': + continue + out.append(line) + return "".join(out) + +def read_file_for_pki(config_auth_path): + full_path = os.path.join(AUTH_DIR, config_auth_path) + output = None + + if os.path.isfile(full_path): + if not os.access(full_path, os.R_OK): + run(f'sudo chmod 644 {full_path}') + + with open(full_path, 'r') as f: + output = f.read() + + return output + +if (len(sys.argv) < 1): + print("Must specify file name!") + sys.exit(1) + +file_name = sys.argv[1] + +with open(file_name, 'r') as f: + config_file = f.read() + +config = ConfigTree(config_file) + +AUTH_DIR = '/config/auth' +pki_base = ['pki'] + +# OpenVPN +base = ['interfaces', 'openvpn'] + +if config.exists(base): + for interface in config.list_nodes(base): + x509_base = base + [interface, 'tls'] + pki_name = f'openvpn_{interface}' + + if config.exists(base + [interface, 'shared-secret-key-file']): + if not config.exists(pki_base + ['openvpn', 'shared-secret']): + config.set(pki_base + ['openvpn', 'shared-secret']) + config.set_tag(pki_base + ['openvpn', 'shared-secret']) + + key_file = config.return_value(base + [interface, 'shared-secret-key-file']) + key = read_file_for_pki(key_file) + key_pki_name = f'{pki_name}_shared' + + if key: + config.set(pki_base + ['openvpn', 'shared-secret', key_pki_name, 'key'], value=wrapped_pem_to_config_value(key)) + config.set(pki_base + ['openvpn', 'shared-secret', key_pki_name, 'version'], value='1') + config.set(base + [interface, 'shared-secret-key'], value=key_pki_name) + else: + print(f'Failed to migrate shared-secret-key on openvpn interface {interface}') + + config.delete(base + [interface, 'shared-secret-key-file']) + + if not config.exists(base + [interface, 'tls']): + continue + + if config.exists(base + [interface, 'tls', 'auth-file']): + if not config.exists(pki_base + ['openvpn', 'shared-secret']): + config.set(pki_base + ['openvpn', 'shared-secret']) + config.set_tag(pki_base + ['openvpn', 'shared-secret']) + + key_file = config.return_value(base + [interface, 'tls', 'auth-file']) + key = read_file_for_pki(key_file) + key_pki_name = f'{pki_name}_auth' + + if key: + config.set(pki_base + ['openvpn', 'shared-secret', key_pki_name, 'key'], value=wrapped_pem_to_config_value(key)) + config.set(pki_base + ['openvpn', 'shared-secret', key_pki_name, 'version'], value='1') + config.set(base + [interface, 'tls', 'auth-key'], value=key_pki_name) + else: + print(f'Failed to migrate auth-key on openvpn interface {interface}') + + config.delete(base + [interface, 'tls', 'auth-file']) + + if config.exists(base + [interface, 'tls', 'crypt-file']): + if not config.exists(pki_base + ['openvpn', 'shared-secret']): + config.set(pki_base + ['openvpn', 'shared-secret']) + config.set_tag(pki_base + ['openvpn', 'shared-secret']) + + key_file = config.return_value(base + [interface, 'tls', 'crypt-file']) + key = read_file_for_pki(key_file) + key_pki_name = f'{pki_name}_crypt' + + if key: + config.set(pki_base + ['openvpn', 'shared-secret', key_pki_name, 'key'], value=wrapped_pem_to_config_value(key)) + config.set(pki_base + ['openvpn', 'shared-secret', key_pki_name, 'version'], value='1') + config.set(base + [interface, 'tls', 'crypt-key'], value=key_pki_name) + else: + print(f'Failed to migrate crypt-key on openvpn interface {interface}') + + config.delete(base + [interface, 'tls', 'crypt-file']) + + if config.exists(x509_base + ['ca-cert-file']): + if not config.exists(pki_base + ['ca']): + config.set(pki_base + ['ca']) + config.set_tag(pki_base + ['ca']) + + cert_file = config.return_value(x509_base + ['ca-cert-file']) + cert_path = os.path.join(AUTH_DIR, cert_file) + cert = None + + if os.path.isfile(cert_path): + if not os.access(cert_path, os.R_OK): + run(f'sudo chmod 644 {cert_path}') + + with open(cert_path, 'r') as f: + cert_data = f.read() + cert = load_certificate(cert_data, wrap_tags=False) + + if cert: + cert_pem = encode_certificate(cert) + config.set(pki_base + ['ca', pki_name, 'certificate'], value=wrapped_pem_to_config_value(cert_pem)) + config.set(x509_base + ['ca-certificate'], value=pki_name) + else: + print(f'Failed to migrate CA certificate on openvpn interface {interface}') + + config.delete(x509_base + ['ca-cert-file']) + + if config.exists(x509_base + ['crl-file']): + if not config.exists(pki_base + ['ca']): + config.set(pki_base + ['ca']) + config.set_tag(pki_base + ['ca']) + + crl_file = config.return_value(x509_base + ['crl-file']) + crl_path = os.path.join(AUTH_DIR, crl_file) + crl = None + + if os.path.isfile(crl_path): + if not os.access(crl_path, os.R_OK): + run(f'sudo chmod 644 {crl_path}') + + with open(crl_path, 'r') as f: + crl_data = f.read() + crl = load_crl(crl_data, wrap_tags=False) + + if crl: + crl_pem = encode_certificate(crl) + config.set(pki_base + ['ca', pki_name, 'crl'], value=wrapped_pem_to_config_value(crl_pem)) + else: + print(f'Failed to migrate CRL on openvpn interface {interface}') + + config.delete(x509_base + ['crl-file']) + + if config.exists(x509_base + ['cert-file']): + if not config.exists(pki_base + ['certificate']): + config.set(pki_base + ['certificate']) + config.set_tag(pki_base + ['certificate']) + + cert_file = config.return_value(x509_base + ['cert-file']) + cert_path = os.path.join(AUTH_DIR, cert_file) + cert = None + + if os.path.isfile(cert_path): + if not os.access(cert_path, os.R_OK): + run(f'sudo chmod 644 {cert_path}') + + with open(cert_path, 'r') as f: + cert_data = f.read() + cert = load_certificate(cert_data, wrap_tags=False) + + if cert: + cert_pem = encode_certificate(cert) + config.set(pki_base + ['certificate', pki_name, 'certificate'], value=wrapped_pem_to_config_value(cert_pem)) + config.set(x509_base + ['certificate'], value=pki_name) + else: + print(f'Failed to migrate certificate on openvpn interface {interface}') + + config.delete(x509_base + ['cert-file']) + + if config.exists(x509_base + ['key-file']): + key_file = config.return_value(x509_base + ['key-file']) + key_path = os.path.join(AUTH_DIR, key_file) + key = None + + if os.path.isfile(key_path): + if not os.access(key_path, os.R_OK): + run(f'sudo chmod 644 {key_path}') + + with open(key_path, 'r') as f: + key_data = f.read() + key = load_private_key(key_data, passphrase=None, wrap_tags=False) + + if key: + key_pem = encode_private_key(key, passphrase=None) + config.set(pki_base + ['certificate', pki_name, 'private', 'key'], value=wrapped_pem_to_config_value(key_pem)) + else: + print(f'Failed to migrate private key on openvpn interface {interface}') + + config.delete(x509_base + ['key-file']) + + if config.exists(x509_base + ['dh-file']): + if not config.exists(pki_base + ['dh']): + config.set(pki_base + ['dh']) + config.set_tag(pki_base + ['dh']) + + dh_file = config.return_value(x509_base + ['dh-file']) + dh_path = os.path.join(AUTH_DIR, dh_file) + dh = None + + if os.path.isfile(dh_path): + if not os.access(dh_path, os.R_OK): + run(f'sudo chmod 644 {dh_path}') + + with open(dh_path, 'r') as f: + dh_data = f.read() + dh = load_dh_parameters(dh_data, wrap_tags=False) + + if dh: + dh_pem = encode_dh_parameters(dh) + config.set(pki_base + ['dh', pki_name, 'parameters'], value=wrapped_pem_to_config_value(dh_pem)) + config.set(x509_base + ['dh-params'], value=pki_name) + else: + print(f'Failed to migrate DH parameters on openvpn interface {interface}') + + config.delete(x509_base + ['dh-file']) + +# Wireguard +base = ['interfaces', 'wireguard'] + +if config.exists(base): + for interface in config.list_nodes(base): + private_key_path = base + [interface, 'private-key'] + + key_file = 'default' + if config.exists(private_key_path): + key_file = config.return_value(private_key_path) + + full_key_path = f'/config/auth/wireguard/{key_file}/private.key' + + if not os.path.exists(full_key_path): + print(f'Could not find wireguard private key for migration on interface "{interface}"') + continue + + with open(full_key_path, 'r') as f: + key_data = f.read().strip() + config.set(private_key_path, value=key_data) + + for peer in config.list_nodes(base + [interface, 'peer']): + config.rename(base + [interface, 'peer', peer, 'pubkey'], 'public-key') + +# Ethernet EAPoL +base = ['interfaces', 'ethernet'] + +if config.exists(base): + for interface in config.list_nodes(base): + if not config.exists(base + [interface, 'eapol']): + continue + + x509_base = base + [interface, 'eapol'] + pki_name = f'eapol_{interface}' + + if config.exists(x509_base + ['ca-cert-file']): + if not config.exists(pki_base + ['ca']): + config.set(pki_base + ['ca']) + config.set_tag(pki_base + ['ca']) + + cert_file = config.return_value(x509_base + ['ca-cert-file']) + cert_path = os.path.join(AUTH_DIR, cert_file) + cert = None + + if os.path.isfile(cert_path): + if not os.access(cert_path, os.R_OK): + run(f'sudo chmod 644 {cert_path}') + + with open(cert_path, 'r') as f: + cert_data = f.read() + cert = load_certificate(cert_data, wrap_tags=False) + + if cert: + cert_pem = encode_certificate(cert) + config.set(pki_base + ['ca', pki_name, 'certificate'], value=wrapped_pem_to_config_value(cert_pem)) + config.set(x509_base + ['ca-certificate'], value=pki_name) + else: + print(f'Failed to migrate CA certificate on eapol config for interface {interface}') + + config.delete(x509_base + ['ca-cert-file']) + + if config.exists(x509_base + ['cert-file']): + if not config.exists(pki_base + ['certificate']): + config.set(pki_base + ['certificate']) + config.set_tag(pki_base + ['certificate']) + + cert_file = config.return_value(x509_base + ['cert-file']) + cert_path = os.path.join(AUTH_DIR, cert_file) + cert = None + + if os.path.isfile(cert_path): + if not os.access(cert_path, os.R_OK): + run(f'sudo chmod 644 {cert_path}') + + with open(cert_path, 'r') as f: + cert_data = f.read() + cert = load_certificate(cert_data, wrap_tags=False) + + if cert: + cert_pem = encode_certificate(cert) + config.set(pki_base + ['certificate', pki_name, 'certificate'], value=wrapped_pem_to_config_value(cert_pem)) + config.set(x509_base + ['certificate'], value=pki_name) + else: + print(f'Failed to migrate certificate on eapol config for interface {interface}') + + config.delete(x509_base + ['cert-file']) + + if config.exists(x509_base + ['key-file']): + key_file = config.return_value(x509_base + ['key-file']) + key_path = os.path.join(AUTH_DIR, key_file) + key = None + + if os.path.isfile(key_path): + if not os.access(key_path, os.R_OK): + run(f'sudo chmod 644 {key_path}') + + with open(key_path, 'r') as f: + key_data = f.read() + key = load_private_key(key_data, passphrase=None, wrap_tags=False) + + if key: + key_pem = encode_private_key(key, passphrase=None) + config.set(pki_base + ['certificate', pki_name, 'private', 'key'], value=wrapped_pem_to_config_value(key_pem)) + else: + print(f'Failed to migrate private key on eapol config for interface {interface}') + + config.delete(x509_base + ['key-file']) + +try: + with open(file_name, 'w') as f: + f.write(config.to_string()) +except OSError as e: + print("Failed to save the modified config: {}".format(e)) + sys.exit(1) diff --git a/src/migration-scripts/ipsec/5-to-6 b/src/migration-scripts/ipsec/5-to-6 index ba5ce0fca..e9adee01b 100755 --- a/src/migration-scripts/ipsec/5-to-6 +++ b/src/migration-scripts/ipsec/5-to-6 @@ -74,6 +74,17 @@ log_mode = log + ['log-modes'] if config.exists(log_mode): config.rename(log_mode, 'subsystem') +# Rename "ipsec-interfaces interface" to "interface" +base_interfaces = base + ['ipsec-interfaces', 'interface'] +if config.exists(base_interfaces): + config.copy(base_interfaces, base + ['interface']) + config.delete(base_interfaces) + +# Remove deprecated "auto-update" option +tmp = base + ['auto-update'] +if config.exists(tmp): + config.delete(tmp) + try: with open(file_name, 'w') as f: f.write(config.to_string()) diff --git a/src/migration-scripts/l2tp/3-to-4 b/src/migration-scripts/l2tp/3-to-4 new file mode 100755 index 000000000..18eabadec --- /dev/null +++ b/src/migration-scripts/l2tp/3-to-4 @@ -0,0 +1,169 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 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/>. + +# - remove primary/secondary identifier from nameserver +# - TODO: remove radius server req-limit + +import os + +from sys import argv +from sys import exit +from vyos.configtree import ConfigTree +from vyos.pki import load_certificate +from vyos.pki import load_crl +from vyos.pki import load_private_key +from vyos.pki import encode_certificate +from vyos.pki import encode_private_key +from vyos.util import run + +if (len(argv) < 1): + print("Must specify file name!") + exit(1) + +file_name = argv[1] + +with open(file_name, 'r') as f: + config_file = f.read() + +config = ConfigTree(config_file) +base = ['vpn', 'l2tp', 'remote-access', 'ipsec-settings'] +pki_base = ['pki'] + +if not config.exists(base): + exit(0) + +AUTH_DIR = '/config/auth' + +def wrapped_pem_to_config_value(pem): + return "".join(pem.strip().split("\n")[1:-1]) + +if not config.exists(base + ['authentication', 'x509']): + exit(0) + +x509_base = base + ['authentication', 'x509'] +pki_name = 'l2tp_remote_access' + +if not config.exists(pki_base + ['ca']): + config.set(pki_base + ['ca']) + config.set_tag(pki_base + ['ca']) + +if not config.exists(pki_base + ['certificate']): + config.set(pki_base + ['certificate']) + config.set_tag(pki_base + ['certificate']) + +if config.exists(x509_base + ['ca-cert-file']): + cert_file = config.return_value(x509_base + ['ca-cert-file']) + cert_path = os.path.join(AUTH_DIR, cert_file) + cert = None + + if os.path.isfile(cert_path): + if not os.access(cert_path, os.R_OK): + run(f'sudo chmod 644 {cert_path}') + + with open(cert_path, 'r') as f: + cert_data = f.read() + cert = load_certificate(cert_data, wrap_tags=False) + + if cert: + cert_pem = encode_certificate(cert) + config.set(pki_base + ['ca', pki_name, 'certificate'], value=wrapped_pem_to_config_value(cert_pem)) + config.set(x509_base + ['ca-certificate'], value=pki_name) + else: + print(f'Failed to migrate CA certificate on l2tp remote-access config') + + config.delete(x509_base + ['ca-cert-file']) + +if config.exists(x509_base + ['crl-file']): + crl_file = config.return_value(x509_base + ['crl-file']) + crl_path = os.path.join(AUTH_DIR, crl_file) + crl = None + + if os.path.isfile(crl_path): + if not os.access(crl_path, os.R_OK): + run(f'sudo chmod 644 {crl_path}') + + with open(crl_path, 'r') as f: + crl_data = f.read() + crl = load_certificate(crl_data, wrap_tags=False) + + if crl: + crl_pem = encode_certificate(crl) + config.set(pki_base + ['ca', pki_name, 'crl'], value=wrapped_pem_to_config_value(crl_pem)) + else: + print(f'Failed to migrate CRL on l2tp remote-access config') + + config.delete(x509_base + ['crl-file']) + +if config.exists(x509_base + ['server-cert-file']): + cert_file = config.return_value(x509_base + ['server-cert-file']) + cert_path = os.path.join(AUTH_DIR, cert_file) + cert = None + + if os.path.isfile(cert_path): + if not os.access(cert_path, os.R_OK): + run(f'sudo chmod 644 {cert_path}') + + with open(cert_path, 'r') as f: + cert_data = f.read() + cert = load_certificate(cert_data, wrap_tags=False) + + if cert: + cert_pem = encode_certificate(cert) + config.set(pki_base + ['certificate', pki_name, 'certificate'], value=wrapped_pem_to_config_value(cert_pem)) + config.set(x509_base + ['certificate'], value=pki_name) + else: + print(f'Failed to migrate certificate on l2tp remote-access config') + + config.delete(x509_base + ['server-cert-file']) + +if config.exists(x509_base + ['server-key-file']): + key_file = config.return_value(x509_base + ['server-key-file']) + key_passphrase = None + + if config.exists(x509_base + ['server-key-password']): + key_passphrase = config.return_value(x509_base + ['server-key-password']) + + key_path = os.path.join(AUTH_DIR, key_file) + key = None + + if os.path.isfile(key_path): + if not os.access(key_path, os.R_OK): + run(f'sudo chmod 644 {key_path}') + + with open(key_path, 'r') as f: + key_data = f.read() + key = load_private_key(key_data, passphrase=key_passphrase, wrap_tags=False) + + if key: + key_pem = encode_private_key(key, passphrase=key_passphrase) + config.set(pki_base + ['certificate', pki_name, 'private', 'key'], value=wrapped_pem_to_config_value(key_pem)) + + if key_passphrase: + config.set(pki_base + ['certificate', pki_name, 'private', 'password-protected']) + config.set(x509_base + ['private-key-passphrase'], value=key_passphrase) + else: + print(f'Failed to migrate private key on l2tp remote-access config') + + config.delete(x509_base + ['server-key-file']) + if config.exists(x509_base + ['server-key-password']): + config.delete(x509_base + ['server-key-password']) + +try: + with open(file_name, 'w') as f: + f.write(config.to_string()) +except OSError as e: + print("Failed to save the modified config: {}".format(e)) + exit(1) diff --git a/src/migration-scripts/openconnect/0-to-1 b/src/migration-scripts/openconnect/0-to-1 new file mode 100755 index 000000000..83cd09143 --- /dev/null +++ b/src/migration-scripts/openconnect/0-to-1 @@ -0,0 +1,136 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 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/>. + +# - Update SSL to use PKI configuration + +import os + +from sys import argv +from sys import exit +from vyos.configtree import ConfigTree +from vyos.pki import load_certificate +from vyos.pki import load_crl +from vyos.pki import load_private_key +from vyos.pki import encode_certificate +from vyos.pki import encode_private_key +from vyos.util import run + +if (len(argv) < 1): + print("Must specify file name!") + exit(1) + +file_name = argv[1] + +with open(file_name, 'r') as f: + config_file = f.read() + +config = ConfigTree(config_file) +base = ['vpn', 'openconnect'] +pki_base = ['pki'] + +if not config.exists(base): + exit(0) + +AUTH_DIR = '/config/auth' + +def wrapped_pem_to_config_value(pem): + return "".join(pem.strip().split("\n")[1:-1]) + +if not config.exists(base + ['ssl']): + exit(0) + +x509_base = base + ['ssl'] +pki_name = 'openconnect' + +if not config.exists(pki_base + ['ca']): + config.set(pki_base + ['ca']) + config.set_tag(pki_base + ['ca']) + +if not config.exists(pki_base + ['certificate']): + config.set(pki_base + ['certificate']) + config.set_tag(pki_base + ['certificate']) + +if config.exists(x509_base + ['ca-cert-file']): + cert_file = config.return_value(x509_base + ['ca-cert-file']) + cert_path = os.path.join(AUTH_DIR, cert_file) + cert = None + + if os.path.isfile(cert_path): + if not os.access(cert_path, os.R_OK): + run(f'sudo chmod 644 {cert_path}') + + with open(cert_path, 'r') as f: + cert_data = f.read() + cert = load_certificate(cert_data, wrap_tags=False) + + if cert: + cert_pem = encode_certificate(cert) + config.set(pki_base + ['ca', pki_name, 'certificate'], value=wrapped_pem_to_config_value(cert_pem)) + config.set(x509_base + ['ca-certificate'], value=pki_name) + else: + print(f'Failed to migrate CA certificate on openconnect config') + + config.delete(x509_base + ['ca-cert-file']) + +if config.exists(x509_base + ['cert-file']): + cert_file = config.return_value(x509_base + ['cert-file']) + cert_path = os.path.join(AUTH_DIR, cert_file) + cert = None + + if os.path.isfile(cert_path): + if not os.access(cert_path, os.R_OK): + run(f'sudo chmod 644 {cert_path}') + + with open(cert_path, 'r') as f: + cert_data = f.read() + cert = load_certificate(cert_data, wrap_tags=False) + + if cert: + cert_pem = encode_certificate(cert) + config.set(pki_base + ['certificate', pki_name, 'certificate'], value=wrapped_pem_to_config_value(cert_pem)) + config.set(x509_base + ['certificate'], value=pki_name) + else: + print(f'Failed to migrate certificate on openconnect config') + + config.delete(x509_base + ['cert-file']) + +if config.exists(x509_base + ['key-file']): + key_file = config.return_value(x509_base + ['key-file']) + key_path = os.path.join(AUTH_DIR, key_file) + key = None + + if os.path.isfile(key_path): + if not os.access(key_path, os.R_OK): + run(f'sudo chmod 644 {key_path}') + + with open(key_path, 'r') as f: + key_data = f.read() + key = load_private_key(key_data, passphrase=None, wrap_tags=False) + + if key: + key_pem = encode_private_key(key, passphrase=None) + config.set(pki_base + ['certificate', pki_name, 'private', 'key'], value=wrapped_pem_to_config_value(key_pem)) + else: + print(f'Failed to migrate private key on openconnect config') + + config.delete(x509_base + ['key-file']) + +try: + with open(file_name, 'w') as f: + f.write(config.to_string()) +except OSError as e: + print("Failed to save the modified config: {}".format(e)) + exit(1) diff --git a/src/migration-scripts/quagga/7-to-8 b/src/migration-scripts/quagga/7-to-8 index 9c277a6f1..15c44924f 100755 --- a/src/migration-scripts/quagga/7-to-8 +++ b/src/migration-scripts/quagga/7-to-8 @@ -14,61 +14,14 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -# - T2450: drop interface-route and interface-route6 from "protocols static" +# - T3391: Migrate "maximum-paths" setting from "protocols bgp asn maximum-paths" +# under the IPv4 address-family tree. Reason is we currently have no way in +# configuring this for IPv6 address-family. This mimics the FRR configuration. from sys import argv from sys import exit - from vyos.configtree import ConfigTree -def migrate_interface_route(config, base, path, route_route6): - """ Generic migration function which can be called on every instance of - interface-route, beeing it ipv4, ipv6 or nested under the "static table" nodes. - - What we do? - - Drop 'interface-route' or 'interface-route6' and migrate the route unter the - 'route' or 'route6' tag node. - """ - if config.exists(base + path): - for route in config.list_nodes(base + path): - interface = config.list_nodes(base + path + [route, 'next-hop-interface']) - - tmp = base + path + [route, 'next-hop-interface'] - for interface in config.list_nodes(tmp): - new_base = base + [route_route6, route, 'interface'] - config.set(new_base) - config.set_tag(base + [route_route6]) - config.set_tag(new_base) - config.copy(tmp + [interface], new_base + [interface]) - - config.delete(base + path) - -def migrate_route(config, base, path, route_route6): - """ Generic migration function which can be called on every instance of - route, beeing it ipv4, ipv6 or even nested under the static table nodes. - - What we do? - - for consistency reasons rename next-hop-interface to interface - - for consistency reasons rename next-hop-vrf to vrf - """ - if config.exists(base + path): - for route in config.list_nodes(base + path): - next_hop = base + path + [route, 'next-hop'] - if config.exists(next_hop): - for gateway in config.list_nodes(next_hop): - # IPv4 routes calls it next-hop-interface, rename this to - # interface instead so it's consitent with IPv6 - interface_path = next_hop + [gateway, 'next-hop-interface'] - if config.exists(interface_path): - config.rename(interface_path, 'interface') - - # When VRFs got introduced, I (c-po) named it next-hop-vrf, - # we can also call it vrf which is simply shorter. - vrf_path = next_hop + [gateway, 'next-hop-vrf'] - if config.exists(vrf_path): - config.rename(vrf_path, 'vrf') - - if (len(argv) < 2): print("Must specify file name!") exit(1) @@ -78,41 +31,27 @@ file_name = argv[1] with open(file_name, 'r') as f: config_file = f.read() -base = ['protocols', 'static'] - +base = ['protocols', 'bgp'] config = ConfigTree(config_file) + if not config.exists(base): # Nothing to do exit(0) -# Migrate interface-route into route -migrate_interface_route(config, base, ['interface-route'], 'route') - -# Migrate interface-route6 into route6 -migrate_interface_route(config, base, ['interface-route6'], 'route6') - -# Cleanup nodes inside route -migrate_route(config, base, ['route'], 'route') - -# Cleanup nodes inside route6 -migrate_route(config, base, ['route6'], 'route6') - -# -# PBR table cleanup -table_path = base + ['table'] -if config.exists(table_path): - for table in config.list_nodes(table_path): - # Migrate interface-route into route - migrate_interface_route(config, table_path + [table], ['interface-route'], 'route') - - # Migrate interface-route6 into route6 - migrate_interface_route(config, table_path + [table], ['interface-route6'], 'route6') - - # Cleanup nodes inside route - migrate_route(config, table_path + [table], ['route'], 'route') - - # Cleanup nodes inside route6 - migrate_route(config, table_path + [table], ['route6'], 'route6') +# Check if BGP is actually configured and obtain the ASN +asn_list = config.list_nodes(base) +if asn_list: + # There's always just one BGP node, if any + bgp_base = base + [asn_list[0]] + + maximum_paths = bgp_base + ['maximum-paths'] + if config.exists(maximum_paths): + for bgp_type in ['ebgp', 'ibgp']: + if config.exists(maximum_paths + [bgp_type]): + new_base = bgp_base + ['address-family', 'ipv4-unicast', 'maximum-paths'] + config.set(new_base) + config.copy(maximum_paths + [bgp_type], new_base + [bgp_type]) + config.delete(maximum_paths) try: with open(file_name, 'w') as f: diff --git a/src/migration-scripts/quagga/8-to-9 b/src/migration-scripts/quagga/8-to-9 index 15c44924f..38507bd3d 100755 --- a/src/migration-scripts/quagga/8-to-9 +++ b/src/migration-scripts/quagga/8-to-9 @@ -14,14 +14,76 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -# - T3391: Migrate "maximum-paths" setting from "protocols bgp asn maximum-paths" -# under the IPv4 address-family tree. Reason is we currently have no way in -# configuring this for IPv6 address-family. This mimics the FRR configuration. +# - T2450: drop interface-route and interface-route6 from "protocols static" from sys import argv from sys import exit + from vyos.configtree import ConfigTree +def migrate_interface_route(config, base, path, route_route6): + """ Generic migration function which can be called on every instance of + interface-route, beeing it ipv4, ipv6 or nested under the "static table" nodes. + + What we do? + - Drop 'interface-route' or 'interface-route6' and migrate the route unter the + 'route' or 'route6' tag node. + """ + if config.exists(base + path): + for route in config.list_nodes(base + path): + interface = config.list_nodes(base + path + [route, 'next-hop-interface']) + + tmp = base + path + [route, 'next-hop-interface'] + for interface in config.list_nodes(tmp): + new_base = base + [route_route6, route, 'interface'] + config.set(new_base) + config.set_tag(base + [route_route6]) + config.set_tag(new_base) + config.copy(tmp + [interface], new_base + [interface]) + + config.delete(base + path) + +def migrate_route(config, base, path, route_route6): + """ Generic migration function which can be called on every instance of + route, beeing it ipv4, ipv6 or even nested under the static table nodes. + + What we do? + - for consistency reasons rename next-hop-interface to interface + - for consistency reasons rename next-hop-vrf to vrf + """ + if config.exists(base + path): + for route in config.list_nodes(base + path): + next_hop = base + path + [route, 'next-hop'] + if config.exists(next_hop): + for gateway in config.list_nodes(next_hop): + # IPv4 routes calls it next-hop-interface, rename this to + # interface instead so it's consitent with IPv6 + interface_path = next_hop + [gateway, 'next-hop-interface'] + if config.exists(interface_path): + config.rename(interface_path, 'interface') + + # When VRFs got introduced, I (c-po) named it next-hop-vrf, + # we can also call it vrf which is simply shorter. + vrf_path = next_hop + [gateway, 'next-hop-vrf'] + if config.exists(vrf_path): + config.rename(vrf_path, 'vrf') + + next_hop = base + path + [route, 'interface'] + if config.exists(next_hop): + for interface in config.list_nodes(next_hop): + # IPv4 routes calls it next-hop-interface, rename this to + # interface instead so it's consitent with IPv6 + interface_path = next_hop + [interface, 'next-hop-interface'] + if config.exists(interface_path): + config.rename(interface_path, 'interface') + + # When VRFs got introduced, I (c-po) named it next-hop-vrf, + # we can also call it vrf which is simply shorter. + vrf_path = next_hop + [interface, 'next-hop-vrf'] + if config.exists(vrf_path): + config.rename(vrf_path, 'vrf') + + if (len(argv) < 2): print("Must specify file name!") exit(1) @@ -31,27 +93,41 @@ file_name = argv[1] with open(file_name, 'r') as f: config_file = f.read() -base = ['protocols', 'bgp'] -config = ConfigTree(config_file) +base = ['protocols', 'static'] +config = ConfigTree(config_file) if not config.exists(base): # Nothing to do exit(0) -# Check if BGP is actually configured and obtain the ASN -asn_list = config.list_nodes(base) -if asn_list: - # There's always just one BGP node, if any - bgp_base = base + [asn_list[0]] - - maximum_paths = bgp_base + ['maximum-paths'] - if config.exists(maximum_paths): - for bgp_type in ['ebgp', 'ibgp']: - if config.exists(maximum_paths + [bgp_type]): - new_base = bgp_base + ['address-family', 'ipv4-unicast', 'maximum-paths'] - config.set(new_base) - config.copy(maximum_paths + [bgp_type], new_base + [bgp_type]) - config.delete(maximum_paths) +# Migrate interface-route into route +migrate_interface_route(config, base, ['interface-route'], 'route') + +# Migrate interface-route6 into route6 +migrate_interface_route(config, base, ['interface-route6'], 'route6') + +# Cleanup nodes inside route +migrate_route(config, base, ['route'], 'route') + +# Cleanup nodes inside route6 +migrate_route(config, base, ['route6'], 'route6') + +# +# PBR table cleanup +table_path = base + ['table'] +if config.exists(table_path): + for table in config.list_nodes(table_path): + # Migrate interface-route into route + migrate_interface_route(config, table_path + [table], ['interface-route'], 'route') + + # Migrate interface-route6 into route6 + migrate_interface_route(config, table_path + [table], ['interface-route6'], 'route6') + + # Cleanup nodes inside route + migrate_route(config, table_path + [table], ['route'], 'route') + + # Cleanup nodes inside route6 + migrate_route(config, table_path + [table], ['route6'], 'route6') try: with open(file_name, 'w') as f: diff --git a/src/migration-scripts/sstp/3-to-4 b/src/migration-scripts/sstp/3-to-4 new file mode 100755 index 000000000..0568f043f --- /dev/null +++ b/src/migration-scripts/sstp/3-to-4 @@ -0,0 +1,136 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 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/>. + +# - Update SSL to use PKI configuration + +import os + +from sys import argv +from sys import exit +from vyos.configtree import ConfigTree +from vyos.pki import load_certificate +from vyos.pki import load_crl +from vyos.pki import load_private_key +from vyos.pki import encode_certificate +from vyos.pki import encode_private_key +from vyos.util import run + +if (len(argv) < 1): + print("Must specify file name!") + exit(1) + +file_name = argv[1] + +with open(file_name, 'r') as f: + config_file = f.read() + +config = ConfigTree(config_file) +base = ['vpn', 'sstp'] +pki_base = ['pki'] + +if not config.exists(base): + exit(0) + +AUTH_DIR = '/config/auth' + +def wrapped_pem_to_config_value(pem): + return "".join(pem.strip().split("\n")[1:-1]) + +if not config.exists(base + ['ssl']): + exit(0) + +x509_base = base + ['ssl'] +pki_name = 'sstp' + +if not config.exists(pki_base + ['ca']): + config.set(pki_base + ['ca']) + config.set_tag(pki_base + ['ca']) + +if not config.exists(pki_base + ['certificate']): + config.set(pki_base + ['certificate']) + config.set_tag(pki_base + ['certificate']) + +if config.exists(x509_base + ['ca-cert-file']): + cert_file = config.return_value(x509_base + ['ca-cert-file']) + cert_path = os.path.join(AUTH_DIR, cert_file) + cert = None + + if os.path.isfile(cert_path): + if not os.access(cert_path, os.R_OK): + run(f'sudo chmod 644 {cert_path}') + + with open(cert_path, 'r') as f: + cert_data = f.read() + cert = load_certificate(cert_data, wrap_tags=False) + + if cert: + cert_pem = encode_certificate(cert) + config.set(pki_base + ['ca', pki_name, 'certificate'], value=wrapped_pem_to_config_value(cert_pem)) + config.set(x509_base + ['ca-certificate'], value=pki_name) + else: + print(f'Failed to migrate CA certificate on sstp config') + + config.delete(x509_base + ['ca-cert-file']) + +if config.exists(x509_base + ['cert-file']): + cert_file = config.return_value(x509_base + ['cert-file']) + cert_path = os.path.join(AUTH_DIR, cert_file) + cert = None + + if os.path.isfile(cert_path): + if not os.access(cert_path, os.R_OK): + run(f'sudo chmod 644 {cert_path}') + + with open(cert_path, 'r') as f: + cert_data = f.read() + cert = load_certificate(cert_data, wrap_tags=False) + + if cert: + cert_pem = encode_certificate(cert) + config.set(pki_base + ['certificate', pki_name, 'certificate'], value=wrapped_pem_to_config_value(cert_pem)) + config.set(x509_base + ['certificate'], value=pki_name) + else: + print(f'Failed to migrate certificate on sstp config') + + config.delete(x509_base + ['cert-file']) + +if config.exists(x509_base + ['key-file']): + key_file = config.return_value(x509_base + ['key-file']) + key_path = os.path.join(AUTH_DIR, key_file) + key = None + + if os.path.isfile(key_path): + if not os.access(key_path, os.R_OK): + run(f'sudo chmod 644 {key_path}') + + with open(key_path, 'r') as f: + key_data = f.read() + key = load_private_key(key_data, passphrase=None, wrap_tags=False) + + if key: + key_pem = encode_private_key(key, passphrase=None) + config.set(pki_base + ['certificate', pki_name, 'private', 'key'], value=wrapped_pem_to_config_value(key_pem)) + else: + print(f'Failed to migrate private key on sstp config') + + config.delete(x509_base + ['key-file']) + +try: + with open(file_name, 'w') as f: + f.write(config.to_string()) +except OSError as e: + print("Failed to save the modified config: {}".format(e)) + exit(1) diff --git a/src/migration-scripts/vrf/0-to-1 b/src/migration-scripts/vrf/0-to-1 index 29b2fab74..2b41ef3c7 100755 --- a/src/migration-scripts/vrf/0-to-1 +++ b/src/migration-scripts/vrf/0-to-1 @@ -91,6 +91,16 @@ for vrf in config.list_nodes(base): if config.exists(vrf_path): config.rename(vrf_path, 'vrf') + next_hop = route_path + [route, 'interface'] + if config.exists(next_hop): + for interface in config.list_nodes(next_hop): + interface_path = next_hop + [interface, 'next-hop-interface'] + if config.exists(interface_path): + config.rename(interface_path, 'interface') + vrf_path = next_hop + [interface, 'next-hop-vrf'] + if config.exists(vrf_path): + config.rename(vrf_path, 'vrf') + # # Cleanup nodes inside route6 # diff --git a/src/migration-scripts/vrf/1-to-2 b/src/migration-scripts/vrf/1-to-2 index 20128e957..9bc704e02 100755 --- a/src/migration-scripts/vrf/1-to-2 +++ b/src/migration-scripts/vrf/1-to-2 @@ -49,6 +49,7 @@ for vrf in config.list_nodes(base): new_static_base = vrf_base + [vrf, 'protocols'] config.set(new_static_base) config.copy(static_base, new_static_base + ['static']) + config.set_tag(new_static_base + ['static', 'route']) # Now delete the old configuration config.delete(base) diff --git a/src/migration-scripts/vrf/2-to-3 b/src/migration-scripts/vrf/2-to-3 new file mode 100755 index 000000000..8e0f97141 --- /dev/null +++ b/src/migration-scripts/vrf/2-to-3 @@ -0,0 +1,144 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 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/>. + +# Since connection tracking zones are int16, VRFs tables maximum value must +# be limited to 65535 +# Also, interface names in nftables cannot start from numbers, +# so VRF name should not start from a number + +from sys import argv +from sys import exit +from random import randrange +from random import choice +from string import ascii_lowercase +from vyos.configtree import ConfigTree +import re + + +# Helper function to find all config items with a VRF name +def _search_vrfs(config_commands, vrf_name): + vrf_values = [] + # Regex to find path of config command with old VRF + regex_filter = re.compile(rf'^set (?P<cmd_path>[^\']+vrf) \'{vrf_name}\'$') + # Check each command for VRF value + for config_command in config_commands: + search_result = regex_filter.search(config_command) + if search_result: + # Append VRF command to a list + vrf_values.append(search_result.group('cmd_path').split()) + if vrf_values: + return vrf_values + else: + return None + + +# Helper function to find all config items with a table number +def _search_tables(config_commands, table_num): + table_items = {'table_tags': [], 'table_values': []} + # Regex to find values and nodes with a table number + regex_tags = re.compile(rf'^set (?P<cmd_path>[^\']+table {table_num}) ?.*$') + regex_values = re.compile( + rf'^set (?P<cmd_path>[^\']+table) \'{table_num}\'$') + for config_command in config_commands: + # Search for tag nodes + search_result = regex_tags.search(config_command) + if search_result: + # Append table node path to a tag nodes list + cmd_path = search_result.group('cmd_path').split() + if cmd_path not in table_items['table_tags']: + table_items['table_tags'].append(cmd_path) + # Search for value nodes + search_result = regex_values.search(config_command) + if search_result: + # Append table node path to a value nodes list + table_items['table_values'].append( + search_result.group('cmd_path').split()) + return table_items + + +if (len(argv) < 2): + print("Must specify file name!") + exit(1) + +file_name = argv[1] + +with open(file_name, 'r') as f: + config_file = f.read() + +base = ['vrf', 'name'] +config = ConfigTree(config_file) + +if not config.exists(base): + # Nothing to do + exit(0) + +# Get a list of all currently used VRFs and tables +vrfs_current = {} +for vrf in config.list_nodes(base): + vrfs_current[vrf] = int(config.return_value(base + [vrf, 'table'])) + +# Check VRF names and table numbers +name_regex = re.compile(r'^\d.*$') +for vrf_name, vrf_table in vrfs_current.items(): + # Check table number + if vrf_table > 65535: + # Find new unused table number + vrfs_current[vrf_name] = None + while not vrfs_current[vrf_name]: + table_random = randrange(100, 65535) + if table_random not in vrfs_current.values(): + vrfs_current[vrf_name] = table_random + # Update number to a new one + config.set(['vrf', 'name', vrf_name, 'table'], + vrfs_current[vrf_name], + replace=True) + # Check config items with old table number and replace to new one + config_commands = config.to_commands().split('\n') + table_config_lines = _search_tables(config_commands, vrf_table) + # Rename table nodes + if table_config_lines.get('table_tags'): + for table_config_path in table_config_lines.get('table_tags'): + config.rename(table_config_path, f'{vrfs_current[vrf_name]}') + # Replace table values + if table_config_lines.get('table_values'): + for table_config_path in table_config_lines.get('table_values'): + config.set(table_config_path, + f'{vrfs_current[vrf_name]}', + replace=True) + + # Check VRF name + if name_regex.match(vrf_name): + vrf_name_new = None + while not vrf_name_new: + vrf_name_rand = f'{choice(ascii_lowercase)}{vrf_name}'[:15] + if vrf_name_rand not in vrfs_current: + vrf_name_new = vrf_name_rand + # Update VRF name to a new one + config.rename(['vrf', 'name', vrf_name], vrf_name_new) + # Check config items with old VRF name and replace to new one + config_commands = config.to_commands().split('\n') + vrf_config_lines = _search_vrfs(config_commands, vrf_name) + # Rename VRF to a new name + if vrf_config_lines: + for vrf_value_path in vrf_config_lines: + config.set(vrf_value_path, vrf_name_new, replace=True) + +try: + with open(file_name, 'w') as f: + f.write(config.to_string()) +except OSError as e: + print("Failed to save the modified config: {}".format(e)) + exit(1) |