softroute/scripts/gateway.sh.j2
2025-11-30 15:07:11 +08:00

388 lines
16 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
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 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.
{% 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 {{ 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} 监听,支持自动重连。"
# ==============================================================================
# 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 失败。"
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; # SSH SOCKS5 代理的地址
port = ${LOCAL_SOCKS5_PORT}; # SSH SOCKS5 代理的端口
type = socks5; # 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
server={{ domestic_dns1 }}
server={{ domestic_dns2 }}
# 国外域名通过 gfwlist2new 生成的配置文件转发到代理
# dnsmasq will read additional configuration files from /etc/dnsmasq.d
conf-dir=/etc/dnsmasq.d
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 (保持不变)
# ==============================================================================
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
cp $LOCAL_SCRIPT_NAME ${GFWLIST2NEW_DIR}
# 确保安装 requests 依赖
log_info "安装 Python 依赖requests..."
sudo pip3 install requests || log_error "安装 requests 依赖失败。"
# 写入新的 Python 脚本(假设您已将上面的内容保存到临时文件并复制到这里)
# 推荐您手动将上面的 Python 代码保存到 ${GFWLIST2NEW_DIR}/${LOCAL_SCRIPT_NAME}
# 如果要用脚本自动写入,代码会非常长,这里跳过写入步骤,假设文件已存在。
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 规则生成脚本..."
# 使用 -p, -f, -d 参数执行新脚本
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)..."
# 设置定时任务
(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 (修正完整版)
# ==============================================================================
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
# 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 连接本身被重定向,防止无限循环。
# 假设变量 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 规则
sudo netfilter-persistent save || log_error "保存 iptables 规则失败。"
sudo systemctl enable netfilter-persistent || log_error "启用 netfilter-persistent 服务失败。"
log_info "IPTABLES 规则已配置并保存。"