diff options
| -rw-r--r-- | data/templates/firewall/nftables-policy.j2 | 4 | ||||
| -rw-r--r-- | data/templates/firewall/nftables.j2 | 361 | ||||
| -rw-r--r-- | python/vyos/firewall.py | 67 | ||||
| -rw-r--r-- | python/vyos/template.py | 8 | ||||
| -rwxr-xr-x | src/conf_mode/firewall.py | 174 | 
5 files changed, 325 insertions, 289 deletions
| diff --git a/data/templates/firewall/nftables-policy.j2 b/data/templates/firewall/nftables-policy.j2 index 1c9bda64f..699349e2b 100644 --- a/data/templates/firewall/nftables-policy.j2 +++ b/data/templates/firewall/nftables-policy.j2 @@ -25,7 +25,7 @@ table ip vyos_mangle {      chain VYOS_PBR_UD_{{ route_text }} {  {%         if conf.rule is vyos_defined %}  {%             for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %} -        {{ rule_conf | nft_rule(route_text, rule_id, 'ip') }} +        {{ rule_conf | nft_rule('route', route_text, rule_id, 'ip') }}  {%             endfor %}  {%         endif %}      } @@ -54,7 +54,7 @@ table ip6 vyos_mangle {      chain VYOS_PBR6_UD_{{ route_text }} {  {%         if conf.rule is vyos_defined %}  {%             for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %} -        {{ rule_conf | nft_rule(route_text, rule_id, 'ip6') }} +        {{ rule_conf | nft_rule('route6', route_text, rule_id, 'ip6') }}  {%             endfor %}  {%         endif %}      } diff --git a/data/templates/firewall/nftables.j2 b/data/templates/firewall/nftables.j2 index 2c7115134..dcfe71a58 100644 --- a/data/templates/firewall/nftables.j2 +++ b/data/templates/firewall/nftables.j2 @@ -7,86 +7,147 @@  delete table ip vyos_filter  {% endif %}  table ip vyos_filter { -    chain VYOS_FW_FORWARD { -        type filter hook forward priority 0; policy accept; -{% if state_policy is vyos_defined %} -        jump VYOS_STATE_POLICY -{% endif %} -{% if interface is vyos_defined %} -{%     for ifname, ifconf in interface.items() %} -{%         if ifconf.in is vyos_defined and ifconf.in.name is vyos_defined %} -        iifname {{ ifname }} counter jump NAME_{{ ifconf.in.name }} -{%         endif %} -{%         if ifconf.out is vyos_defined and ifconf.out.name is vyos_defined %} -        oifname {{ ifname }} counter jump NAME_{{ ifconf.out.name }} -{%         endif %} -{%     endfor %} -{% endif %} -        jump VYOS_POST_FW +{% if ip is vyos_defined %} +{%     if ip.forward is vyos_defined %} +{%         set ns = namespace(sets=[]) %} +{%         for prior, conf in ip.forward.items() %} +{%             set def_action = conf.default_action %} +    chain VYOS_FORWARD_{{ prior }} { +        type filter hook forward priority {{ prior }}; policy {{ def_action }}; +{%             if conf.rule is vyos_defined %} +{%                 for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %} +        {{ rule_conf | nft_rule('FWD', prior, rule_id) }} +{%                     if rule_conf.recent is vyos_defined %} +{%                         set ns.sets = ns.sets + ['FWD_' + prior + '_' + rule_id] %} +{%                     endif %} +{%                 endfor %} +{%             endif %}      } -    chain VYOS_FW_LOCAL { -        type filter hook input priority 0; policy accept; -{% if state_policy is vyos_defined %} -        jump VYOS_STATE_POLICY -{% endif %} -{% if interface is vyos_defined %} -{%     for ifname, ifconf in interface.items() %} -{%         if ifconf.local is vyos_defined and ifconf.local.name is vyos_defined %} -        iifname {{ ifname }} counter jump NAME_{{ ifconf.local.name }} -{%         endif %} -{%     endfor %} -{% endif %} -        jump VYOS_POST_FW +{%         endfor %} +{%         for set_name in ns.sets %} +    set RECENT_{{ set_name }} { +        type ipv4_addr +        size 65535 +        flags dynamic      } -    chain VYOS_FW_OUTPUT { -        type filter hook output priority 0; policy accept; -{% if state_policy is vyos_defined %} -        jump VYOS_STATE_POLICY -{% endif %} -        jump VYOS_POST_FW +{%         endfor %} +{%     endif %} + +{%     if ip.input is vyos_defined %} +{%         set ns = namespace(sets=[]) %} +{%         for prior, conf in ip.input.items() %} +{%             set def_action = conf.default_action %} +    chain VYOS_INPUT_{{ prior }} { +        type filter hook input priority {{ prior }}; policy {{ def_action }}; +{%             if conf.rule is vyos_defined %} +{%                 for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %} +        {{ rule_conf | nft_rule('INP',prior, rule_id) }} +{%                     if rule_conf.recent is vyos_defined %} +{%                         set ns.sets = ns.sets + ['INP_' + prior + '_' + rule_id] %} +{%                     endif %} +{%                 endfor %} +{%             endif %} +    } +{%         endfor %} +{%         for set_name in ns.sets %} +    set RECENT_{{ set_name }} { +        type ipv4_addr +        size 65535 +        flags dynamic      } -    chain VYOS_POST_FW { -        return +{%         endfor %} +{%     endif %} + +{%     if ip.output is vyos_defined %} +{%         set ns = namespace(sets=[]) %} +{%         for prior, conf in ip.output.items() %} +{%             set def_action = conf.default_action %} +    chain VYOS_OUTPUT_{{ prior }} { +        type filter hook output priority {{ prior }}; policy {{ def_action }}; +{%             if conf.rule is vyos_defined %} +{%                 for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %} +        {{ rule_conf | nft_rule('OUT', prior, rule_id) }} +{%                     if rule_conf.recent is vyos_defined %} +{%                         set ns.sets = ns.sets + ['OUT_' + prior + '_' + rule_id] %} +{%                     endif %} +{%                 endfor %} +{%             endif %}      } +{%         endfor %} +{%         for set_name in ns.sets %} +    set RECENT_{{ set_name }} { +        type ipv4_addr +        size 65535 +        flags dynamic +    } +{%         endfor %} +{%     endif %} +      chain VYOS_FRAG_MARK {          type filter hook prerouting priority -450; policy accept;          ip frag-off & 0x3fff != 0 meta mark set 0xffff1 return      } -{% if name is vyos_defined %} -{%     set ns = namespace(sets=[]) %} -{%     for name_text, conf in name.items() %} -    chain NAME_{{ name_text }} { -{%         if conf.rule is vyos_defined %} -{%             for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %} -        {{ rule_conf | nft_rule(name_text, rule_id) }} -{%                 if rule_conf.recent is vyos_defined %} -{%                     set ns.sets = ns.sets + [name_text + '_' + rule_id] %} -{%                 endif %} -{%             endfor %} -{%         endif %} -        {{ conf | nft_default_rule(name_text) }} +{%     if ip.prerouting is vyos_defined %} +{%         set ns = namespace(sets=[]) %} +{%         for prior, conf in ip.prerouting.items() %} +    chain VYOS_PREROUTING_{{ prior }} { +        type filter hook prerouting priority {{ prior }}; policy accept; +{%             if conf.rule is vyos_defined %} +{%                 for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %} +        {{ rule_conf | nft_rule('PRE', prior, rule_id) }} +{%                     if rule_conf.recent is vyos_defined %} +{%                         set ns.sets = ns.sets + ['PRE_' + prior + '_' + rule_id] %} +{%                     endif %} +{%                 endfor %} +{%             endif %} +        {{ conf | nft_default_rule(prior) }} +        # jump VYOS_POST_FW      } -{%     endfor %} -{%     for set_name in ip_fqdn %} -    set FQDN_{{ set_name }} { +{%         endfor %} +{%         for set_name in ns.sets %} +    set RECENT_{{ set_name }} {          type ipv4_addr -        flags interval +        size 65535 +        flags dynamic      } -{%     endfor %} -{%     for set_name in ns.sets %} +{%         endfor %} +{%     endif %} +{%     if ip.name is vyos_defined %} +{%         set ns = namespace(sets=[]) %} +{%         for name_text, conf in ip.name.items() %} +    chain NAME_{{ name_text }} { +{%             if conf.rule is vyos_defined %} +{%                 for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %} +        {{ rule_conf | nft_rule('NAM', name_text, rule_id) }} +{%                     if rule_conf.recent is vyos_defined %} +{%                         set ns.sets = ns.sets + ['NAM_' + name_text + '_' + rule_id] %} +{%                     endif %} +{%                 endfor %} +{%             endif %} +        {{ conf | nft_default_rule(name_text) }} +    } +{%         endfor %} +{%         for set_name in ns.sets %}      set RECENT_{{ set_name }} {          type ipv4_addr          size 65535          flags dynamic      } -{%     endfor %} -{%     if geoip_updated.name is vyos_defined %} -{%         for setname in geoip_updated.name %} -    set {{ setname }} { +{%         endfor %} +{%         for set_name in ip_fqdn %} +    set FQDN_{{ set_name }} {          type ipv4_addr          flags interval      }  {%         endfor %} +{%         if geoip_updated.name is vyos_defined %} +{%             for setname in geoip_updated.name %} +    set {{ setname }} { +        type ipv4_addr +        flags interval +    } +{%             endfor %} +{%         endif %}  {%     endif %}  {% endif %} @@ -95,107 +156,128 @@ table ip vyos_filter {  {% if zone is vyos_defined %}  {{ zone_tmpl.zone_chains(zone, state_policy is vyos_defined, False) }}  {% endif %} - -{% if state_policy is vyos_defined %} -    chain VYOS_STATE_POLICY { -{%     if state_policy.established is vyos_defined %} -        {{ state_policy.established | nft_state_policy('established') }} -{%     endif %} -{%     if state_policy.invalid is vyos_defined %} -        {{ state_policy.invalid | nft_state_policy('invalid') }} -{%     endif %} -{%     if state_policy.related is vyos_defined %} -        {{ state_policy.related | nft_state_policy('related') }} -{%     endif %} -        return -    } -{% endif %}  }  {% if first_install is not vyos_defined %}  delete table ip6 vyos_filter  {% endif %}  table ip6 vyos_filter { -    chain VYOS_FW6_FORWARD { -        type filter hook forward priority 0; policy accept; -{% if state_policy is vyos_defined %} -        jump VYOS_STATE_POLICY6 -{% endif %} -{% if interface is vyos_defined %} -{%     for ifname, ifconf in interface.items() %} -{%         if ifconf.in is vyos_defined and ifconf.in.ipv6_name is vyos_defined %} -        iifname {{ ifname }} counter jump NAME6_{{ ifconf.in.ipv6_name }} -{%         endif %} -{%         if ifconf.out is vyos_defined and ifconf.out.ipv6_name is vyos_defined %} -        oifname {{ ifname }} counter jump NAME6_{{ ifconf.out.ipv6_name }} -{%         endif %} -{%     endfor %} -{% endif %} -        jump VYOS_POST_FW6 +{% if ipv6 is vyos_defined %} +{%     if ipv6.forward is vyos_defined %} +{%         set ns = namespace(sets=[]) %} +{%         for prior, conf in ipv6.forward.items() %} +{%             set def_action = conf.default_action %} +    chain VYOS_IPV6_FORWARD_{{ prior }} { +        type filter hook forward priority {{ prior }}; policy {{ def_action }}; +{%             if conf.rule is vyos_defined %} +{%                 for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %} +        {{ rule_conf | nft_rule('FWD', prior, rule_id ,'ip6') }} +{%                     if rule_conf.recent is vyos_defined %} +{%                         set ns.sets = ns.sets + ['FWD_' + prior + '_' + rule_id] %} +{%                     endif %} +{%                 endfor %} +{%             endif %}      } -    chain VYOS_FW6_LOCAL { -        type filter hook input priority 0; policy accept; -{% if state_policy is vyos_defined %} -        jump VYOS_STATE_POLICY6 -{% endif %} -{% if interface is vyos_defined %} -{%     for ifname, ifconf in interface.items() %} -{%         if ifconf.local is vyos_defined and ifconf.local.ipv6_name is vyos_defined %} -        iifname {{ ifname }} counter jump NAME6_{{ ifconf.local.ipv6_name }} -{%         endif %} -{%     endfor %} -{% endif %} -        jump VYOS_POST_FW6 +{%         endfor %} +{%         for set_name in ns.sets %} +    set RECENT6_{{ set_name }} { +        type ipv6_addr +        size 65535 +        flags dynamic      } -    chain VYOS_FW6_OUTPUT { -        type filter hook output priority 0; policy accept; -{% if state_policy is vyos_defined %} -        jump VYOS_STATE_POLICY6 -{% endif %} -        jump VYOS_POST_FW6 +{%         endfor %} +{%     endif %} + +{%     if ipv6.input is vyos_defined %} +{%         set ns = namespace(sets=[]) %} +{%         for prior, conf in ipv6.input.items() %} +{%             set def_action = conf.default_action %} +    chain VYOS_IPV6_INPUT_{{ prior }} { +        type filter hook input priority {{ prior }}; policy {{ def_action }}; +{%             if conf.rule is vyos_defined %} +{%                 for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %} +        {{ rule_conf | nft_rule('INP', prior, rule_id ,'ip6') }} +{%                     if rule_conf.recent is vyos_defined %} +{%                         set ns.sets = ns.sets + ['INP_' + prior + '_' + rule_id] %} +{%                     endif %} +{%                 endfor %} +{%             endif %}      } -    chain VYOS_POST_FW6 { -        return +{%         endfor %} +{%         for set_name in ns.sets %} +    set RECENT6_{{ set_name }} { +        type ipv6_addr +        size 65535 +        flags dynamic      } +{%         endfor %} +{%     endif %} + +{%     if ipv6.output is vyos_defined %} +{%         set ns = namespace(sets=[]) %} +{%         for prior, conf in ipv6.output.items() %} +{%             set def_action = conf.default_action %} +    chain VYOS_IPV6_OUTPUT_{{ prior }} { +        type filter hook output priority {{ prior }}; policy {{ def_action }}; +{%             if conf.rule is vyos_defined %} +{%                 for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %} +        {{ rule_conf | nft_rule('OUT', prior, rule_id ,'ip6') }} +{%                     if rule_conf.recent is vyos_defined %} +{%                         set ns.sets = ns.sets + ['OUT_ ' + prior + '_' + rule_id] %} +{%                     endif %} +{%                 endfor %} +{%             endif %} +    } +{%         endfor %} +{%         for set_name in ns.sets %} +    set RECENT6_{{ set_name }} { +        type ipv6_addr +        size 65535 +        flags dynamic +    } +{%         endfor %} +{%     endif %}      chain VYOS_FRAG6_MARK {          type filter hook prerouting priority -450; policy accept;          exthdr frag exists meta mark set 0xffff1 return      } -{% if ipv6_name is vyos_defined %} -{%     set ns = namespace(sets=[]) %} -{%     for name_text, conf in ipv6_name.items() %} + +{%     if ipv6.ipv6_name is vyos_defined %} +{%         set ns = namespace(sets=[]) %} +{%         for name_text, conf in ipv6.ipv6_name.items() %}      chain NAME6_{{ name_text }} { -{%         if conf.rule is vyos_defined %} -{%             for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %} -        {{ rule_conf | nft_rule(name_text, rule_id, 'ip6') }} -{%                 if rule_conf.recent is vyos_defined %} -{%                     set ns.sets = ns.sets + [name_text + '_' + rule_id] %} -{%                 endif %} -{%             endfor %} -{%         endif %} +{%             if conf.rule is vyos_defined %} +{%                 for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %} +        {{ rule_conf | nft_rule('NAM', name_text, rule_id, 'ip6') }} +{%                     if rule_conf.recent is vyos_defined %} +{%                         set ns.sets = ns.sets + ['NAM_' + name_text + '_' + rule_id] %} +{%                     endif %} +{%                 endfor %} +{%             endif %}          {{ conf | nft_default_rule(name_text, ipv6=True) }}      } -{%     endfor %} -{%     for set_name in ip6_fqdn %} +{%         endfor %} +{%         for set_name in ip6_fqdn %}      set FQDN_{{ set_name }} {          type ipv6_addr          flags interval      } -{%     endfor %} -{%     for set_name in ns.sets %} +{%         endfor %} +{%         for set_name in ns.sets %}      set RECENT6_{{ set_name }} {          type ipv6_addr          size 65535          flags dynamic      } -{%     endfor %} -{%     if geoip_updated.ipv6_name is vyos_defined %} -{%         for setname in geoip_updated.ipv6_name %} +{%         endfor %} +{%         if geoip_updated.ipv6_name is vyos_defined %} +{%             for setname in geoip_updated.ipv6_name %}      set {{ setname }} {          type ipv6_addr          flags interval      } -{%         endfor %} +{%             endfor %} +{%         endif %}  {%     endif %}  {% endif %} @@ -204,19 +286,4 @@ table ip6 vyos_filter {  {% if zone is vyos_defined %}  {{ zone_tmpl.zone_chains(zone, state_policy is vyos_defined, True) }}  {% endif %} - -{% if state_policy is vyos_defined %} -    chain VYOS_STATE_POLICY6 { -{%     if state_policy.established is vyos_defined %} -        {{ state_policy.established | nft_state_policy('established') }} -{%     endif %} -{%     if state_policy.invalid is vyos_defined %} -        {{ state_policy.invalid | nft_state_policy('invalid') }} -{%     endif %} -{%     if state_policy.related is vyos_defined %} -        {{ state_policy.related | nft_state_policy('related') }} -{%     endif %} -        return -    } -{% endif %} -} +}
\ No newline at end of file diff --git a/python/vyos/firewall.py b/python/vyos/firewall.py index 903cc8535..86a324062 100644 --- a/python/vyos/firewall.py +++ b/python/vyos/firewall.py @@ -41,14 +41,19 @@ def fqdn_config_parse(firewall):      firewall['ip6_fqdn'] = {}      for domain, path in dict_search_recursive(firewall, 'fqdn'): -        fw_name = path[1] # name/ipv6-name -        rule = path[3] # rule id -        suffix = path[4][0] # source/destination (1 char) -        set_name = f'{fw_name}_{rule}_{suffix}' - -        if path[0] == 'name': +        hook_name = path[1] +        priority = path[2] + +        fw_name = path[2] +        rule = path[4] +        suffix = path[5][0] +        set_name = f'{hook_name}_{priority}_{rule}_{suffix}' +             +        if (path[0] == 'ip') and (path[1] == 'forward' or path[1] == 'input' or path[1] == 'output' or path[1] == 'name'):              firewall['ip_fqdn'][set_name] = domain -        elif path[0] == 'ipv6_name': +        elif (path[0] == 'ipv6') and (path[1] == 'forward' or path[1] == 'input' or path[1] == 'output' or path[1] == 'ipv6_name'): +            if path[1] == 'ipv6_name': +                set_name = f'name6_{priority}_{rule}_{suffix}'              firewall['ip6_fqdn'][set_name] = domain  def fqdn_resolve(fqdn, ipv6=False): @@ -80,7 +85,7 @@ def nft_action(vyos_action):          return 'return'      return vyos_action -def parse_rule(rule_conf, fw_name, rule_id, ip_name): +def parse_rule(rule_conf, hook, fw_name, rule_id, ip_name):      output = []      def_suffix = '6' if ip_name == 'ip6' else '' @@ -129,16 +134,34 @@ def parse_rule(rule_conf, fw_name, rule_id, ip_name):              if 'fqdn' in side_conf:                  fqdn = side_conf['fqdn'] +                hook_name = ''                  operator = ''                  if fqdn[0] == '!':                      operator = '!=' -                output.append(f'{ip_name} {prefix}addr {operator} @FQDN_{fw_name}_{rule_id}_{prefix}') +                if hook == 'FWD': +                    hook_name = 'forward' +                if hook == 'INP': +                    hook_name = 'input' +                if hook == 'OUT': +                    hook_name = 'output' +                if hook == 'NAM': +                    hook_name = f'name{def_suffix}' +                output.append(f'{ip_name} {prefix}addr {operator} @FQDN_{hook_name}_{fw_name}_{rule_id}_{prefix}')              if dict_search_args(side_conf, 'geoip', 'country_code'):                  operator = '' +                hook_name = ''                  if dict_search_args(side_conf, 'geoip', 'inverse_match') != None:                      operator = '!=' -                output.append(f'{ip_name} {prefix}addr {operator} @GEOIP_CC_{fw_name}_{rule_id}') +                if hook == 'FWD': +                    hook_name = 'forward' +                if hook == 'INP': +                    hook_name = 'input' +                if hook == 'OUT': +                    hook_name = 'output' +                if hook == 'NAM': +                    hook_name = f'name{def_suffix}' +                output.append(f'{ip_name} {prefix}addr {operator} @GEOIP_CC_{hook_name}_{fw_name}_{rule_id}')              if 'mac_address' in side_conf:                  suffix = side_conf["mac_address"] @@ -324,7 +347,7 @@ def parse_rule(rule_conf, fw_name, rule_id, ip_name):      if 'recent' in rule_conf:          count = rule_conf['recent']['count']          time = rule_conf['recent']['time'] -        output.append(f'add @RECENT{def_suffix}_{fw_name}_{rule_id} {{ {ip_name} saddr limit rate over {count}/{time} burst {count} packets }}') +        output.append(f'add @RECENT{def_suffix}_{hook}_{fw_name}_{rule_id} {{ {ip_name} saddr limit rate over {count}/{time} burst {count} packets }}')      if 'time' in rule_conf:          output.append(parse_time(rule_conf['time'])) @@ -348,7 +371,9 @@ def parse_rule(rule_conf, fw_name, rule_id, ip_name):          output.append(parse_policy_set(rule_conf['set'], def_suffix))      if 'action' in rule_conf: -        output.append(nft_action(rule_conf['action'])) +        # Change action=return to action=action +        # #output.append(nft_action(rule_conf['action'])) +        output.append(f'{rule_conf["action"]}')          if 'jump' in rule_conf['action']:              target = rule_conf['jump_target']              output.append(f'NAME{def_suffix}_{target}') @@ -365,7 +390,7 @@ def parse_rule(rule_conf, fw_name, rule_id, ip_name):      else:          output.append('return') -    output.append(f'comment "{fw_name}-{rule_id}"') +    output.append(f'comment "{hook}-{fw_name}-{rule_id}"')      return " ".join(output)  def parse_tcp_flags(flags): @@ -493,13 +518,15 @@ def geoip_update(firewall, force=False):          # Map country codes to set names          for codes, path in dict_search_recursive(firewall, 'country_code'): -            set_name = f'GEOIP_CC_{path[1]}_{path[3]}' -            if path[0] == 'name': -                for code in codes: -                    ipv4_codes.setdefault(code, []).append(set_name) -            elif path[0] == 'ipv6_name': -                for code in codes: -                    ipv6_codes.setdefault(code, []).append(set_name) +            set_name = f'GEOIP_CC_{path[1]}_{path[2]}_{path[4]}' +            if path[1] == 'ipv6_name': +                set_name = f'GEOIP_CC_name6_{path[2]}_{path[4]}' +                if ( path[0] == 'ip' ) and ( path[1] == 'forward' or path[1] == 'input' or path[1] == 'output' or path[1] == 'name' ): +                    for code in codes: +                        ipv4_codes.setdefault(code, []).append(set_name) +                elif ( path[0] == 'ipv6' ) and ( path[1] == 'forward' or path[1] == 'input' or path[1] == 'output' or path[1] == 'ipv6_name' ): +                    for code in codes: +                        ipv6_codes.setdefault(code, []).append(set_name)          if not ipv4_codes and not ipv6_codes:              if force: diff --git a/python/vyos/template.py b/python/vyos/template.py index 6469623fd..5a3505b91 100644 --- a/python/vyos/template.py +++ b/python/vyos/template.py @@ -44,7 +44,6 @@ def _get_environment(location=None):          loader=loc_loader,          trim_blocks=True,          undefined=ChainableUndefined, -        extensions=['jinja2.ext.loopcontrols']      )      env.filters.update(_FILTERS)      env.tests.update(_TESTS) @@ -574,9 +573,9 @@ def nft_action(vyos_action):      return vyos_action  @register_filter('nft_rule') -def nft_rule(rule_conf, fw_name, rule_id, ip_name='ip'): +def nft_rule(rule_conf, fw_hook, fw_name, rule_id, ip_name='ip'):      from vyos.firewall import parse_rule -    return parse_rule(rule_conf, fw_name, rule_id, ip_name) +    return parse_rule(rule_conf, fw_hook, fw_name, rule_id, ip_name)  @register_filter('nft_default_rule')  def nft_default_rule(fw_conf, fw_name, ipv6=False): @@ -587,7 +586,8 @@ def nft_default_rule(fw_conf, fw_name, ipv6=False):          action_suffix = default_action[:1].upper()          output.append(f'log prefix "[{fw_name[:19]}-default-{action_suffix}]"') -    output.append(nft_action(default_action)) +    #output.append(nft_action(default_action)) +    output.append(f'{default_action}')      if 'default_jump_target' in fw_conf:          target = fw_conf['default_jump_target']          def_suffix = '6' if ipv6 else '' diff --git a/src/conf_mode/firewall.py b/src/conf_mode/firewall.py index 7242e503a..4c5341e22 100755 --- a/src/conf_mode/firewall.py +++ b/src/conf_mode/firewall.py @@ -23,6 +23,7 @@ from sys import exit  from vyos.base import Warning  from vyos.config import Config +from vyos.configdict import dict_merge  from vyos.configdict import node_changed  from vyos.configdiff import get_config_diff, Diff  from vyos.configdep import set_dependents, call_dependents @@ -36,6 +37,7 @@ from vyos.utils.dict import dict_search_args  from vyos.utils.dict import dict_search_recursive  from vyos.utils.process import process_named_running  from vyos.utils.process import rc_cmd +from vyos.xml import defaults  from vyos import ConfigError  from vyos import airbag  airbag.enable() @@ -95,19 +97,22 @@ def geoip_updated(conf, firewall):      updated = False      for key, path in dict_search_recursive(firewall, 'geoip'): -        set_name = f'GEOIP_CC_{path[1]}_{path[3]}' -        if path[0] == 'name': +        set_name = f'GEOIP_CC_{path[1]}_{path[2]}_{path[4]}' +        if path[1] == 'ipv6_name': +            set_name = f'GEOIP_CC_name6_{path[2]}_{path[4]}' + +        if (path[0] == 'ip') and ( path[1] == 'forward' or path[1] == 'input' or path[1] == 'output' or path[1] == 'name' ):              out['name'].append(set_name) -        elif path[0] == 'ipv6_name': +        elif (path[0] == 'ipv6') and ( path[1] == 'forward' or path[1] == 'input' or path[1] == 'output' or path[1] == 'ipv6_name' ):              out['ipv6_name'].append(set_name)          updated = True      if 'delete' in node_diff:          for key, path in dict_search_recursive(node_diff['delete'], 'geoip'): -            set_name = f'GEOIP_CC_{path[1]}_{path[3]}' -            if path[0] == 'name': +            set_name = f'GEOIP_CC_{path[2]}_{path[4]}' +            if path[1] == 'name':                  out['deleted_name'].append(set_name) -            elif path[0] == 'ipv6-name': +            elif path[1] == 'ipv6-name':                  out['deleted_ipv6_name'].append(set_name)              updated = True @@ -128,13 +133,14 @@ def get_config(config=None):                                      get_first_key=True,                                      with_recursive_defaults=True) -    firewall['group_resync'] = bool('group' in firewall or node_changed(conf, base + ['group'])) +    firewall['group_resync'] = bool('group' in firewall or node_changed(conf, base + ['group']))      if firewall['group_resync']:          # Update nat and policy-route as firewall groups were updated          set_dependents('group_resync', conf) -    if 'config_trap' in firewall and firewall['config_trap'] == 'enable': +    #if 'config_trap' in firewall and firewall['config_trap'] == 'enable': +    if 'config_trap' in firewall and firewall['global_options']['config_trap'] == 'enable':          diff = get_config_diff(conf)          firewall['trap_diff'] = diff.get_child_nodes_diff_str(base)          firewall['trap_targets'] = conf.get_config_dict(['service', 'snmp', 'trap-target'], @@ -159,10 +165,10 @@ def verify_rule(firewall, rule_conf, ipv6):              raise ConfigError('jump-target defined, but action jump needed and it is not defined')          target = rule_conf['jump_target']          if not ipv6: -            if target not in dict_search_args(firewall, 'name'): +            if target not in dict_search_args(firewall, 'ip', 'name'):                  raise ConfigError(f'Invalid jump-target. Firewall name {target} does not exist on the system')          else: -            if target not in dict_search_args(firewall, 'ipv6_name'): +            if target not in dict_search_args(firewall, 'ipv6', 'ipv6_name'):                  raise ConfigError(f'Invalid jump-target. Firewall ipv6-name {target} does not exist on the system')      if 'queue_options' in rule_conf: @@ -291,95 +297,45 @@ def verify(firewall):                  for group_name, group in groups.items():                      verify_nested_group(group_name, group, groups, []) -    for name in ['name', 'ipv6_name']: -        if name in firewall: -            for name_id, name_conf in firewall[name].items(): -                if 'jump' in name_conf['default_action'] and 'default_jump_target' not in name_conf: -                    raise ConfigError('default-action set to jump, but no default-jump-target specified') -                if 'default_jump_target' in name_conf: -                    target = name_conf['default_jump_target'] -                    if 'jump' not in name_conf['default_action']: -                        raise ConfigError('default-jump-target defined,but default-action jump needed and it is not defined') -                    if name_conf['default_jump_target'] == name_id: -                        raise ConfigError(f'Loop detected on default-jump-target.') -                    ## Now need to check that default-jump-target exists (other firewall chain/name) -                    if target not in dict_search_args(firewall, name): -                        raise ConfigError(f'Invalid jump-target. Firewall {name} {target} does not exist on the system') - -                if 'rule' in name_conf: -                    for rule_id, rule_conf in name_conf['rule'].items(): -                        verify_rule(firewall, rule_conf, name == 'ipv6_name') - -    if 'interface' in firewall: -        for ifname, if_firewall in firewall['interface'].items(): -            # verify ifname needs to be disabled, dynamic devices come up later -            # verify_interface_exists(ifname) - -            for direction in ['in', 'out', 'local']: -                name = dict_search_args(if_firewall, direction, 'name') -                ipv6_name = dict_search_args(if_firewall, direction, 'ipv6_name') - -                if name and dict_search_args(firewall, 'name', name) == None: -                    raise ConfigError(f'Invalid firewall name "{name}" referenced on interface {ifname}') - -                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}') - -    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') +    if 'ip' in firewall: +        for name in ['name','forward','input','output']: +            if name in firewall['ip']: +                for name_id, name_conf in firewall['ip'][name].items(): +                    if 'jump' in name_conf['default_action'] and 'default_jump_target' not in name_conf: +                        raise ConfigError('default-action set to jump, but no default-jump-target specified') +                    if 'default_jump_target' in name_conf: +                        target = name_conf['default_jump_target'] +                        if 'jump' not in name_conf['default_action']: +                            raise ConfigError('default-jump-target defined, but default-action jump needed and it is not defined') +                        if name_conf['default_jump_target'] == name_id: +                            raise ConfigError(f'Loop detected on default-jump-target.') +                        ## Now need to check that default-jump-target exists (other firewall chain/name) +                        if target not in dict_search_args(firewall['ip'], 'name'): +                            raise ConfigError(f'Invalid jump-target. Firewall name {target} does not exist on the system') + +                    if 'rule' in name_conf: +                        for rule_id, rule_conf in name_conf['rule'].items(): +                            verify_rule(firewall, rule_conf, False) + +    if 'ipv6' in firewall: +        for name in ['ipv6_name','forward','input','output']: +            if name in firewall['ipv6']: +                for name_id, name_conf in firewall['ipv6'][name].items(): +                    if 'jump' in name_conf['default_action'] and 'default_jump_target' not in name_conf: +                        raise ConfigError('default-action set to jump, but no default-jump-target specified') +                    if 'default_jump_target' in name_conf: +                        target = name_conf['default_jump_target'] +                        if 'jump' not in name_conf['default_action']: +                            raise ConfigError('default-jump-target defined, but default-action jump needed and it is not defined') +                        if name_conf['default_jump_target'] == name_id: +                            raise ConfigError(f'Loop detected on default-jump-target.') +                        ## Now need to check that default-jump-target exists (other firewall chain/name) +                        if target not in dict_search_args(firewall['ipv6'], 'ipv6_name'): +                            raise ConfigError(f'Invalid jump-target. Firewall name {target} does not exist on the system') + +                    if 'rule' in name_conf: +                        for rule_id, rule_conf in name_conf['rule'].items(): +                            verify_rule(firewall, rule_conf, True)      return None @@ -387,19 +343,6 @@ 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 @@ -408,9 +351,8 @@ def apply_sysfs(firewall):          paths = glob(conf['sysfs'])          value = None -        if name in firewall: -            conf_value = firewall[name] - +        if name in firewall['global_options']: +            conf_value = firewall['global_options'][name]              if conf_value in conf:                  value = conf[conf_value]              elif conf_value == 'enable': @@ -427,7 +369,7 @@ def post_apply_trap(firewall):      if 'first_install' in firewall:          return None -    if 'config_trap' not in firewall or firewall['config_trap'] != 'enable': +    if 'config_trap' not in firewall['global_options'] or firewall['global_options']['config_trap'] != 'enable':          return None      if not process_named_running('snmpd'): | 
