initial commit
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
flowtable ft {
|
||||
hook ingress priority filter
|
||||
devices = { eth0, eth1 }
|
||||
}
|
||||
|
||||
chain input {
|
||||
type filter hook input priority filter; policy drop;
|
||||
|
||||
ct state established,related accept
|
||||
ct state invalid drop
|
||||
|
||||
iif lo accept
|
||||
ip protocol icmp accept
|
||||
ip6 nexthdr icmpv6 accept
|
||||
|
||||
meta mark 0x00000001 accept
|
||||
|
||||
iifname eth0 tcp dport 22 accept
|
||||
iifname eth0.11 tcp dport 22 accept
|
||||
|
||||
iifname eth1 udp dport 51820 accept
|
||||
iifname { eth0, eth0.2, eth0.3, eth0.4, eth0.10, eth0.11, eth0.12 } udp dport 53 accept
|
||||
iifname { eth0, eth0.2, eth0.3, eth0.4, eth0.10, eth0.11, eth0.12 } tcp dport 53 accept
|
||||
|
||||
iifname eth0.3 udp dport 67 accept
|
||||
iifname eth1 udp dport 68 accept
|
||||
|
||||
#include "/etc/nftables.d/90-input.nft"
|
||||
}
|
||||
|
||||
chain forward {
|
||||
type filter hook forward priority filter; policy drop;
|
||||
|
||||
ct state established,related accept
|
||||
ct state invalid drop
|
||||
|
||||
iifname { eth0, eth0.2, eth0.3, eth0.4, eth0.10, eth0.11, eth0.12 } oifname eth1 ct state new flow add @ft
|
||||
iifname { eth0, eth0.2, eth0.3, eth0.4, eth0.10, eth0.11, eth0.12 } oifname eth1 accept
|
||||
|
||||
tcp flags syn tcp option maxseg size set rt mtu
|
||||
|
||||
include "/etc/nftables.d/90-forward.nft"
|
||||
}
|
||||
|
||||
chain output {
|
||||
type route hook output priority filter; policy accept;
|
||||
|
||||
#include "/etc/nftables.d/90-output.nft"
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
chain postrouting {
|
||||
type nat hook postrouting priority srcnat; policy accept;
|
||||
oifname eth1 masquerade
|
||||
}
|
||||
|
||||
chain prerouting {
|
||||
type nat hook prerouting priority dstnat; policy accept;
|
||||
include "/etc/nftables.d/90-dstnat.nft"
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
chain vpn_prerouting_dnat {
|
||||
type nat hook prerouting priority dstnat - 5; policy accept;
|
||||
|
||||
iifname wg0 ip daddr 10.250.251.0/24 counter dnat ip prefix to 10.1.0.0/24
|
||||
iifname wg0 ip daddr 10.250.252.0/24 counter dnat ip prefix to 10.2.0.0/24
|
||||
iifname wg0 ip daddr 10.250.253.0/24 counter dnat ip prefix to 10.10.0.0/24
|
||||
iifname wg0 ip daddr 10.250.254.0/24 counter dnat ip prefix to 10.11.0.0/24
|
||||
iifname wg0 ip daddr 10.250.255.0/24 counter dnat ip prefix to 10.12.0.0/24
|
||||
iifname wg0 ip daddr 10.250.249.0/24 counter dnat ip prefix to 10.13.0.0/24
|
||||
}
|
||||
|
||||
chain vpn_postrouting_snat {
|
||||
type nat hook postrouting priority srcnat; policy accept;
|
||||
|
||||
oifname wg0 ip saddr 10.1.0.0/24 counter snat ip prefix to 10.250.251.0/24
|
||||
oifname wg0 ip saddr 10.2.0.0/24 counter snat ip prefix to 10.250.252.0/24
|
||||
oifname wg0 ip saddr 10.10.0.0/24 counter snat ip prefix to 10.250.253.0/24
|
||||
oifname wg0 ip saddr 10.11.0.0/24 counter snat ip prefix to 10.250.254.0/24
|
||||
oifname wg0 ip saddr 10.12.0.0/24 counter snat ip prefix to 10.250.255.0/24
|
||||
oifname wg0 ip saddr 10.13.0.0/24 counter snat ip prefix to 10.250.249.0/24
|
||||
}
|
||||
|
||||
chain vpn_prerouting_pbr {
|
||||
type filter hook prerouting priority mangle - 10; policy accept;
|
||||
|
||||
iifname wg0 ct state new counter ct mark set 0x000000c7
|
||||
ip daddr 10.0.0.0/8 return
|
||||
iifname != "wg0" ct mark 0x000000c7 counter mark set 0x000000c7
|
||||
}
|
||||
|
||||
chain vpn_output_pbr {
|
||||
type route hook output priority mangle - 10; policy accept;
|
||||
|
||||
ct mark 0x000000c7 counter meta mark set 0x000000c7
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
chain proxy_prerouting {
|
||||
type filter hook prerouting priority filter - 50; policy accept;
|
||||
|
||||
fib daddr type local accept
|
||||
|
||||
include "/etc/nftables.d/90-proxy.nft"
|
||||
}
|
||||
|
||||
chain proxy_output {
|
||||
type route hook output priority mangle; policy accept;
|
||||
|
||||
#meta mark 0x000000ff return
|
||||
|
||||
#meta l4proto { tcp, udp } ip daddr @cloudflare_ip meta mark set 0x00000001 accept
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
set private_ip {
|
||||
type ipv4_addr
|
||||
flags interval
|
||||
auto-merge
|
||||
elements = { 10.0.0.0/8, 100.64.0.0/10, 127.0.0.0/8, 169.254.0.0/16, 172.16.0.0/12, 192.0.0.0/24, 192.0.2.0/24, 192.88.99.0/24, 192.168.0.0/16, 198.18.0.0/15, 198.51.100.0/24, 203.0.113.0/24, 224.0.0.0/4, 240.0.0.0/4 }
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#!/usr/sbin/nft -f
|
||||
|
||||
flush ruleset
|
||||
|
||||
table inet filter {
|
||||
include "/etc/nftables.d/40-sets.nft"
|
||||
include "/etc/nftables.d/90-sets.nft"
|
||||
include "/etc/nftables.d/10-filter.nft"
|
||||
include "/etc/nftables.d/20-vpn.nft"
|
||||
include "/etc/nftables.d/30-proxy.nft"
|
||||
}
|
||||
|
||||
table ip nat {
|
||||
include "/etc/nftables.d/10-nat.nft"
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
- name: reload nftables
|
||||
ansible.builtin.command: nft -f /etc/nftables.conf
|
||||
listen: reload nftables
|
||||
@@ -0,0 +1,41 @@
|
||||
---
|
||||
- name: ensure /etc/nftables.d exists
|
||||
ansible.builtin.file:
|
||||
path: /etc/nftables.d
|
||||
state: directory
|
||||
mode: "0755"
|
||||
|
||||
- name: deploy nftables rule
|
||||
ansible.builtin.copy:
|
||||
src: "{{ item }}"
|
||||
dest: "/etc/nftables.d/{{ item }}"
|
||||
mode: "0644"
|
||||
loop:
|
||||
- 10-filter.nft
|
||||
- 10-nat.nft
|
||||
- 20-vpn.nft
|
||||
- 30-proxy.nft
|
||||
- 40-sets.nft
|
||||
notify: reload nftables
|
||||
|
||||
- name: render forward
|
||||
ansible.builtin.template:
|
||||
src: 90-forward.nft.j2
|
||||
dest: /etc/nftables.d/90-forward.nft
|
||||
mode: "0644"
|
||||
notify: reload nftables
|
||||
|
||||
- name: render dstnat
|
||||
ansible.builtin.template:
|
||||
src: 90-dstnat.nft.j2
|
||||
dest: /etc/nftables.d/90-dstnat.nft
|
||||
mode: "0644"
|
||||
notify: reload nftables
|
||||
|
||||
- name: deploy nftables.conf
|
||||
ansible.builtin.copy:
|
||||
src: nftables.conf
|
||||
dest: /etc/nftables.conf
|
||||
mode: "0644"
|
||||
validate: "nft -c -f %s"
|
||||
notify: reload nftables
|
||||
@@ -0,0 +1,31 @@
|
||||
#jinja2: trim_blocks: True, lstrip_blocks: True
|
||||
{% macro render_dstnat_rule(ifaces, proto, port, target_ip, item_name) %}
|
||||
{% set lines = [] %}
|
||||
{% set active_ifaces = ifaces if (ifaces is iterable and ifaces is not string) else [ifaces] %}
|
||||
{% for current_iface in active_ifaces %}
|
||||
{% set comment_str = ' comment "' ~ current_iface ~ ' -> ' ~ item_name ~ '"' %}
|
||||
{% set rule_line = 'iifname "' ~ current_iface ~ '" ' ~ proto ~ ' dport ' ~ port ~ ' counter dnat ip to ' ~ target_ip ~ ':' ~ port ~ comment_str %}
|
||||
{% set _ = lines.append(rule_line) %}
|
||||
{% endfor %}
|
||||
{{ lines | join('\n') }}
|
||||
{% endmacro %}
|
||||
{% filter regex_replace('\n[ \t]*\n+', '\n') %}
|
||||
{% for item in groups[nft_managed_group] | sort %}
|
||||
{% set client = hostvars[item] %}
|
||||
{% if 'nft_dst' in client and client.nft_dst is not none %}
|
||||
{% set target_ip = client.container_ip | default(client.ansible_host | default(item)) %}
|
||||
{% set raw_expose = client.nft_dst %}
|
||||
{% set exposes = raw_expose if (raw_expose is iterable and raw_expose is not string and raw_expose is not mapping) else [raw_expose] %}
|
||||
{% for expose in exposes %}
|
||||
{% set protos = expose.proto if (expose.proto is defined and expose.proto is iterable and expose.proto is not string) else [expose.proto | default('tcp')] %}
|
||||
{% set ports = expose.port if (expose.port is defined and expose.port is iterable and expose.port is not string) else [expose.port] %}
|
||||
{% set ifaces = expose.iface %}
|
||||
{% for p in protos | sort %}
|
||||
{% for port in ports | sort %}
|
||||
{{ render_dstnat_rule(ifaces, p, port, target_ip, item) }}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endfilter %}
|
||||
@@ -0,0 +1,95 @@
|
||||
#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 %}
|
||||
Reference in New Issue
Block a user