diff options
Diffstat (limited to 'src')
-rwxr-xr-x | src/conf_mode/conntrack_sync.py | 5 | ||||
-rwxr-xr-x | src/conf_mode/container.py | 27 | ||||
-rwxr-xr-x | src/conf_mode/high-availability.py | 8 | ||||
-rwxr-xr-x | src/conf_mode/protocols_bgp.py | 10 | ||||
-rwxr-xr-x | src/conf_mode/protocols_nhrp.py | 9 | ||||
-rwxr-xr-x | src/conf_mode/ssh.py | 19 | ||||
-rwxr-xr-x | src/conf_mode/vrf.py | 14 | ||||
-rw-r--r-- | src/etc/dhcp/dhclient-enter-hooks.d/03-vyos-ipwrapper | 2 | ||||
-rwxr-xr-x | src/op_mode/containers_op.py | 80 | ||||
-rwxr-xr-x | src/op_mode/show_openvpn.py | 23 | ||||
-rwxr-xr-x | src/op_mode/show_uptime.py | 17 |
11 files changed, 100 insertions, 114 deletions
diff --git a/src/conf_mode/conntrack_sync.py b/src/conf_mode/conntrack_sync.py index 311e01529..c4b2bb488 100755 --- a/src/conf_mode/conntrack_sync.py +++ b/src/conf_mode/conntrack_sync.py @@ -116,6 +116,7 @@ def generate(conntrack): return None def apply(conntrack): + systemd_service = 'conntrackd.service' if not conntrack: # Failover mechanism daemon should be indicated that it no longer needs # to execute conntrackd actions on transition. This is only required @@ -123,7 +124,7 @@ def apply(conntrack): if process_named_running('conntrackd'): resync_vrrp() - call('systemctl stop conntrackd.service') + call(f'systemctl stop {systemd_service}') return None # Failover mechanism daemon should be indicated that it needs to execute @@ -132,7 +133,7 @@ def apply(conntrack): if not process_named_running('conntrackd'): resync_vrrp() - call('systemctl restart conntrackd.service') + call(f'systemctl reload-or-restart {systemd_service}') return None if __name__ == '__main__': diff --git a/src/conf_mode/container.py b/src/conf_mode/container.py index 7e1dc5911..2110fd9e0 100755 --- a/src/conf_mode/container.py +++ b/src/conf_mode/container.py @@ -15,13 +15,13 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. import os -import json from ipaddress import ip_address from ipaddress import ip_network from time import sleep from json import dumps as json_write +from vyos.base import Warning from vyos.config import Config from vyos.configdict import dict_merge from vyos.configdict import node_changed @@ -110,15 +110,21 @@ def verify(container): if 'image' not in container_config: raise ConfigError(f'Container image for "{name}" is mandatory!') - # verify container image exists locally - image = container_config['image'] - # Check if requested container image exists locally. If it does not - # exist locally - inform the user. + # exist locally - inform the user. This is required as there is a + # shared container image storage accross all VyOS images. A user can + # delete a container image from the system, boot into another version + # of VyOS and then it would fail to boot. This is to prevent any + # configuration error when container images are deleted from the + # global storage. A per image local storage would be a super waste + # of diskspace as there will be a full copy (up tu several GB/image) + # on upgrade. This is the "cheapest" and fastest solution in terms + # of image upgrade and deletion. + image = container_config['image'] if run(f'podman image exists {image}') != 0: - raise ConfigError(f'Image "{image}" used in contianer "{name}" does not exist '\ - f'locally.\nPlease use "add container image {image}" to add it '\ - 'to the system!') + Warning(f'Image "{image}" used in contianer "{name}" does not exist '\ + f'locally. Please use "add container image {image}" to add it '\ + f'to the system! Container "{name}" will not be started!') if 'network' in container_config: if len(container_config['network']) > 1: @@ -279,6 +285,11 @@ def apply(container): for name, container_config in container['name'].items(): image = container_config['image'] + if run(f'podman image exists {image}') != 0: + # container image does not exist locally - user already got + # informed by a WARNING in verfiy() - bail out early + continue + if 'disable' in container_config: # check if there is a container by that name running tmp = _cmd('podman ps -a --format "{{.Names}}"') diff --git a/src/conf_mode/high-availability.py b/src/conf_mode/high-availability.py index f939f9469..e14050dd3 100755 --- a/src/conf_mode/high-availability.py +++ b/src/conf_mode/high-availability.py @@ -28,7 +28,6 @@ from vyos.template import render from vyos.template import is_ipv4 from vyos.template import is_ipv6 from vyos.util import call -from vyos.util import is_systemd_service_running from vyos.xml import defaults from vyos import ConfigError from vyos import airbag @@ -161,12 +160,7 @@ def apply(ha): call(f'systemctl stop {service_name}') return None - # XXX: T3944 - reload keepalived configuration if service is already running - # to not cause any service disruption when applying changes. - if is_systemd_service_running(service_name): - call(f'systemctl reload {service_name}') - else: - call(f'systemctl restart {service_name}') + call(f'systemctl reload-or-restart {service_name}') return None if __name__ == '__main__': diff --git a/src/conf_mode/protocols_bgp.py b/src/conf_mode/protocols_bgp.py index a9173ab87..cd46cbcb4 100755 --- a/src/conf_mode/protocols_bgp.py +++ b/src/conf_mode/protocols_bgp.py @@ -169,6 +169,16 @@ def verify(bgp): peer_group = peer_config['peer_group'] if 'remote_as' in peer_config and 'remote_as' in bgp['peer_group'][peer_group]: raise ConfigError(f'Peer-group member "{peer}" cannot override remote-as of peer-group "{peer_group}"!') + if 'interface' in peer_config: + if 'peer_group' in peer_config['interface']: + peer_group = peer_config['interface']['peer_group'] + if 'remote_as' in peer_config['interface'] and 'remote_as' in bgp['peer_group'][peer_group]: + raise ConfigError(f'Peer-group member "{peer}" cannot override remote-as of peer-group "{peer_group}"!') + if 'v6only' in peer_config['interface']: + if 'peer_group' in peer_config['interface']['v6only']: + peer_group = peer_config['interface']['v6only']['peer_group'] + if 'remote_as' in peer_config['interface']['v6only'] and 'remote_as' in bgp['peer_group'][peer_group]: + raise ConfigError(f'Peer-group member "{peer}" cannot override remote-as of peer-group "{peer_group}"!') # Only checks for ipv4 and ipv6 neighbors # Check if neighbor address is assigned as system interface address diff --git a/src/conf_mode/protocols_nhrp.py b/src/conf_mode/protocols_nhrp.py index 92b335085..e4848dea5 100755 --- a/src/conf_mode/protocols_nhrp.py +++ b/src/conf_mode/protocols_nhrp.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2021 VyOS maintainers and contributors +# Copyright (C) 2021-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 @@ -81,6 +81,11 @@ def verify(nhrp): for map_name, map_conf in nhrp_conf['dynamic_map'].items(): if 'nbma_domain_name' not in map_conf: raise ConfigError(f'nbma-domain-name missing on dynamic-map {map_name} on tunnel {name}') + + if 'cisco_authentication' in nhrp_conf: + if len(nhrp_conf['cisco_authentication']) > 8: + raise ConfigError('Maximum length of the secret is 8 characters!') + return None def generate(nhrp): @@ -104,7 +109,7 @@ def apply(nhrp): if rule_handle: remove_nftables_rule('ip filter', 'VYOS_FW_OUTPUT', rule_handle) - action = 'reload-or-restart' if nhrp and 'tunnel' in nhrp else 'stop' + action = 'restart' if nhrp and 'tunnel' in nhrp else 'stop' run(f'systemctl {action} opennhrp') return None diff --git a/src/conf_mode/ssh.py b/src/conf_mode/ssh.py index 487e8c229..28669694b 100755 --- a/src/conf_mode/ssh.py +++ b/src/conf_mode/ssh.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2018-2021 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 @@ -33,6 +33,9 @@ airbag.enable() config_file = r'/run/sshd/sshd_config' systemd_override = r'/etc/systemd/system/ssh.service.d/override.conf' +sshguard_config_file = '/etc/sshguard/sshguard.conf' +sshguard_whitelist = '/etc/sshguard/whitelist' + key_rsa = '/etc/ssh/ssh_host_rsa_key' key_dsa = '/etc/ssh/ssh_host_dsa_key' key_ed25519 = '/etc/ssh/ssh_host_ed25519_key' @@ -54,6 +57,11 @@ def get_config(config=None): # pass config file path - used in override template ssh['config_file'] = config_file + # Ignore default XML values if config doesn't exists + # Delete key from dict + if not conf.exists(base + ['dynamic-protection']): + del ssh['dynamic_protection'] + return ssh def verify(ssh): @@ -86,6 +94,10 @@ def generate(ssh): render(config_file, 'ssh/sshd_config.j2', ssh) render(systemd_override, 'ssh/override.conf.j2', ssh) + + if 'dynamic_protection' in ssh: + render(sshguard_config_file, 'ssh/sshguard_config.j2', ssh) + render(sshguard_whitelist, 'ssh/sshguard_whitelist.j2', ssh) # Reload systemd manager configuration call('systemctl daemon-reload') @@ -95,7 +107,12 @@ def apply(ssh): if not ssh: # SSH access is removed in the commit call('systemctl stop ssh.service') + call('systemctl stop sshguard.service') return None + if 'dynamic_protection' not in ssh: + call('systemctl stop sshguard.service') + else: + call('systemctl restart sshguard.service') call('systemctl restart ssh.service') return None diff --git a/src/conf_mode/vrf.py b/src/conf_mode/vrf.py index f2d041083..972d0289b 100755 --- a/src/conf_mode/vrf.py +++ b/src/conf_mode/vrf.py @@ -83,7 +83,8 @@ def get_config(config=None): conf = Config() base = ['vrf'] - vrf = conf.get_config_dict(base, get_first_key=True) + vrf = conf.get_config_dict(base, key_mangling=('-', '_'), + no_tag_node_value_mangle=True, get_first_key=True) # determine which VRF has been removed for name in node_changed(conf, base + ['name']): @@ -152,7 +153,7 @@ def apply(vrf): # set the default VRF global behaviour bind_all = '0' - if 'bind-to-all' in vrf: + if 'bind_to_all' in vrf: bind_all = '1' sysctl_write('net.ipv4.tcp_l3mdev_accept', bind_all) sysctl_write('net.ipv4.udp_l3mdev_accept', bind_all) @@ -222,6 +223,15 @@ def apply(vrf): # add VRF description if available vrf_if.set_alias(config.get('description', '')) + # Enable/Disable IPv4 forwarding + tmp = dict_search('ip.disable_forwarding', config) + value = '0' if (tmp != None) else '1' + vrf_if.set_ipv4_forwarding(value) + # Enable/Disable IPv6 forwarding + tmp = dict_search('ipv6.disable_forwarding', config) + value = '0' if (tmp != None) else '1' + vrf_if.set_ipv6_forwarding(value) + # Enable/Disable of an interface must always be done at the end of the # derived class to make use of the ref-counting set_admin_state() # function. We will only enable the interface if 'up' was called as diff --git a/src/etc/dhcp/dhclient-enter-hooks.d/03-vyos-ipwrapper b/src/etc/dhcp/dhclient-enter-hooks.d/03-vyos-ipwrapper index 74a7e83bf..5d879471d 100644 --- a/src/etc/dhcp/dhclient-enter-hooks.d/03-vyos-ipwrapper +++ b/src/etc/dhcp/dhclient-enter-hooks.d/03-vyos-ipwrapper @@ -26,7 +26,7 @@ function iptovtysh () { local VTYSH_GATEWAY="" local VTYSH_DEV="" local VTYSH_TAG="210" - local VTYSH_DISTANCE="" + local VTYSH_DISTANCE=$IF_METRIC # convert default route to 0.0.0.0/0 if [ "$4" == "default" ] ; then VTYSH_NETADDR="0.0.0.0/0" diff --git a/src/op_mode/containers_op.py b/src/op_mode/containers_op.py deleted file mode 100755 index c55a48b3c..000000000 --- a/src/op_mode/containers_op.py +++ /dev/null @@ -1,80 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (C) 2021-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 <http://www.gnu.org/licenses/>. - -import os -import argparse - -from getpass import getuser -from vyos.configquery import ConfigTreeQuery -from vyos.base import Warning -from vyos.util import cmd -from subprocess import STDOUT - -parser = argparse.ArgumentParser() -parser.add_argument("-a", "--all", action="store_true", help="Show all containers") -parser.add_argument("-i", "--image", action="store_true", help="Show container images") -parser.add_argument("-n", "--networks", action="store_true", help="Show container images") -parser.add_argument("-p", "--pull", action="store", help="Pull image for container") -parser.add_argument("-d", "--remove", action="store", help="Delete container image") -parser.add_argument("-u", "--update", action="store", help="Update given container image") - -config = ConfigTreeQuery() -base = ['container'] - -if getuser() != 'root': - raise OSError('This functions needs to be run as root to return correct results!') - -if __name__ == '__main__': - args = parser.parse_args() - - if args.all: - print(cmd('podman ps --all')) - elif args.image: - print(cmd('podman image ls')) - elif args.networks: - print(cmd('podman network ls')) - - elif args.pull: - image = args.pull - registry_config = '/etc/containers/registries.conf' - if not os.path.exists(registry_config): - Warning('No container registry configured. Please use full URL when '\ - 'adding an image. E.g. prefix with docker.io/image-name.') - try: - print(os.system(f'podman image pull {image}')) - except Exception as e: - print(f'Unable to download image "{image}". {e}') - - elif args.remove: - image = args.remove - try: - print(os.system(f'podman image rm {image}')) - except FileNotFoundError as e: - print(f'Unable to delete image "{image}". {e}') - - elif args.update: - tmp = config.get_config_dict(base + ['name', args.update], - key_mangling=('-', '_'), get_first_key=True) - try: - image = tmp['image'] - print(cmd(f'podman image pull {image}')) - except Exception as e: - print(f'Unable to download image "{image}". {e}') - else: - parser.print_help() - exit(1) - - exit(0) diff --git a/src/op_mode/show_openvpn.py b/src/op_mode/show_openvpn.py index f7b99cc0d..9a5adcffb 100755 --- a/src/op_mode/show_openvpn.py +++ b/src/op_mode/show_openvpn.py @@ -26,10 +26,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 Tunnel IP Local Host TX bytes RX bytes Connected Since +--------- ----------- --------- ---------- -------- -------- --------------- {% for c in clients %} -{{ "%-15s"|format(c.name) }} {{ "%-21s"|format(c.remote) }} {{ "%-21s"|format(local) }} {{ "%-9s"|format(c.tx_bytes) }} {{ "%-9s"|format(c.rx_bytes) }} {{ c.online_since }} +{{ "%-15s"|format(c.name) }} {{ "%-21s"|format(c.remote) }} {{ "%-15s"|format(c.tunnel) }} {{ "%-21s"|format(local) }} {{ "%-9s"|format(c.tx_bytes) }} {{ "%-9s"|format(c.rx_bytes) }} {{ c.online_since }} {% endfor %} {% endif %} """ @@ -50,6 +50,19 @@ def bytes2HR(size): output="{0:.1f} {1}".format(size, suff[suffIdx]) return output +def get_vpn_tunnel_address(peer, interface): + lst = [] + status_file = '/var/run/openvpn/{}.status'.format(interface) + + with open(status_file, 'r') as f: + lines = f.readlines() + for line in lines: + if peer in line: + lst.append(line) + tunnel_ip = lst[1].split(',')[0] + + return tunnel_ip + def get_status(mode, interface): status_file = '/var/run/openvpn/{}.status'.format(interface) # this is an empirical value - I assume we have no more then 999999 @@ -110,7 +123,7 @@ def get_status(mode, interface): 'tx_bytes': bytes2HR(line.split(',')[3]), 'online_since': line.split(',')[4] } - + client["tunnel"] = get_vpn_tunnel_address(client['remote'], interface) data['clients'].append(client) continue else: @@ -173,5 +186,7 @@ if __name__ == '__main__': if len(remote_host) >= 1: client['remote'] = str(remote_host[0]) + ':' + remote_port + client['tunnel'] = 'N/A' + tmpl = jinja2.Template(outp_tmpl) print(tmpl.render(data)) diff --git a/src/op_mode/show_uptime.py b/src/op_mode/show_uptime.py index 1b5e33fa9..b70c60cf8 100755 --- a/src/op_mode/show_uptime.py +++ b/src/op_mode/show_uptime.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2021 VyOS maintainers and contributors +# Copyright (C) 2021-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 as @@ -26,14 +26,17 @@ def get_uptime_seconds(): def get_load_averages(): from re import search from vyos.util import cmd + from vyos.cpu import get_core_count data = cmd("uptime") matches = search(r"load average:\s*(?P<one>[0-9\.]+)\s*,\s*(?P<five>[0-9\.]+)\s*,\s*(?P<fifteen>[0-9\.]+)\s*", data) + core_count = get_core_count() + res = {} - res[1] = float(matches["one"]) - res[5] = float(matches["five"]) - res[15] = float(matches["fifteen"]) + res[1] = float(matches["one"]) / core_count + res[5] = float(matches["five"]) / core_count + res[15] = float(matches["fifteen"]) / core_count return res @@ -53,9 +56,9 @@ def get_formatted_output(): 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) + out += "1 minute: {:.01f}%\n".format(avgs[1]*100) + out += "5 minutes: {:.01f}%\n".format(avgs[5]*100) + out += "15 minutes: {:.01f}%\n".format(avgs[15]*100) return out |