diff options
Diffstat (limited to 'src/conf_mode')
-rwxr-xr-x | src/conf_mode/firewall.py | 9 | ||||
-rwxr-xr-x | src/conf_mode/interfaces_ethernet.py | 20 | ||||
-rwxr-xr-x | src/conf_mode/load-balancing_reverse-proxy.py | 38 | ||||
-rwxr-xr-x | src/conf_mode/nat.py | 8 | ||||
-rwxr-xr-x | src/conf_mode/nat66.py | 6 | ||||
-rwxr-xr-x | src/conf_mode/policy_route.py | 4 | ||||
-rwxr-xr-x | src/conf_mode/protocols_nhrp.py | 4 | ||||
-rwxr-xr-x | src/conf_mode/service_https.py | 39 | ||||
-rwxr-xr-x | src/conf_mode/system_conntrack.py | 7 | ||||
-rwxr-xr-x | src/conf_mode/vpn_openconnect.py | 52 | ||||
-rwxr-xr-x | src/conf_mode/vpn_sstp.py | 65 | ||||
-rwxr-xr-x | src/conf_mode/vrf.py | 11 |
12 files changed, 92 insertions, 171 deletions
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_ethernet.py b/src/conf_mode/interfaces_ethernet.py index 2c0f846c3..504d48f89 100755 --- a/src/conf_mode/interfaces_ethernet.py +++ b/src/conf_mode/interfaces_ethernet.py @@ -15,7 +15,6 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. import os -import pprint from glob import glob from sys import exit @@ -26,7 +25,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 +32,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 +263,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/load-balancing_reverse-proxy.py b/src/conf_mode/load-balancing_reverse-proxy.py index 2a0acd84a..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,30 +56,6 @@ def get_config(config=None): return lb - -def _verify_cert(lb: dict, config: dict) -> None: - if 'ca_certificate' in config['ssl']: - ca_name = config['ssl']['ca_certificate'] - pki_ca = lb['pki'].get('ca') - if pki_ca is None: - raise ConfigError(f'CA certificates does not exist in PKI') - else: - ca = pki_ca.get(ca_name) - if ca is None: - raise ConfigError(f'CA certificate "{ca_name}" does not exist') - - elif 'certificate' in config['ssl']: - cert_names = config['ssl']['certificate'] - pki_certs = lb['pki'].get('certificate') - if pki_certs is None: - raise ConfigError(f'Certificates does not exist in PKI') - - for cert_name in cert_names: - pki_cert = pki_certs.get(cert_name) - if pki_cert is None: - raise ConfigError(f'Certificate "{cert_name}" does not exist') - - def verify(lb): if not lb: return None @@ -107,12 +85,12 @@ def verify(lb): raise ConfigError(f'Cannot use both "send-proxy" and "send-proxy-v2" for server "{bk_server}"') for front, front_config in lb['service'].items(): - if 'ssl' in front_config: - _verify_cert(lb, front_config) + for cert in dict_search('ssl.certificate', front_config) or []: + verify_pki_certificate(lb, cert) for back, back_config in lb['backend'].items(): - if 'ssl' in back_config: - _verify_cert(lb, back_config) + tmp = dict_search('ssl.ca_certificate', front_config) + if tmp: verify_pki_ca_certificate(lb, tmp) def generate(lb): diff --git a/src/conf_mode/nat.py b/src/conf_mode/nat.py index b3f38c04a..76c07a9ec 100755 --- a/src/conf_mode/nat.py +++ b/src/conf_mode/nat.py @@ -223,19 +223,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/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/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_nhrp.py b/src/conf_mode/protocols_nhrp.py index c339c6391..9f66407f2 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 @@ -93,7 +93,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/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/system_conntrack.py b/src/conf_mode/system_conntrack.py index 4bea31bbb..56d798e43 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 @@ -218,7 +215,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/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}') |