commit 2dc83c0626a98c4af409f2306f266a0e42702c3f Author: pyrschtjag Date: Sun Aug 16 23:16:49 2026 +0000 initial commit diff --git a/ansible.cfg b/ansible.cfg new file mode 100644 index 0000000..3ececf7 --- /dev/null +++ b/ansible.cfg @@ -0,0 +1,9 @@ +[defaults] +inventory = inventory/ +roles_path = roles/ +host_key_checking = False +forks = 8 + +[inventory] +enable_plugins = community.proxmox.proxmox, host_list, yaml, ini +cache = True diff --git a/inventory/group_vars/all/main_vars.yml b/inventory/group_vars/all/main_vars.yml new file mode 100644 index 0000000..d52af44 --- /dev/null +++ b/inventory/group_vars/all/main_vars.yml @@ -0,0 +1,3 @@ +nft_managed_group: all +dnsmasq_managed_group: all +xray_managed_group: all diff --git a/inventory/group_vars/all/xray_sets.yml b/inventory/group_vars/all/xray_sets.yml new file mode 100644 index 0000000..d128d00 --- /dev/null +++ b/inventory/group_vars/all/xray_sets.yml @@ -0,0 +1,73 @@ +xray_ip_sets: + refilter: + urls: + - https://raw.githubusercontent.com/1andrevich/Re-filter-lists/refs/heads/main/community_ips.lst + - https://raw.githubusercontent.com/1andrevich/Re-filter-lists/refs/heads/main/discord_ips.lst + - https://raw.githubusercontent.com/1andrevich/Re-filter-lists/refs/heads/main/ipsum.lst + + cdn: + urls: + - https://raw.githubusercontent.com/123jjck/cdn-ip-ranges/refs/heads/main/all/all_plain_ipv4.txt + + telegram: + urls: + - https://raw.githubusercontent.com/fernvenue/telegram-cidr-list/refs/heads/master/CIDRv4.txt + + russian_whitelist: + urls: + - https://raw.githubusercontent.com/hxehex/russia-mobile-internet-whitelist/refs/heads/main/cidrwhitelist.txt + - https://raw.githubusercontent.com/ebrasha/cidr-ip-ranges-by-country/refs/heads/master/CIDR/RU-ipv4-Hackers.Zone.txt + + cloudflare: + static: + - 1.1.1.1 + - 1.0.0.1 + + google: + urls: + - https://raw.githubusercontent.com/lord-alfred/ipranges/main/google/ipv4.txt + +xray_domain_sets: + v2ray: + urls: + - https://raw.githubusercontent.com/v2ray/domain-list-community/refs/heads/master/data/spotify + - https://raw.githubusercontent.com/v2ray/domain-list-community/refs/heads/master/data/microsoft + - https://raw.githubusercontent.com/v2ray/domain-list-community/refs/heads/master/data/openai + + torrent: + static: + - bt.t-ru.org + - bt2.t-ru.org + - bt3.t-ru.org + - bt4.t-ru.org + - rutracker.org + - rutracker.net + - tapochek.net + - nnmclub.to + - rutor.info + - bigfangroup.org + + vps: + static: + - dev.oyacoi.ru + - vector.oyacoi.ru + + terraform: + static: + - terraform.io + - hashicorp.com + +xray_static_sets: + - private + +xray_lists_global: + cache_dir: /var/lib/xray-lists/cache + output_dir: /var/lib/xray-lists/generated + dnsmasq_output: /var/lib/xray-lists/generated/nftsets.conf + proxy: "socks5h://127.0.0.1:1080" + proxy_user: "{{ lookup('env', 'SOCKS5_USERNAME') }}" + proxy_pass: "{{ lookup('env', 'SOCKS5_PASSWORD') }}" + http_timeout: 20 + +xray_tproxy_port: 61219 +xray_fwmark: "0x00000001" diff --git a/inventory/group_vars/proxmox_all_lxc.yml b/inventory/group_vars/proxmox_all_lxc.yml new file mode 100644 index 0000000..9168cb0 --- /dev/null +++ b/inventory/group_vars/proxmox_all_lxc.yml @@ -0,0 +1,5 @@ +ansible_connection: community.proxmox.proxmox_pct_remote +ansible_host: 10.1.0.4 +ansible_user: root +ansible_ssh_private_key_file: "~/.ssh/id_ed25519" +ansible_python_interpreter: /usr/bin/python3 diff --git a/inventory/host_vars/3ds.yml b/inventory/host_vars/3ds.yml new file mode 100644 index 0000000..6407402 --- /dev/null +++ b/inventory/host_vars/3ds.yml @@ -0,0 +1,4 @@ +nft_to: + - to: [workuter,oyacoi-odcm] + proto: tcp + port: 5000 diff --git a/inventory/host_vars/asf.yml b/inventory/host_vars/asf.yml new file mode 100644 index 0000000..37a1d5f --- /dev/null +++ b/inventory/host_vars/asf.yml @@ -0,0 +1,3 @@ +dnsmasq: + - name: asf.oyacoi.ru + ip: 10.10.0.2 diff --git a/inventory/host_vars/bananawrt.yml b/inventory/host_vars/bananawrt.yml new file mode 100644 index 0000000..6a3d8fa --- /dev/null +++ b/inventory/host_vars/bananawrt.yml @@ -0,0 +1,3 @@ +dnsmasq: + - name: bananawrt.oyacoi.ru + ip: 10.10.0.2 diff --git a/inventory/host_vars/bylampa.yml b/inventory/host_vars/bylampa.yml new file mode 100644 index 0000000..fabb67d --- /dev/null +++ b/inventory/host_vars/bylampa.yml @@ -0,0 +1,3 @@ +dnsmasq: + - name: bylampa.oyacoi.ru + ip: 10.10.0.2 diff --git a/inventory/host_vars/camera0.yml b/inventory/host_vars/camera0.yml new file mode 100644 index 0000000..8d49349 --- /dev/null +++ b/inventory/host_vars/camera0.yml @@ -0,0 +1,4 @@ +nft_from: + - iface: [eth1,eth0.2] + to: camera0 + proto: [tcp,udp] diff --git a/inventory/host_vars/coturn.yml b/inventory/host_vars/coturn.yml new file mode 100644 index 0000000..ae772c4 --- /dev/null +++ b/inventory/host_vars/coturn.yml @@ -0,0 +1,12 @@ +nft_dst: + - iface: [eth0,eth0.2] + proto: [tcp,udp] + port: [3478,5349] + +nft_from: + - iface: [eth0,eth0.2,eth0.3,eth0.4,wg0] + proto: [tcp,udp] + port: [3478,5349] + - iface: [eth0,eth0.2,eth0.3,eth0.4,wg0] + proto: udp + port: ["49152-65535"] diff --git a/inventory/host_vars/firebat.yml b/inventory/host_vars/firebat.yml new file mode 100644 index 0000000..632e341 --- /dev/null +++ b/inventory/host_vars/firebat.yml @@ -0,0 +1,3 @@ +dnsmasq: + - name: proxmox.oyacoi.ru + ip: 10.10.0.2 diff --git a/inventory/host_vars/gitea.yml b/inventory/host_vars/gitea.yml new file mode 100644 index 0000000..b59da5d --- /dev/null +++ b/inventory/host_vars/gitea.yml @@ -0,0 +1,8 @@ +nft_from: + - iface: wg0 + proto: tcp + port: 22 + +dnsmasq: + - name: gitea.oyacoi.ru + ip: 10.10.0.2 diff --git a/inventory/host_vars/haproxy.yml b/inventory/host_vars/haproxy.yml new file mode 100644 index 0000000..e172dc2 --- /dev/null +++ b/inventory/host_vars/haproxy.yml @@ -0,0 +1,4 @@ +nft_to: + - to: nginx + proto: tcp + port: [80, 81, 443, 444, 24445] diff --git a/inventory/host_vars/jellyfin.oyacoi.ru b/inventory/host_vars/jellyfin.oyacoi.ru new file mode 100644 index 0000000..683e0ae --- /dev/null +++ b/inventory/host_vars/jellyfin.oyacoi.ru @@ -0,0 +1,3 @@ +dnsmasq: + - name: jellyfin.oyacoi.ru + ip: 10.10.0.2 diff --git a/inventory/host_vars/mcsmanager.yml b/inventory/host_vars/mcsmanager.yml new file mode 100644 index 0000000..ee03853 --- /dev/null +++ b/inventory/host_vars/mcsmanager.yml @@ -0,0 +1,13 @@ +nft_dst: + - iface: [eth0,eth0.2] + proto: tcp + port: 25565 + +nft_from: + - iface: [eth0,wg0] + proto: tcp + port: 25565 + +dnsmasq: + - name: mcsmanager.oyacoi.ru + ip: 10.10.0.2 diff --git a/inventory/host_vars/nfs.yaml b/inventory/host_vars/nfs.yaml new file mode 100644 index 0000000..362ebee --- /dev/null +++ b/inventory/host_vars/nfs.yaml @@ -0,0 +1,7 @@ +nft_to: + - to: workuter + proto: [tcp, udp] + port: 32765 + - to: oyacoi-odcm + proto: [tcp, udp] + port: 32765 diff --git a/inventory/host_vars/nginx.yml b/inventory/host_vars/nginx.yml new file mode 100644 index 0000000..23686c7 --- /dev/null +++ b/inventory/host_vars/nginx.yml @@ -0,0 +1,42 @@ +nft_to: + - to: vaultwarden + proto: tcp + port: 8000 + - to: gitea + proto: tcp + port: 3000 + - to: radicale + proto: tcp + port: 5232 + - to: slskd + proto: tcp + port: 5030 + - to: asf + proto: tcp + port: 1337 + - to: rtorrent + proto: tcp + port: 80 + - to: jellyfin + proto: tcp + port: 8096 + - to: prosody + proto: tcp + port: 5280 + - to: torrserver + proto: tcp + port: 8090 + - to: prowlarr + proto: tcp + port: 9696 + - to: prowlarr #jackett + proto: tcp + port: 9117 + - to: bylampa + proto: tcp + port: 80 + +nft_from: + - iface: [eth0,eth0.2] + proto: tcp + port: [80,443,24444] diff --git a/inventory/host_vars/ntfy.yml b/inventory/host_vars/ntfy.yml new file mode 100644 index 0000000..80d8cc8 --- /dev/null +++ b/inventory/host_vars/ntfy.yml @@ -0,0 +1,3 @@ +dnsmasq: + - name: ntfy.oyacoi.ru + ip: 10.10.0.2 diff --git a/inventory/host_vars/oyacoi-odcm.yml b/inventory/host_vars/oyacoi-odcm.yml new file mode 100644 index 0000000..7ad6852 --- /dev/null +++ b/inventory/host_vars/oyacoi-odcm.yml @@ -0,0 +1,15 @@ +nft_to: + - to: nfs + proto: [tcp, udp] + port: [2049, 111, 32765, 32767] + - to: [zone:eth0.10,zone:eth0.11,zone:eth0.12] + proto: tcp + port: 22 + - to: [xiawrt,rbpi4] + proto: tcp + port: 22 + +xray_policy: + - bypass: private + - bypass: russian_whitelist + - proxy: all diff --git a/inventory/host_vars/prosody.yml b/inventory/host_vars/prosody.yml new file mode 100644 index 0000000..4987c27 --- /dev/null +++ b/inventory/host_vars/prosody.yml @@ -0,0 +1,20 @@ +nft_dst: + - iface: [eth0,eth0.2] + proto: tcp + port: [5000,5222,5223,5280,5270,5269] + +nft_to: + - to: pgsql + proto: tcp + port: 5432 + +nft_from: + - iface: [eth0,eth0.2,wg0] + proto: tcp + port: [5000,5222,5223,5269,5270,5280] + +dnsmasq: + - name: talk.oyacoi.ru + ip: 10.10.0.2 + - name: upload.oyacoi.ru + ip: 10.10.0.2 diff --git a/inventory/host_vars/prowlarr.yml b/inventory/host_vars/prowlarr.yml new file mode 100644 index 0000000..31a4a51 --- /dev/null +++ b/inventory/host_vars/prowlarr.yml @@ -0,0 +1,5 @@ +dnsmasq: + - name: prowlarr.oyacoi.ru + ip: 10.10.0.2 + - name: jackett.oyacoi.ru + ip: 10.10.0.2 diff --git a/inventory/host_vars/ps3.yml b/inventory/host_vars/ps3.yml new file mode 100644 index 0000000..11d158e --- /dev/null +++ b/inventory/host_vars/ps3.yml @@ -0,0 +1,8 @@ +nft_to: + - to: ps3netsrv + proto: tcp + port: 38008 + +dnsmasq: + - name: ps3.oyacoi.ru + ip: 10.10.0.2 diff --git a/inventory/host_vars/radicale.yml b/inventory/host_vars/radicale.yml new file mode 100644 index 0000000..ae1b1c2 --- /dev/null +++ b/inventory/host_vars/radicale.yml @@ -0,0 +1,3 @@ +dnsmasq: + - name: radicale.oyacoi.ru + ip: 10.10.0.2 diff --git a/inventory/host_vars/rbpi4.yml b/inventory/host_vars/rbpi4.yml new file mode 100644 index 0000000..0b61889 --- /dev/null +++ b/inventory/host_vars/rbpi4.yml @@ -0,0 +1,3 @@ +dnsmasq: + - name: rustdesk.dttx.ru + ip: 176.119.157.97 diff --git a/inventory/host_vars/router.yml b/inventory/host_vars/router.yml new file mode 100644 index 0000000..5dcb9d9 --- /dev/null +++ b/inventory/host_vars/router.yml @@ -0,0 +1,6 @@ +ansible_host: 10.1.0.1 +ansible_connection: ssh +ansible_user: root +ansible_ssh_private_key_file: ~/.ssh/id_ed25519 +zone_iface: eth0 +container_ip: 10.1.0.1 diff --git a/inventory/host_vars/rtorrent.yml b/inventory/host_vars/rtorrent.yml new file mode 100644 index 0000000..0dad120 --- /dev/null +++ b/inventory/host_vars/rtorrent.yml @@ -0,0 +1,13 @@ +nft_dst: + - iface: eth1 + proto: tcp + port: ["6890-6899"] + +nft_from: + - iface: eth1 + proto: tcp + port: ["6890-6899"] + +dnsmasq: + - name: rutorrent.oyacoi.ru + ip: 10.10.0.2 diff --git a/inventory/host_vars/runner.yml b/inventory/host_vars/runner.yml new file mode 100644 index 0000000..ccc1593 --- /dev/null +++ b/inventory/host_vars/runner.yml @@ -0,0 +1,4 @@ +nft_to: + - to: firebat + proto: tcp + port: [22, 8006] diff --git a/inventory/host_vars/slskd.yml b/inventory/host_vars/slskd.yml new file mode 100644 index 0000000..4b399cb --- /dev/null +++ b/inventory/host_vars/slskd.yml @@ -0,0 +1,13 @@ +nft_dst: + - iface: eth1 + proto: tcp + port: 50300 + +nft_from: + - iface: eth1 + proto: tcp + port: 50300 + +dnsmasq: + - name: slskd.oyacoi.ru + ip: 10.10.0.2 diff --git a/inventory/host_vars/steamcmd.yml b/inventory/host_vars/steamcmd.yml new file mode 100644 index 0000000..ca4773d --- /dev/null +++ b/inventory/host_vars/steamcmd.yml @@ -0,0 +1,9 @@ +nft_dst: + - iface: eth0 + proto: udp + port: 2456 + +nft_from: + - iface: [eth0,wg0] + proto: udp + port: [2456,2457] diff --git a/inventory/host_vars/torrserver.yml b/inventory/host_vars/torrserver.yml new file mode 100644 index 0000000..de6bac5 --- /dev/null +++ b/inventory/host_vars/torrserver.yml @@ -0,0 +1,18 @@ +nft_dst: + - iface: eth1 + proto: [tcp, udp] + port: 6990 + +nft_to: + - to: flaresolverr + proto: tcp + port: 8191 + +nft_from: + - iface: eth1 + proto: [tcp,udp] + port: 6990 + +dnsmasq: + - name: torrserver.oyacoi.ru + ip: 10.10.0.2 diff --git a/inventory/host_vars/vaultwarden.yml b/inventory/host_vars/vaultwarden.yml new file mode 100644 index 0000000..168a326 --- /dev/null +++ b/inventory/host_vars/vaultwarden.yml @@ -0,0 +1,8 @@ +nft_to: + - to: pgsql + proto: tcp + port: 5432 + +dnsmasq: + - name: vaultwarden.oyacoi.ru + ip: 10.10.0.2 diff --git a/inventory/host_vars/workuter.yml b/inventory/host_vars/workuter.yml new file mode 100644 index 0000000..838a64b --- /dev/null +++ b/inventory/host_vars/workuter.yml @@ -0,0 +1,10 @@ +nft_to: + - to: nfs + proto: [tcp, udp] + port: [2049, 111, 32765, 32767] + - to: [zone:eth0.10,zone:eth0.11,zone:eth0.12] + proto: tcp + port: 22 + - to: [xiawrt,rbpi4] + proto: tcp + port: 22 diff --git a/inventory/host_vars/xiawrt.yml b/inventory/host_vars/xiawrt.yml new file mode 100644 index 0000000..36e44dc --- /dev/null +++ b/inventory/host_vars/xiawrt.yml @@ -0,0 +1,8 @@ +nft_to: + - to: zabbix + proto: tcp + port: 10051 + +dnsmasq: + - name: xiawrt.oyacoi.ru + ip: 10.10.0.2 diff --git a/inventory/host_vars/zabbix.yml b/inventory/host_vars/zabbix.yml new file mode 100644 index 0000000..b38ea1a --- /dev/null +++ b/inventory/host_vars/zabbix.yml @@ -0,0 +1,11 @@ +nft_to: + - to: "zone:eth0.11" + proto: tcp + port: 10050 + - to: xiawrt + proto: tcp + port: 10050 + +dnsmasq: + - name: zabbix.oyacoi.ru + ip: 10.10.0.2 diff --git a/inventory/proxmox.yml b/inventory/proxmox.yml new file mode 100644 index 0000000..a581001 --- /dev/null +++ b/inventory/proxmox.yml @@ -0,0 +1,13 @@ +plugin: community.proxmox.proxmox +url: https://10.1.0.4:8006 +user: root@pam +password: "{{ lookup('env', 'PROXMOX_PASSWORD') }}" +validate_certs: false +want_facts: true + +filter_by_types: + - lxc + +compose: + zone_iface: "'eth0.' ~ proxmox_net0.tag" + container_ip: "proxmox_net0.ip | default('') | regex_replace('/.*', '')" diff --git a/inventory/static.yml b/inventory/static.yml new file mode 100644 index 0000000..7b1e203 --- /dev/null +++ b/inventory/static.yml @@ -0,0 +1,82 @@ +all: + children: + static: + hosts: + workuter: + container_ip: "10.1.0.2" + zone_iface: "eth0" + + oyacoi-odcm: + container_ip: "10.1.0.3" + zone_iface: "eth0" + + firebat: + container_ip: "10.1.0.4" + zone_iface: "eth0" + ansible_host: 10.1.0.4 + ansible_user: root + ansible_ssh_private_key_file: "~/.ssh/id_ed25519" + + ps2: + container_ip: "10.1.0.5" + zone_iface: "eth0" + + ps3: + container_ip: "10.1.0.6" + zone_iface: "eth0" + + tanix: + container_ip: "10.1.0.8" + zone_iface: "eth0" + + bananawrt: + container_ip: "10.1.0.100" + zone_iface: "eth0" + + ps4: + container_ip: "10.2.0.2" + zone_iface: "eth0.2" + + note13: + container_ip: "10.2.0.3" + zone_iface: "eth0.2" + + iphone11: + container_ip: "10.2.0.4" + zone_iface: "eth0.2" + + huawei-tablet: + container_ip: "10.2.0.5" + zone_iface: "eth0.2" + + uni: + container_ip: "10.2.0.6" + zone_iface: "eth0.2" + + papperwhite: + container_ip: "10.2.0.7" + zone_iface: "eth0.2" + + psp: + container_ip: "10.2.0.8" + zone_iface: "eth0.2" + + dsi: + container_ip: "10.2.0.9" + zone_iface: "eth0.2" + + 3ds: + container_ip: "10.2.0.10" + zone_iface: "eth0.2" + + camera0: + container_ip: "10.3.0.5" + zone_iface: "eth0.3" + + xiawrt: + container_ip: "10.250.250.1" + zone_iface: "wg0" + + rbpi4: + container_ip: "10.250.250.5" + zone_iface: "wg0" diff --git a/playbooks/dnsmasq.yml b/playbooks/dnsmasq.yml new file mode 100644 index 0000000..267c58f --- /dev/null +++ b/playbooks/dnsmasq.yml @@ -0,0 +1,5 @@ +--- +- hosts: router + become: true + roles: + - dnsmasq diff --git a/playbooks/nftables.yml b/playbooks/nftables.yml new file mode 100644 index 0000000..94576f1 --- /dev/null +++ b/playbooks/nftables.yml @@ -0,0 +1,14 @@ +--- +- hosts: router + become: true + roles: + - xray-lists + - dnsmasq + - nftables + + tasks: + - name: enable update timer + systemd: + name: xray-lists.timer + enabled: yes + state: started diff --git a/playbooks/router.yml b/playbooks/router.yml new file mode 100644 index 0000000..c5f241d --- /dev/null +++ b/playbooks/router.yml @@ -0,0 +1,15 @@ +--- +- hosts: router + become: yes + roles: + - router + - xray-lists + - dnsmasq + - nftables + + tasks: + - name: enable update timer + systemd: + name: xray-lists.timer + enabled: yes + state: started diff --git a/playbooks/xray-lists.yml b/playbooks/xray-lists.yml new file mode 100644 index 0000000..7952698 --- /dev/null +++ b/playbooks/xray-lists.yml @@ -0,0 +1,5 @@ +--- +- hosts: router + become: true + roles: + - xray-lists diff --git a/requirements.yml b/requirements.yml new file mode 100644 index 0000000..f440506 --- /dev/null +++ b/requirements.yml @@ -0,0 +1,2 @@ +collections: + - name: community.proxmox diff --git a/roles/dnsmasq/handlers/main.yml b/roles/dnsmasq/handlers/main.yml new file mode 100644 index 0000000..4b75bd2 --- /dev/null +++ b/roles/dnsmasq/handlers/main.yml @@ -0,0 +1,5 @@ +--- +- name: restart dnsmasq + ansible.builtin.service: + name: dnsmasq + state: restarted diff --git a/roles/dnsmasq/tasks/main.yml b/roles/dnsmasq/tasks/main.yml new file mode 100644 index 0000000..9b84308 --- /dev/null +++ b/roles/dnsmasq/tasks/main.yml @@ -0,0 +1,20 @@ +--- +- name: ensure /etc/dnsmasq.d exists + ansible.builtin.file: + path: /etc/dnsmasq.d + state: directory + mode: "0755" + +- name: render local + ansible.builtin.template: + src: 90-local.conf.j2 + dest: /etc/dnsmasq.d/90-local.conf + mode: "0644" + notify: restart dnsmasq + +- name: render domain + ansible.builtin.template: + src: 90-domains.conf.j2 + dest: /etc/dnsmasq.d/90-domains.conf + mode: "0644" + notify: restart dnsmasq diff --git a/roles/dnsmasq/templates/90-domains.conf.j2 b/roles/dnsmasq/templates/90-domains.conf.j2 new file mode 100644 index 0000000..30e760b --- /dev/null +++ b/roles/dnsmasq/templates/90-domains.conf.j2 @@ -0,0 +1,15 @@ +#jinja2: trim_blocks: True, lstrip_blocks: True +{% for item in groups[dnsmasq_managed_group] | sort %} + {% set client = hostvars[item] %} + {% if 'dnsmasq' in client and client.dnsmasq %} + {% set default_ip = client.container_ip | default(client.ansible_host | default(none)) %} + {% set domains = client.dnsmasq if (client.dnsmasq is iterable and client.dnsmasq is not string) else [client.dnsmasq] %} + {% for d in domains %} + {% set entry = d if (d is mapping) else {'name': d} %} + {% set ip = entry.ip | default(default_ip) %} + {% if ip %} +host-record={{ entry.name }},{{ ip }} + {% endif %} + {% endfor %} + {% endif %} +{% endfor %} diff --git a/roles/dnsmasq/templates/90-local.conf.j2 b/roles/dnsmasq/templates/90-local.conf.j2 new file mode 100644 index 0000000..bdc277c --- /dev/null +++ b/roles/dnsmasq/templates/90-local.conf.j2 @@ -0,0 +1,8 @@ +#jinja2: trim_blocks: True, lstrip_blocks: True +{% for item in groups[dnsmasq_managed_group] | sort %} + {% set client = hostvars[item] %} + {% set ip = client.container_ip | default(client.ansible_host | default(none)) %} + {% if ip %} +host-record={{ item }},{{ item }}.lan,{{ ip }} + {% endif %} +{% endfor %} diff --git a/roles/nftables/files/10-filter.nft b/roles/nftables/files/10-filter.nft new file mode 100644 index 0000000..766eeaf --- /dev/null +++ b/roles/nftables/files/10-filter.nft @@ -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" +} diff --git a/roles/nftables/files/10-nat.nft b/roles/nftables/files/10-nat.nft new file mode 100644 index 0000000..40a047d --- /dev/null +++ b/roles/nftables/files/10-nat.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" +} diff --git a/roles/nftables/files/20-vpn.nft b/roles/nftables/files/20-vpn.nft new file mode 100644 index 0000000..56cad03 --- /dev/null +++ b/roles/nftables/files/20-vpn.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 +} diff --git a/roles/nftables/files/30-proxy.nft b/roles/nftables/files/30-proxy.nft new file mode 100644 index 0000000..4aacf78 --- /dev/null +++ b/roles/nftables/files/30-proxy.nft @@ -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 +} diff --git a/roles/nftables/files/40-sets.nft b/roles/nftables/files/40-sets.nft new file mode 100644 index 0000000..761f079 --- /dev/null +++ b/roles/nftables/files/40-sets.nft @@ -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 } +} diff --git a/roles/nftables/files/nftables.conf b/roles/nftables/files/nftables.conf new file mode 100644 index 0000000..dcdee7a --- /dev/null +++ b/roles/nftables/files/nftables.conf @@ -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" +} diff --git a/roles/nftables/handlers/main.yml b/roles/nftables/handlers/main.yml new file mode 100644 index 0000000..62573de --- /dev/null +++ b/roles/nftables/handlers/main.yml @@ -0,0 +1,4 @@ +--- +- name: reload nftables + ansible.builtin.command: nft -f /etc/nftables.conf + listen: reload nftables diff --git a/roles/nftables/tasks/main.yml b/roles/nftables/tasks/main.yml new file mode 100644 index 0000000..965647d --- /dev/null +++ b/roles/nftables/tasks/main.yml @@ -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 diff --git a/roles/nftables/templates/90-dstnat.nft.j2 b/roles/nftables/templates/90-dstnat.nft.j2 new file mode 100644 index 0000000..02916c5 --- /dev/null +++ b/roles/nftables/templates/90-dstnat.nft.j2 @@ -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 %} diff --git a/roles/nftables/templates/90-forward.nft.j2 b/roles/nftables/templates/90-forward.nft.j2 new file mode 100644 index 0000000..fb1a47c --- /dev/null +++ b/roles/nftables/templates/90-forward.nft.j2 @@ -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 %} diff --git a/roles/router/files/ifupdown2/interfaces b/roles/router/files/ifupdown2/interfaces new file mode 100644 index 0000000..acb310e --- /dev/null +++ b/roles/router/files/ifupdown2/interfaces @@ -0,0 +1,52 @@ +auto lo +iface lo inet loopback + post-up ip rule add fwmark 0x1 lookup 100 2>/dev/null || true + post-up ip route add local 0.0.0.0/0 dev lo table 100 2>/dev/null || true + pre-down ip route del local 0.0.0.0/0 dev lo table 100 2>/dev/null || true + pre-down ip rule del fwmark 0x1 lookup 100 2>/dev/null || true + +auto eth0 +iface eth0 inet manual + address 10.1.0.1/24 + +auto eth0.2 +iface eth0.2 inet static + address 10.2.0.1/24 + vlan-raw-device eth0 + +auto eth0.3 +iface eth0.3 inet static + address 10.3.0.1/24 + vlan-raw-device eth0 + +auto eth0.4 +iface eth0.4 inet static + address 10.4.0.1/24 + vlan-raw-device eth0 + +auto eth0.10 +iface eth0.10 inet static + address 10.10.0.1/24 + vlan-raw-device eth0 + +auto eth0.11 +iface eth0.11 inet static + address 10.11.0.1/24 + vlan-raw-device eth0 + +auto eth0.12 +iface eth0.12 inet static + address 10.12.0.1/24 + vlan-raw-device eth0 + +auto eth1 +iface eth1 inet dhcp + +auto wg0 +iface wg0 inet manual + post-up ip route add 10.250.250.0/24 dev wg0 2>/dev/null || true + post-up ip rule add fwmark 0xc7 lookup 199 2>/dev/null || true + post-up ip route add default dev wg0 table 199 2>/dev/null || true + pre-down ip route del default dev wg0 table 199 2>/dev/null || true + pre-down ip rule del fwmark 0xc7 lookup 199 2>/dev/null || true + pre-down ip route del 10.250.250.0/24 dev wg0 2>/dev/null || true diff --git a/roles/router/files/sysctl/01-forwarding.conf b/roles/router/files/sysctl/01-forwarding.conf new file mode 100644 index 0000000..119d730 --- /dev/null +++ b/roles/router/files/sysctl/01-forwarding.conf @@ -0,0 +1 @@ +net.ipv4.ip_forward=1 diff --git a/roles/router/files/sysctl/02-rpfilter.conf b/roles/router/files/sysctl/02-rpfilter.conf new file mode 100644 index 0000000..1c50fee --- /dev/null +++ b/roles/router/files/sysctl/02-rpfilter.conf @@ -0,0 +1,2 @@ +net.ipv4.conf.all.rp_filter = 0 +net.ipv4.conf.wg0.rp_filter = 0 diff --git a/roles/router/handlers/main.yml b/roles/router/handlers/main.yml new file mode 100644 index 0000000..15c6aa3 --- /dev/null +++ b/roles/router/handlers/main.yml @@ -0,0 +1,3 @@ +--- +- name: reload ifupdown2 + command: ifreload -a diff --git a/roles/router/tasks/main.yml b/roles/router/tasks/main.yml new file mode 100644 index 0000000..47e34e2 --- /dev/null +++ b/roles/router/tasks/main.yml @@ -0,0 +1,6 @@ +--- +- name: include network configuration + include_tasks: network.yml + +- name: include xray-lists configuration + include_tasks: xray_lists.yml diff --git a/roles/router/tasks/network.yml b/roles/router/tasks/network.yml new file mode 100644 index 0000000..76f484f --- /dev/null +++ b/roles/router/tasks/network.yml @@ -0,0 +1,9 @@ +--- +- name: deploy ifupdown config + copy: + src: ifupdown2/interfaces + dest: /etc/network/interfaces + owner: root + group: root + mode: '0644' + notify: reload ifupdown2 diff --git a/roles/router/tasks/xray_lists.yml b/roles/router/tasks/xray_lists.yml new file mode 100644 index 0000000..a68dc14 --- /dev/null +++ b/roles/router/tasks/xray_lists.yml @@ -0,0 +1,99 @@ +--- +- name: install required system packages + apt: + name: + - python3-venv + - git + state: present + update_cache: yes + +- name: ensure base directories exist + file: + path: "{{ item }}" + state: directory + owner: root + group: root + mode: '0755' + loop: + - /opt/xray-lists + - /var/lib/xray-lists + +- name: clone xray-lists repository + git: + repo: 'https://gitea.oyacoi.ru/pyrschtjag/xray-lists' + dest: /opt/xray-lists-src + version: main + force: yes + +- name: check if xray-lists is installed + command: /opt/xray-lists/venv/bin/pip show xray-lists + register: pip_check + changed_when: false + failed_when: false + +- name: install xray-lists package into venv + command: /opt/xray-lists/venv/bin/pip install -e /opt/xray-lists-src[socks] + when: pip_check.rc != 0 + +- name: deploy update helper script + copy: + dest: /var/lib/xray-lists/update.sh + owner: root + group: root + mode: '0755' + content: | + #!/bin/sh + set -e + out=$(/opt/xray-lists/venv/bin/xray-lists) + echo "$out" + + dns_changed=0 + elements_changed=0 + + if echo "$out" | grep -A 10 "changed:" | grep -q "nftsets.conf"; then dns_changed=1; fi + if echo "$out" | grep -A 10 "changed:" | grep -q "\.elements\.nft"; then elements_changed=1; fi + + if [ "$dns_changed" -eq 1 ] && [ "$elements_changed" -eq 1 ]; then exit 12; + elif [ "$dns_changed" -eq 1 ]; then exit 10; + elif [ "$elements_changed" -eq 1 ]; then exit 11; + fi + exit 0 + +- name: deploy systemd service unit + copy: + dest: /etc/systemd/system/xray-lists.service + owner: root + group: root + mode: '0644' + content: | + [Unit] + Description=Update Xray lists + + [Service] + Type=oneshot + ExecStart=/bin/sh -c '\ + /var/lib/xray-lists/update.sh; \ + rc=$$?; \ + case "$$rc" in \ + 10) systemctl restart dnsmasq ;; \ + 11) nft -f /etc/nftables.conf ;; \ + 12) nft -f /etc/nftables.conf && systemctl restart dnsmasq ;; \ + esac' + +- name: deploy systemd timer unit + copy: + dest: /etc/systemd/system/xray-lists.timer + owner: root + group: root + mode: '0644' + content: | + [Unit] + Description=Run xray-lists update daily and on boot + + [Timer] + OnBootSec=5min + OnUnitActiveSec=12h + Persistent=true + + [Install] + WantedBy=timers.target diff --git a/roles/xray-lists/handlers/main.yml b/roles/xray-lists/handlers/main.yml new file mode 100644 index 0000000..835f6ae --- /dev/null +++ b/roles/xray-lists/handlers/main.yml @@ -0,0 +1,16 @@ +--- +- name: reload nftables + ansible.builtin.command: nft -f /etc/nftables.conf + listen: reload nftables + +- name: restart dnsmasq + ansible.builtin.service: + name: dnsmasq + state: restarted + +- name: restart xray-lists timer + ansible.builtin.systemd: + name: xray-lists.timer + state: restarted + daemon_reload: yes + listen: restart xray-lists timer diff --git a/roles/xray-lists/tasks/main.yml b/roles/xray-lists/tasks/main.yml new file mode 100644 index 0000000..1fc578f --- /dev/null +++ b/roles/xray-lists/tasks/main.yml @@ -0,0 +1,55 @@ +--- +- name: collect xray policy hosts + ansible.builtin.set_fact: + _xray_hosts_with_policy: >- + {{ + (_xray_hosts_with_policy | default([])) + + [{'inventory_hostname': item, 'xray_policy': hostvars[item].xray_policy}] + }} + loop: "{{ groups[xray_managed_group] }}" + when: hostvars[item].xray_policy is defined + +- name: validate xray policy sets + ansible.builtin.assert: + that: >- + (item.1.bypass | default(item.1.proxy)) == 'all' + or (item.1.bypass | default(item.1.proxy)) in xray_ip_sets + or (item.1.bypass | default(item.1.proxy)) in xray_domain_sets + or (item.1.bypass | default(item.1.proxy)) in (xray_static_sets | default([])) + fail_msg: >- + host {{ item.0.inventory_hostname }}: unknown xray set + '{{ item.1.bypass | default(item.1.proxy) }}' in xray_policy + loop: "{{ query('ansible.builtin.subelements', _xray_hosts_with_policy | default([]), 'xray_policy', {'skip_missing': True}) }}" + loop_control: + label: "{{ item.0.inventory_hostname }} -> {{ item.1 }}" + +- name: render xray-lists config + ansible.builtin.template: + src: xray-config.yaml.j2 + dest: /etc/xray-lists/config.yaml + mode: "0640" + notify: restart xray-lists timer + +- name: bootstrap empty config files + ansible.builtin.copy: + dest: "/etc/nftables.d/{{ item }}" + content: "" + force: false + mode: "0644" + loop: + - 90-sets.nft + - 90-proxy.nft + +- name: render nft sets + ansible.builtin.template: + src: 90-sets.nft.j2 + dest: /etc/nftables.d/90-sets.nft + mode: "0644" + notify: reload nftables + +- name: render proxy prerouting + ansible.builtin.template: + src: 90-proxy.nft.j2 + dest: /etc/nftables.d/90-proxy.nft + mode: "0644" + notify: reload nftables diff --git a/roles/xray-lists/templates/90-proxy.nft.j2 b/roles/xray-lists/templates/90-proxy.nft.j2 new file mode 100644 index 0000000..8500ef6 --- /dev/null +++ b/roles/xray-lists/templates/90-proxy.nft.j2 @@ -0,0 +1,31 @@ +#jinja2: trim_blocks: True, lstrip_blocks: True +{%- macro set_ref(name) -%} + {%- if name == 'all' -%} + 0.0.0.0/0 + {%- elif name in xray_ip_sets or name in (xray_static_sets | default([])) -%} + @{{ name }}_ip + {%- elif name in xray_domain_sets -%} + @{{ name_dom }}_dom + {%- else -%} + INVALID_XRAY_SET_{{ name }} + {%- endif -%} +{%- endmacro -%} + +{%- set rules_list = [] -%} +{%- for item in groups[xray_managed_group] | default([]) | sort -%} + {%- set client = hostvars[item] -%} + {%- if client.xray_policy is defined -%} + {%- set src_ip = client.container_ip | default(client.ansible_host | default(item)) -%} + {%- for rule in client.xray_policy -%} + {%- if rule.bypass is defined -%} + {%- set _ = rules_list.append("meta l4proto { tcp, udp } ip saddr " ~ src_ip ~ " ip daddr " ~ set_ref(rule.bypass) ~ " accept") -%} + {%- elif rule.proxy is defined -%} + {%- set _ = rules_list.append("meta l4proto { tcp, udp } ip saddr " ~ src_ip ~ " ip daddr " ~ set_ref(rule.proxy) ~ " tproxy ip to :" ~ (xray_tproxy_port | default(61219) | string) ~ " meta mark set " ~ (xray_fwmark | default('0x00000001')) ~ " accept") -%} + {%- endif -%} + {%- endfor -%} + {%- endif -%} +{%- endfor -%} + +{%- if rules_list | length > 0 -%} +{{- rules_list | join('\n') -}} +{%- endif -%} diff --git a/roles/xray-lists/templates/90-sets.nft.j2 b/roles/xray-lists/templates/90-sets.nft.j2 new file mode 100644 index 0000000..1681b27 --- /dev/null +++ b/roles/xray-lists/templates/90-sets.nft.j2 @@ -0,0 +1,15 @@ +#jinja2: trim_blocks: True, lstrip_blocks: True +{%- for id, item in xray_ip_sets.items() -%} +set {{ id }}_ip { + type ipv4_addr + flags interval + auto-merge + include "{{ xray_lists_global.output_dir }}/{{ id }}.elements.nft" +} +{% endfor -%} +{%- for id, item in xray_domain_sets.items() -%} +set {{ id }}_dom { + type ipv4_addr + flags interval +} +{% endfor -%} diff --git a/roles/xray-lists/templates/xray-config.yaml.j2 b/roles/xray-lists/templates/xray-config.yaml.j2 new file mode 100644 index 0000000..3f5e5c9 --- /dev/null +++ b/roles/xray-lists/templates/xray-config.yaml.j2 @@ -0,0 +1,47 @@ +#jinja2: trim_blocks: True, lstrip_blocks: True +global: + cache_dir: {{ xray_lists_global.cache_dir }} + output_dir: {{ xray_lists_global.output_dir }} + dnsmasq_output: {{ xray_lists_global.dnsmasq_output }} + {% if xray_lists_global.proxy is defined %} + proxy: "{{ xray_lists_global.proxy }}" + {% endif %} + {% if xray_lists_global.proxy_user is defined %} + proxy_user: "{{ xray_lists_global.proxy_user }}" + proxy_pass: "{{ xray_lists_global.proxy_pass }}" + {% endif %} + http_timeout: {{ xray_lists_global.http_timeout | default(20) }} +ip_sets: + {% for id, item in xray_ip_sets.items() %} + - id: {{ id }} + output: {{ id }}_ip.elements.nft + {% if item.static is defined %} + static: + {% for s in item.static %} + - {{ s }} + {% endfor %} + {% endif %} + {% if item.urls is defined %} + urls: + {% for u in item.urls %} + - {{ u }} + {% endfor %} + {% endif %} + {% endfor %} +domain_sets: + {% for id, item in xray_domain_sets.items() %} + - id: {{ id }} + dnsmasq_target: "4#inet#filter#{{ id }}_dom" + {% if item.static is defined %} + static: + {% for s in item.static %} + - {{ s }} + {% endfor %} + {% endif %} + {% if item.urls is defined %} + urls: + {% for u in item.urls %} + - {{ u }} + {% endfor %} + {% endif %} + {% endfor %}