-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Expand file tree
/
Copy pathPrevent_DNS_Leak.conf
More file actions
41 lines (39 loc) · 5.37 KB
/
Copy pathPrevent_DNS_Leak.conf
File metadata and controls
41 lines (39 loc) · 5.37 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
[General]
# Name: Prevent DNS Leak
# Author: Aethersailor
# Project: Custom_OpenClash_Rules
# Description: 通过 DNS 劫持、规则跟随、no-resolve 与代理兜底降低 DNS 泄露风险
# Compatibility: 需 OpenClash 支持 [General]、[Overwrite] 与 ruby_edit,Mihomo 支持 respect-rules
# Action: 强制规则模式并改写最终 DNS、规则和代理组配置
# Conflicts: 会强制启用路由器自身代理和 DNS 劫持,并将最终 MATCH 改为代理策略
# Verification: 检查最终运行配置中的 dns.respect-rules、proxy-server-nameserver、no-resolve 与 MATCH
#
# 功能:
# 1. 强制使用 rule 模式,并启用路由器自身代理
# 2. 启用 OpenClash DNS 劫持,拦截客户端和路由器自身的 TCP/UDP 53 端口查询
# 3. 启用 dns.respect-rules,使 DNS 上游连接遵循分流规则
# 4. 禁止 OpenClash 自动追加 WAN DNS 和自动补充 default-nameserver
# 5. 为目标 IP 类规则以及 behavior: ipcidr 的 RULE-SET 添加 no-resolve
# 6. 将最终 MATCH/FINAL 改为代理策略;默认创建 COCR-DNS-Leak-Guard 专用策略组
# 7. 专用策略组自动引入全部代理节点和代理集合,且为空时回退 REJECT
# 8. 清除 DNS 列表中的 system 解析器,并关闭 DNS HTTP/3 偏好
#
# 可选参数:
# - EN_KEY1:指定最终 MATCH 使用的现有代理组或代理节点;留空时使用 COCR-DNS-Leak-Guard
# - EN_KEY2:指定 proxy-server-nameserver,多个地址使用英文分号分隔
#
# 限制:
# - 不会替用户选择或更换具体 DNS 上游;建议使用可靠的加密 DNS
# - 未设置 EN_KEY2 且缺少 proxy-server-nameserver 时,会尝试复用 default-nameserver
# - 如 default-nameserver 使用普通 UDP/TCP DNS,代理节点域名的引导解析仍可能被观察
# - 不处理 AND、OR、NOT 等逻辑规则内部嵌套的 IP 规则
# - 不阻断客户端自带的 DoH、DoT 或 DoQ;建议同时启用 Block_Encrypted_DNS.conf
# - IPv6 已在局域网启用但未被 OpenClash 接管时,仍需单独处理 IPv6 DNS 泄露
PROXY_MODE = rule
ROUTER_SELF_PROXY = 1
ENABLE_REDIRECT_DNS = 1
ENABLE_RESPECT_RULES = 1
APPEND_WAN_DNS = 0
APPEND_DEFAULT_DNS = 0
[Overwrite]
ruby_edit "$CONFIG_FILE" "['rules']" "begin; dns = Value['dns'].is_a?(Hash) ? Value['dns'] : {}; Value['dns'] = dns; dns['enable'] = true; dns['respect-rules'] = true; dns['prefer-h3'] = false; ['nameserver', 'fallback', 'default-nameserver', 'proxy-server-nameserver', 'direct-nameserver'].each { |key| if dns[key].is_a?(Array); dns[key] = dns[key].reject { |server| server.to_s.strip.match?(/^system($|:\/\/)/i) }; end }; custom_proxy_dns = ENV['EN_KEY2'].to_s.strip; if !custom_proxy_dns.empty?; dns['proxy-server-nameserver'] = custom_proxy_dns.split(';').map(&:strip).reject(&:empty?).uniq; elsif !dns['proxy-server-nameserver'].is_a?(Array) || dns['proxy-server-nameserver'].empty?; bootstrap_dns = dns['default-nameserver'].to_a.map { |server| server.to_s.strip }.reject { |server| server.empty? || server.match?(/^system($|:\/\/)/i) }; if bootstrap_dns.any?; dns['proxy-server-nameserver'] = bootstrap_dns.uniq; else; YAML.LOG_WARN('Prevent DNS Leak: proxy-server-nameserver is empty; configure EN_KEY2 or an IP-based default-nameserver'); end; end; add_no_resolve = lambda { |rule_list| next rule_list unless rule_list.is_a?(Array); rule_list.map { |rule| next rule unless rule.is_a?(String); parts = rule.split(',').map(&:strip); next rule if parts.length < 3; options = parts.drop(3).map { |part| part.downcase }; next rule if options.include?('no-resolve') || options.include?('src'); rule_type = parts[0].to_s.upcase; needs_no_resolve = ['IP-CIDR', 'IP-CIDR6', 'GEOIP'].include?(rule_type); if rule_type == 'RULE-SET'; provider = (Value['rule-providers'] || {})[parts[1].to_s]; needs_no_resolve = provider.is_a?(Hash) && provider['behavior'].to_s.strip.downcase == 'ipcidr'; end; if needs_no_resolve; parts.insert(3, 'no-resolve'); parts.join(','); else; rule; end } }; if Value['sub-rules'].is_a?(Hash); Value['sub-rules'].each { |name, rule_list| Value['sub-rules'][name] = add_no_resolve.call(rule_list) }; end; groups = Value['proxy-groups'].is_a?(Array) ? Value['proxy-groups'] : []; Value['proxy-groups'] = groups; guard_name = 'COCR-DNS-Leak-Guard'; requested_target = ENV['EN_KEY1'].to_s.strip; builtins = ['GLOBAL']; valid_targets = builtins + groups.map { |group| group['name'] if group.is_a?(Hash) }.compact + Value['proxies'].to_a.map { |proxy| proxy['name'] if proxy.is_a?(Hash) }.compact; unsafe_targets = ['DIRECT', 'REJECT', 'REJECT-DROP', 'PASS', 'COMPATIBLE']; use_custom_target = !requested_target.empty? && valid_targets.include?(requested_target) && !unsafe_targets.include?(requested_target.upcase); if use_custom_target; target = requested_target; else; if !requested_target.empty?; YAML.LOG_WARN('Prevent DNS Leak: invalid or non-proxy EN_KEY1, fallback to COCR-DNS-Leak-Guard'); end; groups.reject! { |group| group.is_a?(Hash) && group['name'].to_s == guard_name }; groups << {'name' => guard_name, 'type' => 'select', 'include-all' => true, 'exclude-type' => 'Direct', 'empty-fallback' => 'REJECT'}; target = guard_name; end; rules = add_no_resolve.call(Value['rules']); rules = [] unless rules.is_a?(Array); terminal_found = false; rules.map! { |rule| if rule.is_a?(String) && ['MATCH', 'FINAL'].include?(rule.split(',', 2)[0].to_s.strip.upcase); terminal_found = true; 'MATCH,' + target; else; rule; end }; rules << 'MATCH,' + target unless terminal_found; rules.uniq end"