From 9c99d67db3191b03045181a81fce3be1169c927f Mon Sep 17 00:00:00 2001 From: yumoqing Date: Sun, 30 Nov 2025 15:07:11 +0800 Subject: [PATCH] bugfix --- scripts/gateway.sh.j2 | 71 +++++++++++++++++++++++++--------- scripts/gfw_rules_generator.py | 22 ++++++++--- scripts/render.py | 11 +----- 3 files changed, 70 insertions(+), 34 deletions(-) diff --git a/scripts/gateway.sh.j2 b/scripts/gateway.sh.j2 index cbf4114..4f04c5a 100644 --- a/scripts/gateway.sh.j2 +++ b/scripts/gateway.sh.j2 @@ -5,6 +5,7 @@ set -eo pipefail # 网关主机配置变量 - 请根据你的实际环境修改! # ============================================================================== # 网关主机内网IP (用于其他内网主机连接) +curuser=$(id -un) GATEWAY_LAN_IP="{{ gateway_lan_ip }}" GATEWAY_LAN_CIDR="{{ gateway_lan_cidr }}" # 内网网段 # DHCP 租约范围 @@ -19,6 +20,7 @@ LAN_INTERFACE="{{ lan_interface }}" # 连接内网的网卡,我们将配置静 # SSH SOCKS5 代理配置 REMOTE_SSH_USER="{{ remote_ssh_user }}" # 远程服务器的SSH用户名 REMOTE_SSH_IP="{{ remote_ssh_ip }}" # 远程服务器的IP地址 +REMOTE_SSH_NAME="{{ remote_ssh_name }}" # 远程服务器的IP地址 REMOTE_SSH_PORT="{{ remote_ssh_port }}" # 远程服务器的SSH端口 LOCAL_SOCKS5_PORT="{{ local_socks5_port }}" # SSH SOCKS5 代理在本机监听的端口 (127.0.0.1:1080) @@ -71,7 +73,7 @@ install_package() { # ============================================================================== # 0. 前置检查与环境初始化 # ============================================================================== -check_root +# check_root log_info "开始网关主机配置脚本..." log_info "更新系统软件包列表..." @@ -149,11 +151,12 @@ max-lease-time ${DHCP_LEASE_TIME}; authoritative; # A slightly different configuration for an internal subnet. -subnet {{ gateway_lan_cidr%.* }}.0 netmask 255.255.255.0 { +{% set net_base = gateway_lan_ip.split('.') | slice(3) | join('.') %} +subnet {{ net_base }}.0 netmask 255.255.255.0 { range ${DHCP_START_IP} ${DHCP_END_IP}; option routers ${GATEWAY_LAN_IP}; option domain-name-servers ${GATEWAY_LAN_IP}; # DNS 指向网关主机本身 - option broadcast-address {{ gateway_lan_cidr%.* }}.255; + option broadcast-address {{ net_base }}.255; default-lease-time ${DHCP_LEASE_TIME}; max-lease-time ${DHCP_LEASE_TIME}; } @@ -186,7 +189,7 @@ User=${curuser} Type=simple # 注意:这里需要确保用户 {{ remote_ssh_user }} 是一个实际存在的用户,并且可以访>问其 $HOME/.ssh # 为了简化,我们暂时用root运行,但更推荐使用非root用户 -ExecStart=/usr/bin/ssh -D ${LOCAL_SOCKS5_PORT} -N -p ${REMOTE_SSH_PORT} ${REMOTE_SSH_USER}@${REMOTE_SSH_IP} -o ExitOnForwardFailure=yes -o ServerAliveInterval=60 -o ServerAliveCountMax=3 +ExecStart=/usr/bin/ssh -D ${LOCAL_SOCKS5_PORT} -N -p ${REMOTE_SSH_PORT} ${REMOTE_SSH_USER}@${REMOTE_SSH_NAME} -o ExitOnForwardFailure=yes -o ServerAliveInterval=60 -o ServerAliveCountMax=3 # 使用 Restart 策略确保连接断开时自动重连 Restart=always RestartSec=5 @@ -308,12 +311,19 @@ log_info "设置 GFW 规则定时更新任务 (每天凌晨 3:00)..." (sudo crontab -l 2>/dev/null; echo "0 3 * * * cd ${GFWLIST2NEW_DIR} && sudo python3 ${LOCAL_SCRIPT_NAME} -p ${SOCKS5_SERVER_ADDR} -f "{{ foreign_dns1 }} {{ foreign_dns2 }}" -d "{{ domestic_dns1 }} {{domestic_dns1}}" && sudo systemctl restart dnsmasq") | sudo crontab - log_info "规则生成器配置完成并设置定时更新。" cd - > /dev/null + # ============================================================================== -# 7. 配置 IPTABLES 规则实现透明代理和 NAT (保持不变) +# 7. 配置 IPTABLES 规则实现透明代理和 NAT (修正完整版) # ============================================================================== log_info "配置 IPTABLES 规则 (NAT 和透明代理)..." +# 变量定义检查(确保这些变量在脚本前面已正确定义) +# REMOTE_SSH_IP: 远程SOCKS5服务器的公网IP +# WAN_INTERFACE: 外网网卡名,如 eth0 +# LAN_INTERFACE: 内网网卡名,如 enx... + # 清除现有规则,避免冲突 +log_info "清除现有 IPTABLES 规则..." sudo iptables -F sudo iptables -X sudo iptables -t nat -F @@ -330,15 +340,44 @@ sudo iptables -A FORWARD -i {{ lan_interface }} -o {{ wan_interface }} -j ACCEPT sudo iptables -A FORWARD -o {{ lan_interface }} -i {{ wan_interface }} -m state --state RELATED,ESTABLISHED -j ACCEPT log_info "已配置 IP 转发规则。" -# 3. 透明代理规则 (使用 redsocks 和 ipset) -# 重定向到 redsocks 的流量,不处理来自 redsocks 本身的流量 -sudo iptables -t nat -A PREROUTING -i {{ lan_interface }} -p tcp -s {{ gateway_lan_cidr }} -m set --match-set gfwlist dst -j REDIRECT --to-ports ${REDSOCKS_PORT} -log_info "已配置透明代理重定向规则 (所有端口,目标IP在 gfwlist 中的内网TCP流量到 redsocks)。" +# 3. 透明代理规则 (核心的 PREROUTING 链) +log_info "配置透明代理 PREROUTING 排除和重定向规则..." -# 排除 redsocks 自身流量循环 -# 这一步非常重要,避免 redsocks 自己产生流量又被 iptables 捕获 -sudo iptables -t nat -A OUTPUT -p tcp -d 127.0.0.1 --dport ${REDSOCKS_PORT} -j RETURN -sudo iptables -t nat -A OUTPUT -p tcp -d 127.0.0.1 --dport ${LOCAL_SOCKS5_PORT} -j RETURN +# --- 排除规则(必须在重定向规则之前) --- + +# 3.1 排除所有重定向到本地的流量(已经通过 OUTPUT 排除,这里可以再加一层保险) +sudo iptables -t nat -A PREROUTING -d 127.0.0.0/8 -j RETURN || log_error "排除本地回环失败。" + +# 3.2 排除局域网内流量 (不代理内网互访) +sudo iptables -t nat -A PREROUTING -i {{ lan_interface }} -d {{ gateway_lan_cidr }} -j RETURN || log_error "排除内网流量失败。" + +# 3.3 【❗ 补充:排除远程 SSH 服务器 IP ❗】 +# 避免 SSH 连接本身被重定向,防止无限循环。 +# 假设变量 REMOTE_SSH_IP 已经正确替换。 +sudo iptables -t nat -A PREROUTING -d {{ remote_ssh_ip }} -j RETURN || log_error "排除远程 SSH IP 失败。" +log_info "已添加规则:排除远程 SSH 服务器 {{ remote_ssh_ip }}。" + + +# 3.4 【❗ 补充:排除国内 IP ❗】 +# 排除目标 IP 在 china_ip 集合中的流量(国内流量直连) +# 假设 china_ip 集合通过其他脚本/步骤创建 +# 注意:您的 gfwlist2new/gfw_rules_generator 脚本只处理 gfwlist,这里不需要 china_ip 的 ipset 排除 +# 而是依赖 gfwlist ipset 的精准捕获。我们移除这个容易出错的步骤,只依赖 gfwlist。 +# 如果需要排除国内 IP 段,需要有外部脚本维护 china_ip 集合。 + +# --- 重定向规则 --- + +# 3.5 核心重定向规则: +# 只有流量目标 IP 在 gfwlist ipset 集合中,才重定向到 redsocks。 +# 注意:该规则必须在排除规则之后。 +sudo iptables -t nat -A PREROUTING -i {{ lan_interface }} -p tcp -s {{ gateway_lan_cidr }} -m set --match-set gfwlist dst -j REDIRECT --to-ports ${REDSOCKS_PORT} +log_info "已配置透明代理重定向规则 (目标IP在 gfwlist 中的内网TCP流量到 redsocks)。" + + +# 4. 排除 redsocks 自身流量循环 (OUTPUT 链) +# 这一步非常重要,避免 redsocks/ssh 客户端自己产生流量又被 iptables 捕获 +# 排除目标地址是 127.0.0.1 的流量,它们是 SSH 和 Redsocks 的内部通信。 +sudo iptables -t nat -A OUTPUT -d 127.0.0.1 -p tcp -j RETURN log_info "已配置排除 redsocks 自身流量循环的规则。" # 保存 iptables 规则 @@ -346,9 +385,3 @@ sudo netfilter-persistent save || log_error "保存 iptables 规则失败。" sudo systemctl enable netfilter-persistent || log_error "启用 netfilter-persistent 服务失败。" log_info "IPTABLES 规则已配置并保存。" -log_info "----------------------------------------------------------------------------------" -log_info "网关主机配置完成!" -log_info "请检查 redsocks 和 ssh -D 进程是否正常运行。" -log_info "请注意:SSH SOCKS5 代理可能需要手动输入密码或配置免密登录才能持久运行。" -log_info "内网主机现在应该可以自动通过 DHCP 获取 IP 并访问网络。" -log_info "----------------------------------------------------------------------------------" diff --git a/scripts/gfw_rules_generator.py b/scripts/gfw_rules_generator.py index 187df0a..8dc4abc 100644 --- a/scripts/gfw_rules_generator.py +++ b/scripts/gfw_rules_generator.py @@ -10,7 +10,6 @@ GFWLIST_URL = "https://raw.githubusercontent.com/gfwlist/gfwlist/master/gfwlist. def log_info(message): print(f"[INFO] {message}") -# 注意:foreign_dns_list 和 domestic_dns_list 现在是列表 def generate_rules(proxy_server, foreign_dns_list, domestic_dns_list, ipset_name="gfwlist"): """ 生成 Dnsmasq 和 IPSet 规则文件。 @@ -19,10 +18,18 @@ def generate_rules(proxy_server, foreign_dns_list, domestic_dns_list, ipset_name log_info("错误:必须提供至少一个国内 DNS 和一个国外 DNS。") sys.exit(1) + # --- 代理配置:通过本地 SOCKS5 隧道下载 GFWList --- + proxies = { + 'http': f'socks5h://{proxy_server}', # socks5h 使用远程 DNS 解析 + 'https': f'socks5h://{proxy_server}' + } + log_info(f"配置代理:使用 {proxy_server} 通过隧道下载 GFWList...") + # ---------------------------------------------------- + try: log_info(f"正在从 {GFWLIST_URL} 下载 GFWList...") - # 尝试通过代理下载 GFWList (这里假设环境已配置或可以直接访问 GitHub) - response = requests.get(GFWLIST_URL, timeout=30) + # 传入 proxies 参数,强制 requests 走代理 + response = requests.get(GFWLIST_URL, timeout=30, proxies=proxies) response.raise_for_status() # 1. 解码 GFWList @@ -33,7 +40,7 @@ def generate_rules(proxy_server, foreign_dns_list, domestic_dns_list, ipset_name content = response.text except requests.exceptions.RequestException as e: - log_info(f"下载 GFWList 失败,请检查网络连接: {e}") + log_info(f"致命错误:通过代理下载 GFWList 失败,请检查 SSH SOCKS5 隧道状态!: {e}") # 如果下载失败,中止脚本以避免生成空文件 sys.exit(1) @@ -67,13 +74,13 @@ def generate_rules(proxy_server, foreign_dns_list, domestic_dns_list, ipset_name try: with open(dnsmasq_conf_path, 'w') as f: - f.write(f"# Dnsmasq rules generated by gfw_rules_generator.py on {os.path.basename(__file__)}\n") + f.write(f"# Dnsmasq rules generated by {os.path.basename(__file__)} on {os.uname().nodename}\n") # 写入所有国内 DNS 服务器 for dns in domestic_dns_list: f.write(f"server={dns}\n") - # GFWList 域名设置 (通过代理的上游 DNS 解析,并加入 IPSet) + # GFWList 域名设置 for domain in sorted(list(domains)): # 1. 将域名加入 IPSet 集合 (由 dnsmasq 动态处理) f.write(f"ipset=/{domain}/{ipset_name}\n") @@ -107,6 +114,9 @@ if __name__ == "__main__": args = parser.parse_args() + # 在开始生成规则之前,必须确保 requests 库已经安装 socks5 支持 + # 确保在 Bash 脚本中已经执行: sudo pip3 install requests PySocks + 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 842c8ff..f6456b6 100644 --- a/scripts/render.py +++ b/scripts/render.py @@ -15,10 +15,11 @@ config_vars = { "dhcp_lease_time": "7200", "remote_ssh_user": "kww", "remote_ssh_ip": "8.222.165.87", # !!! 替换为你的远程SSH服务器IP + "remote_ssh_name": "atvoe.com", # !!! 替换为你的远程SSH服务器域名 "remote_ssh_port": "22", "local_socks5_port": "1080", "redsocks_port": "55555", - "domestic_dns1": "223.5.5.5" + "domestic_dns1": "223.5.5.5", "domestic_dns2": "114.114.114.114", "foreign_dns1": "8.8.8.8", "foreign_dns2": "1.1.1.1", @@ -39,13 +40,5 @@ with open('gateway.sh', 'w') as f: f.write(rendered_gateway_script) print("Generated gateway_config.sh") -# 4. 渲染 client_config.sh -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) -print("Generated client_config.sh") - print("\n!!! 请务必检查生成的 .sh 文件,特别是 SSH 代理和网卡名称等配置。") print("在执行前,给它们添加可执行权限:chmod +x gateway_config.sh client_config.sh")