diff options
Diffstat (limited to 'src')
-rwxr-xr-x | src/conf_mode/firewall.py | 26 | ||||
-rwxr-xr-x | src/conf_mode/interfaces_bridge.py | 2 | ||||
-rwxr-xr-x | src/conf_mode/interfaces_pseudo-ethernet.py | 2 | ||||
-rwxr-xr-x | src/conf_mode/interfaces_virtual-ethernet.py | 2 | ||||
-rwxr-xr-x | src/conf_mode/interfaces_vti.py | 2 | ||||
-rwxr-xr-x | src/conf_mode/interfaces_wwan.py | 2 | ||||
-rwxr-xr-x | src/conf_mode/policy_route.py | 47 | ||||
-rwxr-xr-x | src/conf_mode/service_dhcp-server.py | 34 | ||||
-rwxr-xr-x | src/conf_mode/system_host-name.py | 2 | ||||
-rwxr-xr-x | src/conf_mode/system_option.py | 4 | ||||
-rwxr-xr-x | src/conf_mode/system_syslog.py | 2 | ||||
-rw-r--r-- | src/etc/dhcp/dhclient-enter-hooks.d/06-vyos-nodefaultroute | 20 | ||||
-rw-r--r-- | src/etc/sysctl.d/30-vyos-router.conf | 10 | ||||
-rw-r--r-- | src/etc/systemd/system/kea-dhcp-ddns-server.service.d/override.conf | 7 | ||||
-rwxr-xr-x | src/helpers/geoip-update.py | 17 | ||||
-rwxr-xr-x | src/init/vyos-router | 8 | ||||
-rwxr-xr-x | src/op_mode/firewall.py | 3 | ||||
-rwxr-xr-x | src/op_mode/image_installer.py | 4 | ||||
-rw-r--r-- | src/op_mode/tech_support.py | 10 | ||||
-rw-r--r-- | src/systemd/vyos.target | 2 |
20 files changed, 183 insertions, 23 deletions
diff --git a/src/conf_mode/firewall.py b/src/conf_mode/firewall.py index cebe57092..274ca2ce6 100755 --- a/src/conf_mode/firewall.py +++ b/src/conf_mode/firewall.py @@ -205,7 +205,7 @@ def verify_rule(firewall, family, hook, priority, rule_id, rule_conf): if 'jump' not in rule_conf['action']: raise ConfigError('jump-target defined, but action jump needed and it is not defined') target = rule_conf['jump_target'] - if hook != 'name': # This is a bit clumsy, but consolidates a chunk of code. + if hook != 'name': # This is a bit clumsy, but consolidates a chunk of code. verify_jump_target(firewall, hook, target, family, recursive=True) else: verify_jump_target(firewall, hook, target, family, recursive=False) @@ -268,12 +268,12 @@ def verify_rule(firewall, family, hook, priority, rule_id, rule_conf): if dict_search_args(rule_conf, 'gre', 'flags', 'checksum') is None: # There is no builtin match in nftables for the GRE key, so we need to do a raw lookup. - # The offset of the key within the packet shifts depending on the C-flag. - # 99% of the time, nobody will have checksums enabled - it's usually a manual config option. - # We can either assume it is unset unless otherwise directed + # The offset of the key within the packet shifts depending on the C-flag. + # 99% of the time, nobody will have checksums enabled - it's usually a manual config option. + # We can either assume it is unset unless otherwise directed # (confusing, requires doco to explain why it doesn't work sometimes) - # or, demand an explicit selection to be made for this specific match rule. - # This check enforces the latter. The user is free to create rules for both cases. + # or, demand an explicit selection to be made for this specific match rule. + # This check enforces the latter. The user is free to create rules for both cases. raise ConfigError('Matching GRE tunnel key requires an explicit checksum flag match. For most cases, use "gre flags checksum unset"') if dict_search_args(rule_conf, 'gre', 'flags', 'key', 'unset') is not None: @@ -286,7 +286,7 @@ def verify_rule(firewall, family, hook, priority, rule_id, rule_conf): if gre_inner_value < 0 or gre_inner_value > 65535: raise ConfigError('inner-proto outside valid ethertype range 0-65535') except ValueError: - pass # Symbolic constant, pre-validated before reaching here. + pass # Symbolic constant, pre-validated before reaching here. tcp_flags = dict_search_args(rule_conf, 'tcp', 'flags') if tcp_flags: @@ -437,6 +437,16 @@ def verify(firewall): for ifname in interfaces: verify_hardware_offload(ifname) + if 'offload' in firewall.get('global_options', {}).get('state_policy', {}): + offload_path = firewall['global_options']['state_policy']['offload'] + if 'offload_target' not in offload_path: + raise ConfigError('offload-target must be specified') + + offload_target = offload_path['offload_target'] + + if not dict_search_args(firewall, 'flowtable', offload_target): + raise ConfigError(f'Invalid offload-target. Flowtable "{offload_target}" does not exist on the system') + if 'group' in firewall: for group_type in nested_group_types: if group_type in firewall['group']: @@ -627,7 +637,7 @@ def apply(firewall): # Call helper script to Update set contents if 'name' in firewall['geoip_updated'] or 'ipv6_name' in firewall['geoip_updated']: print('Updating GeoIP. Please wait...') - geoip_update(firewall) + geoip_update(firewall=firewall) return None diff --git a/src/conf_mode/interfaces_bridge.py b/src/conf_mode/interfaces_bridge.py index aff93af2a..95dcc543e 100755 --- a/src/conf_mode/interfaces_bridge.py +++ b/src/conf_mode/interfaces_bridge.py @@ -25,6 +25,7 @@ from vyos.configdict import has_vlan_subinterface_configured from vyos.configverify import verify_dhcpv6 from vyos.configverify import verify_mirror_redirect from vyos.configverify import verify_vrf +from vyos.configverify import verify_mtu_ipv6 from vyos.ifconfig import BridgeIf from vyos.configdict import has_address_configured from vyos.configdict import has_vrf_configured @@ -136,6 +137,7 @@ def verify(bridge): verify_dhcpv6(bridge) verify_vrf(bridge) + verify_mtu_ipv6(bridge) verify_mirror_redirect(bridge) ifname = bridge['ifname'] diff --git a/src/conf_mode/interfaces_pseudo-ethernet.py b/src/conf_mode/interfaces_pseudo-ethernet.py index 446beffd3..b066fd542 100755 --- a/src/conf_mode/interfaces_pseudo-ethernet.py +++ b/src/conf_mode/interfaces_pseudo-ethernet.py @@ -27,6 +27,7 @@ from vyos.configverify import verify_bridge_delete 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_mtu_ipv6 from vyos.configverify import verify_mirror_redirect from vyos.ifconfig import MACVLANIf from vyos.utils.network import interface_exists @@ -71,6 +72,7 @@ def verify(peth): verify_vrf(peth) verify_address(peth) verify_mtu_parent(peth, peth['parent']) + verify_mtu_ipv6(peth) verify_mirror_redirect(peth) # use common function to verify VLAN configuration verify_vlan_config(peth) diff --git a/src/conf_mode/interfaces_virtual-ethernet.py b/src/conf_mode/interfaces_virtual-ethernet.py index cb6104f59..59ce474fc 100755 --- a/src/conf_mode/interfaces_virtual-ethernet.py +++ b/src/conf_mode/interfaces_virtual-ethernet.py @@ -23,6 +23,7 @@ from vyos.configdict import get_interface_dict from vyos.configverify import verify_address from vyos.configverify import verify_bridge_delete from vyos.configverify import verify_vrf +from vyos.configverify import verify_mtu_ipv6 from vyos.ifconfig import VethIf from vyos.utils.network import interface_exists airbag.enable() @@ -62,6 +63,7 @@ def verify(veth): return None verify_vrf(veth) + verify_mtu_ipv6(veth) verify_address(veth) if 'peer_name' not in veth: diff --git a/src/conf_mode/interfaces_vti.py b/src/conf_mode/interfaces_vti.py index 20629c6c1..915bde066 100755 --- a/src/conf_mode/interfaces_vti.py +++ b/src/conf_mode/interfaces_vti.py @@ -20,6 +20,7 @@ from vyos.config import Config from vyos.configdict import get_interface_dict from vyos.configverify import verify_mirror_redirect from vyos.configverify import verify_vrf +from vyos.configverify import verify_mtu_ipv6 from vyos.ifconfig import VTIIf from vyos import ConfigError from vyos import airbag @@ -40,6 +41,7 @@ def get_config(config=None): def verify(vti): verify_vrf(vti) + verify_mtu_ipv6(vti) verify_mirror_redirect(vti) return None diff --git a/src/conf_mode/interfaces_wwan.py b/src/conf_mode/interfaces_wwan.py index 230eb14d6..ddbebfb4a 100755 --- a/src/conf_mode/interfaces_wwan.py +++ b/src/conf_mode/interfaces_wwan.py @@ -26,6 +26,7 @@ from vyos.configverify import verify_authentication from vyos.configverify import verify_interface_exists from vyos.configverify import verify_mirror_redirect from vyos.configverify import verify_vrf +from vyos.configverify import verify_mtu_ipv6 from vyos.ifconfig import WWANIf from vyos.utils.dict import dict_search from vyos.utils.process import cmd @@ -98,6 +99,7 @@ def verify(wwan): verify_interface_exists(wwan, ifname) verify_authentication(wwan) verify_vrf(wwan) + verify_mtu_ipv6(wwan) verify_mirror_redirect(wwan) return None diff --git a/src/conf_mode/policy_route.py b/src/conf_mode/policy_route.py index 223175b8a..521764896 100755 --- a/src/conf_mode/policy_route.py +++ b/src/conf_mode/policy_route.py @@ -21,13 +21,16 @@ from sys import exit from vyos.base import Warning from vyos.config import Config +from vyos.configdiff import get_config_diff, Diff from vyos.template import render 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 run from vyos.utils.network import get_vrf_tableid from vyos.defaults import rt_global_table from vyos.defaults import rt_global_vrf +from vyos.firewall import geoip_update from vyos import ConfigError from vyos import airbag airbag.enable() @@ -43,6 +46,43 @@ valid_groups = [ 'interface_group' ] +def geoip_updated(conf, policy): + diff = get_config_diff(conf) + node_diff = diff.get_child_nodes_diff(['policy'], expand_nodes=Diff.DELETE, recursive=True) + + out = { + 'name': [], + 'ipv6_name': [], + 'deleted_name': [], + 'deleted_ipv6_name': [] + } + updated = False + + for key, path in dict_search_recursive(policy, 'geoip'): + set_name = f'GEOIP_CC_{path[0]}_{path[1]}_{path[3]}' + if (path[0] == 'route'): + out['name'].append(set_name) + elif (path[0] == 'route6'): + set_name = f'GEOIP_CC6_{path[0]}_{path[1]}_{path[3]}' + out['ipv6_name'].append(set_name) + + updated = True + + if 'delete' in node_diff: + for key, path in dict_search_recursive(node_diff['delete'], 'geoip'): + set_name = f'GEOIP_CC_{path[0]}_{path[1]}_{path[3]}' + if (path[0] == 'route'): + out['deleted_name'].append(set_name) + elif (path[0] == 'route6'): + set_name = f'GEOIP_CC6_{path[0]}_{path[1]}_{path[3]}' + out['deleted_ipv6_name'].append(set_name) + updated = True + + if updated: + return out + + return False + def get_config(config=None): if config: conf = config @@ -60,6 +100,7 @@ def get_config(config=None): if 'dynamic_group' in policy['firewall_group']: del policy['firewall_group']['dynamic_group'] + policy['geoip_updated'] = geoip_updated(conf, policy) return policy def verify_rule(policy, name, rule_conf, ipv6, rule_id): @@ -203,6 +244,12 @@ def apply(policy): apply_table_marks(policy) + if policy['geoip_updated']: + # Call helper script to Update set contents + if 'name' in policy['geoip_updated'] or 'ipv6_name' in policy['geoip_updated']: + print('Updating GeoIP. Please wait...') + geoip_update(policy=policy) + return None if __name__ == '__main__': diff --git a/src/conf_mode/service_dhcp-server.py b/src/conf_mode/service_dhcp-server.py index e46d916fd..99c7e6a1f 100755 --- a/src/conf_mode/service_dhcp-server.py +++ b/src/conf_mode/service_dhcp-server.py @@ -43,6 +43,7 @@ airbag.enable() ctrl_socket = '/run/kea/dhcp4-ctrl-socket' config_file = '/run/kea/kea-dhcp4.conf' +config_file_d2 = '/run/kea/kea-dhcp-ddns.conf' lease_file = '/config/dhcp/dhcp4-leases.csv' lease_file_glob = '/config/dhcp/dhcp4-leases*' user_group = '_kea' @@ -170,6 +171,15 @@ def get_config(config=None): return dhcp +def verify_ddns_domain_servers(domain_type, domain): + if 'dns_server' in domain: + invalid_servers = [] + for server_no, server_config in domain['dns_server'].items(): + if 'address' not in server_config: + invalid_servers.append(server_no) + if len(invalid_servers) > 0: + raise ConfigError(f'{domain_type} DNS servers {", ".join(invalid_servers)} in DDNS configuration need to have an IP address') + return None def verify(dhcp): # bail out early - looks like removal from running config @@ -422,6 +432,22 @@ def verify(dhcp): if not interface_exists(interface): raise ConfigError(f'listen-interface "{interface}" does not exist') + if 'dynamic_dns_update' in dhcp: + ddns = dhcp['dynamic_dns_update'] + if 'tsig_key' in ddns: + invalid_keys = [] + for tsig_key_name, tsig_key_config in ddns['tsig_key'].items(): + if not ('algorithm' in tsig_key_config and 'secret' in tsig_key_config): + invalid_keys.append(tsig_key_name) + if len(invalid_keys) > 0: + raise ConfigError(f'Both algorithm and secret need to be set for TSIG keys: {", ".join(invalid_keys)}') + + if 'forward_domain' in ddns: + verify_ddns_domain_servers('Forward', ddns['forward_domain']) + + if 'reverse_domain' in ddns: + verify_ddns_domain_servers('Reverse', ddns['reverse_domain']) + return None @@ -485,6 +511,14 @@ def generate(dhcp): user=user_group, group=user_group, ) + if 'dynamic_dns_update' in dhcp: + render( + config_file_d2, + 'dhcp-server/kea-dhcp-ddns.conf.j2', + dhcp, + user=user_group, + group=user_group + ) return None diff --git a/src/conf_mode/system_host-name.py b/src/conf_mode/system_host-name.py index fef034d1c..de4accda2 100755 --- a/src/conf_mode/system_host-name.py +++ b/src/conf_mode/system_host-name.py @@ -175,7 +175,7 @@ def apply(config): # Restart services that use the hostname if hostname_new != hostname_old: - tmp = systemd_services['rsyslog'] + tmp = systemd_services['syslog'] call(f'systemctl restart {tmp}') # If SNMP is running, restart it too diff --git a/src/conf_mode/system_option.py b/src/conf_mode/system_option.py index b45a9d8a6..3d76a1eaa 100755 --- a/src/conf_mode/system_option.py +++ b/src/conf_mode/system_option.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2019-2024 VyOS maintainers and contributors +# Copyright (C) 2019-2025 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 @@ -136,6 +136,8 @@ def generate(options): mode = options['kernel']['amd_pstate_driver'] cmdline_options.append( f'initcall_blacklist=acpi_cpufreq_init amd_pstate={mode}') + if 'quiet' in options['kernel']: + cmdline_options.append('quiet') grub_util.update_kernel_cmdline_options(' '.join(cmdline_options)) return None diff --git a/src/conf_mode/system_syslog.py b/src/conf_mode/system_syslog.py index 414bd4b6b..bdab09f3c 100755 --- a/src/conf_mode/system_syslog.py +++ b/src/conf_mode/system_syslog.py @@ -35,7 +35,7 @@ rsyslog_conf = '/run/rsyslog/rsyslog.conf' logrotate_conf = '/etc/logrotate.d/vyos-rsyslog' systemd_socket = 'syslog.socket' -systemd_service = systemd_services['rsyslog'] +systemd_service = systemd_services['syslog'] def get_config(config=None): if config: diff --git a/src/etc/dhcp/dhclient-enter-hooks.d/06-vyos-nodefaultroute b/src/etc/dhcp/dhclient-enter-hooks.d/06-vyos-nodefaultroute new file mode 100644 index 000000000..38f674276 --- /dev/null +++ b/src/etc/dhcp/dhclient-enter-hooks.d/06-vyos-nodefaultroute @@ -0,0 +1,20 @@ +# Don't add default route if no-default-route is configured for interface + +# As configuration is not available to cli-shell-api at the first boot, we must use vyos.config, which contains a workaround for this +function get_no_default_route { +python3 - <<PYEND +from vyos.config import Config +import os + +config = Config() +if config.exists('interfaces'): + iface_types = config.list_nodes('interfaces') + for iface_type in iface_types: + if config.exists("interfaces {} {} dhcp-options no-default-route".format(iface_type, os.environ['interface'])): + print("True") +PYEND +} + +if [[ "$(get_no_default_route)" == 'True' ]]; then + new_routers="" +fi diff --git a/src/etc/sysctl.d/30-vyos-router.conf b/src/etc/sysctl.d/30-vyos-router.conf index 76be41ddc..ef81cebac 100644 --- a/src/etc/sysctl.d/30-vyos-router.conf +++ b/src/etc/sysctl.d/30-vyos-router.conf @@ -83,6 +83,16 @@ net.ipv4.conf.default.ignore_routes_with_linkdown=1 net.ipv6.conf.all.ignore_routes_with_linkdown=1 net.ipv6.conf.default.ignore_routes_with_linkdown=1 +# Disable IPv6 interface autoconfigurationnable packet forwarding for IPv6 +net.ipv6.conf.all.autoconf=0 +net.ipv6.conf.default.autoconf=0 +net.ipv6.conf.*.autoconf=0 + +# Disable IPv6 router advertisements +net.ipv6.conf.all.accept_ra=0 +net.ipv6.conf.default.accept_ra=0 +net.ipv6.conf.*.accept_ra=0 + # Enable packet forwarding for IPv6 net.ipv6.conf.all.forwarding=1 diff --git a/src/etc/systemd/system/kea-dhcp-ddns-server.service.d/override.conf b/src/etc/systemd/system/kea-dhcp-ddns-server.service.d/override.conf new file mode 100644 index 000000000..cdfdea8eb --- /dev/null +++ b/src/etc/systemd/system/kea-dhcp-ddns-server.service.d/override.conf @@ -0,0 +1,7 @@ +[Unit] +After= +After=vyos-router.service + +[Service] +ExecStart= +ExecStart=/usr/sbin/kea-dhcp-ddns -c /run/kea/kea-dhcp-ddns.conf diff --git a/src/helpers/geoip-update.py b/src/helpers/geoip-update.py index 34accf2cc..061c95401 100755 --- a/src/helpers/geoip-update.py +++ b/src/helpers/geoip-update.py @@ -25,20 +25,19 @@ def get_config(config=None): conf = config else: conf = ConfigTreeQuery() - base = ['firewall'] - if not conf.exists(base): - return None - - return conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True, - no_tag_node_value_mangle=True) + return ( + conf.get_config_dict(['firewall'], key_mangling=('-', '_'), get_first_key=True, + no_tag_node_value_mangle=True) if conf.exists(['firewall']) else None, + conf.get_config_dict(['policy'], key_mangling=('-', '_'), get_first_key=True, + no_tag_node_value_mangle=True) if conf.exists(['policy']) else None, + ) if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument("--force", help="Force update", action="store_true") args = parser.parse_args() - firewall = get_config() - - if not geoip_update(firewall, force=args.force): + firewall, policy = get_config() + if not geoip_update(firewall=firewall, policy=policy, force=args.force): sys.exit(1) diff --git a/src/init/vyos-router b/src/init/vyos-router index 081adf214..8584234b3 100755 --- a/src/init/vyos-router +++ b/src/init/vyos-router @@ -460,6 +460,14 @@ start () nfct helper add tns inet6 tcp nft --file /usr/share/vyos/vyos-firewall-init.conf || log_failure_msg "could not initiate firewall rules" + # Ensure rsyslog is the default syslog daemon + SYSTEMD_SYSLOG="/etc/systemd/system/syslog.service" + SYSTEMD_RSYSLOG="/lib/systemd/system/rsyslog.service" + if [ ! -L ${SYSTEMD_SYSLOG} ] || [ "$(readlink -f ${SYSTEMD_SYSLOG})" != "${SYSTEMD_RSYSLOG}" ]; then + ln -sf ${SYSTEMD_RSYSLOG} ${SYSTEMD_SYSLOG} + systemctl daemon-reload + fi + # As VyOS does not execute commands that are not present in the CLI we call # the script by hand to have a single source for the login banner and MOTD ${vyos_conf_scripts_dir}/system_syslog.py || log_failure_msg "could not reset syslog" diff --git a/src/op_mode/firewall.py b/src/op_mode/firewall.py index 086536e4e..ac47e3273 100755 --- a/src/op_mode/firewall.py +++ b/src/op_mode/firewall.py @@ -598,6 +598,9 @@ def show_firewall_group(name=None): prefix = 'DA_' if dynamic_type == 'address_group' else 'DA6_' if dynamic_type in firewall['group']['dynamic_group']: for dynamic_name, dynamic_conf in firewall['group']['dynamic_group'][dynamic_type].items(): + if name and name != dynamic_name: + continue + references = find_references(dynamic_type, dynamic_name) row = [dynamic_name, textwrap.fill(dynamic_conf.get('description') or '', 50), dynamic_type + '(dynamic)', '\n'.join(references) or 'N/D'] diff --git a/src/op_mode/image_installer.py b/src/op_mode/image_installer.py index 2660309a5..3af2232bb 100755 --- a/src/op_mode/image_installer.py +++ b/src/op_mode/image_installer.py @@ -491,6 +491,8 @@ def get_cli_kernel_options(config_file: str) -> list: config = ConfigTree(read_file(config_file)) config_dict = loads(config.to_json()) kernel_options = dict_search('system.option.kernel', config_dict) + if kernel_options is None: + kernel_options = {} cmdline_options = [] # XXX: This code path and if statements must be kept in sync with the Kernel @@ -504,6 +506,8 @@ def get_cli_kernel_options(config_file: str) -> list: mode = kernel_options['amd-pstate-driver'] cmdline_options.append( f'initcall_blacklist=acpi_cpufreq_init amd_pstate={mode}') + if 'quiet' in kernel_options: + cmdline_options.append('quiet') return cmdline_options diff --git a/src/op_mode/tech_support.py b/src/op_mode/tech_support.py index 24ac0af1b..c4496dfa3 100644 --- a/src/op_mode/tech_support.py +++ b/src/op_mode/tech_support.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2024 VyOS maintainers and contributors +# Copyright (C) 2025 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,6 +20,7 @@ import json import vyos.opmode from vyos.utils.process import cmd +from vyos.base import Warning def _get_version_data(): from vyos.version import get_version_data @@ -51,7 +52,12 @@ def _get_storage(): def _get_devices(): devices = {} devices["pci"] = cmd("lspci") - devices["usb"] = cmd("lsusb") + + try: + devices["usb"] = cmd("lsusb") + except OSError: + Warning("Could not retrieve information about USB devices") + devices["usb"] = {} return devices diff --git a/src/systemd/vyos.target b/src/systemd/vyos.target index c5d04891d..ea1593fe9 100644 --- a/src/systemd/vyos.target +++ b/src/systemd/vyos.target @@ -1,3 +1,3 @@ [Unit] Description=VyOS target -After=multi-user.target vyos-grub-update.service +After=multi-user.target vyos-grub-update.service systemd-sysctl.service |