softroute/scripts/gateway.sh
2025-11-27 16:00:47 +08:00

355 lines
15 KiB
Bash
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 (用于其他内网主机连接)
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_PORT="{{ remote_ssh_port }}" # 远程服务器的SSH端口
LOCAL_SOCKS5_PORT="{{ local_socks5_port }}" # SSH SOCKS5 代理在本机监听的端口 (127.0.0.1:1080)
# 透明代理工具 redsocks2 监听端口
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代理访问)
# 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, redsocks2, git, python3-pip, isc-dhcp-server..."
install_package net-tools
install_package iptables-persistent
install_package ipset
install_package dnsmasq
install_package redsocks2
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 {{ gateway_lan_cidr%.* }}.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;
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 "启动 SSH SOCKS5 代理 (${REMOTE_SSH_USER}@${REMOTE_SSH_IP}:${REMOTE_SSH_PORT})..."
# 检查 SSH 密钥是否存在,否则可能需要密码或生成密钥
if [ ! -f "$HOME/.ssh/id_rsa" ] && [ ! -f "$HOME/.ssh/id_dsa" ]; then
log_warn "SSH 密钥文件不存在。SSH 连接可能需要输入密码或先生成密钥对。"
log_warn "请确保可以无密码SSH登录到远程服务器或手动输入密码完成连接。"
fi
# 确保旧的代理进程被杀死
sudo pkill -f "ssh -D ${LOCAL_SOCKS5_PORT}" || true
# 启动 SSH SOCKS5 代理,后台运行
sudo ssh -D ${LOCAL_SOCKS5_PORT} -N -f -p ${REMOTE_SSH_PORT} ${REMOTE_SSH_USER}@${REMOTE_SSH_IP} -o ExitOnForwardFailure=yes
if [ $? -ne 0 ]; then
log_error "SSH SOCKS5 代理启动失败。请检查远程SSH服务器信息和连接。"
fi
log_info "SSH SOCKS5 代理已在 127.0.0.1:${LOCAL_SOCKS5_PORT} 启动。"
# 验证代理是否在监听 (需要一点时间启动)
sleep 2
if ! ss -tnlp | grep ":${LOCAL_SOCKS5_PORT}" &>/dev/null; then
log_error "SSH SOCKS5 代理端口 ${LOCAL_SOCKS5_PORT} 未在监听。请手动检查 SSH 进程。"
fi
# ==============================================================================
# 4. 配置 redsocks2 (透明 SOCKS5 代理) (保持不变)
# ==============================================================================
log_info "配置 redsocks2 透明代理..."
# 备份现有配置
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 redsocks2 || log_error "重启 redsocks2 服务失败。"
sudo systemctl enable redsocks2 || log_error "启用 redsocks2 服务失败。"
log_info "redsocks2 透明代理已配置并启动,监听 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_dns }}
# 国外域名通过 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 "安装和配置 gfwlist2new 工具..."
if [ ! -d "${GFWLIST2NEW_DIR}" ]; then
log_info "克隆 gfwlist2new 仓库..."
sudo git clone "{{ gfwlist2new_repo }}" "${GFWLIST2NEW_DIR}" || log_error "克隆 gfwlist2new 仓库失败。"
fi
cd "${GFWLIST2NEW_DIR}"
log_info "安装 gfwlist2new 依赖..."
sudo pip3 install -r requirements.txt || log_error "安装 gfwlist2new 依赖失败。"
log_info "配置 gfwlist2new 的配置文件 config.conf..."
# 备份现有配置
sudo cp config.conf config.conf.bak_$(date +%Y%m%d%H%M%S) || log_warn "备份 gfwlist2new config.conf 失败。"
# 更新 config.conf 以适应我们的需求
# gfwlist2new 默认会将 gfwlist 中的域名指向代理IP
# IPSET_NAME 是 ipset 列表的名称
# DNSMASQ_CONF_PATH 是 dnsmasq 配置文件路径
# SOCKS5_SERVER 是代理服务器的IP和端口 (这里是 redsocks2 监听的IP和端口)
cat <<EOF | sudo tee config.conf > /dev/null
[DEFAULT]
# IPSet Config
IPSET_NAME = gfwlist
IPSET_FILE = /etc/ipset/gfwlist.conf
# Dnsmasq Config
DNSMASQ_CONF_PATH = /etc/dnsmasq.d/gfwlist_router.conf
DNSMASQ_LOG_PATH = /var/log/dnsmasq.log
DNS_PROXY_SERVER = 127.0.0.1#${REDSOCKS_PORT} # 这里的端口要指向 redsocks2
# Proxy Config
SOCKS5_SERVER = 127.0.0.1:${LOCAL_SOCKS5_PORT} # 这里的端口是SSH SOCKS5代理
# Other
GITHUB_RAW_URL = raw.githubusercontent.com
EOF
log_info "执行 gfwlist2new 生成 ipset 和 dnsmasq 规则..."
# 第一次运行,生成规则并应用
sudo python3 gfwlist2new.py -s "${SOCKS5_SERVER}" -f "{{ foreign_dns }}" -d "{{ domestic_dns }}" || log_error "gfwlist2new 运行失败。"
# 重启 dnsmasq 应用新规则
sudo systemctl restart dnsmasq || log_error "重启 dnsmasq (gfwlist) 失败。"
log_info "设置 gfwlist2new 定时更新任务 (每天凌晨 3:00)..."
(sudo crontab -l 2>/dev/null; echo "0 3 * * * cd ${GFWLIST2NEW_DIR} && sudo python3 gfwlist2new.py -s ${SOCKS5_SERVER} -f {{ foreign_dns }} -d {{ domestic_dns }} && sudo systemctl restart dnsmasq") | sudo crontab -
log_info "gfwlist2new 配置完成并设置定时更新。"
cd - > /dev/null
# ==============================================================================
# 7. 配置 IPTABLES 规则实现透明代理和 NAT (保持不变)
# ==============================================================================
log_info "配置 IPTABLES 规则 (NAT 和透明代理)..."
# 清除现有规则,避免冲突
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. 透明代理规则 (使用 redsocks2 和 ipset)
# 重定向到 redsocks2 的流量,不处理来自 redsocks2 本身的流量
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流量到 redsocks2)。"
# 排除 redsocks2 自身流量循环
# 这一步非常重要,避免 redsocks2 自己产生流量又被 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
log_info "已配置排除 redsocks2 自身流量循环的规则。"
# 保存 iptables 规则
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 "请检查 redsocks2 和 ssh -D 进程是否正常运行。"
log_info "请注意SSH SOCKS5 代理可能需要手动输入密码或配置免密登录才能持久运行。"
log_info "内网主机现在应该可以自动通过 DHCP 获取 IP 并访问网络。"
log_info "----------------------------------------------------------------------------------"