diff options
Diffstat (limited to 'src/conf_mode')
58 files changed, 209 insertions, 329 deletions
diff --git a/src/conf_mode/container.py b/src/conf_mode/container.py index e967bee71..910a92a7c 100755 --- a/src/conf_mode/container.py +++ b/src/conf_mode/container.py @@ -32,6 +32,7 @@ from vyos.utils.file import write_file from vyos.utils.process import call from vyos.utils.process import cmd from vyos.utils.process import run +from vyos.utils.network import interface_exists from vyos.template import bracketize_ipv6 from vyos.template import inc_ip from vyos.template import is_ipv4 @@ -471,7 +472,7 @@ def apply(container): # T5147: Networks are started only as soon as there is a consumer. # If only a network is created in the first place, no need to assign # it to a VRF as there's no consumer, yet. - if os.path.exists(f'/sys/class/net/{network_name}'): + if interface_exists(network_name): tmp = Interface(network_name) tmp.add_ipv6_eui64_address('fe80::/64') tmp.set_vrf(network_config.get('vrf', '')) diff --git a/src/conf_mode/firewall.py b/src/conf_mode/firewall.py index 810437dda..3cf618363 100755 --- a/src/conf_mode/firewall.py +++ b/src/conf_mode/firewall.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2021-2023 VyOS maintainers and contributors +# Copyright (C) 2021-2024 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 @@ -18,7 +18,6 @@ import os import re from glob import glob -from json import loads from sys import exit from vyos.base import Warning @@ -31,11 +30,9 @@ from vyos.ethtool import Ethtool from vyos.firewall import fqdn_config_parse from vyos.firewall import geoip_update from vyos.template import render -from vyos.utils.process import call -from vyos.utils.process import cmd from vyos.utils.dict import dict_search_args from vyos.utils.dict import dict_search_recursive -from vyos.utils.process import process_named_running +from vyos.utils.process import call from vyos.utils.process import rc_cmd from vyos import ConfigError from vyos import airbag @@ -491,7 +488,7 @@ def apply_sysfs(firewall): f.write(value) def apply(firewall): - install_result, output = rc_cmd(f'nft -f {nftables_conf}') + install_result, output = rc_cmd(f'nft --file {nftables_conf}') if install_result == 1: raise ConfigError(f'Failed to apply firewall: {output}') diff --git a/src/conf_mode/interfaces_bonding.py b/src/conf_mode/interfaces_bonding.py index 8184d8415..371b219c0 100755 --- a/src/conf_mode/interfaces_bonding.py +++ b/src/conf_mode/interfaces_bonding.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2019-2023 VyOS maintainers and contributors +# Copyright (C) 2019-2024 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,10 +14,8 @@ # 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 - from sys import exit -from netifaces import interfaces + from vyos.config import Config from vyos.configdict import get_interface_dict from vyos.configdict import is_node_changed @@ -29,7 +27,6 @@ from vyos.configverify import verify_bridge_delete from vyos.configverify import verify_dhcpv6 from vyos.configverify import verify_mirror_redirect from vyos.configverify import verify_mtu_ipv6 -from vyos.configverify import verify_source_interface from vyos.configverify import verify_vlan_config from vyos.configverify import verify_vrf from vyos.ifconfig import BondIf @@ -38,6 +35,7 @@ from vyos.ifconfig import Section from vyos.template import render_to_string from vyos.utils.dict import dict_search from vyos.utils.dict import dict_to_paths_values +from vyos.utils.network import interface_exists from vyos.configdict import has_address_configured from vyos.configdict import has_vrf_configured from vyos.configdep import set_dependents, call_dependents @@ -209,7 +207,7 @@ def verify(bond): if interface == 'lo': raise ConfigError('Loopback interface "lo" can not be added to a bond') - if interface not in interfaces(): + if not interface_exists(interface): raise ConfigError(error_msg + 'it does not exist!') if 'is_bridge_member' in interface_config: diff --git a/src/conf_mode/interfaces_ethernet.py b/src/conf_mode/interfaces_ethernet.py index 2c0f846c3..41efdb03c 100755 --- a/src/conf_mode/interfaces_ethernet.py +++ b/src/conf_mode/interfaces_ethernet.py @@ -15,9 +15,7 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. import os -import pprint -from glob import glob from sys import exit from vyos.base import Warning @@ -26,7 +24,6 @@ from vyos.configdict import get_interface_dict from vyos.configdict import is_node_changed from vyos.configverify import verify_address from vyos.configverify import verify_dhcpv6 -from vyos.configverify import verify_eapol from vyos.configverify import verify_interface_exists from vyos.configverify import verify_mirror_redirect from vyos.configverify import verify_mtu @@ -34,6 +31,8 @@ from vyos.configverify import verify_mtu_ipv6 from vyos.configverify import verify_vlan_config from vyos.configverify import verify_vrf from vyos.configverify import verify_bond_bridge_member +from vyos.configverify import verify_pki_certificate +from vyos.configverify import verify_pki_ca_certificate from vyos.ethtool import Ethtool from vyos.ifconfig import EthernetIf from vyos.ifconfig import BondIf @@ -263,6 +262,22 @@ def verify_allowedbond_changes(ethernet: dict): f' on interface "{ethernet["ifname"]}".' \ f' Interface is a bond member') +def verify_eapol(ethernet: dict): + """ + Common helper function used by interface implementations to perform + recurring validation of EAPoL configuration. + """ + if 'eapol' not in ethernet: + return + + if 'certificate' not in ethernet['eapol']: + raise ConfigError('Certificate must be specified when using EAPoL!') + + verify_pki_certificate(ethernet, ethernet['eapol']['certificate'], no_password_protected=True) + + if 'ca_certificate' in ethernet['eapol']: + for ca_cert in ethernet['eapol']['ca_certificate']: + verify_pki_ca_certificate(ethernet, ca_cert) def verify(ethernet): if 'deleted' in ethernet: diff --git a/src/conf_mode/interfaces_geneve.py b/src/conf_mode/interfaces_geneve.py index f6694ddde..769139e0f 100755 --- a/src/conf_mode/interfaces_geneve.py +++ b/src/conf_mode/interfaces_geneve.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2019-2022 VyOS maintainers and contributors +# Copyright (C) 2019-2024 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 @@ -15,7 +15,6 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. from sys import exit -from netifaces import interfaces from vyos.config import Config from vyos.configdict import get_interface_dict @@ -26,6 +25,7 @@ from vyos.configverify import verify_bridge_delete from vyos.configverify import verify_mirror_redirect from vyos.configverify import verify_bond_bridge_member from vyos.ifconfig import GeneveIf +from vyos.utils.network import interface_exists from vyos import ConfigError from vyos import airbag @@ -77,8 +77,8 @@ def generate(geneve): def apply(geneve): # Check if GENEVE interface already exists if 'rebuild_required' in geneve or 'delete' in geneve: - if geneve['ifname'] in interfaces(): - g = GeneveIf(geneve['ifname']) + if interface_exists(geneve['ifname']): + g = GeneveIf(**geneve) # GENEVE is super picky and the tunnel always needs to be recreated, # thus we can simply always delete it first. g.remove() diff --git a/src/conf_mode/interfaces_l2tpv3.py b/src/conf_mode/interfaces_l2tpv3.py index e1db3206e..e25793543 100755 --- a/src/conf_mode/interfaces_l2tpv3.py +++ b/src/conf_mode/interfaces_l2tpv3.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2019-2020 VyOS maintainers and contributors +# Copyright (C) 2019-2024 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,10 +14,7 @@ # 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 - from sys import exit -from netifaces import interfaces from vyos.config import Config from vyos.configdict import get_interface_dict @@ -30,6 +27,7 @@ from vyos.configverify import verify_bond_bridge_member from vyos.ifconfig import L2TPv3If from vyos.utils.kernel import check_kmod from vyos.utils.network import is_addr_assigned +from vyos.utils.network import interface_exists from vyos import ConfigError from vyos import airbag airbag.enable() @@ -87,7 +85,7 @@ def generate(l2tpv3): def apply(l2tpv3): # Check if L2TPv3 interface already exists - if l2tpv3['ifname'] in interfaces(): + if interface_exists(l2tpv3['ifname']): # L2TPv3 is picky when changing tunnels/sessions, thus we can simply # always delete it first. l = L2TPv3If(**l2tpv3) diff --git a/src/conf_mode/interfaces_loopback.py b/src/conf_mode/interfaces_loopback.py index 08d34477a..a784e9ec2 100755 --- a/src/conf_mode/interfaces_loopback.py +++ b/src/conf_mode/interfaces_loopback.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2019-2020 VyOS maintainers and contributors +# Copyright (C) 2019-2024 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,8 +14,6 @@ # 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 - from sys import exit from vyos.config import Config diff --git a/src/conf_mode/interfaces_macsec.py b/src/conf_mode/interfaces_macsec.py index 0a927ac88..eb0ca9a8b 100755 --- a/src/conf_mode/interfaces_macsec.py +++ b/src/conf_mode/interfaces_macsec.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020-2023 VyOS maintainers and contributors +# Copyright (C) 2020-2024 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 @@ -16,7 +16,6 @@ import os -from netifaces import interfaces from sys import exit from vyos.config import Config @@ -35,6 +34,7 @@ from vyos.ifconfig import Interface from vyos.template import render from vyos.utils.process import call from vyos.utils.dict import dict_search +from vyos.utils.network import interface_exists from vyos.utils.process import is_systemd_service_running from vyos import ConfigError from vyos import airbag @@ -172,8 +172,8 @@ def apply(macsec): if 'deleted' in macsec or 'shutdown_required' in macsec: call(f'systemctl stop {systemd_service}') - if macsec['ifname'] in interfaces(): - tmp = MACsecIf(macsec['ifname']) + if interface_exists(macsec['ifname']): + tmp = MACsecIf(**macsec) tmp.remove() if 'deleted' in macsec: diff --git a/src/conf_mode/interfaces_openvpn.py b/src/conf_mode/interfaces_openvpn.py index 45569dd21..505ec55c6 100755 --- a/src/conf_mode/interfaces_openvpn.py +++ b/src/conf_mode/interfaces_openvpn.py @@ -16,7 +16,6 @@ import os import re -import tempfile from cryptography.hazmat.primitives.asymmetric import ec from glob import glob @@ -26,7 +25,6 @@ from ipaddress import IPv4Network from ipaddress import IPv6Address from ipaddress import IPv6Network from ipaddress import summarize_address_range -from netifaces import interfaces from secrets import SystemRandom from shutil import rmtree @@ -63,6 +61,7 @@ from vyos.utils.process import call from vyos.utils.permission import chown from vyos.utils.process import cmd from vyos.utils.network import is_addr_assigned +from vyos.utils.network import interface_exists from vyos import ConfigError from vyos import airbag @@ -683,7 +682,7 @@ def apply(openvpn): if os.path.isfile(cleanup_file): os.unlink(cleanup_file) - if interface in interfaces(): + if interface_exists(interface): VTunIf(interface).remove() # dynamically load/unload DCO Kernel extension if requested diff --git a/src/conf_mode/interfaces_pppoe.py b/src/conf_mode/interfaces_pppoe.py index 42f084309..412676c7d 100755 --- a/src/conf_mode/interfaces_pppoe.py +++ b/src/conf_mode/interfaces_pppoe.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2019-2021 VyOS maintainers and contributors +# Copyright (C) 2019-2024 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 @@ -17,16 +17,12 @@ import os from sys import exit -from copy import deepcopy -from netifaces import interfaces from vyos.config import Config from vyos.configdict import get_interface_dict from vyos.configdict import is_node_changed -from vyos.configdict import get_pppoe_interfaces from vyos.configverify import verify_authentication from vyos.configverify import verify_source_interface -from vyos.configverify import verify_interface_exists from vyos.configverify import verify_vrf from vyos.configverify import verify_mtu_ipv6 from vyos.configverify import verify_mirror_redirect diff --git a/src/conf_mode/interfaces_pseudo-ethernet.py b/src/conf_mode/interfaces_pseudo-ethernet.py index dce5c2358..446beffd3 100755 --- a/src/conf_mode/interfaces_pseudo-ethernet.py +++ b/src/conf_mode/interfaces_pseudo-ethernet.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2019-2022 VyOS maintainers and contributors +# Copyright (C) 2019-2024 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 @@ -15,7 +15,6 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. from sys import exit -from netifaces import interfaces from vyos.config import Config from vyos.configdict import get_interface_dict @@ -29,8 +28,8 @@ from vyos.configverify import verify_source_interface from vyos.configverify import verify_vlan_config from vyos.configverify import verify_mtu_parent from vyos.configverify import verify_mirror_redirect -from vyos.configverify import verify_bond_bridge_member from vyos.ifconfig import MACVLANIf +from vyos.utils.network import interface_exists from vyos import ConfigError from vyos import airbag @@ -84,8 +83,8 @@ def generate(peth): def apply(peth): # Check if the MACVLAN interface already exists if 'rebuild_required' in peth or 'deleted' in peth: - if peth['ifname'] in interfaces(): - p = MACVLANIf(peth['ifname']) + if interface_exists(peth['ifname']): + p = MACVLANIf(**peth) # MACVLAN is always needs to be recreated, # thus we can simply always delete it first. p.remove() diff --git a/src/conf_mode/interfaces_tunnel.py b/src/conf_mode/interfaces_tunnel.py index efa5ebc64..43ba72857 100755 --- a/src/conf_mode/interfaces_tunnel.py +++ b/src/conf_mode/interfaces_tunnel.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2018-2022 yOS maintainers and contributors +# Copyright (C) 2018-2024 yOS 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,10 +14,7 @@ # 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 - from sys import exit -from netifaces import interfaces from vyos.config import Config from vyos.configdict import get_interface_dict @@ -31,10 +28,10 @@ from vyos.configverify import verify_vrf from vyos.configverify import verify_tunnel from vyos.configverify import verify_bond_bridge_member from vyos.ifconfig import Interface -from vyos.ifconfig import Section from vyos.ifconfig import TunnelIf -from vyos.utils.network import get_interface_config from vyos.utils.dict import dict_search +from vyos.utils.network import get_interface_config +from vyos.utils.network import interface_exists from vyos import ConfigError from vyos import airbag airbag.enable() @@ -202,7 +199,7 @@ def apply(tunnel): if ('deleted' in tunnel or 'encapsulation_changed' in tunnel or encap in ['gretap', 'ip6gretap', 'erspan', 'ip6erspan'] or remote in ['any'] or 'key_changed' in tunnel): - if interface in interfaces(): + if interface_exists(interface): tmp = Interface(interface) tmp.remove() if 'deleted' in tunnel: diff --git a/src/conf_mode/interfaces_virtual-ethernet.py b/src/conf_mode/interfaces_virtual-ethernet.py index 8efe89c41..cb6104f59 100755 --- a/src/conf_mode/interfaces_virtual-ethernet.py +++ b/src/conf_mode/interfaces_virtual-ethernet.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2022 VyOS maintainers and contributors +# Copyright (C) 2022-2024 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 @@ -16,7 +16,6 @@ from sys import exit -from netifaces import interfaces from vyos import ConfigError from vyos import airbag from vyos.config import Config @@ -25,7 +24,7 @@ from vyos.configverify import verify_address from vyos.configverify import verify_bridge_delete from vyos.configverify import verify_vrf from vyos.ifconfig import VethIf - +from vyos.utils.network import interface_exists airbag.enable() def get_config(config=None): @@ -92,8 +91,8 @@ def generate(peth): def apply(veth): # Check if the Veth interface already exists if 'rebuild_required' in veth or 'deleted' in veth: - if veth['ifname'] in interfaces(): - p = VethIf(veth['ifname']) + if interface_exists(veth['ifname']): + p = VethIf(**veth) p.remove() if 'deleted' not in veth: diff --git a/src/conf_mode/interfaces_vti.py b/src/conf_mode/interfaces_vti.py index 9871810ae..e6a833df7 100755 --- a/src/conf_mode/interfaces_vti.py +++ b/src/conf_mode/interfaces_vti.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2021 VyOS maintainers and contributors +# Copyright (C) 2021-2024 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,14 +14,12 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -from netifaces import interfaces from sys import exit from vyos.config import Config from vyos.configdict import get_interface_dict from vyos.configverify import verify_mirror_redirect from vyos.ifconfig import VTIIf -from vyos.utils.dict import dict_search from vyos import ConfigError from vyos import airbag airbag.enable() diff --git a/src/conf_mode/interfaces_vxlan.py b/src/conf_mode/interfaces_vxlan.py index 4251e611b..39365968a 100755 --- a/src/conf_mode/interfaces_vxlan.py +++ b/src/conf_mode/interfaces_vxlan.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2019-2023 VyOS maintainers and contributors +# Copyright (C) 2019-2024 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,10 +14,7 @@ # 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 - from sys import exit -from netifaces import interfaces from vyos.base import Warning from vyos.config import Config @@ -35,6 +32,7 @@ from vyos.ifconfig import Interface from vyos.ifconfig import VXLANIf from vyos.template import is_ipv6 from vyos.utils.dict import dict_search +from vyos.utils.network import interface_exists from vyos import ConfigError from vyos import airbag airbag.enable() @@ -212,8 +210,8 @@ def generate(vxlan): def apply(vxlan): # Check if the VXLAN interface already exists if 'rebuild_required' in vxlan or 'delete' in vxlan: - if vxlan['ifname'] in interfaces(): - v = VXLANIf(vxlan['ifname']) + if interface_exists(vxlan['ifname']): + v = VXLANIf(**vxlan) # VXLAN is super picky and the tunnel always needs to be recreated, # thus we can simply always delete it first. v.remove() diff --git a/src/conf_mode/interfaces_wireguard.py b/src/conf_mode/interfaces_wireguard.py index 79e5d3f44..0e0b77877 100755 --- a/src/conf_mode/interfaces_wireguard.py +++ b/src/conf_mode/interfaces_wireguard.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2018-2023 VyOS maintainers and contributors +# Copyright (C) 2018-2024 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 @@ -17,7 +17,6 @@ from sys import exit from vyos.config import Config -from vyos.configdict import dict_merge from vyos.configdict import get_interface_dict from vyos.configdict import is_node_changed from vyos.configverify import verify_vrf diff --git a/src/conf_mode/load-balancing_reverse-proxy.py b/src/conf_mode/load-balancing_reverse-proxy.py index 7338fe573..694a4e1ea 100755 --- a/src/conf_mode/load-balancing_reverse-proxy.py +++ b/src/conf_mode/load-balancing_reverse-proxy.py @@ -20,6 +20,9 @@ from sys import exit from shutil import rmtree from vyos.config import Config +from vyos.configverify import verify_pki_certificate +from vyos.configverify import verify_pki_ca_certificate +from vyos.utils.dict import dict_search from vyos.utils.process import call from vyos.utils.network import check_port_availability from vyos.utils.network import is_listen_port_bind_service @@ -33,8 +36,7 @@ airbag.enable() load_balancing_dir = '/run/haproxy' load_balancing_conf_file = f'{load_balancing_dir}/haproxy.cfg' systemd_service = 'haproxy.service' -systemd_override = r'/run/systemd/system/haproxy.service.d/10-override.conf' - +systemd_override = '/run/systemd/system/haproxy.service.d/10-override.conf' def get_config(config=None): if config: @@ -54,7 +56,6 @@ def get_config(config=None): return lb - def verify(lb): if not lb: return None @@ -83,6 +84,15 @@ def verify(lb): if {'send_proxy', 'send_proxy_v2'} <= set(bk_server_conf): raise ConfigError(f'Cannot use both "send-proxy" and "send-proxy-v2" for server "{bk_server}"') + for front, front_config in lb['service'].items(): + for cert in dict_search('ssl.certificate', front_config) or []: + verify_pki_certificate(lb, cert) + + for back, back_config in lb['backend'].items(): + tmp = dict_search('ssl.ca_certificate', front_config) + if tmp: verify_pki_ca_certificate(lb, tmp) + + def generate(lb): if not lb: # Delete /run/haproxy/haproxy.cfg diff --git a/src/conf_mode/nat.py b/src/conf_mode/nat.py index b3f38c04a..4cd9b570d 100755 --- a/src/conf_mode/nat.py +++ b/src/conf_mode/nat.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020-2023 VyOS maintainers and contributors +# Copyright (C) 2020-2024 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,8 +14,6 @@ # 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 jmespath -import json import os from sys import exit @@ -223,19 +221,19 @@ def generate(nat): render(nftables_static_nat_conf, 'firewall/nftables-static-nat.j2', nat) # dry-run newly generated configuration - tmp = run(f'nft -c -f {nftables_nat_config}') + tmp = run(f'nft --check --file {nftables_nat_config}') if tmp > 0: raise ConfigError('Configuration file errors encountered!') - tmp = run(f'nft -c -f {nftables_static_nat_conf}') + tmp = run(f'nft --check --file {nftables_static_nat_conf}') if tmp > 0: raise ConfigError('Configuration file errors encountered!') return None def apply(nat): - cmd(f'nft -f {nftables_nat_config}') - cmd(f'nft -f {nftables_static_nat_conf}') + cmd(f'nft --file {nftables_nat_config}') + cmd(f'nft --file {nftables_static_nat_conf}') if not nat or 'deleted' in nat: os.unlink(nftables_nat_config) diff --git a/src/conf_mode/nat64.py b/src/conf_mode/nat64.py index 6026c61d0..c1e7ebf85 100755 --- a/src/conf_mode/nat64.py +++ b/src/conf_mode/nat64.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2023 VyOS maintainers and contributors +# Copyright (C) 2023-2024 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 @@ -26,7 +26,6 @@ from json import dumps as json_write from vyos import ConfigError from vyos import airbag from vyos.config import Config -from vyos.configdict import dict_merge from vyos.configdict import is_node_changed from vyos.utils.dict import dict_search from vyos.utils.file import write_file diff --git a/src/conf_mode/nat66.py b/src/conf_mode/nat66.py index 4c1ead258..fe017527d 100755 --- a/src/conf_mode/nat66.py +++ b/src/conf_mode/nat66.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020-2023 VyOS maintainers and contributors +# Copyright (C) 2020-2024 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,8 +14,6 @@ # 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 jmespath -import json import os from sys import exit @@ -106,7 +104,7 @@ def apply(nat): if not nat: return None - cmd(f'nft -f {nftables_nat66_config}') + cmd(f'nft --file {nftables_nat66_config}') call_dependents() return None diff --git a/src/conf_mode/netns.py b/src/conf_mode/netns.py index 7cee33bc6..b57e46a0d 100755 --- a/src/conf_mode/netns.py +++ b/src/conf_mode/netns.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2021 VyOS maintainers and contributors +# Copyright (C) 2021-2024 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 @@ -17,14 +17,11 @@ import os from sys import exit -from tempfile import NamedTemporaryFile from vyos.config import Config from vyos.configdict import node_changed -from vyos.ifconfig import Interface from vyos.utils.process import call from vyos.utils.dict import dict_search -from vyos.utils.network import get_interface_config from vyos import ConfigError from vyos import airbag airbag.enable() diff --git a/src/conf_mode/policy_local-route.py b/src/conf_mode/policy_local-route.py index 91e4fce2c..f458f4e82 100755 --- a/src/conf_mode/policy_local-route.py +++ b/src/conf_mode/policy_local-route.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020-2023 VyOS maintainers and contributors +# Copyright (C) 2020-2024 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,23 +14,19 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -import os - from itertools import product from sys import exit -from netifaces import interfaces from vyos.config import Config from vyos.configdict import dict_merge from vyos.configdict import node_changed from vyos.configdict import leaf_node_changed -from vyos.template import render +from vyos.configverify import verify_interface_exists from vyos.utils.process import call from vyos import ConfigError from vyos import airbag airbag.enable() - def get_config(config=None): if config: @@ -227,8 +223,7 @@ def verify(pbr): if 'inbound_interface' in pbr_route['rule'][rule]: interface = pbr_route['rule'][rule]['inbound_interface'] - if interface not in interfaces(): - raise ConfigError(f'Interface "{interface}" does not exist') + verify_interface_exists(interface) return None diff --git a/src/conf_mode/policy_route.py b/src/conf_mode/policy_route.py index 6d7a06714..c58fe1bce 100755 --- a/src/conf_mode/policy_route.py +++ b/src/conf_mode/policy_route.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2021-2023 VyOS maintainers and contributors +# Copyright (C) 2021-2024 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 @@ -177,7 +177,7 @@ def cleanup_table_marks(): cmd(f'{cmd_str} rule del fwmark {fwmark} table {table}') def apply(policy): - install_result = run(f'nft -f {nftables_conf}') + install_result = run(f'nft --file {nftables_conf}') if install_result == 1: raise ConfigError('Failed to apply policy based routing') diff --git a/src/conf_mode/protocols_babel.py b/src/conf_mode/protocols_babel.py index 104711b55..90b6e4a31 100755 --- a/src/conf_mode/protocols_babel.py +++ b/src/conf_mode/protocols_babel.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2021-2023 VyOS maintainers and contributors +# Copyright (C) 2021-2024 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,15 +14,12 @@ # 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 - from sys import exit from vyos.config import Config from vyos.config import config_dict_merge from vyos.configdict import dict_merge from vyos.configdict import node_changed -from vyos.configverify import verify_common_route_maps from vyos.configverify import verify_access_list from vyos.configverify import verify_prefix_list from vyos.utils.dict import dict_search diff --git a/src/conf_mode/protocols_bfd.py b/src/conf_mode/protocols_bfd.py index 37421efb4..1c01a9013 100755 --- a/src/conf_mode/protocols_bfd.py +++ b/src/conf_mode/protocols_bfd.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2019-2021 VyOS maintainers and contributors +# Copyright (C) 2019-2024 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,8 +14,6 @@ # 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 - from vyos.config import Config from vyos.configverify import verify_vrf from vyos.template import is_ipv6 diff --git a/src/conf_mode/protocols_bgp.py b/src/conf_mode/protocols_bgp.py index 512fa26e9..2b16de775 100755 --- a/src/conf_mode/protocols_bgp.py +++ b/src/conf_mode/protocols_bgp.py @@ -285,6 +285,7 @@ def verify(bgp): elif tmp != 'default': raise ConfigError(f'{error_msg} "{tmp}"!') + peer_groups_context = dict() # Common verification for both peer-group and neighbor statements for neighbor in ['neighbor', 'peer_group']: # bail out early if there is no neighbor or peer-group statement @@ -301,6 +302,18 @@ def verify(bgp): raise ConfigError(f'Specified peer-group "{peer_group}" for '\ f'neighbor "{neighbor}" does not exist!') + if 'remote_as' in peer_config: + is_ibgp = True + if peer_config['remote_as'] != 'internal' and \ + peer_config['remote_as'] != bgp['system_as']: + is_ibgp = False + + if peer_group not in peer_groups_context: + peer_groups_context[peer_group] = is_ibgp + elif peer_groups_context[peer_group] != is_ibgp: + raise ConfigError(f'Peer-group members must be ' + f'all internal or all external') + if 'local_role' in peer_config: #Ensure Local Role has only one value. if len(peer_config['local_role']) > 1: diff --git a/src/conf_mode/protocols_igmp-proxy.py b/src/conf_mode/protocols_igmp-proxy.py index 40db417dd..afcef0985 100755 --- a/src/conf_mode/protocols_igmp-proxy.py +++ b/src/conf_mode/protocols_igmp-proxy.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2018-2020 VyOS maintainers and contributors +# Copyright (C) 2018-2024 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 @@ -17,10 +17,10 @@ import os from sys import exit -from netifaces import interfaces from vyos.base import Warning from vyos.config import Config +from vyos.configverify import verify_interface_exists from vyos.template import render from vyos.utils.process import call from vyos.utils.dict import dict_search @@ -65,8 +65,7 @@ def verify(igmp_proxy): upstream = 0 for interface, config in igmp_proxy['interface'].items(): - if interface not in interfaces(): - raise ConfigError(f'Interface "{interface}" does not exist') + verify_interface_exists(interface) if dict_search('role', config) == 'upstream': upstream += 1 diff --git a/src/conf_mode/protocols_isis.py b/src/conf_mode/protocols_isis.py index 6c9925b80..9cadfd081 100755 --- a/src/conf_mode/protocols_isis.py +++ b/src/conf_mode/protocols_isis.py @@ -14,8 +14,6 @@ # 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 - from sys import exit from sys import argv diff --git a/src/conf_mode/protocols_nhrp.py b/src/conf_mode/protocols_nhrp.py index c339c6391..0bd68b7d8 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-2023 VyOS maintainers and contributors +# Copyright (C) 2021-2024 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,7 +19,6 @@ import os from vyos.config import Config from vyos.configdict import node_changed from vyos.template import render -from vyos.utils.process import process_named_running from vyos.utils.process import run from vyos import ConfigError from vyos import airbag @@ -93,7 +92,7 @@ def generate(nhrp): return None def apply(nhrp): - nft_rc = run(f'nft -f {nhrp_nftables_conf}') + nft_rc = run(f'nft --file {nhrp_nftables_conf}') if nft_rc != 0: raise ConfigError('Failed to apply NHRP tunnel firewall rules') diff --git a/src/conf_mode/protocols_ospfv3.py b/src/conf_mode/protocols_ospfv3.py index afd767dbf..1bb172293 100755 --- a/src/conf_mode/protocols_ospfv3.py +++ b/src/conf_mode/protocols_ospfv3.py @@ -14,8 +14,6 @@ # 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 - from sys import exit from sys import argv diff --git a/src/conf_mode/protocols_rip.py b/src/conf_mode/protocols_rip.py index bd47dfd00..9afac544d 100755 --- a/src/conf_mode/protocols_rip.py +++ b/src/conf_mode/protocols_rip.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2021-2022 VyOS maintainers and contributors +# Copyright (C) 2021-2024 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,8 +14,6 @@ # 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 - from sys import exit from vyos.config import Config diff --git a/src/conf_mode/protocols_ripng.py b/src/conf_mode/protocols_ripng.py index dd1550033..23416ff96 100755 --- a/src/conf_mode/protocols_ripng.py +++ b/src/conf_mode/protocols_ripng.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2021 VyOS maintainers and contributors +# Copyright (C) 2021-2024 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,8 +14,6 @@ # 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 - from sys import exit from vyos.config import Config diff --git a/src/conf_mode/protocols_segment-routing.py b/src/conf_mode/protocols_segment-routing.py index d865c2ac0..b36c2ca11 100755 --- a/src/conf_mode/protocols_segment-routing.py +++ b/src/conf_mode/protocols_segment-routing.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2023 VyOS maintainers and contributors +# Copyright (C) 2023-2024 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,8 +14,6 @@ # 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 - from sys import exit from vyos.config import Config diff --git a/src/conf_mode/protocols_static.py b/src/conf_mode/protocols_static.py index 5def8d645..a2373218a 100755 --- a/src/conf_mode/protocols_static.py +++ b/src/conf_mode/protocols_static.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2021-2023 VyOS maintainers and contributors +# Copyright (C) 2021-2024 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,8 +14,6 @@ # 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 - from sys import exit from sys import argv diff --git a/src/conf_mode/protocols_static_neighbor-proxy.py b/src/conf_mode/protocols_static_neighbor-proxy.py index 10cc1e748..8a1ea1df9 100755 --- a/src/conf_mode/protocols_static_neighbor-proxy.py +++ b/src/conf_mode/protocols_static_neighbor-proxy.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2023 VyOS maintainers and contributors +# Copyright (C) 2023-2024 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,19 +14,14 @@ # 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 - from sys import exit from vyos.config import Config -from vyos.configdict import node_changed from vyos.utils.process import call from vyos import ConfigError from vyos import airbag - airbag.enable() - def get_config(config=None): if config: conf = config @@ -38,9 +33,7 @@ def get_config(config=None): return config - def verify(config): - if 'arp' in config: for neighbor, neighbor_conf in config['arp'].items(): if 'interface' not in neighbor_conf: @@ -55,11 +48,9 @@ def verify(config): f"ARP neighbor-proxy for '{neighbor}' requires an interface to be set!" ) - def generate(config): pass - def apply(config): if not config: # Cleanup proxy @@ -83,7 +74,6 @@ def apply(config): for interface in neighbor_conf['interface']: call(f'ip -6 neighbor add proxy {neighbor} dev {interface}') - if __name__ == '__main__': try: c = get_config() diff --git a/src/conf_mode/qos.py b/src/conf_mode/qos.py index 4a0b4d0c5..3dfb4bab8 100755 --- a/src/conf_mode/qos.py +++ b/src/conf_mode/qos.py @@ -14,15 +14,14 @@ # 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 - from sys import exit from netifaces import interfaces -from vyos.base import Warning from vyos.config import Config -from vyos.configdep import set_dependents, call_dependents +from vyos.configdep import set_dependents +from vyos.configdep import call_dependents from vyos.configdict import dict_merge +from vyos.configverify import verify_interface_exists from vyos.ifconfig import Section from vyos.qos import CAKE from vyos.qos import DropTail @@ -36,8 +35,8 @@ from vyos.qos import RateLimiter from vyos.qos import RoundRobin from vyos.qos import TrafficShaper from vyos.qos import TrafficShaperHFSC -from vyos.utils.process import run from vyos.utils.dict import dict_search_recursive +from vyos.utils.process import run from vyos import ConfigError from vyos import airbag airbag.enable() @@ -214,11 +213,10 @@ def apply(qos): return None for interface, interface_config in qos['interface'].items(): - if not os.path.exists(f'/sys/class/net/{interface}'): + if not verify_interface_exists(interface, warning_only=True): # When shaper is bound to a dialup (e.g. PPPoE) interface it is # possible that it is yet not availbale when to QoS code runs. - # Skip the configuration and inform the user - Warning(f'Interface "{interface}" does not exist!') + # Skip the configuration and inform the user via warning_only=True continue for direction in ['egress', 'ingress']: diff --git a/src/conf_mode/service_dhcp-server.py b/src/conf_mode/service_dhcp-server.py index ba3d69b07..f4fb78f57 100755 --- a/src/conf_mode/service_dhcp-server.py +++ b/src/conf_mode/service_dhcp-server.py @@ -143,8 +143,12 @@ def get_config(config=None): dhcp['shared_network_name'][network]['subnet'][subnet].update( {'range' : new_range_dict}) - if dict_search('high_availability.certificate', dhcp): - dhcp['pki'] = conf.get_config_dict(['pki'], key_mangling=('-', '_'), get_first_key=True, no_tag_node_value_mangle=True) + if len(dhcp['high_availability']) == 1: + ## only default value for mode is set, need to remove ha node + del dhcp['high_availability'] + else: + if dict_search('high_availability.certificate', dhcp): + dhcp['pki'] = conf.get_config_dict(['pki'], key_mangling=('-', '_'), get_first_key=True, no_tag_node_value_mangle=True) return dhcp @@ -316,7 +320,7 @@ def verify(dhcp): raise ConfigError(f'Invalid CA certificate specified for DHCP high-availability') for address in (dict_search('listen_address', dhcp) or []): - if is_addr_assigned(address): + if is_addr_assigned(address, include_vrf=True): listen_ok = True # no need to probe further networks, we have one that is valid continue diff --git a/src/conf_mode/service_dns_forwarding.py b/src/conf_mode/service_dns_forwarding.py index ecad765f4..7e863073a 100755 --- a/src/conf_mode/service_dns_forwarding.py +++ b/src/conf_mode/service_dns_forwarding.py @@ -16,7 +16,6 @@ import os -from netifaces import interfaces from sys import exit from glob import glob @@ -24,9 +23,9 @@ from vyos.config import Config from vyos.hostsd_client import Client as hostsd_client from vyos.template import render from vyos.template import bracketize_ipv6 +from vyos.utils.network import interface_exists from vyos.utils.process import call from vyos.utils.permission import chown - from vyos import ConfigError from vyos import airbag airbag.enable() @@ -330,7 +329,7 @@ def apply(dns): # names (DHCP) to use DNS servers. We need to check if the # value is an interface name - only if this is the case, add the # interface based DNS forwarder. - if interface in interfaces(): + if interface_exists(interface): hc.add_name_server_tags_recursor(['dhcp-' + interface, 'dhcpv6-' + interface ]) diff --git a/src/conf_mode/service_https.py b/src/conf_mode/service_https.py index 46efc3c93..9e58b4c72 100755 --- a/src/conf_mode/service_https.py +++ b/src/conf_mode/service_https.py @@ -24,13 +24,14 @@ from time import sleep from vyos.base import Warning from vyos.config import Config from vyos.config import config_dict_merge -from vyos.configdiff import get_config_diff from vyos.configverify import verify_vrf +from vyos.configverify import verify_pki_certificate +from vyos.configverify import verify_pki_ca_certificate +from vyos.configverify import verify_pki_dh_parameters from vyos.defaults import api_config_state from vyos.pki import wrap_certificate from vyos.pki import wrap_private_key from vyos.pki import wrap_dh_parameters -from vyos.pki import load_dh_parameters from vyos.template import render from vyos.utils.dict import dict_search from vyos.utils.process import call @@ -84,33 +85,14 @@ def verify(https): if https is None: return None - if 'certificates' in https and 'certificate' in https['certificates']: - cert_name = https['certificates']['certificate'] - if 'pki' not in https: - raise ConfigError('PKI is not configured!') - - if cert_name not in https['pki']['certificate']: - raise ConfigError('Invalid certificate in configuration!') + if dict_search('certificates.certificate', https) != None: + verify_pki_certificate(https, https['certificates']['certificate']) - pki_cert = https['pki']['certificate'][cert_name] - - if 'certificate' not in pki_cert: - raise ConfigError('Missing certificate in configuration!') + tmp = dict_search('certificates.ca_certificate', https) + if tmp != None: verify_pki_ca_certificate(https, tmp) - if 'private' not in pki_cert or 'key' not in pki_cert['private']: - raise ConfigError('Missing certificate private key in configuration!') - - if 'dh_params' in https['certificates']: - dh_name = https['certificates']['dh_params'] - if dh_name not in https['pki']['dh']: - raise ConfigError('Invalid DH parameter in configuration!') - - pki_dh = https['pki']['dh'][dh_name] - dh_params = load_dh_parameters(pki_dh['parameters']) - dh_numbers = dh_params.parameter_numbers() - dh_bits = dh_numbers.p.bit_length() - if dh_bits < 2048: - raise ConfigError(f'Minimum DH key-size is 2048 bits') + tmp = dict_search('certificates.dh_params', https) + if tmp != None: verify_pki_dh_parameters(https, tmp, 2048) else: Warning('No certificate specified, using build-in self-signed certificates. '\ @@ -214,7 +196,8 @@ def apply(https): https_service_name = 'nginx.service' if https is None: - call(f'systemctl stop {http_api_service_name}') + if is_systemd_service_active(http_api_service_name): + call(f'systemctl stop {http_api_service_name}') call(f'systemctl stop {https_service_name}') return diff --git a/src/conf_mode/service_lldp.py b/src/conf_mode/service_lldp.py index 3c647a0e8..04b1db880 100755 --- a/src/conf_mode/service_lldp.py +++ b/src/conf_mode/service_lldp.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2017-2022 VyOS maintainers and contributors +# Copyright (C) 2017-2024 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 @@ -24,7 +24,6 @@ from vyos.utils.network import is_addr_assigned from vyos.utils.network import is_loopback_addr from vyos.version import get_version_data from vyos.utils.process import call -from vyos.utils.dict import dict_search from vyos.template import render from vyos import ConfigError from vyos import airbag diff --git a/src/conf_mode/service_mdns_repeater.py b/src/conf_mode/service_mdns_repeater.py index 6526c23d1..207da5e03 100755 --- a/src/conf_mode/service_mdns_repeater.py +++ b/src/conf_mode/service_mdns_repeater.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2017-2022 VyOS maintainers and contributors +# Copyright (C) 2017-2024 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 @@ -18,9 +18,10 @@ import os from json import loads from sys import exit -from netifaces import ifaddresses, interfaces, AF_INET, AF_INET6 +from netifaces import ifaddresses, AF_INET, AF_INET6 from vyos.config import Config +from vyos.configverify import verify_interface_exists from vyos.ifconfig.vrrp import VRRP from vyos.template import render from vyos.utils.process import call @@ -64,8 +65,7 @@ def verify(mdns): # For mdns-repeater to work it is essential that the interfaces has # an IPv4 address assigned for interface in mdns['interface']: - if interface not in interfaces(): - raise ConfigError(f'Interface "{interface}" does not exist!') + verify_interface_exists(interface) if mdns['ip_version'] in ['ipv4', 'both'] and AF_INET not in ifaddresses(interface): raise ConfigError('mDNS repeater requires an IPv4 address to be ' diff --git a/src/conf_mode/service_ssh.py b/src/conf_mode/service_ssh.py index ee5e1eca2..9abdd33dc 100755 --- a/src/conf_mode/service_ssh.py +++ b/src/conf_mode/service_ssh.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2018-2022 VyOS maintainers and contributors +# Copyright (C) 2018-2024 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 @@ -30,7 +30,6 @@ from vyos import airbag airbag.enable() config_file = r'/run/sshd/sshd_config' -systemd_override = r'/run/systemd/system/ssh.service.d/override.conf' sshguard_config_file = '/etc/sshguard/sshguard.conf' sshguard_whitelist = '/etc/sshguard/whitelist' @@ -81,8 +80,6 @@ def generate(ssh): if not ssh: if os.path.isfile(config_file): os.unlink(config_file) - if os.path.isfile(systemd_override): - os.unlink(systemd_override) return None @@ -99,13 +96,10 @@ def generate(ssh): call(f'ssh-keygen -q -N "" -t ed25519 -f {key_ed25519}') 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') return None @@ -114,7 +108,7 @@ def apply(ssh): systemd_service_sshguard = 'sshguard.service' if not ssh: # SSH access is removed in the commit - call(f'systemctl stop {systemd_service_ssh}') + call(f'systemctl stop ssh@*.service') call(f'systemctl stop {systemd_service_sshguard}') return None @@ -126,9 +120,13 @@ def apply(ssh): # we need to restart the service if e.g. the VRF name changed systemd_action = 'reload-or-restart' if 'restart_required' in ssh: + # this is only true if something for the VRFs changed, thus we + # stop all VRF services and only restart then new ones + call(f'systemctl stop ssh@*.service') systemd_action = 'restart' - call(f'systemctl {systemd_action} {systemd_service_ssh}') + for vrf in ssh['vrf']: + call(f'systemctl {systemd_action} ssh@{vrf}.service') return None if __name__ == '__main__': diff --git a/src/conf_mode/service_tftp-server.py b/src/conf_mode/service_tftp-server.py index 3ad346e2e..5b7303c40 100755 --- a/src/conf_mode/service_tftp-server.py +++ b/src/conf_mode/service_tftp-server.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2018-2020 VyOS maintainers and contributors +# Copyright (C) 2018-2024 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 @@ -15,7 +15,6 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. import os -import stat import pwd from copy import deepcopy diff --git a/src/conf_mode/service_upnp.py b/src/conf_mode/service_upnp.py index cf26bf9ce..0df8dc09e 100755 --- a/src/conf_mode/service_upnp.py +++ b/src/conf_mode/service_upnp.py @@ -54,9 +54,7 @@ def get_config(config=None): def get_all_interface_addr(prefix, filter_dev, filter_family): list_addr = [] - interfaces = netifaces.interfaces() - - for interface in interfaces: + for interface in netifaces.interfaces(): if filter_dev and interface in filter_dev: continue addrs = netifaces.ifaddresses(interface) diff --git a/src/conf_mode/system_conntrack.py b/src/conf_mode/system_conntrack.py index 3d42389f6..031fe63b0 100755 --- a/src/conf_mode/system_conntrack.py +++ b/src/conf_mode/system_conntrack.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2021-2023 VyOS maintainers and contributors +# Copyright (C) 2021-2024 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 @@ -15,19 +15,16 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. import os -import re from sys import exit from vyos.config import Config from vyos.configdep import set_dependents, call_dependents -from vyos.utils.process import process_named_running from vyos.utils.dict import dict_search from vyos.utils.dict import dict_search_args from vyos.utils.dict import dict_search_recursive from vyos.utils.process import cmd from vyos.utils.process import rc_cmd -from vyos.utils.process import run from vyos.template import render from vyos import ConfigError from vyos import airbag @@ -223,7 +220,7 @@ def apply(conntrack): cmd(f'modprobe -a {module_str}') # Load new nftables ruleset - install_result, output = rc_cmd(f'nft -f {nftables_ct_file}') + install_result, output = rc_cmd(f'nft --file {nftables_ct_file}') if install_result == 1: raise ConfigError(f'Failed to apply configuration: {output}') diff --git a/src/conf_mode/system_console.py b/src/conf_mode/system_console.py index a888b125e..19bbb8875 100755 --- a/src/conf_mode/system_console.py +++ b/src/conf_mode/system_console.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020-2023 VyOS maintainers and contributors +# Copyright (C) 2020-2024 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 @@ -15,13 +15,10 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. import os -import re from pathlib import Path from vyos.config import Config from vyos.utils.process import call -from vyos.utils.file import read_file -from vyos.utils.file import write_file from vyos.system import grub_util from vyos.template import render from vyos import ConfigError diff --git a/src/conf_mode/system_flow-accounting.py b/src/conf_mode/system_flow-accounting.py index 206f513c8..2dacd92da 100755 --- a/src/conf_mode/system_flow-accounting.py +++ b/src/conf_mode/system_flow-accounting.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2018-2023 VyOS maintainers and contributors +# Copyright (C) 2018-2024 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 @@ -20,11 +20,10 @@ import re from sys import exit from ipaddress import ip_address -from vyos.base import Warning from vyos.config import Config from vyos.config import config_dict_merge from vyos.configverify import verify_vrf -from vyos.ifconfig import Section +from vyos.configverify import verify_interface_exists from vyos.template import render from vyos.utils.process import call from vyos.utils.process import cmd @@ -184,10 +183,7 @@ def verify(flow_config): # check that all configured interfaces exists in the system for interface in flow_config['interface']: - if interface not in Section.interfaces(): - # Changed from error to warning to allow adding dynamic interfaces - # and interface templates - Warning(f'Interface "{interface}" is not presented in the system') + verify_interface_exists(interface, warning_only=True) # check sFlow configuration if 'sflow' in flow_config: diff --git a/src/conf_mode/system_frr.py b/src/conf_mode/system_frr.py index 07f291000..d9ac543d0 100755 --- a/src/conf_mode/system_frr.py +++ b/src/conf_mode/system_frr.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2021-2023 VyOS maintainers and contributors +# Copyright (C) 2021-2024 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,7 +14,6 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -from pathlib import Path from sys import exit from vyos import ConfigError diff --git a/src/conf_mode/system_host-name.py b/src/conf_mode/system_host-name.py index 6204cf247..8975cadb6 100755 --- a/src/conf_mode/system_host-name.py +++ b/src/conf_mode/system_host-name.py @@ -71,9 +71,9 @@ def get_config(config=None): hosts['nameserver'].append(ns) else: tmp = '' - if_type = Section.section(ns) - if conf.exists(['interfaces', if_type, ns, 'address']): - tmp = conf.return_values(['interfaces', if_type, ns, 'address']) + config_path = Section.get_config_path(ns) + if conf.exists(['interfaces', config_path, 'address']): + tmp = conf.return_values(['interfaces', config_path, 'address']) hosts['nameservers_dhcp_interfaces'].update({ ns : tmp }) diff --git a/src/conf_mode/system_ip.py b/src/conf_mode/system_ip.py index 833f89554..b945b51f2 100755 --- a/src/conf_mode/system_ip.py +++ b/src/conf_mode/system_ip.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2019-2023 VyOS maintainers and contributors +# Copyright (C) 2019-2024 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 @@ -22,7 +22,6 @@ from vyos.configverify import verify_route_map from vyos.template import render_to_string from vyos.utils.dict import dict_search from vyos.utils.file import write_file -from vyos.utils.process import call from vyos.utils.process import is_systemd_service_active from vyos.utils.system import sysctl_write diff --git a/src/conf_mode/system_option.py b/src/conf_mode/system_option.py index 7ed451e16..a2e5db575 100755 --- a/src/conf_mode/system_option.py +++ b/src/conf_mode/system_option.py @@ -16,12 +16,12 @@ import os -from netifaces import interfaces from sys import exit from time import sleep from vyos.config import Config from vyos.configverify import verify_source_interface +from vyos.configverify import verify_interface_exists from vyos.system import grub_util from vyos.template import render from vyos.utils.process import cmd @@ -56,9 +56,7 @@ def verify(options): if 'http_client' in options: config = options['http_client'] if 'source_interface' in config: - if not config['source_interface'] in interfaces(): - raise ConfigError(f'Source interface {source_interface} does not ' - f'exist'.format(**config)) + verify_interface_exists(config['source_interface']) if {'source_address', 'source_interface'} <= set(config): raise ConfigError('Can not define both HTTP source-interface and source-address') diff --git a/src/conf_mode/system_timezone.py b/src/conf_mode/system_timezone.py index cd3d4b229..39770fdb4 100755 --- a/src/conf_mode/system_timezone.py +++ b/src/conf_mode/system_timezone.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2019 VyOS maintainers and contributors +# Copyright (C) 2019-2024 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 @@ -15,7 +15,6 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. import sys -import os from copy import deepcopy from vyos.config import Config diff --git a/src/conf_mode/system_update-check.py b/src/conf_mode/system_update-check.py index 8d641a97d..71ac13e51 100755 --- a/src/conf_mode/system_update-check.py +++ b/src/conf_mode/system_update-check.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2022 VyOS maintainers and contributors +# Copyright (C) 2022-2024 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,9 +14,7 @@ # 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 json -import jmespath from pathlib import Path from sys import exit @@ -27,7 +25,6 @@ from vyos import ConfigError from vyos import airbag airbag.enable() - base = ['system', 'update-check'] service_name = 'vyos-system-update' service_conf = Path(f'/run/{service_name}.conf') diff --git a/src/conf_mode/vpn_ipsec.py b/src/conf_mode/vpn_ipsec.py index 0c2f232df..dc78c755e 100755 --- a/src/conf_mode/vpn_ipsec.py +++ b/src/conf_mode/vpn_ipsec.py @@ -21,7 +21,6 @@ import jmespath from sys import exit from time import sleep -from time import time from vyos.base import Warning from vyos.config import Config @@ -47,7 +46,6 @@ from vyos.utils.network import interface_exists from vyos.utils.dict import dict_search from vyos.utils.dict import dict_search_args from vyos.utils.process import call -from vyos.utils.process import run from vyos import ConfigError from vyos import airbag airbag.enable() @@ -168,9 +166,7 @@ def verify(ipsec): for interface in ipsec['interface']: # exclude check interface for dynamic interfaces if tmp.match(interface): - if not interface_exists(interface): - Warning(f'Interface "{interface}" does not exist yet and cannot be used ' - f'for IPsec until it is up!') + verify_interface_exists(interface, warning_only=True) else: verify_interface_exists(interface) diff --git a/src/conf_mode/vpn_openconnect.py b/src/conf_mode/vpn_openconnect.py index 08e4fc6db..8159fedea 100755 --- a/src/conf_mode/vpn_openconnect.py +++ b/src/conf_mode/vpn_openconnect.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2018-2023 VyOS maintainers and contributors +# Copyright (C) 2018-2024 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,6 +19,8 @@ from sys import exit from vyos.base import Warning from vyos.config import Config +from vyos.configverify import verify_pki_certificate +from vyos.configverify import verify_pki_ca_certificate from vyos.pki import wrap_certificate from vyos.pki import wrap_private_key from vyos.template import render @@ -75,7 +77,7 @@ def verify(ocserv): if "accounting" in ocserv: if "mode" in ocserv["accounting"] and "radius" in ocserv["accounting"]["mode"]: if not origin["accounting"]['radius']['server']: - raise ConfigError('Openconnect accounting mode radius requires at least one RADIUS server') + raise ConfigError('OpenConnect accounting mode radius requires at least one RADIUS server') if "authentication" not in ocserv or "mode" not in ocserv["authentication"]: raise ConfigError('Accounting depends on OpenConnect authentication configuration') elif "radius" not in ocserv["authentication"]["mode"]: @@ -89,12 +91,12 @@ def verify(ocserv): raise ConfigError('OpenConnect authentication modes are mutually-exclusive, remove either local or radius from your configuration') if "radius" in ocserv["authentication"]["mode"]: if not ocserv["authentication"]['radius']['server']: - raise ConfigError('Openconnect authentication mode radius requires at least one RADIUS server') + raise ConfigError('OpenConnect authentication mode radius requires at least one RADIUS server') if "local" in ocserv["authentication"]["mode"]: if not ocserv.get("authentication", {}).get("local_users"): - raise ConfigError('openconnect mode local required at least one user') + raise ConfigError('OpenConnect mode local required at least one user') if not ocserv["authentication"]["local_users"]["username"]: - raise ConfigError('openconnect mode local required at least one user') + raise ConfigError('OpenConnect mode local required at least one user') else: # For OTP mode: verify that each local user has an OTP key if "otp" in ocserv["authentication"]["mode"]["local"]: @@ -127,40 +129,20 @@ def verify(ocserv): if 'default_config' not in ocserv["authentication"]["identity_based_config"]: raise ConfigError('OpenConnect identity-based-config enabled but default-config not set') else: - raise ConfigError('openconnect authentication mode required') + raise ConfigError('OpenConnect authentication mode required') else: - raise ConfigError('openconnect authentication credentials required') + raise ConfigError('OpenConnect authentication credentials required') # Check ssl if 'ssl' not in ocserv: - raise ConfigError('openconnect ssl required') + raise ConfigError('SSL missing on OpenConnect config!') - if not ocserv['pki'] or 'certificate' not in ocserv['pki']: - raise ConfigError('PKI not configured') + if 'certificate' not in ocserv['ssl']: + raise ConfigError('SSL certificate missing on OpenConnect config!') + verify_pki_certificate(ocserv, ocserv['ssl']['certificate']) - ssl = ocserv['ssl'] - if 'certificate' not in ssl: - raise ConfigError('openconnect ssl certificate required') - - cert_name = ssl['certificate'] - - if cert_name not in ocserv['pki']['certificate']: - raise ConfigError('Invalid openconnect ssl certificate') - - cert = ocserv['pki']['certificate'][cert_name] - - if 'certificate' not in cert: - raise ConfigError('Missing certificate in PKI') - - if 'private' not in cert or 'key' not in cert['private']: - raise ConfigError('Missing private key in PKI') - - if 'ca_certificate' in ssl: - if 'ca' not in ocserv['pki']: - raise ConfigError('PKI not configured') - - if ssl['ca_certificate'] not in ocserv['pki']['ca']: - raise ConfigError('Invalid openconnect ssl CA certificate') + if 'ca_certificate' in ocserv['ssl']: + verify_pki_ca_certificate(ocserv, ocserv['ssl']['ca_certificate']) # Check network settings if "network_settings" in ocserv: @@ -172,7 +154,7 @@ def verify(ocserv): else: ocserv["network_settings"]["push_route"] = ["default"] else: - raise ConfigError('openconnect network settings required') + raise ConfigError('OpenConnect network settings required!') def generate(ocserv): if not ocserv: @@ -276,7 +258,7 @@ def apply(ocserv): break sleep(0.250) if counter > 5: - raise ConfigError('openconnect failed to start, check the logs for details') + raise ConfigError('OpenConnect failed to start, check the logs for details') break counter += 1 diff --git a/src/conf_mode/vpn_sstp.py b/src/conf_mode/vpn_sstp.py index 8661a8aff..7490fd0e0 100755 --- a/src/conf_mode/vpn_sstp.py +++ b/src/conf_mode/vpn_sstp.py @@ -20,6 +20,8 @@ from sys import exit from vyos.config import Config from vyos.configdict import get_accel_dict +from vyos.configverify import verify_pki_certificate +from vyos.configverify import verify_pki_ca_certificate from vyos.pki import wrap_certificate from vyos.pki import wrap_private_key from vyos.template import render @@ -46,51 +48,6 @@ cert_key_path = os.path.join(cfg_dir, 'sstp-cert.key') ca_cert_file_path = os.path.join(cfg_dir, 'sstp-ca.pem') -def verify_certificate(config): - # - # SSL certificate checks - # - if not config['pki']: - raise ConfigError('PKI is not configured') - - if 'ssl' not in config: - raise ConfigError('SSL missing on SSTP config') - - ssl = config['ssl'] - - # CA - if 'ca_certificate' not in ssl: - raise ConfigError('SSL CA certificate missing on SSTP config') - - ca_name = ssl['ca_certificate'] - - if ca_name not in config['pki']['ca']: - raise ConfigError('Invalid CA certificate on SSTP config') - - if 'certificate' not in config['pki']['ca'][ca_name]: - raise ConfigError('Missing certificate data for CA certificate on SSTP config') - - # Certificate - if 'certificate' not in ssl: - raise ConfigError('SSL certificate missing on SSTP config') - - cert_name = ssl['certificate'] - - if cert_name not in config['pki']['certificate']: - raise ConfigError('Invalid certificate on SSTP config') - - pki_cert = config['pki']['certificate'][cert_name] - - if 'certificate' not in pki_cert: - raise ConfigError('Missing certificate data for certificate on SSTP config') - - if 'private' not in pki_cert or 'key' not in pki_cert['private']: - raise ConfigError('Missing private key for certificate on SSTP config') - - if 'password_protected' in pki_cert['private']: - raise ConfigError('Encrypted private key is not supported on SSTP config') - - def get_config(config=None): if config: conf = config @@ -124,7 +81,17 @@ def verify(sstp): verify_accel_ppp_ip_pool(sstp) verify_accel_ppp_name_servers(sstp) verify_accel_ppp_wins_servers(sstp) - verify_certificate(sstp) + + if 'ssl' not in sstp: + raise ConfigError('SSL missing on SSTP config!') + + if 'certificate' not in sstp['ssl']: + raise ConfigError('SSL certificate missing on SSTP config!') + verify_pki_certificate(sstp, sstp['ssl']['certificate']) + + if 'ca_certificate' not in sstp['ssl']: + raise ConfigError('SSL CA certificate missing on SSTP config!') + verify_pki_ca_certificate(sstp, sstp['ssl']['ca_certificate']) def generate(sstp): @@ -154,15 +121,15 @@ def generate(sstp): def apply(sstp): + systemd_service = 'accel-ppp@sstp.service' if not sstp: - call('systemctl stop accel-ppp@sstp.service') + call(f'systemctl stop {systemd_service}') for file in [sstp_chap_secrets, sstp_conf]: if os.path.exists(file): os.unlink(file) - return None - call('systemctl restart accel-ppp@sstp.service') + call(f'systemctl reload-or-restart {systemd_service}') if __name__ == '__main__': diff --git a/src/conf_mode/vrf.py b/src/conf_mode/vrf.py index 16908100f..1fc813189 100755 --- a/src/conf_mode/vrf.py +++ b/src/conf_mode/vrf.py @@ -14,8 +14,6 @@ # 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 - from sys import exit from json import loads @@ -33,6 +31,7 @@ from vyos.utils.network import get_vrf_members from vyos.utils.network import interface_exists from vyos.utils.process import call from vyos.utils.process import cmd +from vyos.utils.process import popen from vyos.utils.system import sysctl_write from vyos import ConfigError from vyos import frr @@ -227,7 +226,11 @@ def apply(vrf): # Remove nftables conntrack zone map item nft_del_element = f'delete element inet vrf_zones ct_iface_map {{ "{tmp}" }}' - cmd(f'nft {nft_del_element}') + # Check if deleting is possible first to avoid raising errors + _, err = popen(f'nft --check {nft_del_element}') + if not err: + # Remove map element + cmd(f'nft {nft_del_element}') # Delete the VRF Kernel interface call(f'ip link delete dev {tmp}') @@ -307,7 +310,7 @@ def apply(vrf): if vrf['conntrack']: for chain, rule in nftables_rules.items(): cmd(f'nft add rule inet vrf_zones {chain} {rule}') - + if 'name' not in vrf or not vrf['conntrack']: for chain, rule in nftables_rules.items(): cmd(f'nft flush chain inet vrf_zones {chain}') diff --git a/src/conf_mode/vrf_vni.py b/src/conf_mode/vrf_vni.py index 23b341079..8dab164d7 100644 --- a/src/conf_mode/vrf_vni.py +++ b/src/conf_mode/vrf_vni.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2023 VyOS maintainers and contributors +# Copyright (C) 2023-2024 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,7 +19,6 @@ from sys import exit from vyos.config import Config from vyos.template import render_to_string -from vyos.utils.dict import dict_search from vyos import ConfigError from vyos import frr from vyos import airbag |