diff options
Diffstat (limited to 'src/op_mode')
-rwxr-xr-x | src/op_mode/accelppp.py | 38 | ||||
-rwxr-xr-x | src/op_mode/generate_public_key_command.py | 59 | ||||
-rwxr-xr-x | src/op_mode/ipsec.py | 125 | ||||
-rwxr-xr-x | src/op_mode/nhrp.py | 101 | ||||
-rwxr-xr-x | src/op_mode/openvpn.py | 10 | ||||
-rwxr-xr-x | src/op_mode/show_openconnect_otp.py | 2 |
6 files changed, 235 insertions, 100 deletions
diff --git a/src/op_mode/accelppp.py b/src/op_mode/accelppp.py index 2fd045dc3..87a25bb96 100755 --- a/src/op_mode/accelppp.py +++ b/src/op_mode/accelppp.py @@ -27,29 +27,51 @@ from vyos.util import rc_cmd accel_dict = { 'ipoe': { 'port': 2002, - 'path': 'service ipoe-server' + 'path': 'service ipoe-server', + 'base_path': 'service ipoe-server' }, 'pppoe': { 'port': 2001, - 'path': 'service pppoe-server' + 'path': 'service pppoe-server', + 'base_path': 'service pppoe-server' }, 'pptp': { 'port': 2003, - 'path': 'vpn pptp' + 'path': 'vpn pptp', + 'base_path': 'vpn pptp' }, 'l2tp': { 'port': 2004, - 'path': 'vpn l2tp' + 'path': 'vpn l2tp', + 'base_path': 'vpn l2tp remote-access' }, 'sstp': { 'port': 2005, - 'path': 'vpn sstp' + 'path': 'vpn sstp', + 'base_path': 'vpn sstp' } } -def _get_raw_statistics(accel_output, pattern): - return vyos.accel_ppp.get_server_statistics(accel_output, pattern, sep=':') +def _get_config_settings(protocol): + '''Get config dict from VyOS configuration''' + conf = ConfigTreeQuery() + base_path = accel_dict[protocol]['base_path'] + data = conf.get_config_dict(base_path, + key_mangling=('-', '_'), + get_first_key=True, + no_tag_node_value_mangle=True) + if conf.exists(f'{base_path} authentication local-users'): + # Delete sensitive data + del data['authentication']['local_users'] + return {'config_option': data} + + +def _get_raw_statistics(accel_output, pattern, protocol): + return { + **vyos.accel_ppp.get_server_statistics(accel_output, pattern, sep=':'), + **_get_config_settings(protocol) + } def _get_raw_sessions(port): @@ -103,7 +125,7 @@ def show_statistics(raw: bool, protocol: str): rc, output = rc_cmd(f'/usr/bin/accel-cmd -p {port} show stat') if raw: - return _get_raw_statistics(output, pattern) + return _get_raw_statistics(output, pattern, protocol) return output diff --git a/src/op_mode/generate_public_key_command.py b/src/op_mode/generate_public_key_command.py index f071ae350..8ba55c901 100755 --- a/src/op_mode/generate_public_key_command.py +++ b/src/op_mode/generate_public_key_command.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2022 VyOS maintainers and contributors +# Copyright (C) 2022-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 @@ -19,28 +19,51 @@ import sys import urllib.parse import vyos.remote +from vyos.template import generate_uuid4 -def get_key(path): + +def get_key(path) -> list: + """Get public keys from a local file or remote URL + + Args: + path: Path to the public keys file + + Returns: list of public keys split by new line + + """ url = urllib.parse.urlparse(path) if url.scheme == 'file' or url.scheme == '': with open(os.path.expanduser(path), 'r') as f: key_string = f.read() else: key_string = vyos.remote.get_remote_config(path) - return key_string.split() - -try: - username = sys.argv[1] - algorithm, key, identifier = get_key(sys.argv[2]) -except Exception as e: - print("Failed to retrieve the public key: {}".format(e)) - sys.exit(1) - -print('# To add this key as an embedded key, run the following commands:') -print('configure') -print(f'set system login user {username} authentication public-keys {identifier} key {key}') -print(f'set system login user {username} authentication public-keys {identifier} type {algorithm}') -print('commit') -print('save') -print('exit') + return key_string.split('\n') + + +if __name__ == "__main__": + first_loop = True + + for k in get_key(sys.argv[2]): + k = k.split() + # Skip empty list entry + if k == []: + continue + + try: + username = sys.argv[1] + # Github keys don't have identifier for example 'vyos@localhost' + # 'ssh-rsa AAAA... vyos@localhost' + # Generate uuid4 identifier + identifier = f'github@{generate_uuid4("")}' if sys.argv[2].startswith('https://github.com') else k[2] + algorithm, key = k[0], k[1] + except Exception as e: + print("Failed to retrieve the public key: {}".format(e)) + sys.exit(1) + + if first_loop: + print('# To add this key as an embedded key, run the following commands:') + print('configure') + print(f'set system login user {username} authentication public-keys {identifier} key {key}') + print(f'set system login user {username} authentication public-keys {identifier} type {algorithm}') + first_loop = False diff --git a/src/op_mode/ipsec.py b/src/op_mode/ipsec.py index f6417764a..8e76f4cc0 100755 --- a/src/op_mode/ipsec.py +++ b/src/op_mode/ipsec.py @@ -14,25 +14,19 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -import os import re import sys import typing -from collections import OrderedDict from hurry import filesize from re import split as re_split from tabulate import tabulate -from subprocess import TimeoutExpired -from vyos.util import call from vyos.util import convert_data from vyos.util import seconds_to_human import vyos.opmode - - -SWANCTL_CONF = '/etc/swanctl/swanctl.conf' +import vyos.ipsec def _convert(text): @@ -43,22 +37,13 @@ def _alphanum_key(key): return [_convert(c) for c in re_split('([0-9]+)', str(key))] -def _get_vici_sas(): - from vici import Session as vici_session - - try: - session = vici_session() - except Exception: - raise vyos.opmode.UnconfiguredSubsystem("IPsec not initialized") - sas = list(session.list_sas()) - return sas - - def _get_raw_data_sas(): - get_sas = _get_vici_sas() - sas = convert_data(get_sas) - return sas - + try: + get_sas = vyos.ipsec.get_vici_sas() + sas = convert_data(get_sas) + return sas + except (vyos.ipsec.ViciInitiateError) as err: + raise vyos.opmode.UnconfiguredSubsystem(err) def _get_formatted_output_sas(sas): sa_data = [] @@ -139,22 +124,14 @@ def _get_formatted_output_sas(sas): # Connections block -def _get_vici_connections(): - from vici import Session as vici_session - - try: - session = vici_session() - except Exception: - raise vyos.opmode.UnconfiguredSubsystem("IPsec not initialized") - connections = list(session.list_conns()) - return connections - def _get_convert_data_connections(): - get_connections = _get_vici_connections() - connections = convert_data(get_connections) - return connections - + try: + get_connections = vyos.ipsec.get_vici_connections() + connections = convert_data(get_connections) + return connections + except (vyos.ipsec.ViciInitiateError) as err: + raise vyos.opmode.UnconfiguredSubsystem(err) def _get_parent_sa_proposal(connection_name: str, data: list) -> dict: """Get parent SA proposals by connection name @@ -239,7 +216,8 @@ def _get_child_sa_state(connection_name: str, tunnel_name: str, # Get all child SA states # there can be multiple SAs per tunnel child_sa_states = [ - v['state'] for k, v in child_sas.items() if v['name'] == tunnel_name + v['state'] for k, v in child_sas.items() if + v['name'] == tunnel_name ] return 'up' if 'INSTALLED' in child_sa_states else child_sa @@ -406,39 +384,46 @@ def _get_formatted_output_conections(data): # Connections block end -def get_peer_connections(peer, tunnel): - search = rf'^[\s]*({peer}-(tunnel-[\d]+|vti)).*' - matches = [] - if not os.path.exists(SWANCTL_CONF): - raise vyos.opmode.UnconfiguredSubsystem("IPsec not initialized") - suffix = None if tunnel is None else (f'tunnel-{tunnel}' if - tunnel.isnumeric() else tunnel) - with open(SWANCTL_CONF, 'r') as f: - for line in f.readlines(): - result = re.match(search, line) - if result: - if tunnel is None: - matches.append(result[1]) - else: - if result[2] == suffix: - matches.append(result[1]) - return matches - - -def reset_peer(peer: str, tunnel:typing.Optional[str]): - conns = get_peer_connections(peer, tunnel) - - if not conns: - raise vyos.opmode.IncorrectValue('Peer or tunnel(s) not found, aborting') - - for conn in conns: - try: - call(f'sudo /usr/sbin/ipsec down {conn}{{*}}', timeout = 10) - call(f'sudo /usr/sbin/ipsec up {conn}', timeout = 10) - except TimeoutExpired as e: - raise vyos.opmode.InternalError(f'Timed out while resetting {conn}') - - print('Peer reset result: success') +def _get_childsa_id_list(ike_sas: list) -> list: + """ + Generate list of CHILD SA ids based on list of OrderingDict + wich is returned by vici + :param ike_sas: list of IKE SAs generated by vici + :type ike_sas: list + :return: list of IKE SAs ids + :rtype: list + """ + list_childsa_id: list = [] + for ike in ike_sas: + for ike_sa in ike.values(): + for child_sa in ike_sa['child-sas'].values(): + list_childsa_id.append(child_sa['uniqueid'].decode('ascii')) + return list_childsa_id + + +def reset_peer(peer: str, tunnel: typing.Optional[str] = None): + # Convert tunnel to Strongwan format of CHILD_SA + if tunnel: + if tunnel.isnumeric(): + tunnel = f'{peer}-tunnel-{tunnel}' + elif tunnel == 'vti': + tunnel = f'{peer}-vti' + try: + sa_list: list = vyos.ipsec.get_vici_sas_by_name(peer, tunnel) + + if not sa_list: + raise vyos.opmode.IncorrectValue('Peer not found, aborting') + if tunnel and sa_list: + childsa_id_list: list = _get_childsa_id_list(sa_list) + if not childsa_id_list: + raise vyos.opmode.IncorrectValue( + 'Peer or tunnel(s) not found, aborting') + vyos.ipsec.terminate_vici_by_name(peer, tunnel) + print('Peer reset result: success') + except (vyos.ipsec.ViciInitiateError) as err: + raise vyos.opmode.UnconfiguredSubsystem(err) + except (vyos.ipsec.ViciInitiateError) as err: + raise vyos.opmode.IncorrectValue(err) def show_sa(raw: bool): diff --git a/src/op_mode/nhrp.py b/src/op_mode/nhrp.py new file mode 100755 index 000000000..5ff91a59c --- /dev/null +++ b/src/op_mode/nhrp.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 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 <http://www.gnu.org/licenses/>. + +import sys +import tabulate +import vyos.opmode + +from vyos.util import cmd +from vyos.util import process_named_running +from vyos.util import colon_separated_to_dict + + +def _get_formatted_output(output_dict: dict) -> str: + """ + Create formatted table for CLI output + :param output_dict: dictionary for API + :type output_dict: dict + :return: tabulate string + :rtype: str + """ + print(f"Status: {output_dict['Status']}") + output: str = tabulate.tabulate(output_dict['routes'], headers='keys', + numalign="left") + return output + + +def _get_formatted_dict(output_string: str) -> dict: + """ + Format string returned from CMD to API list + :param output_string: String received by CMD + :type output_string: str + :return: dictionary for API + :rtype: dict + """ + formatted_dict: dict = { + 'Status': '', + 'routes': [] + } + output_list: list = output_string.split('\n\n') + for list_a in output_list: + output_dict = colon_separated_to_dict(list_a, True) + if 'Status' in output_dict: + formatted_dict['Status'] = output_dict['Status'] + else: + formatted_dict['routes'].append(output_dict) + return formatted_dict + + +def show_interface(raw: bool): + """ + Command 'show nhrp interface' + :param raw: if API + :type raw: bool + """ + if not process_named_running('opennhrp'): + raise vyos.opmode.UnconfiguredSubsystem('OpenNHRP is not running.') + interface_string: str = cmd('sudo opennhrpctl interface show') + interface_dict: dict = _get_formatted_dict(interface_string) + if raw: + return interface_dict + else: + return _get_formatted_output(interface_dict) + + +def show_tunnel(raw: bool): + """ + Command 'show nhrp tunnel' + :param raw: if API + :type raw: bool + """ + if not process_named_running('opennhrp'): + raise vyos.opmode.UnconfiguredSubsystem('OpenNHRP is not running.') + tunnel_string: str = cmd('sudo opennhrpctl show') + tunnel_dict: list = _get_formatted_dict(tunnel_string) + if raw: + return tunnel_dict + else: + return _get_formatted_output(tunnel_dict) + + +if __name__ == '__main__': + try: + res = vyos.opmode.run(sys.modules[__name__]) + if res: + print(res) + except (ValueError, vyos.opmode.Error) as e: + print(e) + sys.exit(1) diff --git a/src/op_mode/openvpn.py b/src/op_mode/openvpn.py index 3797a7153..79130c7c0 100755 --- a/src/op_mode/openvpn.py +++ b/src/op_mode/openvpn.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2022 VyOS maintainers and contributors +# Copyright (C) 2022-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 @@ -153,6 +153,8 @@ def _get_raw_data(mode: str) -> dict: d = data[intf] d['local_host'] = conf_dict[intf].get('local-host', '') d['local_port'] = conf_dict[intf].get('local-port', '') + if conf.exists(f'interfaces openvpn {intf} server client'): + d['configured_clients'] = conf.list_nodes(f'interfaces openvpn {intf} server client') if mode in ['client', 'site-to-site']: for client in d['clients']: if 'shared-secret-key-file' in list(conf_dict[intf]): @@ -171,8 +173,8 @@ def _format_openvpn(data: dict) -> str: 'TX bytes', 'RX bytes', 'Connected Since'] out = '' - data_out = [] for intf in list(data): + data_out = [] l_host = data[intf]['local_host'] l_port = data[intf]['local_port'] for client in list(data[intf]['clients']): @@ -190,7 +192,9 @@ def _format_openvpn(data: dict) -> str: data_out.append([name, remote, tunnel, local, tx_bytes, rx_bytes, online_since]) - out += tabulate(data_out, headers) + if data_out: + out += tabulate(data_out, headers) + out += "\n" return out diff --git a/src/op_mode/show_openconnect_otp.py b/src/op_mode/show_openconnect_otp.py index ae532ccc9..88982c50b 100755 --- a/src/op_mode/show_openconnect_otp.py +++ b/src/op_mode/show_openconnect_otp.py @@ -46,7 +46,7 @@ def get_otp_ocserv(username): # options which we need to update into the dictionary retrived. default_values = defaults(base) ocserv = dict_merge(default_values, ocserv) - # workaround a "know limitation" - https://phabricator.vyos.net/T2665 + # workaround a "know limitation" - https://vyos.dev/T2665 del ocserv['authentication']['local_users']['username']['otp'] if not ocserv["authentication"]["local_users"]["username"]: return None |