Compare commits

...
14 Commits
Author SHA1 Message Date
pyrschtjag 33ddc88ee9 added xray-core role 2026-08-28 11:11:43 +00:00
pyrschtjag 6e248bb709 change router role 2026-08-28 11:11:42 +00:00
pyrschtjag 4e6aace464 change router playbook 2026-08-28 11:11:22 +00:00
pyrschtjag d5762dfc07 remove unused playbooks 2026-08-28 11:11:13 +00:00
pyrschtjag fa6a65aed2 change static inventory 2026-08-28 11:11:09 +00:00
pyrschtjag 47e6340bf0 init empty unbound role 2026-08-28 11:11:03 +00:00
pyrschtjag 681f384810 add dnsmasq role 2026-08-28 11:10:57 +00:00
pyrschtjag 66062e6122 change group_vars 2026-08-28 11:10:55 +00:00
pyrschtjag 271c290498 change nftables role 2026-08-28 11:10:51 +00:00
pyrschtjag abeb2e0eb9 add xray-lists role 2026-08-28 11:10:47 +00:00
pyrschtjag 98873cb5eb change host_inventory 2026-08-28 11:10:44 +00:00
pyrschtjag a928e0ec70 add .gitignore 2026-08-28 11:10:40 +00:00
pyrschtjag 7ce01c7173 add vault 2026-08-28 11:10:34 +00:00
pyrschtjag 3020de7dc1 change host_vars 2026-08-28 11:10:12 +00:00
62 changed files with 805 additions and 290 deletions
+1
View File
@@ -0,0 +1 @@
.vault_pass
+1
View File
@@ -3,6 +3,7 @@ inventory = inventory/
roles_path = roles/
host_key_checking = False
forks = 8
vault_password_file = .vault_pass
[inventory]
enable_plugins = community.proxmox.proxmox, host_list, yaml, ini
+1
View File
@@ -1,3 +1,4 @@
nft_managed_group: all
dnsmasq_managed_group: all
xray_managed_group: all
xray_core_group: all
+103
View File
@@ -0,0 +1,103 @@
$ANSIBLE_VAULT;1.1;AES256
36306165333832633935626535303038333964363533393135303736393561653763666534626538
3931363931616363643366656130616236646538303737380a343234643634316632326137613535
64316133356635323935623162366533313563316335376439336534323234623038373038373361
3933353334353939660a656163383564313632393434653435376437643538613138383764336364
35356235666661303736373335333139653530346335313731343136303039396661633164383433
35373935633136343764356439333865303032353864373138313736623666626563343362626264
63613261376163326633626234376339326132303930396562356631306463316361643334643938
62616665346336373630393833626430386233343539636336383539383232643766386339323433
31313764356338643533383637303165393064303233373363656435623261653763376138333863
62643366303866656433376561323739393334663361653166653366303835373863633737316231
64383336373535343539633365616562386361353532373465386461363863393266313237626634
61656663373833376330316631653161373130303639313231306134323630356335383561316564
61343835333533633166316431323234383837393734313630643065313132396234343064343764
39353865633865333862666364666434386533313534306236346133663031393664353664336362
34616438356337303536613832386635353565653362366533386436626564613038333938646561
37643931653633323165303638336535366337643465376335366135373331336633356466366630
38356535303438626438646239303933373366363836336364613333336234343033343932346561
64343834353638323235616466346130353431333864386637653636346536323462623430383661
31336661643135303331323434653964306133376237326263333265376230353737623236656234
63666661633330613933366462346262363532336437653062363837366533306131383634616534
34323234343839306265626261323565326164633239616461363930386164373564633062323331
36383439313730343532373065663665396236336335646465613931323563623734656164653138
32646665326162666462396265663638333531336231316462393536356163626466306663643266
35663162313836316361316136323636613532343366653437656666343731643863653031373961
36383065626431643830623362303931306634316561343961623464656562323830656435646464
37353532666635653433363930333862626332663233646565383061646164353332646330303239
30663635343637633431643538346263376366306434333334623566326336396432626264396265
63386536333139633438353163656132626533313332376633336165373662616561373532363939
63303132616465363534623664343533313164353866313131653538643837353764663837393661
65346431376435303364633835323431663064366532343737356339303462323733303134396230
33303665646133663365356638653637633163313863393564343661326666663335636338613531
37303333316166353536633762343265383139326431383936643464363761353330353864303239
37633236306266636165393732656537616161613165653265366562376664313964323939333861
37326235613835623530353531376262383165616232613535316634646135313138393131343737
39346233623330643863393762393638316164303066353762623139343730656334613035336430
65666235346663616131383630663033646330396333303666333639636433323065663232613564
61313337363138353234363530613964353530383261346661336465373266356135633030363239
61626664633335336631383661613465613037366237643939623862653264323136623436623836
61366132313338313934663435626366643838313835653730366131616238313133306232346439
37396263653462346139353638646663383130383634626234373034366536366662643539656530
61363436383137306535633765636564313832303835643831666562323165623032633835636639
61303831393037303464623561326265336662613932316666633133653161346537303965373931
63323633396438383131316661353435363130346262343862373037646536376363363039613864
63333065626637326465353033643065313837393830376161383033383265363866323533616539
63303532343761643636316336313031633330366332666566386234343339663733373866646435
36386338303863353136373336356432386531366237393866653931363537363361313035633438
30393864393639326562393039323561316531396437326535663932626663313832393232373939
35393439336562613031366637363536333938313534663035343839363534356137303064333030
37353066313031326563666531383062396665643437666333623232333662373739656263633463
36393933393239373939346438366266623937393634633139393362613335393832303262393038
31323733663736626139376566363863303439386161623834363533613433373631666334396631
30316633623336613136643666363738633133393966303938643432626638373037643139343538
31303633653039663131623839643363636133646230626231353765613665326638376663613265
61303131663137313465353036636362316139333566316632363265656461323939666161653861
37336664313039353533336334306461326363323536386366376634383437633862356563366234
35663662316266373837393663643733613931326464323133313134333964626161303564383931
61663335343462353237396438353366396535306364363436343739393864633232623463323934
32353933326337616361396365323835373333333030373762386536313534396434386537623835
61393633616265633664636432303162333262656135343339313235656565633364383461383031
63333138383263646563643039306134366138383137366466316331636339653066636331643036
35303238626566393663663139343362363438383436316635363433303530666435323232386431
62663365643961356137333933353230366161313463653865356432616232373833346239646361
38356339313937396632633033326337353434653361303530373963343163653363363134323836
35323639333261636563346435623334366635316139656434356165646362613031383931393766
39643530303966653830636363336334326336303438386364316263303639623236613632326637
33313837333232613735353831393038376433336436646530663265396466333762323332383030
36646237653731363236663935333862336533383438646536376336333633326333383530613765
37373937396264643761353762383335373036313230303661353239313362363630326232323735
31653830663838666634643232346235353266323061636563646630636339613064306339363961
34343664646434326137643436333362633763363133656332666335636265363662383235316533
37316632373135646637393565316131396235353662396139323962363939386666323134306530
31363034373661626131633366313438616465306464393330303263306665646135396436313230
32376232613763326336326266323637626530636562653534313431343839643034333663323336
31303530636336366430353066316335386535616265626632376631393237633563383763333938
34373835326336646230636535643531326639326566376237353835643632323432393132396130
66323864636438346464363466346263393765633966646263363030656266356330636139316565
33396365336235356639343432393238343264653163316663303235343038663262326237613363
30663136633436663431323663653337656235313335323732373738336335646264656534666236
35663339663762313135313732653766363139373130366330646537663435383438656637353134
66396233656162316164386564366232666265303230303032323663663538373237326236396337
34396265653730626566336437373564636461636433393133343933626630393035343634326338
64656231653361306262316339613938613432353137393962383036633164616531326236366664
39613939356265366433653966323566396138323935303137313739373038626162623465366437
66313133623231666361666236316666303533383430663834666139616131366161313563353063
65316130396135346332343338653231646437623761623231343135666330643532643665656162
65636334353637376634646139313135383564363435666333363431326332333131633131623861
34626563376135366365316466653539306465653437376263363163663964656436303631343531
39353936303566303661376331323862323532356637666535326539626637393666333264663734
33303536613164623437613834386562616565373438663065643663316665373331633232343330
34343535666662396238313135326564303665373231386361383135666437636435303831316662
61656238336236333963363637363030313537356662633130633332636564306131623262383535
64326536616235623038393363323766633736333131666361623961353434623738376135353332
35643664356566653035326235363464633233336534646639383662333438333530373930623665
65306634383539323064313235656531623261626535383832356263396539636433316434323632
39303335346662613232626231353938336362636266303538363234646163663038313663313765
66313365303738303262633061346530343966653830663535363164626665366239333030343833
66326364376238626263336666393665346630383534313261623931343062353432366434653566
39616530313837633335376435306533353638333734623766343732643064653363633763373134
30623962333761303833393339313931633633323561303765366565323333666633313563343132
62346139613131626664363735336330636264666638343330336238636338386263363339383963
30393236623930376235353532646432616331373637303261346264623133643738623163663035
3666313562366662363833356165343337336264336264393261
@@ -0,0 +1,4 @@
xray_id: "{{ encrypted_xray_id }}"
xray_xhttp_path: "{{ encrypted_xray_xhttp_path }}"
xray_encryption: "{{ encrypted_xray_encryption }}"
xray_outbounds: "{{ encrypted_xray_outbounds }}"
+6 -2
View File
@@ -43,6 +43,7 @@ xray_domain_sets:
- rutracker.org
- rutracker.net
- tapochek.net
- bt.tapochek.net
- nnmclub.to
- rutor.info
- bigfangroup.org
@@ -57,6 +58,9 @@ xray_domain_sets:
- terraform.io
- hashicorp.com
output_rules:
- cloudflare
xray_static_sets:
- private
@@ -65,8 +69,8 @@ xray_lists_global:
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') }}"
proxy_user: "{{ encrypted_proxy_user }}"
proxy_pass: "{{ encrypted_proxy_pass }}"
http_timeout: 20
xray_tproxy_port: 61219
+2 -4
View File
@@ -1,4 +1,2 @@
nft_from:
- iface: [eth1,eth0.2]
to: camera0
proto: [tcp,udp]
dhcp-host:
- mac: "b8:88:80:92:b5:4c"
+2
View File
@@ -0,0 +1,2 @@
dhcp-host:
- mac: "d4:f0:ea:78:ec:a0"
+5
View File
@@ -1,3 +1,8 @@
nft_to:
- to: pgsql
proto: tcp
port: 5432
nft_from:
- iface: wg0
proto: tcp
+19 -1
View File
@@ -1,4 +1,22 @@
nft_to:
- to: nginx
proto: tcp
port: [80, 81, 443, 444, 24445]
port: [80,81,443,444,24445,5222,5223,5269,5000,5270,5280]
- to: coturn
proto: tcp
port: [3478,5349]
- to: coturn
proto: udp
port: [3478,5349,"49152-65535"]
- to: mcsmanager
proto: tcp
port: 25565
- to: steamcmd
proto: udp
port: [2456,2457]
- to: rbpi4
proto: tcp
port: "21114-21119"
- to: rbpi4
proto: udp
port: 21116
+4
View File
@@ -0,0 +1,4 @@
xray_policy:
- bypass: private
- bypass: russian_whitelist
- proxy: all
+6
View File
@@ -35,6 +35,12 @@ nft_to:
- to: bylampa
proto: tcp
port: 80
- to: firebat
proto: tcp
port: 8006
- to: mcsmanager
proto: tcp
port: [23333,24444]
nft_from:
- iface: [eth0,eth0.2]
+2 -2
View File
@@ -1,7 +1,7 @@
nft_to:
- to: nfs
proto: [tcp, udp]
port: [2049, 111, 32765, 32767]
proto: [tcp,udp]
port: [2049,111,32765,32767]
- to: [zone:eth0.10,zone:eth0.11,zone:eth0.12]
proto: tcp
port: 22
+3
View File
@@ -2,6 +2,9 @@ nft_to:
- to: firebat
proto: tcp
port: [22, 8006]
- to: nginx
proto: tcp
port: 443
xray_policy:
- proxy: terraform
+2
View File
@@ -0,0 +1,2 @@
dhcp-host:
- mac: "c8:5c:cc:91:71:58"
+2
View File
@@ -0,0 +1,2 @@
dhcp-host:
- mac: "ac:ba:c0:9c:1e:4c"
+23 -7
View File
@@ -57,22 +57,38 @@ all:
container_ip: "10.2.0.7"
zone_iface: "eth0.2"
psp:
3ds:
container_ip: "10.2.0.8"
zone_iface: "eth0.2"
dsi:
container_ip: "10.2.0.9"
zone_iface: "eth0.2"
yandex-lite-2:
container_ip: "10.3.0.2"
zone_iface: "eth0.3"
3ds:
container_ip: "10.2.0.10"
zone_iface: "eth0.2"
fryer:
container_ip: "10.3.0.3"
zone_iface: "eth0.3"
vacuum:
container_ip: "10.3.0.4"
zone_iface: "eth0.3"
camera0:
container_ip: "10.3.0.5"
zone_iface: "eth0.3"
psp:
container_ip: "10.4.0.2"
zone_iface: "eth0.4"
dsi:
container_ip: "10.4.0.3"
zone_iface: "eth0.4"
haproxy:
container_ip: "10.255.255.100"
zone_iface: "wg0"
xiawrt:
container_ip: "10.250.250.1"
zone_iface: "wg0"
-5
View File
@@ -1,5 +0,0 @@
---
- hosts: router
become: true
roles:
- dnsmasq
-14
View File
@@ -1,14 +0,0 @@
---
- hosts: router
become: true
roles:
- xray-lists
- dnsmasq
- nftables
tasks:
- name: enable update timer
systemd:
name: xray-lists.timer
enabled: yes
state: started
+1 -7
View File
@@ -4,12 +4,6 @@
roles:
- router
- xray-lists
- unbound
- dnsmasq
- nftables
tasks:
- name: enable update timer
systemd:
name: xray-lists.timer
enabled: yes
state: started
-5
View File
@@ -1,5 +0,0 @@
---
- hosts: router
become: true
roles:
- xray-lists
+5
View File
@@ -0,0 +1,5 @@
---
- hosts: router
become: yes
roles:
- xray-core
+14
View File
@@ -0,0 +1,14 @@
interface=lo
interface=eth0
interface=eth0.2
interface=eth0.3
interface=eth0.4
interface=eth0.10
interface=eth0.11
interface=eth0.12
bind-dynamic
no-resolv
server=127.0.0.1#5353
#server=1.1.1.1
domain=lan
local=/lan/
@@ -0,0 +1,10 @@
server=/dev.oyacoi.ru/9.9.9.9
server=/vector.oyacoi.ru/9.9.9.9
server=/.themoviedb.org/9.9.9.9
server=/.tmdb.org/9.9.9.9
server=/tmdb-image-prod.b-cdn.net/9.9.9.9
server=/infolada.ru/217.113.115.150
server=/infolada.ru/217.113.114.100
server=/start.infolada.ru/217.113.115.150
server=/start.infolada.ru/217.113.114.100
conf-file=/var/lib/xray-lists/generated/nftsets.conf
+3
View File
@@ -0,0 +1,3 @@
dhcp-range=interface:eth0.3,10.3.0.200,10.3.0.254,255.255.255.0,2h
dhcp-option=interface:eth0.3,option:router,10.3.0.1
dhcp-option=interface:eth0.3,option:dns-server,10.3.0.1
@@ -0,0 +1,4 @@
filterwin2k
domain-needed
bogus-priv
cache-size=0
+19
View File
@@ -5,6 +5,18 @@
state: directory
mode: "0755"
- name: deploy dnsmasq rule
ansible.builtin.copy:
src: "{{ item }}"
dest: "/etc/dnsmasq.d/{{ item }}"
mode: "0644"
loop:
- 10-upstream.conf
- 20-custom-domains.conf
- 20-dhcp.conf
- 20-dns-optimizations.conf
notify: restart dnsmasq
- name: render local
ansible.builtin.template:
src: 90-local.conf.j2
@@ -12,6 +24,13 @@
mode: "0644"
notify: restart dnsmasq
- name: render dhcp-host
ansible.builtin.template:
src: 90-dhcp-host.conf.j2
dest: /etc/dnsmasq.d/90-dhcp-host.conf
mode: "0644"
notify: restart dnsmasq
- name: render domain
ansible.builtin.template:
src: 90-domains.conf.j2
@@ -0,0 +1,13 @@
#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 client['dhcp-host'] is defined and client['dhcp-host'] and ip %}
{% set entries = client['dhcp-host'] if (client['dhcp-host'] is iterable and client['dhcp-host'] is not string) else [client['dhcp-host']] %}
{% for entry in entries %}
{% if entry.mac %}
dhcp-host={{ entry.mac }},{{ ip }},{{ item }}
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
-2
View File
@@ -10,8 +10,6 @@ chain input {
ct state invalid drop
iif lo accept
ip protocol icmp accept
ip6 nexthdr icmpv6 accept
meta mark 0x00000001 accept
+5 -4
View File
@@ -3,13 +3,14 @@ chain proxy_prerouting {
fib daddr type local accept
include "/etc/nftables.d/90-proxy.nft"
meta mark 0x00000001 iif "lo" meta l4proto { tcp, udp } tproxy ip to :61219 counter accept
include "/etc/nftables.d/90-proxy-prerouting.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
meta mark != 0 return
include "/etc/nftables.d/90-proxy-output.nft"
}
+2 -2
View File
@@ -1,4 +1,4 @@
---
- name: reload nftables
- name: restart nftables
ansible.builtin.command: nft -f /etc/nftables.conf
listen: reload nftables
listen: restart nftables
+41
View File
@@ -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: restart nftables
- name: render forward
ansible.builtin.template:
src: 90-forward.nft.j2
dest: /etc/nftables.d/90-forward.nft
mode: "0644"
notify: restart nftables
- name: render dstnat
ansible.builtin.template:
src: 90-dstnat.nft.j2
dest: /etc/nftables.d/90-dstnat.nft
mode: "0644"
notify: restart nftables
- name: deploy nftables.conf
ansible.builtin.copy:
src: nftables.conf
dest: /etc/nftables.conf
mode: "0644"
validate: "nft -c -f %s"
notify: restart nftables
+5
View File
@@ -0,0 +1,5 @@
---
- name: install nftables
ansible.builtin.package:
name: nftables
state: present
+4 -39
View File
@@ -1,41 +1,6 @@
---
- name: ensure /etc/nftables.d exists
ansible.builtin.file:
path: /etc/nftables.d
state: directory
mode: "0755"
- name: include nftables install
ansible.builtin.include_tasks: install.yml
- 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
- name: include nftables configurure
ansible.builtin.include_tasks: configure.yml
+10 -22
View File
@@ -1,31 +1,19 @@
#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) }}
{% set target_ip = client.container_ip %}
{% for client in client.nft_dst %}
{% set ports = client.port if (client.port is iterable and client.port is not string) else [client.port] %}
{% set protos = client.proto if (client.proto is iterable and client.proto is not string) else [client.proto] %}
{% set ifaces = client.iface if (client.iface is iterable and client.iface is not string) else [client.iface] %}
{% for proto in protos %}
{% for port in ports %}
{% for iface in ifaces %}
iifname "{{ iface }}" {{ proto }} dport {{ port }} counter dnat ip to {{ target_ip }}:{{ port }} comment "{{ iface }} -> {{ item }}"
{% endfor %}
{% endfor %}
{% endfor %}
{% endfor %}
{% endif %}
{% endfor %}
{% endfilter %}
+37 -85
View File
@@ -1,95 +1,47 @@
#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(' ')) %}
{% for item in groups[nft_managed_group] | sort %}
{% set client = hostvars[item] %}
{% if 'nft_to' in client and client.nft_to is not none %}
{% set rules = client.nft_to if (client.nft_to is iterable and client.nft_to is not string) else [client.nft_to] %}
{% for rule in rules %}
{% set rule = rule if rule is mapping else {'to': rule} %}
{% set dests = rule.to if (rule.to is iterable and rule.to is not string) else [rule.to] %}
{% set protos = rule.proto if (rule.proto is iterable and rule.proto is not string) else [rule.proto | default(none)] %}
{% set ports = rule.port if (rule.port is iterable and rule.port is not string) else [rule.port | default(none)] %}
{% for dest in dests %}
{% if dest.startswith('zone:') %}
{% set oif = dest.split(':')[1] %}
{% set daddr = none %}
{% set dest_name = oif %}
{% else %}
{% set oif = hostvars[dest].zone_iface %}
{% set daddr = hostvars[dest].container_ip %}
{% set dest_name = dest %}
{% endif %}
{% for proto in protos %}
{% for port in ports %}
iifname "{{ client.zone_iface }}" ip saddr {{ client.container_ip }} oifname "{{ oif }}" {% if daddr %}ip daddr {{ daddr }} {% endif %}{% if proto and port %}{{ proto }} dport {{ port }} {% endif %}counter accept comment "{{ item }} -> {{ dest_name }}"
{% endfor %}
{% endfor %}
{% endfor %}
{% endfor %}
{% endfor %}
{{ lines | join('\n') }}
{% endmacro %}
{% filter regex_replace('\n[ \t]*\n+', '\n') %}
{# === Managed Hosts Forward Rules === #}
{% endif %}
{% endfor %}
{% 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 %}
{% if 'nft_from' in client and client.nft_from is not none %}
{% set rules = client.nft_from if (client.nft_from is iterable and client.nft_from is not string) else [client.nft_from] %}
{% for rule in rules %}
{% set ifaces = rule.iface if (rule.iface is iterable and rule.iface is not string) else [rule.iface] %}
{% set protos = rule.proto if (rule.proto is iterable and rule.proto is not string) else [rule.proto | default(none)] %}
{% set ports = rule.port if (rule.port is iterable and rule.port is not string) else [rule.port | default(none)] %}
{% for iface in ifaces %}
{% for proto in protos %}
{% for port in ports %}
iifname "{{ iface }}" oifname "{{ client.zone_iface }}" ip daddr {{ client.container_ip }} {% if proto and port %}{{ proto }} dport {{ port }} {% endif %}counter accept comment "{{ iface }} -> {{ item }}"
{% endfor %}
{% endfor %}
{% 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 %}
+3 -2
View File
@@ -1,2 +1,3 @@
net.ipv4.conf.all.rp_filter = 0
net.ipv4.conf.wg0.rp_filter = 0
net.ipv4.conf.lo.rp_filter=0
net.ipv4.conf.all.rp_filter=0
net.ipv4.conf.wg0.rp_filter=0
-3
View File
@@ -1,6 +1,3 @@
---
- name: include network configuration
include_tasks: network.yml
- name: include xray-lists configuration
include_tasks: xray_lists.yml
+58
View File
@@ -0,0 +1,58 @@
server:
verbosity: 3
port: 5353
interface: 127.0.0.1
#interface: 10.1.0.1
do-ip4: yes
do-ip6: no
do-udp: yes
do-tcp: yes
num-threads: 4
msg-cache-slabs: 4
rrset-cache-slabs: 4
infra-cache-slabs: 4
key-cache-slabs: 4
msg-cache-size: 64m
rrset-cache-size: 128m
key-cache-size: 32m
neg-cache-size: 4m
cache-min-ttl: 300
cache-max-ttl: 86400
prefetch: yes
prefetch-key: yes
serve-expired: yes
edns-buffer-size: 1232
so-reuseport: yes
auto-trust-anchor-file: "/var/lib/unbound/root.key"
harden-glue: yes
harden-dnssec-stripped: yes
qname-minimisation: yes
hide-identity: yes
hide-version: yes
tls-system-cert: yes
pad-queries: yes
pad-queries-block-size: 128
access-control: 127.0.0.0/8 allow
access-control: 10.0.0.0/8 allow
local-zone: "10.0.in-addr.arpa." nodefault
local-zone: "lan." static
insecure-lan-zones: yes
ip-ratelimit: 200
ip-ratelimit-slabs: 4
tcp-connection-limit: 10.0.0.0/8 64
forward-zone:
name: "brawlstarsgame.com"
forward-tls-upstream: yes
forward-addr: 45.139.239.56@853#dns.nullsproxy.com
forward-addr: 141.95.97.120@853#dns.nullsproxy.com
forward-addr: 179.43.147.42@853#dns.nullsproxy.com
forward-addr: 185.211.245.131@853#dns.nullsproxy.com
forward-addr: 82.27.0.149@853#dns.nullsproxy.com
forward-addr: 81.17.20.83@853#dns.nullsproxy.com
forward-zone:
name: "."
forward-tls-upstream: yes
forward-addr: 1.1.1.1@853#cloudflare-dns.com
forward-addr: 1.0.0.1@853#cloudflare-dns.com
+6
View File
@@ -0,0 +1,6 @@
---
- name: restart unbound
ansible.builtin.service:
name: unbound
state: restarted
listen: restart unbound
+15
View File
@@ -0,0 +1,15 @@
---
- name: ensure /etc/unbound exists
ansible.builtin.file:
path: /etc/unbound
state: directory
mode: "0755"
- name: deploy unbound config
ansible.builtin.copy:
src: "{{ item }}"
dest: "/etc/unbound/{{ item }}"
mode: "0744"
loop:
- unbound.conf
notify: restart unbound
+5
View File
@@ -0,0 +1,5 @@
---
- name: install unbound
ansible.builtin.package:
name: unbound
state: present
+6
View File
@@ -0,0 +1,6 @@
---
- name: include unbound install
ansible.builtin.include_tasks: install.yml
- name: include unbound configurure
ansible.builtin.include_tasks: configure.yml
+7
View File
@@ -0,0 +1,7 @@
{
"dns": {
"tag": "dns-in",
"servers": ["localhost"],
"queryStrategy": "UseIPv4"
}
}
+35
View File
@@ -0,0 +1,35 @@
{
"inbounds": [
{
"port": 61219,
"listen": "127.0.0.1",
"protocol": "dokodemo-door",
"settings": {
"followRedirect": true,
"network": "tcp,udp"
},
"streamSettings": {
"sockopt": {
"tproxy": "tproxy"
}
},
"tag": "tproxy"
},
{
"tag": "socks-in",
"ip": "127.0.0.1",
"port": 1080,
"protocol": "socks",
"settings": {
"auth": "password",
"accounts": [
{
"user": "embargo",
"pass": "moistnes12"
}
],
"udp": true
}
}
]
}
+9
View File
@@ -0,0 +1,9 @@
{
"log": {
"access": "/var/log/xray-core/access.log",
"error": "/var/log/xray-core/error.log",
"loglevel": "warning",
"dnsLog": false,
"maskAddress": ""
}
}
+23
View File
@@ -0,0 +1,23 @@
{
"policy": {
"levels": {
"0": {
"handshake": 4,
"connIdle": 300,
"uplinkOnly": 2,
"downlinkOnly": 5,
"statsUserUplink": false,
"statsUserDownlink": false,
"statsUserOnline": false,
"bufferSize": 512
}
},
"system": {
"statsInboundUplink": false,
"statsInboundDownlink": false,
"statsOutboundUplink": false,
"statsOutboundDownlink": false
}
}
}
+6
View File
@@ -0,0 +1,6 @@
---
- name: restart xray-core
ansible.builtin.service:
name: xray-core
state: restarted
listen: restart xray-core
+33
View File
@@ -0,0 +1,33 @@
---
- name: ensure /etc/xray-core exists
ansible.builtin.file:
path: /etc/xray-core/config
state: directory
mode: "0755"
- name: ensure /var/log/xray-core exists
ansible.builtin.file:
path: /var/log/xray-core
state: directory
mode: "0755"
- name: deploy static xray-core config
ansible.builtin.copy:
src: "{{ item }}"
dest: "/etc/xray-core/config/{{ item }}"
mode: "0744"
loop:
- dns.jsonc
- inbounds.jsonc
- log.jsonc
- policy.jsonc
- name: deploy dynamic xray-core config
ansible.builtin.template:
src: "{{ item }}.j2"
dest: "/etc/xray-core/config/{{ item }}"
mode: "0744"
loop:
- observatory.jsonc
- outbounds.jsonc
- routing.jsonc
+5
View File
@@ -0,0 +1,5 @@
---
- name: install unbound
ansible.builtin.package:
name: unbound
state: present
+6
View File
@@ -0,0 +1,6 @@
---
#- name: include unbound install
# ansible.builtin.include_tasks: install.yml
- name: include xray-core configurure
ansible.builtin.include_tasks: configure.yml
@@ -0,0 +1,8 @@
{
"observatory": {
"subjectSelector": ["vless-"],
"probeUrl": "https://www.google.com/generate_204",
"probeInterval": "30s",
"enableConcurrency": true
}
}
@@ -0,0 +1,67 @@
{
"outbounds": [
{% for item in xray_outbounds %}
{
"tag": "vless-{{ item.tag }}",
"protocol": "vless",
"settings": {
"vnext": [
{
"address": "{{ item.address }}",
"port": 443,
"users": [
{
"id": "{{ xray_id }}",
"flow": "xtls-rprx-vision",
"encryption": "{{ xray_encryption }}"
}
]
}
],
"domainStrategy": "UseIPv4"
},
"streamSettings": {
"network": "xhttp",
"xhttpSettings": {
"path": "{{ xray_xhttp_path }}",
"mode": "stream-one"
},
"security": "tls",
"tlsSettings": {
"alpn": [
"h2",
"h3"
],
"fingerprint": "firefox"
},
"sockopt": {
"mark": 255
}
}
},
{% endfor %}
{
"tag": "direct",
"protocol": "freedom",
"settings": {
"domainStrategy": "UseIPv4"
},
"streamSettings": {
"sockopt": {
"mark": 255,
"interface": "eth1",
"tcpFastOpen": true
}
}
},
{
"tag": "blocked",
"protocol": "blackhole",
"settings": {
"response": {
"type": "none"
}
}
}
]
}
@@ -0,0 +1,41 @@
{
"routing": {
"domainStrategy": "IPIfNonMatch",
{% if xray_outbounds | length > 1 %}
"balancers": [
{
"tag": "balancer-vless",
"selector": ["vless-"],
"strategy": {
"type": "leastLoad",
"settings": {
"costs": [
{% for item in xray_outbounds %}
{
"match": "vless-{{ item.tag }}",
"value": {{ item.value }}
}{{ "," if not loop.last else "" }}
{% endfor %}
]
}
}
}
],
{% endif %}
"rules": [
{
"type": "field",
"protocol": ["bittorrent"],
"outboundTag": "direct"
},
{
"type": "field",
"inboundTag": [
"tproxy",
"socks-in"
],
"balancerTag": "{{ 'balancer-vless' if xray_outbounds | length > 1 else 'vless-' ~ xray_outbounds[0].tag }}"
}
]
}
}
+62
View File
@@ -0,0 +1,62 @@
---
#- 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) }}'"
# quiet: true
# loop: "{{ query('ansible.builtin.subelements', _xray_hosts_with_policy | default([]), 'xray_policy', {'skip_missing': True}) }}"
# loop_control:
# label: "{{ item.0.inventory_hostname }} -> {{ item.1.bypass | default(item.1.proxy) }}"
- name: render xray-lists config
ansible.builtin.template:
src: xray-config.yaml.j2
dest: /var/lib/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-prerouting.nft
- 90-proxy-output.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-prerouting.nft.j2
dest: /etc/nftables.d/90-proxy-prerouting.nft
mode: "0644"
notify: reload nftables
- name: render proxy output
ansible.builtin.template:
src: 90-proxy-output.nft.j2
dest: /etc/nftables.d/90-proxy-output.nft
mode: "0644"
notify: reload nftables
@@ -100,3 +100,9 @@
[Install]
WantedBy=timers.target
- name: enable and start xray-lists
ansible.builtin.systemd:
name: xray-lists.timer
enabled: true
state: started
+4 -53
View File
@@ -1,55 +1,6 @@
---
- 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: include xray-lists install
ansible.builtin.include_tasks: install.yml
- 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
- name: include xray-lists configure
ansible.builtin.include_tasks: configure.yml
@@ -0,0 +1,11 @@
#jinja2: trim_blocks: True, lstrip_blocks: True
{% macro set_daddr(rule) %}
{%- if rule in (xray_ip_sets | default([])) or rule in (xray_static_sets | default([])) -%}
{{ rule }}_ip
{%- elif rule in (xray_domain_sets | default([])) -%}
{{ rule }}_dom
{%- endif -%}
{% endmacro %}
{% for rule in output_rules | default([]) | sort %}
ip daddr @{{ set_daddr(rule) }} meta mark set {{ xray_fwmark }} accept
{% endfor %}
@@ -0,0 +1,26 @@
#jinja2: trim_blocks: True, lstrip_blocks: True
{% macro set_daddr(name) %}
{%- if name == 'all' %}
0.0.0.0/0
{%- elif name in (xray_ip_sets | default([])) or name in (xray_static_sets | default([])) %}
@{{ name }}_ip
{%- elif name in (xray_domain_sets | default([])) %}
@{{ name }}_dom
{%- else %}
invalid_xray_set_{{ name }}
{% endif %}
{% endmacro %}
{% 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 %}
{% for rule in client.xray_policy %}
{% set target_set = rule.bypass | default(rule.proxy) %}
{% if rule.bypass is defined %}
meta l4proto { tcp, udp } ip saddr {{ src_ip }} ip daddr {{ set_daddr(target_set) }} accept
{% elif rule.proxy is defined %}
meta l4proto { tcp, udp } ip saddr {{ src_ip }} ip daddr {{ set_daddr(target_set) }} tproxy ip to :{{ xray_tproxy_port }} meta mark set {{ xray_fwmark }} accept
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
@@ -1,31 +0,0 @@
#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
{%- 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 -%}
@@ -45,3 +45,7 @@ domain_sets:
{% endfor %}
{% endif %}
{% endfor %}
output_rules:
{% for rule in output_rules %}
- {{ rule }}
{% endfor %}