diff options
Diffstat (limited to 'src/conf_mode/vpn_ipsec.py')
-rwxr-xr-x | src/conf_mode/vpn_ipsec.py | 78 |
1 files changed, 61 insertions, 17 deletions
diff --git a/src/conf_mode/vpn_ipsec.py b/src/conf_mode/vpn_ipsec.py index 4efedd995..433c51e7e 100755 --- a/src/conf_mode/vpn_ipsec.py +++ b/src/conf_mode/vpn_ipsec.py @@ -23,10 +23,11 @@ from vyos.config import Config from vyos.configdict import leaf_node_changed from vyos.configverify import verify_interface_exists from vyos.ifconfig import Interface +from vyos.template import ip_from_cidr from vyos.template import render +from vyos.validate import is_ipv6_link_local from vyos.util import call from vyos.util import dict_search -from vyos.util import get_interface_address from vyos.util import process_named_running from vyos.util import run from vyos.util import cidr_fit @@ -35,9 +36,9 @@ from vyos import airbag airbag.enable() authby_translate = { - 'pre-shared-secret': 'secret', - 'rsa': 'rsasig', - 'x509': 'rsasig' + 'pre-shared-secret': 'psk', + 'rsa': 'pubkey', + 'x509': 'pubkey' } default_pfs = 'dh-group2' pfs_translate = { @@ -73,12 +74,18 @@ any_log_modes = [ ike_ciphers = {} esp_ciphers = {} +dhcp_wait_attempts = 2 +dhcp_wait_sleep = 1 + mark_base = 0x900000 -CA_PATH = "/etc/ipsec.d/cacerts/" -CRL_PATH = "/etc/ipsec.d/crls/" +CERT_PATH="/etc/swanctl/x509/" +KEY_PATH="/etc/swanctl/private/" +CA_PATH = "/etc/swanctl/x509ca/" +CRL_PATH = "/etc/swanctl/x509crl/" DHCP_BASE = "/var/lib/dhcp/dhclient" +DHCP_HOOK_IFLIST="/tmp/ipsec_dhcp_waiting" LOCAL_KEY_PATHS = ['/config/auth/', '/config/ipsec.d/rsa-keys/'] X509_PATH = '/config/auth/' @@ -96,6 +103,7 @@ def get_config(config=None): ipsec = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True, no_tag_node_value_mangle=True) + ipsec['dhcp_no_address'] = {} 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') @@ -119,7 +127,7 @@ def get_config(config=None): if enc and hash: ciphers.append(f"{enc}-{hash}-{pfs_translate[pfs]}" if pfs else f"{enc}-{hash}") - ike_ciphers[group] = ','.join(ciphers) + '!' + ike_ciphers[group] = ','.join(ciphers) if 'esp_group' in ipsec: for group, esp_conf in ipsec['esp_group'].items(): @@ -139,7 +147,7 @@ def get_config(config=None): hash = proposal['hash'] if 'hash' in proposal else None if enc and hash: ciphers.append(f"{enc}-{hash}-{pfs_translate[pfs]}" if pfs else f"{enc}-{hash}") - esp_ciphers[group] = ','.join(ciphers) + '!' + esp_ciphers[group] = ','.join(ciphers) return ipsec @@ -162,6 +170,15 @@ def verify_rsa_local_key(ipsec): def verify_rsa_key(ipsec, key_name): return dict_search(f'rsa_key_name.{key_name}.rsa_key', ipsec['rsa_keys']) +def get_dhcp_address(iface): + addresses = Interface(iface).get_addr() + if not addresses: + return None + for address in addresses: + if not is_ipv6_link_local(address): + return ip_from_cidr(address) + return None + def verify(ipsec): if not ipsec: return None @@ -252,9 +269,17 @@ def verify(ipsec): if not os.path.exists(f'{DHCP_BASE}_{dhcp_interface}.conf'): raise ConfigError(f"Invalid dhcp-interface on site-to-site peer {peer}") - address = Interface(dhcp_interface).get_addr() + 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: - raise ConfigError(f"Failed to get address from dhcp-interface on site-to-site peer {peer}") + ipsec['dhcp_no_address'][peer] = dhcp_interface + print(f"Failed to get address from dhcp-interface on site-to-site peer {peer} -- skipped") + continue if 'vti' in peer_conf: if 'local_address' in peer_conf and 'dhcp_interface' in peer_conf: @@ -291,16 +316,28 @@ def generate(ipsec): data = {} if ipsec: + if ipsec['dhcp_no_address']: + with open(DHCP_HOOK_IFLIST, 'w') as f: + f.write(" ".join(ipsec['dhcp_no_address'].values())) + data = ipsec data['authby'] = authby_translate data['ciphers'] = {'ike': ike_ciphers, 'esp': esp_ciphers} data['marks'] = {} data['rsa_local_key'] = verify_rsa_local_key(ipsec) - data['x509_path'] = X509_PATH if 'site_to_site' in data and 'peer' in data['site_to_site']: for peer, peer_conf in ipsec['site_to_site']['peer'].items(): + if peer in ipsec['dhcp_no_address']: + continue + if peer_conf['authentication']['mode'] == 'x509': + cert_file = os.path.join(X509_PATH, peer_conf['authentication']['x509']['cert_file']) + call(f'cp -f {cert_file} {CERT_PATH}') + + key_file = os.path.join(X509_PATH, peer_conf['authentication']['x509']['key']['file']) + call(f'cp -f {key_file} {KEY_PATH}') + ca_cert_file = os.path.join(X509_PATH, peer_conf['authentication']['x509']['ca_cert_file']) call(f'cp -f {ca_cert_file} {CA_PATH}') @@ -312,22 +349,29 @@ def generate(ipsec): if 'local_address' in peer_conf: local_ip = peer_conf['local_address'] elif 'dhcp_interface' in peer_conf: - local_ip = Interface(peer_conf['dhcp_interface']).get_addr() + local_ip = get_dhcp_address(peer_conf['dhcp_interface']) data['site_to_site']['peer'][peer]['local_address'] = local_ip if 'vti' in peer_conf and 'bind' in peer_conf['vti']: vti_interface = peer_conf['vti']['bind'] data['marks'][vti_interface] = get_mark(vti_interface) - else: + + if 'tunnel' in peer_conf: for tunnel, tunnel_conf in peer_conf['tunnel'].items(): - local_prefix = dict_search('local.prefix', tunnel_conf) - remote_prefix = dict_search('remote.prefix', tunnel_conf) + local_prefixes = dict_search('local.prefix', tunnel_conf) + remote_prefixes = dict_search('remote.prefix', tunnel_conf) - if not local_prefix or not remote_prefix: + if not local_prefixes or not remote_prefixes: continue - passthrough = cidr_fit(local_prefix, remote_prefix) + passthrough = [] + + for local_prefix in local_prefixes: + for remote_prefix in remote_prefixes: + if cidr_fit(local_prefix, remote_prefix): + passthrough.append(local_prefix) + data['site_to_site']['peer'][peer]['tunnel'][tunnel]['passthrough'] = passthrough if 'logging' in ipsec and 'log_modes' in ipsec['logging']: |