softroute/ecs/gateway_config.sh.j2
2025-11-30 11:45:02 +08:00

312 lines
14 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
curuser=ymq
sudo systemctl stop systemd-resolved
sudo systemctl disable systemd-resolved
sudo rm /etc/resolv.conf
echo "nameserver 1.1.1.1" | sudo tee /etc/resolv.conf
# ==============================================================================
# 网关主机配置变量 - 请根据你的实际环境修改!
# ==============================================================================
# 网关主机的主网卡名称 (例如 eth0, ens5 等)
GATEWAY_MAIN_INTERFACE="{{ gateway_main_interface }}"
# 网关主机的内网IP (脚本会自动获取用于监听和DNS)
# 我们假设该网卡上有一个内网IP例如 10.0.0.10
GATEWAY_LAN_IP=""
GATEWAY_PUBLIC_IP="" # 网关主机的公网IP脚本会尝试获取
GATEWAY_LAN_CIDR="{{ internal_network_range }}" # 你的云平台内部网络的IP范围例如 10.0.0.0/16
# 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
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..."
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 服务器
# 动态获取网关主机的内网IP和公网IP
log_info "获取网关主机的 IP 地址..."
GATEWAY_LAN_IP=$(ip -4 addr show dev ${GATEWAY_MAIN_INTERFACE} | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | head -1)
if [ -z "${GATEWAY_LAN_IP}" ]; then
log_error "无法获取网卡 ${GATEWAY_MAIN_INTERFACE} 的内网 IP 地址。请检查网卡名称或网络配置。"
fi
log_info "网关主机的内网 IP: ${GATEWAY_LAN_IP}"
# 尝试获取公网IP (可能需要专门的服务或配置这里假设通常第一个IP是公网或路由器的出口IP)
# 更准确的获取公网IP方式是 curl ifconfig.me 但这取决于网络能否访问外网
# 这里我们直接使用网卡IP进行MASQUERADE或如果云平台有专门的公网IP需要替换
GATEWAY_PUBLIC_IP=$(curl -s ifconfig.me) # 尝试通过外部服务获取公网IP如果无法访问则MASQUERADE会使用网卡主IP
if [ -z "${GATEWAY_PUBLIC_IP}" ]; then
log_warn "无法通过外部服务获取公网 IP。MASQUERADE 将使用网卡主 IP。"
# 另一种方式是获取该网卡上的任何一个非10. 172.16-31. 192.168. 的IP
# GATEWAY_PUBLIC_IP=$(ip -4 addr show dev ${GATEWAY_MAIN_INTERFACE} | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | grep -vE '^(10\.|172\.(1[6-9]|2[0-9]|3[0-1])\.|192\.168\.)' | head -1)
fi
log_info "网关主机的公网 IP (或出口 IP): ${GATEWAY_PUBLIC_IP:-Using main interface IP}"
DNSMASQ_LISTEN_IP="${GATEWAY_LAN_IP}"
# ==============================================================================
# 1. 配置网卡和 IP 转发
# ==============================================================================
log_info "确保网关主机主网卡 (${GATEWAY_MAIN_INTERFACE}) 为 DHCP 模式..."
# Netplan 配置 (通常云服务器默认就是 DHCP)
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:
{{ gateway_main_interface }}:
dhcp4: true # 云服务器通常使用DHCP获取IP
optional: true
EOF
sudo chmod 600 ${NETPLAN_FILE}
sudo netplan try --yes
sudo netplan apply || log_error "应用 Netplan 配置失败,请检查网卡名称。"
log_info "网卡 ${GATEWAY_MAIN_INTERFACE} 已配置为 DHCP 模式。"
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. SSH SOCKS5 代理 (使用 Systemd 持久化)
# ==============================================================================
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_IP} -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} 监听,支持自动重连。"
# ==============================================================================
# 3. Redsocks2 透明代理 (保持不变)
# ==============================================================================
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;
port = ${LOCAL_SOCKS5_PORT};
type = 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}。"
# ==============================================================================
# 4. Dnsmasq 智能 DNS 转发 (保持不变监听内网IP)
# ==============================================================================
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
listen-address=${DNSMASQ_LISTEN_IP},127.0.0.1
no-resolv
no-poll
server={{ domestic_dns }}
conf-dir=/etc/dnsmasq.d
EOF
# 确保 dnsmasq 不会干扰云平台的DHCP (不再是 isc-dhcp-server)
# 这些选项只是建议如果云平台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 转发已初步配置,监听 ${DNSMASQ_LISTEN_IP}。"
# ==============================================================================
# 5. 安装和配置 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 失败。"
cat <<EOF | sudo tee config.conf > /dev/null
[DEFAULT]
IPSET_NAME = gfwlist
IPSET_FILE = /etc/ipset/gfwlist.conf
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代理
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 运行失败。"
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
# ==============================================================================
# 6. 配置 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 (使内网访问外网)
# MASQUERADE 应该基于出口接口,这里就是 GATEWAY_MAIN_INTERFACE
# --to-source 可以明确指定公网IP如果GATEWAY_PUBLIC_IP能获取到
if [ -n "${GATEWAY_PUBLIC_IP}" ]; then
sudo iptables -t nat -A POSTROUTING -o ${GATEWAY_MAIN_INTERFACE} -s ${GATEWAY_LAN_CIDR} -j SNAT --to-source ${GATEWAY_PUBLIC_IP}
log_info "已配置 NAT 规则出口IP为 ${GATEWAY_PUBLIC_IP}。"
else
sudo iptables -t nat -A POSTROUTING -o ${GATEWAY_MAIN_INTERFACE} -s ${GATEWAY_LAN_CIDR} -j MASQUERADE
log_info "已配置 NAT 规则,使用 ${GATEWAY_MAIN_INTERFACE} 主IP作为出口。"
fi
# 2. IP 转发规则 (允许所有来自内网段的流量通过 GATEWAY_MAIN_INTERFACE 转发)
# 这里更简化,所有来自内网段的流量都接受转发
sudo iptables -A FORWARD -s ${GATEWAY_LAN_CIDR} -o ${GATEWAY_MAIN_INTERFACE} -j ACCEPT
sudo iptables -A FORWARD -i ${GATEWAY_MAIN_INTERFACE} -d ${GATEWAY_LAN_CIDR} -m state --state RELATED,ESTABLISHED -j ACCEPT
log_info "已配置 IP 转发规则。"
# 3. 透明代理规则 (使用 redsocks2 和 ipset)
# 重定向所有来自内网段的 TCP 流量如果目标IP在 gfwlist 中,则到 redsocks2
sudo iptables -t nat -A PREROUTING -i ${GATEWAY_MAIN_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)。"
# 4. 排除 redsocks2 自身流量循环 (保持不变)
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 "----------------------------------------------------------------------------------"