diff options
Diffstat (limited to 'src/conf_mode/vpn_ipsec.py')
-rwxr-xr-x | src/conf_mode/vpn_ipsec.py | 92 |
1 files changed, 58 insertions, 34 deletions
diff --git a/src/conf_mode/vpn_ipsec.py b/src/conf_mode/vpn_ipsec.py index 388f2a709..dc78c755e 100755 --- a/src/conf_mode/vpn_ipsec.py +++ b/src/conf_mode/vpn_ipsec.py @@ -21,7 +21,6 @@ import jmespath from sys import exit from time import sleep -from time import time from vyos.base import Warning from vyos.config import Config @@ -32,10 +31,7 @@ from vyos.configverify import verify_interface_exists from vyos.configverify import dynamic_interface_pattern from vyos.defaults import directories from vyos.ifconfig import Interface -from vyos.pki import encode_certificate from vyos.pki import encode_public_key -from vyos.pki import find_chain -from vyos.pki import load_certificate from vyos.pki import load_private_key from vyos.pki import wrap_certificate from vyos.pki import wrap_crl @@ -50,7 +46,6 @@ from vyos.utils.network import interface_exists from vyos.utils.dict import dict_search from vyos.utils.dict import dict_search_args from vyos.utils.process import call -from vyos.utils.process import run from vyos import ConfigError from vyos import airbag airbag.enable() @@ -75,7 +70,7 @@ KEY_PATH = f'{swanctl_dir}/private/' CA_PATH = f'{swanctl_dir}/x509ca/' CRL_PATH = f'{swanctl_dir}/x509crl/' -DHCP_HOOK_IFLIST = '/tmp/ipsec_dhcp_waiting' +DHCP_HOOK_IFLIST = '/tmp/ipsec_dhcp_interfaces' def get_config(config=None): if config: @@ -94,6 +89,7 @@ def get_config(config=None): with_recursive_defaults=True, with_pki=True) + ipsec['dhcp_interfaces'] = set() 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 + ['interface']) @@ -126,11 +122,11 @@ def verify_pki_x509(pki, x509_conf): if not pki or 'ca' not in pki or 'certificate' not in pki: raise ConfigError(f'PKI is not configured') - ca_cert_name = x509_conf['ca_certificate'] cert_name = x509_conf['certificate'] - if not dict_search_args(pki, 'ca', ca_cert_name, 'certificate'): - raise ConfigError(f'Missing CA certificate on specified PKI CA certificate "{ca_cert_name}"') + for ca_cert_name in x509_conf['ca_certificate']: + if not dict_search_args(pki, 'ca', ca_cert_name, 'certificate'): + raise ConfigError(f'Missing CA certificate on specified PKI CA certificate "{ca_cert_name}"') if not dict_search_args(pki, 'certificate', cert_name, 'certificate'): raise ConfigError(f'Missing certificate on specified PKI certificate "{cert_name}"') @@ -170,9 +166,7 @@ def verify(ipsec): for interface in ipsec['interface']: # exclude check interface for dynamic interfaces if tmp.match(interface): - if not interface_exists(interface): - Warning(f'Interface "{interface}" does not exist yet and cannot be used ' - f'for IPsec until it is up!') + verify_interface_exists(interface, warning_only=True) else: verify_interface_exists(interface) @@ -229,6 +223,32 @@ def verify(ipsec): if 'remote_access' in ipsec: if 'connection' in ipsec['remote_access']: for name, ra_conf in ipsec['remote_access']['connection'].items(): + if 'local_address' not in ra_conf and 'dhcp_interface' not in ra_conf: + raise ConfigError(f"Missing local-address or dhcp-interface on remote-access connection {name}") + + if 'dhcp_interface' in ra_conf: + dhcp_interface = ra_conf['dhcp_interface'] + + verify_interface_exists(dhcp_interface) + dhcp_base = directories['isc_dhclient_dir'] + + if not os.path.exists(f'{dhcp_base}/dhclient_{dhcp_interface}.conf'): + raise ConfigError(f"Invalid dhcp-interface on remote-access connection {name}") + + ipsec['dhcp_interfaces'].add(dhcp_interface) + + address = get_dhcp_address(dhcp_interface) + count = 0 + while not address and count < dhcp_wait_attempts: + address = get_dhcp_address(dhcp_interface) + count += 1 + sleep(dhcp_wait_sleep) + + if not address: + ipsec['dhcp_no_address'][f'ra_{name}'] = dhcp_interface + print(f"Failed to get address from dhcp-interface on remote-access connection {name} -- skipped") + continue + if 'esp_group' in ra_conf: if 'esp_group' not in ipsec or ra_conf['esp_group'] not in ipsec['esp_group']: raise ConfigError(f"Invalid esp-group on {name} remote-access config") @@ -386,6 +406,8 @@ def verify(ipsec): if not os.path.exists(f'{dhcp_base}/dhclient_{dhcp_interface}.conf'): raise ConfigError(f"Invalid dhcp-interface on site-to-site peer {peer}") + ipsec['dhcp_interfaces'].add(dhcp_interface) + address = get_dhcp_address(dhcp_interface) count = 0 while not address and count < dhcp_wait_attempts: @@ -394,7 +416,7 @@ def verify(ipsec): sleep(dhcp_wait_sleep) if not address: - ipsec['dhcp_no_address'][peer] = dhcp_interface + ipsec['dhcp_no_address'][f'peer_{peer}'] = dhcp_interface print(f"Failed to get address from dhcp-interface on site-to-site peer {peer} -- skipped") continue @@ -443,32 +465,24 @@ def cleanup_pki_files(): os.unlink(file_path) def generate_pki_files_x509(pki, x509_conf): - ca_cert_name = x509_conf['ca_certificate'] - ca_cert_data = dict_search_args(pki, 'ca', ca_cert_name, 'certificate') - ca_cert_crls = dict_search_args(pki, 'ca', ca_cert_name, 'crl') or [] - ca_index = 1 - crl_index = 1 + for ca_cert_name in x509_conf['ca_certificate']: + ca_cert_data = dict_search_args(pki, 'ca', ca_cert_name, 'certificate') + ca_cert_crls = dict_search_args(pki, 'ca', ca_cert_name, 'crl') or [] + crl_index = 1 - ca_cert = load_certificate(ca_cert_data) - pki_ca_certs = [load_certificate(ca['certificate']) for ca in pki['ca'].values()] + with open(os.path.join(CA_PATH, f'{ca_cert_name}.pem'), 'w') as f: + f.write(wrap_certificate(ca_cert_data)) - ca_cert_chain = find_chain(ca_cert, pki_ca_certs) + for crl in ca_cert_crls: + with open(os.path.join(CRL_PATH, f'{ca_cert_name}_{crl_index}.pem'), 'w') as f: + f.write(wrap_crl(crl)) + crl_index += 1 cert_name = x509_conf['certificate'] cert_data = dict_search_args(pki, 'certificate', cert_name, 'certificate') key_data = dict_search_args(pki, 'certificate', cert_name, 'private', 'key') protected = 'passphrase' in x509_conf - for ca_cert_obj in ca_cert_chain: - with open(os.path.join(CA_PATH, f'{ca_cert_name}_{ca_index}.pem'), 'w') as f: - f.write(encode_certificate(ca_cert_obj)) - ca_index += 1 - - for crl in ca_cert_crls: - with open(os.path.join(CRL_PATH, f'{ca_cert_name}_{crl_index}.pem'), 'w') as f: - f.write(wrap_crl(crl)) - crl_index += 1 - with open(os.path.join(CERT_PATH, f'{cert_name}.pem'), 'w') as f: f.write(wrap_certificate(cert_data)) @@ -503,9 +517,9 @@ def generate(ipsec): render(charon_conf, 'ipsec/charon.j2', {'install_routes': default_install_routes}) return - if ipsec['dhcp_no_address']: + if ipsec['dhcp_interfaces']: with open(DHCP_HOOK_IFLIST, 'w') as f: - f.write(" ".join(ipsec['dhcp_no_address'].values())) + f.write(" ".join(ipsec['dhcp_interfaces'])) elif os.path.exists(DHCP_HOOK_IFLIST): os.unlink(DHCP_HOOK_IFLIST) @@ -522,13 +536,23 @@ def generate(ipsec): if 'remote_access' in ipsec and 'connection' in ipsec['remote_access']: for rw, rw_conf in ipsec['remote_access']['connection'].items(): + if f'ra_{rw}' in ipsec['dhcp_no_address']: + continue + + local_ip = '' + if 'local_address' in rw_conf: + local_ip = rw_conf['local_address'] + elif 'dhcp_interface' in rw_conf: + local_ip = get_dhcp_address(rw_conf['dhcp_interface']) + + ipsec['remote_access']['connection'][rw]['local_address'] = local_ip if 'authentication' in rw_conf and 'x509' in rw_conf['authentication']: generate_pki_files_x509(ipsec['pki'], rw_conf['authentication']['x509']) if 'site_to_site' in ipsec and 'peer' in ipsec['site_to_site']: for peer, peer_conf in ipsec['site_to_site']['peer'].items(): - if peer in ipsec['dhcp_no_address']: + if f'peer_{peer}' in ipsec['dhcp_no_address']: continue if peer_conf['authentication']['mode'] == 'x509': |