This commit is contained in:
yumoqing 2025-11-30 15:07:11 +08:00
parent 41a086d7ec
commit 9c99d67db3
3 changed files with 70 additions and 34 deletions

View File

@ -5,6 +5,7 @@ set -eo pipefail
# 网关主机配置变量 - 请根据你的实际环境修改! # 网关主机配置变量 - 请根据你的实际环境修改!
# ============================================================================== # ==============================================================================
# 网关主机内网IP (用于其他内网主机连接) # 网关主机内网IP (用于其他内网主机连接)
curuser=$(id -un)
GATEWAY_LAN_IP="{{ gateway_lan_ip }}" GATEWAY_LAN_IP="{{ gateway_lan_ip }}"
GATEWAY_LAN_CIDR="{{ gateway_lan_cidr }}" # 内网网段 GATEWAY_LAN_CIDR="{{ gateway_lan_cidr }}" # 内网网段
# DHCP 租约范围 # DHCP 租约范围
@ -19,6 +20,7 @@ LAN_INTERFACE="{{ lan_interface }}" # 连接内网的网卡,我们将配置静
# SSH SOCKS5 代理配置 # SSH SOCKS5 代理配置
REMOTE_SSH_USER="{{ remote_ssh_user }}" # 远程服务器的SSH用户名 REMOTE_SSH_USER="{{ remote_ssh_user }}" # 远程服务器的SSH用户名
REMOTE_SSH_IP="{{ remote_ssh_ip }}" # 远程服务器的IP地址 REMOTE_SSH_IP="{{ remote_ssh_ip }}" # 远程服务器的IP地址
REMOTE_SSH_NAME="{{ remote_ssh_name }}" # 远程服务器的IP地址
REMOTE_SSH_PORT="{{ remote_ssh_port }}" # 远程服务器的SSH端口 REMOTE_SSH_PORT="{{ remote_ssh_port }}" # 远程服务器的SSH端口
LOCAL_SOCKS5_PORT="{{ local_socks5_port }}" # SSH SOCKS5 代理在本机监听的端口 (127.0.0.1:1080) LOCAL_SOCKS5_PORT="{{ local_socks5_port }}" # SSH SOCKS5 代理在本机监听的端口 (127.0.0.1:1080)
@ -71,7 +73,7 @@ install_package() {
# ============================================================================== # ==============================================================================
# 0. 前置检查与环境初始化 # 0. 前置检查与环境初始化
# ============================================================================== # ==============================================================================
check_root # check_root
log_info "开始网关主机配置脚本..." log_info "开始网关主机配置脚本..."
log_info "更新系统软件包列表..." log_info "更新系统软件包列表..."
@ -149,11 +151,12 @@ max-lease-time ${DHCP_LEASE_TIME};
authoritative; authoritative;
# A slightly different configuration for an internal subnet. # 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}; range ${DHCP_START_IP} ${DHCP_END_IP};
option routers ${GATEWAY_LAN_IP}; option routers ${GATEWAY_LAN_IP};
option domain-name-servers ${GATEWAY_LAN_IP}; # DNS 指向网关主机本身 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}; default-lease-time ${DHCP_LEASE_TIME};
max-lease-time ${DHCP_LEASE_TIME}; max-lease-time ${DHCP_LEASE_TIME};
} }
@ -186,7 +189,7 @@ User=${curuser}
Type=simple Type=simple
# 注意:这里需要确保用户 {{ remote_ssh_user }} 是一个实际存在的用户,并且可以访>问其 $HOME/.ssh # 注意:这里需要确保用户 {{ remote_ssh_user }} 是一个实际存在的用户,并且可以访>问其 $HOME/.ssh
# 为了简化我们暂时用root运行但更推荐使用非root用户 # 为了简化我们暂时用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 策略确保连接断开时自动重连
Restart=always Restart=always
RestartSec=5 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 - (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 "规则生成器配置完成并设置定时更新。" log_info "规则生成器配置完成并设置定时更新。"
cd - > /dev/null cd - > /dev/null
# ============================================================================== # ==============================================================================
# 7. 配置 IPTABLES 规则实现透明代理和 NAT (保持不变) # 7. 配置 IPTABLES 规则实现透明代理和 NAT (修正完整版)
# ============================================================================== # ==============================================================================
log_info "配置 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 -F
sudo iptables -X sudo iptables -X
sudo iptables -t nat -F 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 sudo iptables -A FORWARD -o {{ lan_interface }} -i {{ wan_interface }} -m state --state RELATED,ESTABLISHED -j ACCEPT
log_info "已配置 IP 转发规则。" log_info "已配置 IP 转发规则。"
# 3. 透明代理规则 (使用 redsocks 和 ipset) # 3. 透明代理规则 (核心的 PREROUTING 链)
# 重定向到 redsocks 的流量,不处理来自 redsocks 本身的流量 log_info "配置透明代理 PREROUTING 排除和重定向规则..."
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)。"
# 排除 redsocks 自身流量循环 # --- 排除规则(必须在重定向规则之前) ---
# 这一步非常重要,避免 redsocks 自己产生流量又被 iptables 捕获
sudo iptables -t nat -A OUTPUT -p tcp -d 127.0.0.1 --dport ${REDSOCKS_PORT} -j RETURN # 3.1 排除所有重定向到本地的流量(已经通过 OUTPUT 排除,这里可以再加一层保险)
sudo iptables -t nat -A OUTPUT -p tcp -d 127.0.0.1 --dport ${LOCAL_SOCKS5_PORT} -j RETURN 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 自身流量循环的规则。" log_info "已配置排除 redsocks 自身流量循环的规则。"
# 保存 iptables 规则 # 保存 iptables 规则
@ -346,9 +385,3 @@ sudo netfilter-persistent save || log_error "保存 iptables 规则失败。"
sudo systemctl enable netfilter-persistent || log_error "启用 netfilter-persistent 服务失败。" sudo systemctl enable netfilter-persistent || log_error "启用 netfilter-persistent 服务失败。"
log_info "IPTABLES 规则已配置并保存。" log_info "IPTABLES 规则已配置并保存。"
log_info "----------------------------------------------------------------------------------"
log_info "网关主机配置完成!"
log_info "请检查 redsocks 和 ssh -D 进程是否正常运行。"
log_info "请注意SSH SOCKS5 代理可能需要手动输入密码或配置免密登录才能持久运行。"
log_info "内网主机现在应该可以自动通过 DHCP 获取 IP 并访问网络。"
log_info "----------------------------------------------------------------------------------"

View File

@ -10,7 +10,6 @@ GFWLIST_URL = "https://raw.githubusercontent.com/gfwlist/gfwlist/master/gfwlist.
def log_info(message): def log_info(message):
print(f"[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"): def generate_rules(proxy_server, foreign_dns_list, domestic_dns_list, ipset_name="gfwlist"):
""" """
生成 Dnsmasq IPSet 规则文件 生成 Dnsmasq IPSet 规则文件
@ -19,10 +18,18 @@ def generate_rules(proxy_server, foreign_dns_list, domestic_dns_list, ipset_name
log_info("错误:必须提供至少一个国内 DNS 和一个国外 DNS。") log_info("错误:必须提供至少一个国内 DNS 和一个国外 DNS。")
sys.exit(1) 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: try:
log_info(f"正在从 {GFWLIST_URL} 下载 GFWList...") log_info(f"正在从 {GFWLIST_URL} 下载 GFWList...")
# 尝试通过代理下载 GFWList (这里假设环境已配置或可以直接访问 GitHub) # 传入 proxies 参数,强制 requests 走代理
response = requests.get(GFWLIST_URL, timeout=30) response = requests.get(GFWLIST_URL, timeout=30, proxies=proxies)
response.raise_for_status() response.raise_for_status()
# 1. 解码 GFWList # 1. 解码 GFWList
@ -33,7 +40,7 @@ def generate_rules(proxy_server, foreign_dns_list, domestic_dns_list, ipset_name
content = response.text content = response.text
except requests.exceptions.RequestException as e: except requests.exceptions.RequestException as e:
log_info(f"下载 GFWList 失败,请检查网络连接: {e}") log_info(f"致命错误:通过代理下载 GFWList 失败,请检查 SSH SOCKS5 隧道状态!: {e}")
# 如果下载失败,中止脚本以避免生成空文件 # 如果下载失败,中止脚本以避免生成空文件
sys.exit(1) sys.exit(1)
@ -67,13 +74,13 @@ def generate_rules(proxy_server, foreign_dns_list, domestic_dns_list, ipset_name
try: try:
with open(dnsmasq_conf_path, 'w') as f: 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 服务器 # 写入所有国内 DNS 服务器
for dns in domestic_dns_list: for dns in domestic_dns_list:
f.write(f"server={dns}\n") f.write(f"server={dns}\n")
# GFWList 域名设置 (通过代理的上游 DNS 解析,并加入 IPSet) # GFWList 域名设置
for domain in sorted(list(domains)): for domain in sorted(list(domains)):
# 1. 将域名加入 IPSet 集合 (由 dnsmasq 动态处理) # 1. 将域名加入 IPSet 集合 (由 dnsmasq 动态处理)
f.write(f"ipset=/{domain}/{ipset_name}\n") f.write(f"ipset=/{domain}/{ipset_name}\n")
@ -107,6 +114,9 @@ if __name__ == "__main__":
args = parser.parse_args() args = parser.parse_args()
# 在开始生成规则之前,必须确保 requests 库已经安装 socks5 支持
# 确保在 Bash 脚本中已经执行: sudo pip3 install requests PySocks
log_info("开始生成 GFWList 规则...") log_info("开始生成 GFWList 规则...")
generate_rules(args.proxy, args.foreign_dns, args.domestic_dns, args.name) generate_rules(args.proxy, args.foreign_dns, args.domestic_dns, args.name)
log_info("规则生成完成。") log_info("规则生成完成。")

View File

@ -15,10 +15,11 @@ config_vars = {
"dhcp_lease_time": "7200", "dhcp_lease_time": "7200",
"remote_ssh_user": "kww", "remote_ssh_user": "kww",
"remote_ssh_ip": "8.222.165.87", # !!! 替换为你的远程SSH服务器IP "remote_ssh_ip": "8.222.165.87", # !!! 替换为你的远程SSH服务器IP
"remote_ssh_name": "atvoe.com", # !!! 替换为你的远程SSH服务器域名
"remote_ssh_port": "22", "remote_ssh_port": "22",
"local_socks5_port": "1080", "local_socks5_port": "1080",
"redsocks_port": "55555", "redsocks_port": "55555",
"domestic_dns1": "223.5.5.5" "domestic_dns1": "223.5.5.5",
"domestic_dns2": "114.114.114.114", "domestic_dns2": "114.114.114.114",
"foreign_dns1": "8.8.8.8", "foreign_dns1": "8.8.8.8",
"foreign_dns2": "1.1.1.1", "foreign_dns2": "1.1.1.1",
@ -39,13 +40,5 @@ with open('gateway.sh', 'w') as f:
f.write(rendered_gateway_script) f.write(rendered_gateway_script)
print("Generated gateway_config.sh") 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("\n!!! 请务必检查生成的 .sh 文件,特别是 SSH 代理和网卡名称等配置。")
print("在执行前给它们添加可执行权限chmod +x gateway_config.sh client_config.sh") print("在执行前给它们添加可执行权限chmod +x gateway_config.sh client_config.sh")