From 201257fe60afc40d101d162cc08e2878dfa3467b Mon Sep 17 00:00:00 2001 From: zsdc Date: Tue, 4 Jan 2022 15:38:29 +0200 Subject: ipsec: T1925: Fixed `show vpn ipsec sa` output After the a1aaf4fb9c0e4111670ef3dd491796fa35a2311f commit, only single (latest) CHILD_SA for each connection can be displayed in the `show vpn ipsec sa` output. This commit backs the proper behavior for the command and adds a little optimization to the formatter to make it easier. --- src/op_mode/show_ipsec_sa.py | 186 +++++++++++++++++++++---------------------- 1 file changed, 92 insertions(+), 94 deletions(-) (limited to 'src/op_mode') diff --git a/src/op_mode/show_ipsec_sa.py b/src/op_mode/show_ipsec_sa.py index e72f0f965..5b8f00dba 100755 --- a/src/op_mode/show_ipsec_sa.py +++ b/src/op_mode/show_ipsec_sa.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2019 VyOS maintainers and contributors +# Copyright (C) 2022 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 @@ -14,119 +14,117 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import re -import sys +from re import split as re_split +from sys import exit -import vici -import tabulate -import hurry.filesize +from hurry import filesize +from tabulate import tabulate +from vici import Session as vici_session + +from vyos.util import seconds_to_human -import vyos.util def convert(text): return int(text) if text.isdigit() else text.lower() + def alphanum_key(key): - return [convert(c) for c in re.split('([0-9]+)', str(key))] + return [convert(c) for c in re_split('([0-9]+)', str(key))] -def format_output(conns, sas): + +def format_output(sas): sa_data = [] - for peer, parent_conn in conns.items(): - if peer not in sas: - continue - - parent_sa = sas[peer] - child_sas = parent_sa['child-sas'] - installed_sas = {v['name'].decode(): v for k, v in child_sas.items() if v["state"] == b"INSTALLED"} - - # parent_sa["state"] = IKE state, child_sas["state"] = ESP state - state = 'down' - uptime = 'N/A' - - if parent_sa["state"] == b"ESTABLISHED" and installed_sas: - state = "up" - - remote_host = parent_sa["remote-host"].decode() - remote_id = parent_sa["remote-id"].decode() - - if remote_host == remote_id: - remote_id = "N/A" - - # The counters can only be obtained from the child SAs - for child_conn in parent_conn['children']: - if child_conn not in installed_sas: - data = [child_conn, "down", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A"] - sa_data.append(data) - continue - - isa = installed_sas[child_conn] - csa_name = isa['name'] - csa_name = csa_name.decode() - - bytes_in = hurry.filesize.size(int(isa["bytes-in"].decode())) - bytes_out = hurry.filesize.size(int(isa["bytes-out"].decode())) - bytes_str = "{0}/{1}".format(bytes_in, bytes_out) - - pkts_in = hurry.filesize.size(int(isa["packets-in"].decode()), system=hurry.filesize.si) - pkts_out = hurry.filesize.size(int(isa["packets-out"].decode()), system=hurry.filesize.si) - pkts_str = "{0}/{1}".format(pkts_in, pkts_out) - # Remove B from <1K values - pkts_str = re.sub(r'B', r'', pkts_str) - - uptime = vyos.util.seconds_to_human(isa['install-time'].decode()) - - enc = isa["encr-alg"].decode() - if "encr-keysize" in isa: - key_size = isa["encr-keysize"].decode() - else: - key_size = "" - if "integ-alg" in isa: - hash = isa["integ-alg"].decode() - else: - hash = "" - if "dh-group" in isa: - dh_group = isa["dh-group"].decode() - else: - dh_group = "" - - proposal = enc - if key_size: - proposal = "{0}_{1}".format(proposal, key_size) - if hash: - proposal = "{0}/{1}".format(proposal, hash) - if dh_group: - proposal = "{0}/{1}".format(proposal, dh_group) - - data = [csa_name, state, uptime, bytes_str, pkts_str, remote_host, remote_id, proposal] - sa_data.append(data) + for sa in sas: + for parent_sa in sa.values(): + # create an item for each child-sa + for child_sa in parent_sa.get('child-sas', {}).values(): + # prepare a list for output data + sa_out_name = sa_out_state = sa_out_uptime = sa_out_bytes = sa_out_packets = sa_out_remote_addr = sa_out_remote_id = sa_out_proposal = 'N/A' + + # collect raw data + sa_name = child_sa.get('name') + sa_state = child_sa.get('state') + sa_uptime = child_sa.get('install-time') + sa_bytes_in = child_sa.get('bytes-in') + sa_bytes_out = child_sa.get('bytes-out') + sa_packets_in = child_sa.get('packets-in') + sa_packets_out = child_sa.get('packets-out') + sa_remote_addr = parent_sa.get('remote-host') + sa_remote_id = parent_sa.get('remote-id') + sa_proposal_encr_alg = child_sa.get('encr-alg') + sa_proposal_integ_alg = child_sa.get('integ-alg') + sa_proposal_encr_keysize = child_sa.get('encr-keysize') + sa_proposal_dh_group = child_sa.get('dh-group') + + # format data to display + if sa_name: + sa_out_name = sa_name.decode() + if sa_state: + if sa_state == b'INSTALLED': + sa_out_state = 'up' + else: + sa_out_state = 'down' + if sa_uptime: + sa_out_uptime = seconds_to_human(sa_uptime.decode()) + if sa_bytes_in and sa_bytes_out: + bytes_in = filesize.size(int(sa_bytes_in.decode())) + bytes_out = filesize.size(int(sa_bytes_out.decode())) + sa_out_bytes = f'{bytes_in}/{bytes_out}' + if sa_packets_in and sa_packets_out: + packets_in = filesize.size(int(sa_packets_in.decode()), + system=filesize.si) + packets_out = filesize.size(int(sa_packets_out.decode()), + system=filesize.si) + sa_out_packets = f'{packets_in}/{packets_out}' + if sa_remote_addr: + sa_out_remote_addr = sa_remote_addr.decode() + if sa_remote_id: + sa_out_remote_id = sa_remote_id.decode() + # format proposal + if sa_proposal_encr_alg: + sa_out_proposal = sa_proposal_encr_alg.decode() + if sa_proposal_encr_keysize: + sa_proposal_encr_keysize_str = sa_proposal_encr_keysize.decode() + sa_out_proposal = f'{sa_out_proposal}_{sa_proposal_encr_keysize_str}' + if sa_proposal_integ_alg: + sa_proposal_integ_alg_str = sa_proposal_integ_alg.decode() + sa_out_proposal = f'{sa_out_proposal}/{sa_proposal_integ_alg_str}' + if sa_proposal_dh_group: + sa_proposal_dh_group_str = sa_proposal_dh_group.decode() + sa_out_proposal = f'{sa_out_proposal}/{sa_proposal_dh_group_str}' + + # add a new item to output data + sa_data.append([ + sa_out_name, sa_out_state, sa_out_uptime, sa_out_bytes, + sa_out_packets, sa_out_remote_addr, sa_out_remote_id, + sa_out_proposal + ]) + + # return output data return sa_data + if __name__ == '__main__': try: - session = vici.Session() - conns = {} - sas = {} + session = vici_session() + sas = list(session.list_sas()) - for conn in session.list_conns(): - for key in conn: - conns[key] = conn[key] - - for sa in session.list_sas(): - for key in sa: - sas[key] = sa[key] - - headers = ["Connection", "State", "Uptime", "Bytes In/Out", "Packets In/Out", "Remote address", "Remote ID", "Proposal"] - sa_data = format_output(conns, sas) + sa_data = format_output(sas) sa_data = sorted(sa_data, key=alphanum_key) - output = tabulate.tabulate(sa_data, headers) + + headers = [ + "Connection", "State", "Uptime", "Bytes In/Out", "Packets In/Out", + "Remote address", "Remote ID", "Proposal" + ] + output = tabulate(sa_data, headers) print(output) except PermissionError: print("You do not have a permission to connect to the IPsec daemon") - sys.exit(1) + exit(1) except ConnectionRefusedError: print("IPsec is not runing") - sys.exit(1) + exit(1) except Exception as e: print("An error occured: {0}".format(e)) - sys.exit(1) + exit(1) -- cgit v1.2.3 From ed67750b94e8bc779ec0e2cf6d568a3f7292de13 Mon Sep 17 00:00:00 2001 From: sarthurdev <965089+sarthurdev@users.noreply.github.com> Date: Sat, 29 Jan 2022 13:18:28 +0100 Subject: firewall: T4218: Adds a prefix to all user defined chains --- data/templates/firewall/nftables.tmpl | 4 ++-- data/templates/zone_policy/nftables.tmpl | 12 ++++++------ python/vyos/template.py | 3 ++- smoketest/scripts/cli/test_firewall.py | 6 +++--- smoketest/scripts/cli/test_zone_policy.py | 4 ++-- src/conf_mode/firewall-interface.py | 11 +++++++---- src/conf_mode/firewall.py | 7 +++++-- src/op_mode/firewall.py | 3 ++- 8 files changed, 29 insertions(+), 21 deletions(-) (limited to 'src/op_mode') diff --git a/data/templates/firewall/nftables.tmpl b/data/templates/firewall/nftables.tmpl index 33c821e84..468a5a32f 100644 --- a/data/templates/firewall/nftables.tmpl +++ b/data/templates/firewall/nftables.tmpl @@ -32,7 +32,7 @@ table ip filter { {% endif %} {% if name is defined %} {% for name_text, conf in name.items() %} - chain {{ name_text }} { + chain NAME_{{ name_text }} { {% if conf.rule is defined %} {% for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not defined %} {{ rule_conf | nft_rule(name_text, rule_id) }} @@ -82,7 +82,7 @@ table ip6 filter { {% endif %} {% if ipv6_name is defined %} {% for name_text, conf in ipv6_name.items() %} - chain {{ name_text }} { + chain NAME6_{{ name_text }} { {% if conf.rule is defined %} {% for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not defined %} {{ rule_conf | nft_rule(name_text, rule_id, 'ip6') }} diff --git a/data/templates/zone_policy/nftables.tmpl b/data/templates/zone_policy/nftables.tmpl index e59208a0d..093da6bd8 100644 --- a/data/templates/zone_policy/nftables.tmpl +++ b/data/templates/zone_policy/nftables.tmpl @@ -13,7 +13,7 @@ table ip filter { chain VZONE_{{ zone_name }}_IN { iifname lo counter return {% for from_zone, from_conf in zone_conf.from.items() if from_conf.firewall.name is defined %} - iifname { {{ zone[from_zone].interface | join(",") }} } counter jump {{ from_conf.firewall.name }} + iifname { {{ zone[from_zone].interface | join(",") }} } counter jump NAME_{{ from_conf.firewall.name }} iifname { {{ zone[from_zone].interface | join(",") }} } counter return {% endfor %} counter {{ zone_conf.default_action if zone_conf.default_action is defined else 'drop' }} @@ -21,7 +21,7 @@ table ip filter { chain VZONE_{{ zone_name }}_OUT { oifname lo counter return {% for from_zone, from_conf in zone_conf.from_local.items() if from_conf.firewall.name is defined %} - oifname { {{ zone[from_zone].interface | join(",") }} } counter jump {{ from_conf.firewall.name }} + oifname { {{ zone[from_zone].interface | join(",") }} } counter jump NAME_{{ from_conf.firewall.name }} oifname { {{ zone[from_zone].interface | join(",") }} } counter return {% endfor %} counter {{ zone_conf.default_action if zone_conf.default_action is defined else 'drop' }} @@ -34,7 +34,7 @@ table ip filter { {% endif %} {% for from_zone, from_conf in zone_conf.from.items() if from_conf.firewall.name is defined %} {% if zone[from_zone].local_zone is not defined %} - iifname { {{ zone[from_zone].interface | join(",") }} } counter jump {{ from_conf.firewall.name }} + iifname { {{ zone[from_zone].interface | join(",") }} } counter jump NAME_{{ from_conf.firewall.name }} iifname { {{ zone[from_zone].interface | join(",") }} } counter return {% endif %} {% endfor %} @@ -50,7 +50,7 @@ table ip6 filter { chain VZONE6_{{ zone_name }}_IN { iifname lo counter return {% for from_zone, from_conf in zone_conf.from.items() if from_conf.firewall.ipv6_name is defined %} - iifname { {{ zone[from_zone].interface | join(",") }} } counter jump {{ from_conf.firewall.ipv6_name }} + iifname { {{ zone[from_zone].interface | join(",") }} } counter jump NAME6_{{ from_conf.firewall.ipv6_name }} iifname { {{ zone[from_zone].interface | join(",") }} } counter return {% endfor %} counter {{ zone_conf.default_action if zone_conf.default_action is defined else 'drop' }} @@ -58,7 +58,7 @@ table ip6 filter { chain VZONE6_{{ zone_name }}_OUT { oifname lo counter return {% for from_zone, from_conf in zone_conf.from_local.items() if from_conf.firewall.ipv6_name is defined %} - oifname { {{ zone[from_zone].interface | join(",") }} } counter jump {{ from_conf.firewall.ipv6_name }} + oifname { {{ zone[from_zone].interface | join(",") }} } counter jump NAME6_{{ from_conf.firewall.ipv6_name }} oifname { {{ zone[from_zone].interface | join(",") }} } counter return {% endfor %} counter {{ zone_conf.default_action if zone_conf.default_action is defined else 'drop' }} @@ -71,7 +71,7 @@ table ip6 filter { {% endif %} {% for from_zone, from_conf in zone_conf.from.items() if from_conf.firewall.ipv6_name is defined %} {% if zone[from_zone].local_zone is not defined %} - iifname { {{ zone[from_zone].interface | join(",") }} } counter jump {{ from_conf.firewall.ipv6_name }} + iifname { {{ zone[from_zone].interface | join(",") }} } counter jump NAME6_{{ from_conf.firewall.ipv6_name }} iifname { {{ zone[from_zone].interface | join(",") }} } counter return {% endif %} {% endfor %} diff --git a/python/vyos/template.py b/python/vyos/template.py index 633b28ade..3675aef5d 100644 --- a/python/vyos/template.py +++ b/python/vyos/template.py @@ -548,6 +548,7 @@ def nft_intra_zone_action(zone_conf, ipv6=False): if 'intra_zone_filtering' in zone_conf: intra_zone = zone_conf['intra_zone_filtering'] fw_name = 'ipv6_name' if ipv6 else 'name' + name_prefix = 'NAME6_' if ipv6 else 'NAME_' if 'action' in intra_zone: if intra_zone['action'] == 'accept': @@ -555,5 +556,5 @@ def nft_intra_zone_action(zone_conf, ipv6=False): return intra_zone['action'] elif dict_search_args(intra_zone, 'firewall', fw_name): name = dict_search_args(intra_zone, 'firewall', fw_name) - return f'jump {name}' + return f'jump {name_prefix}{name}' return 'return' diff --git a/smoketest/scripts/cli/test_firewall.py b/smoketest/scripts/cli/test_firewall.py index 6b74e6c92..ecc0c29a0 100755 --- a/smoketest/scripts/cli/test_firewall.py +++ b/smoketest/scripts/cli/test_firewall.py @@ -63,7 +63,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase): self.cli_commit() nftables_search = [ - ['iifname "eth0"', 'jump smoketest'], + ['iifname "eth0"', 'jump NAME_smoketest'], ['ip saddr { 172.16.99.0/24 }', 'ip daddr 172.16.10.10', 'th dport { 53, 123 }', 'return'], ['ether saddr { 00:01:02:03:04:05 }', 'return'] ] @@ -94,7 +94,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase): self.cli_commit() nftables_search = [ - ['iifname "eth0"', 'jump smoketest'], + ['iifname "eth0"', 'jump NAME_smoketest'], ['saddr 172.16.20.10', 'daddr 172.16.10.10', 'return'], ['tcp flags & (syn | ack) == syn', 'tcp dport { 8888 }', 'reject'], ['smoketest default-action', 'drop'] @@ -124,7 +124,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase): self.cli_commit() nftables_search = [ - ['iifname "eth0"', 'jump v6-smoketest'], + ['iifname "eth0"', 'jump NAME6_v6-smoketest'], ['saddr 2002::1', 'daddr 2002::1:1', 'return'], ['meta l4proto { tcp, udp }', 'th dport { 8888 }', 'reject'], ['smoketest default-action', 'drop'] diff --git a/smoketest/scripts/cli/test_zone_policy.py b/smoketest/scripts/cli/test_zone_policy.py index c0af6164b..00dfe0182 100755 --- a/smoketest/scripts/cli/test_zone_policy.py +++ b/smoketest/scripts/cli/test_zone_policy.py @@ -44,8 +44,8 @@ class TestZonePolicy(VyOSUnitTestSHIM.TestCase): ['oifname { "eth0" }', 'jump VZONE_smoketest-eth0'], ['jump VZONE_smoketest-local_IN'], ['jump VZONE_smoketest-local_OUT'], - ['iifname { "eth0" }', 'jump smoketest'], - ['oifname { "eth0" }', 'jump smoketest'] + ['iifname { "eth0" }', 'jump NAME_smoketest'], + ['oifname { "eth0" }', 'jump NAME_smoketest'] ] nftables_output = cmd('sudo nft list table ip filter') diff --git a/src/conf_mode/firewall-interface.py b/src/conf_mode/firewall-interface.py index a7442ecbd..9a5d278e9 100755 --- a/src/conf_mode/firewall-interface.py +++ b/src/conf_mode/firewall-interface.py @@ -31,6 +31,9 @@ from vyos import ConfigError from vyos import airbag airbag.enable() +NAME_PREFIX = 'NAME_' +NAME6_PREFIX = 'NAME6_' + NFT_CHAINS = { 'in': 'VYOS_FW_FORWARD', 'out': 'VYOS_FW_FORWARD', @@ -127,7 +130,7 @@ def apply(if_firewall): name = dict_search_args(if_firewall, direction, 'name') if name: - rule_exists = cleanup_rule('ip filter', chain, if_prefix, ifname, name) + rule_exists = cleanup_rule('ip filter', chain, if_prefix, ifname, f'{NAME_PREFIX}{name}') if not rule_exists: rule_action = 'insert' @@ -138,13 +141,13 @@ def apply(if_firewall): rule_action = 'add' rule_prefix = f'position {handle}' - run(f'nft {rule_action} rule ip filter {chain} {rule_prefix} {if_prefix}ifname {ifname} counter jump {name}') + run(f'nft {rule_action} rule ip filter {chain} {rule_prefix} {if_prefix}ifname {ifname} counter jump {NAME_PREFIX}{name}') else: cleanup_rule('ip filter', chain, if_prefix, ifname) ipv6_name = dict_search_args(if_firewall, direction, 'ipv6_name') if ipv6_name: - rule_exists = cleanup_rule('ip6 filter', ipv6_chain, if_prefix, ifname, ipv6_name) + rule_exists = cleanup_rule('ip6 filter', ipv6_chain, if_prefix, ifname, f'{NAME6_PREFIX}{ipv6_name}') if not rule_exists: rule_action = 'insert' @@ -155,7 +158,7 @@ def apply(if_firewall): rule_action = 'add' rule_prefix = f'position {handle}' - run(f'nft {rule_action} rule ip6 filter {ipv6_chain} {rule_prefix} {if_prefix}ifname {ifname} counter jump {ipv6_name}') + run(f'nft {rule_action} rule ip6 filter {ipv6_chain} {rule_prefix} {if_prefix}ifname {ifname} counter jump {NAME6_PREFIX}{ipv6_name}') else: cleanup_rule('ip6 filter', ipv6_chain, if_prefix, ifname) diff --git a/src/conf_mode/firewall.py b/src/conf_mode/firewall.py index 358b938e3..5b6c57d04 100755 --- a/src/conf_mode/firewall.py +++ b/src/conf_mode/firewall.py @@ -54,6 +54,9 @@ sysfs_config = { 'twa_hazards_protection': {'sysfs': '/proc/sys/net/ipv4/tcp_rfc1337'} } +NAME_PREFIX = 'NAME_' +NAME6_PREFIX = 'NAME6_' + preserve_chains = [ 'INPUT', 'FORWARD', @@ -281,9 +284,9 @@ def cleanup_commands(firewall): else: commands.append(f'flush chain {table} {chain}') elif chain not in preserve_chains and not chain.startswith("VZONE"): - if table == 'ip filter' and dict_search_args(firewall, 'name', chain): + if table == 'ip filter' and dict_search_args(firewall, 'name', chain.replace(NAME_PREFIX, "", 1)): commands.append(f'flush chain {table} {chain}') - elif table == 'ip6 filter' and dict_search_args(firewall, 'ipv6_name', chain): + elif table == 'ip6 filter' and dict_search_args(firewall, 'ipv6_name', chain.replace(NAME6_PREFIX, "", 1)): commands.append(f'flush chain {table} {chain}') else: commands += cleanup_rule(table, chain) diff --git a/src/op_mode/firewall.py b/src/op_mode/firewall.py index b6bb5b802..3146fc357 100755 --- a/src/op_mode/firewall.py +++ b/src/op_mode/firewall.py @@ -88,7 +88,8 @@ def get_config_firewall(conf, name=None, ipv6=False, interfaces=True): def get_nftables_details(name, ipv6=False): suffix = '6' if ipv6 else '' - command = f'sudo nft list chain ip{suffix} filter {name}' + name_prefix = 'NAME6_' if ipv6 else 'NAME_' + command = f'sudo nft list chain ip{suffix} filter {name_prefix}{name}' try: results = cmd(command) except: -- cgit v1.2.3 From 7f7be911b749b6c65ac3c6e57192e9e4ce2dcd24 Mon Sep 17 00:00:00 2001 From: Viacheslav Hletenko Date: Thu, 10 Feb 2022 22:05:19 +0000 Subject: openvpn: T4236: Add generator for ovpn configurations in op-mode This generator generates client .ovpn files with required initial configuration It gets information from interface vtun, pki ca and certificates --- .../generate-openvpn-config-client.xml.in | 58 +++++++++ src/op_mode/generate_ovpn_client_file.py | 145 +++++++++++++++++++++ 2 files changed, 203 insertions(+) create mode 100644 op-mode-definitions/generate-openvpn-config-client.xml.in create mode 100755 src/op_mode/generate_ovpn_client_file.py (limited to 'src/op_mode') diff --git a/op-mode-definitions/generate-openvpn-config-client.xml.in b/op-mode-definitions/generate-openvpn-config-client.xml.in new file mode 100644 index 000000000..4f9f31bfe --- /dev/null +++ b/op-mode-definitions/generate-openvpn-config-client.xml.in @@ -0,0 +1,58 @@ + + + + + + + Generate OpenVPN client configuration ovpn file + + + + + Generate Client config + + + + + Local interface used for connection + + + + + + + + CA certificate + + pki ca + + + + + + Cerificate used by client + + pki certificate + + + + + + Certificate key used by client + + sudo ${vyos_op_scripts_dir}/generate_ovpn_client_file.py --interface "$5" --ca "$7" --cert "$9" --key "${11}" + + + sudo ${vyos_op_scripts_dir}/generate_ovpn_client_file.py --interface "$5" --ca "$7" --cert "$9" + + + + + + + + + + + + diff --git a/src/op_mode/generate_ovpn_client_file.py b/src/op_mode/generate_ovpn_client_file.py new file mode 100755 index 000000000..29db41e37 --- /dev/null +++ b/src/op_mode/generate_ovpn_client_file.py @@ -0,0 +1,145 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2022 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 argparse +import os + +from jinja2 import Template + +from vyos.configquery import ConfigTreeQuery +from vyos.ifconfig import Section +from vyos.util import cmd + + +client_config = """ + +client +nobind +remote {{ remote_host }} {{ port }} +remote-cert-tls server +proto {{ 'tcp-client' if protocol == 'tcp-active' else 'udp' }} +dev {{ device }} +dev-type {{ device }} +persist-key +persist-tun +verb 3 + +# Encryption options +{% if encryption is defined and encryption is not none %} +{% if encryption.cipher is defined and encryption.cipher is not none %} +cipher {{ encryption.cipher }} +{% if encryption.cipher == 'bf128' %} +keysize 128 +{% elif encryption.cipher == 'bf256' %} +keysize 256 +{% endif %} +{% endif %} +{% if encryption.ncp_ciphers is defined and encryption.ncp_ciphers is not none %} +data-ciphers {{ encryption.ncp_ciphers }} +{% endif %} +{% endif %} + +{% if hash is defined and hash is not none %} +auth {{ hash }} +{% endif %} +keysize 256 +comp-lzo {{ '' if use_lzo_compression is defined else 'no' }} + + +-----BEGIN CERTIFICATE----- +{{ ca }} +-----END CERTIFICATE----- + + + + +-----BEGIN CERTIFICATE----- +{{ cert }} +-----END CERTIFICATE----- + + + + +-----BEGIN PRIVATE KEY----- +{{ key }} +-----END PRIVATE KEY----- + + + +""" + +config = ConfigTreeQuery() +base = ['interfaces', 'openvpn'] + +if not config.exists(base): + print('OpenVPN not configured') + exit(0) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument("-i", "--interface", type=str, help='OpenVPN interface the client is connecting to', required=True) + parser.add_argument("-a", "--ca", type=str, help='OpenVPN CA cerificate', required=True) + parser.add_argument("-c", "--cert", type=str, help='OpenVPN client cerificate', required=True) + parser.add_argument("-k", "--key", type=str, help='OpenVPN client cerificate key', action="store") + args = parser.parse_args() + + interface = args.interface + ca = args.ca + cert = args.cert + key = args.key + if not key: + key = args.cert + + if interface not in Section.interfaces('openvpn'): + exit(f'OpenVPN interface "{interface}" does not exist!') + + if not config.exists(['pki', 'ca', ca, 'certificate']): + exit(f'OpenVPN CA certificate "{ca}" does not exist!') + + if not config.exists(['pki', 'certificate', cert, 'certificate']): + exit(f'OpenVPN certificate "{cert}" does not exist!') + + if not config.exists(['pki', 'certificate', cert, 'private', 'key']): + exit(f'OpenVPN certificate key "{key}" does not exist!') + + ca = config.value(['pki', 'ca', ca, 'certificate']) + cert = config.value(['pki', 'certificate', cert, 'certificate']) + key = config.value(['pki', 'certificate', key, 'private', 'key']) + remote_host = config.value(base + [interface, 'local-host']) + + ovpn_conf = config.get_config_dict(base + [interface], key_mangling=('-', '_'), get_first_key=True) + + port = '1194' if 'local_port' not in ovpn_conf else ovpn_conf['local_port'] + proto = 'udp' if 'protocol' not in ovpn_conf else ovpn_conf['protocol'] + device = 'tun' if 'device_type' not in ovpn_conf else ovpn_conf['device_type'] + + config = { + 'interface' : interface, + 'ca' : ca, + 'cert' : cert, + 'key' : key, + 'device' : device, + 'port' : port, + 'proto' : proto, + 'remote_host' : remote_host, + 'address' : [], + } + +# Clear out terminal first +print('\x1b[2J\x1b[H') +client = Template(client_config, trim_blocks=True).render(config) +print(client) -- cgit v1.2.3 From f23040a0f7d425550350f91410272196f842308e Mon Sep 17 00:00:00 2001 From: srividya0208 Date: Sun, 20 Feb 2022 15:06:21 -0500 Subject: T4115:Reboot:Options "in" and "at" are not working When reboot is executed with "in" option it only accepts minutes till 99 value and does not accept greater values and "at" is also working same like in option where as it should work with exact timings. --- op-mode-definitions/reboot.xml.in | 4 ++-- src/op_mode/powerctrl.py | 25 +++++++++++++++++++++---- 2 files changed, 23 insertions(+), 6 deletions(-) (limited to 'src/op_mode') diff --git a/op-mode-definitions/reboot.xml.in b/op-mode-definitions/reboot.xml.in index 2c8daec5d..6414742d9 100644 --- a/op-mode-definitions/reboot.xml.in +++ b/op-mode-definitions/reboot.xml.in @@ -25,7 +25,7 @@ <Minutes> - sudo ${vyos_op_scripts_dir}/powerctrl.py --yes --reboot $3 $4 + sudo ${vyos_op_scripts_dir}/powerctrl.py --yes --reboot_in $3 $4 @@ -40,7 +40,7 @@ Reboot at a specific date - <DDMMYYYY> <DD/MM/YYYY> <DD.MM.YYYY> <DD:MM:YYYY> + <DD/MM/YYYY> <DD.MM.YYYY> <DD:MM:YYYY> sudo ${vyos_op_scripts_dir}/powerctrl.py --yes --reboot $3 $5 diff --git a/src/op_mode/powerctrl.py b/src/op_mode/powerctrl.py index 679b03c0b..fd4f86d88 100755 --- a/src/op_mode/powerctrl.py +++ b/src/op_mode/powerctrl.py @@ -33,10 +33,12 @@ def utc2local(datetime): def parse_time(s): try: - if re.match(r'^\d{1,2}$', s): - if (int(s) > 59): + if re.match(r'^\d{1,9999}$', s): + if (int(s) > 59) and (int(s) < 1440): s = str(int(s)//60) + ":" + str(int(s)%60) return datetime.strptime(s, "%H:%M").time() + if (int(s) >= 1440): + return s.split() else: return datetime.strptime(s, "%M").time() else: @@ -141,7 +143,7 @@ def execute_shutdown(time, reboot=True, ask=True): cmd(f'/usr/bin/wall "{wall_msg}"') else: if not ts: - exit(f'Invalid time "{time[0]}". The valid format is HH:MM') + exit(f'Invalid time "{time[0]}". Uses 24 Hour Clock format') else: exit(f'Invalid date "{time[1]}". A valid format is YYYY-MM-DD [HH:MM]') else: @@ -172,7 +174,12 @@ def main(): action.add_argument("--reboot", "-r", help="Reboot the system", nargs="*", - metavar="Minutes|HH:MM") + metavar="HH:MM") + + action.add_argument("--reboot_in", "-i", + help="Reboot the system", + nargs="*", + metavar="Minutes") action.add_argument("--poweroff", "-p", help="Poweroff the system", @@ -190,7 +197,17 @@ def main(): try: if args.reboot is not None: + for r in args.reboot: + if ':' not in r and '/' not in r and '.' not in r: + print("Incorrect format! Use HH:MM") + exit(1) execute_shutdown(args.reboot, reboot=True, ask=args.yes) + if args.reboot_in is not None: + for i in args.reboot_in: + if ':' in i: + print("Incorrect format! Use Minutes") + exit(1) + execute_shutdown(args.reboot_in, reboot=True, ask=args.yes) if args.poweroff is not None: execute_shutdown(args.poweroff, reboot=False, ask=args.yes) if args.cancel: -- cgit v1.2.3 From a3b7e985911eeaccac4fa229563b78c5a64e7e90 Mon Sep 17 00:00:00 2001 From: Daniil Baturin Date: Mon, 21 Feb 2022 08:06:40 -0500 Subject: T2719: initial batch of standardized structure op mode scripts --- src/op_mode/cpu_summary.py | 36 +++++++++++++++++--------- src/op_mode/show_cpu.py | 63 ++++++++++++++++++++++++++------------------- src/op_mode/show_ram.py | 19 +++++++++----- src/op_mode/show_uptime.py | 27 ++++++++++++++----- src/op_mode/show_version.py | 22 +++++++++++----- 5 files changed, 109 insertions(+), 58 deletions(-) (limited to 'src/op_mode') diff --git a/src/op_mode/cpu_summary.py b/src/op_mode/cpu_summary.py index cfd321522..3bdf5a718 100755 --- a/src/op_mode/cpu_summary.py +++ b/src/op_mode/cpu_summary.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2018 VyOS maintainers and contributors +# Copyright (C) 2018-2022 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,18 +19,30 @@ from vyos.util import colon_separated_to_dict FILE_NAME = '/proc/cpuinfo' -with open(FILE_NAME, 'r') as f: - data_raw = f.read() +def get_raw_data(): + with open(FILE_NAME, 'r') as f: + data_raw = f.read() -data = colon_separated_to_dict(data_raw) + data = colon_separated_to_dict(data_raw) -# Accumulate all data in a dict for future support for machine-readable output -cpu_data = {} -cpu_data['cpu_number'] = len(data['processor']) -cpu_data['models'] = list(set(data['model name'])) + # Accumulate all data in a dict for future support for machine-readable output + cpu_data = {} + cpu_data['cpu_number'] = len(data['processor']) + cpu_data['models'] = list(set(data['model name'])) -# Strip extra whitespace from CPU model names, /proc/cpuinfo is prone to that -cpu_data['models'] = map(lambda s: re.sub(r'\s+', ' ', s), cpu_data['models']) + # Strip extra whitespace from CPU model names, /proc/cpuinfo is prone to that + cpu_data['models'] = list(map(lambda s: re.sub(r'\s+', ' ', s), cpu_data['models'])) + + return cpu_data + +def get_formatted_output(): + cpu_data = get_raw_data() + + out = "CPU(s): {0}\n".format(cpu_data['cpu_number']) + out += "CPU model(s): {0}".format(",".join(cpu_data['models'])) + + return out + +if __name__ == '__main__': + print(get_formatted_output()) -print("CPU(s): {0}".format(cpu_data['cpu_number'])) -print("CPU model(s): {0}".format(",".join(cpu_data['models']))) diff --git a/src/op_mode/show_cpu.py b/src/op_mode/show_cpu.py index 0040e950d..9973d9789 100755 --- a/src/op_mode/show_cpu.py +++ b/src/op_mode/show_cpu.py @@ -21,7 +21,7 @@ from sys import exit from vyos.util import popen, DEVNULL OUT_TMPL_SRC = """ -{% if cpu %} +{%- if cpu -%} {% if 'vendor' in cpu %}CPU Vendor: {{cpu.vendor}}{% endif %} {% if 'model' in cpu %}Model: {{cpu.model}}{% endif %} {% if 'cpus' in cpu %}Total CPUs: {{cpu.cpus}}{% endif %} @@ -31,31 +31,42 @@ OUT_TMPL_SRC = """ {% if 'mhz' in cpu %}Current MHz: {{cpu.mhz}}{% endif %} {% if 'mhz_min' in cpu %}Minimum MHz: {{cpu.mhz_min}}{% endif %} {% if 'mhz_max' in cpu %}Maximum MHz: {{cpu.mhz_max}}{% endif %} -{% endif %} +{%- endif -%} """ -cpu = {} -cpu_json, code = popen('lscpu -J', stderr=DEVNULL) - -if code == 0: - cpu_info = json.loads(cpu_json) - if len(cpu_info) > 0 and 'lscpu' in cpu_info: - for prop in cpu_info['lscpu']: - if (prop['field'].find('Thread(s)') > -1): cpu['threads'] = prop['data'] - if (prop['field'].find('Core(s)')) > -1: cpu['cores'] = prop['data'] - if (prop['field'].find('Socket(s)')) > -1: cpu['sockets'] = prop['data'] - if (prop['field'].find('CPU(s):')) > -1: cpu['cpus'] = prop['data'] - if (prop['field'].find('CPU MHz')) > -1: cpu['mhz'] = prop['data'] - if (prop['field'].find('CPU min MHz')) > -1: cpu['mhz_min'] = prop['data'] - if (prop['field'].find('CPU max MHz')) > -1: cpu['mhz_max'] = prop['data'] - if (prop['field'].find('Vendor ID')) > -1: cpu['vendor'] = prop['data'] - if (prop['field'].find('Model name')) > -1: cpu['model'] = prop['data'] - -if len(cpu) > 0: - tmp = { 'cpu':cpu } +def get_raw_data(): + cpu = {} + cpu_json, code = popen('lscpu -J', stderr=DEVNULL) + + if code == 0: + cpu_info = json.loads(cpu_json) + if len(cpu_info) > 0 and 'lscpu' in cpu_info: + for prop in cpu_info['lscpu']: + if (prop['field'].find('Thread(s)') > -1): cpu['threads'] = prop['data'] + if (prop['field'].find('Core(s)')) > -1: cpu['cores'] = prop['data'] + if (prop['field'].find('Socket(s)')) > -1: cpu['sockets'] = prop['data'] + if (prop['field'].find('CPU(s):')) > -1: cpu['cpus'] = prop['data'] + if (prop['field'].find('CPU MHz')) > -1: cpu['mhz'] = prop['data'] + if (prop['field'].find('CPU min MHz')) > -1: cpu['mhz_min'] = prop['data'] + if (prop['field'].find('CPU max MHz')) > -1: cpu['mhz_max'] = prop['data'] + if (prop['field'].find('Vendor ID')) > -1: cpu['vendor'] = prop['data'] + if (prop['field'].find('Model name')) > -1: cpu['model'] = prop['data'] + + return cpu + +def get_formatted_output(): + cpu = get_raw_data() + + tmp = {'cpu':cpu} tmpl = Template(OUT_TMPL_SRC) - print(tmpl.render(tmp)) - exit(0) -else: - print('CPU information could not be determined\n') - exit(1) + return tmpl.render(tmp) + +if __name__ == '__main__': + cpu = get_raw_data() + + if len(cpu) > 0: + print(get_formatted_output()) + else: + print('CPU information could not be determined\n') + exit(1) + diff --git a/src/op_mode/show_ram.py b/src/op_mode/show_ram.py index 5818ec132..2b0be3965 100755 --- a/src/op_mode/show_ram.py +++ b/src/op_mode/show_ram.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2021 VyOS maintainers and contributors +# Copyright (C) 2022 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 @@ -55,10 +55,17 @@ def get_system_memory_human(): return mem -if __name__ == '__main__': - mem = get_system_memory_human() +def get_raw_data(): + return get_system_memory_human() + +def get_formatted_output(): + mem = get_raw_data() - print("Total: {}".format(mem["total"])) - print("Free: {}".format(mem["free"])) - print("Used: {}".format(mem["used"])) + out = "Total: {}\n".format(mem["total"]) + out += "Free: {}\n".format(mem["free"]) + out += "Used: {}".format(mem["used"]) + return out + +if __name__ == '__main__': + print(get_formatted_output()) diff --git a/src/op_mode/show_uptime.py b/src/op_mode/show_uptime.py index c3dea52e6..1b5e33fa9 100755 --- a/src/op_mode/show_uptime.py +++ b/src/op_mode/show_uptime.py @@ -37,14 +37,27 @@ def get_load_averages(): return res -if __name__ == '__main__': +def get_raw_data(): from vyos.util import seconds_to_human - print("Uptime: {}\n".format(seconds_to_human(get_uptime_seconds()))) + res = {} + res["uptime_seconds"] = get_uptime_seconds() + res["uptime"] = seconds_to_human(get_uptime_seconds()) + res["load_average"] = get_load_averages() + + return res - avgs = get_load_averages() +def get_formatted_output(): + data = get_raw_data() - print("Load averages:") - print("1 minute: {:.02f}%".format(avgs[1]*100)) - print("5 minutes: {:.02f}%".format(avgs[5]*100)) - print("15 minutes: {:.02f}%".format(avgs[15]*100)) + out = "Uptime: {}\n\n".format(data["uptime"]) + avgs = data["load_average"] + out += "Load averages:\n" + out += "1 minute: {:.02f}%\n".format(avgs[1]*100) + out += "5 minutes: {:.02f}%\n".format(avgs[5]*100) + out += "15 minutes: {:.02f}%\n".format(avgs[15]*100) + + return out + +if __name__ == '__main__': + print(get_formatted_output()) diff --git a/src/op_mode/show_version.py b/src/op_mode/show_version.py index 7962e1e7b..b82ab6eca 100755 --- a/src/op_mode/show_version.py +++ b/src/op_mode/show_version.py @@ -26,10 +26,6 @@ from jinja2 import Template from sys import exit from vyos.util import call -parser = argparse.ArgumentParser() -parser.add_argument("-f", "--funny", action="store_true", help="Add something funny to the output") -parser.add_argument("-j", "--json", action="store_true", help="Produce JSON output") - version_output_tmpl = """ Version: VyOS {{version}} Release train: {{release_train}} @@ -51,7 +47,20 @@ Hardware UUID: {{hardware_uuid}} Copyright: VyOS maintainers and contributors """ +def get_raw_data(): + version_data = vyos.version.get_full_version_data() + return version_data + +def get_formatted_output(): + version_data = get_raw_data() + tmpl = Template(version_output_tmpl) + return tmpl.render(version_data) + if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument("-f", "--funny", action="store_true", help="Add something funny to the output") + parser.add_argument("-j", "--json", action="store_true", help="Produce JSON output") + args = parser.parse_args() version_data = vyos.version.get_full_version_data() @@ -60,9 +69,8 @@ if __name__ == '__main__': import json print(json.dumps(version_data)) exit(0) - - tmpl = Template(version_output_tmpl) - print(tmpl.render(version_data)) + else: + print(get_formatted_output()) if args.funny: print(vyos.limericks.get_random()) -- cgit v1.2.3 From c06861440cd21ff7c668b35ed1039f5fac4101b9 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Fri, 4 Mar 2022 20:08:27 +0100 Subject: op-mode: lldp: T3999: bugfix cap' referenced before assignment --- src/op_mode/lldp_op.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/op_mode') diff --git a/src/op_mode/lldp_op.py b/src/op_mode/lldp_op.py index b9ebc991a..17f6bf552 100755 --- a/src/op_mode/lldp_op.py +++ b/src/op_mode/lldp_op.py @@ -54,6 +54,7 @@ def parse_data(data, interface): for local_if, values in neighbor.items(): if interface is not None and local_if != interface: continue + cap = '' for chassis, c_value in values.get('chassis', {}).items(): # bail out early if no capabilities found if 'capability' not in c_value: @@ -62,7 +63,6 @@ def parse_data(data, interface): if isinstance(capabilities, dict): capabilities = [capabilities] - cap = '' for capability in capabilities: if capability['enabled']: if capability['type'] == 'Router': -- cgit v1.2.3 From c37829f1e902b84a5bc3bc5618ee97ae1ba0dd86 Mon Sep 17 00:00:00 2001 From: Daniil Baturin Date: Tue, 22 Mar 2022 04:50:34 -0400 Subject: T4313: handle exceptions in the "generate public-key-command" script --- src/op_mode/generate_public_key_command.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'src/op_mode') diff --git a/src/op_mode/generate_public_key_command.py b/src/op_mode/generate_public_key_command.py index 7a7b6c923..f071ae350 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) 2021 VyOS maintainers and contributors +# Copyright (C) 2022 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 @@ -29,8 +29,12 @@ def get_key(path): key_string = vyos.remote.get_remote_config(path) return key_string.split() -username = sys.argv[1] -algorithm, key, identifier = get_key(sys.argv[2]) +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') @@ -39,3 +43,4 @@ print(f'set system login user {username} authentication public-keys {identifier} print('commit') print('save') print('exit') + -- cgit v1.2.3