From ecdc9b1f0bf47b762669d8600aaddc5cfa5ed206 Mon Sep 17 00:00:00 2001 From: aapostoliuk Date: Thu, 16 Mar 2023 12:33:18 +0200 Subject: ipsec: T5043: Rewritten and fixed 'reset vpn' commands 1. Rewritten CLI of 'reset vpn' commands. 2. Created 'reset vpn ipsec remote-access' commands to reset RA IKEv2 session. 3. Created 'reset vpn ipsec site-to-site all' command to reset all configured IPSec site-to-site peers sessions. 4. Rewritten 'reset vpn l2t|pptp|sstp' commands to new opmode style. --- op-mode-definitions/vpn-ipsec.xml.in | 97 +++++++++++++++++++++++++----------- 1 file changed, 68 insertions(+), 29 deletions(-) (limited to 'op-mode-definitions/vpn-ipsec.xml.in') diff --git a/op-mode-definitions/vpn-ipsec.xml.in b/op-mode-definitions/vpn-ipsec.xml.in index ee006a2d5..5baaec7ce 100644 --- a/op-mode-definitions/vpn-ipsec.xml.in +++ b/op-mode-definitions/vpn-ipsec.xml.in @@ -7,49 +7,88 @@ Reset Virtual Private Network (VPN) information - + - Reset all tunnels for given peer - - vpn ipsec site-to-site peer - + Reset IPSec VPN sessions - + - Reset a specific tunnel for given peer + Reset all tunnels for given DMVPN profile - vpn ipsec site-to-site peer ${COMP_WORDS[3]} tunnel + vpn ipsec profile - sudo ${vyos_op_scripts_dir}/ipsec.py reset_peer --peer="$4" --tunnel="$6" + + + + Reset a specific tunnel for given DMVPN profile + + sudo ${vyos_op_scripts_dir}/vpn_ipsec.py --action="reset-profile" --name="$6" --tunnel="$8" + + + sudo ${vyos_op_scripts_dir}/vpn_ipsec.py --action="reset-profile" --name="$6" --tunnel="all" - + - Reset the VTI tunnel for given peer + Reset remote access IPSec VPN connections - sudo ${vyos_op_scripts_dir}/ipsec.py reset_peer --peer="$4" --tunnel="vti" + + + + Reset all users current remote access IPSec VPN sessions + + sudo ${vyos_op_scripts_dir}/ipsec.py reset_ra + + + + Reset specified user current remote access IPsec VPN session(s) + + sudo ${vyos_op_scripts_dir}/ipsec.py reset_ra --user="$6" + + - - sudo ${vyos_op_scripts_dir}/ipsec.py reset_peer --peer="$4" - - - - Reset all tunnels for given DMVPN profile - - vpn ipsec profile - - - - + - Reset a specific tunnel for given DMVPN profile + Reset site-to-site IPSec VPN connections - sudo ${vyos_op_scripts_dir}/vpn_ipsec.py --action="reset-profile" --name="$4" --tunnel="$6" - + + + + Reset all site-to-site IPSec VPN sessions + + sudo ${vyos_op_scripts_dir}/ipsec.py reset_all_peers + + + + Reset all tunnels for given peer + + vpn ipsec site-to-site peer + + + + + + Reset a specific tunnel for given peer + + vpn ipsec site-to-site peer ${COMP_WORDS[5]} tunnel + + + sudo ${vyos_op_scripts_dir}/ipsec.py reset_peer --peer="$6" --tunnel="$8" + + + + Reset the VTI tunnel for given peer + + sudo ${vyos_op_scripts_dir}/ipsec.py reset_peer --peer="$6" --tunnel="vti" + + + sudo ${vyos_op_scripts_dir}/ipsec.py reset_peer --peer="$6" + + + - sudo ${vyos_op_scripts_dir}/vpn_ipsec.py --action="reset-profile" --name="$4" --tunnel="all" - + -- cgit v1.2.3 From a78982625a8a18069bd5a13744734873698fd0f9 Mon Sep 17 00:00:00 2001 From: aapostoliuk Date: Thu, 30 Mar 2023 18:28:56 +0300 Subject: ipsec: T5093: Fixed 'reset vpn ipsec profile' command Fixed 'reset vpn ipsec profile' command using vici library and new op-mode style. Added ability to use 'reset vpn ipsec profile' command with 'remote-host' option. --- op-mode-definitions/vpn-ipsec.xml.in | 19 +++++++-- python/vyos/ipsec.py | 38 +++++++++++++++++ src/completion/list_ipsec_profile_tunnels.py | 48 +++++++++++++++++++++ src/op_mode/ipsec.py | 62 ++++++++++++++++++++++++++++ src/op_mode/vpn_ipsec.py | 61 ++------------------------- 5 files changed, 168 insertions(+), 60 deletions(-) create mode 100644 src/completion/list_ipsec_profile_tunnels.py (limited to 'op-mode-definitions/vpn-ipsec.xml.in') diff --git a/op-mode-definitions/vpn-ipsec.xml.in b/op-mode-definitions/vpn-ipsec.xml.in index 5baaec7ce..5a7e6dd63 100644 --- a/op-mode-definitions/vpn-ipsec.xml.in +++ b/op-mode-definitions/vpn-ipsec.xml.in @@ -14,7 +14,7 @@ - Reset all tunnels for given DMVPN profile + Reset a specific tunnel for given DMVPN profile vpn ipsec profile @@ -23,11 +23,24 @@ Reset a specific tunnel for given DMVPN profile + + + - sudo ${vyos_op_scripts_dir}/vpn_ipsec.py --action="reset-profile" --name="$6" --tunnel="$8" + + + + Reset a specific tunnel for given DMVPN NBMA + + <x.x.x.x> <h:h:h:h:h:h:h:h> + + + sudo ${vyos_op_scripts_dir}/ipsec.py reset_profile_dst --profile="$5" --tunnel="$7" --nbma_dst="$9" + + + sudo ${vyos_op_scripts_dir}/ipsec.py reset_profile_all --profile="$5" --tunnel="$7" - sudo ${vyos_op_scripts_dir}/vpn_ipsec.py --action="reset-profile" --name="$6" --tunnel="all" diff --git a/python/vyos/ipsec.py b/python/vyos/ipsec.py index cb7c39ff6..bb5611025 100644 --- a/python/vyos/ipsec.py +++ b/python/vyos/ipsec.py @@ -139,3 +139,41 @@ def terminate_vici_by_name(ike_name: str, child_name: str) -> None: else: raise ViciCommandError( f'Failed to terminate SA for IKE {ike_name}') + + +def vici_initiate(ike_sa_name: str, child_sa_name: str, src_addr: str, + dst_addr: str) -> bool: + """Initiate IKE SA connection with specific peer + + Args: + ike_sa_name (str): an IKE SA connection name + child_sa_name (str): a child SA profile name + src_addr (str): source address + dst_addr (str): remote address + + Returns: + bool: a result of initiation command + """ + from vici import Session as vici_session + + try: + session = vici_session() + except Exception: + raise ViciInitiateError("IPsec not initialized") + + try: + session_generator = session.initiate({ + 'ike': ike_sa_name, + 'child': child_sa_name, + 'timeout': '-1', + 'my-host': src_addr, + 'other-host': dst_addr + }) + # a dummy `for` loop is required because of requirements + # from vici. Without a full iteration on the output, the + # command to vici may not be executed completely + for _ in session_generator: + pass + return True + except Exception: + raise ViciCommandError(f'Failed to initiate SA for IKE {ike_sa_name}') \ No newline at end of file diff --git a/src/completion/list_ipsec_profile_tunnels.py b/src/completion/list_ipsec_profile_tunnels.py new file mode 100644 index 000000000..df6c52f6d --- /dev/null +++ b/src/completion/list_ipsec_profile_tunnels.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2019-2023 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 . + +import os +import sys +import argparse + +from vyos.config import Config +from vyos.util import dict_search + +def get_tunnels_from_ipsecprofile(profile): + config = Config() + base = ['vpn', 'ipsec', 'profile', profile, 'bind'] + profile_conf = config.get_config_dict(base, effective=True, key_mangling=('-', '_')) + tunnels = [] + + try: + for tunnel in (dict_search('bind.tunnel', profile_conf) or []): + tunnels.append(tunnel) + except: + pass + + return tunnels + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("-p", "--profile", type=str, help="List tunnels per profile") + args = parser.parse_args() + + tunnels = [] + + tunnels = get_tunnels_from_ipsecprofile(args.profile) + + print(" ".join(tunnels)) + diff --git a/src/op_mode/ipsec.py b/src/op_mode/ipsec.py index 6acde08ea..7f4fb72e5 100755 --- a/src/op_mode/ipsec.py +++ b/src/op_mode/ipsec.py @@ -13,6 +13,7 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . + import re import sys import typing @@ -487,6 +488,67 @@ def reset_ra(username: typing.Optional[str] = None): vyos.ipsec.terminate_vici_ikeid_list(list_sa_id) +def reset_profile_dst(profile: str, tunnel: str, nbma_dst: str): + if profile and tunnel and nbma_dst: + ike_sa_name = f'dmvpn-{profile}-{tunnel}' + try: + # Get IKE SAs + sa_list = convert_data( + vyos.ipsec.get_vici_sas_by_name(ike_sa_name, None)) + if not sa_list: + raise vyos.opmode.IncorrectValue( + f'SA(s) for profile {profile} tunnel {tunnel} not found, aborting') + sa_nbma_list = list([x for x in sa_list if + ike_sa_name in x and x[ike_sa_name][ + 'remote-host'] == nbma_dst]) + if not sa_nbma_list: + raise vyos.opmode.IncorrectValue( + f'SA(s) for profile {profile} tunnel {tunnel} remote-host {nbma_dst} not found, aborting') + # terminate IKE SAs + vyos.ipsec.terminate_vici_ikeid_list(list( + [x[ike_sa_name]['uniqueid'] for x in sa_nbma_list if + ike_sa_name in x])) + # initiate IKE SAs + for ike in sa_nbma_list: + if ike_sa_name in ike: + vyos.ipsec.vici_initiate(ike_sa_name, 'dmvpn', + ike[ike_sa_name]['local-host'], + ike[ike_sa_name]['remote-host']) + print( + f'Profile {profile} tunnel {tunnel} remote-host {nbma_dst} reset result: success') + except (vyos.ipsec.ViciInitiateError) as err: + raise vyos.opmode.UnconfiguredSubsystem(err) + except (vyos.ipsec.ViciCommandError) as err: + raise vyos.opmode.IncorrectValue(err) + + +def reset_profile_all(profile: str, tunnel: str): + if profile and tunnel: + ike_sa_name = f'dmvpn-{profile}-{tunnel}' + try: + # Get IKE SAs + sa_list: list = convert_data( + vyos.ipsec.get_vici_sas_by_name(ike_sa_name, None)) + if not sa_list: + raise vyos.opmode.IncorrectValue( + f'SA(s) for profile {profile} tunnel {tunnel} not found, aborting') + # terminate IKE SAs + vyos.ipsec.terminate_vici_by_name(ike_sa_name, None) + # initiate IKE SAs + for ike in sa_list: + if ike_sa_name in ike: + vyos.ipsec.vici_initiate(ike_sa_name, 'dmvpn', + ike[ike_sa_name]['local-host'], + ike[ike_sa_name]['remote-host']) + print( + f'Profile {profile} tunnel {tunnel} remote-host {ike[ike_sa_name]["remote-host"]} reset result: success') + print(f'Profile {profile} tunnel {tunnel} reset result: success') + except (vyos.ipsec.ViciInitiateError) as err: + raise vyos.opmode.UnconfiguredSubsystem(err) + except (vyos.ipsec.ViciCommandError) as err: + raise vyos.opmode.IncorrectValue(err) + + def show_sa(raw: bool): sa_data = _get_raw_data_sas() if raw: diff --git a/src/op_mode/vpn_ipsec.py b/src/op_mode/vpn_ipsec.py index 2392cfe92..b81d1693e 100755 --- a/src/op_mode/vpn_ipsec.py +++ b/src/op_mode/vpn_ipsec.py @@ -16,12 +16,12 @@ import re import argparse -from subprocess import TimeoutExpired from vyos.util import call SWANCTL_CONF = '/etc/swanctl/swanctl.conf' + def get_peer_connections(peer, tunnel, return_all = False): search = rf'^[\s]*(peer_{peer}_(tunnel_[\d]+|vti)).*' matches = [] @@ -34,57 +34,6 @@ def get_peer_connections(peer, tunnel, return_all = False): matches.append(result[1]) return matches -def reset_peer(peer, tunnel): - if not peer: - print('Invalid peer, aborting') - return - - conns = get_peer_connections(peer, tunnel, return_all = (not tunnel or tunnel == 'all')) - - if not conns: - print('Tunnel(s) not found, aborting') - return - - result = True - for conn in conns: - try: - call(f'/usr/sbin/ipsec down {conn}{{*}}', timeout = 10) - call(f'/usr/sbin/ipsec up {conn}', timeout = 10) - except TimeoutExpired as e: - print(f'Timed out while resetting {conn}') - result = False - - - print('Peer reset result: ' + ('success' if result else 'failed')) - -def get_profile_connection(profile, tunnel = None): - search = rf'(dmvpn-{profile}-[\w]+)' if tunnel == 'all' else rf'(dmvpn-{profile}-{tunnel})' - with open(SWANCTL_CONF, 'r') as f: - for line in f.readlines(): - result = re.search(search, line) - if result: - return result[1] - return None - -def reset_profile(profile, tunnel): - if not profile: - print('Invalid profile, aborting') - return - - if not tunnel: - print('Invalid tunnel, aborting') - return - - conn = get_profile_connection(profile) - - if not conn: - print('Profile not found, aborting') - return - - call(f'/usr/sbin/ipsec down {conn}') - result = call(f'/usr/sbin/ipsec up {conn}') - - print('Profile reset result: ' + ('success' if result == 0 else 'failed')) def debug_peer(peer, tunnel): peer = peer.replace(':', '-') @@ -119,6 +68,7 @@ def debug_peer(peer, tunnel): for conn in conns: call(f'/usr/sbin/ipsec statusall | grep {conn}') + if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('--action', help='Control action', required=True) @@ -127,9 +77,6 @@ if __name__ == '__main__': args = parser.parse_args() - if args.action == 'reset-peer': - reset_peer(args.name, args.tunnel) - elif args.action == "reset-profile": - reset_profile(args.name, args.tunnel) - elif args.action == "vpn-debug": + + if args.action == "vpn-debug": debug_peer(args.name, args.tunnel) -- cgit v1.2.3