diff options
Diffstat (limited to 'src/conf_mode')
-rwxr-xr-x | src/conf_mode/firewall.py | 74 | ||||
-rwxr-xr-x | src/conf_mode/interfaces-vxlan.py | 33 | ||||
-rwxr-xr-x | src/conf_mode/load-balancing-haproxy.py | 14 | ||||
-rwxr-xr-x | src/conf_mode/nat.py | 14 | ||||
-rwxr-xr-x | src/conf_mode/nat66.py | 22 |
5 files changed, 123 insertions, 34 deletions
diff --git a/src/conf_mode/firewall.py b/src/conf_mode/firewall.py index f6480ab0a..8028492a7 100755 --- a/src/conf_mode/firewall.py +++ b/src/conf_mode/firewall.py @@ -283,8 +283,8 @@ def verify_rule(firewall, rule_conf, ipv6): for direction in ['inbound_interface','outbound_interface']: if direction in rule_conf: - if 'interface_name' in rule_conf[direction] and 'interface_group' in rule_conf[direction]: - raise ConfigError(f'Cannot specify both interface-group and interface-name for {direction}') + if 'name' in rule_conf[direction] and 'group' in rule_conf[direction]: + raise ConfigError(f'Cannot specify both interface group and interface name for {direction}') def verify_nested_group(group_name, group, groups, seen): if 'include' not in group: @@ -374,12 +374,82 @@ def verify(firewall): for rule_id, rule_conf in name_conf['rule'].items(): verify_rule(firewall, rule_conf, True) + #### ZONESSSS + local_zone = False + zone_interfaces = [] + + if 'zone' in firewall: + for zone, zone_conf in firewall['zone'].items(): + if 'local_zone' not in zone_conf and 'interface' not in zone_conf: + raise ConfigError(f'Zone "{zone}" has no interfaces and is not the local zone') + + if 'local_zone' in zone_conf: + if local_zone: + raise ConfigError('There cannot be multiple local zones') + if 'interface' in zone_conf: + raise ConfigError('Local zone cannot have interfaces assigned') + if 'intra_zone_filtering' in zone_conf: + raise ConfigError('Local zone cannot use intra-zone-filtering') + local_zone = True + + if 'interface' in zone_conf: + found_duplicates = [intf for intf in zone_conf['interface'] if intf in zone_interfaces] + + if found_duplicates: + raise ConfigError(f'Interfaces cannot be assigned to multiple zones') + + zone_interfaces += zone_conf['interface'] + + if 'intra_zone_filtering' in zone_conf: + intra_zone = zone_conf['intra_zone_filtering'] + + if len(intra_zone) > 1: + raise ConfigError('Only one intra-zone-filtering action must be specified') + + if 'firewall' in intra_zone: + v4_name = dict_search_args(intra_zone, 'firewall', 'name') + if v4_name and not dict_search_args(firewall, 'ipv4', 'name', v4_name): + raise ConfigError(f'Firewall name "{v4_name}" does not exist') + + v6_name = dict_search_args(intra_zone, 'firewall', 'ipv6_name') + if v6_name and not dict_search_args(firewall, 'ipv6', 'name', v6_name): + raise ConfigError(f'Firewall ipv6-name "{v6_name}" does not exist') + + if not v4_name and not v6_name: + raise ConfigError('No firewall names specified for intra-zone-filtering') + + if 'from' in zone_conf: + for from_zone, from_conf in zone_conf['from'].items(): + if from_zone not in firewall['zone']: + raise ConfigError(f'Zone "{zone}" refers to a non-existent or deleted zone "{from_zone}"') + + v4_name = dict_search_args(from_conf, 'firewall', 'name') + if v4_name and not dict_search_args(firewall, 'ipv4', 'name', v4_name): + raise ConfigError(f'Firewall name "{v4_name}" does not exist') + + v6_name = dict_search_args(from_conf, 'firewall', 'ipv6_name') + if v6_name and not dict_search_args(firewall, 'ipv6', 'name', v6_name): + raise ConfigError(f'Firewall ipv6-name "{v6_name}" does not exist') + return None def generate(firewall): if not os.path.exists(nftables_conf): firewall['first_install'] = True + if 'zone' in firewall: + for local_zone, local_zone_conf in firewall['zone'].items(): + if 'local_zone' not in local_zone_conf: + continue + + local_zone_conf['from_local'] = {} + + for zone, zone_conf in firewall['zone'].items(): + if zone == local_zone or 'from' not in zone_conf: + continue + if local_zone in zone_conf['from']: + local_zone_conf['from_local'][zone] = zone_conf['from'][local_zone] + render(nftables_conf, 'firewall/nftables.j2', firewall) return None diff --git a/src/conf_mode/interfaces-vxlan.py b/src/conf_mode/interfaces-vxlan.py index 05f68112a..6bf3227d5 100755 --- a/src/conf_mode/interfaces-vxlan.py +++ b/src/conf_mode/interfaces-vxlan.py @@ -34,6 +34,7 @@ from vyos.configverify import verify_bond_bridge_member 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 import ConfigError from vyos import airbag airbag.enable() @@ -53,7 +54,7 @@ def get_config(config=None): # VXLAN interfaces are picky and require recreation if certain parameters # change. But a VXLAN interface should - of course - not be re-created if # it's description or IP address is adjusted. Feels somehow logic doesn't it? - for cli_option in ['parameters', 'external', 'gpe', 'group', 'port', 'remote', + for cli_option in ['parameters', 'gpe', 'group', 'port', 'remote', 'source-address', 'source-interface', 'vni']: if is_node_changed(conf, base + [ifname, cli_option]): vxlan.update({'rebuild_required': {}}) @@ -94,17 +95,17 @@ def verify(vxlan): if not any(tmp in ['group', 'remote', 'source_address', 'source_interface'] for tmp in vxlan): raise ConfigError('Group, remote, source-address or source-interface must be configured') - if 'vni' not in vxlan and 'external' not in vxlan: - raise ConfigError( - 'Must either configure VXLAN "vni" or use "external" CLI option!') + if 'vni' not in vxlan and dict_search('parameters.external', vxlan) == None: + raise ConfigError('Must either configure VXLAN "vni" or use "external" CLI option!') - if {'external', 'vni'} <= set(vxlan): - raise ConfigError('Can not specify both "external" and "VNI"!') + if dict_search('parameters.external', vxlan): + if 'vni' in vxlan: + raise ConfigError('Can not specify both "external" and "VNI"!') - if {'external', 'other_tunnels'} <= set(vxlan): - other_tunnels = ', '.join(vxlan['other_tunnels']) - raise ConfigError(f'Only one VXLAN tunnel is supported when "external" '\ - f'CLI option is used. Additional tunnels: {other_tunnels}') + if 'other_tunnels' in vxlan: + other_tunnels = ', '.join(vxlan['other_tunnels']) + raise ConfigError(f'Only one VXLAN tunnel is supported when "external" '\ + f'CLI option is used. Additional tunnels: {other_tunnels}') if 'gpe' in vxlan and 'external' not in vxlan: raise ConfigError(f'VXLAN-GPE is only supported when "external" '\ @@ -164,10 +165,22 @@ def verify(vxlan): raise ConfigError(f'VNI "{vni}" is already assigned to a different VLAN!') vnis_used.append(vni) + if dict_search('parameters.neighbor_suppress', vxlan): + if 'is_bridge_member' not in vxlan: + raise ConfigError('Neighbor suppression requires that VXLAN interface '\ + 'is member of a bridge interface!') + verify_mtu_ipv6(vxlan) verify_address(vxlan) verify_bond_bridge_member(vxlan) verify_mirror_redirect(vxlan) + + # We use a defaultValue for port, thus it's always safe to use + if vxlan['port'] == '8472': + Warning('Starting from VyOS 1.4, the default port for VXLAN '\ + 'has been changed to 4789. This matches the IANA assigned '\ + 'standard port number!') + return None def generate(vxlan): diff --git a/src/conf_mode/load-balancing-haproxy.py b/src/conf_mode/load-balancing-haproxy.py index 8fe429653..ec4311bb5 100755 --- a/src/conf_mode/load-balancing-haproxy.py +++ b/src/conf_mode/load-balancing-haproxy.py @@ -94,8 +94,8 @@ def generate(lb): if os.path.isfile(file): os.unlink(file) # Delete old directories - #if os.path.isdir(load_balancing_dir): - # rmtree(load_balancing_dir, ignore_errors=True) + if os.path.isdir(load_balancing_dir): + rmtree(load_balancing_dir, ignore_errors=True) return None @@ -106,15 +106,12 @@ def generate(lb): # SSL Certificates for frontend for front, front_config in lb['service'].items(): if 'ssl' in front_config: - cert_file_path = os.path.join(load_balancing_dir, 'cert.pem') - cert_key_path = os.path.join(load_balancing_dir, 'cert.pem.key') - ca_cert_file_path = os.path.join(load_balancing_dir, 'ca.pem') if 'certificate' in front_config['ssl']: - #cert_file_path = os.path.join(load_balancing_dir, 'cert.pem') - #cert_key_path = os.path.join(load_balancing_dir, 'cert.key') cert_name = front_config['ssl']['certificate'] pki_cert = lb['pki']['certificate'][cert_name] + cert_file_path = os.path.join(load_balancing_dir, f'{cert_name}.pem') + cert_key_path = os.path.join(load_balancing_dir, f'{cert_name}.pem.key') with open(cert_file_path, 'w') as f: f.write(wrap_certificate(pki_cert['certificate'])) @@ -126,6 +123,7 @@ def generate(lb): if 'ca_certificate' in front_config['ssl']: ca_name = front_config['ssl']['ca_certificate'] pki_ca_cert = lb['pki']['ca'][ca_name] + ca_cert_file_path = os.path.join(load_balancing_dir, f'{ca_name}.pem') with open(ca_cert_file_path, 'w') as f: f.write(wrap_certificate(pki_ca_cert['certificate'])) @@ -133,11 +131,11 @@ def generate(lb): # SSL Certificates for backend for back, back_config in lb['backend'].items(): if 'ssl' in back_config: - ca_cert_file_path = os.path.join(load_balancing_dir, 'ca.pem') if 'ca_certificate' in back_config['ssl']: ca_name = back_config['ssl']['ca_certificate'] pki_ca_cert = lb['pki']['ca'][ca_name] + ca_cert_file_path = os.path.join(load_balancing_dir, f'{ca_name}.pem') with open(ca_cert_file_path, 'w') as f: f.write(wrap_certificate(pki_ca_cert['certificate'])) diff --git a/src/conf_mode/nat.py b/src/conf_mode/nat.py index 52a7a71fd..44b13d413 100755 --- a/src/conf_mode/nat.py +++ b/src/conf_mode/nat.py @@ -151,8 +151,11 @@ def verify(nat): err_msg = f'Source NAT configuration error in rule {rule}:' if 'outbound_interface' in config: - if config['outbound_interface'] not in 'any' and config['outbound_interface'] not in interfaces(): - Warning(f'rule "{rule}" interface "{config["outbound_interface"]}" does not exist on this system') + if 'name' in config['outbound_interface'] and 'group' in config['outbound_interface']: + raise ConfigError(f'{err_msg} - Cannot specify both interface group and interface name for nat source rule "{rule}"') + elif 'name' in config['outbound_interface']: + if config['outbound_interface']['name'] not in 'any' and config['outbound_interface']['name'] not in interfaces(): + Warning(f'{err_msg} - interface "{config["outbound_interface"]["name"]}" does not exist on this system') if not dict_search('translation.address', config) and not dict_search('translation.port', config): if 'exclude' not in config and 'backend' not in config['load_balance']: @@ -172,8 +175,11 @@ def verify(nat): err_msg = f'Destination NAT configuration error in rule {rule}:' if 'inbound_interface' in config: - if config['inbound_interface'] not in 'any' and config['inbound_interface'] not in interfaces(): - Warning(f'rule "{rule}" interface "{config["inbound_interface"]}" does not exist on this system') + if 'name' in config['inbound_interface'] and 'group' in config['inbound_interface']: + raise ConfigError(f'{err_msg} - Cannot specify both interface group and interface name for destination nat rule "{rule}"') + elif 'name' in config['inbound_interface']: + if config['inbound_interface']['name'] not in 'any' and config['inbound_interface']['name'] not in interfaces(): + Warning(f'{err_msg} - interface "{config["inbound_interface"]["name"]}" does not exist on this system') if not dict_search('translation.address', config) and not dict_search('translation.port', config) and 'redirect' not in config['translation']: if 'exclude' not in config and 'backend' not in config['load_balance']: diff --git a/src/conf_mode/nat66.py b/src/conf_mode/nat66.py index 46d796bc8..0ba08aef3 100755 --- a/src/conf_mode/nat66.py +++ b/src/conf_mode/nat66.py @@ -62,11 +62,13 @@ def verify(nat): if dict_search('source.rule', nat): for rule, config in dict_search('source.rule', nat).items(): err_msg = f'Source NAT66 configuration error in rule {rule}:' - if 'outbound_interface' not in config: - raise ConfigError(f'{err_msg} outbound-interface not specified') - if config['outbound_interface'] not in interfaces(): - raise ConfigError(f'rule "{rule}" interface "{config["outbound_interface"]}" does not exist on this system') + if 'outbound_interface' in config: + if 'name' in config['outbound_interface'] and 'group' in config['outbound_interface']: + raise ConfigError(f'{err_msg} - Cannot specify both interface group and interface name for nat source rule "{rule}"') + elif 'name' in config['outbound_interface']: + if config['outbound_interface']['name'] not in 'any' and config['outbound_interface']['name'] not in interfaces(): + Warning(f'{err_msg} - interface "{config["outbound_interface"]["name"]}" does not exist on this system') addr = dict_search('translation.address', config) if addr != None: @@ -85,12 +87,12 @@ def verify(nat): for rule, config in dict_search('destination.rule', nat).items(): err_msg = f'Destination NAT66 configuration error in rule {rule}:' - if 'inbound_interface' not in config: - raise ConfigError(f'{err_msg}\n' \ - 'inbound-interface not specified') - else: - if config['inbound_interface'] not in 'any' and config['inbound_interface'] not in interfaces(): - Warning(f'rule "{rule}" interface "{config["inbound_interface"]}" does not exist on this system') + if 'inbound_interface' in config: + if 'name' in config['inbound_interface'] and 'group' in config['inbound_interface']: + raise ConfigError(f'{err_msg} - Cannot specify both interface group and interface name for destination nat rule "{rule}"') + elif 'name' in config['inbound_interface']: + if config['inbound_interface']['name'] not in 'any' and config['inbound_interface']['name'] not in interfaces(): + Warning(f'{err_msg} - interface "{config["inbound_interface"]["name"]}" does not exist on this system') return None |