#jinja2: trim_blocks: True, lstrip_blocks: True {% set ip_to_host = {} %} {% for host in groups['all'] | default([]) %} {% set hv = hostvars[host] | default({}) %} {% if hv.ansible_host is defined and (hv.ansible_connection | default('')) != 'community.proxmox.proxmox_pct_remote' %} {% set _ = ip_to_host.update({(hv.ansible_host | string): host}) %} {% endif %} {% if hv.container_ip is defined and hv.container_ip %} {% set _ = ip_to_host.update({(hv.container_ip | string): host}) %} {% endif %} {% endfor %} {% macro render_rule(service_name, iif, saddr, oif, daddr, protos, ports, dest_name) %} {% set lines = [] %} {% set iifs = iif if (iif is iterable and iif is not string) else [iif] %} {% set oifs = oif if (oif is iterable and oif is not string) else [oif] %} {% set active_protos = protos | sort if protos | length > 0 else [none] %} {% set active_ports = ports if ports | length > 0 else [none] %} {% for current_iif in iifs %} {% for current_oif in oifs %} {% for p in active_protos %} {% for port in active_ports %} {% set proto_rule = '' %} {% if p and port %} {% set proto_rule = p ~ ' dport ' ~ port %} {% elif p %} {% set proto_rule = 'meta l4proto ' ~ p %} {% endif %} {# Resolve source name: prefer an explicit host resolved via saddr, otherwise fall back to the current interface for this specific line (not the whole iif list/service_name) #} {% set resolved_service_name = service_name if service_name else current_iif %} {% if saddr and ip_to_host[saddr | string] is defined %} {% set resolved_service_name = ip_to_host[saddr | string] %} {% endif %} {# Resolve destination IP to inventory hostname only for comment #} {% set resolved_dest_name = dest_name %} {% if daddr and ip_to_host[daddr | string] is defined %} {% set resolved_dest_name = ip_to_host[daddr | string] %} {% endif %} {% set comment_text = resolved_service_name ~ ' -> ' ~ resolved_dest_name %} {% set comment_str = ' comment "' ~ comment_text ~ '"' %} {% set parts = ['iifname "' ~ current_iif ~ '"'] %} {% if saddr %} {% set _ = parts.append('ip saddr ' ~ saddr) %} {% endif %} {% if current_oif %} {% set _ = parts.append('oifname "' ~ current_oif ~ '"') %} {% endif %} {% if daddr %} {% set _ = parts.append('ip daddr ' ~ daddr) %} {% endif %} {% if proto_rule %} {% set _ = parts.append(proto_rule) %} {% endif %} {% set _ = parts.append('counter accept' ~ comment_str) %} {% set _ = lines.append(parts | join(' ')) %} {% endfor %} {% endfor %} {% endfor %} {% endfor %} {{ lines | join('\n') }} {% endmacro %} {% filter regex_replace('\n[ \t]*\n+', '\n') %} {# === Managed Hosts Forward Rules === #} {% for item in groups[nft_managed_group] | sort %} {% set client = hostvars[item] %} {% if client.nft_to is defined and client.nft_to is not none %} {% set raw_rules = client.nft_to if (client.nft_to is iterable and client.nft_to is not string and client.nft_to is not mapping) else [client.nft_to] %} {% for r in raw_rules %} {% set rule_dict = r if (r is mapping) else {'to': r} %} {% set raw_dests = rule_dict.to if (rule_dict.to is iterable and rule_dict.to is not string) else [rule_dict.to] %} {% set protos = rule_dict.proto if (rule_dict.proto is defined and rule_dict.proto is iterable and rule_dict.proto is not string) else ([rule_dict.proto] if rule_dict.proto is defined else []) %} {% set ports = rule_dict.port if (rule_dict.port is defined and rule_dict.port is iterable and rule_dict.port is not string) else ([rule_dict.port] if rule_dict.port is defined else []) %} {% for dest in raw_dests %} {% set dest_name = dest | regex_replace('^zone:', '') %} {% if dest.startswith('zone:') %} {{ render_rule(item, client.zone_iface, client.container_ip, dest.split(':')[1], none, protos, ports, dest_name) }} {% else %} {{ render_rule(item, client.zone_iface, client.container_ip, hostvars[dest].zone_iface, hostvars[dest].container_ip, protos, ports, dest_name) }} {% endif %} {% endfor %} {% endfor %} {% endif %} {% endfor %} {% for item in groups[nft_managed_group] | sort %} {% set client = hostvars[item] %} {% if client.nft_from is defined and client.nft_from is not none %} {% set raw_from_rules = client.nft_from if (client.nft_from is iterable and client.nft_from is not string and client.nft_from is not mapping) else [client.nft_from] %} {% for r in raw_from_rules %} {% set protos = r.proto if (r.proto is defined and r.proto is iterable and r.proto is not string) else ([r.proto] if r.proto is defined else []) %} {% set ports = r.port if (r.port is defined and r.port is iterable and r.port is not string) else ([r.port] if r.port is defined else []) %} {{ render_rule(none, r.iface, none, client.zone_iface, client.container_ip, protos, ports, item) }} {% endfor %} {% endif %} {% endfor %} {% endfilter %}