softroute/scripts/gateway.sh.j2
2025-12-01 22:40:58 +08:00

434 lines
18 KiB
Django/Jinja
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/bash
set -eo pipefail
# ==============================================================================
# 网关主机配置变量 - 请根据你的实际环境修改!
# ==============================================================================
# 网关主机内网IP (用于其他内网主机连接)
curuser=$(id -un)
GATEWAY_LAN_IP="{{ gateway_lan_ip }}"
GATEWAY_LAN_CIDR="{{ gateway_lan_cidr }}" # 内网网段
# DHCP 租约范围
DHCP_START_IP="{{ dhcp_start_ip }}"
DHCP_END_IP="{{ dhcp_end_ip }}"
DHCP_LEASE_TIME="{{ dhcp_lease_time }}" # 租约时间,秒 (例如 2 小时)
# 网关主机网卡名称
WAN_INTERFACE="{{ wan_interface }}" # 连接外网的网卡通常是DHCP获取IP
LAN_INTERFACE="{{ lan_interface }}" # 连接内网的网卡我们将配置静态IP
# 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)
# 透明代理工具 redsocks 监听端口
REDSOCKS_PORT="{{ redsocks_port }}" # redsocks 在本机监听的端口
# DNSMASQ 配置
DNSMASQ_LISTEN_IP="${GATEWAY_LAN_IP}" # dnsmasq 监听的IP
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 }}"
GFWLIST2NEW_DIR="{{ gfwlist2new_dir }}"
# ==============================================================================
# 通用函数
# ==============================================================================
log_info() {
echo -e "\e[32m[INFO] $(date +'%Y-%m-%d %H:%M:%S') $1\e[0m"
}
log_warn() {
echo -e "\e[33m[WARN] $(date +'%Y-%m-%d %H:%M:%S') $1\e[0m" >&2
}
log_error() {
echo -e "\e[31m[ERROR] $(date +'%Y-%m-%d %H:%M:%S') $1\e[0m" >&2
exit 1
}
check_root() {
if [[ $EUID -ne 0 ]]; then
log_error "此脚本必须以 root 用户或使用 sudo 运行。"
fi
}
install_package() {
PACKAGE="$1"
if ! dpkg -s "$PACKAGE" &>/dev/null; then
log_info "安装 $PACKAGE..."
sudo apt install -y "$PACKAGE" || log_error "安装 $PACKAGE 失败。"
else
log_info "$PACKAGE 已安装。"
fi
}
# ==============================================================================
# 0. 前置检查与环境初始化
# ==============================================================================
# check_root
# 释放53端口
sudo systemctl stop systemd-resolved
sudo systemctl disable systemd-resolved
sudo rm /etc/resolv.conf
# 创建新的 resolv.conf 文件,将本机地址作为唯一的 DNS 服务器
echo "nameserver 127.0.0.1" | sudo tee /etc/resolv.conf > /dev/null
sudo mkdir -p --mode=0755 /usr/share/keyrings
curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg | sudo tee /usr/share/keyrings/cloudflare-main.gpg > /dev/null
cat <<EOF | sudo tee /etc/apt/sources.list.d/cloudflared.list>/dev/null
deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared jammy main
EOF
log_info "开始网关主机配置脚本..."
log_info "更新系统软件包列表..."
sudo apt update || log_error "apt update 失败。"
log_info "禁用 UFW 以避免与 iptables 冲突..."
sudo ufw disable || log_warn "UFW 未运行或禁用失败,请手动确认。"
log_info "安装必要工具net-tools, iptables-persistent, ipset, dnsmasq, redsocks, git, python3-pip, isc-dhcp-server..."
install_package net-tools
install_package iptables-persistent
install_package ipset
install_package dnsmasq
install_package redsocks
install_package cloudflared
install_package git
install_package python3-pip
install_package isc-dhcp-server # 新增 DHCP 服务器安装
# ==============================================================================
# 1. 配置内网网卡 IP 和 IP 转发
# ==============================================================================
log_info "配置网关主机内网网卡 (${LAN_INTERFACE}) 静态 IP..."
# Netplan 配置
NETPLAN_FILE="/etc/netplan/01-netcfg.yaml"
# 备份现有配置
sudo cp ${NETPLAN_FILE} ${NETPLAN_FILE}.bak_$(date +%Y%m%d%H%M%S) || log_warn "备份Netplan文件失败。"
cat <<EOF | sudo tee ${NETPLAN_FILE} > /dev/null
network:
version: 2
renderer: networkd
ethernets:
{{ wan_interface }}:
dhcp4: true # 外网网卡通常通过DHCP获取IP
optional: true # 允许此接口暂时不可用
{{ lan_interface }}:
addresses:
- ${GATEWAY_LAN_IP}/24
# 由于 dnsmasq 也会提供 DNS 服务,这里不需要单独配置 nameservers
EOF
sudo netplan try
sudo netplan apply || log_error "应用 Netplan 配置失败请检查网卡名称和IP地址。"
log_info "内网网卡 ${LAN_INTERFACE} 已配置 IP: ${GATEWAY_LAN_IP}。"
log_info "开启 IP 转发功能..."
sudo sed -i '/^#net.ipv4.ip_forward=1/s/^#//' /etc/sysctl.conf || true # 取消注释
echo "net.ipv4.ip_forward=1" | sudo tee -a /etc/sysctl.conf > /dev/null # 确保添加
sudo sysctl -p > /dev/null || log_error "应用 sysctl 配置失败。"
log_info "IP 转发已开启。"
# ==============================================================================
# 2. 配置 DHCP 服务器 (ISC DHCP Server)
# ==============================================================================
log_info "配置 ISC DHCP Server..."
# 确保 DHCP 服务器只监听内网接口
sudo sed -i "s/^INTERFACESv4=.*/INTERFACESv4=\"${LAN_INTERFACE}\"/g" /etc/default/isc-dhcp-server || log_error "配置 isc-dhcp-server 监听接口失败。"
# 备份 DHCP 主配置文件
sudo cp /etc/dhcp/dhcpd.conf /etc/dhcp/dhcpd.conf.bak_$(date +%Y%m%d%H%M%S) || log_warn "备份 dhcpd.conf 失败。"
# 清空并写入新的 DHCP 配置
cat <<EOF | sudo tee /etc/dhcp/dhcpd.conf > /dev/null
# DHCPD CONFIGURATION FILE
# The ddns-update-style parameter to use for DDNS updates.
ddns-update-style none;
# Default lease time
default-lease-time ${DHCP_LEASE_TIME};
max-lease-time ${DHCP_LEASE_TIME};
# If this DHCP server is the official DHCP server for the local network segment
# then uncomment the authoritative directive.
authoritative;
# A slightly different configuration for an internal subnet.
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 {{ net_base }}.255;
default-lease-time ${DHCP_LEASE_TIME};
max-lease-time ${DHCP_LEASE_TIME};
}
# No service will be given on this subnet, but DECLINEs from hosts on this
# subnet will be processed.
# subnet 192.168.1.0 netmask 255.255.255.0 {
# }
EOF
sudo systemctl restart isc-dhcp-server || log_error "重启 isc-dhcp-server 服务失败。"
sudo systemctl enable isc-dhcp-server || log_error "启用 isc-dhcp-server 服务失败。"
log_info "ISC DHCP Server 已配置并启动,在 ${LAN_INTERFACE} 上提供 DHCP 服务。"
# ==============================================================================
# 3. 启动 SSH SOCKS5 代理 (保持不变)
# ==============================================================================
log_info "${curuser}:配置 SSH SOCKS5 代理 Systemd 服务..."
SSH_SOCKS5_SERVICE_FILE="/etc/systemd/system/ssh-socks5.service"
# 动态创建 Service 文件
cat <<EOF | sudo tee ${SSH_SOCKS5_SERVICE_FILE} > /dev/null
[Unit]
Description=SSH SOCKS5 Proxy Service
After=network-online.target
[Service]
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_NAME} -o ExitOnForwardFailure=yes -o ServerAliveInterval=60 -o ServerAliveCountMax=3
# 使用 Restart 策略确保连接断开时自动重连
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
log_info "启动并启用 SSH SOCKS5 代理服务..."
sudo systemctl daemon-reload
sudo systemctl enable ssh-socks5.service || log_error "启用 ssh-socks5 服务失败>。"
sudo systemctl restart ssh-socks5.service || log_error "启动 ssh-socks5 服务失败
。请检查连接和免密登录配置。"
sleep 5 # 留出时间让服务启动和重试
if ! ss -tnlp | grep ":${LOCAL_SOCKS5_PORT}" &>/dev/null; then
log_error "SSH SOCKS5 代理端口 ${LOCAL_SOCKS5_PORT} 未在监听。请手动检查 SSH 进程。"
fi
log_info "SSH SOCKS5 代理已通过 Systemd 启动,并在 127.0.0.1:${LOCAL_SOCKS5_PORT} 监听,支持自动重连。"
cat <<EOF | sudo tee /etc/systemd/system/cloudflared-dns.service || log_warn "创建systemd DNS服务端口53053"
[Unit]
Description=cloudflared DNS-over-HTTPS proxy
After=network.target
[Service]
Type=simple
ExecStart=/usr/bin/cloudflared proxy-dns \
--address 127.0.0.1 \
--port 53053 \
--upstream https://1.1.1.1/dns-query \
--upstream https://1.0.0.1/dns-query \
--upstream https://8.8.8.8/dns-query \
--upstream https://8.8.4.4/dns-query
Restart=on-failure
User=nobody
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now cloudflared-dns
sudo systemctl status cloudflared-dns
# ==============================================================================
# 4. 配置 redsocks (透明 SOCKS5 代理) (请替换为以下修正代码!)
# ==============================================================================
log_info "配置 redsocks 透明代理..."
# 备份现有配置
sudo cp /etc/redsocks.conf /etc/redsocks.conf.bak_$(date +%Y%m%d%H%M%S) || log_warn "备份 redsocks.conf 失败。"
# ----------------------------------------------------------------------------------
# ❗❗ 关键修正:移除所有注释和分号,以避免 redsocks 配置文件解析错误 ❗❗
cat <<EOF | sudo tee /etc/redsocks.conf > /dev/null
base {
log_debug = off;
log_info = on;
log = "syslog:daemon";
daemon = on;
redirector = iptables;
}
redsocks {
local_ip = 127.0.0.1;
local_port = ${REDSOCKS_PORT};
ip = 127.0.0.1;
port = ${LOCAL_SOCKS5_PORT};
type = socks5;
}
EOF
# ----------------------------------------------------------------------------------
sudo systemctl restart redsocks || log_error "重启 redsocks 服务失败。"
sudo systemctl enable redsocks || log_error "启用 redsocks 服务失败。"
log_info "redsocks 透明代理已配置并启动,监听 127.0.0.1:${REDSOCKS_PORT}。"
# ==============================================================================
# 5. 配置 dnsmasq 智能 DNS 转发 (保持不变)
# ==============================================================================
log_info "配置 dnsmasq 智能 DNS 转发..."
# 备份现有配置
sudo cp /etc/dnsmasq.conf /etc/dnsmasq.conf.bak_$(date +%Y%m%d%H%M%S) || log_warn "备份 dnsmasq.conf 失败。"
sudo rm -f /etc/dnsmasq.d/* # 清理旧的额外配置
cat <<EOF | sudo tee /etc/dnsmasq.conf > /dev/null
# 指定监听地址只监听内网IP和本地环回
listen-address=${DNSMASQ_LISTEN_IP},127.0.0.1
# 不使用 /etc/resolv.conf 中的 DNS 服务器
no-resolv
no-poll
# 将国外 DNS 指向 cloudflared永不污染
server=127.0.0.1#53053
# 国内域名直连公共DNS
server={{ domestic_dns1 }}
server={{ domestic_dns2 }}
# 国外域名通过 gfwlist2new 生成的配置文件转发到代理
# dnsmasq will read additional configuration files from /etc/dnsmasq.d
conf-dir=/etc/dnsmasq.d
user=root
EOF
# 注意DHCP 服务器功能由 isc-dhcp-server 提供dnsmasq 不再提供 DHCP。
# 确保 dnsmasq 不会干扰 DHCP
echo "dhcp-option=option:router,${GATEWAY_LAN_IP}" | sudo tee -a /etc/dnsmasq.conf > /dev/null
echo "dhcp-option=option:dns-server,${GATEWAY_LAN_IP}" | sudo tee -a /etc/dnsmasq.conf > /dev/null
sudo systemctl restart dnsmasq || log_error "重启 dnsmasq 服务失败。"
sudo systemctl enable dnsmasq || log_error "启用 dnsmasq 服务失败。"
log_info "dnsmasq 智能 DNS 转发已初步配置。"
# ==============================================================================
# 6. 安装和配置 gfwlist2new (已修正 crontab 参数和 PySocks 依赖)
# ==============================================================================
log_info "集成和配置 GFW 规则生成器 (gfw_rules_generator.py)..."
LOCAL_SCRIPT_NAME="gfw_rules_generator.py"
if [ ! -d "${GFWLIST2NEW_DIR}" ]; then
log_info "创建脚本目录..."
sudo mkdir -p "${GFWLIST2NEW_DIR}" || log_error "创建目录 ${GFWLIST2NEW_DIR} 失败。"
fi
sudo cp $LOCAL_SCRIPT_NAME ${GFWLIST2NEW_DIR}
# 确保安装 requests 和 PySocks 依赖
log_info "安装 Python 依赖requests 和 PySocks..."
sudo pip3 install requests PySocks || log_error "安装 requests/PySocks 依赖失败。"
log_info "请确保 gfw_rules_generator.py 文件已存在于 ${GFWLIST2NEW_DIR}/"
cd "${GFWLIST2NEW_DIR}"
# 定义 SOCKS5 代理地址
SOCKS5_SERVER_ADDR="127.0.0.1:${LOCAL_SOCKS5_PORT}"
log_info "执行 GFW 规则生成脚本..."
# 修正后的参数传递方式 (注意双引号和变量组)
sudo python3 "${LOCAL_SCRIPT_NAME}" \
-p "${SOCKS5_SERVER_ADDR}" \
-f {{ foreign_dns1 }} {{ foreign_dns2 }} \
-d {{ domestic_dns1 }} {{ domestic_dns2 }} \
|| log_error "规则生成脚本运行失败。"
sudo systemctl restart dnsmasq || log_error "重启 dnsmasq (gfwlist) 失败。"
log_info "设置 GFW 规则定时更新任务 (每天凌晨 3:00)..."
# 设置定时任务(已修正国内 DNS 变量)
(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_dns2 }} && sudo systemctl restart dnsmasq") | sudo crontab -
log_info "规则生成器配置完成并设置定时更新。"
cd - > /dev/null
# ==============================================================================
# 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
sudo iptables -t nat -X
sudo iptables -t mangle -F
sudo iptables -t mangle -X
sudo iptables -I INPUT -p udp --dport 5353 -j ACCEPT
sudo iptables -I INPUT -p tcp --dport 5353 -j ACCEPT
# 1. 开启 NAT (使内网访问外网)
sudo iptables -t nat -A POSTROUTING -o {{ wan_interface }} -j MASQUERADE
log_info "已配置 NAT 规则。"
# 2. IP 转发规则 (允许内网到外网,并允许已建立连接回传)
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. 透明代理规则 (核心的 PREROUTING 链)
log_info "配置透明代理 PREROUTING 排除和重定向规则..."
# --- 排除规则(必须在重定向规则之前) ---
# 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 连接本身被重定向)
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 在 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)。"
# ❗❗ 关键修正:必须排除远程 SSH 服务器的 IP ❗❗
# 确保 SSH 隧道本身可以直连建立,不被代理。
sudo iptables -t nat -A OUTPUT -d {{ remote_ssh_ip }} -p tcp -j RETURN
log_info "已配置排除远程 SSH 服务器 {{ remote_ssh_ip }} 的规则。"
# 4. 排除 redsocks 自身流量循环 (OUTPUT 链)
# 排除目标地址是 127.0.0.1 的流量,它们是 SSH 和 Redsocks 的内部通信。
sudo iptables -t nat -A OUTPUT -d 127.0.0.1 -p tcp -j RETURN
log_info "已配置排除 redsocks 自身流量循环的规则。"
# 核心 OUTPUT 重定向规则捕获所有目标IP在 gfwlist ipset 集合中的网关主机自身TCP流量重定向到 redsocks。
sudo iptables -t nat -A OUTPUT -p tcp -m set --match-set gfwlist dst -j REDIRECT --to-ports ${REDSOCKS_PORT}
# 保存 iptables 规则
sudo netfilter-persistent save || log_error "保存 iptables 规则失败。"
sudo systemctl enable netfilter-persistent || log_error "启用 netfilter-persistent 服务失败。"
log_info "IPTABLES 规则已配置并保存。"