diff options
-rw-r--r-- | data/templates/dhcp-client/ipv6.override.conf.j2 | 3 | ||||
-rw-r--r-- | data/templates/ipsec/swanctl/peer.j2 | 15 | ||||
-rw-r--r-- | interface-definitions/vpn_ipsec.xml.in | 57 | ||||
-rw-r--r-- | op-mode-definitions/system-image.xml.in | 2 | ||||
-rw-r--r-- | python/vyos/system/grub_util.py | 5 | ||||
-rwxr-xr-x | smoketest/scripts/cli/test_vpn_ipsec.py | 88 | ||||
-rwxr-xr-x | src/conf_mode/system_option.py | 7 | ||||
-rwxr-xr-x | src/op_mode/image_installer.py | 44 |
8 files changed, 207 insertions, 14 deletions
diff --git a/data/templates/dhcp-client/ipv6.override.conf.j2 b/data/templates/dhcp-client/ipv6.override.conf.j2 index b0c0e0544..d270a55fc 100644 --- a/data/templates/dhcp-client/ipv6.override.conf.j2 +++ b/data/templates/dhcp-client/ipv6.override.conf.j2 @@ -4,6 +4,9 @@ [Unit] ConditionPathExists={{ dhcp6_client_dir }}/dhcp6c.%i.conf +{% if ifname.startswith('pppoe') %} +After=ppp@{{ ifname }}.service +{% endif %} [Service] ExecStart= diff --git a/data/templates/ipsec/swanctl/peer.j2 b/data/templates/ipsec/swanctl/peer.j2 index 3a9af2c94..cf0865c88 100644 --- a/data/templates/ipsec/swanctl/peer.j2 +++ b/data/templates/ipsec/swanctl/peer.j2 @@ -68,8 +68,19 @@ rekey_packets = 0 rekey_time = 0s {% endif %} - local_ts = 0.0.0.0/0,::/0 - remote_ts = 0.0.0.0/0,::/0 +{# set default traffic-selectors #} +{% set local_ts = '0.0.0.0/0,::/0' %} +{% set remote_ts = '0.0.0.0/0,::/0' %} +{% if peer_conf.vti.traffic_selector is vyos_defined %} +{% if peer_conf.vti.traffic_selector.local is vyos_defined and peer_conf.vti.traffic_selector.local.prefix is vyos_defined %} +{% set local_ts = peer_conf.vti.traffic_selector.local.prefix | join(',') %} +{% endif %} +{% if peer_conf.vti.traffic_selector.remote is vyos_defined and peer_conf.vti.traffic_selector.remote.prefix is vyos_defined %} +{% set remote_ts = peer_conf.vti.traffic_selector.remote.prefix | join(',') %} +{% endif %} +{% endif %} + local_ts = {{ local_ts }} + remote_ts = {{ remote_ts }} updown = "/etc/ipsec.d/vti-up-down {{ peer_conf.vti.bind }}" {# The key defaults to 0 and will match any policies which similarly do not have a lookup key configuration. #} {# Thus we simply shift the key by one to also support a vti0 interface #} diff --git a/interface-definitions/vpn_ipsec.xml.in b/interface-definitions/vpn_ipsec.xml.in index 0cf526fad..873a4f882 100644 --- a/interface-definitions/vpn_ipsec.xml.in +++ b/interface-definitions/vpn_ipsec.xml.in @@ -1244,6 +1244,63 @@ <children> #include <include/ipsec/bind.xml.i> #include <include/ipsec/esp-group.xml.i> + <node name="traffic-selector"> + <properties> + <help>Traffic-selectors parameters</help> + </properties> + <children> + <node name="local"> + <properties> + <help>Local parameters for interesting traffic</help> + </properties> + <children> + <leafNode name="prefix"> + <properties> + <help>Local IPv4 or IPv6 prefix</help> + <valueHelp> + <format>ipv4net</format> + <description>Local IPv4 prefix</description> + </valueHelp> + <valueHelp> + <format>ipv6net</format> + <description>Local IPv6 prefix</description> + </valueHelp> + <constraint> + <validator name="ipv4-prefix"/> + <validator name="ipv6-prefix"/> + </constraint> + <multi/> + </properties> + </leafNode> + </children> + </node> + <node name="remote"> + <properties> + <help>Remote parameters for interesting traffic</help> + </properties> + <children> + <leafNode name="prefix"> + <properties> + <help>Remote IPv4 or IPv6 prefix</help> + <valueHelp> + <format>ipv4net</format> + <description>Remote IPv4 prefix</description> + </valueHelp> + <valueHelp> + <format>ipv6net</format> + <description>Remote IPv6 prefix</description> + </valueHelp> + <constraint> + <validator name="ipv4-prefix"/> + <validator name="ipv6-prefix"/> + </constraint> + <multi/> + </properties> + </leafNode> + </children> + </node> + </children> + </node> </children> </node> </children> diff --git a/op-mode-definitions/system-image.xml.in b/op-mode-definitions/system-image.xml.in index 44b055be6..847029dcd 100644 --- a/op-mode-definitions/system-image.xml.in +++ b/op-mode-definitions/system-image.xml.in @@ -193,7 +193,7 @@ <properties> <help>Show installed VyOS images</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/image_info.py show_images_summary</command> + <command>${vyos_op_scripts_dir}/image_info.py show_images_summary</command> <children> <node name="details"> <properties> diff --git a/python/vyos/system/grub_util.py b/python/vyos/system/grub_util.py index 4a3d8795e..ad95bb4f9 100644 --- a/python/vyos/system/grub_util.py +++ b/python/vyos/system/grub_util.py @@ -56,13 +56,12 @@ def set_kernel_cmdline_options(cmdline_options: str, version: str = '', @image.if_not_live_boot def update_kernel_cmdline_options(cmdline_options: str, - root_dir: str = '') -> None: + root_dir: str = '', + version = image.get_running_image()) -> None: """Update Kernel custom cmdline options""" if not root_dir: root_dir = disk.find_persistence() - version = image.get_running_image() - boot_opts_current = grub.get_boot_opts(version, root_dir) boot_opts_proposed = grub.BOOT_OPTS_STEM + f'{version} {cmdline_options}' diff --git a/smoketest/scripts/cli/test_vpn_ipsec.py b/smoketest/scripts/cli/test_vpn_ipsec.py index 91a76e6f6..c1d943bde 100755 --- a/smoketest/scripts/cli/test_vpn_ipsec.py +++ b/smoketest/scripts/cli/test_vpn_ipsec.py @@ -352,6 +352,94 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase): self.tearDownPKI() + def test_site_to_site_vti_ts_afi(self): + local_address = '192.0.2.10' + vti = 'vti10' + # IKE + self.cli_set(base_path + ['ike-group', ike_group, 'key-exchange', 'ikev2']) + self.cli_set(base_path + ['ike-group', ike_group, 'disable-mobike']) + # ESP + self.cli_set(base_path + ['esp-group', esp_group, 'compression']) + # VTI interface + self.cli_set(vti_path + [vti, 'address', '10.1.1.1/24']) + + # vpn ipsec auth psk <tag> id <x.x.x.x> + self.cli_set(base_path + ['authentication', 'psk', connection_name, 'id', local_id]) + self.cli_set(base_path + ['authentication', 'psk', connection_name, 'id', remote_id]) + self.cli_set(base_path + ['authentication', 'psk', connection_name, 'id', peer_ip]) + self.cli_set(base_path + ['authentication', 'psk', connection_name, 'secret', secret]) + + # Site to site + peer_base_path = base_path + ['site-to-site', 'peer', connection_name] + self.cli_set(peer_base_path + ['authentication', 'mode', 'pre-shared-secret']) + self.cli_set(peer_base_path + ['connection-type', 'none']) + self.cli_set(peer_base_path + ['force-udp-encapsulation']) + self.cli_set(peer_base_path + ['ike-group', ike_group]) + self.cli_set(peer_base_path + ['default-esp-group', esp_group]) + self.cli_set(peer_base_path + ['local-address', local_address]) + self.cli_set(peer_base_path + ['remote-address', peer_ip]) + self.cli_set(peer_base_path + ['vti', 'bind', vti]) + self.cli_set(peer_base_path + ['vti', 'esp-group', esp_group]) + self.cli_set(peer_base_path + ['vti', 'traffic-selector', 'local', 'prefix', '0.0.0.0/0']) + self.cli_set(peer_base_path + ['vti', 'traffic-selector', 'remote', 'prefix', '192.0.2.1/32']) + self.cli_set(peer_base_path + ['vti', 'traffic-selector', 'remote', 'prefix', '192.0.2.3/32']) + + self.cli_commit() + + swanctl_conf = read_file(swanctl_file) + if_id = vti.lstrip('vti') + # The key defaults to 0 and will match any policies which similarly do + # not have a lookup key configuration - thus we shift the key by one + # to also support a vti0 interface + if_id = str(int(if_id) +1) + swanctl_conf_lines = [ + f'version = 2', + f'auth = psk', + f'proposals = aes128-sha1-modp1024', + f'esp_proposals = aes128-sha1-modp1024', + f'local_addrs = {local_address} # dhcp:no', + f'mobike = no', + f'remote_addrs = {peer_ip}', + f'mode = tunnel', + f'local_ts = 0.0.0.0/0', + f'remote_ts = 192.0.2.1/32,192.0.2.3/32', + f'ipcomp = yes', + f'start_action = none', + f'replay_window = 32', + f'if_id_in = {if_id}', # will be 11 for vti10 - shifted by one + f'if_id_out = {if_id}', + f'updown = "/etc/ipsec.d/vti-up-down {vti}"' + ] + for line in swanctl_conf_lines: + self.assertIn(line, swanctl_conf) + + # Check IPv6 TS + self.cli_delete(peer_base_path + ['vti', 'traffic-selector']) + self.cli_set(peer_base_path + ['vti', 'traffic-selector', 'local', 'prefix', '::/0']) + self.cli_set(peer_base_path + ['vti', 'traffic-selector', 'remote', 'prefix', '::/0']) + self.cli_commit() + swanctl_conf = read_file(swanctl_file) + swanctl_conf_lines = [ + f'local_ts = ::/0', + f'remote_ts = ::/0', + f'updown = "/etc/ipsec.d/vti-up-down {vti}"' + ] + for line in swanctl_conf_lines: + self.assertIn(line, swanctl_conf) + + # Check both TS (IPv4 + IPv6) + self.cli_delete(peer_base_path + ['vti', 'traffic-selector']) + self.cli_commit() + swanctl_conf = read_file(swanctl_file) + swanctl_conf_lines = [ + f'local_ts = 0.0.0.0/0,::/0', + f'remote_ts = 0.0.0.0/0,::/0', + f'updown = "/etc/ipsec.d/vti-up-down {vti}"' + ] + for line in swanctl_conf_lines: + self.assertIn(line, swanctl_conf) + + def test_dmvpn(self): ike_lifetime = '3600' esp_lifetime = '1800' diff --git a/src/conf_mode/system_option.py b/src/conf_mode/system_option.py index 064a1aa91..b45a9d8a6 100755 --- a/src/conf_mode/system_option.py +++ b/src/conf_mode/system_option.py @@ -122,6 +122,10 @@ def generate(options): render(ssh_config, 'system/ssh_config.j2', options) render(usb_autosuspend, 'system/40_usb_autosuspend.j2', options) + # XXX: This code path and if statements must be kept in sync with the Kernel + # option handling in image_installer.py:get_cli_kernel_options(). This + # occurance is used for having the appropriate options passed to GRUB + # when re-configuring options on the CLI. cmdline_options = [] if 'kernel' in options: if 'disable_mitigations' in options['kernel']: @@ -131,8 +135,7 @@ def generate(options): if 'amd_pstate_driver' in options['kernel']: mode = options['kernel']['amd_pstate_driver'] cmdline_options.append( - f'initcall_blacklist=acpi_cpufreq_init amd_pstate={mode}' - ) + f'initcall_blacklist=acpi_cpufreq_init amd_pstate={mode}') grub_util.update_kernel_cmdline_options(' '.join(cmdline_options)) return None diff --git a/src/op_mode/image_installer.py b/src/op_mode/image_installer.py index 179913f15..2660309a5 100755 --- a/src/op_mode/image_installer.py +++ b/src/op_mode/image_installer.py @@ -24,7 +24,9 @@ from glob import glob from sys import exit from os import environ from os import readlink -from os import getpid, getppid +from os import getpid +from os import getppid +from json import loads from typing import Union from urllib.parse import urlparse from passlib.hosts import linux_context @@ -35,15 +37,23 @@ from psutil import disk_partitions from vyos.base import Warning from vyos.configtree import ConfigTree from vyos.remote import download -from vyos.system import disk, grub, image, compat, raid, SYSTEM_CFG_VER +from vyos.system import disk +from vyos.system import grub +from vyos.system import image +from vyos.system import compat +from vyos.system import raid +from vyos.system import SYSTEM_CFG_VER +from vyos.system import grub_util from vyos.template import render from vyos.utils.auth import ( DEFAULT_PASSWORD, EPasswdStrength, evaluate_strength ) +from vyos.utils.dict import dict_search from vyos.utils.io import ask_input, ask_yes_no, select_entry from vyos.utils.file import chmod_2775 +from vyos.utils.file import read_file from vyos.utils.process import cmd, run, rc_cmd from vyos.version import get_version_data @@ -477,6 +487,25 @@ def setup_grub(root_dir: str) -> None: render(grub_cfg_menu, grub.TMPL_GRUB_MENU, {}) render(grub_cfg_options, grub.TMPL_GRUB_OPTS, {}) +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) + cmdline_options = [] + + # XXX: This code path and if statements must be kept in sync with the Kernel + # option handling in system_options.py:generate(). This occurance is used + # for having the appropriate options passed to GRUB after an image upgrade! + if 'disable-mitigations' in kernel_options: + cmdline_options.append('mitigations=off') + if 'disable-power-saving' in kernel_options: + cmdline_options.append('intel_idle.max_cstate=0 processor.max_cstate=1') + if 'amd-pstate-driver' in kernel_options: + mode = kernel_options['amd-pstate-driver'] + cmdline_options.append( + f'initcall_blacklist=acpi_cpufreq_init amd_pstate={mode}') + + return cmdline_options def configure_authentication(config_file: str, password: str) -> None: """Write encrypted password to config file @@ -491,10 +520,7 @@ def configure_authentication(config_file: str, password: str) -> None: plaintext exposed """ encrypted_password = linux_context.hash(password) - - with open(config_file) as f: - config_string = f.read() - + config_string = read_file(config_file) config = ConfigTree(config_string) config.set([ 'system', 'login', 'user', 'vyos', 'authentication', @@ -1045,6 +1071,12 @@ def add_image(image_path: str, vrf: str = None, username: str = '', if set_as_default: grub.set_default(image_name, root_dir) + cmdline_options = get_cli_kernel_options( + f'{target_config_dir}/config.boot') + grub_util.update_kernel_cmdline_options(' '.join(cmdline_options), + root_dir=root_dir, + version=image_name) + except OSError as e: # if no space error, remove image dir and cleanup if e.errno == ENOSPC: |