From eabc5f1c2e5bfe548cb3d62f2f85f8d61be29b92 Mon Sep 17 00:00:00 2001 From: Simon <965089+sarthurdev@users.noreply.github.com> Date: Fri, 28 May 2021 16:35:12 +0200 Subject: ipsec: T2816: IPSec python rework, includes DMVPN and VTI support --- .../dhcp/dhclient-exit-hooks.d/ipsec-dhclient-hook | 46 ++++++++++++ src/etc/ipsec.d/key-pair.template | 67 ++++++++++++++++++ src/etc/ipsec.d/vti-up-down | 82 ++++++++++++++++++++++ 3 files changed, 195 insertions(+) create mode 100644 src/etc/dhcp/dhclient-exit-hooks.d/ipsec-dhclient-hook create mode 100644 src/etc/ipsec.d/key-pair.template create mode 100644 src/etc/ipsec.d/vti-up-down (limited to 'src/etc') diff --git a/src/etc/dhcp/dhclient-exit-hooks.d/ipsec-dhclient-hook b/src/etc/dhcp/dhclient-exit-hooks.d/ipsec-dhclient-hook new file mode 100644 index 000000000..36edf04f3 --- /dev/null +++ b/src/etc/dhcp/dhclient-exit-hooks.d/ipsec-dhclient-hook @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 + +import os +import sys + +from vyos.util import call + +IPSEC_CONF="/etc/ipsec.conf" +IPSEC_SECRETS="/etc/ipsec.secrets" + +def getlines(file): + with open(file, 'r') as f: + return f.readlines() + +def writelines(file, lines): + with open(file, 'w') as f: + f.writelines(lines) + +if __name__ == '__main__': + interface = os.getenv('interface') + new_ip = os.getenv('new_ip_address') + old_ip = os.getenv('old_ip_address') + reason = os.getenv('reason') + + if (old_ip == new_ip and reason != 'BOUND') or reason in ['REBOOT', 'EXPIRE']: + sys.exit(0) + + conf_lines = getlines(IPSEC_CONF) + secrets_lines = getlines(IPSEC_SECRETS) + found = False + to_match = f'# dhcp:{interface}' + + for i, line in enumerate(conf_lines): + if line.find(to_match) > 0: + conf_lines[i] = line.replace(old_ip, new_ip) + found = True + + for i, line in enumerate(secrets_lines): + if line.find(to_match) > 0: + secrets_lines[i] = line.replace(old_ip, new_ip) + + if found: + writelines(IPSEC_CONF, conf_lines) + writelines(IPSEC_SECRETS, secrets_lines) + call('sudo /usr/sbin/ipsec rereadall') + call('sudo /usr/sbin/ipsec reload') diff --git a/src/etc/ipsec.d/key-pair.template b/src/etc/ipsec.d/key-pair.template new file mode 100644 index 000000000..56be97516 --- /dev/null +++ b/src/etc/ipsec.d/key-pair.template @@ -0,0 +1,67 @@ +[ req ] + default_bits = 2048 + default_keyfile = privkey.pem + distinguished_name = req_distinguished_name + string_mask = utf8only + attributes = req_attributes + dirstring_type = nobmp +# SHA-1 is deprecated, so use SHA-2 instead. + default_md = sha256 +# Extension to add when the -x509 option is used. + x509_extensions = v3_ca + +[ req_distinguished_name ] + countryName = Country Name (2 letter code) + countryName_min = 2 + countryName_max = 2 + ST = State Name + localityName = Locality Name (eg, city) + organizationName = Organization Name (eg, company) + organizationalUnitName = Organizational Unit Name (eg, department) + commonName = Common Name (eg, Device hostname) + commonName_max = 64 + emailAddress = Email Address + emailAddress_max = 40 +[ req_attributes ] + challengePassword = A challenge password (optional) + challengePassword_min = 4 + challengePassword_max = 20 +[ v3_ca ] + subjectKeyIdentifier=hash + authorityKeyIdentifier=keyid:always,issuer:always + basicConstraints = critical, CA:true + keyUsage = critical, digitalSignature, cRLSign, keyCertSign +[ v3_intermediate_ca ] +# Extensions for a typical intermediate CA (`man x509v3_config`). + subjectKeyIdentifier = hash + authorityKeyIdentifier = keyid:always,issuer + basicConstraints = critical, CA:true, pathlen:0 + keyUsage = critical, digitalSignature, cRLSign, keyCertSign +[ usr_cert ] +# Extensions for client certificates (`man x509v3_config`). + basicConstraints = CA:FALSE + nsCertType = client, email + nsComment = "OpenSSL Generated Client Certificate" + subjectKeyIdentifier = hash + authorityKeyIdentifier = keyid,issuer + keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment + extendedKeyUsage = clientAuth, emailProtection +[ server_cert ] +# Extensions for server certificates (`man x509v3_config`). + basicConstraints = CA:FALSE + nsCertType = server + nsComment = "OpenSSL Generated Server Certificate" + subjectKeyIdentifier = hash + authorityKeyIdentifier = keyid,issuer:always + keyUsage = critical, digitalSignature, keyEncipherment + extendedKeyUsage = serverAuth +[ crl_ext ] +# Extension for CRLs (`man x509v3_config`). + authorityKeyIdentifier=keyid:always +[ ocsp ] +# Extension for OCSP signing certificates (`man ocsp`). + basicConstraints = CA:FALSE + subjectKeyIdentifier = hash + authorityKeyIdentifier = keyid,issuer + keyUsage = critical, digitalSignature + extendedKeyUsage = critical, OCSPSigning diff --git a/src/etc/ipsec.d/vti-up-down b/src/etc/ipsec.d/vti-up-down new file mode 100644 index 000000000..416966056 --- /dev/null +++ b/src/etc/ipsec.d/vti-up-down @@ -0,0 +1,82 @@ +#!/usr/bin/env python3 +## Script called up strongswan to bring the vti interface up/down based on the state of the IPSec tunnel. +## Called as vti_up_down vti_intf_name + +import os +import sys + +from vyos.config import Config +from vyos.util import call, get_interface_config, get_interface_address + +def get_config(config, base): + if not config.exists(base): + return None + + return conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True, no_tag_node_value_mangle=True) + +def get_dhcp_address(interface): + addr = get_interface_address(interface) + if not addr: + return None + if len(addr['addr_info']) == 0: + return None + return addr['addr_info'][0]['local'] + +if __name__ == '__main__': + verb = os.getenv('PLUTO_VERB') + connection = os.getenv('PLUTO_CONNECTION') + parent_conn = connection[:-3] + interface = sys.argv[1] + + print(f'vti-up-down: start: {verb} {connection} {interface}') + + if verb in ['up-client', 'up-host']: + call('sudo /usr/sbin/ip route delete default table 220') + + vti_base = ['interfaces', 'vti', interface] + ipsec_base = ['vpn', 'ipsec', 'site-to-site'] + + conf = Config() + vti_conf = get_config(conf, vti_base) + ipsec_conf = get_config(conf, ipsec_base) + + if not vti_conf or 'disable' in vti_conf or not ipsec_conf or 'peer' not in ipsec_conf: + print('vti-up-down: exit: vti not found, disabled or no peers found') + sys.exit(0) + + peer_conf = None + + for peer, peer_tmp_conf in ipsec_conf['peer'].items(): + if 'vti' in peer_tmp_conf and 'bind' in peer_tmp_conf['vti']: + bind = peer_tmp_conf['vti']['bind'] + if isinstance(bind, str): + bind = [bind] + if interface in bind: + peer_conf = peer_tmp_conf + break + + if not peer_conf: + print(f'vti-up-down: exit: No peer found for {interface}') + sys.exit(0) + + vti_link = get_interface_config(interface) + vti_link_up = vti_link['operstate'] == 'UP' if vti_link else False + + child_sa_installed = False + try: + child_sa_installed = (call(f'sudo /usr/sbin/swanctl -l -r -i {connection} {parent_conn} | grep -s -q state=INSTALLED', timeout = 5) == 0) + except: + print('vti-up-down: child-sa check failed') + + if verb in ['up-client', 'up-host']: + if not vti_link_up: + if 'dhcp_interface' in peer_conf: + local_ip = get_dhcp_address(peer_conf['dhcp_interface']) + call(f'sudo /usr/sbin/ip tunnel change {interface} local {local_ip}') + if child_sa_installed: + call(f'sudo /usr/sbin/ip link set {interface} up') + elif verb in ['down-client', 'down-host']: + if vti_link_up and not child_sa_installed: + call(f'sudo /usr/sbin/ip link set {interface} down') + + print('vti-up-down: finish') -- cgit v1.2.3