From 1e6ced50f6372b1a276ed503f21831ce2efc64f0 Mon Sep 17 00:00:00 2001 From: yichya QC Date: Sat, 17 Feb 2024 01:56:54 +0800 Subject: [PATCH] feat: dns hijacking preview; deprecate global http / socks inbound --- README.md | 9 ++- core/root/usr/share/xray/common/stream.mjs | 2 +- core/root/usr/share/xray/feature/dns.mjs | 57 +++++++++++++------ .../usr/share/xray/feature/extra_inbound.mjs | 14 ++++- core/root/usr/share/xray/feature/outbound.mjs | 11 ++-- core/root/usr/share/xray/gen_config.uc | 44 +++++++------- core/root/usr/share/xray/protocol/trojan.mjs | 2 +- core/root/usr/share/xray/protocol/vless.mjs | 2 +- .../luci-static/resources/view/xray/core.js | 9 +-- .../resources/view/xray/preview.js | 18 +++++- 10 files changed, 106 insertions(+), 62 deletions(-) diff --git a/README.md b/README.md index 56d499e4..0c6257a1 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,13 @@ Focus on making the most of Xray (HTTP/HTTPS/Socks/TProxy inbounds, multiple pro ## Warnings +* For security concerns, global SOCKS / HTTP inbound (listen on 0.0.0.0, port 1080 / 1081 by default) is deprecated and will be removed in next major version (4.0.0). + * These settings are moved out of main luci app. Select "Preview or Deprecated" in "Extra Settings" tab and reboot to let those settings show again in preview app. + * Use Extra Inbound to manually add ports (avoid using common ports like 1080, also set listen addresses carefully) and adjust related workloads to use that. * Since version 3.2.0 sniffing and global custom settings are deprecated. - * These features are moved out of main luci app. Select "Preview or Deprecated" in "Extra Settings" tab and reboot to let those settings show again in preview app. - * These likely to be removed in version 4.0.0. Use FakeDNS instead of sniffing and use "Custom Configuration Hook" for global custom settings. + * These features are moved out of main luci app (into preview app). + * Global custom settings will be removed in version 4.0.0. Use "Custom Configuration Hook" for global custom settings. + * Sniffing might get completely reimplemented later. Use FakeDNS instead of sniffing to avoid incompatibilities. * This project **DOES NOT SUPPORT** the following versions of OpenWrt because of the requirements of firewall4 and cilent-side rendering LuCI: * LEDE / OpenWrt prior to 22.03 * [Lean's OpenWrt Source](https://github.com/coolsnowwolf/lede) (which uses a variant of LuCI shipped with OpenWrt 18.06) @@ -41,6 +45,7 @@ Fork this repository and: * 2024-01-19 chore: bump version * 2024-01-24 feat: add alias to LAN Hosts Access Control * 2024-02-04 fix: avoid firewall restart failure & some minor adjustments +* 2024-02-17 feat: add DNS Hijacking (preview) ## Changelog since 3.2.0 diff --git a/core/root/usr/share/xray/common/stream.mjs b/core/root/usr/share/xray/common/stream.mjs index f65558d1..043dcaec 100644 --- a/core/root/usr/share/xray/common/stream.mjs +++ b/core/root/usr/share/xray/common/stream.mjs @@ -1,6 +1,6 @@ "use strict"; -import { tls_outbound_settings, reality_outbound_settings } from "./tls.mjs"; +import { reality_outbound_settings, tls_outbound_settings } from "./tls.mjs"; function stream_tcp_fake_http_request(server) { if (server["tcp_guise"] == "http") { diff --git a/core/root/usr/share/xray/feature/dns.mjs b/core/root/usr/share/xray/feature/dns.mjs index 53180dc4..75966a51 100644 --- a/core/root/usr/share/xray/feature/dns.mjs +++ b/core/root/usr/share/xray/feature/dns.mjs @@ -2,6 +2,7 @@ import { access } from "fs"; import { fake_dns_domains } from "./fake_dns.mjs"; +import { direct_outbound } from "./outbound.mjs"; const fallback_fast_dns = "223.5.5.5:53"; const fallback_secure_dns = "8.8.8.8:53"; @@ -67,29 +68,51 @@ export function dns_server_inbounds(proxy) { return result; }; -export function dns_server_tags(proxy) { - let result = []; +export function dns_rules(proxy, tcp_hijack_inbound_tags, udp_hijack_inbound_tags) { const dns_port = int(proxy["dns_port"] || 5300); const dns_count = int(proxy["dns_count"] || 3); + let dns_server_tags = []; for (let i = dns_port; i <= dns_port + dns_count; i++) { - push(result, sprintf("dns_server_inbound:%d", i)); + push(dns_server_tags, sprintf("dns_server_inbound:%d", i)); } - return result; -}; - -export function dns_server_outbound() { - return { - protocol: "dns", - settings: { - nonIPQuery: "skip" + return [ + { + type: "field", + port: "53", + inboundTag: tcp_hijack_inbound_tags, + outboundTag: "dns_tcp_hijack_outbound" }, - streamSettings: { - sockopt: { - mark: 254 - } + { + type: "field", + port: "53", + inboundTag: udp_hijack_inbound_tags, + outboundTag: "dns_udp_hijack_outbound" }, - tag: "dns_server_outbound" - }; + { + type: "field", + inboundTag: dns_server_tags, + outboundTag: "dns_server_outbound" + }, + ]; +}; + +export function dns_server_outbounds(proxy) { + return [ + direct_outbound("dns_tcp_hijack_outbound", proxy.dns_tcp_hijack || ""), + direct_outbound("dns_udp_hijack_outbound", proxy.dns_udp_hijack || ""), + { + protocol: "dns", + settings: { + nonIPQuery: "skip" + }, + streamSettings: { + sockopt: { + mark: 254 + } + }, + tag: "dns_server_outbound" + } + ]; }; export function dns_conf(proxy, config, manual_tproxy, fakedns) { diff --git a/core/root/usr/share/xray/feature/extra_inbound.mjs b/core/root/usr/share/xray/feature/extra_inbound.mjs index 8ee5f3b5..bd52abb0 100644 --- a/core/root/usr/share/xray/feature/extra_inbound.mjs +++ b/core/root/usr/share/xray/feature/extra_inbound.mjs @@ -52,10 +52,18 @@ export function extra_inbound_balancers(extra_inbound) { return result; }; -export function extra_inbound_global_tcp_tags(extra_inbound) { - return map(filter(extra_inbound, v => v["specify_outbound"] != "1" && v["inbound_type"] != "tproxy_udp"), v => `extra_inbound_${v[".name"]}`); +export function extra_inbound_global_tcp(extra_inbound) { + return map(filter(extra_inbound, v => v["specify_outbound"] != "1" && v["inbound_type"] == "tproxy_tcp"), v => `extra_inbound_${v[".name"]}`); }; -export function extra_inbound_global_udp_tags(extra_inbound) { +export function extra_inbound_global_udp(extra_inbound) { return map(filter(extra_inbound, v => v["specify_outbound"] != "1" && v["inbound_type"] == "tproxy_udp"), v => `extra_inbound_${v[".name"]}`); }; + +export function extra_inbound_global_http(extra_inbound) { + return map(filter(extra_inbound, v => v["specify_outbound"] != "1" && v["inbound_type"] == "http"), v => `extra_inbound_${v[".name"]}`); +}; + +export function extra_inbound_global_socks5(extra_inbound) { + return map(filter(extra_inbound, v => v["specify_outbound"] != "1" && v["inbound_type"] == "socks5"), v => `extra_inbound_${v[".name"]}`); +}; diff --git a/core/root/usr/share/xray/feature/outbound.mjs b/core/root/usr/share/xray/feature/outbound.mjs index 34ee50e6..32577bc2 100644 --- a/core/root/usr/share/xray/feature/outbound.mjs +++ b/core/root/usr/share/xray/feature/outbound.mjs @@ -1,11 +1,11 @@ "use strict"; +import { http_outbound } from "../protocol/http.mjs"; import { shadowsocks_outbound } from "../protocol/shadowsocks.mjs"; +import { socks_outbound } from "../protocol/socks.mjs"; import { trojan_outbound } from "../protocol/trojan.mjs"; import { vless_outbound } from "../protocol/vless.mjs"; import { vmess_outbound } from "../protocol/vmess.mjs"; -import { http_outbound } from "../protocol/http.mjs"; -import { socks_outbound } from "../protocol/socks.mjs"; function override_custom_config_recursive(x, y) { if (type(x) != "object" || type(y) != "object") { @@ -58,12 +58,13 @@ function server_outbound_recursive(t, server, tag, config) { return result; } -export function direct_outbound(tag) { +export function direct_outbound(tag, redirect) { return { protocol: "freedom", tag: tag, settings: { - domainStrategy: "UseIPv4" + domainStrategy: "UseIPv4", + redirect: redirect || "" }, streamSettings: { sockopt: { @@ -82,7 +83,7 @@ export function blackhole_outbound() { export function server_outbound(server, tag, config) { if (server == null) { - return [direct_outbound(tag)]; + return [direct_outbound(tag, null)]; } return server_outbound_recursive([], server, tag, config); }; diff --git a/core/root/usr/share/xray/gen_config.uc b/core/root/usr/share/xray/gen_config.uc index 9cbc0cc3..4f1d144d 100644 --- a/core/root/usr/share/xray/gen_config.uc +++ b/core/root/usr/share/xray/gen_config.uc @@ -4,8 +4,8 @@ import { access } from "fs"; import { load_config } from "./common/config.mjs"; import { bridge_outbounds, bridge_rules, bridges } from "./feature/bridge.mjs"; -import { blocked_domain_rules, dns_conf, dns_server_inbounds, dns_server_outbound, dns_server_tags, fast_domain_rules, secure_domain_rules } from "./feature/dns.mjs"; -import { extra_inbound_balancers, extra_inbound_global_tcp_tags, extra_inbound_global_udp_tags, extra_inbound_rules, extra_inbounds } from "./feature/extra_inbound.mjs"; +import { blocked_domain_rules, dns_conf, dns_rules, dns_server_inbounds, dns_server_outbounds, fast_domain_rules, secure_domain_rules } from "./feature/dns.mjs"; +import { extra_inbound_balancers, extra_inbound_global_http, extra_inbound_global_socks5, extra_inbound_global_tcp, extra_inbound_global_udp, extra_inbound_rules, extra_inbounds } from "./feature/extra_inbound.mjs"; import { fake_dns_balancers, fake_dns_conf, fake_dns_rules } from "./feature/fake_dns.mjs"; import { dokodemo_inbound, http_inbound, https_inbound, socks_inbound } from "./feature/inbound.mjs"; import { manual_tproxy_outbound_tags, manual_tproxy_outbounds, manual_tproxy_rules } from "./feature/manual_tproxy.mjs"; @@ -61,9 +61,9 @@ function inbounds(proxy, config, extra_inbound) { function outbounds(proxy, config, manual_tproxy, bridge, extra_inbound, fakedns) { let result = [ - direct_outbound("direct"), + direct_outbound("direct", null), blackhole_outbound(), - dns_server_outbound(), + ...dns_server_outbounds(proxy), ...manual_tproxy_outbounds(config, manual_tproxy), ...bridge_outbounds(config, bridge) ]; @@ -108,16 +108,19 @@ function rules(proxy, bridge, manual_tproxy, extra_inbound, fakedns) { const tproxy_udp_inbound_v4_tags = ["tproxy_udp_inbound_v4"]; const tproxy_tcp_inbound_v6_tags = ["tproxy_tcp_inbound_v6"]; const tproxy_udp_inbound_v6_tags = ["tproxy_udp_inbound_v6"]; - const built_in_tcp_inbounds = [...tproxy_tcp_inbound_v4_tags, "socks_inbound", "https_inbound", "http_inbound"]; - const built_in_udp_inbounds = [...tproxy_udp_inbound_v4_tags, "dns_conf_inbound"]; - const extra_inbound_global_tcp = extra_inbound_global_tcp_tags() || []; - const extra_inbound_global_udp = extra_inbound_global_udp_tags() || []; + const extra_inbound_global_tcp_tags = extra_inbound_global_tcp() || []; + const extra_inbound_global_udp_tags = extra_inbound_global_udp() || []; + const extra_inbound_global_http_tags = extra_inbound_global_http() || []; + const extra_inbound_global_socks5_tags = extra_inbound_global_socks5() || []; + const built_in_tcp_inbounds = [...tproxy_tcp_inbound_v4_tags, ...extra_inbound_global_tcp_tags, ...extra_inbound_global_http_tags, ...extra_inbound_global_socks5_tags, "socks_inbound", "https_inbound", "http_inbound"]; + const built_in_udp_inbounds = [...tproxy_udp_inbound_v4_tags, ...extra_inbound_global_udp_tags, "dns_conf_inbound"]; let result = [ ...fake_dns_rules(fakedns), ...manual_tproxy_rules(manual_tproxy), ...extra_inbound_rules(extra_inbound), ...system_route_rules(proxy), ...bridge_rules(bridge), + ...dns_rules(proxy, [...tproxy_tcp_inbound_v6_tags, ...tproxy_tcp_inbound_v4_tags, ...extra_inbound_global_tcp_tags], [...tproxy_udp_inbound_v6_tags, ...tproxy_udp_inbound_v4_tags, ...extra_inbound_global_udp_tags]), ...function () { let direct_rules = []; if (geoip_existence) { @@ -126,7 +129,7 @@ function rules(proxy, bridge, manual_tproxy, extra_inbound, fakedns) { if (length(geoip_direct_code_list) > 0) { push(direct_rules, { type: "field", - inboundTag: [...built_in_tcp_inbounds, ...built_in_udp_inbounds, ...extra_inbound_global_tcp, ...extra_inbound_global_udp], + inboundTag: [...built_in_tcp_inbounds, ...built_in_udp_inbounds], outboundTag: "direct", ip: geoip_direct_code_list }); @@ -143,7 +146,7 @@ function rules(proxy, bridge, manual_tproxy, extra_inbound, fakedns) { } push(direct_rules, { type: "field", - inboundTag: [...tproxy_tcp_inbound_v6_tags, ...tproxy_udp_inbound_v6_tags, ...built_in_tcp_inbounds, ...built_in_udp_inbounds, ...extra_inbound_global_tcp, ...extra_inbound_global_udp], + inboundTag: [...tproxy_tcp_inbound_v6_tags, ...tproxy_udp_inbound_v6_tags, ...built_in_tcp_inbounds, ...built_in_udp_inbounds], outboundTag: "direct", ip: ["geoip:private"] }); @@ -152,40 +155,35 @@ function rules(proxy, bridge, manual_tproxy, extra_inbound, fakedns) { }(), { type: "field", - inboundTag: [...tproxy_tcp_inbound_v6_tags], + inboundTag: tproxy_tcp_inbound_v6_tags, balancerTag: "tcp_outbound_v6" }, { type: "field", - inboundTag: [...tproxy_udp_inbound_v6_tags], + inboundTag: tproxy_udp_inbound_v6_tags, balancerTag: "udp_outbound_v6" }, { type: "field", - inboundTag: [...built_in_tcp_inbounds, ...extra_inbound_global_tcp], + inboundTag: built_in_tcp_inbounds, balancerTag: "tcp_outbound_v4" }, { type: "field", - inboundTag: [...built_in_udp_inbounds, ...extra_inbound_global_udp], + inboundTag: built_in_udp_inbounds, balancerTag: "udp_outbound_v4" }, - { - type: "field", - inboundTag: dns_server_tags(proxy), - outboundTag: "dns_server_outbound" - }, ]; if (proxy["tproxy_sniffing"] == "1") { if (length(secure_domain_rules(proxy)) > 0) { splice(result, 0, 0, { type: "field", - inboundTag: [...tproxy_tcp_inbound_v4_tags, ...extra_inbound_global_tcp], + inboundTag: [...tproxy_tcp_inbound_v4_tags, ...extra_inbound_global_tcp_tags], balancerTag: "tcp_outbound_v4", domain: secure_domain_rules(proxy), }, { type: "field", - inboundTag: [...tproxy_udp_inbound_v4_tags, ...extra_inbound_global_udp], + inboundTag: [...tproxy_udp_inbound_v4_tags, ...extra_inbound_global_udp_tags], balancerTag: "udp_outbound_v4", domain: secure_domain_rules(proxy), }, { @@ -203,14 +201,14 @@ function rules(proxy, bridge, manual_tproxy, extra_inbound, fakedns) { if (length(blocked_domain_rules(proxy)) > 0) { splice(result, 0, 0, { type: "field", - inboundTag: [...tproxy_tcp_inbound_v4_tags, ...tproxy_udp_inbound_v4_tags, ...tproxy_tcp_inbound_v6_tags, ...tproxy_udp_inbound_v6_tags, ...extra_inbound_global_tcp, ...extra_inbound_global_udp], + inboundTag: [...tproxy_tcp_inbound_v4_tags, ...tproxy_udp_inbound_v4_tags, ...tproxy_tcp_inbound_v6_tags, ...tproxy_udp_inbound_v6_tags, ...extra_inbound_global_tcp_tags, ...extra_inbound_global_udp_tags], outboundTag: "blackhole_outbound", domain: blocked_domain_rules(proxy), }); } splice(result, 0, 0, { type: "field", - inboundTag: [...tproxy_tcp_inbound_v4_tags, ...tproxy_udp_inbound_v4_tags, ...tproxy_tcp_inbound_v6_tags, ...tproxy_udp_inbound_v6_tags, ...extra_inbound_global_tcp, ...extra_inbound_global_udp], + inboundTag: [...tproxy_tcp_inbound_v4_tags, ...tproxy_udp_inbound_v4_tags, ...tproxy_tcp_inbound_v6_tags, ...tproxy_udp_inbound_v6_tags, ...extra_inbound_global_tcp_tags, ...extra_inbound_global_udp_tags], outboundTag: "direct", domain: fast_domain_rules(proxy) }); diff --git a/core/root/usr/share/xray/protocol/trojan.mjs b/core/root/usr/share/xray/protocol/trojan.mjs index b533e96e..9fa99572 100644 --- a/core/root/usr/share/xray/protocol/trojan.mjs +++ b/core/root/usr/share/xray/protocol/trojan.mjs @@ -1,7 +1,7 @@ "use strict"; import { stream_settings } from "../common/stream.mjs"; -import { tls_inbound_settings, fallbacks } from "../common/tls.mjs"; +import { fallbacks, tls_inbound_settings } from "../common/tls.mjs"; function trojan_inbound_user(k) { return { diff --git a/core/root/usr/share/xray/protocol/vless.mjs b/core/root/usr/share/xray/protocol/vless.mjs index 35c727e8..e551bb01 100644 --- a/core/root/usr/share/xray/protocol/vless.mjs +++ b/core/root/usr/share/xray/protocol/vless.mjs @@ -1,7 +1,7 @@ "use strict"; import { stream_settings } from "../common/stream.mjs"; -import { tls_inbound_settings, reality_inbound_settings, fallbacks } from "../common/tls.mjs"; +import { fallbacks, reality_inbound_settings, tls_inbound_settings } from "../common/tls.mjs"; function vless_inbound_user(k, flow) { return { diff --git a/core/root/www/luci-static/resources/view/xray/core.js b/core/root/www/luci-static/resources/view/xray/core.js index 251d38ee..f5b120d7 100644 --- a/core/root/www/luci-static/resources/view/xray/core.js +++ b/core/root/www/luci-static/resources/view/xray/core.js @@ -243,14 +243,6 @@ return view.extend({ s.tab('inbounds', _('Inbounds')); - o = s.taboption('inbounds', form.Value, 'socks_port', _('Socks5 proxy port')); - o.datatype = 'port'; - o.placeholder = 1080; - - o = s.taboption('inbounds', form.Value, 'http_port', _('HTTP proxy port')); - o.datatype = 'port'; - o.placeholder = 1081; - o = s.taboption('inbounds', form.Value, 'tproxy_port_tcp_v4', _('Transparent proxy port (TCP4)')); o.datatype = 'port'; o.placeholder = 1082; @@ -301,6 +293,7 @@ return view.extend({ destination.textvalue = destination_format(config_data, "destination", 60, true); let balancer_strategy = extra_inbounds.option(form.Value, 'balancer_strategy', _('Balancer Strategy'), _('Strategy leastPing requires observatory (see "Extra Options" tab) to be enabled.')); + balancer_strategy.depends("specify_outbound", "1"); balancer_strategy.value("random"); balancer_strategy.value("leastPing"); balancer_strategy.value("roundRobin"); diff --git a/core/root/www/luci-static/resources/view/xray/preview.js b/core/root/www/luci-static/resources/view/xray/preview.js index 4110d0e8..ed2710ac 100644 --- a/core/root/www/luci-static/resources/view/xray/preview.js +++ b/core/root/www/luci-static/resources/view/xray/preview.js @@ -11,6 +11,14 @@ return view.extend({ s.addremove = false; s.anonymous = true; + s.tab("dns_hijack", _("DNS Hijacking")); + + let dns_tcp_hijack = s.taboption('dns_hijack', form.Value, 'dns_tcp_hijack', _('Hijack TCP DNS Requests'), _("Redirect all outgoing TCP requests with destination port 53 to the address specified. In most cases not necessary.")); + dns_tcp_hijack.datatype = 'or(ip4addr, ip4addrport)'; + + let dns_udp_hijack = s.taboption('dns_hijack', form.Value, 'dns_udp_hijack', _('Hijack UDP DNS Requests'), _("Redirect all outgoing UDP requests with destination port 53 to the address specified. Recommended to use 127.0.0.1:53.")); + dns_udp_hijack.datatype = 'or(ip4addr, ip4addrport)'; + s.tab("firewall", _("Extra Firewall Options")); let mark = s.taboption('firewall', form.Value, 'mark', _('Socket Mark Number'), _('Avoid proxy loopback problems with local (gateway) traffic')); @@ -30,7 +38,15 @@ return view.extend({ let ttl_hop_limit_match = s.taboption('firewall', form.Value, 'ttl_hop_limit_match', _('TTL / Hop Limit Match'), _("Only override TTL / hop limit for packets with specific TTL / hop limit.")); ttl_hop_limit_match.datatype = 'uinteger'; - s.tab("sniffing", _("Sniffing")); + s.tab("sniffing", _("Legacy Inbounds and Sniffing")); + + let socks_port = s.taboption('sniffing', form.Value, 'socks_port', _('Socks5 proxy port'), _("Deprecated for security concerns. Use Extra Inbound instead.")); + socks_port.datatype = 'port'; + socks_port.placeholder = 1080; + + let http_port = s.taboption('sniffing', form.Value, 'http_port', _('HTTP proxy port'), _("Deprecated for security concerns. Use Extra Inbound instead.")); + http_port.datatype = 'port'; + http_port.placeholder = 1081; s.taboption('sniffing', form.Flag, 'tproxy_sniffing', _('Enable Sniffing'), _('Route requests according to domain settings in "DNS Settings" tab in core settings. Deprecated; use FakeDNS instead.'));