From ca1caa6c6effb82b22dad0db4c4f47247c3722ad Mon Sep 17 00:00:00 2001 From: Dmytro Aleksandrov Date: Thu, 22 Aug 2019 17:26:30 +0300 Subject: [op-mode] T1607 rewrite 'reset conntrack', 'reset & show ip[v6]' to python/xml syntax --- src/op_mode/clear_conntrack.py | 26 ++++++++++++++++++++++++++ src/op_mode/powerctrl.py | 17 ++--------------- 2 files changed, 28 insertions(+), 15 deletions(-) create mode 100755 src/op_mode/clear_conntrack.py (limited to 'src/op_mode') diff --git a/src/op_mode/clear_conntrack.py b/src/op_mode/clear_conntrack.py new file mode 100755 index 000000000..0e52b9086 --- /dev/null +++ b/src/op_mode/clear_conntrack.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2019 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 subprocess +import sys + +from vyos.util import ask_yes_no + +if not ask_yes_no("This will clear all currently tracked and expected connections. Continue?"): + sys.exit(1) +else: + subprocess.check_call(['/usr/sbin/conntrack -F'], shell=True, stderr=subprocess.DEVNULL) + subprocess.check_call(['/usr/sbin/conntrack -F expect'], shell=True, stderr=subprocess.DEVNULL) diff --git a/src/op_mode/powerctrl.py b/src/op_mode/powerctrl.py index 2f6112fb7..e3644e063 100755 --- a/src/op_mode/powerctrl.py +++ b/src/op_mode/powerctrl.py @@ -22,20 +22,7 @@ import re from datetime import datetime, timedelta, time as type_time, date as type_date from subprocess import check_output, CalledProcessError, STDOUT - -def yn(msg, default=False): - default_msg = "[Y/n]" if default else "[y/N]" - while True: - sys.stdout.write("%s %s " % (msg,default_msg)) - c = input().lower() - if c == '': - return default - elif c in ("y", "ye","yes"): - return True - elif c in ("n", "no"): - return False - else: - sys.stdout.write("Please respond with yes/y or no/n\n") +from vyos.util import ask_yes_no def valid_time(s): @@ -80,7 +67,7 @@ def cancel_shutdown(): def execute_shutdown(time, reboot = True, ask=True): if not ask: action = "reboot" if reboot else "poweroff" - if not yn("Are you sure you want to %s this system?" % action): + if not ask_yes_no("Are you sure you want to %s this system?" % action): sys.exit(0) action = "-r" if reboot else "-P" -- cgit v1.2.3 From c83d12cf79635a14f87696fa9817840cc2f6fa8d Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 26 Aug 2019 13:14:35 +0200 Subject: openvpn: T1548: add 'show openvpn' command vyos@vyos:~$ show openvpn site-to-site OpenVPN status on vtun1 Client CN Remote Host Local Host TX bytes RX bytes Connected Since --------- ----------- ---------- -------- -------- --------------- None (PSK) N/A 172.18.201.10:1195 3.3 KiB 3.3 KiB N/A vyos@vyos:~$ show openvpn server OpenVPN status on vtun10 Client CN Remote Host Local Host TX bytes RX bytes Connected Since --------- ----------- ---------- -------- -------- --------------- client1 172.18.202.10:58644 172.18.201.10:1194 63.6 KiB 63.4 KiB Mon Aug 26 11:47:56 2019 client3 172.18.204.10:52641 172.18.201.10:1194 63.1 KiB 62.7 KiB Mon Aug 26 11:47:58 2019 OpenVPN status on vtun11 Client CN Remote Host Local Host TX bytes RX bytes Connected Since --------- ----------- ---------- -------- -------- --------------- client2 172.18.203.10:39472 172.18.201.10:1200 61.2 KiB 61.5 KiB Mon Aug 26 11:50:30 2019 --- op-mode-definitions/openvpn.xml | 25 ++++++ src/op_mode/show_openvpn.py | 170 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 195 insertions(+) create mode 100755 src/op_mode/show_openvpn.py (limited to 'src/op_mode') diff --git a/op-mode-definitions/openvpn.xml b/op-mode-definitions/openvpn.xml index 2adbfba53..368cc9115 100644 --- a/op-mode-definitions/openvpn.xml +++ b/op-mode-definitions/openvpn.xml @@ -110,6 +110,31 @@ + + + Show OpenVPN information + + + + + Show tunnel status for OpenVPN client interfaces + + sudo ${vyos_op_scripts_dir}/show_openvpn.py --mode=client + + + + Show tunnel status for OpenVPN server interfaces + + sudo ${vyos_op_scripts_dir}/show_openvpn.py --mode=server + + + + Show tunnel status for OpenVPN site-to-site interfaces + + sudo ${vyos_op_scripts_dir}/show_openvpn.py --mode=site-to-site + + + diff --git a/src/op_mode/show_openvpn.py b/src/op_mode/show_openvpn.py new file mode 100755 index 000000000..ee3729fc3 --- /dev/null +++ b/src/op_mode/show_openvpn.py @@ -0,0 +1,170 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2018 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 jinja2 +import argparse + +from vyos.config import Config + +outp_tmpl = """ +{% if clients %} +OpenVPN status on {{ intf }} + +Client CN Remote Host Local Host TX bytes RX bytes Connected Since +--------- ----------- ---------- -------- -------- --------------- +{%- for c in clients %} +{{ "%-15s"|format(c.name) }} {{ "%-21s"|format(c.remote) }} {{ "%-15s"|format(local) }} {{ "%-9s"|format(c.tx_bytes) }} {{ "%-9s"|format(c.tx_bytes) }} {{ c.online_since }} +{%- endfor %} +{% endif %} +""" + +def bytes2HR(size): + # we need to operate in integers + size = int(size) + + suff = ['B', 'KB', 'MB', 'GB', 'TB'] + suffIdx = 0 + + while size > 1024: + # incr. suffix index + suffIdx += 1 + # divide + size = size/1024.0 + + output="{0:.1f} {1}".format(size, suff[suffIdx]) + return output + +def get_status(mode, interface): + status_file = '/opt/vyatta/etc/openvpn/status/{}.status'.format(interface) + # this is an empirical value - I assume we have no more then 999999 + # current OpenVPN connections + routing_table_line = 999999 + + data = { + 'mode': mode, + 'intf': interface, + 'local': '', + 'date': '', + 'clients': [], + } + + with open(status_file, 'r') as f: + lines = f.readlines() + for line_no, line in enumerate(lines): + # remove trailing newline character first + line = line.rstrip('\n') + + # check first line header + if line_no == 0: + if mode == 'server': + if not line == 'OpenVPN CLIENT LIST': + raise NameError('Expected "OpenVPN CLIENT LIST"') + else: + if not line == 'OpenVPN STATISTICS': + raise NameError('Expected "OpenVPN STATISTICS"') + + continue + + # second line informs us when the status file has been last updated + if line_no == 1: + data['date'] = line.lstrip('Updated,').rstrip('\n') + continue + + if mode == 'server': + # followed by line3 giving output information and the actual output data + # + # Common Name,Real Address,Bytes Received,Bytes Sent,Connected Since + # client1,172.18.202.10:55904,2880587,2882653,Fri Aug 23 16:25:48 2019 + # client3,172.18.204.10:41328,2850832,2869729,Fri Aug 23 16:25:43 2019 + # client2,172.18.203.10:48987,2856153,2871022,Fri Aug 23 16:25:45 2019 + if (line_no >= 3) and (line_no < routing_table_line): + # indicator that there are no more clients and we will continue with the + # routing table + if line == 'ROUTING TABLE': + routing_table_line = line_no + continue + + client = { + 'name': line.split(',')[0], + 'remote': line.split(',')[1], + 'rx_bytes': bytes2HR(line.split(',')[2]), + 'tx_bytes': bytes2HR(line.split(',')[3]), + 'online_since': line.split(',')[4] + } + + data['clients'].append(client) + continue + else: + if line_no == 2: + client = { + 'name': 'N/A', + 'rx_bytes': bytes2HR(line.split(',')[1]), + 'tx_bytes': '', + 'online_since': 'N/A' + } + continue + + if line_no == 3: + client['tx_bytes'] = bytes2HR(line.split(',')[1]) + data['clients'].append(client) + break + + return data + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('-m', '--mode', help='OpenVPN operation mode (server, client, site-2-site)', required=True) + + args = parser.parse_args() + + # Do nothing if service is not configured + config = Config() + if len(config.list_effective_nodes('interfaces openvpn')) == 0: + print("No OpenVPN interfaces configured") + sys.exit(0) + + # search all OpenVPN interfaces and add those with a matching mode to our + # interfaces list + interfaces = [] + for intf in config.list_effective_nodes('interfaces openvpn'): + # get interface type (server, client, site-to-site) + mode = config.return_effective_value('interfaces openvpn {} mode'.format(intf)) + if args.mode == mode: + interfaces.append(intf) + + for intf in interfaces: + data = get_status(args.mode, intf) + local_host = config.return_effective_value('interfaces openvpn {} local-host'.format(intf)) + local_port = config.return_effective_value('interfaces openvpn {} local-port'.format(intf)) + data['local'] = local_host + ':' + local_port + + if args.mode in ['client', 'site-to-site']: + for client in data['clients']: + if config.exists_effective('interfaces openvpn {} shared-secret-key-file'.format(intf)): + client['name'] = "None (PSK)" + + remote_host = config.return_effective_values('interfaces openvpn {} remote-host'.format(intf)) + remote_port = config.return_effective_value('interfaces openvpn {} remote-port'.format(intf)) + if len(remote_host) >= 1: + client['remote'] = str(remote_host[0]) + ':' + remote_port + else: + client['remote'] = 'N/A' + + + tmpl = jinja2.Template(outp_tmpl) + print(tmpl.render(data)) + -- cgit v1.2.3 From 37e4d24f3679c2f24513cf5dcfbeb761d5bf701d Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 26 Aug 2019 13:35:36 +0200 Subject: openvpn: T1548: add missing if statement in 'show openvpn' command --- src/op_mode/show_openvpn.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) (limited to 'src/op_mode') diff --git a/src/op_mode/show_openvpn.py b/src/op_mode/show_openvpn.py index ee3729fc3..23a8156ec 100755 --- a/src/op_mode/show_openvpn.py +++ b/src/op_mode/show_openvpn.py @@ -24,10 +24,10 @@ outp_tmpl = """ {% if clients %} OpenVPN status on {{ intf }} -Client CN Remote Host Local Host TX bytes RX bytes Connected Since ---------- ----------- ---------- -------- -------- --------------- +Client CN Remote Host Local Host TX bytes RX bytes Connected Since +--------- ----------- ---------- -------- -------- --------------- {%- for c in clients %} -{{ "%-15s"|format(c.name) }} {{ "%-21s"|format(c.remote) }} {{ "%-15s"|format(local) }} {{ "%-9s"|format(c.tx_bytes) }} {{ "%-9s"|format(c.tx_bytes) }} {{ c.online_since }} +{{ "%-15s"|format(c.name) }} {{ "%-21s"|format(c.remote) }} {{ "%-21s"|format(local) }} {{ "%-9s"|format(c.tx_bytes) }} {{ "%-9s"|format(c.tx_bytes) }} {{ c.online_since }} {%- endfor %} {% endif %} """ @@ -57,7 +57,7 @@ def get_status(mode, interface): data = { 'mode': mode, 'intf': interface, - 'local': '', + 'local': 'N/A', 'date': '', 'clients': [], } @@ -112,6 +112,7 @@ def get_status(mode, interface): if line_no == 2: client = { 'name': 'N/A', + 'remote': 'N/A', 'rx_bytes': bytes2HR(line.split(',')[1]), 'tx_bytes': '', 'online_since': 'N/A' @@ -150,7 +151,8 @@ if __name__ == '__main__': data = get_status(args.mode, intf) local_host = config.return_effective_value('interfaces openvpn {} local-host'.format(intf)) local_port = config.return_effective_value('interfaces openvpn {} local-port'.format(intf)) - data['local'] = local_host + ':' + local_port + if local_host and local_port: + data['local'] = local_host + ':' + local_port if args.mode in ['client', 'site-to-site']: for client in data['clients']: @@ -161,9 +163,6 @@ if __name__ == '__main__': remote_port = config.return_effective_value('interfaces openvpn {} remote-port'.format(intf)) if len(remote_host) >= 1: client['remote'] = str(remote_host[0]) + ':' + remote_port - else: - client['remote'] = 'N/A' - tmpl = jinja2.Template(outp_tmpl) print(tmpl.render(data)) -- cgit v1.2.3 From 76f498220c796a39a92ce3bed47c1cc5bac1a99d Mon Sep 17 00:00:00 2001 From: Dmytro Aleksandrov Date: Fri, 30 Aug 2019 18:43:44 +0300 Subject: [op-mode] T1621 rewrite misc commands to python/xml syntax --- op-mode-definitions/bandwidth-test.xml | 29 +++++ op-mode-definitions/disks.xml | 50 ++++++++ op-mode-definitions/generate-ssh-server-key.xml | 16 +++ op-mode-definitions/show-disk.xml | 23 ---- op-mode-definitions/show-history.xml | 31 +++++ op-mode-definitions/show-host.xml | 12 ++ op-mode-definitions/telnet.xml | 8 +- op-mode-definitions/terminal.xml | 93 +++++++++++++++ python/vyos/util.py | 8 ++ src/completion/list_disks.py | 36 ++++++ src/completion/list_disks.sh | 5 - src/helpers/vyos-sudo.py | 9 +- src/op_mode/format_disk.py | 148 ++++++++++++++++++++++++ src/op_mode/generate_ssh_server_key.py | 27 +++++ src/op_mode/toggle_help_binding.sh | 25 ++++ 15 files changed, 480 insertions(+), 40 deletions(-) create mode 100644 op-mode-definitions/bandwidth-test.xml create mode 100644 op-mode-definitions/disks.xml create mode 100644 op-mode-definitions/generate-ssh-server-key.xml delete mode 100644 op-mode-definitions/show-disk.xml create mode 100644 op-mode-definitions/show-history.xml create mode 100755 src/completion/list_disks.py delete mode 100755 src/completion/list_disks.sh create mode 100755 src/op_mode/format_disk.py create mode 100755 src/op_mode/generate_ssh_server_key.py create mode 100755 src/op_mode/toggle_help_binding.sh (limited to 'src/op_mode') diff --git a/op-mode-definitions/bandwidth-test.xml b/op-mode-definitions/bandwidth-test.xml new file mode 100644 index 000000000..d1e459b17 --- /dev/null +++ b/op-mode-definitions/bandwidth-test.xml @@ -0,0 +1,29 @@ + + + + + + + Initiate or wait for bandwidth test + + + + + Wait for bandwidth test connections (port TCP/5001) + + iperf -s + + + + Initiate a bandwidth test to specified host (port TCP/5001) + + <hostname> <x.x.x.x> <h:h:h:h:h:h:h:h> + + + iperf -c $4 + + + + + + diff --git a/op-mode-definitions/disks.xml b/op-mode-definitions/disks.xml new file mode 100644 index 000000000..fb39c4f3c --- /dev/null +++ b/op-mode-definitions/disks.xml @@ -0,0 +1,50 @@ + + + + + Format a device + + + + + Format a disk drive + + + + + + + + Format this disk the same as another disk + + + + + ${vyos_op_scripts_dir}/format_disk.py --target $3 --proto $5 + + + + + + + + + + + Show status of disk device + + + + + + + + Show disk drive formatting + + ${vyos_op_scripts_dir}/show_disk_format.sh $3 + + + + + + diff --git a/op-mode-definitions/generate-ssh-server-key.xml b/op-mode-definitions/generate-ssh-server-key.xml new file mode 100644 index 000000000..a6ebf1b78 --- /dev/null +++ b/op-mode-definitions/generate-ssh-server-key.xml @@ -0,0 +1,16 @@ + + + + + Generate an object + + + + + Regenerate the host SSH keys and restart the SSH server + + ${vyos_op_scripts_dir}/generate_ssh_server_key.py + + + + diff --git a/op-mode-definitions/show-disk.xml b/op-mode-definitions/show-disk.xml deleted file mode 100644 index 37da07fbe..000000000 --- a/op-mode-definitions/show-disk.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - Show status of disk device - - - - - - - - Show disk drive formatting - - ${vyos_op_scripts_dir}/show_disk_format.sh $3 - - - - - - diff --git a/op-mode-definitions/show-history.xml b/op-mode-definitions/show-history.xml new file mode 100644 index 000000000..7fb286264 --- /dev/null +++ b/op-mode-definitions/show-history.xml @@ -0,0 +1,31 @@ + + + + + + + Show command history + + HISTTIMEFORMAT='%FT%T%z ' HISTFILE="$HOME/.bash_history" \set -o history; history + + + + Show recent command history + + HISTTIMEFORMAT='%FT%T%z ' HISTFILE="$HOME/.bash_history" \set -o history; history 20 + + + + + + + Show last N commands in history + + <NUMBER> + + + HISTTIMEFORMAT='%FT%T%z ' HISTFILE="$HOME/.bash_history" \set -o history; history $3 + + + + diff --git a/op-mode-definitions/show-host.xml b/op-mode-definitions/show-host.xml index d7f8104aa..eee1288a1 100644 --- a/op-mode-definitions/show-host.xml +++ b/op-mode-definitions/show-host.xml @@ -7,6 +7,12 @@ Show host information + + + Show host current date + + /bin/date + Show domain name @@ -25,6 +31,12 @@ /usr/bin/host $4 + + + Show host operating system details + + /bin/uname -a + diff --git a/op-mode-definitions/telnet.xml b/op-mode-definitions/telnet.xml index 7ec75be88..c5bb6d283 100644 --- a/op-mode-definitions/telnet.xml +++ b/op-mode-definitions/telnet.xml @@ -5,23 +5,23 @@ Telnet to a node - + Telnet to a host <hostname> <x.x.x.x> <h:h:h:h:h:h:h:h> - /usr/bin/telnet $2 + /usr/bin/telnet $3 - + Telnet to a host:port <0-65535> - /usr/bin/telnet $2 $3 + /usr/bin/telnet $3 $5 diff --git a/op-mode-definitions/terminal.xml b/op-mode-definitions/terminal.xml index db74f867e..9c4e629cb 100644 --- a/op-mode-definitions/terminal.xml +++ b/op-mode-definitions/terminal.xml @@ -26,4 +26,97 @@ + + + Set operational options + + + + + Bash builtin set command + + <OPTION> + + + builtin $3 + + + + + Control console behaviors + + + + + Reconfigure console keyboard layout + + sudo dpkg-reconfigure -f dialog keyboard-configuration && sudo systemctl restart keyboard-setup + + + + + + + Control terminal behaviors + + + + + + Set key behaviors + + + + + Enable/disable getting help using question mark (default enabled) + + enable disable + + + ${vyos_op_scripts_dir}/toggle_help_binding.sh $5 + + + + + + + Set terminal pager to default (less) + + VYATTA_PAGER=${_vyatta_default_pager} + + + + Set terminal pager + + <PROGRAM> + + + VYATTA_PAGER=$4 + + + + + Set terminal to given number of rows (0 disables paging) + + <NUMBER> + + + if [ "$4" -eq 0 ]; then VYATTA_PAGER=cat; else VYATTA_PAGER=${_vyatta_default_pager}; stty rows $4; fi + + + + + Set terminal to given number of columns + + <NUMBER> + + + stty columns $4 + + + + + + + diff --git a/python/vyos/util.py b/python/vyos/util.py index 88bb0c8f4..67a602f7a 100644 --- a/python/vyos/util.py +++ b/python/vyos/util.py @@ -15,6 +15,7 @@ import os import re +import getpass import grp import time import subprocess @@ -191,3 +192,10 @@ def ask_yes_no(question, default=False) -> bool: return False else: sys.stdout.write("Please respond with yes/y or no/n\n") + + +def is_admin() -> bool: + """Look if current user is in sudo group""" + current_user = getpass.getuser() + (_, _, _, admin_group_members) = grp.getgrnam('sudo') + return current_user in admin_group_members diff --git a/src/completion/list_disks.py b/src/completion/list_disks.py new file mode 100755 index 000000000..ff1135e23 --- /dev/null +++ b/src/completion/list_disks.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2019 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 . + +# Completion script used by show disks to collect physical disk + +import argparse + +parser = argparse.ArgumentParser() +parser.add_argument("-e", "--exclude", type=str, help="Exclude specified device from the result list") +args = parser.parse_args() + +disks = set() +with open('/proc/partitions') as partitions_file: + for line in partitions_file: + fields = line.strip().split() + if len(fields) == 4 and fields[3].isalpha() and fields[3] != 'name': + disks.add(fields[3]) + +if args.exclude: + disks.remove(args.exclude) + +for disk in disks: + print(disk) diff --git a/src/completion/list_disks.sh b/src/completion/list_disks.sh deleted file mode 100755 index f32e558fd..000000000 --- a/src/completion/list_disks.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash - -# Completion script used by show disks to collect physical disk - -awk 'NR > 2 && $4 !~ /[0-9]$/ { print $4 }' . -import getpass -import grp import os import sys - -def is_admin() -> bool: - """Look if current user is in sudo group""" - current_user = getpass.getuser() - (_, _, _, admin_group_members) = grp.getgrnam('sudo') - return current_user in admin_group_members +from vyos.util import is_admin if __name__ == '__main__': diff --git a/src/op_mode/format_disk.py b/src/op_mode/format_disk.py new file mode 100755 index 000000000..5a3b250ee --- /dev/null +++ b/src/op_mode/format_disk.py @@ -0,0 +1,148 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2019 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 +import re +import subprocess +import sys +from datetime import datetime +from time import sleep + +from vyos.util import is_admin, ask_yes_no + + +def list_disks(): + disks = set() + with open('/proc/partitions') as partitions_file: + for line in partitions_file: + fields = line.strip().split() + if len(fields) == 4 and fields[3].isalpha() and fields[3] != 'name': + disks.add(fields[3]) + return disks + + +def is_busy(disk: str): + """Check if given disk device is busy by re-reading it's partition table""" + + cmd = 'sudo blockdev --rereadpt /dev/{}'.format(disk) + status = subprocess.call([cmd], shell=True, stderr=subprocess.DEVNULL) + return status != 0 + + +def backup_partitions(disk: str): + """Save sfdisk partitions output to a backup file""" + + device_path = '/dev/' + disk + backup_ts = datetime.now().strftime('%Y-%m-%d-%H:%M') + backup_file = '/var/tmp/backup_{}.{}'.format(disk, backup_ts) + cmd = 'sudo /sbin/sfdisk -d {} > {}'.format(device_path, backup_file) + subprocess.check_call([cmd], shell=True) + + +def list_partitions(disk: str): + """List partition numbers of a given disk""" + + parts = set() + part_num_expr = re.compile(disk + '([0-9]+)') + with open('/proc/partitions') as partitions_file: + for line in partitions_file: + fields = line.strip().split() + if len(fields) == 4 and fields[3] != 'name' and part_num_expr.match(fields[3]): + part_idx = part_num_expr.match(fields[3]).group(1) + parts.add(int(part_idx)) + return parts + + +def delete_partition(disk: str, partition_idx: int): + cmd = 'sudo /sbin/parted /dev/{} rm {}'.format(disk, partition_idx) + subprocess.check_call([cmd], shell=True) + + +def format_disk_like(target: str, proto: str): + cmd = 'sudo /sbin/sfdisk -d /dev/{} | sudo /sbin/sfdisk --force /dev/{}'.format(proto, target) + subprocess.check_call([cmd], shell=True) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + group = parser.add_argument_group() + group.add_argument('-t', '--target', type=str, required=True, help='Target device to format') + group.add_argument('-p', '--proto', type=str, required=True, help='Prototype device to use as reference') + args = parser.parse_args() + + if not is_admin(): + print('Must be admin or root to format disk') + sys.exit(1) + + target_disk = args.target + eligible_target_disks = list_disks() + + proto_disk = args.proto + eligible_proto_disks = eligible_target_disks.copy() + eligible_proto_disks.remove(target_disk) + + fmt = { + 'target_disk': target_disk, + 'proto_disk': proto_disk, + } + + if proto_disk == target_disk: + print('The two disk drives must be different.') + sys.exit(1) + + if not os.path.exists('/dev/' + proto_disk): + print('Device /dev/{proto_disk} does not exist'.format_map(fmt)) + sys.exit(1) + + if not os.path.exists('/dev/' + target_disk): + print('Device /dev/{target_disk} does not exist'.format_map(fmt)) + sys.exit(1) + + if target_disk not in eligible_target_disks: + print('Device {target_disk} can not be formatted'.format_map(fmt)) + sys.exit(1) + + if proto_disk not in eligible_proto_disks: + print('Device {proto_disk} can not be used as a prototype for {target_disk}'.format_map(fmt)) + sys.exit(1) + + if is_busy(target_disk): + print("Disk device {target_disk} is busy. Can't format it now".format_map(fmt)) + sys.exit(1) + + print('This will re-format disk {target_disk} so that it has the same disk\n' + 'partion sizes and offsets as {proto_disk}. This will not copy\n' + 'data from {proto_disk} to {target_disk}. But this will erase all\n' + 'data on {target_disk}.\n'.format_map(fmt)) + + if not ask_yes_no("Do you wish to proceed?"): + print('OK. Disk drive {target_disk} will not be re-formated'.format_map(fmt)) + sys.exit(0) + + print('OK. Re-formating disk drive {target_disk}...'.format_map(fmt)) + + print('Making backup copy of partitions...') + backup_partitions(target_disk) + sleep(1) + + print('Deleting old partitions...') + for p in list_partitions(target_disk): + delete_partition(disk=target_disk, partition_idx=p) + + print('Creating new partitions on {target_disk} based on {proto_disk}...'.format_map(fmt)) + format_disk_like(target=target_disk, proto=proto_disk) + print('Done.') diff --git a/src/op_mode/generate_ssh_server_key.py b/src/op_mode/generate_ssh_server_key.py new file mode 100755 index 000000000..f205919b8 --- /dev/null +++ b/src/op_mode/generate_ssh_server_key.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2019 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 subprocess +import sys + +from vyos.util import ask_yes_no + +if not ask_yes_no('Do you really want to remove the existing SSH host keys?'): + sys.exit(0) +else: + subprocess.check_call(['sudo rm -v /etc/ssh/ssh_host_*'], shell=True) + subprocess.check_call(['sudo dpkg-reconfigure openssh-server'], shell=True) + subprocess.check_call(['sudo systemctl restart ssh'], shell=True) diff --git a/src/op_mode/toggle_help_binding.sh b/src/op_mode/toggle_help_binding.sh new file mode 100755 index 000000000..a8708f3da --- /dev/null +++ b/src/op_mode/toggle_help_binding.sh @@ -0,0 +1,25 @@ +#!/bin/bash +# +# Copyright (C) 2019 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 . + +# Script for [un-]binding the question mark key for getting help +if [ "$1" == 'disable' ]; then + sed -i "/^bind '\"?\": .* # vyatta key binding$/d" $HOME/.bashrc + echo "bind '\"?\": self-insert' # vyatta key binding" >> $HOME/.bashrc + bind '"?": self-insert' +else + sed -i "/^bind '\"?\": .* # vyatta key binding$/d" $HOME/.bashrc + bind '"?": possible-completions' +fi -- cgit v1.2.3 From 1017c8103f12ebd6db4f250d8a154571fff32db1 Mon Sep 17 00:00:00 2001 From: hagbard Date: Mon, 9 Sep 2019 11:55:54 -0700 Subject: [wireguard]: T1572 - Wireguard keyPair per interface - param key location added in op-mode script - param delkey and listkey implemented in op-mode script - param delkey implemented in op-mode script - generate and store named keys - interface implementation tu use cli option 'private-key' --- Makefile | 1 + interface-definitions/interfaces-wireguard.xml | 8 ++ op-mode-definitions/wireguard.xml | 53 ++++++- src/conf_mode/interface-wireguard.py | 11 +- src/op_mode/wireguard.py | 188 ++++++++++++++++--------- 5 files changed, 188 insertions(+), 73 deletions(-) (limited to 'src/op_mode') diff --git a/Makefile b/Makefile index d7b3f047d..ad05acff5 100644 --- a/Makefile +++ b/Makefile @@ -42,6 +42,7 @@ op_mode_definitions: rm -f $(OP_TMPL_DIR)/generate/node.def rm -f $(OP_TMPL_DIR)/show/vpn/node.def rm -f $(OP_TMPL_DIR)/show/system/node.def + rm -f $(OP_TMPL_DIR)/delete/node.def .PHONY: all all: clean interface_definitions op_mode_definitions diff --git a/interface-definitions/interfaces-wireguard.xml b/interface-definitions/interfaces-wireguard.xml index 6e2622018..f2a7cc316 100644 --- a/interface-definitions/interfaces-wireguard.xml +++ b/interface-definitions/interfaces-wireguard.xml @@ -77,6 +77,14 @@ + + + Private key to use on that interface + + + + + peer alias diff --git a/op-mode-definitions/wireguard.xml b/op-mode-definitions/wireguard.xml index fa5e4a206..785af202c 100644 --- a/op-mode-definitions/wireguard.xml +++ b/op-mode-definitions/wireguard.xml @@ -20,6 +20,12 @@ ${vyos_op_scripts_dir}/wireguard.py --genpsk + + + Generates named wireguard keypairs + + sudo ${vyos_op_scripts_dir}/wireguard.py --genkey --location "$4" + @@ -33,7 +39,7 @@ - show wireguard public key + Show wireguard public key ${vyos_op_scripts_dir}/wireguard.py --showpub @@ -43,6 +49,31 @@ ${vyos_op_scripts_dir}/wireguard.py --showpriv + + + Shows named wireguard keys + + + + + Show wireguard private named key + + + + + ${vyos_op_scripts_dir}/wireguard.py --showpub --location "$5" + + + + Show wireguard public named key + + + + + ${vyos_op_scripts_dir}/wireguard.py --showpriv --location "$5" + + + @@ -81,5 +112,25 @@ + + + + + Delete wireguard properties + + + + + Delete wireguard named keypair + + + + + sudo ${vyos_op_scripts_dir}/wireguard.py --delkdir --location "$4" + + + + + diff --git a/src/conf_mode/interface-wireguard.py b/src/conf_mode/interface-wireguard.py index 4c0e90ca6..0f9e66aa6 100755 --- a/src/conf_mode/interface-wireguard.py +++ b/src/conf_mode/interface-wireguard.py @@ -29,6 +29,9 @@ from vyos.ifconfig import WireGuardIf ifname = str(os.environ['VYOS_TAGNODE_VALUE']) intfc = WireGuardIf(ifname) +kdir = r'/config/auth/wireguard' + + def check_kmod(): if not os.path.exists('/sys/module/wireguard'): sl.syslog(sl.LOG_NOTICE, "loading wirguard kmod") @@ -52,7 +55,7 @@ def get_config(): 'fwmark': 0x00, 'mtu': 1420, 'peer': {}, - 'pk' : '/config/auth/wireguard/private.key' + 'pk': '{}/private.key'.format(kdir) } } @@ -77,6 +80,9 @@ def get_config(): ifname + ' description') if c.exists(ifname + ' mtu'): config_data[ifname]['mtu'] = c.return_value(ifname + ' mtu') + if c.exists(ifname + ' private-key'): + config_data[ifname]['pk'] = "{0}/{1}/private.key".format( + kdir, c.return_value(ifname + ' private-key')) if c.exists(ifname + ' peer'): for p in c.list_nodes(ifname + ' peer'): if not c.exists(ifname + ' peer ' + p + ' disable'): @@ -107,13 +113,14 @@ def get_config(): return config_data + def verify(c): if not c: return None if not os.path.exists(c[ifname]['pk']): raise ConfigError( - "No keys found, generate them by executing: \'run generate wireguard keypair\'") + "No keys found, generate them by executing: \'run generate wireguard [keypair|named-keypairs]\'") if c[ifname]['status'] != 'delete': if not c[ifname]['addr']: diff --git a/src/op_mode/wireguard.py b/src/op_mode/wireguard.py index 66622c04c..e48da2e40 100755 --- a/src/op_mode/wireguard.py +++ b/src/op_mode/wireguard.py @@ -19,91 +19,139 @@ import argparse import os import sys +import shutil import subprocess import syslog as sl + from vyos import ConfigError dir = r'/config/auth/wireguard' -pk = dir + '/private.key' -pub = dir + '/public.key' psk = dir + '/preshared.key' + def check_kmod(): - """ check if kmod is loaded, if not load it """ - if not os.path.exists('/sys/module/wireguard'): - sl.syslog(sl.LOG_NOTICE, "loading wirguard kmod") - if os.system('sudo modprobe wireguard') != 0: - sl.syslog(sl.LOG_ERR, "modprobe wireguard failed") - raise ConfigError("modprobe wireguard failed") - -def generate_keypair(): - """ generates a keypair which is stored in /config/auth/wireguard """ - ret = subprocess.call(['wg genkey | tee ' + pk + '|wg pubkey > ' + pub], shell=True) - if ret != 0: - raise ConfigError("wireguard key-pair generation failed") - else: - sl.syslog(sl.LOG_NOTICE, "new keypair wireguard key generated in " + dir) - -def genkey(): - """ helper function to check, regenerate the keypair """ - old_umask = os.umask(0o077) - if os.path.exists(pk) and os.path.exists(pub): - try: - choice = input("You already have a wireguard key-pair already, do you want to re-generate? [y/n] ") - if choice == 'y' or choice == 'Y': - generate_keypair() - except KeyboardInterrupt: - sys.exit(0) - else: - """ if keypair is bing executed from a running iso """ - if not os.path.exists(dir): - os.umask(old_umask) - subprocess.call(['sudo mkdir -p ' + dir], shell=True) - subprocess.call(['sudo chgrp vyattacfg ' + dir], shell=True) - subprocess.call(['sudo chmod 770 ' + dir], shell=True) - generate_keypair() - os.umask(old_umask) + """ check if kmod is loaded, if not load it """ + if not os.path.exists('/sys/module/wireguard'): + sl.syslog(sl.LOG_NOTICE, "loading wirguard kmod") + if os.system('sudo modprobe wireguard') != 0: + sl.syslog(sl.LOG_ERR, "modprobe wireguard failed") + raise ConfigError("modprobe wireguard failed") -def showkey(key): - """ helper function to show privkey or pubkey """ - if key == "pub": - if os.path.exists(pub): - print ( open(pub).read().strip() ) + +def generate_keypair(pk, pub): + """ generates a keypair which is stored in /config/auth/wireguard """ + old_umask = os.umask(0o027) + ret = subprocess.call( + ['wg genkey | tee ' + pk + '|wg pubkey > ' + pub], shell=True) + if ret != 0: + raise ConfigError("wireguard key-pair generation failed") else: - print("no public key found") + sl.syslog( + sl.LOG_NOTICE, "new keypair wireguard key generated in " + dir) + os.umask(old_umask) - if key == "pk": - if os.path.exists(pk): - print ( open(pk).read().strip() ) + +def genkey(location): + """ helper function to check, regenerate the keypair """ + pk = "{}/private.key".format(location) + pub = "{}/public.key".format(location) + old_umask = os.umask(0o027) + if os.path.exists(pk) and os.path.exists(pub): + try: + choice = input( + "You already have a wireguard key-pair, do you want to re-generate? [y/n] ") + if choice == 'y' or choice == 'Y': + generate_keypair(pk, pub) + except KeyboardInterrupt: + sys.exit(0) + else: + """ if keypair is bing executed from a running iso """ + if not os.path.exists(location): + subprocess.call(['sudo mkdir -p ' + location], shell=True) + subprocess.call(['sudo chgrp vyattacfg ' + location], shell=True) + subprocess.call(['sudo chmod 750 ' + location], shell=True) + generate_keypair(pk, pub) + os.umask(old_umask) + + +def showkey(key): + """ helper function to show privkey or pubkey """ + if os.path.exists(key): + print (open(key).read().strip()) else: - print("no private key found") + print ("{} not found".format(key)) + def genpsk(): - """ generates a preshared key and shows it on stdout, it's stroed only in the config """ - subprocess.call(['wg genpsk'], shell=True) + """ + generates a preshared key and shows it on stdout, + it's stored only in the cli config + """ + + subprocess.call(['wg genpsk'], shell=True) + + +def list_key_dirs(): + """ lists all dirs under /config/auth/wireguard """ + if os.path.exists(dir): + nks = next(os.walk(dir))[1] + for nk in nks: + print (nk) + + +def del_key_dir(kname): + """ deletes /config/auth/wireguard/ """ + kdir = "{0}/{1}".format(dir, kname) + if not os.path.isdir(kdir): + print ("named keypair {} not found".format(kname)) + return 1 + shutil.rmtree(kdir) + if __name__ == '__main__': - check_kmod() - - parser = argparse.ArgumentParser(description='wireguard key management') - parser.add_argument('--genkey', action="store_true", help='generate key-pair') - parser.add_argument('--showpub', action="store_true", help='shows public key') - parser.add_argument('--showpriv', action="store_true", help='shows private key') - parser.add_argument('--genpsk', action="store_true", help='generates preshared-key') - args = parser.parse_args() - - try: - if args.genkey: - genkey() - if args.showpub: - showkey("pub") - if args.showpriv: - showkey("pk") - if args.genpsk: - genpsk() - - except ConfigError as e: - print(e) - sys.exit(1) + check_kmod() + parser = argparse.ArgumentParser(description='wireguard key management') + parser.add_argument( + '--genkey', action="store_true", help='generate key-pair') + parser.add_argument( + '--showpub', action="store_true", help='shows public key') + parser.add_argument( + '--showpriv', action="store_true", help='shows private key') + parser.add_argument( + '--genpsk', action="store_true", help='generates preshared-key') + parser.add_argument( + '--location', action="store", help='key location within {}'.format(dir)) + parser.add_argument( + '--listkdir', action="store_true", help='lists named keydirectories') + parser.add_argument( + '--delkdir', action="store_true", help='removes named keydirectories') + args = parser.parse_args() + + try: + if args.genkey: + if args.location: + genkey("{0}/{1}".format(dir, args.location)) + else: + genkey(dir) + + if args.showpub: + if args.location: + showkey("{0}/{1}/public.key".format(dir, args.location)) + else: + showkey("{}/public.key".format(dir)) + if args.showpriv: + if args.location: + showkey("{0}/{1}/private.key".format(dir, args.location)) + else: + showkey("{}/private".format(dir)) + if args.genpsk: + genpsk() + if args.listkdir: + list_key_dirs() + if args.delkdir: + del_key_dir(args.location) + except ConfigError as e: + print(e) + sys.exit(1) -- cgit v1.2.3 From db07e6fa76d90eaf80a06729753fb89266437674 Mon Sep 17 00:00:00 2001 From: hagbard Date: Tue, 10 Sep 2019 11:28:53 -0700 Subject: [wireguard]: T1650 - cli option to delete default wg key --- op-mode-definitions/wireguard.xml | 6 +++--- src/conf_mode/interface-wireguard.py | 7 ++----- src/op_mode/wireguard.py | 18 +++++++++--------- 3 files changed, 14 insertions(+), 17 deletions(-) (limited to 'src/op_mode') diff --git a/op-mode-definitions/wireguard.xml b/op-mode-definitions/wireguard.xml index 785af202c..3c54c81bd 100644 --- a/op-mode-definitions/wireguard.xml +++ b/op-mode-definitions/wireguard.xml @@ -12,7 +12,7 @@ generate a wireguard keypair - ${vyos_op_scripts_dir}/wireguard.py --genkey + sudo ${vyos_op_scripts_dir}/wireguard.py --genkey @@ -119,9 +119,9 @@ Delete wireguard properties - + - Delete wireguard named keypair + Delete a wireguard keypair diff --git a/src/conf_mode/interface-wireguard.py b/src/conf_mode/interface-wireguard.py index 0f9e66aa6..d51a7a08d 100755 --- a/src/conf_mode/interface-wireguard.py +++ b/src/conf_mode/interface-wireguard.py @@ -31,7 +31,6 @@ intfc = WireGuardIf(ifname) kdir = r'/config/auth/wireguard' - def check_kmod(): if not os.path.exists('/sys/module/wireguard'): sl.syslog(sl.LOG_NOTICE, "loading wirguard kmod") @@ -55,7 +54,7 @@ def get_config(): 'fwmark': 0x00, 'mtu': 1420, 'peer': {}, - 'pk': '{}/private.key'.format(kdir) + 'pk' : '{}/default/private.key'.format(kdir) } } @@ -81,8 +80,7 @@ def get_config(): if c.exists(ifname + ' mtu'): config_data[ifname]['mtu'] = c.return_value(ifname + ' mtu') if c.exists(ifname + ' private-key'): - config_data[ifname]['pk'] = "{0}/{1}/private.key".format( - kdir, c.return_value(ifname + ' private-key')) + config_data[ifname]['pk'] = "{0}/{1}/private.key".format(kdir,c.return_value(ifname + ' private-key')) if c.exists(ifname + ' peer'): for p in c.list_nodes(ifname + ' peer'): if not c.exists(ifname + ' peer ' + p + ' disable'): @@ -113,7 +111,6 @@ def get_config(): return config_data - def verify(c): if not c: return None diff --git a/src/op_mode/wireguard.py b/src/op_mode/wireguard.py index e48da2e40..4e93ec6aa 100755 --- a/src/op_mode/wireguard.py +++ b/src/op_mode/wireguard.py @@ -91,18 +91,16 @@ def genpsk(): subprocess.call(['wg genpsk'], shell=True) - def list_key_dirs(): - """ lists all dirs under /config/auth/wireguard """ + """ lists all dirs under /config/auth/wireguard """ if os.path.exists(dir): nks = next(os.walk(dir))[1] for nk in nks: print (nk) - def del_key_dir(kname): """ deletes /config/auth/wireguard/ """ - kdir = "{0}/{1}".format(dir, kname) + kdir = "{0}/{1}".format(dir,kname) if not os.path.isdir(kdir): print ("named keypair {} not found".format(kname)) return 1 @@ -133,24 +131,26 @@ if __name__ == '__main__': if args.location: genkey("{0}/{1}".format(dir, args.location)) else: - genkey(dir) - + genkey("{}/default".format(dir)) if args.showpub: if args.location: showkey("{0}/{1}/public.key".format(dir, args.location)) else: - showkey("{}/public.key".format(dir)) + showkey("{}/default/public.key".format(dir)) if args.showpriv: if args.location: showkey("{0}/{1}/private.key".format(dir, args.location)) else: - showkey("{}/private".format(dir)) + showkey("{}/default/private.key".format(dir)) if args.genpsk: genpsk() if args.listkdir: list_key_dirs() if args.delkdir: - del_key_dir(args.location) + if args.location: + del_key_dir(args.location) + else: + del_key_dir("default") except ConfigError as e: print(e) -- cgit v1.2.3