From c23c5f197956672e287bf43ef381c61508d790fd Mon Sep 17 00:00:00 2001 From: yumoqing Date: Sun, 30 Nov 2025 11:58:24 +0800 Subject: [PATCH] bugfix --- scripts/{gateway.sh => gateway.sh.j2} | 9 ++- scripts/gfw_rules_generator.py | 102 ++++++++++++++++++++++++++ scripts/render.py | 49 +++++++------ 3 files changed, 134 insertions(+), 26 deletions(-) rename scripts/{gateway.sh => gateway.sh.j2} (97%) create mode 100644 scripts/gfw_rules_generator.py diff --git a/scripts/gateway.sh b/scripts/gateway.sh.j2 similarity index 97% rename from scripts/gateway.sh rename to scripts/gateway.sh.j2 index d28c88c..d0e4cee 100644 --- a/scripts/gateway.sh +++ b/scripts/gateway.sh.j2 @@ -27,8 +27,10 @@ REDSOCKS_PORT="{{ redsocks_port }}" # redsocks2 在本机监听的端口 # DNSMASQ 配置 DNSMASQ_LISTEN_IP="${GATEWAY_LAN_IP}" # dnsmasq 监听的IP -DOMESTIC_DNS="{{ domestic_dns }}" # 国内DNS服务器 -FOREIGN_DNS="{{ foreign_dns }}" # 国外DNS服务器 (通过SSH SOCKS5代理访问) +DOMESTIC_DNS1="{{ domestic_dns1 }}" # 国内DNS服务器 +DOMESTIC_DNS2="{{ domestic_dns2 }}" # 国内DNS服务器 +FOREIGN_DNS1="{{ foreign_dns1 }}" # 国外DNS服务器 (通过SSH SOCKS5代理访问) +FOREIGN_DNS2="{{ foreign_dns2 }}" # 国外DNS服务器 (通过SSH SOCKS5代理访问) # GFWLIST2NEW 工具仓库和安装路径 GFWLIST2NEW_REPO="{{ gfwlist2new_repo }}" @@ -240,7 +242,8 @@ no-resolv no-poll # 国内域名直连公共DNS -server={{ domestic_dns }} +server={{ domestic_dns1 }} +server={{ domestic_dns2 }} # 国外域名通过 gfwlist2new 生成的配置文件转发到代理 # dnsmasq will read additional configuration files from /etc/dnsmasq.d diff --git a/scripts/gfw_rules_generator.py b/scripts/gfw_rules_generator.py new file mode 100644 index 0000000..4a5cab3 --- /dev/null +++ b/scripts/gfw_rules_generator.py @@ -0,0 +1,102 @@ +import argparse +import base64 +import requests +import os +import sys + +# GFWList 官方(或常见)的地址 +GFWLIST_URL = "https://raw.githubusercontent.com/gfwlist/gfwlist/master/gfwlist.txt" + +def log_info(message): + print(f"[INFO] {message}") + +def generate_rules(proxy_server, foreign_dns, domestic_dns, ipset_name="gfwlist"): + """ + 生成 Dnsmasq 和 IPSet 规则文件。 + """ + try: + log_info(f"正在从 {GFWLIST_URL} 下载 GFWList...") + response = requests.get(GFWLIST_URL, timeout=30) + response.raise_for_status() + + # 1. 解码 GFWList + try: + content = base64.b64decode(response.text).decode('utf-8') + except: + # 如果内容不是 base64 编码,则直接使用 + content = response.text + + except requests.exceptions.RequestException as e: + log_info(f"下载 GFWList 失败,使用本地缓存:{e}") + # 允许继续运行,依赖旧的规则文件 + content = "" + + domains = set() + for line in content.splitlines(): + line = line.strip() + if not line or line.startswith(('#', '!')): + continue + + # 提取域名(忽略 IP、通配符规则和特殊标记) + if line.startswith('||'): + domain = line[2:] + elif line.startswith('.'): + domain = line[1:] + elif '/' in line: + continue + else: + domain = line + + # 简单过滤,确保是域名 + if domain and '.' in domain and not domain.startswith('*.'): + domains.add(domain) + + # 2. 生成 DNSMasq 配置文件 + dnsmasq_conf_path = "/etc/dnsmasq.d/gfwlist_router.conf" + + # 确保 foreign_dns 包含端口 (例如 127.0.0.1#1086) + proxy_dns_server = f"{foreign_dns}#{proxy_server.split(':')[1]}" + + try: + with open(dnsmasq_conf_path, 'w') as f: + f.write(f"# Dnsmasq rules generated by gfw_rules_generator.py\n") + + # 设置国内 DNS + f.write(f"server={domestic_dns}\n") + + # GFWList 域名设置 + for domain in sorted(list(domains)): + # 1. 将域名加入 IPSet 集合 + f.write(f"ipset=/{domain}/{ipset_name}\n") + # 2. 通过代理的 DNS 服务器解析 + f.write(f"server=/{domain}/{proxy_dns_server}\n") + + log_info(f"已生成 Dnsmasq 规则到 {dnsmasq_conf_path},包含 {len(domains)} 个域名。") + except IOError as e: + log_info(f"写入 Dnsmasq 文件失败: {e}") + sys.exit(1) + + # 3. 确保 IPSet 存在 + # 脚本不负责添加 IP 地址(由 Dnsmasq 在运行时动态添加) + # 但需要确保 IPSet 集合本身存在。 + try: + os.system(f"sudo ipset create {ipset_name} hash:ip family inet hashsize 1024 maxelem 65536 &>/dev/null || sudo ipset flush {ipset_name}") + log_info(f"已确保 IPSet 集合 '{ipset_name}' 存在。") + except Exception as e: + log_info(f"创建 IPSet 失败: {e}") + sys.exit(1) + + return True + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="GFWList Rules Generator for Dnsmasq/IPSet") + parser.add_argument("-p", "--proxy", required=True, help="SOCKS5 Proxy server (e.g., 127.0.0.1:1086)") + parser.add_argument("-f", "--foreign-dns", required=True, help="Foreign DNS IP (e.g., 8.8.8.8)") + parser.add_argument("-d", "--domestic-dns", required=True, help="Domestic DNS IP (e.g., 223.5.5.5)") + parser.add_argument("-n", "--name", default="gfwlist", help="IPSet Name") + + args = parser.parse_args() + + log_info("开始生成 GFWList 规则...") + generate_rules(args.proxy, args.foreign_dns, args.domestic_dns, args.name) + log_info("规则生成完成。") diff --git a/scripts/render.py b/scripts/render.py index c8cbe72..842c8ff 100644 --- a/scripts/render.py +++ b/scripts/render.py @@ -2,26 +2,29 @@ from jinja2 import Environment, FileSystemLoader # 1. 定义你的客制化参数字典 config_vars = { - # 全局变量 - "gateway_lan_ip": "192.168.10.1", - "gateway_lan_cidr": "192.168.10.0/24", - "wan_interface": "eth0", # 网关主机外网网卡 - "lan_interface": "eth1", # 网关主机内网网卡 - "client_lan_interface": "eth0", # 内网主机网卡 + # 全局变量 + "gateway_lan_ip": "192.168.2.1", + "gateway_lan_cidr": "192.168.2.0/24", + "wan_interface": "eno1", # 网关主机外网网卡 + "lan_interface": "enx00e04c6800ae", # 网关主机内网网卡 + "client_lan_interface": "eth0", # 内网主机网卡 + + # 网关主机独有变量 + "dhcp_start_ip": "192.168.2.100", + "dhcp_end_ip": "192.168.2.200", + "dhcp_lease_time": "7200", + "remote_ssh_user": "kww", + "remote_ssh_ip": "8.222.165.87", # !!! 替换为你的远程SSH服务器IP + "remote_ssh_port": "22", + "local_socks5_port": "1080", + "redsocks_port": "55555", + "domestic_dns1": "223.5.5.5" + "domestic_dns2": "114.114.114.114", + "foreign_dns1": "8.8.8.8", + "foreign_dns2": "1.1.1.1", + "gfwlist2new_repo": "https://github.com/cokebar/gfwlist2dnsmasq_python.git", + "gfwlist2new_dir": "/opt/gfwlist2new", - # 网关主机独有变量 - "dhcp_start_ip": "192.168.10.100", - "dhcp_end_ip": "192.168.10.200", - "dhcp_lease_time": "7200", - "remote_ssh_user": "your_remote_user", - "remote_ssh_ip": "your_remote_server_ip", # !!! 替换为你的远程SSH服务器IP - "remote_ssh_port": "22", - "local_socks5_port": "1080", - "redsocks_port": "12345", - "domestic_dns": "223.5.5.5,114.114.114.114", - "foreign_dns": "8.8.8.8,1.1.1.1", - "gfwlist2new_repo": "https://github.com/louisabrahams/gfwlist2new.git", - "gfwlist2new_dir": "/opt/gfwlist2new", } # 2. 设置 Jinja2 环境 @@ -29,11 +32,11 @@ config_vars = { env = Environment(loader=FileSystemLoader('.')) # 3. 渲染 gateway_config.sh -gateway_template = env.get_template('gateway_config.sh.j2') +gateway_template = env.get_template('gateway.sh.j2') rendered_gateway_script = gateway_template.render(config_vars) -with open('gateway_config.sh', 'w') as f: - f.write(rendered_gateway_script) +with open('gateway.sh', 'w') as f: + f.write(rendered_gateway_script) print("Generated gateway_config.sh") # 4. 渲染 client_config.sh @@ -41,7 +44,7 @@ client_template = env.get_template('client_config.sh.j2') rendered_client_script = client_template.render(config_vars) # 客户端脚本也需要一些全局变量 with open('client_config.sh', 'w') as f: - f.write(rendered_client_script) + f.write(rendered_client_script) print("Generated client_config.sh") print("\n!!! 请务必检查生成的 .sh 文件,特别是 SSH 代理和网卡名称等配置。")