diff options
| author | sarthurdev <965089+sarthurdev@users.noreply.github.com> | 2022-09-05 14:43:08 +0200 | 
|---|---|---|
| committer | sarthurdev <965089+sarthurdev@users.noreply.github.com> | 2022-09-13 11:59:12 +0200 | 
| commit | 30945f39d6d1f0fdba34ce1c2d887a1a6823ecbe (patch) | |
| tree | ca9b4d04f2245870a44ee73592ee0e1d7b85c40d /src | |
| parent | 24e5529be7b5a28868a666dabae0a1d61e6a5c15 (diff) | |
| download | vyos-1x-30945f39d6d1f0fdba34ce1c2d887a1a6823ecbe.tar.gz vyos-1x-30945f39d6d1f0fdba34ce1c2d887a1a6823ecbe.zip | |
zone-policy: T2199: Migrate zone-policy to firewall node
Diffstat (limited to 'src')
| -rwxr-xr-x | src/conf_mode/firewall.py | 117 | ||||
| -rwxr-xr-x | src/conf_mode/zone_policy.py | 192 | ||||
| -rwxr-xr-x | src/migration-scripts/firewall/7-to-8 | 12 | 
3 files changed, 85 insertions, 236 deletions
| diff --git a/src/conf_mode/firewall.py b/src/conf_mode/firewall.py index f8ad1f798..eeb57bd30 100755 --- a/src/conf_mode/firewall.py +++ b/src/conf_mode/firewall.py @@ -48,8 +48,6 @@ airbag.enable()  policy_route_conf_script = '/usr/libexec/vyos/conf_mode/policy-route.py'  nftables_conf = '/run/nftables.conf' -nftables_zone_conf = '/run/nftables_zone.conf' -nftables6_zone_conf = '/run/nftables_zone6.conf'  sysfs_config = {      'all_ping': {'sysfs': '/proc/sys/net/ipv4/icmp_echo_ignore_all', 'enable': '0', 'disable': '1'}, @@ -87,37 +85,6 @@ snmp_event_source = 1  snmp_trap_mib = 'VYATTA-TRAP-MIB'  snmp_trap_name = 'mgmtEventTrap' -def get_firewall_zones(conf): -    used_v4 = [] -    used_v6 = [] -    zone_policy = conf.get_config_dict(['zone-policy'], key_mangling=('-', '_'), get_first_key=True, -                                    no_tag_node_value_mangle=True) - -    if 'zone' in zone_policy: -        for zone, zone_conf in zone_policy['zone'].items(): -            if 'from' in zone_conf: -                for from_zone, from_conf in zone_conf['from'].items(): -                    name = dict_search_args(from_conf, 'firewall', 'name') -                    if name: -                        used_v4.append(name) - -                    ipv6_name = dict_search_args(from_conf, 'firewall', 'ipv6_name') -                    if ipv6_name: -                        used_v6.append(ipv6_name) - -            if 'intra_zone_filtering' in zone_conf: -                name = dict_search_args(zone_conf, 'intra_zone_filtering', 'firewall', 'name') -                if name: -                    used_v4.append(name) - -                ipv6_name = dict_search_args(zone_conf, 'intra_zone_filtering', 'firewall', 'ipv6_name') -                if ipv6_name: -                    used_v6.append(ipv6_name) -    else: -        return None - -    return {'name': used_v4, 'ipv6_name': used_v6} -  def geoip_updated(conf, firewall):      diff = get_config_diff(conf)      node_diff = diff.get_child_nodes_diff(['firewall'], expand_nodes=Diff.DELETE, recursive=True) @@ -171,6 +138,9 @@ def get_config(config=None):          if tmp in default_values:              del default_values[tmp] +    if 'zone' in default_values: +        del default_values['zone'] +      firewall = dict_merge(default_values, firewall)      # Merge in defaults for IPv4 ruleset @@ -187,8 +157,12 @@ def get_config(config=None):              firewall['ipv6_name'][ipv6_name] = dict_merge(default_values,                                                            firewall['ipv6_name'][ipv6_name]) +    if 'zone' in firewall: +        default_values = defaults(base + ['zone']) +        for zone in firewall['zone']: +            firewall['zone'][zone] = dict_merge(default_values, firewall['zone'][zone]) +      firewall['policy_resync'] = bool('group' in firewall or node_changed(conf, base + ['group'])) -    firewall['zone_policy'] = get_firewall_zones(conf)      if 'config_trap' in firewall and firewall['config_trap'] == 'enable':          diff = get_config_diff(conf) @@ -332,11 +306,61 @@ def verify(firewall):                  if ipv6_name and dict_search_args(firewall, 'ipv6_name', ipv6_name) == None:                      raise ConfigError(f'Invalid firewall ipv6-name "{ipv6_name}" referenced on interface {ifname}') -    if firewall['zone_policy']: -        for fw_name, used_names in firewall['zone_policy'].items(): -            for name in used_names: -                if dict_search_args(firewall, fw_name, name) == None: -                    raise ConfigError(f'Firewall {fw_name.replace("_", "-")} "{name}" is still referenced in zone-policy') +    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, '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, '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 @@ -344,11 +368,18 @@ def generate(firewall):      if not os.path.exists(nftables_conf):          firewall['first_install'] = True -    if os.path.exists(nftables_zone_conf): -        firewall['zone_conf'] = nftables_zone_conf +    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'] = {} -    if os.path.exists(nftables6_zone_conf): -        firewall['zone6_conf'] = nftables6_zone_conf +            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/zone_policy.py b/src/conf_mode/zone_policy.py deleted file mode 100755 index c6ab4e304..000000000 --- a/src/conf_mode/zone_policy.py +++ /dev/null @@ -1,192 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (C) 2021-2022 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 -# published by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# 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 json import loads -from sys import exit - -from vyos.config import Config -from vyos.configdict import dict_merge -from vyos.configdiff import get_config_diff -from vyos.template import render -from vyos.util import cmd -from vyos.util import dict_search_args -from vyos.util import run -from vyos.xml import defaults -from vyos import ConfigError -from vyos import airbag -airbag.enable() - -firewall_conf_script = '/usr/libexec/vyos/conf_mode/firewall.py' -nftables_conf = '/run/nftables_zone.conf' -nftables6_conf = '/run/nftables_zone6.conf' - -def get_config(config=None): -    if config: -        conf = config -    else: -        conf = Config() -    base = ['zone-policy'] -    zone_policy = conf.get_config_dict(base, key_mangling=('-', '_'), -                                       get_first_key=True, -                                       no_tag_node_value_mangle=True) - -    zone_policy['firewall'] = conf.get_config_dict(['firewall'], -                                                   key_mangling=('-', '_'), -                                                   get_first_key=True, -                                                   no_tag_node_value_mangle=True) - -    diff = get_config_diff(conf) -    zone_policy['firewall_changed'] = diff.is_node_changed(['firewall']) - -    if 'zone' in zone_policy: -        # We have gathered the dict representation of the CLI, but there are default -        # options which we need to update into the dictionary retrived. -        default_values = defaults(base + ['zone']) -        for zone in zone_policy['zone']: -            zone_policy['zone'][zone] = dict_merge(default_values, -                                                   zone_policy['zone'][zone]) - -    return zone_policy - -def verify(zone_policy): -    # bail out early - looks like removal from running config -    if not zone_policy: -        return None - -    local_zone = False -    interfaces = [] - -    if 'zone' in zone_policy: -        for zone, zone_conf in zone_policy['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 interfaces] - -                if found_duplicates: -                    raise ConfigError(f'Interfaces cannot be assigned to multiple zones') - -                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(zone_policy, 'firewall', '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(zone_policy, '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 zone_policy['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(zone_policy, 'firewall', 'name', v4_name): -                        raise ConfigError(f'Firewall name "{v4_name}" does not exist') - -                    v6_name = dict_search_args(from_conf, 'firewall', 'v6_name') -                    if v6_name and not dict_search_args(zone_policy, 'firewall', 'ipv6_name', v6_name): -                        raise ConfigError(f'Firewall ipv6-name "{v6_name}" does not exist') - -    return None - -def has_ipv4_fw(zone_conf): -    if 'from' not in zone_conf: -        return False -    zone_from = zone_conf['from'] -    return any([True for fz in zone_from if dict_search_args(zone_from, fz, 'firewall', 'name')]) - -def has_ipv6_fw(zone_conf): -    if 'from' not in zone_conf: -        return False -    zone_from = zone_conf['from'] -    return any([True for fz in zone_from if dict_search_args(zone_from, fz, 'firewall', 'ipv6_name')]) - -def get_local_from(zone_policy, local_zone_name): -    # Get all zone firewall names from the local zone -    out = {} -    for zone, zone_conf in zone_policy['zone'].items(): -        if zone == local_zone_name: -            continue -        if 'from' not in zone_conf: -            continue -        if local_zone_name in zone_conf['from']: -            out[zone] = zone_conf['from'][local_zone_name] -    return out - -def generate(zone_policy): -    data = zone_policy or {} - -    if not os.path.exists(nftables_conf): -        data['first_install'] = True - -    if 'zone' in data: -        for zone, zone_conf in data['zone'].items(): -            zone_conf['ipv4'] = has_ipv4_fw(zone_conf) -            zone_conf['ipv6'] = has_ipv6_fw(zone_conf) - -            if 'local_zone' in zone_conf: -                zone_conf['from_local'] = get_local_from(data, zone) - -    render(nftables_conf, 'zone_policy/nftables.j2', data) -    render(nftables6_conf, 'zone_policy/nftables6.j2', data) -    return None - -def update_firewall(): -    # Update firewall to refresh nftables -    tmp = run(firewall_conf_script) -    if tmp > 0: -        Warning('Failed to update firewall configuration!') - -def apply(zone_policy): -    # If firewall will not update in this commit, we need to call the conf script -    if not zone_policy['firewall_changed']: -        update_firewall() - -    return None - -if __name__ == '__main__': -    try: -        c = get_config() -        verify(c) -        generate(c) -        apply(c) -    except ConfigError as e: -        print(e) -        exit(1) diff --git a/src/migration-scripts/firewall/7-to-8 b/src/migration-scripts/firewall/7-to-8 index 6929e20a5..ce527acf5 100755 --- a/src/migration-scripts/firewall/7-to-8 +++ b/src/migration-scripts/firewall/7-to-8 @@ -15,6 +15,7 @@  # along with this program.  If not, see <http://www.gnu.org/licenses/>.  # T2199: Migrate interface firewall nodes to firewall interfaces <ifname> <direction> name/ipv6-name <name> +# T2199: Migrate zone-policy to firewall node  import re @@ -34,9 +35,10 @@ with open(file_name, 'r') as f:      config_file = f.read()  base = ['firewall'] +zone_base = ['zone-policy']  config = ConfigTree(config_file) -if not config.exists(base): +if not config.exists(base) and not config.exists(zone_base):      # Nothing to do      exit(0) @@ -80,6 +82,14 @@ for iftype in config.list_nodes(['interfaces']):                      for vifc in config.list_nodes(['interfaces', iftype, ifname, 'vif-s', vifs, 'vif-c']):                          migrate_interface(config, iftype, ifname, vifs=vifs, vifc=vifc) +if config.exists(zone_base + ['zone']): +    config.set(['firewall', 'zone']) +    config.set_tag(['firewall', 'zone']) + +    for zone in config.list_nodes(zone_base + ['zone']): +        config.copy(zone_base + ['zone', zone], ['firewall', 'zone', zone]) +    config.delete(zone_base) +  try:      with open(file_name, 'w') as f:          f.write(config.to_string()) | 
