diff options
author | Christian Breunig <christian@breunig.cc> | 2024-01-22 07:56:32 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-01-22 07:56:32 +0100 |
commit | 39d0464616be1fc12be201223a84937b43c19382 (patch) | |
tree | 1143f24e0fbbb8026da16dc4092ff36d27435bf4 /src | |
parent | 4412d1ae8499de3a49d99c37f66b93c6b4693295 (diff) | |
parent | 2ec023752bdd400835eb69a8f1f9d2873cef61fa (diff) | |
download | vyos-1x-39d0464616be1fc12be201223a84937b43c19382.tar.gz vyos-1x-39d0464616be1fc12be201223a84937b43c19382.zip |
Merge pull request #2856 from c-po/firewall-backports
firewall: T5729: T5681: T5217: backport subsystem from current branch
Diffstat (limited to 'src')
-rwxr-xr-x | src/conf_mode/firewall.py | 31 | ||||
-rwxr-xr-x | src/conf_mode/load-balancing_wan.py | 5 | ||||
-rwxr-xr-x | src/conf_mode/nat.py | 92 | ||||
-rwxr-xr-x | src/conf_mode/nat66.py | 45 | ||||
-rwxr-xr-x | src/conf_mode/system_flow-accounting.py | 2 | ||||
-rwxr-xr-x | src/op_mode/firewall.py | 23 |
6 files changed, 58 insertions, 140 deletions
diff --git a/src/conf_mode/firewall.py b/src/conf_mode/firewall.py index ee19555c4..acb7dfa41 100755 --- a/src/conf_mode/firewall.py +++ b/src/conf_mode/firewall.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2021-2022 VyOS maintainers and contributors +# Copyright (C) 2021-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 @@ -39,6 +39,7 @@ from vyos.utils.process import process_named_running from vyos.utils.process import rc_cmd from vyos import ConfigError from vyos import airbag + airbag.enable() nftables_conf = '/run/nftables.conf' @@ -98,7 +99,7 @@ def geoip_updated(conf, firewall): elif (path[0] == 'ipv6'): set_name = f'GEOIP_CC6_{path[1]}_{path[2]}_{path[4]}' out['ipv6_name'].append(set_name) - + updated = True if 'delete' in node_diff: @@ -138,6 +139,8 @@ def get_config(config=None): fqdn_config_parse(firewall) + set_dependents('conntrack', conf) + return firewall def verify_rule(firewall, rule_conf, ipv6): @@ -167,6 +170,16 @@ def verify_rule(firewall, rule_conf, ipv6): if not dict_search_args(firewall, 'flowtable', offload_target): raise ConfigError(f'Invalid offload-target. Flowtable "{offload_target}" does not exist on the system') + if rule_conf['action'] != 'synproxy' and 'synproxy' in rule_conf: + raise ConfigError('"synproxy" option allowed only for action synproxy') + if rule_conf['action'] == 'synproxy': + if 'state' in rule_conf: + raise ConfigError('For action "synproxy" state cannot be defined') + if not rule_conf.get('synproxy', {}).get('tcp'): + raise ConfigError('synproxy TCP MSS is not defined') + if rule_conf.get('protocol', {}) != 'tcp': + raise ConfigError('For action "synproxy" the protocol must be set to TCP') + if 'queue_options' in rule_conf: if 'queue' not in rule_conf['action']: raise ConfigError('queue-options defined, but action queue needed and it is not defined') @@ -434,17 +447,6 @@ def generate(firewall): if local_zone in zone_conf['from']: local_zone_conf['from_local'][zone] = zone_conf['from'][local_zone] - # Determine if conntrack is needed - firewall['ipv4_conntrack_action'] = 'return' - firewall['ipv6_conntrack_action'] = 'return' - - for rules, path in dict_search_recursive(firewall, 'rule'): - if any(('state' in rule_conf or 'connection_status' in rule_conf) for rule_conf in rules.values()): - if path[0] == 'ipv4': - firewall['ipv4_conntrack_action'] = 'accept' - elif path[0] == 'ipv6': - firewall['ipv6_conntrack_action'] = 'accept' - render(nftables_conf, 'firewall/nftables.j2', firewall) return None @@ -474,8 +476,7 @@ def apply(firewall): apply_sysfs(firewall) - if firewall['group_resync']: - call_dependents() + call_dependents() # T970 Enable a resolver (systemd daemon) that checks # domain-group/fqdn addresses and update entries for domains by timeout diff --git a/src/conf_mode/load-balancing_wan.py b/src/conf_mode/load-balancing_wan.py index ad9c80d72..5da0b906b 100755 --- a/src/conf_mode/load-balancing_wan.py +++ b/src/conf_mode/load-balancing_wan.py @@ -21,6 +21,7 @@ from shutil import rmtree from vyos.base import Warning from vyos.config import Config +from vyos.configdep import set_dependents, call_dependents from vyos.utils.process import cmd from vyos.template import render from vyos import ConfigError @@ -49,6 +50,8 @@ def get_config(config=None): if lb.from_defaults(['rule', rule, 'limit']): del lb['rule'][rule]['limit'] + set_dependents('conntrack', conf) + return lb @@ -132,6 +135,8 @@ def apply(lb): cmd('sudo sysctl -w net.netfilter.nf_conntrack_acct=1') cmd(f'systemctl restart {systemd_service}') + call_dependents() + return None diff --git a/src/conf_mode/nat.py b/src/conf_mode/nat.py index ffd4a33e7..bd9b5162c 100755 --- a/src/conf_mode/nat.py +++ b/src/conf_mode/nat.py @@ -18,13 +18,12 @@ import jmespath import json import os -from distutils.version import LooseVersion -from platform import release as kernel_version from sys import exit from netifaces import interfaces from vyos.base import Warning from vyos.config import Config +from vyos.configdep import set_dependents, call_dependents from vyos.template import render from vyos.template import is_ip_network from vyos.utils.kernel import check_kmod @@ -38,10 +37,7 @@ from vyos import ConfigError from vyos import airbag airbag.enable() -if LooseVersion(kernel_version()) > LooseVersion('5.1'): - k_mod = ['nft_nat', 'nft_chain_nat'] -else: - k_mod = ['nft_nat', 'nft_chain_nat_ipv4'] +k_mod = ['nft_nat', 'nft_chain_nat'] nftables_nat_config = '/run/nftables_nat.conf' nftables_static_nat_conf = '/run/nftables_static-nat-rules.nft' @@ -53,18 +49,27 @@ valid_groups = [ 'port_group' ] -def get_handler(json, chain, target): - """ Get nftable rule handler number of given chain/target combination. - Handler is required when adding NAT/Conntrack helper targets """ - for x in json: - if x['chain'] != chain: - continue - if x['target'] != target: - continue - return x['handle'] +def get_config(config=None): + if config: + conf = config + else: + conf = Config() - return None + base = ['nat'] + nat = conf.get_config_dict(base, key_mangling=('-', '_'), + get_first_key=True, + with_recursive_defaults=True) + set_dependents('conntrack', conf) + + if not conf.exists(base): + nat['deleted'] = '' + return nat + + nat['firewall_group'] = conf.get_config_dict(['firewall', 'group'], key_mangling=('-', '_'), get_first_key=True, + no_tag_node_value_mangle=True) + + return nat def verify_rule(config, err_msg, groups_dict): """ Common verify steps used for both source and destination NAT """ @@ -105,7 +110,7 @@ def verify_rule(config, err_msg, groups_dict): group_obj = dict_search_args(groups_dict, group, group_name) if group_obj is None: - raise ConfigError(f'Invalid {error_group} "{group_name}" on firewall rule') + raise ConfigError(f'Invalid {error_group} "{group_name}" on nat rule') if not group_obj: Warning(f'{error_group} "{group_name}" has no members!') @@ -129,62 +134,11 @@ def verify_rule(config, err_msg, groups_dict): if count != 100: Warning(f'Sum of weight for nat load balance rule is not 100. You may get unexpected behaviour') -def get_config(config=None): - if config: - conf = config - else: - conf = Config() - - base = ['nat'] - nat = conf.get_config_dict(base, key_mangling=('-', '_'), - get_first_key=True, - with_recursive_defaults=True) - - # read in current nftable (once) for further processing - tmp = cmd('nft -j list table raw') - nftable_json = json.loads(tmp) - - # condense the full JSON table into a list with only relevand informations - pattern = 'nftables[?rule].rule[?expr[].jump].{chain: chain, handle: handle, target: expr[].jump.target | [0]}' - condensed_json = jmespath.search(pattern, nftable_json) - - if not conf.exists(base): - if get_handler(condensed_json, 'PREROUTING', 'VYOS_CT_HELPER'): - nat['helper_functions'] = 'remove' - - # Retrieve current table handler positions - nat['pre_ct_ignore'] = get_handler(condensed_json, 'PREROUTING', 'VYOS_CT_HELPER') - nat['pre_ct_conntrack'] = get_handler(condensed_json, 'PREROUTING', 'NAT_CONNTRACK') - nat['out_ct_ignore'] = get_handler(condensed_json, 'OUTPUT', 'VYOS_CT_HELPER') - nat['out_ct_conntrack'] = get_handler(condensed_json, 'OUTPUT', 'NAT_CONNTRACK') - nat['deleted'] = '' - return nat - - nat['firewall_group'] = conf.get_config_dict(['firewall', 'group'], key_mangling=('-', '_'), get_first_key=True, - no_tag_node_value_mangle=True) - - # check if NAT connection tracking helpers need to be set up - this has to - # be done only once - if not get_handler(condensed_json, 'PREROUTING', 'NAT_CONNTRACK'): - nat['helper_functions'] = 'add' - - # Retrieve current table handler positions - nat['pre_ct_ignore'] = get_handler(condensed_json, 'PREROUTING', 'VYOS_CT_IGNORE') - nat['pre_ct_conntrack'] = get_handler(condensed_json, 'PREROUTING', 'VYOS_CT_PREROUTING_HOOK') - nat['out_ct_ignore'] = get_handler(condensed_json, 'OUTPUT', 'VYOS_CT_IGNORE') - nat['out_ct_conntrack'] = get_handler(condensed_json, 'OUTPUT', 'VYOS_CT_OUTPUT_HOOK') - - return nat - def verify(nat): if not nat or 'deleted' in nat: # no need to verify the CLI as NAT is going to be deactivated return None - if 'helper_functions' in nat: - if not (nat['pre_ct_ignore'] or nat['pre_ct_conntrack'] or nat['out_ct_ignore'] or nat['out_ct_conntrack']): - raise Exception('could not determine nftable ruleset handlers') - if dict_search('source.rule', nat): for rule, config in dict_search('source.rule', nat).items(): err_msg = f'Source NAT configuration error in rule {rule}:' @@ -265,6 +219,8 @@ def apply(nat): os.unlink(nftables_nat_config) os.unlink(nftables_static_nat_conf) + call_dependents() + return None if __name__ == '__main__': diff --git a/src/conf_mode/nat66.py b/src/conf_mode/nat66.py index ed716b2a2..4c1ead258 100755 --- a/src/conf_mode/nat66.py +++ b/src/conf_mode/nat66.py @@ -23,6 +23,7 @@ from netifaces import interfaces from vyos.base import Warning from vyos.config import Config +from vyos.configdep import set_dependents, call_dependents from vyos.template import render from vyos.utils.process import cmd from vyos.utils.kernel import check_kmod @@ -36,18 +37,6 @@ k_mod = ['nft_nat', 'nft_chain_nat'] nftables_nat66_config = '/run/nftables_nat66.nft' -def get_handler(json, chain, target): - """ Get nftable rule handler number of given chain/target combination. - Handler is required when adding NAT66/Conntrack helper targets """ - for x in json: - if x['chain'] != chain: - continue - if x['target'] != target: - continue - return x['handle'] - - return None - def get_config(config=None): if config: conf = config @@ -57,35 +46,10 @@ def get_config(config=None): base = ['nat66'] nat = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True) - # read in current nftable (once) for further processing - tmp = cmd('nft -j list table ip6 raw') - nftable_json = json.loads(tmp) - - # condense the full JSON table into a list with only relevand informations - pattern = 'nftables[?rule].rule[?expr[].jump].{chain: chain, handle: handle, target: expr[].jump.target | [0]}' - condensed_json = jmespath.search(pattern, nftable_json) + set_dependents('conntrack', conf) if not conf.exists(base): - nat['helper_functions'] = 'remove' - nat['pre_ct_ignore'] = get_handler(condensed_json, 'PREROUTING', 'VYOS_CT_HELPER') - nat['pre_ct_conntrack'] = get_handler(condensed_json, 'PREROUTING', 'NAT_CONNTRACK') - nat['out_ct_ignore'] = get_handler(condensed_json, 'OUTPUT', 'VYOS_CT_HELPER') - nat['out_ct_conntrack'] = get_handler(condensed_json, 'OUTPUT', 'NAT_CONNTRACK') nat['deleted'] = '' - return nat - - # check if NAT66 connection tracking helpers need to be set up - this has to - # be done only once - if not get_handler(condensed_json, 'PREROUTING', 'NAT_CONNTRACK'): - nat['helper_functions'] = 'add' - - # Retrieve current table handler positions - nat['pre_ct_ignore'] = get_handler(condensed_json, 'PREROUTING', 'VYOS_CT_IGNORE') - nat['pre_ct_conntrack'] = get_handler(condensed_json, 'PREROUTING', 'VYOS_CT_PREROUTING_HOOK') - nat['out_ct_ignore'] = get_handler(condensed_json, 'OUTPUT', 'VYOS_CT_IGNORE') - nat['out_ct_conntrack'] = get_handler(condensed_json, 'OUTPUT', 'VYOS_CT_OUTPUT_HOOK') - else: - nat['helper_functions'] = 'has' return nat @@ -94,10 +58,6 @@ def verify(nat): # no need to verify the CLI as NAT66 is going to be deactivated return None - if 'helper_functions' in nat and nat['helper_functions'] != 'has': - if not (nat['pre_ct_conntrack'] or nat['out_ct_conntrack']): - raise Exception('could not determine nftable ruleset handlers') - 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}:' @@ -147,6 +107,7 @@ def apply(nat): return None cmd(f'nft -f {nftables_nat66_config}') + call_dependents() return None diff --git a/src/conf_mode/system_flow-accounting.py b/src/conf_mode/system_flow-accounting.py index f29fc94fb..206f513c8 100755 --- a/src/conf_mode/system_flow-accounting.py +++ b/src/conf_mode/system_flow-accounting.py @@ -38,7 +38,7 @@ uacctd_conf_path = '/run/pmacct/uacctd.conf' systemd_service = 'uacctd.service' systemd_override = f'/run/systemd/system/{systemd_service}.d/override.conf' nftables_nflog_table = 'raw' -nftables_nflog_chain = 'VYOS_CT_PREROUTING_HOOK' +nftables_nflog_chain = 'VYOS_PREROUTING_HOOK' egress_nftables_nflog_table = 'inet mangle' egress_nftables_nflog_chain = 'FORWARD' diff --git a/src/op_mode/firewall.py b/src/op_mode/firewall.py index d426b62e5..36bb013fe 100755 --- a/src/op_mode/firewall.py +++ b/src/op_mode/firewall.py @@ -113,19 +113,14 @@ def output_firewall_name(family, hook, priority, firewall_conf, single_rule_id=N if hook in ['input', 'forward', 'output']: def_action = firewall_conf['default_action'] if 'default_action' in firewall_conf else 'accept' - row = ['default', def_action, 'all'] - rule_details = details['default-action'] - row.append(rule_details.get('packets', 0)) - row.append(rule_details.get('bytes', 0)) - rows.append(row) + else: + def_action = firewall_conf['default_action'] if 'default_action' in firewall_conf else 'drop' + row = ['default', def_action, 'all'] + rule_details = details['default-action'] + row.append(rule_details.get('packets', 0)) + row.append(rule_details.get('bytes', 0)) - elif 'default_action' in firewall_conf and not single_rule_id: - row = ['default', firewall_conf['default_action'], 'all'] - if 'default-action' in details: - rule_details = details['default-action'] - row.append(rule_details.get('packets', 0)) - row.append(rule_details.get('bytes', 0)) - rows.append(row) + rows.append(row) if rows: header = ['Rule', 'Action', 'Protocol', 'Packets', 'Bytes', 'Conditions'] @@ -314,7 +309,7 @@ def show_firewall_group(name=None): family = ['ipv6'] group_type = 'network_group' else: - family = ['ipv4', 'ipv6'] + family = ['ipv4', 'ipv6', 'bridge'] for item in family: # Look references in firewall @@ -540,4 +535,4 @@ if __name__ == '__main__': elif args.action == 'show_statistics': show_statistics() elif args.action == 'show_summary': - show_summary()
\ No newline at end of file + show_summary() |