diff options
-rw-r--r-- | Makefile | 3 | ||||
-rw-r--r-- | data/templates/container/registries.conf.j2 | 10 | ||||
-rw-r--r-- | data/templates/rsyslog/rsyslog.conf.j2 | 2 | ||||
-rw-r--r-- | interface-definitions/container.xml.in | 48 | ||||
-rwxr-xr-x | smoketest/scripts/cli/test_interfaces_vxlan.py | 27 | ||||
-rwxr-xr-x | smoketest/scripts/cli/test_system_syslog.py | 25 | ||||
-rwxr-xr-x | src/conf_mode/container.py | 7 | ||||
-rwxr-xr-x | src/conf_mode/interfaces_vxlan.py | 2 | ||||
-rwxr-xr-x | src/conf_mode/vpn_ipsec.py | 27 |
9 files changed, 146 insertions, 5 deletions
@@ -24,7 +24,8 @@ op_xml_obj = $(op_xml_src:.xml.in=.xml) .ONESHELL: libvyosconfig: if ! [ -f $(LIBVYOSCONFIG_BUILD_PATH) ]; then - git clone https://github.com/vyos/libvyosconfig.git /tmp/libvyosconfig || exit 1 + rm -rf /tmp/libvyosconfig && \ + git clone https://github.com/vyos/libvyosconfig.git /tmp/libvyosconfig || exit 1 cd /tmp/libvyosconfig && \ git checkout 677d1e2bf8109b9fd4da60e20376f992b747e384 || exit 1 ./build.sh diff --git a/data/templates/container/registries.conf.j2 b/data/templates/container/registries.conf.j2 index eb7ff8775..b5c7eed9b 100644 --- a/data/templates/container/registries.conf.j2 +++ b/data/templates/container/registries.conf.j2 @@ -28,4 +28,14 @@ {% set _ = registry_list.append(r) %} {% endfor %} unqualified-search-registries = {{ registry_list }} +{% for r, r_options in registry.items() if r_options.disable is not vyos_defined %} +[[registry]] +{% if r_options.mirror is vyos_defined %} +location = "{{ r_options.mirror.host_name if r_options.mirror.host_name is vyos_defined else r_options.mirror.address }}{{ ":" + r_options.mirror.port if r_options.mirror.port is vyos_defined }}{{ r_options.mirror.path if r_options.mirror.path is vyos_defined }}" +{% else %} +location = "{{ r }}" +{% endif %} +insecure = {{ 'true' if r_options.insecure is vyos_defined else 'false' }} +prefix = "{{ r }}" +{% endfor %} {% endif %} diff --git a/data/templates/rsyslog/rsyslog.conf.j2 b/data/templates/rsyslog/rsyslog.conf.j2 index e2ff334ff..68e34f3f8 100644 --- a/data/templates/rsyslog/rsyslog.conf.j2 +++ b/data/templates/rsyslog/rsyslog.conf.j2 @@ -98,7 +98,7 @@ if prifilt("{{ tmp | join(',') }}") then { action( type="omfwd" # Remote syslog server where we send our logs to - target="{{ remote_name | bracketize_ipv6 }}" + target="{{ remote_name }}" # Port on the remote syslog server port="{{ remote_options.port }}" protocol="{{ remote_options.protocol }}" diff --git a/interface-definitions/container.xml.in b/interface-definitions/container.xml.in index 5c320e8c6..3a5cfbaa6 100644 --- a/interface-definitions/container.xml.in +++ b/interface-definitions/container.xml.in @@ -571,6 +571,54 @@ <children> #include <include/interface/authentication.xml.i> #include <include/generic-disable-node.xml.i> + <leafNode name="insecure"> + <properties> + <help>Allow registry access over unencrypted HTTP or TLS connections with untrusted certificates</help> + <valueless/> + </properties> + </leafNode> + <node name="mirror"> + <properties> + <help>Registry mirror, use host-name|address[:port][/path]</help> + </properties> + <children> + <leafNode name="address"> + <properties> + <help>IP address of container registry mirror</help> + <valueHelp> + <format>ipv4</format> + <description>IPv4 address of container registry mirror</description> + </valueHelp> + <valueHelp> + <format>ipv6</format> + <description>IPv6 address of container registry mirror</description> + </valueHelp> + <constraint> + <validator name="ip-address"/> + <validator name="ipv6-link-local"/> + </constraint> + </properties> + </leafNode> + <leafNode name="host-name"> + <properties> + <help>Hostname of container registry mirror</help> + <valueHelp> + <format>hostname</format> + <description>FQDN of container registry mirror</description> + </valueHelp> + <constraint> + <validator name="fqdn"/> + </constraint> + </properties> + </leafNode> + #include <include/port-number.xml.i> + <leafNode name="path"> + <properties> + <help>Path of container registry mirror, optional, must be start with '/' if not empty</help> + </properties> + </leafNode> + </children> + </node> </children> </tagNode> </children> diff --git a/smoketest/scripts/cli/test_interfaces_vxlan.py b/smoketest/scripts/cli/test_interfaces_vxlan.py index b2076b43b..05900a4ba 100755 --- a/smoketest/scripts/cli/test_interfaces_vxlan.py +++ b/smoketest/scripts/cli/test_interfaces_vxlan.py @@ -25,6 +25,7 @@ from vyos.utils.network import interface_exists from vyos.utils.network import get_vxlan_vlan_tunnels from vyos.utils.network import get_vxlan_vni_filter from vyos.template import is_ipv6 +from vyos import ConfigError from base_interfaces_test import BasicInterfaceTest def convert_to_list(ranges_to_convert): @@ -114,6 +115,32 @@ class VXLANInterfaceTest(BasicInterfaceTest.TestCase): self.assertEqual(Interface(interface).get_admin_state(), 'up') ttl += 10 + + def test_vxlan_group_remote_error(self): + intf = 'vxlan60' + options = [ + 'group 239.4.4.5', + 'mtu 1420', + 'remote 192.168.0.254', + 'source-address 192.168.0.1', + 'source-interface eth0', + 'vni 60' + ] + params = [] + for option in options: + opts = option.split() + params.append(opts[0]) + self.cli_set(self._base_path + [ intf ] + opts) + + with self.assertRaises(ConfigSessionError) as cm: + self.cli_commit() + + exception = cm.exception + self.assertIn('Both group and remote cannot be specified', str(exception)) + for param in params: + self.cli_delete(self._base_path + [intf, param]) + + def test_vxlan_external(self): interface = 'vxlan0' source_address = '192.0.2.1' diff --git a/smoketest/scripts/cli/test_system_syslog.py b/smoketest/scripts/cli/test_system_syslog.py index e642b5660..ba325ced8 100755 --- a/smoketest/scripts/cli/test_system_syslog.py +++ b/smoketest/scripts/cli/test_system_syslog.py @@ -18,6 +18,7 @@ import unittest from base_vyostest_shim import VyOSUnitTestSHIM +from vyos.configsession import ConfigSessionError from vyos.utils.file import read_file from vyos.utils.process import cmd from vyos.utils.process import process_named_running @@ -28,6 +29,8 @@ RSYSLOG_CONF = '/run/rsyslog/rsyslog.conf' base_path = ['system', 'syslog'] +dummy_interface = 'dum372874' + def get_config(string=''): """ Retrieve current "running configuration" from FRR @@ -127,15 +130,22 @@ class TestRSYSLOGService(VyOSUnitTestSHIM.TestCase): self.assertNotIn('module(load="immark"', config) def test_remote(self): + dummy_if_path = ['interfaces', 'dummy', dummy_interface] rhosts = { '169.254.0.1': { 'facility': {'auth' : {'level': 'info'}}, 'protocol': 'udp', }, - '169.254.0.2': { + '2001:db8::1': { + 'facility': {'all' : {'level': 'debug'}}, 'port': '1514', 'protocol': 'udp', }, + 'syslog.vyos.net': { + 'facility': {'all' : {'level': 'debug'}}, + 'port': '1515', + 'protocol': 'tcp', + }, '169.254.0.3': { 'facility': {'auth' : {'level': 'info'}, 'kern' : {'level': 'debug'}, @@ -169,6 +179,15 @@ class TestRSYSLOGService(VyOSUnitTestSHIM.TestCase): protocol = remote_options['protocol'] self.cli_set(remote_base + ['protocol'], value=protocol) + if 'source_address' in remote_options: + source_address = remote_options['source_address'] + self.cli_set(remote_base + ['source-address', source_address]) + + # check validate() - source address does not exist + with self.assertRaises(ConfigSessionError): + self.cli_commit() + self.cli_set(dummy_if_path + ['address', f'{source_address}/32']) + self.cli_commit() config = read_file(RSYSLOG_CONF) @@ -211,6 +230,9 @@ class TestRSYSLOGService(VyOSUnitTestSHIM.TestCase): else: self.assertIn( ' TCP_Framing="traditional"', config) + # cleanup dummy interface + self.cli_delete(dummy_if_path) + def test_vrf_source_address(self): rhosts = { '169.254.0.10': { }, @@ -252,7 +274,6 @@ class TestRSYSLOGService(VyOSUnitTestSHIM.TestCase): value=vrf) self.cli_commit() - config = read_file(RSYSLOG_CONF) for remote, remote_options in rhosts.items(): config = get_config(f'# Remote syslog to {remote}') diff --git a/src/conf_mode/container.py b/src/conf_mode/container.py index 3636b0871..18d660a4e 100755 --- a/src/conf_mode/container.py +++ b/src/conf_mode/container.py @@ -289,6 +289,13 @@ def verify(container): if 'registry' in container: for registry, registry_config in container['registry'].items(): + if 'mirror' in registry_config: + if 'host_name' in registry_config['mirror'] and 'address' in registry_config['mirror']: + raise ConfigError(f'Container registry mirror address/host-name are mutually exclusive!') + + if 'path' in registry_config['mirror'] and not registry_config['mirror']['path'].startswith('/'): + raise ConfigError('Container registry mirror path must start with "/"!') + if 'authentication' not in registry_config: continue if not {'username', 'password'} <= set(registry_config['authentication']): diff --git a/src/conf_mode/interfaces_vxlan.py b/src/conf_mode/interfaces_vxlan.py index 68646e8ff..256b65708 100755 --- a/src/conf_mode/interfaces_vxlan.py +++ b/src/conf_mode/interfaces_vxlan.py @@ -95,6 +95,8 @@ def verify(vxlan): if 'group' in vxlan: if 'source_interface' not in vxlan: raise ConfigError('Multicast VXLAN requires an underlaying interface') + if 'remote' in vxlan: + raise ConfigError('Both group and remote cannot be specified') verify_source_interface(vxlan) if not any(tmp in ['group', 'remote', 'source_address', 'source_interface'] for tmp in vxlan): diff --git a/src/conf_mode/vpn_ipsec.py b/src/conf_mode/vpn_ipsec.py index 25604d2a2..71a503e61 100755 --- a/src/conf_mode/vpn_ipsec.py +++ b/src/conf_mode/vpn_ipsec.py @@ -156,6 +156,8 @@ def get_config(config=None): _, vti = get_interface_dict(conf, ['interfaces', 'vti'], vti_interface) ipsec['vti_interface_dicts'][vti_interface] = vti + ipsec['vpp_ipsec_exists'] = conf.exists(['vpp', 'settings', 'ipsec']) + return ipsec def get_dhcp_address(iface): @@ -484,6 +486,17 @@ def verify(ipsec): else: raise ConfigError(f"Missing ike-group on site-to-site peer {peer}") + # verify encryption algorithm compatibility for IKE with VPP + if ipsec['vpp_ipsec_exists']: + ike_group = ipsec['ike_group'][peer_conf['ike_group']] + for proposal, proposal_config in ike_group.get('proposal', {}).items(): + algs = ['gmac', 'serpent', 'twofish'] + if any(alg in proposal_config['encryption'] for alg in algs): + raise ConfigError( + f'Encryption algorithm {proposal_config["encryption"]} cannot be used ' + f'for IKE proposal {proposal} for site-to-site peer {peer} with VPP' + ) + if 'authentication' not in peer_conf or 'mode' not in peer_conf['authentication']: raise ConfigError(f"Missing authentication on site-to-site peer {peer}") @@ -562,7 +575,7 @@ def verify(ipsec): esp_group_name = tunnel_conf['esp_group'] if 'esp_group' in tunnel_conf else peer_conf['default_esp_group'] - if esp_group_name not in ipsec['esp_group']: + if esp_group_name not in ipsec.get('esp_group'): raise ConfigError(f"Invalid esp-group on tunnel {tunnel} for site-to-site peer {peer}") esp_group = ipsec['esp_group'][esp_group_name] @@ -574,6 +587,18 @@ def verify(ipsec): if ('local' in tunnel_conf and 'prefix' in tunnel_conf['local']) or ('remote' in tunnel_conf and 'prefix' in tunnel_conf['remote']): raise ConfigError(f"Local/remote prefix cannot be used with ESP transport mode on tunnel {tunnel} for site-to-site peer {peer}") + # verify ESP encryption algorithm compatibility with VPP + # because Marvel plugin for VPP doesn't support all algorithms that Strongswan does + if ipsec['vpp_ipsec_exists']: + for proposal, proposal_config in esp_group.get('proposal', {}).items(): + algs = ['aes128', 'aes192', 'aes256', 'aes128gcm128', 'aes192gcm128', 'aes256gcm128'] + if proposal_config['encryption'] not in algs: + raise ConfigError( + f'Encryption algorithm {proposal_config["encryption"]} cannot be used ' + f'for ESP proposal {proposal} on tunnel {tunnel} for site-to-site peer {peer} with VPP' + ) + + def cleanup_pki_files(): for path in [CERT_PATH, CA_PATH, CRL_PATH, KEY_PATH, PUBKEY_PATH]: if not os.path.exists(path): |