mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-11-10 09:07:51 +08:00
🎈 Sync 2026-10-04 14:38:18
This commit is contained in:
1 parent
0e6611f767
commit
aba12667c5
88 files changed
+4600
-1458
No files matched your search
@@ -59,6 +59,49 @@ function resolve_ipv6(str) {
|
||||
return ipv6.addr;
|
||||
}
|
||||
|
||||
/* The node server addresses the generator exported (generate_client.uc's
|
||||
* export_node_addresses()), as an nftables anonymous set - or null when there
|
||||
* is nothing to protect.
|
||||
*
|
||||
* Only the literal-address form is rendered here. A node configured with a
|
||||
* host name reaches the same set at runtime: the dnsmasq snippet runtime/dns.sh
|
||||
* writes resolves the name and back-fills the set through `nftset=`, so one
|
||||
* mechanism covers both without this template needing a resolver of its own.
|
||||
* A literal address has no name to resolve, so it can only come from here.
|
||||
*
|
||||
* readfile() rather than a UCI read on purpose: the generator is the only
|
||||
* place that knows which nodes are actually in use (main_node, main_udp_node,
|
||||
* urltest membership, custom-mode routing_nodes, wireguard endpoints), and a
|
||||
* copy of that selection in this template is a copy that can drift. A missing
|
||||
* file renders null, which is the behaviour before this existed. */
|
||||
function node_addr_to_nftarr(run_dir, family) {
|
||||
let raw = readfile(run_dir + '/node-addr-ips.txt');
|
||||
if (isEmpty(raw))
|
||||
return null;
|
||||
|
||||
let out = [];
|
||||
for (let entry in split(trim(raw), /[\r\n]/)) {
|
||||
let parsed = fw4.parse_subnet(entry)?.[0];
|
||||
if (!parsed || parsed.family !== family)
|
||||
continue;
|
||||
push(out, (parsed.bits > -1) ? `${parsed.addr}/${parsed.bits}` : parsed.addr);
|
||||
}
|
||||
|
||||
return isEmpty(out) ? null : array_to_nftarr(out);
|
||||
}
|
||||
|
||||
/* Whether the node bypass has anything to protect at all.
|
||||
|
||||
Either export file counts. The domains file is the one that matters most:
|
||||
a host-name node - the common case, and the one the device this was written
|
||||
for uses - appears only there, and its set has to exist before dnsmasq can
|
||||
put anything in it. Gating on the literal-address file instead is what left
|
||||
the whole mechanism doing nothing. */
|
||||
function node_addr_protected(run_dir) {
|
||||
return !isEmpty(readfile(run_dir + '/node-addr-ips.txt'))
|
||||
|| !isEmpty(readfile(run_dir + '/node-addr-domains.txt'));
|
||||
}
|
||||
|
||||
/* Normalize a list of IPv6 entries into an nftables anonymous set
|
||||
(or null when empty), so IPv6 ACLs match in a single set lookup
|
||||
instead of one rule per address. */
|
||||
@@ -140,6 +183,26 @@ function dnsmasq_nftset() {
|
||||
|
||||
/* Misc config */
|
||||
const resources_dir = '/etc/homeproxy-pro/resources';
|
||||
const run_dir = '/var/run/homeproxy-pro';
|
||||
|
||||
/* Resolved here, in the ucode block, rather than with `{% set %}` in the
|
||||
* template body below: utpl's `set` tag takes a plain expression and rejects a
|
||||
* call outright - `{% set x = f(); %}` is a parse error ("Unexpected token,
|
||||
* Expecting ';'"). A parse error kills the whole render, fw4_post.nft comes
|
||||
* out empty, and the health gate silently keeps the previous ruleset, so a
|
||||
* template that fails this way does not look broken at all. r41 shipped
|
||||
* `{% set node_addr_v4 = node_addr_to_nftarr(4); %}` and never rendered once.
|
||||
*
|
||||
* Both helpers take run_dir as a parameter rather than closing over it. In
|
||||
* this ucode a closure captures the block variables that already exist where
|
||||
* the function is DEFINED, not where it is called: a function defined above
|
||||
* `const run_dir` cannot see run_dir even when it is called far below, and
|
||||
* the error is "Reference error: access to undeclared variable run_dir" -
|
||||
* which reads as though run_dir were missing, when it is in fact declared 80
|
||||
* lines above the call. Passing it in is the only spelling that works. */
|
||||
const node_protect = node_addr_protected(run_dir);
|
||||
const node_addr_v4 = node_addr_to_nftarr(run_dir, 4);
|
||||
const node_addr_v6 = node_addr_to_nftarr(run_dir, 6);
|
||||
|
||||
/* UCI config start */
|
||||
const cfgname = 'homeproxy-pro';
|
||||
@@ -399,6 +462,42 @@ set homeproxy_routing_port {
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
{# Node server addresses.
|
||||
Declared when the generator exported any node address at all - and NOT
|
||||
only when there is a literal address to put in it.
|
||||
|
||||
That distinction is the whole point of the set existing. A node configured
|
||||
with a host name contributes no element here; dnsmasq resolves the name and
|
||||
back-fills this set through the nftset= snippet runtime/dns.sh writes. A
|
||||
set that is declared only when it already has elements is therefore never
|
||||
declared for a host-name node, the back-fill has nothing to land in, and the
|
||||
return rules below - which are what actually exclude the address - are not
|
||||
emitted either. r41 gated both on the literal form and was inert for the
|
||||
configuration it was written for.
|
||||
|
||||
An empty set is harmless: nft accepts one with no elements, and the cost is
|
||||
one lookup per packet until the first answer back-fills it. #}
|
||||
{% if (node_protect): %}
|
||||
set homeproxy_node_addr_v4 {
|
||||
type ipv4_addr
|
||||
flags interval
|
||||
auto-merge
|
||||
{% if (node_addr_v4): %}
|
||||
elements = { {{ node_addr_v4 }} }
|
||||
{% endif %}
|
||||
}
|
||||
{% endif %}
|
||||
{% if (node_protect && v6_handled): %}
|
||||
set homeproxy_node_addr_v6 {
|
||||
type ipv6_addr
|
||||
flags interval
|
||||
auto-merge
|
||||
{% if (node_addr_v6): %}
|
||||
elements = { {{ node_addr_v6 }} }
|
||||
{% endif %}
|
||||
}
|
||||
{% endif /* node_addr_v6 */ %}
|
||||
|
||||
{# DNS hijack & TCP redirect #}
|
||||
chain dstnat {
|
||||
{% if ((dns_redirect === '1') && (dns_hijacked !== '1')): %}
|
||||
@@ -461,6 +560,13 @@ chain homeproxy_redirect_lanac {
|
||||
chain homeproxy_redirect {
|
||||
meta mark {{ self_mark }} counter return
|
||||
|
||||
{% if (node_protect): %}
|
||||
ip daddr @homeproxy_node_addr_v4 counter return comment "!{{ cfgname }}: node address"
|
||||
{% endif %}
|
||||
{% if (node_protect && v6_handled): %}
|
||||
ip6 daddr @homeproxy_node_addr_v6 counter return comment "!{{ cfgname }}: node address"
|
||||
{% endif %}
|
||||
|
||||
ip daddr @homeproxy_wan_proxy_addr_v4 counter goto homeproxy_redirect_proxy_port
|
||||
{% if (v6_handled): %}
|
||||
ip6 daddr @homeproxy_wan_proxy_addr_v6 counter goto homeproxy_redirect_proxy_port
|
||||
@@ -582,6 +688,13 @@ chain homeproxy_mangle_lanac {
|
||||
}
|
||||
|
||||
chain homeproxy_mangle_prerouting {
|
||||
{% if (node_protect): %}
|
||||
ip daddr @homeproxy_node_addr_v4 counter return comment "!{{ cfgname }}: node address"
|
||||
{% endif %}
|
||||
{% if (node_protect && v6_handled): %}
|
||||
ip6 daddr @homeproxy_node_addr_v6 counter return comment "!{{ cfgname }}: node address"
|
||||
{% endif %}
|
||||
|
||||
ip daddr @homeproxy_wan_proxy_addr_v4 counter goto homeproxy_mangle_tproxy_port
|
||||
{% if (v6_handled): %}
|
||||
ip6 daddr @homeproxy_wan_proxy_addr_v6 counter goto homeproxy_mangle_tproxy_port
|
||||
@@ -646,6 +759,13 @@ chain homeproxy_mangle_prerouting {
|
||||
chain homeproxy_mangle_output {
|
||||
meta mark {{ self_mark }} counter return
|
||||
|
||||
{% if (node_protect): %}
|
||||
ip daddr @homeproxy_node_addr_v4 counter return comment "!{{ cfgname }}: node address"
|
||||
{% endif %}
|
||||
{% if (node_protect && v6_handled): %}
|
||||
ip6 daddr @homeproxy_node_addr_v6 counter return comment "!{{ cfgname }}: node address"
|
||||
{% endif %}
|
||||
|
||||
ip daddr @homeproxy_wan_proxy_addr_v4 counter goto homeproxy_mangle_mark
|
||||
{% if (v6_handled): %}
|
||||
ip6 daddr @homeproxy_wan_proxy_addr_v6 counter goto homeproxy_mangle_mark
|
||||
|
||||
Reference in new issue
Block a user