diff options
| author | Christian Breunig <christian@breunig.cc> | 2023-10-25 20:30:08 +0200 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-10-25 20:30:08 +0200 | 
| commit | ef55eab3c7cdac7b8febca9af15b5118aa475c45 (patch) | |
| tree | cdd435b79159542d8ccbfe3d3fdfe8a16356c3ee /src | |
| parent | 73eb7777a5d3a3bce48719f651c6b0f3a1c1a79d (diff) | |
| parent | 51abbc0f1b2ccf4785cf7f29f1fe6f4af6007ee6 (diff) | |
| download | vyos-1x-ef55eab3c7cdac7b8febca9af15b5118aa475c45.tar.gz vyos-1x-ef55eab3c7cdac7b8febca9af15b5118aa475c45.zip | |
Merge pull request #2406 from nicolas-fort/T5681
T5681: Firewall,Nat and Nat66: simplified and standarize interface matcher
Diffstat (limited to 'src')
| -rwxr-xr-x | src/conf_mode/firewall.py | 4 | ||||
| -rwxr-xr-x | src/conf_mode/nat.py | 20 | ||||
| -rwxr-xr-x | src/conf_mode/nat66.py | 22 | ||||
| -rwxr-xr-x | src/migration-scripts/firewall/11-to-12 | 75 | ||||
| -rwxr-xr-x | src/migration-scripts/nat/6-to-7 | 67 | ||||
| -rwxr-xr-x | src/migration-scripts/nat66/1-to-2 | 63 | 
6 files changed, 229 insertions, 22 deletions
| diff --git a/src/conf_mode/firewall.py b/src/conf_mode/firewall.py index 9791cf009..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: diff --git a/src/conf_mode/nat.py b/src/conf_mode/nat.py index cb97a8662..44b13d413 100755 --- a/src/conf_mode/nat.py +++ b/src/conf_mode/nat.py @@ -151,11 +151,11 @@ def verify(nat):              err_msg = f'Source NAT configuration error in rule {rule}:'              if 'outbound_interface' in config: -                if 'interface_name' in config['outbound_interface'] and 'interface_group' in config['outbound_interface']: -                    raise ConfigError(f'Cannot specify both interface-group and interface-name for nat source rule "{rule}"') -                elif 'interface_name' in config['outbound_interface']: -                    if config['outbound_interface']['interface_name'] not in 'any' and config['outbound_interface']['interface_name'] not in interfaces(): -                        Warning(f'rule "{rule}" interface "{config["outbound_interface"]["interface_name"]}" 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']: @@ -175,11 +175,11 @@ def verify(nat):              err_msg = f'Destination NAT configuration error in rule {rule}:'              if 'inbound_interface' in config: -                if 'interface_name' in config['inbound_interface'] and 'interface_group' in config['inbound_interface']: -                    raise ConfigError(f'Cannot specify both interface-group and interface-name for destination nat rule "{rule}"') -                elif 'interface_name' in config['inbound_interface']: -                    if config['inbound_interface']['interface_name'] not in 'any' and config['inbound_interface']['interface_name'] not in interfaces(): -                        Warning(f'rule "{rule}" interface "{config["inbound_interface"]["interface_name"]}" 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 diff --git a/src/migration-scripts/firewall/11-to-12 b/src/migration-scripts/firewall/11-to-12 new file mode 100755 index 000000000..51b2fa860 --- /dev/null +++ b/src/migration-scripts/firewall/11-to-12 @@ -0,0 +1,75 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2023 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/>. + +# T5681: Firewall re-writing. Simplify cli when mathcing interface +# From +    # set firewall ... rule <rule> [inbound-interface | outboubd-interface] interface-name <iface> +    # set firewall ... rule <rule> [inbound-interface | outboubd-interface] interface-group <iface_group> +# To +    # set firewall ... rule <rule> [inbound-interface | outboubd-interface] name <iface> +    # set firewall ... rule <rule> [inbound-interface | outboubd-interface] group <iface_group> + +import re + +from sys import argv +from sys import exit + +from vyos.configtree import ConfigTree +from vyos.ifconfig import Section + +if len(argv) < 2: +    print("Must specify file name!") +    exit(1) + +file_name = argv[1] + +with open(file_name, 'r') as f: +    config_file = f.read() + +base = ['firewall'] +config = ConfigTree(config_file) + +if not config.exists(base): +    # Nothing to do +    exit(0) + +## FORT +## Migration from base chains +#if config.exists(base + ['interface', iface, direction]): +for family in ['ipv4', 'ipv6']: +    if config.exists(base + [family]): +        for hook in ['forward', 'input', 'output', 'name']: +            if config.exists(base + [family, hook]): +                for priority in config.list_nodes(base + [family, hook]): +                    if config.exists(base + [family, hook, priority, 'rule']): +                        for rule in config.list_nodes(base + [family, hook, priority, 'rule']): +                            for direction in ['inbound-interface', 'outbound-interface']: +                                if config.exists(base + [family, hook, priority, 'rule', rule, direction]): +                                    if config.exists(base + [family, hook, priority, 'rule', rule, direction, 'interface-name']): +                                        iface = config.return_value(base + [family, hook, priority, 'rule', rule, direction, 'interface-name']) +                                        config.set(base + [family, hook, priority, 'rule', rule, direction, 'name'], value=iface) +                                        config.delete(base + [family, hook, priority, 'rule', rule, direction, 'interface-name']) +                                    elif config.exists(base + [family, hook, priority, 'rule', rule, direction, 'interface-group']): +                                        group = config.return_value(base + [family, hook, priority, 'rule', rule, direction, 'interface-group']) +                                        config.set(base + [family, hook, priority, 'rule', rule, direction, 'group'], value=group) +                                        config.delete(base + [family, hook, priority, 'rule', rule, direction, 'interface-group']) + +try: +    with open(file_name, 'w') as f: +        f.write(config.to_string()) +except OSError as e: +    print("Failed to save the modified config: {}".format(e)) +    exit(1)
\ No newline at end of file diff --git a/src/migration-scripts/nat/6-to-7 b/src/migration-scripts/nat/6-to-7 new file mode 100755 index 000000000..b5f6328ef --- /dev/null +++ b/src/migration-scripts/nat/6-to-7 @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2023 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/>. + +# T5681: Firewall re-writing. Simplify cli when mathcing interface +# From +#   'set nat [source|destination] rule X [inbound-interface|outbound interface] interface-name <iface>' +#   'set nat [source|destination] rule X [inbound-interface|outbound interface] interface-group <iface_group>' +# to +#   'set nat [source|destination] rule X [inbound-interface|outbound interface] name <iface>' +#   'set nat [source|destination] rule X [inbound-interface|outbound interface] group <iface_group>' + +from sys import argv,exit +from vyos.configtree import ConfigTree + +if len(argv) < 2: +    print("Must specify file name!") +    exit(1) + +file_name = argv[1] + +with open(file_name, 'r') as f: +    config_file = f.read() + +config = ConfigTree(config_file) + +if not config.exists(['nat']): +    # Nothing to do +    exit(0) + +for direction in ['source', 'destination']: +    # If a node doesn't exist, we obviously have nothing to do. +    if not config.exists(['nat', direction]): +        continue + +    # However, we also need to handle the case when a 'source' or 'destination' sub-node does exist, +    # but there are no rules under it. +    if not config.list_nodes(['nat', direction]): +        continue + +    for rule in config.list_nodes(['nat', direction, 'rule']): +        base = ['nat', direction, 'rule', rule] +        for iface in ['inbound-interface','outbound-interface']: +            if config.exists(base + [iface]): +                if config.exists(base + [iface, 'interface-name']): +                    tmp = config.return_value(base + [iface, 'interface-name']) +                    config.delete(base + [iface, 'interface-name']) +                    config.set(base + [iface, 'name'], value=tmp) + +try: +    with open(file_name, 'w') as f: +        f.write(config.to_string()) +except OSError as e: +    print("Failed to save the modified config: {}".format(e)) +    exit(1) diff --git a/src/migration-scripts/nat66/1-to-2 b/src/migration-scripts/nat66/1-to-2 new file mode 100755 index 000000000..b7d4e3f6b --- /dev/null +++ b/src/migration-scripts/nat66/1-to-2 @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2023 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/>. + +# T5681: Firewall re-writing. Simplify cli when mathcing interface +# From +#   'set nat66 [source|destination] rule X [inbound-interface|outbound interface] <iface>' +# to +#   'set nat66 [source|destination] rule X [inbound-interface|outbound interface] name <iface>' + +from sys import argv,exit +from vyos.configtree import ConfigTree + +if len(argv) < 2: +    print("Must specify file name!") +    exit(1) + +file_name = argv[1] + +with open(file_name, 'r') as f: +    config_file = f.read() + +config = ConfigTree(config_file) +if not config.exists(['nat66']): +    # Nothing to do +    exit(0) + +for direction in ['source', 'destination']: +    # If a node doesn't exist, we obviously have nothing to do. +    if not config.exists(['nat66', direction]): +        continue + +    # However, we also need to handle the case when a 'source' or 'destination' sub-node does exist, +    # but there are no rules under it. +    if not config.list_nodes(['nat66', direction]): +        continue + +    for rule in config.list_nodes(['nat66', direction, 'rule']): +        base = ['nat66', direction, 'rule', rule] +        for iface in ['inbound-interface','outbound-interface']: +            if config.exists(base + [iface]): +                tmp = config.return_value(base + [iface]) +                config.delete(base + [iface]) +                config.set(base + [iface, 'name'], value=tmp) + +try: +    with open(file_name, 'w') as f: +        f.write(config.to_string()) +except OSError as e: +    print("Failed to save the modified config: {}".format(e)) +    exit(1) | 
